Quitanza

The quitanza format

Format version 1, issued by Quitanza API v0.3. This page is the normative specification: fields, canonicalization, the verification algorithm, and a worked example with real bytes you can verify without any Quitanza code.

A quitanza is a signed JSON document proving how an escrow closed: who paid whom, how much, against which terms, with what outcome, and the exact head of the evidence trail at the moment of issuance. Anyone holding the document and the issuer's public key can verify it offline.

Fields

Field Type Presence Meaning
id string always qtz_ followed by 32 hex characters.
escrowId string always The escrow this quitanza closes.
outcome string always released, refunded, or split.
payer hex(64) always Payer's raw 32-byte Ed25519 public key.
payee hex(64) always Payee's raw 32-byte Ed25519 public key.
amount string always Decimal string, e.g. "25.00". Never a float.
asset string always Asset symbol, e.g. "USDC".
termsHash hex(64) always SHA-256 of the canonical JSON of the escrow's Terms.
trailHead hex(64) always Hash of the last evidence-trail entry at issuance.
trailLength integer always Trail entry count at issuance.
issuedAt string always ISO 8601 instant.
deliveryHash hex(64) when a delivery exists SHA-256 of the canonical JSON of the delivered payload.
verdictId string clean settlements The verdict that passed or failed the delivery.
disputeId string disputed settlements The dispute whose ruling closed the matter.
mandateHash hex(64) mandated escrows Content hash of the mandate the payer acted under.
anchor object chain-backed custody Where the settlement moved funds on chain. See the Anchor addendum below.
signature object always { "signer": hex(64), "signature": hex(128) }, the issuer's Ed25519 signature.

A quitanza born of a dispute carries disputeId; one born of a clean settlement carries verdictId and deliveryHash. A refund quitanza proves the matter closed in the payer's favor. Proof is symmetric.

Canonicalization

Hashing and signing always operate on canonical JSON:

  1. Object keys sorted lexicographically (code-unit order), at every nesting level.
  2. No whitespace anywhere.
  3. Keys whose value is undefined are omitted; null is kept.
  4. Arrays keep their order; their elements are canonicalized recursively.
  5. The result is UTF-8 encoded for hashing and signing.

Equivalent objects therefore produce identical bytes, so verification is byte-exact and implementation-independent.

Verification algorithm

Given a quitanza document and the issuer's public key:

  1. Remove the signature field; the remainder is the signed body.
  2. Compute the canonical JSON of the body (rules above).
  3. Check signature.signer equals the issuer public key you trust.
  4. Verify signature.signature as an Ed25519 signature over the UTF-8 bytes of step 2, using that key.

To additionally bind the proof to history, fetch the evidence trail (GET /v1/escrows/{escrowId}/trail) and check: every entry's hash equals SHA-256 of the canonical JSON of {index, prevHash, at, event, body}, each prevHash matches the previous entry's hash (64 zeros for the genesis entry), and the entry at index trailLength - 1 has hash trailHead.

Worked example: real bytes

The document below was issued by a real engine run; every byte verifies.

{
  "id": "qtz_3cd1d5ba17904e2eb1437a1011afb02c",
  "escrowId": "esc_77a341165e084c0daadcae6be877c6ca",
  "outcome": "released",
  "payer": "145acbb3f6e695d4313e4f373630c5226154d2561162cf028e5b5926877bca93",
  "payee": "e0bf901f8fb87ae3f5890afc34e80e5e65eab05df47a2855363516d838bd12b2",
  "amount": "25.00",
  "asset": "USDC",
  "termsHash": "07c1aef495dd22528506ca3a7baf3122f8d5a730c867a26ede7a20901ce0c0db",
  "trailHead": "247ea9423f4c9bb4489ce19b2ce50c6beff6f811df5d97542cc9d715cfb52071",
  "trailLength": 4,
  "issuedAt": "2026-06-10T12:00:13.000Z",
  "deliveryHash": "9f45517c2cf50d8e72bb6f8f173e25f54dc0d5daf3b196b785ea4e22f4626ea2",
  "verdictId": "vrd_664601e9f96b4777bd48313c05057ec3",
  "signature": {
    "signer": "f327d5d6dcffe11a9c81f832a525a8b3936b7a5299f82e306c4cded8e1ea7b16",
    "signature": "d86329a4c26da2545c2b874c0e02ac44191fcefcca5a931d3a8b1d615eeafc9db86c4431a0e58fb2d2e7b019476fcdaef9f4fde88f85caf47b9bcc015ca3130f"
  }
}

