usertrust

JSON Schemas

Published JSON Schemas for trust receipts and audit chain events, with stable $ids.

usertrust publishes JSON Schema (draft 2020-12) definitions for its two public interop shapes: the receipt returned by every governed call, and one persisted line of the hash-chained audit log. The $id URLs are stable — pin them in validators, CI checks, or any tool that consumes receipts or audit logs.

Schema$idDescribes
Trust Receipt v1receipt.v1.schema.jsonThe TrustReceipt returned by every governed call
Trust Receipt v2receipt.v2.schema.jsonv1 plus root-level additions: the four-tier usage split, pricing (appliedRates, tableVersion), and cacheWrite1h (present only when the call wrote 1-hour cache) — see Pricing
Audit Event v1audit-event.v1.schema.jsonOne line of the audit chain (.usertrust/audit/*.jsonl)

Versioning

  • v1 is frozen. Once published, a versioned schema never changes meaning. Description wording may improve; the validated shape does not. receipt.v1.schema.json keeps validating exactly what it always validated — a v1-only validator does not need to change to keep accepting receipts.
  • Changes ship as a new version. Additive or breaking changes become a new schema at a new URL — receipt.v2.schema.json is the first of these: it adds the usage object (fresh/cache-read/cache-write/output token split) and the pricing object (the resolved appliedRates and the tableVersion they came from), so a metered LLM receipt's cost is independently recomputable from the record alone. usage is present when a complete report priced the call (always for usageSource provider, and for an estimated settle whose complete counts priced it) and is omitted, never zero-filled, when the cost is the metered pre-call estimate or an incomplete report. pricing is present on every LLM settle and absent on non-LLM action receipts. v1 URLs keep working and keep meaning the same thing.
  • Additions go where the frozen schema left room. v1 declares the receipt root additionalProperties: true but closes meter with additionalProperties: false. Both v2 additions are therefore ROOT-level objects: a v1 validator accepts every v2 receipt unchanged, which is what "v1 keeps meaning the same thing" has to mean in practice. Anything added later must clear the same bar.
  • All three schemas are open. Receipts may carry additional diagnostic fields, and audit events may carry extra fields that are covered by the chain hash. Validators must ignore unknown fields, not reject them.

Validating a receipt

With ajv v8 (npm install ajv ajv-formats):

import Ajv2020 from "ajv/dist/2020.js";
import addFormats from "ajv-formats";

const schema = await fetch("https://usertrust.ai/schemas/receipt.v1.schema.json").then((r) =>
  r.json(),
);

const ajv = addFormats(new Ajv2020());
const validate = ajv.compile(schema);

if (!validate(receipt)) {
  console.error(validate.errors);
}

No validator handy? The schemas are plain static JSON — fetch one and read required / properties directly. String formats (date-time, uuid) are annotations; ajv-formats enforces them, other validators may not.

Schema validation checks shape, not integrity. To check an audit log's hash chain and sequence continuity, run the zero-dependency usertrust-verify package against the vault. It works offline. Without an external anchor, a log rewritten together with its sidecar still passes; Verification says what the check does and does not catch.