Documentation: all sections

Verifiable receipts

Every chat completion and embedding comes with a signed receipt: a small, metadata-only attestation that Saxeo served the request, on which machine, in which region, under which tier, signed with secp256k1 so anyone can verify it without trusting us. The receipt proves that inference happened and how; it never carries the prompt or completion.

What you get back

Non-streaming POST /v1/chat/completions and POST /v1/embeddings responses add these headers:

The x-sable-receipt* headers are CORS-exposed, so a browser client can read them from a cross-origin response. You don't need a server-side proxy just to collect receipts.

curl -i https://www.saxeonetwork.tech/__api/v1/chat/completions \
-H "Authorization: Bearer $SAXEO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "model": "saxeo-llama-3.3-70b",
  "messages": [{"role":"user","content":"hi"}]
}'

# ... response headers ...
# x-sable-node: gateway
# x-sable-region: unspecified
# x-sable-receipt: eyJ2IjoxLCJyZXF1ZXN0X2lkIjoi...
# x-sable-receipt-sig: 0x4f8c...1b
# x-sable-receipt-signer: 0xA1b2...9F

The receipt payload

Base64url-decode x-sable-receipt and you get canonical JSON. It is metadata only: note content_fingerprint, a SHA-256 prefix over the request body, not the content itself.

{
  "v": 2,
  "request_id": "req_8f3c...",
  "content_fingerprint": "9b1c4e...",
  "model": "saxeo-llama-3.3-70b",
  "privacy_tier": "standard",
  "node": "gateway",
  "region": "unspecified",
  "prompt_tokens": 12,
  "completion_tokens": 48,
  "total_tokens": 60,
  "cost_micro_usd": 648,
  "status": "ok",
  "logging": "metadata-only",
  "issued_at": "2026-05-30T12:00:00Z"
}

status says what outcome this receipt describes: ok, or error, aborted, attestation_failed or timeout, with a content-free error_class alongside it when the request failed. It arrived in v: 2, and it exists because without it a receipt minted for a request that produced nothing was byte-shaped exactly like one for a served completion — so anything counting receipts counted both. A v: 1 receipt has no status field and still verifies byte-for-byte, because verification recovers over the stored bytes rather than a re-serialization.

The fingerprint is the first 8 bytes of SHA-256 over the JSON request body as the gateway parsed and re-serialized it (object keys sorted, defaults materialized), computed in-frame before the payload is sealed. It binds the receipt to one specific request without the request ever reaching the receipt: two receipts carrying the same fingerprint describe the same body, and a changed body changes the fingerprint. cost_micro_usd is the metered cost in millionths of a dollar. See Cost & metering.

What the receipt commits to

Worth being exact about, because it is the whole reason to keep one.

content_fingerprintWhat it commits toThe request. On an inference receipt, the body you sent; on a sandbox receipt, the code you submitted. Nothing else.
prompt_tokens / completion_tokens / cost_micro_usdWhat it commits toCounts and price, metered at the gateway. A completion token count is a number, not a commitment to the text it counted.
model / engine / node / region / privacy_tierWhat it commits toWhich catalog id you asked for, which engine actually served it, and where.
contextWhat it commits toThe documents you declared with sable_context — the inputs, not the completion.
actionsWhat it commits toThe tool calls the model emitted, by name and argument hash. Not their results.
policy / guardrailsWhat it commits toWhich rules governed the call, and what the in-frame detectors saw.
attestationWhat it commits toThat the gateway verified an enclave quote before routing, and — when response_bound is true — that it verified the enclave's signature over this response before returning it.
operatorWhat it commits toOn a sandbox receipt from a counter-signing fleet node: that node's own output_fp over exit_code|stdout|stderr, signed by it and checked against its enrolled key.
loggingWhat it commits toSaxeo's signed claim about its own retention for this call.

Read the gap plainly: no field on an inference receipt commits to the completion text. The receipt proves what you sent, what ran, where, under which rules, what it cost, and — when the model emitted tool calls — which ones. It does not carry a hash of what came back, and there is no field you can recompute from a saved completion to prove the model produced it. The two places an output is committed to at all are operator.output_fp on a sandbox receipt (the serving node's fingerprint, only as trustworthy as that node) and attestation.response_bound on the confidential tier (the gateway's signed statement that it checked an enclave signature over the response — a boolean, not the hash itself).

The logging claim

Every receipt carries logging: "metadata-only". It is a signed, per-request claim that for this specific call Saxeo persisted no prompt, no completion, and no submitted code, only the metadata that appears on the receipt itself. Where most services state their retention posture in a policy page, this is the privacy contract restated as a verifiable artifact: a claim you can pin to one request and check the signature on, not a promise you have to take on trust.

