Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 122 additions & 34 deletions content/docs/en/apis/stacks-blockchain-api/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,140 @@ description: Understand the architecture of the Stacks Blockchain API.

![Stacks Blockchain API architecture](/images/api/architecture.svg)

## RPC Endpoints
## Overview

The `stacks-node` has its own minimal set of http endpoints referred to as `RPC endpoints`.
The Stacks Blockchain API sits between Stacks nodes and the apps that use chain data. It does three
things:

- The `stacks-blockchain-api` allows clients to access RPC endpoints by proxying requests to a load-balanced pool of `stacks-nodes`.
- For more details on RPC endpoints, see: [RPC Endpoints Documentation](https://github.com/blockstack/stacks-blockchain/blob/master/docs/rpc-endpoints.md)
- Common RPC endpoints include:
- `POST /v2/transactions` - Broadcast a transaction
- `GET /v2/pox` - Retrieve current Proof of Transfer (PoX) relevant information
- `POST /v2/contracts/call-read/<contract>/<function>` - Evaluate and return the result of calling a Clarity function
- `POST /v2/fees/transaction` - Evaluate a given transaction and provide transaction fee estimation data
- `GET /v2/accounts/<address>` - Fetch the current nonce required for creating transactions
- **Indexes the chain.** It receives events from a Stacks node and stores them as relational data
in PostgreSQL.
- **Serves that data.** Its REST endpoints and real-time subscriptions read from PostgreSQL, so
they can answer questions a node cannot, such as the full transaction history of an account.
- **Proxies the node.** Requests to the node's own `/v2/*` RPC endpoints pass through the API to a
Stacks node.

## Event ingestion

## Additional Endpoints
A Stacks node doesn't keep an account's transaction history, and it isn't built to serve many
clients at once. The API fills that gap by indexing everything the node reports.

The Stacks Blockchain API implements additional endpoints that provide data unavailable directly from Stacks nodes due to various constraints.
The API runs an event server (port `3700` by default) that a Stacks node pushes events to over
HTTP. To connect a node, add an event observer to its configuration:

```toml
[[events_observer]]
endpoint = "localhost:3700"
events_keys = ["*"]
timeout_ms = 60_000
```

- The `stacks-node` may not persist certain data or may not serve it efficiently to many clients. For instance, while it can return the current STX balance of an account, it cannot provide a history of account transactions.
- The API implements the Rosetta specification by Coinbase, an open standard designed to simplify blockchain deployment and interaction. More information can be found at [Rosetta API](https://www.rosetta-api.org/).
- The API includes support for the Blockchain Naming System (BNS) endpoints. Details are available at [BNS Documentation](https://docs.stacks.co/clarity/example-contracts/bns).
- For Express.js routes, see the directory `/src/api/routes`.
The node then sends:

The API creates an "event observer" http server which listens for events from a stacks-node "event emitter".
- **New blocks**, with each transaction and what it produced: asset transfers, smart contract logs,
and execution costs
- **New burn blocks** from the Bitcoin chain
- **Mempool activity**: newly received transactions, and transactions dropped from the mempool
- **Signer data**: StackerDB chunks and block proposal responses

Events are HTTP POST requests containing:
- Blocks
- Transactions
The API decodes these and writes them to PostgreSQL. The ingestion code is in
[`src/event-stream`](https://github.com/hirosystems/stacks-blockchain-api/tree/main/src/event-stream).

Byproducts of executed transactions such as:
- Asset transfers
- Smart-contract log data
- Execution cost data
### Ingesting from Stacks Node Publisher

The API processes and stores these events as relational data in PostgreSQL. For the "event observer" code, see `/src/event-stream`.
Instead of receiving events directly from a node, the API can consume them from
[Stacks Node Publisher](https://github.com/stx-labs/stacks-node-publisher) (SNP), which stores
every node event in PostgreSQL and streams it to consumers over Redis. This decouples the API from
any single node, and SNP can replay history from any block, so a new API instance can sync without
a node re-sending events.

## OpenAPI and JSON Schema
Set `SNP_EVENT_STREAMING=true` and `SNP_REDIS_URL` to enable it. `SNP_BLOCKS_ONLY_STREAMING=true`
limits the stream to blocks and burn blocks, which speeds up a sync from genesis.

All http endpoints and responses are defined in OpenAPI and JSON Schema.
## Serving data

- See `/docs/openapi.yaml`
- These are used to auto-generate the docs at https://hirosystems.github.io/stacks-blockchain-api/
- JSON Schemas are converted into TypeScript interfaces, which are used internally by the db controller module to transform SQL query results into the correct object shapes.
- OpenAPI and JSON Schemas are also used to generate a standalone `@stacks/blockchain-api-client`.
Endpoints are built with [Fastify](https://fastify.dev/), with request and response schemas
defined in [TypeBox](https://github.com/sinclairzx81/typebox). Route definitions are in
[`src/api/routes`](https://github.com/hirosystems/stacks-blockchain-api/tree/main/src/api/routes).

## Development Setup
- **REST endpoints** under `/extended/v3`, plus the `/extended/v2` endpoints that have no v3
successor, read from PostgreSQL. See the [API reference](/apis/stacks-blockchain-api) for the full
list. Older `/extended/v1` endpoints are deprecated; see
[Migrating from v1 to v3](/apis/stacks-blockchain-api/v1-to-v3-migration).
- **Real-time subscriptions** for blocks, mempool transactions, transaction updates, address
activity, and NFT events are available over WebSocket (JSON-RPC 2.0) and Socket.IO. See
[WebSockets](/apis/stacks-blockchain-api/websockets).

The easiest/quickest way to develop in this repo is using the VS Code debugger. It uses docker-compose to set up a stacks-node and Postgres instance.
Most responses carry an `ETag` tied to the state they were computed from, such as the chain tip, the
mempool, or a specific transaction. Send it back in an `If-None-Match` header, and the API answers
`304 Not Modified` when nothing has changed, without recomputing the response.

Alternatively, you can run `npm run dev:integrated` which does the same thing but without a debugger.
## Stacks node RPC proxy

A Stacks node exposes its own set of HTTP endpoints, referred to as RPC endpoints. The API forwards
requests for the node's `/v2/*` endpoints to the Stacks node it is configured with, and returns the
node's response unchanged. Commonly used RPC endpoints include:

| Endpoint | Purpose |
| --- | --- |
| `POST /v2/transactions` | Broadcast a transaction |
| `POST /v2/fees/transaction` | Estimate the fee for a transaction |
| `POST /v2/contracts/call-read/{deployer_address}/{contract_name}/{function_name}` | Call a read-only Clarity function |
| `GET /v2/accounts/{principal}` | Get an account's balance and nonce |
| `GET /v2/pox` | Get current Proof of Transfer information |

See the [Stacks Node RPC API](/apis/stacks-node-rpc-api) for every RPC endpoint.

Proxied requests take longer than requests the API answers from its own database, because each one
round-trips to a node. Avoid calling them at high frequency when an API endpoint serves the same
data.

The API forwards to a single configured node address (`STACKS_CORE_PROXY_HOST` and
`STACKS_CORE_PROXY_PORT`, falling back to the node's RPC host and port). To spread proxied traffic
across several nodes, point it at a load balancer.

:::callout
### Fee estimation can differ from the node's
When `STACKS_CORE_FEE_ESTIMATOR_ENABLED=true`, the API adjusts `POST /v2/fees/transaction`
responses. If recent blocks had spare capacity, it returns the minimum fee for the transaction's
size. Otherwise it returns the node's estimate scaled by a configurable multiplier, never below
that minimum. It is disabled by default.
:::

## Run modes

The same codebase can run as a single instance or be split up to scale, controlled by
`STACKS_API_MODE`:

| Mode | Runs | Use |
| --- | --- | --- |
| Default | Event server and API server | A single self-contained instance |
| `readonly` | API server only | Serve traffic from a database another instance writes to |
| `writeonly` | Event server only | Ingest into PostgreSQL without serving endpoints |

A common production layout is one write-only instance ingesting events and several read-only
instances behind a load balancer, all sharing one PostgreSQL database. Read-only instances fully
support WebSocket and Socket.IO subscriptions.

## OpenAPI specification and client

The OpenAPI specification is generated from the Fastify route schemas, so it can't drift from the
endpoints it describes. The committed
[`openapi.yaml`](https://github.com/hirosystems/stacks-blockchain-api/blob/main/openapi.yaml) is
regenerated with each release and excludes deprecated endpoints.

The specification powers:

- The [API reference](/apis/stacks-blockchain-api) in these docs
- [`@stacks/blockchain-api-client`](https://www.npmjs.com/package/@stacks/blockchain-api-client), a
typed TypeScript client for the REST and real-time APIs

## Running the API yourself

For local development, [Clarinet](https://github.com/stx-labs/clarinet) runs a full devnet — Bitcoin node, Stacks node,
API, and PostgreSQL — with `clarinet devnet start`.

For production, use the
[`hirosystems/stacks-blockchain-api`](https://hub.docker.com/r/hirosystems/stacks-blockchain-api)
Docker image. The
[repository README](https://github.com/hirosystems/stacks-blockchain-api#readme) covers
configuration, run modes, event replay, and development setup.
2 changes: 1 addition & 1 deletion content/docs/en/apis/stacks-blockchain-api/pagination.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Cursor-paginated endpoints accept two query parameters:
- `limit`: The number of items to return per page. Defaults to `20`, with a maximum of `50`.
- `cursor`: The cursor pointing at the page to fetch. Omit it on your first request to get the first page.

Treat the cursor as an opaque token — copy the value from a response and pass it back unchanged. (Its internal format is endpoint-specific; for example, the transactions endpoint encodes `block_height:microblock_sequence:tx_index`.)
Treat the cursor as an opaque token — copy the value from a response and pass it back unchanged. Its internal format is endpoint-specific and may change, so don't construct or parse cursors yourself.

## Response body

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Get principal nonces
sidebarTitle: Principal nonces
description: "Get a Stacks account's latest nonce state by inspecting its confirmed (anchored + microblock) transactions and the mempool, including the nonce to use for its next transaction."
description: "Get a Stacks account's latest nonce state by inspecting its confirmed transactions and the mempool, including the nonce to use for its next transaction. Only standard principals have nonces; contract principals are not valid."
full: true
---

Expand Down
20 changes: 5 additions & 15 deletions content/docs/en/apis/stacks-blockchain-api/v1-to-v3-migration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,9 @@ v1 flattened everything into the top level (`block_height`, `burn_block_time`,
`execution_cost_runtime`, `pending_balance_inbound`). v3 groups them (`block.height`,
`bitcoin_block.time`, `execution_cost.runtime`, `mempool.inbound`).

### Microblock and unanchored fields are gone
### Only canonical data is returned

Microblocks were removed in the Nakamoto upgrade. v3 has no `microblock_hash`,
`microblock_sequence`, `microblock_canonical`, `is_unanchored`, or `unanchored` query parameter.
There is also no `canonical` field. v3 only returns canonical data.
v1 marked records with a `canonical` flag. v3 has no such field: it only returns canonical data.

### ISO timestamp duplicates are gone

Expand Down Expand Up @@ -123,8 +121,6 @@ block height, a block hash, or the literal `latest`.
| `post_condition_mode` | Removed |
| `anchor_mode` | Removed |
| `canonical` | Removed — v3 only returns canonical data |
| `is_unanchored` | Removed |
| `microblock_*` | Removed — microblocks no longer exist |
| — | `block.index_hash` (new) |
| — | `vm_error` (new) |

Expand Down Expand Up @@ -198,8 +194,7 @@ In v1, `estimated_balance` was the **total** balance plus the pending mempool de
locked STX is excluded. If you were subtracting `locked` yourself, stop.
:::

v1 accepted `until_block` and `unanchored` on the balance endpoints. v3 always reports the
current chain tip.
v1 accepted `until_block` on the balance endpoints. v3 always reports the current chain tip.

### FT and NFT balances

Expand Down Expand Up @@ -371,8 +366,7 @@ The quantity itself is also defined differently. v1 `total_stx` was the circulat
given block height, with `unlocked_stx` tracking the unlocked portion separately. v3 `total` is
the total **liquid** supply at the current chain tip: all STX minted (vesting unlocks included)
plus matured miner coinbase rewards, minus burned STX. There is no separate locked/unlocked
split, and v3 always reports the chain tip — the v1 `height` and `unanchored` query parameters
are gone.
split, and v3 always reports the chain tip — the v1 `height` query parameter is gone.
:::

## Fungible tokens
Expand Down Expand Up @@ -465,8 +459,7 @@ type: warn
### `tx_metadata` has no v3 equivalent
v1 could inline a full transaction object into each row via `tx_metadata=true`. v3 returns the
transaction id and event index only; fetch the transaction separately from
`GET /extended/v3/transactions/{tx_id}` when you need its detail. The `unanchored` parameter is
also gone, as everywhere else in v3.
`GET /extended/v3/transactions/{tx_id}` when you need its detail.
:::

## Network block times
Expand Down Expand Up @@ -514,9 +507,6 @@ These are deprecated with no successor. Plan around them rather than swapping a
| `GET /extended/v1/tx/events` | Global event feed filtered by principal, transaction, or event type. Several v3 endpoints cover parts of it: per-transaction events at `GET /extended/v3/transactions/{tx_id}/events`, a principal's history for one fungible token at `GET /extended/v3/principals/{principal}/transfers/ft/{asset_identifier}`, a principal's STX transfers at `GET /extended/v3/principals/{principal}/transfers/stx/{inbound,outbound}`, and per-principal asset movement at `GET /extended/v3/principals/{principal}/balance-changes`. There is no single global event feed. |
| `GET /extended/v1/address/{principal}/assets` | Closest equivalent is `GET /extended/v3/principals/{principal}/balance-changes`, which reports net balance deltas rather than raw asset events. |
| `GET /extended/v1/contract/by_trait` | Searches deployed contracts by Clarity trait ABI. No v3 equivalent. To look up a specific contract you already know, use `GET /extended/v3/smart-contracts/{contract_id}`. |
| `GET /extended/v1/microblock` | Microblocks were removed in the Nakamoto upgrade and are no longer produced. |
| `GET /extended/v1/microblock/{hash}` | Same. |
| `GET /extended/v1/microblock/unanchored/txs` | Same. |
| `GET /extended/v1/tokens/nft/mints` | Mint events for an asset class. Retired rather than migrated — the endpoint serves negligible traffic. A single instance's mint is the oldest entry in `GET /extended/v3/tokens/nft/{asset_identifier}/{value}/history`, but there is no collection-wide mint feed. |
| `GET /extended/v1/faucets/btc/{address}` | Testnet-only BTC balance helper. No replacement. |

Expand Down
5 changes: 1 addition & 4 deletions content/docs/en/apis/stacks-blockchain-api/websockets.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Instead of polling REST endpoints, you can subscribe to blockchain events and ha
- **WebSockets**: A standard WebSocket connection speaking JSON-RPC 2.0, available at the `/extended/v1/ws` path.
- **Socket.IO**: A [Socket.IO](https://socket.io/) server on the API's root URL, which adds automatic reconnection and fallback transports on top of WebSockets.

Both channels deliver the same events: new blocks, microblocks, mempool transactions, transaction status updates, address activity, and NFT events.
Both channels deliver the same events: new blocks, mempool transactions, transaction status updates, address activity, and NFT events.

| Network | WebSockets | Socket.IO |
|---------|------------|-----------|
Expand Down Expand Up @@ -73,7 +73,6 @@ Both clients cover the same events:
| Event | WebSocket client | Socket.IO client |
|-------|------------------|------------------|
| Blocks | `subscribeBlocks(handler)` | `subscribeBlocks(handler)` |
| Microblocks | `subscribeMicroblocks(handler)` | `subscribeMicroblocks(handler)` |
| Mempool transactions | `subscribeMempool(handler)` | `subscribeMempool(handler)` |
| Transaction updates | `subscribeTxUpdates(txId, handler)` | `subscribeTransaction(txId, handler)` |
| Address transactions | `subscribeAddressTransactions(address, handler)` | `subscribeAddressTransactions(address, handler)` |
Expand All @@ -93,7 +92,6 @@ Open a WebSocket to `/extended/v1/ws` and send JSON-RPC 2.0 messages with the `s
| `event` | Additional params |
|---------|-------------------|
| `block` | — |
| `microblock` | — |
| `mempool` | — |
| `tx_update` | `tx_id` |
| `address_tx_update` | `address` |
Expand Down Expand Up @@ -144,7 +142,6 @@ Connect any standard Socket.IO client to the API's root URL. Subscriptions are m
| Topic | Description |
|-------|-------------|
| `block` | New blocks |
| `microblock` | New microblocks |
| `mempool` | New mempool transactions |
| `transaction:{txId}` | Updates for a specific transaction |
| `address-transaction:{address}` | Transactions involving an address |
Expand Down
Loading