Error codes and rule ids
NextEvery refusal has a stable code; every payment decision names the rules behind it.
Coming next. These codes are what the sandbox answers today. We add codes; we do not change the meaning of one.
The shape of an error
REST answers {"detail": {"code": "…", "message": "…"}} with an HTTP status. MCP tools answer a tool error with {"error": {"code", "message"}} and the same codes. The SDKs raise TatvaPayError with .code.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | invalid_request |
A field is missing or out of range; the message says which. |
| 400 | idempotency_key |
Send an Idempotency-Key header (paying, refunds, forex quotes). |
| 400 | bad_key |
The public JWK is not an Ed25519 public key (or holds a private part). |
| 401 | unauthenticated |
No API key, agent signature or OAuth token. |
| 401 | bad_signature, stale_signature, replay, key_not_active, agent_revoked |
The agent signature failed: wrong key, more than 5 minutes off, a nonce used before, a rotated key, a revoked agent. |
| 401 | invalid_token |
The OAuth access token is unknown, expired or revoked. |
| 403 | forbidden |
Right credentials, wrong party: for example a buyer asking for a refund. |
| 403 | insufficient_scope |
The MCP connection was not given this permission. |
| 404 | not_found, unknown_offer |
Not found, or not yours to see. |
| 409 | quote_taken, quote_cancelled |
The quote was already paid, or a person rejected it. |
| 409 | idempotency_conflict, idempotency_in_progress |
The key was used for a different payment, or the same one is still running. |
| 409 | already_decided |
The approval was already decided. |
| 400 | no_mandate |
No active mandate of this agent covers the quote. Ask for one; a person authorises it. |
| 409 | lrs_limit_exceeded, stale_rates, rate_lock_expired, file_incomplete |
Forex: over the LRS limit, rates too old, the lock lapsed, documents missing. |
| 400 | aadhaar_not_collected |
TatvaPay never collects Aadhaar. |
| 402 | bad_proof, proof_used, not_settled, wrong_seller, underpaid, wrong_resource |
HTTP 402 proofs a seller refused. |
| 429 | rate_limited |
Too many requests; see Retry-After. |
Rule ids
Deterministic rules decide every payment (ruleset 2026-10-01.1). A payment's decision_reasons and the request log name the rule. A model never decides.
| Rule | Checks | When it fails |
|---|---|---|
R01_buyer_agent_active |
the buyer agent is active | refused |
R02_seller_agent_active |
the seller agent is active | refused |
R03_roles |
the buyer may buy and the seller may sell | refused |
R04_mandate_agent |
the mandate is this buyer agent's | refused |
R05_mandate_active |
the mandate is active | refused |
R06_mandate_expiry |
the mandate has not expired | refused |
R07_currency |
INR only | refused |
R08_quote_valid |
the quote is open, unexpired and addressed to this buyer | refused |
R09_rate_lock |
a forex rate lock has not lapsed | refused |
R10_per_payment_cap |
within the mandate's per-payment cap | refused |
R11_period_cap |
within the cap for the day, week or month | refused |
R12_total_cap |
within the mandate's lifetime cap | refused |
R13_seller_allowed |
the seller or category is allowed by the mandate | refused |
R14_spend_class |
within the agent's spend class | refused |
R15_trust_floor |
both agents' trust scores are high enough | refused |
R16_velocity |
not too many payments in 10 minutes | a person approves |
R17_always_ask |
at or below the always-ask limit (₹2,000 by default) | a person approves |
R18_high_value_hold |
at or below ₹50,000 | held for a person |
A person's approval never overrides R01 to R15: they are checked again before the rail is called.
Last updated 1 October 2026
Something unclear or wrong? Tell us