Read it for what it is. This is Saxeo's signed claim about its own logging, not a hardware proof that the host could not have retained the plaintext. The confidential tier is the rung where that guarantee is hardware-backed; on every other tier logging: "metadata-only" is the gateway attesting, under its own signature, to the contract it operates under.

engine: what actually served

Some catalog ids are names for an engine rather than the engine itself. The flagship lane (saxeo, sable-fast, sable-max) most of all. Whenever the upstream engine that served the request differs from the public model id, the receipt carries an engine field naming it. On flagship receipts it is always present, because a flagship id always resolves to a disclosed engine, and because the flagship ids carry an ordered failover chain, the receipt is how you see which link in the chain ran:

{
  "model": "saxeo",
  "engine": "anthropic/claude-opus-5"
}

Billing prices the engine that ran, so engine is also the field that explains the metered cost when a failover served the request. This is the honesty mechanism that keeps flagship routing from being a black box: the signed receipt names the engine, every time.

engine_build: which configuration served

A call that named an Intelligence Engine build — engine/<slug> or a pinned engine/<slug>@<version> — carries an engine_build block naming the build, the version, the base model it expanded to, and the digest of the exact configuration that served:

"engine_build": {
  "model": "engine/triage",
  "slug": "triage",
  "version": 2,
  "base_model": "saxeo-llama-3.3-70b",
  "spec_sha256": "9f3a…"
}

base_model is in the block because a build rewrites the request's model id before anything is metered — without it, a reader of the receipt could not tell what actually ran.

spec_sha256 is the digest over the build's canonical spec: base model, the sha256 of its system prompt (never the prompt), temperature in thousandths, the max_tokens ceiling, guardrail rule set, policy and privacy tier. So two receipts carrying the same digest were served by the same configuration, and a digest that moved is a configuration that changed. An engine version is immutable and its version number is never reused, so the pair (slug, version) can never later describe something else.

Read it honestly: this proves which settings applied, not that the output was correct, safe, or reproducible. The same configuration called twice can return different text.

The block is additive, like attestation and policy before it: a receipt for an ordinary call is byte-identical to one minted before the Intelligence Engine existed.

scrubbed: the outbound scrub ran

A request that opted into outbound scrubbing with sable_scrub: true gets "scrubbed": true on its receipt. The field is present only when the scrub ran; it means the redaction filter executed on the outbound prompt before it left for the vendor. Nothing more. It does not change what is stored (nothing is, scrubbed or not: §3 is unchanged; only the redaction count is logged) and it is not a claim that the content was anonymized. See the scrubbing page for exactly what the filter catches and what it doesn't.

clamped: the serving node's token counts were bounded

A fleet node reports its own token usage, and that report is what decides the bill. So the gateway does not take it on trust: before anything is metered, the reported counts are bounded by a ceiling derived from the bytes that actually crossed the wire. A node can always cap out — report the ceiling and be believed up to it — but it can never invoice for tokens it did not transfer.

When that bound actually bit, the receipt says so:

{
  "node": "node-4c9141b63c8d",
  "prompt_tokens": 2016,
  "completion_tokens": 74,
  "clamped": true
}

"clamped": true means: the serving node reported token counts above the byte-length ceiling, and you were billed at the ceiling instead. The numbers next to it are the bounded ones — the ones you paid for.

Two things it is not. It is not a claim that you were overcharged: the whole point is that the correction happened before billing, so the figure on the receipt is already the right one. And it is not a statement about you or your request — it is a signal about that node. A misconfigured chat template can trip the ceiling as easily as a dishonest operator can, which is why a clamp is counted and surfaced rather than acted on automatically.

The field is omitted entirely when nothing was bounded, so a receipt from a vetted upstream — which is every inference receipt today — is byte-identical to one issued before the field existed, and its signature is unchanged.

What node and region mean

node names the machine that served the request, and it only ever names something that exists. One gateway process serves inference today, so an inference receipt reads "node": "gateway". A sandbox run served by an enrolled fleet node is recorded against that node's real id; one served by the deployment's configured backend is recorded against the backend that executed the code: sandbox:http-runner or sandbox:local-docker.

GET /v1/nodes lists the same set from the same source: the gateway itself, one tee:<name> entry per configured confidential backend carrying its live attestation state, and every enrolled fleet node with a status computed from its heartbeat, never stored optimism. Each entry has a synthetic flag: true means the entry is derived from this deployment's configuration rather than from a separately enrolled machine; enrolled nodes carry synthetic: false.

Receipts issued before August 2026 name one of four seeded ids (node-fra-01, node-nyc-02, node-sin-01, node-lhr-03) describing SGX/SEV/NVIDIA-CC hardware in four cities. That hardware never existed. Those rows are retired and excluded from GET /v1/nodes, but the ids stay resolvable, because old receipts name them and a receipt is a permanent claim. Per-machine node ids are real now for fleet-served sandbox runs; inference still serves from the gateway process.

