TatvaPay
Docs menu· Verify receipts

Verify receipts

Next

Check a TatvaPay receipt yourself, with only our published public keys.

Coming next. Receipts are issued on the sandbox today. The format is stable (tatvapay-receipt-v1).

Every payment has a receipt: a chain of events from the mandate to settlement (mandate_authorised, quote, decision, rail_reference, settlement, and approval, refund when they happen). Both sides, and a regulator, can check it without trusting us.

What is checked

  1. The chain. Each event's hash is sha256_hex(prev_hash + "\n" + canonical_json({receipt_id, seq, kind, at, data})), starting from 64 zeros. Canonical JSON has sorted keys, no spaces and non-ASCII kept. Editing any event breaks every hash after it.
  2. The signature. TatvaPay signs tatvapay-receipt-v1\n<receipt id>\n<event count>\n<head hash> with Ed25519.
  3. The key. It must be one of the keys at /.well-known/tatvapay-keys.json with status current or retired. A revoked key's receipts are not trusted.

With the SDKs

receipt = client.get_receipt("rcpt_…")
check = client.verify_receipt(receipt)      # fetches and caches the public keys
assert check.valid, check.errors
const receipt = await client.getReceipt("rcpt_…");
const check = await client.verifyReceipt(receipt);

Or with no client at all: tatvapay.receipts.verify_receipt(doc, jwks) and verifyReceipt(doc, jwks).

Online

GET /v1/receipts/{id}/verify recomputes the stored chain and checks the signature on our side. The MCP tool verify_receipt does the same.

Last updated 1 October 2026

Something unclear or wrong? Tell us