The issuer's public key is f327d5d6dcffe11a9c81f832a525a8b3936b7a5299f82e306c4cded8e1ea7b16. The canonical JSON of the signed body, step 2 of the algorithm (one line, keys sorted, no whitespace), is:

{"amount":"25.00","asset":"USDC","deliveryHash":"9f45517c2cf50d8e72bb6f8f173e25f54dc0d5daf3b196b785ea4e22f4626ea2","escrowId":"esc_77a341165e084c0daadcae6be877c6ca","id":"qtz_3cd1d5ba17904e2eb1437a1011afb02c","issuedAt":"2026-06-10T12:00:13.000Z","outcome":"released","payee":"e0bf901f8fb87ae3f5890afc34e80e5e65eab05df47a2855363516d838bd12b2","payer":"145acbb3f6e695d4313e4f373630c5226154d2561162cf028e5b5926877bca93","termsHash":"07c1aef495dd22528506ca3a7baf3122f8d5a730c867a26ede7a20901ce0c0db","trailHead":"247ea9423f4c9bb4489ce19b2ce50c6beff6f811df5d97542cc9d715cfb52071","trailLength":4,"verdictId":"vrd_664601e9f96b4777bd48313c05057ec3"}

Verify it with nothing but Node's standard library, no Quitanza code:

import { createPublicKey, verify } from "node:crypto";

const sortKeys = (v) =>
  Array.isArray(v) ? v.map(sortKeys)
  : v !== null && typeof v === "object"
    ? Object.fromEntries(Object.keys(v).sort().map((k) => [k, sortKeys(v[k])]))
    : v;

const quitanza = JSON.parse(/* the document above */);
const { signature, ...body } = quitanza;
const canonical = JSON.stringify(sortKeys(body));

// Raw 32-byte Ed25519 key → DER SPKI by prefixing the fixed header.
const spki = Buffer.concat([
  Buffer.from("302a300506032b6570032100", "hex"),
  Buffer.from(signature.signer, "hex")
]);
const key = createPublicKey({ key: spki, format: "der", type: "spki" });

console.log(
  verify(null, Buffer.from(canonical), key, Buffer.from(signature.signature, "hex"))
); // true

Change any field and verification fails: the amount, the outcome, the trail head.

What the trail head commits to

trailHead is the hash of the last entry in the escrow's evidence trail when the quitanza was issued, and trailLength is the entry count at that moment. Because each trail entry hashes its predecessor, the head transitively commits to every recorded event: creation, funding, the delivery's content hash, each verification check's result, every piece of dispute evidence, any timeout. The quitanza is small; what it pins down is not.

Verifying against a live API

GET /v1/quitanzas/{id}/verify
→ { "signatureValid": true, "trailIntact": true, "trailHeadMatches": true, "issuer": "…" }

Or offline with @quitanza/core: verifyQuitanza(quitanza).

Verifying against a domain

A deployment publishes its issuer keys at /.well-known/quitanza-issuer.json. verifyQuitanzaAgainstDomain(quitanza, "quitanza.com") in @quitanza/sdk fetches that document, requires the quitanza's signing key to be among the published keys, and verifies the signature offline. The CLI form is quitanza verify <id> --domain quitanza.com.

Addendum: the anchor block

When custody is chain-backed, the settlement moves funds in a contract and the quitanza carries an optional anchor object inside the signed body. Sandbox settlements omit it entirely, so every version 1 document above remains valid and the verification algorithm is unchanged: the anchor, when present, is just another signed field.

