API
Receipts and verification
Every settled request gets an Ed25519 signed receipt with its token counts, provider, price version, markup and charge. Anyone can check it against the gateway's public key and the published prices.
Fields
A receipt is {"payload": {...}, "signature": "<hex>"}. The signature covers the payload as canonical JSON (keys sorted, no whitespace).
| Field | Meaning |
|---|---|
v | Receipt version. 2 names the provider; older 1 receipts were all served by abliteration.ai. |
id | Receipt id (rcpt_…). |
gateway | Id of the signing key; equals gateway_id at /v1/identity. |
key_id | The key charged (its id, not the secret). |
created | When it was signed. |
model | The model id you asked for. |
provider | The provider that served the request. |
endpoint | The endpoint called. |
usage | input_tokens, cached_input_tokens, output_tokens. |
usage_source | upstream (reported by the provider), estimated or reserved (no final report arrived). |
price_version | The price schedule the charge was computed from. |
markup_bps | Markup in basis points (1000). |
charged_micros | What the key paid, in micro-USD. |
held_micros | Only when the price arithmetic exceeded the hold: the charge was capped at this amount. |
output_sha256 | SHA-256 of the output. The text itself is never stored. |
upstream_request_id | The provider's request id, or null. |
Fetch and list
| Request | Result |
|---|---|
GET /v1/receipts/:id | One receipt of this key. Another key's receipt is 404 receipt_not_found. |
GET /v1/receipts?limit=&before= | This key's receipts, newest first: {data, next_before}. limit up to 200 (default 50); pass next_before as before for the next page. |
GET /v1/receipts?scope=account&key_id= | A root key or passkey session: the whole account, every child included, with keys for a filter. limit up to 100. |
Non-streaming responses also carry the receipt in the x-gateway-receipt header (base64url JSON).
Verify with the gateway
POST/v1/receipts/verify
Public, no key. Send the receipt as the body. The answer is {"valid": true} or {"valid": false, "reason": "..."}. It checks the signature, that the gateway signed it, and that charged_micros equals the price arithmetic of the price version it names (or, with held_micros, that the arithmetic exceeded the hold and the charge equals it). Every price version ever active is kept, so old receipts still verify.
curl https://inference.dagon.so/v1/receipts/$RECEIPT_ID -H "Authorization: Bearer $DAGON_KEY" > receipt.json
curl https://inference.dagon.so/v1/receipts/verify -H "Content-Type: application/json" -d @receipt.jsonVerify it yourself
GET /v1/identity returns gateway_id, algorithm (ed25519), public_key (32 bytes, hex), price_version and markup_bps. Check the signature locally:
import { createPublicKey, verify } from "node:crypto";
// Canonical JSON: object keys sorted, no whitespace, undefined fields dropped.
const canonical = (v) =>
v === null || typeof v !== "object" ? JSON.stringify(v)
: Array.isArray(v) ? `[${v.map(canonical).join(",")}]`
: `{${Object.keys(v).filter((k) => v[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonical(v[k])}`).join(",")}}`;
const { public_key, gateway_id } = await (await fetch("https://inference.dagon.so/v1/identity")).json();
const key = createPublicKey({
key: Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), Buffer.from(public_key, "hex")]),
format: "der", type: "spki",
});
const ok = receipt.payload.gateway === gateway_id &&
verify(null, Buffer.from(canonical(receipt.payload)), key, Buffer.from(receipt.signature, "hex"));# pip install pynacl requests
import json, requests
from nacl.signing import VerifyKey
ident = requests.get("https://inference.dagon.so/v1/identity").json()
canonical = json.dumps(receipt["payload"], sort_keys=True, separators=(",", ":"), ensure_ascii=False)
VerifyKey(bytes.fromhex(ident["public_key"])).verify(canonical.encode(), bytes.fromhex(receipt["signature"]))
assert receipt["payload"]["gateway"] == ident["gateway_id"]What a receipt proves
A receipt proves what the gateway charged for the usage it saw. It does not prove that the provider ran the model honestly, and model is the id you asked for, not proof of the weights that ran.
Last updated