VDM NexusDocsBeta

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/inference responses under the receipt key. Settlement happens against an operator-credit balance.
  • x402 — returned in the X-Nexus-Receipt response 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:

  • upstream on the x402 variant names the provider service (e.g. "openrouter").
  • provider on 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:

  • Prepaidprompt_hash = sha256(prompt), response_hash = sha256(result) where both are raw strings.
  • x402prompt_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/x402 exports verifyReceipt; 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.

On this page