Receipt structure
Field-by-field walkthrough of what's in a Nexus receipt.
The authoritative specification is at /docs/spec/sir-v2. This page is a friendly walkthrough — when prose conflicts with the spec, the spec wins.
A receipt is JSON. Two variants exist, depending on how the call was paid for:
- Prepaid — returned in the body of
/v1/inferenceresponses under thereceiptkey. Settlement happens against an operator-credit balance. - x402 — returned in the
X-Nexus-Receiptresponse header (base64-encoded) on/v1/chat/completions. Settlement happens per-call on-chain.
Both variants share a common core; each adds a few variant-specific
fields. The discriminator is the presence of the payment field.
Common fields (every v=2 receipt)
type SirCommon = {
v: 2; // wire format version
agent_pubkey: string; // base58 Ed25519 (= Solana wallet address)
model: string; // model the operator routed to
cost_usdc: number; // upstream USD cost paid by the operator
prompt_hash: string; // sha256 hex of the prompt — see "Hashes" below
response_hash: string; // sha256 hex of the response text
timestamp: number; // server wall-clock ms (Date.now())
inference_id: number | null; // operator-side row id (Postgres bigint)
points_total: number; // cumulative points the operator attributes to this agent
nexus_signature: string; // base58 Ed25519 sig by the operator key
};Prepaid variant — /v1/inference
Adds:
type SirPrepaid = SirCommon & {
provider: string; // routing tag, e.g. "openrouter:fast"
balance_remaining: number; // operator-credit balance after this debit (USD)
};No payment field. Verifier checks 4 (payment_on_chain_ok) and 5
(payer_matches) are vacuously true on prepaid receipts.
x402 variant — /v1/chat/completions
Adds:
type SirX402 = SirCommon & {
upstream: string; // inference provider, e.g. "openrouter"
payment: {
scheme: "x402";
amount_usdc: number; // what the agent paid (the flat fee today)
tx_signature: string; // Solana tx signature, base58
network: string; // CAIP-2 genesis-hash form
pay_to: string; // recipient address that received the USDC
};
};Field details
v
Wire format version. Always 2 on signed receipts. Verifiers MUST
reject any other value (see spec §19).
agent_pubkey
The agent's Ed25519 public key in base58. Identical to the Solana wallet address — same key format, same encoding. This is the identity Nexus attributes the call to, regardless of which IP, region, or client library made the request.
upstream (x402) / provider (prepaid)
Both fields name "where the inference came from," but at different abstraction levels:
upstreamon the x402 variant names the provider service (e.g."openrouter").provideron the prepaid variant names the routing decision (e.g."openrouter:fast"— the routing tier the operator picked).
A future revision may unify these; for v=2 they're variant-specific.
model
The model identifier as routed by the operator. For Nexus today this
is the OpenRouter model slug (e.g. openai/gpt-4o-mini,
anthropic/claude-3-haiku).
cost_usdc
The actual cost the operator paid upstream, not what the agent
paid. For x402 calls the agent paid payment.amount_usdc (a flat fee
today). The difference is the operator's spread.
This field is signed (so an operator can't claim a different cost post-hoc), but it is NOT independently verifiable — the verifier has no way to check what the operator actually paid OpenRouter. Treat it as the operator's own assertion. See spec §14.
balance_remaining (prepaid only)
The agent's operator-credit balance after this debit, in USD. Lets clients display a running balance without an extra round-trip.
prompt_hash and response_hash
Hex SHA-256 digests (64 chars). The input to each hash depends on the variant:
- Prepaid —
prompt_hash = sha256(prompt),response_hash = sha256(result)where both are raw strings. - x402 —
prompt_hash = sha256(canonical-JSON(messages)),response_hash = sha256(choices[0].message.content). Receipts signed before 2026-05-23 used a delimited-string form; the reference verifier accepts both.
If the model produces multiple choices, only choices[0] is hashed.
Streaming responses are out of scope for v=2.
timestamp
Server wall-clock Date.now() at the moment the receipt was built. For
paid receipts, the authoritative settlement time is the on-chain
blockTime of payment.tx_signature — this field is just the
sub-second-precision operator timestamp.
inference_id
Integer row id of the operator-side log entry (Postgres bigint). Useful
for support: DM us with inference_id and we can look up what
happened on our side. May be null if the log row failed to write.
points_total
Cumulative points the operator has attributed to this agent_pubkey,
including this call. Signed (can't be silently mutated) but not
cryptographically verifiable against any external state. See
spec §14 — verifiers MUST NOT treat this as a
proof, only as the operator's own record.
payment.tx_signature
Solana transaction signature (base58). Look it up on
Solscan devnet to see the
on-chain settlement. The combination of tx_signature + pay_to +
amount_usdc is the verifier's anchor for did the payment happen?
payment.pay_to
Recipient address that received the USDC. verifyReceipt walks the
transaction's pre/postTokenBalances and confirms a USDC delta of at
least amount_usdc to this owner.
payment.network
CAIP-2 network identifier. Conformant operators MUST use the genesis-hash form:
- Mainnet:
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp - Devnet:
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1aFoKMcMZ9YTs
Short forms like solana:devnet are NOT spec-compliant — tolerant
verifiers may accept them, but operators must not emit them.
nexus_signature
Base58-encoded Ed25519 signature by the operator key, over the
canonical JSON of the receipt with nexus_signature excluded:
- Object keys sorted recursively
- No whitespace
- Primitives via standard
JSON.stringify - Arrays preserve order
- UTF-8 encoded before signing
Verifiers strip nexus_signature, canonicalize the rest, and call
nacl.sign.detached.verify against the operator's public key
(GET /api/v1/operator-key). See spec §8, §9
for the canonical algorithm.
Shipped in v0.2
- Receipt signing. Receipts now carry
nexus_signature, allowing a third party to verify they were issued by Nexus without trusting the agent that delivered them. - Bundled verifier.
@vdm-nexus/x402exportsverifyReceipt; see Verify a receipt. - Open spec. The wire format is now a public specification at /docs/spec/sir-v2. Nexus is the reference implementation; the format is anyone's to issue.
Still tracked
- Streaming receipt continuation. Multi-chunk streamed responses need a multi-segment receipt that ties the final hash to each chunk.
- Key-rotation manifest. Today the operator key endpoint returns a single current pubkey; a multi-key manifest would let verifiers accept receipts signed by recently-rotated keys.