region is the region this deployment declares it runs in (one of eu-central, eu-west, us-east, us-west, ap-southeast), or the literal string unspecified when the operator has declared none. It is not your sable_region pin echoed back: a pin is honored only when it resolves to the region the gateway actually runs in, and refused with 400 otherwise. See Region pinning.

A confidential-tier receipt still reads "node": "gateway". That is the machine you called. The enclave that ran the model is identified in the attestation block below, which is the part that carries a hardware guarantee.

Confidential-tier attestation

A request on the confidential tier (today, the saxeo-confidential-* models) adds an attestation block (also metadata only) proving it ran inside an attested Intel TDX enclave. Standard-tier receipts omit it entirely.

{
  "attestation": {
    "platform": "tdx",
    "measurement": "f06dfda6…",
    "mrtd": "f06dfda6…",
    "tcb_status": "UpToDate",
    "verification": "tee-attested",
    "response_bound": false,
    "signer": "0x79a5061e…"
  }
}

Under an MRTD pin those two fields are the same value by construction — see below. A receipt where they differ came from a deployment pinning the workload register (RTMR3) instead.

The gateway verifies the enclave's TDX quote against a pinned measurement before routing, and verifies a per-response signature from the key bound into that quote after. verification: "tee-attested" means the quote verified before your request was routed. response_bound is the stricter claim — that the enclave signed sha256(request):sha256(response) and both digests match the bytes the gateway sent and received — and it reads false on every receipt this deployment issues today, because the chat handlers do not yet hand the response bytes to the check. A flag that says "this response came from that enclave" has to mean it, so the gateway leaves it false rather than setting it on a weaker check.

measurement is whichever register matched the pin. This deployment pins MRTD, the base image of the confidential VM, so measurement and mrtd carry the same value and the receipt attests the platform and base image rather than the specific workload build. Why, and what that does and does not prove. gpu_verified is recorded, not required. The public GET /v1/attestation returns the live verified status, and the SDK's verifyAttestation() checks it as a pre-flight before you send anything.

Streaming

A streamed chat completion can't set trailing headers, so the receipt arrives as the final SSE event after the stream ends:

event: sable.receipt
data: {"receipt":"eyJ2Ijox...","signature":"0x4f8c...","signer":"0xA1b2...9F"}

Read it the same way you'd read the headers: base64url-decode receipt for the payload, verify signature against signer.

Re-fetching a receipt

Receipts are stored (metadata only, like everything else), so losing the response headers no longer means losing the proof. GET /v1/receipts/:id (session-authed, Bearer sess_…) returns the stored receipt and its signature. The :id is either the receipt's own request_id or the usage-event id shown in the dashboard history. The prompt and completion are not part of the receipt and were never stored, so a re-fetched receipt carries exactly what the original headers did.

Verifying a receipt

Because receipts use standard secp256k1 + EIP-191, any Ethereum signature library verifies them. Recover the signer over the decoded payload string (atob(receipt)) and check it equals x-sable-receipt-signer.

import { verifyMessage } from "viem";

// receipt = x-sable-receipt, signature = x-sable-receipt-sig,
// signer  = x-sable-receipt-signer
async function verifyReceipt(
receipt: string,
signature: `0x${string}`,
signer: `0x${string}`,
): Promise<boolean> {
const payload = atob(receipt); // base64url -> canonical JSON string
return verifyMessage({ address: signer, message: payload, signature });
}

// ethers works too:
//   ethers.verifyMessage(payload, signature) === signer

Verify endpoints

Two public endpoints (no auth) help you verify without wiring up crypto yourself:

The signing key is derived from the deployment's master key, so the signer address is stable per deployment: fetch it once from /v1/receipts/pubkey, pin it, and reject any receipt that recovers to a different address.

operator: who served it (receipt v:3)

When a run executes on an enrolled fleet node that counter-signs its work, the sandbox receipt carries an operator block:

"operator": { "node_id": "node-…", "output_fp": "…", "verified": true }

The serving node holds its own ed25519 key and signs a fingerprint of (input, output) for every run. The gateway verifies that signature against the key it has on record for that node (recorded on first sight, then pinned; a silent key change is refused, not attributed) and checks that the signed input fingerprint is its own hash of the code you submitted. verified: true means both held.

Read this honestly: operator attribution proves which machine served the run: the honest label is operator-attributed, not a proof that the work itself was performed correctly (that is the confidential/TEE tier). It is the accountability rung: it says who to hold responsible. A run on a node that doesn't sign, or whose signature doesn't verify, simply omits the block, and the receipt stays the earlier v:2 shape.

Verifiable context