Field Type Meaning
chain string CAIP-2 chain id. Today always eip155:31337, a local anvil development node.
contract string Address of the escrow contract that held the funds.
escrowRef hex(0x+64) The escrow's id on that contract.
txHash hex(0x+64) The transaction that closed the matter on chain.

Custody is local-only in this release: no public network, no real funds. The anchor proves which transaction the issuer observed when it signed; on a development chain that transaction exists only on the node that produced it. The signature verifies offline either way.

Worked example with an anchor: real bytes

Issued by a real engine run settling through QuitanzaEscrow on a local anvil node. The same verification code as above accepts it unchanged.

{
  "id": "qtz_23c712d99c8c4b6f9ce545cf45adaba7",
  "escrowId": "esc_5e39926e46904f2cb113b0675690a96b",
  "outcome": "released",
  "payer": "9f9c3a9ff4fbd30d8cd926d01c86e49756640a8680914e28f52689b29ecbea84",
  "payee": "44ad3dd06c8fd11768d9e025bf2bed0cf8769a6a37968d1ff444837b49d9c102",
  "amount": "25.00",
  "asset": "USDC",
  "termsHash": "1653b0e2c0441523d3afa70f7092433f22854004cc114967a1942ae3a67fe3e4",
  "trailHead": "be823b4b95836e18020aea0469e5472601dfbd39424a15ef7a075107bbe24124",
  "trailLength": 4,
  "issuedAt": "2026-06-10T22:30:13.000Z",
  "anchor": {
    "chain": "eip155:31337",
    "contract": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
    "escrowRef": "0x65c439bbdaab7fd6a50bba3be38e967a052c84a7bb059b525bc8e54d76229093",
    "txHash": "0x41ac0aa8893430112747b820d407d188c94a36d6862f85dcc4f696ffec070d33"
  },
  "deliveryHash": "3e68fb7b3c76277df0f62e842db9b3cef3c634caac234a9aeaf2d0aaa334688d",
  "verdictId": "vrd_fbe7431240014a1194210e4ea73a2fa6",
  "signature": {
    "signer": "85c3b227bd273d42bc375c62708f3d5bc759525d4f38a1be03d0eaa0b179b424",
    "signature": "a68ad13b2c98f5254e553adf31c261d49fabe5f815bb90fa150bd4f049ac3605c82064b06ad5043acd1a88666c12bb931eeadf85f0d6aabf10657af062d3400e"
  }
}

The canonical signed body, with the anchor in place (keys sorted, one line, no whitespace):

{"amount":"25.00","anchor":{"chain":"eip155:31337","contract":"0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512","escrowRef":"0x65c439bbdaab7fd6a50bba3be38e967a052c84a7bb059b525bc8e54d76229093","txHash":"0x41ac0aa8893430112747b820d407d188c94a36d6862f85dcc4f696ffec070d33"},"asset":"USDC","deliveryHash":"3e68fb7b3c76277df0f62e842db9b3cef3c634caac234a9aeaf2d0aaa334688d","escrowId":"esc_5e39926e46904f2cb113b0675690a96b","id":"qtz_23c712d99c8c4b6f9ce545cf45adaba7","issuedAt":"2026-06-10T22:30:13.000Z","outcome":"released","payee":"44ad3dd06c8fd11768d9e025bf2bed0cf8769a6a37968d1ff444837b49d9c102","payer":"9f9c3a9ff4fbd30d8cd926d01c86e49756640a8680914e28f52689b29ecbea84","termsHash":"1653b0e2c0441523d3afa70f7092433f22854004cc114967a1942ae3a67fe3e4","trailHead":"be823b4b95836e18020aea0469e5472601dfbd39424a15ef7a075107bbe24124","trailLength":4,"verdictId":"vrd_fbe7431240014a1194210e4ea73a2fa6"}

Etymology, briefly

A quitanza was the document a medieval creditor handed a debtor on payment: formal proof the obligation was discharged. First attested 1289, kin to English acquit and quittance. This format is that instrument, as cryptography.

This page as markdown