Node API
The node serves JSON-RPC 2.0 on one listener, bound to 127.0.0.1 port 3413 by
default, split across two surfaces.
| Surface | Path | Serves |
|---|---|---|
| Owner | /v2/owner | Node administration, eight methods |
| Foreign | /v2/foreign | Chain reads, transaction submission, block templates, seventeen methods |
Credentials, and which of these expects one, are covered once in authentication and TLS.
The same listener also serves a REST surface under /v1. See
the v1 REST surface.
Which JSON-RPC method lives where
/v2/owner is node administration only:
get_status, validate_chain, compact_chain, get_peers, get_connected_peers, ban_peer,
unban_peer, get_onion_addresses.
/v2/foreign is everything else, including every chain read and transaction submission:
get_header, get_block, get_blocks, get_tip, get_version, get_kernel,
get_last_n_kernels, get_outputs, get_unspent_outputs, get_pmmr_indices, get_pool_size,
get_stempool_size, get_unconfirmed_transactions, push_transaction, get_block_template,
finalize_block_template, submit_block.
So reading a block is a foreign-surface call, and it takes no credential on a default install:
curl -s -d '{"jsonrpc":"2.0","method":"get_tip","params":[],"id":1}' \
http://127.0.0.1:3413/v2/foreign
Checking sync state is an owner-surface call, and needs the node credential.
Response shapes
total_difficulty is an object keyed by proof-of-work algorithm, not an integer:
{
"total_difficulty": {
"cuckaroo": 0,
"cuckatoo": 1234,
"randomx": 5678,
"progpow": 9012
}
}
Block headers describe their proof with two fields: proof, a string naming the algorithm, and
solution, a tagged union whose shape follows that algorithm.
Status includes supply and max_supply in whole EPIC rather than freemen, and
blocks_to_next_halving as a block count. Reward steps are not all halvings; see
emission.
Kernels appear in two shapes depending on the method. get_block returns a flattened form, while
get_kernel and get_last_n_kernels return the raw type with an externally tagged features field.
Handle both.
Mining over HTTP
The foreign surface offers a three-step block submission flow as an alternative to Stratum:
get_block_template, finalize_block_template, submit_block. It is JSON-RPC over HTTP. See
Stratum.
The v1 REST surface
The node serves REST under /v1 and JSON-RPC under /v2, from the same process on the same port.
The numbers name two interface styles, not two generations: /v1 is registered at
api/src/handlers.rs:536, /v2 at
api/src/handlers.rs:135 and
api/src/handlers.rs:155.
The difference is where the operation is named.
/v1 | /v2 | |
|---|---|---|
| Style | REST | JSON-RPC 2.0 |
| Operation named by | the URL path | the method field in the body |
| HTTP verb | GET, POST to mutate | POST |
| URLs | one per operation | /v2/foreign, /v2/owner, /v2/tor |
| Result | the payload | the payload inside result.Ok |
| Credential | Basic auth across the /v1 prefix | per surface |
Both surfaces return the same payloads. GET /v1/status and get_status on /v2/owner produce
identical bodies, so the JSON-RPC pages document the response shapes for
both.
GET /, GET /v1/ and GET /v2/ return a list of v1 routes, from
api/src/handlers.rs:446.
Routes
Verbs are as the node's route index reports them. The right-hand column is the JSON-RPC method that returns the same thing, and its surface.
Every v1 route and its JSON-RPC equivalent
The router registers 19 patterns, three of them wildcards.
| Route | Verb | JSON-RPC equivalent |
|---|---|---|
/v1/ | GET | none, this is the route index |
/v1/status | GET | get_status, owner |
/v1/chain | GET | get_tip, foreign |
/v1/chain/validate | GET | validate_chain, owner |
/v1/chain/compact | POST | compact_chain, owner |
/v1/blocks/<hash|height> | GET | get_block, foreign |
/v1/headers/<hash|height|commit> | GET | get_header, foreign |
/v1/chain/kernels/<excess>?min_height=&max_height= | GET | get_kernel, foreign |
/v1/chain/outputs/byids?id= | GET | get_outputs, foreign |
/v1/chain/outputs/byheight?start_height=&end_height= | GET | get_outputs, foreign |
/v1/txhashset/roots | GET | none |
/v1/txhashset/lastoutputs?n= | GET | none |
/v1/txhashset/lastrangeproofs | GET | none |
/v1/txhashset/lastkernels | GET | get_last_n_kernels, foreign |
/v1/txhashset/outputs?start_index=&max= | GET | get_unspent_outputs, foreign |
/v1/txhashset/merkleproof?n= | GET | include_merkle_proof on get_outputs, foreign |
/v1/pool | GET | get_pool_size and get_stempool_size, foreign |
/v1/pool/push_tx | POST | push_transaction, foreign |
/v1/peers/all | GET | get_peers, owner |
/v1/peers/connected | GET | get_connected_peers, owner |
/v1/peers/<addr> | GET | get_peers with a peer_addr, owner |
/v1/peers/<addr>/ban | POST | ban_peer, owner |
/v1/peers/<addr>/unban | POST | unban_peer, owner |
/v1/peers/onion_addresses | GET | get_onion_addresses, owner |
/v1/version | GET | get_version, foreign |
/v1/mining/block_template | GET | get_block_template, foreign |
/v1/kerneldownload | GET | none |
A /v1 call, which takes the node credential across the whole prefix:
curl -s -u epic:$(cat ~/.epic/main/.api_secret) http://127.0.0.1:3413/v1/status
{
"protocol_version": 2,
"user_agent": "MW/Epic 4.0.3",
"connections": 0,
"tip": {
"height": 1439,
"last_block_pushed": "9abbd80483f9b082a9362ebac0aba5619bdba7cf647cb15f5ed33102faf3cd1b",
"prev_block_to_last": "6ceed7ff0ff7da1d44b01dc03189edeb1323bfa6d3625768520dd1a0498a0735",
"total_difficulty": {"cuckaroo": 1440, "cuckatoo": 1440, "randomx": 1440, "progpow": 1440}
},
"sync_status": "no_sync",
"supply": 23024,
"max_supply": 21000000,
"blocks_to_next_halving": 479521
}
Captured on a local usernet chain at height 1439 on 2026-08-24.
Full method reference
Ten methods. Blocks, headers, kernels, outputs and the tip.
Eight methods. Status, peers, validation and compaction.
Four methods, one of them irreversible.
Three methods. Mining over HTTP instead of Stratum.
Source
api/src/owner_rpc.rs:29defines the eight owner methodsapi/src/foreign_rpc.rs:37defines the foreign methodsapi/src/types.rsdefines every response shapeapi/src/auth.rsis the Basic auth middleware