An agent doing retrieval can declare the context it was given, and have the receipt prove those inputs. Pass sable_context on a chat or /v1/messages request: an array of strings, or objects { text, label? } where the label is a citation.

curl https://www.saxeonetwork.tech/__api/v1/chat/completions \
-H "authorization: Bearer $SAXEO_KEY" \
-H 'content-type: application/json' \
-d '{
  "model": "saxeo",
  "messages": [{"role":"user","content":"Summarize the policy."}],
  "sable_context": [
    {"text":"Refunds are issued within 30 days.","label":"policy.pdf#p3"},
    {"text":"Enterprise plans renew annually."}
  ]
}'

The gateway fingerprints each item in-frame and stamps a context block on the receipt. Nothing of the text is stored (§3): only sha256 fingerprints, byte lengths, and any labels.

"context": {
  "count": 2,
  "root": "9f2c…",
  "items": [
    { "label": "policy.pdf#p3", "fp": "3b1e…", "len": 33 },
    { "fp": "a07d…", "len": 27 }
  ]
}

root is sha256( sha256(item_0) ++ sha256(item_1) ++ … ) over the items in order, so it is sensitive to both content and ordering. Anyone later holding the same documents recomputes each item's sha256 (its prefix must match fp) and the root (it must match root), and so proves those exact inputs, in that order, fed this exact signed call. It is the input-side twin of the output fingerprint: proof of the context, not just the completion.

Read it honestly: this proves what context was declared, not that the model attended to it and not that the documents are true. It closes the "did the agent get the context it claims" gap, which is the one a third party can check.

Action attestation

When a chat completion emits tool calls, the signed receipt gains an actions block recording the calls the model asked for. Like everything else on the receipt it is metadata only: tool names, argument-hash prefixes, and byte lengths, never the argument text itself.

"actions": {
  "count": 2,
  "root": "7c4a…",
  "calls": [
    { "tool": "search_docs", "args_fp": "3b1e…", "args_len": 48 },
    { "tool": "send_email",  "args_fp": "a07d…", "args_len": 132 }
  ]
}

Each call's fingerprint is sha256("name|arguments"). The root is the sha256 of the full per-call hashes concatenated in emission order, so it is sensitive to both the calls and their ordering. Anyone later holding the same argument payloads recomputes each call's sha256 (its prefix must match args_fp) and the root (it must match root), and so proves the model emitted exactly those tool calls, in that order, on this exact signed call.

It works on buffered and streamed responses identically. The block is omitted entirely when a response carries no tool calls, so tool-free receipts are byte-for-byte unchanged. Past 64 calls the block is omitted rather than truncated, so a partial list can never be mistaken for a complete one. Only tool names, hash prefixes, and byte lengths reach the receipt (§3): the argument text is never stored or logged.

Read it honestly: this proves what tool calls the model emitted through Saxeo, not that the caller executed them and not what they returned. And note what it does not close: content_fingerprint covers the request, context the documents you declared, actions the calls the model asked for — the completion itself is still uncommitted. See what the receipt commits to.

Agent runs: chain a whole session

Tag any billable request with sable_run_id (1 to 64 chars of [A-Za-z0-9._:-], accepted on chat, embeddings, /v1/messages, and sandboxes) and its signed receipt joins a per-run hash chain:

chain(n) = sha256( chain(n-1) ‖ sha256(receipt(n)) )

One head hash then proves the entire session: every model call and sandbox execution, in order, across kinds. Rewriting or omitting any step breaks every later link.

curl https://www.saxeonetwork.tech/__api/v1/chat/completions \
-H "authorization: Bearer $SAXEO_API_KEY" \
-H 'content-type: application/json' \
-d '{
  "model": "saxeo",
  "sable_run_id": "my-agent-42",
  "messages": [{"role": "user", "content": "…"}]
}'

Session-authed: GET /v1/runs lists your runs; GET /v1/runs/{run_id} returns the chained receipts (each independently signed and verifiable); POST /v1/runs/{run_id}/proof mints a run proof (a signed statement carrying the head hash, receipt count, total cost, and time span) which verifies through the same public POST /v1/receipts/verify as everything else. Run heads are batch-anchored to Solana (memo format sable-runs:v1:<root>) when the deployment's anchor account is funded; until a batch finalizes, a run honestly reports no anchor. The proof's embedded trust_model states exactly what is proven: attested by the gateway over its hash chain, publicly anchored when an anchor exists, not zero-knowledge.

Sharing a receipt

Receipts are metadata-only by construction, so an owner can safely publish one: POST /v1/receipts/{id}/share (session-authed) makes it fetchable at GET /v1/receipts/shared/{request_id} and viewable at /r/{request_id} on the site: model, cost, fingerprint, and chain position, never content. DELETE /v1/receipts/{id}/share unshares it. Nothing is public unless you shared it, and unsharing takes effect immediately.