A Mesh API (formerly Rosetta API) implementation for the Stacks blockchain. It provides a standardized set of endpoints for reading blockchain data, constructing transactions, and interacting with smart contracts, all through a single JSON-RPC interface that communicates directly with a Stacks node.
- Network status -- query blockchain identity, sync status, connected peers, and supported operation types
- Block and transaction data -- retrieve full Nakamoto blocks with decoded operations (transfers, contract calls, PoX events, etc.)
- Account balances -- look up STX balances, nonces, and stacking lock status (including historical queries)
- Transaction construction -- offline-compatible flow for building, signing, and broadcasting STX transfers, contract calls, and contract deployments
- Smart contract reads -- call read-only functions, inspect ABIs, read source code, and query data vars and map entries
- Mesh specification v1.5.1 compliant
- Only Nakamoto blocks (Stacks 3.x+) are supported. This API targets the Nakamoto consensus rules and does not handle legacy pre-Nakamoto block formats. Running it against a node that has not activated Nakamoto will produce errors or incomplete data.
- Node.js >= 24
- A running Stacks node with the RPC endpoint accessible
The Stacks node does not need any special configuration. A regular chain follower is
sufficient. However, the node must have an auth_token configured under
[connection_options] in its Stacks.toml config file:
[connection_options]
auth_token = "some-secret-token"This token must match the STACKS_CORE_RPC_AUTH_TOKEN environment variable passed to the Mesh
API (see below).
The API is configured via environment variables (a .env file is also supported):
| Variable | Default | Description |
|---|---|---|
MODE |
online |
online connects to a node and serves the full API. offline serves only the node-less endpoints (see Offline mode) |
API_HOST |
0.0.0.0 |
Address the HTTP server binds to |
API_PORT |
3000 |
Port the HTTP server listens on |
STACKS_CORE_RPC_HOST |
(required online) | Hostname of the Stacks node RPC |
STACKS_CORE_RPC_PORT |
20443 |
Port of the Stacks node RPC |
STACKS_CORE_RPC_AUTH_TOKEN |
(required online) | Auth token for the Stacks node RPC |
STACKS_CORE_RPC_TIMEOUT_MS |
10000 |
RPC request timeout in milliseconds |
STACKS_CHAIN_ID |
(required offline) | Target chain ID, used for transaction signing. Offline only; online reads it from the node's /v2/info |
BLOCK_HASH_MODE |
index_block_hash |
Which block hash to display in responses. index_block_hash (canonical Stacks block id) or block_hash (see Block hash mode) |
TOKEN_METADATA_CACHE_SIZE |
1000 |
Max entries in the token metadata LRU cache |
TOKEN_METADATA_CACHE_TTL_MS |
86400000 |
Token metadata cache TTL (default 24 h) |
TOKEN_METADATA_ERROR_CACHE_TTL_MS |
7200000 |
TTL for negatively-cached token metadata lookups — tokens whose SIP-010 getters failed (default 2 hours) |
CONTRACT_ABI_CACHE_SIZE |
100 |
Max entries in the contract ABI LRU cache |
CONTRACT_ABI_CACHE_TTL_MS |
86400000 |
Contract ABI cache TTL (default 24 h) |
CONSTRUCTION_DEFAULT_FEE |
200 |
Default/fallback suggested fee (µSTX) and floor for /construction/metadata estimates |
Set MODE=offline to run a node-less instance for air-gapped transaction construction and signing. It makes no outbound calls and needs no Stacks node — only STACKS_CHAIN_ID (so signatures target the right chain; any non-mainnet value uses testnet address format).
Offline mode serves only the endpoints that need no chain state:
/construction/derive,/construction/preprocess,/construction/payloads,/construction/combine,/construction/parse,/construction/hash/network/list,/network/options
The node-backed endpoints — /construction/metadata, /construction/submit, /network/status, and the Data API (/account/*, /block/*, /mempool/*, /call) — are not registered and return 404. Run a separate online instance for those, per the standard Mesh online/offline split.
Note:
/construction/parseof acontract_callomits argument names in offline mode (labeling them requires the contract ABI from a node); the operation and its raw args are still returned.
MODE=offline
STACKS_CHAIN_ID=2147483648By default the API displays the index_block_hash (the canonical Stacks block identifier, a.k.a. block_id) wherever a block hash appears in a response (/block, /account/balance, /network/status). Set BLOCK_HASH_MODE=block_hash to display the Stacks block_hash instead, for backwards compatibility with systems that key on it.
The node itself always operates on the index_block_hash, so this only changes what is displayed — internal lookups (including the account-balance tip) still use the index_block_hash. One consequence for input: because the node cannot resolve a bare block_hash, in block_hash mode a point lookup by hash (/block, /account/balance) must also include the block index. Requests that supply both are validated (the hash must match the block at that index); the API's own responses always include both index and hash, so echoing an identifier back always works.
# Install dependencies
npm ci
# Build all packages
npm run build
# Start the API (production)
npm start
# Or start in watch mode (development)
npm run devCreate a .env file in packages/api/ (see packages/api/.env.example):
STACKS_CORE_RPC_HOST=localhost
STACKS_CORE_RPC_PORT=20443
STACKS_CORE_RPC_AUTH_TOKEN=your-auth-tokendocker build -t stacks-mesh-api .
docker run -p 3000:3000 \
-e STACKS_CORE_RPC_HOST=host.docker.internal \
-e STACKS_CORE_RPC_PORT=20443 \
-e STACKS_CORE_RPC_AUTH_TOKEN=your-auth-token \
stacks-mesh-apiThe API will be available at http://localhost:3000.
All endpoints accept POST requests with a JSON body. Every request must include a
network_identifier:
{
"network_identifier": { "blockchain": "stacks", "network": "mainnet" }
}| Endpoint | Description |
|---|---|
/network/list |
List supported networks |
/network/status |
Current block, sync status, and peers |
/network/options |
Mesh spec version, supported operations, and errors |
| Endpoint | Description |
|---|---|
/block |
Get a block by hash or height |
/block/transaction |
Get a specific transaction within a block |
/account/balance |
Get STX balance, nonce, and lock info for an address |
| Endpoint | Description |
|---|---|
/construction/derive |
Derive an address from a public key |
/construction/preprocess |
Parse operations into construction options |
/construction/metadata |
Fetch nonce, balance, and suggested fee |
/construction/payloads |
Build unsigned transaction and signing payloads |
/construction/combine |
Attach signatures to an unsigned transaction |
/construction/parse |
Decode a signed or unsigned transaction into operations |
/construction/hash |
Compute the hash of a signed transaction |
/construction/submit |
Broadcast a signed transaction |
| Endpoint | Description |
|---|---|
/call |
Read-only contract calls and contract metadata queries |
Transaction construction follows the standard Mesh offline flow:
preprocess ──> metadata ──> payloads ──> [sign offline] ──> combine ──> submit
The API supports three transaction types: STX token transfers, contract calls, and contract deployments.
Declare the transfer intent as a pair of operations (sender debits, recipient credits):
curl -s http://localhost:3000/construction/preprocess -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "testnet" },
"operations": [
{
"operation_identifier": { "index": 0 },
"type": "token_transfer",
"account": { "address": "STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6" },
"amount": { "value": "-1000000", "currency": { "symbol": "STX", "decimals": 6 } },
"metadata": { "memo": "hello" }
},
{
"operation_identifier": { "index": 1 },
"type": "token_transfer",
"account": { "address": "ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y" },
"amount": { "value": "1000000", "currency": { "symbol": "STX", "decimals": 6 } },
"metadata": { "memo": "hello" }
}
]
}'Returns options (construction parameters) and required_public_keys.
Fetch the sender's nonce, balance, and a suggested fee:
curl -s http://localhost:3000/construction/metadata -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "testnet" },
"options": { "...options from preprocess..." },
"public_keys": [
{ "hex_bytes": "YOUR_PUBLIC_KEY_HEX", "curve_type": "secp256k1" }
]
}'Returns metadata and suggested_fee.
Build the unsigned transaction. Include a fee operation (negative amount from the sender) plus
the original transfer operations:
curl -s http://localhost:3000/construction/payloads -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "testnet" },
"operations": [
{
"operation_identifier": { "index": 0 },
"type": "fee",
"account": { "address": "STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6" },
"amount": { "value": "-200", "currency": { "symbol": "STX", "decimals": 6 } }
},
{
"operation_identifier": { "index": 1 },
"type": "token_transfer",
"account": { "address": "STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6" },
"amount": { "value": "-1000000", "currency": { "symbol": "STX", "decimals": 6 } },
"metadata": { "memo": "hello" }
},
{
"operation_identifier": { "index": 2 },
"type": "token_transfer",
"account": { "address": "ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y" },
"amount": { "value": "1000000", "currency": { "symbol": "STX", "decimals": 6 } },
"metadata": { "memo": "hello" }
}
],
"metadata": { "...metadata from previous step..." },
"public_keys": [
{ "hex_bytes": "YOUR_PUBLIC_KEY_HEX", "curve_type": "secp256k1" }
]
}'Returns unsigned_transaction (hex) and payloads (the signing payload with ecdsa_recovery
signature type).
Sign the payload hex bytes from the previous step using your private key (secp256k1, recoverable ECDSA). This step happens entirely offline.
Attach the signature to the unsigned transaction:
curl -s http://localhost:3000/construction/combine -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "testnet" },
"unsigned_transaction": "UNSIGNED_TX_HEX",
"signatures": [
{
"signing_payload": { "...payload from step 3..." },
"public_key": { "hex_bytes": "YOUR_PUBLIC_KEY_HEX", "curve_type": "secp256k1" },
"signature_type": "ecdsa_recovery",
"hex_bytes": "SIGNATURE_HEX"
}
]
}'Returns signed_transaction (hex).
Broadcast the signed transaction to the network:
curl -s http://localhost:3000/construction/submit -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "testnet" },
"signed_transaction": "SIGNED_TX_HEX"
}'Returns transaction_identifier with the transaction hash.
The preprocess step for a contract call uses a single operation:
curl -s http://localhost:3000/construction/preprocess -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "testnet" },
"operations": [
{
"operation_identifier": { "index": 0 },
"type": "contract_call",
"account": { "address": "STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6" },
"metadata": {
"contract_identifier": "ST000000000000000000002AMW42H.pox-4",
"function_name": "stack-stx",
"args": [
"0x0100000000000000000000000005f5e100",
"0x0c00000002096861736862797465730200000014b1c0e25ed2ed6e8b2a09adde1b974bea2c92ec050b76657273696f6e020000000100",
"0x0100000000000000000000000000000064",
"0x010000000000000000000000000000000c"
]
}
}
]
}'Function arguments are hex-encoded Clarity values. The rest of the flow (metadata, payloads, sign, combine, submit) is identical to the token transfer example.
The /call endpoint supports several methods for interacting with smart contracts without
broadcasting a transaction.
curl -s http://localhost:3000/call -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "mainnet" },
"method": "contract_call_read_only",
"parameters": {
"deployer_address": "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
"contract_name": "age000-governance-token",
"function_name": "get-balance",
"sender": "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
"arguments": ["0x0516a654a8dfa3ec004870690aca959868fbc4883402"]
}
}'Returns a decoded Clarity value:
{
"idempotent": false,
"result": {
"hex": "0x0700000000000000000000000005f5e100",
"repr": "(ok u100000000)",
"type": "ResponseOk(uint)"
}
}curl -s http://localhost:3000/call -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "mainnet" },
"method": "contract_get_interface",
"parameters": {
"deployer_address": "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
"contract_name": "age000-governance-token"
}
}'curl -s http://localhost:3000/call -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "mainnet" },
"method": "contract_get_source",
"parameters": {
"deployer_address": "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
"contract_name": "age000-governance-token"
}
}'curl -s http://localhost:3000/call -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "mainnet" },
"method": "contract_get_data_var",
"parameters": {
"deployer_address": "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
"contract_name": "age000-governance-token",
"var_name": "token-uri"
}
}'curl -s http://localhost:3000/call -H 'Content-Type: application/json' -d '{
"network_identifier": { "blockchain": "stacks", "network": "mainnet" },
"method": "contract_get_map_entry",
"parameters": {
"deployer_address": "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
"contract_name": "age000-governance-token",
"map_name": "token-balances",
"key": "0x0516a654a8dfa3ec004870690aca959868fbc4883402"
}
}'| Method | Description |
|---|---|
contract_call_read_only |
Invoke a read-only contract function |
contract_get_interface |
Get the contract ABI (functions, variables, maps, tokens) |
contract_get_source |
Get the contract Clarity source code |
contract_get_constant_val |
Get the value of a defined constant |
contract_get_data_var |
Read a define-data-var variable |
contract_get_map_entry |
Look up a define-map entry by key |
The API recognizes the following operation types when serializing block data:
coinbase, fee, token_transfer, token_mint, token_burn, contract_call,
contract_deploy, tenure_change, poison_microblock, stx_lock, contract_log,
handle-unlock, stack-stx, stack-increase, stack-extend, delegate-stx,
delegate-stack-stx, delegate-stack-increase, delegate-stack-extend,
stack-aggregation-commit, stack-aggregation-commit-indexed, stack-aggregation-increase,
revoke-delegate-stx
# Run API unit/integration tests
npm run test:api -w @stacks/mesh-api
# Run construction end-to-end tests (requires Docker)
npm run test:construction -w @stacks/mesh-apiThe construction tests spin up a Stacks node in Docker and run the full transaction lifecycle against it using the mesh-cli (Rosetta CLI) validator.
MIT