Verify receipts
NextCheck 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
- 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. - The signature. TatvaPay signs
tatvapay-receipt-v1\n<receipt id>\n<event count>\n<head hash>with Ed25519. - The key. It must be one of the keys at
/.well-known/tatvapay-keys.jsonwith statuscurrentorretired. Arevokedkey'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