Skip to content

REST API

Base path: <artifact-api-prefix>/app-chain, by default /api/v1/app-chain.

The prefix is fixed into each JVM, native, or container artifact at build time with -PyanoApiPrefix=<path>. It is not editable launch configuration, and changing it requires a rebuild.

Yano’s API calls an app ledger an app chain, so the paths say app-chain.

Every app ledger endpoint is chain-scoped:

/api/v1/app-chain/chains/{chainId}/...

GET /api/v1/app-chain/chains lists the hosted chains with their tip height and state root.

Some endpoints also answer without the chains/{chainId} segment, but only while exactly one chain is configured. With several chains those aliases return 400, and with none they return 503. They are hidden from the OpenAPI document, and these endpoints have no alias at all: query, identity, admin/members*, admin/threshold, proof-subjects/*, snapshots/*, admin/snapshots/*, and observations/*. Use chains/{chainId} in every integration. Swagger UI at /q/swagger-ui/ documents the chain-scoped surface.

Each endpoint has one access level:

LevelWho may call it
ReadAnyone, unless yano.app-chain.api.auth.enabled=true; then any configured key. Some reads use POST because they carry parameters.
SubmitLike a read, except a topic-restricted key (key=topicA|topicB) may submit only to its topics.
PrivilegedOnly a full API key from yano.app-chain.api.keys, even when broad authentication is off. Without a configured key these calls fail closed.
Snapshot adminOnly a key from yano.app-chain.api.snapshot-admin-keys.

Send the key in the X-API-Key header. An unannotated GET is a read and any other unannotated method is privileged, so a new endpoint is never public by accident.

The REST API by accessConcept
READSUBMITSNAPSHOT ADMIN KEYPRIVILEGED · FULL KEYPLUGINSMessagesby id or topicBlocks and statustip, roots, blocksProofsstate and typedStreamserver-sent eventsEffect recordsemitted effectsSnapshotsperiod datasetsQueryread hooksPOST messagestopic and bodySnapshot lifecyclearchive, restore, evictAdminmembers, pauseIdentitydrift and integrityEffect workersclaim and reportObservationsreports and wakeLedger snapshotfor onboardingDomain routesdeclared per routePlugin operationscatalog and statusREADSUBMITSNAPSHOT ADMIN KEYPRIVILEGED · FULL KEYPLUGINSMessagesby id or topicBlocks, statustip, rootsProofsstate and typedStreamserver-sent eventsEffect recordsemitted effectsSnapshotsperiod datasetsQueryread hooksPOST messagestopic and bodySnapshot lifecyclearchive, restore, evictAdminmembers, pauseIdentitydrift and integrityEffect workersclaim and reportObservationsreports and wakeLedger snapshotfor onboardingDomain routesdeclared per routePlugin operationscatalog and status
  • Read: Messages, Blocks and status, Proofs, Stream, Effect records, Snapshots, Query.
  • Submit: POST messages.
  • Snapshot admin key: Snapshot lifecycle.
  • Privileged · full key: Admin, Identity, Effect workers, Observations, Ledger snapshot.
  • Plugins: Domain routes, Plugin operations.
Messages
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET messages?limit=&topic= lists recently accepted messages, local and gossiped. GET messages/{id} returns one finalized message with its height and index. GET messages/by-topic/{topic} and GET messages/by-sender/{senderHex} list finalized references, ascending, with fromHeight and limit.
Blocks and status
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET status, GET tip, GET blocks?from=&limit= (summaries with certSignatures), GET blocks/{height} (one block with its messages), GET state/identity, GET state/oldest-provable, and GET anchor/commitment. GET /api/v1/app-chain/chains lists the hosted chains.
Proofs
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET state/proof/{keyHex} and GET state/entry/{keyHex} (add ?height= for a retained height), GET messages/{id}/proof, GET messages/{id}/proof-package, GET evidence/{id}, GET proof-subjects, POST proof-subjects/{subjectId}/proof, POST proof-subjects/{subjectId}/package, and POST proof/verify. The POST forms are reads: the body only carries parameters.
Stream
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET stream?fromHeight=&topic= streams finalized messages, replayed from fromHeight, then live.
Effect records
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET effects?fromHeight=&limit=, GET effects/{height}/{ordinal}, GET effects/{height}/{ordinal}/proof, and GET effects/stats.
Snapshots
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET snapshots, GET snapshots/{series}/{sequence}, POST snapshots/{series}/{sequence}/proof, POST snapshots/proof/verify, and GET snapshots/status: authenticated snapshots and their proofs.
Query
Paths are relative to /api/v1/app-chain/chains/{chainId}/. POST query/{path} runs the state machine's read hook against one committed snapshot. It is a POST, but a read: no state changes.
POST messages
Paths are relative to /api/v1/app-chain/chains/{chainId}/. POST messages with {topic, body} or {topic, bodyHex}. The receiving node signs the envelope and answers 202 with a messageId; 400 is an application rejection, 429 a full pool, and 503 a node that cannot accept. A topic-restricted key may submit only to its topics.
Snapshot lifecycle
Paths are relative to /api/v1/app-chain/chains/{chainId}/. POST admin/snapshots/{series}/{sequence}/archive, …/restore, and …/evict, plus GET admin/snapshots/jobs and GET admin/snapshots/jobs/{jobId}. They need a key from yano.app-chain.api.snapshot-admin-keys.
Admin
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET admin/members; POST admin/members/add, …/remove, and …/reset; POST admin/threshold; POST admin/pause, admin/resume, admin/drain-pool, admin/force-anchor, and admin/anchor/bootstrap.
Identity
Paths are relative to /api/v1/app-chain/chains/{chainId}/. GET identity returns the redacted identity digests that appchain drift compares. GET state/integrity runs the node's state integrity check.
Effect workers
Paths are relative to /api/v1/app-chain/chains/{chainId}/. POST effects/claim, POST effects/{height}/{ordinal}/report, …/requeue, and …/cancel. These can move real funds, so a topic-restricted key may never call them.
Observations
Paths are relative to /api/v1/app-chain/chains/{chainId}/. POST observations/reports and POST observations/wake.
Ledger snapshot
Paths are relative to /api/v1/app-chain/chains/{chainId}/. POST snapshot with {"path": …} writes an atomic ledger snapshot to a fresh directory on the node.
Domain routes
GET or POST /api/v1/plugins/{bundleId}/{path}. Each route declares whether it is a read or privileged; an undeclared or internal route answers 404. A POST body may hold at most 65,536 bytes, and errors are {code, error}.
Plugin operations
GET /api/v1/plugin-operations, GET /api/v1/plugin-operations/bundles, and GET /api/v1/plugin-operations/bundles/{bundleId}. Always privileged.
Reads and submissions are public unless broad API-key authentication is on; then they need a key, and a topic-restricted key may submit only to its topics. Privileged calls always need a full API key, and snapshot lifecycle calls need a key from their own realm.
  • read
  • submit
  • privileged
  • snapshot admin
  • plugin routes

All paths below are relative to /api/v1/app-chain/chains/{chainId}.

Method and pathAccessPurpose
GET /statusReadRole, tip height, state root, pool size, peer connectivity, counters, anchor and sink progress.
GET /tipRead{chainId, height, stateRoot} of the last finalized block.
GET /blocks/{height}ReadHashes, roots, proposer, certificate signature count, and the full message list.
GET /blocks?from=&limit=ReadBlock summaries, ascending, with messageCount and certSignatures. Defaults to a window ending at the tip.
GET /state/identityReadThe genesis-selected commitment profile and the current root.
GET /state/oldest-provableReadThe oldest height that still has proofs.
GET /anchor/commitmentReadThe anchor commitment for this chain.
Method and pathAccessPurpose
POST /messagesSubmitBody {"topic":"...","body":"<text>"} or {"topic":"...","bodyHex":"<hex>"}. Returns 202 with the content-derived messageId.
GET /messages?limit=100&topic=...ReadRecently accepted messages, local and gossiped, with sender, sequence, body hex, and source.
GET /messages/{messageIdHex}ReadOne finalized message: position (height, index) plus full content. 404 until it is final.
GET /messages/by-topic/{topic}?fromHeight=&limit=ReadFinalized message references on a topic, ascending.
GET /messages/by-sender/{senderHex}?fromHeight=&limit=ReadFinalized message references from a member key, ascending.
GET /stream?fromHeight=&topic=ReadServer-sent events of finalized messages: replay from fromHeight, then live.
Method and pathAccessPurpose
GET /state/proof/{keyHex}ReadProfile-tagged inclusion or exclusion proof for a canonical state key at the tip or a retained ?height=. OrderedLog derives its key from a namespace and message id; use the finalized-message-v1 typed proof subject to avoid constructing it yourself.
GET /state/entry/{keyHex}ReadThe same lookup without proof bytes.
GET /proof-subjectsReadThe typed proof subjects this chain offers.
POST /proof-subjects/{subjectId}/proof, .../packageReadTyped proof in application language. See State and proofs.
GET /messages/{messageIdHex}/proof, .../proof-packageReadA proof that a finalized message is recorded.
GET /evidence/{messageIdHex}ReadA portable, offline-verifiable evidence bundle for a finalized message.
POST /proof/verifyReadVerify a proof on the node.
POST /query/{path}ReadRun the state machine’s read hook against one committed snapshot.
Method and pathAccessPurpose
GET /effects?fromHeight=&limit=ReadEmitted effect records, ascending.
GET /effects/{height}/{ordinal}, .../proofReadOne effect record with this node’s runtime status, and its proof.
GET /effects/statsReadEffect counters.
POST /effects/claimPrivilegedAn external worker claims effects to execute.
POST /effects/{height}/{ordinal}/reportPrivilegedAn external worker reports a result.
POST /effects/{height}/{ordinal}/requeue, .../cancelPrivilegedOperator controls for one effect.
Method and pathAccessPurpose
GET /snapshots?series=&cursor=&limit=, GET /snapshots/{series}/{sequence}, GET /snapshots/statusReadSnapshot descriptors and status.
POST /snapshots/{series}/{sequence}/proof, POST /snapshots/proof/verifyReadProve and verify entries of a snapshot.
POST /admin/snapshots/{series}/{sequence}/{archive|restore|evict}Snapshot adminStart a lifecycle job.
GET /admin/snapshots/jobs, GET /admin/snapshots/jobs/{jobId}Snapshot adminLifecycle job status.
Method and pathAccessPurpose
GET /identityPrivilegedRedacted identity digests that appchain drift compares.
GET /state/integrityPrivilegedThe node’s state integrity check.
POST /snapshotPrivilegedAtomic ledger snapshot for member onboarding. Body {"path":"<fresh dir>"}.
POST /admin/pause, POST /admin/resumePrivilegedPause or resume local submissions.
POST /admin/drain-poolPrivilegedDrop all pending, unfinalized messages.
POST /admin/force-anchorPrivilegedAnchor the current tip now.
POST /admin/anchor/bootstrapPrivilegedOne-time script-anchor bootstrap.
GET /admin/membersPrivilegedEffective member set and threshold.
POST /admin/members/add, .../removePrivilegedStage a member key in or out. Body {"publicKey":"..."}.
POST /admin/members/resetPrivilegedDrop the persisted member override and return to the configured list.
POST /admin/thresholdPrivilegedSet the finality threshold. Body {"threshold": N}.
POST /observations/reports, POST /observations/wakePrivilegedObservation reports and wake-ups.

Plugins contribute domain routes below /api/v1/plugins/{bundleId}/. A route accepts GET or POST and declares its own access, read or privileged; a route that is undeclared or internal answers 404. A POST body may hold at most 65,536 bytes, and errors are {"code": ..., "error": ...}. Plugin catalog and lifecycle status is under /api/v1/plugin-operations and is always privileged.

The node signs with its own member key. The REST caller is trusted local input — the same model as a wallet talking to its own node. This is why the API must not be exposed to untrusted callers without authentication.

The body is opaque. Use body for UTF-8 text or bodyHex for arbitrary bytes. The framework never parses it; only the state machine does.

ResponseMeaning
202 with messageIdAccepted into this node’s pending pool. Not final yet.
400 with codeThe application rejected the command at admission.
429The pending pool is full. Back off and retry.
503The node cannot accept submissions right now.
LimitDefault
max-message-bytes64 KB
default-ttl-seconds600 — an unfinalized message expires out of the pool
pool.max-messages10,000

Topics starting with ~ are reserved for consensus and system traffic.

When the pending pool is full, POST /messages returns 429 and the message is neither stored nor relayed. Back off and retry.

Inbound gossip dropped by a full pool is counted in GET /status under drops.pool_full. That is not an error — the sender’s own node already holds the message.

Every envelope carries a per-sender sequence number. A message whose seq is at or below the sender’s last finalized seq is a replay and is rejected at admission on every ledger node, counted as drops.stale_seq.

Gaps are allowed and meaningless — seqs are wall-clock seeded, so a restart never reuses one. The seq does not define ordering; the sequencer does.

yano.app-chain.message.enforce-sender-seq: true

With that flag the rule becomes consensus-visible: followers reject any block whose per-sender seqs are not strictly increasing above the finalized floor. It defaults to off for compatibility, and — like the state-machine id — all members must agree on it.

Prefer a typed client over raw HTTP where one exists:

ArtifactWhat it adds
yano-x-clientREST, SSE, and client-side proof verification.
yano-x-composite-clientGoverned-profile finality, one-root MPF, epoch-chain, and authorization-policy verification.
yano-x-spring-boot-starterSpring Boot auto-configuration for the client SDK.
yano-appchain-core-testkitJUnit 5 @AppChainCluster embedded clusters.

The exhaustive API, configuration, security, and operations reference is section 4 of the app ledger user guide. The host’s resource class is the final word on paths and access: AppChainResource.java.