Receipt structure
Field-by-field walkthrough of what's in a Nexus receipt.
A signed-inference receipt is JSON, returned in the X-Nexus-Receipt
response header as base64. Here's every field on a v: 2 receipt:
type NexusReceipt = {
v: 2; // receipt schema version
agent_pubkey: string; // base58 Ed25519 pubkey (= Solana pubkey)
upstream: "openrouter"; // which inference provider was used
model: string; // the slug we passed upstream
cost_usdc: number; // actual upstream cost (USD, ~6 decimals)
prompt_hash: string; // sha256(messages.role:content joined by \n)
response_hash: string; // sha256(first choice's message content)
timestamp: number; // server-set wall-clock ms
inference_id: string | null; // UUID of the inference_logs row
payment: {
scheme: "x402";
amount_usdc: number; // what we charged (the flat fee today)
tx_signature: string; // Solana tx signature
network: string; // CAIP-2, e.g. solana:EtWTRABZ…
pay_to: string; // recipient address of the USDC transfer
};
nexus_signature: string; // base58 Ed25519 signature by Nexus
};Field details
v
Receipt schema version. Always 2 on signed receipts. Verifiers should
branch on this — a missing or different v means the operator
signature check does not apply (and the receipt is treated as
untrusted).
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 + model
upstream is always "openrouter" today; future releases may add
direct connections to specific providers. model is the slug we passed
upstream, not the OpenAI-vs-Anthropic-vs-Llama family name.
cost_usdc
The actual cost the upstream provider charged us, NOT the amount the agent paid. This is the raw input to whatever pricing model overlays it.
For the x402 flow today, payment.amount_usdc is a flat fee
(X402_FLAT_PRICE_USDC=0.01 by default) and cost_usdc is whatever
OpenRouter returned. The difference is the spread we hold; a future
release will move toward refund-on-overpay to tighten this.
prompt_hash + response_hash
prompt_hash is computed server-side over the message list, joined as:
${role}:${content}\n${role}:${content}\n…This is hex sha256 (64 chars). The exact join algorithm is in apps/nexus/app/api/v1/chat/completions/route.ts.
response_hash is hex sha256 of the first choice's message.content
string. If the model produces multiple choices, only the first is
hashed — this matches what 99% of callers care about.
timestamp
Server wall-clock Date.now() at the moment the receipt object was
built. Authoritative timestamp for time-window verification is the
on-chain blockTime of payment.tx_signature, but timestamp is the
sub-second precision version.
inference_id
UUID of the row in our private inference_logs table. Useful for
support: if you DM us with inference_id we can look up exactly what
happened on our side without you having to reconstruct it.
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 of the USDC transfer. 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, e.g.
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 (devnet) or
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp (mainnet). The genesis-hash
form is what @x402/svm recognizes — short forms like solana:devnet
are not spec-compliant.
nexus_signature
Base58-encoded Ed25519 signature by the Nexus operator key. The
signature is computed over the canonical JSON of the receipt
object with nexus_signature excluded:
- Object keys sorted recursively
- No whitespace
- Primitives via standard
JSON.stringify - Arrays preserve order
Verifiers strip nexus_signature, canonicalize the rest, and call
nacl.sign.detached.verify against the operator's public key
(GET /api/v1/operator-key).
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.
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.