Dagon InferenceDocs Console

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.

On this page

Fields

A receipt is {"payload": {...}, "signature": "<hex>"}. The signature covers the payload as canonical JSON (keys sorted, no whitespace).

FieldMeaning
vReceipt version. 2 names the provider; older 1 receipts were all served by abliteration.ai.
idReceipt id (rcpt_…).
gatewayId of the signing key; equals gateway_id at /v1/identity.
key_idThe key charged (its id, not the secret).
createdWhen it was signed.
modelThe model id you asked for.
providerThe provider that served the request.
endpointThe endpoint called.
usageinput_tokens, cached_input_tokens, output_tokens.
usage_sourceupstream (reported by the provider), estimated or reserved (no final report arrived).
price_versionThe price schedule the charge was computed from.
markup_bpsMarkup in basis points (1000).
charged_microsWhat the key paid, in micro-USD.
held_microsOnly when the price arithmetic exceeded the hold: the charge was capped at this amount.
output_sha256SHA-256 of the output. The text itself is never stored.
upstream_request_idThe provider's request id, or null.

Fetch and list

RequestResult
GET /v1/receipts/:idOne 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
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.json

Verify 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"));

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