Agentic Commerce Protocol (ACP)
NextSell through ACP agent platforms, and let assistants pay with a delegated token on a UPI mandate.
Next (sandbox). ACP works on the sandbox today, on the fake rail. Nothing is live in production. We implement ACP spec version 2026-04-17.
TatvaPay plays both of ACP's back-end roles:
- Merchant endpoint for TatvaPay seller agents: an agent platform opens a checkout session, the buyer confirms, and the platform completes it.
- Payment service provider (
delegate_payment): the assistant gets a one-time delegated token for that checkout. It is drawn on a UPI mandate the person authorised in the TatvaPay app, never on a card.
Every payment is an ordinary TatvaPay payment: the same rules (a person approves anything above ₹2,000), the same caps, and a signed receipt that records the ACP checkout and the token.
Keys
ACP uses Authorization: Bearer. Issue a protocol key in the consent centre (or POST /v1/pay/protocol-keys with your API key):
| Role | Bound to | Used for |
|---|---|---|
merchant |
a seller agent | the checkout endpoints, by the agent platform |
buyer |
a buyer agent | delegate_payment, by the assistant |
A key is shown once and revocable in one tap. Register a public key with it (Ed25519 or P-256) and every request must also carry Signature and Timestamp.
Every request sends API-Version: 2026-04-17; every POST sends an Idempotency-Key.
Checkout (merchant side)
Item ids are your published offer SKUs. Amounts are paise; the currency is inr.
POST /acp/checkout_sessions
Authorization: Bearer tpp_<merchant key>
API-Version: 2026-04-17
Idempotency-Key: 6a1f…
{"line_items": [{"id": "airport_transfer", "quantity": 1}], "currency": "inr", "capabilities": {}}
The answer is the full session: status, line_items with totals, totals (the last is total), one digital fulfilment option, messages, links, and the payment handler:
{
"id": "tatvapay_upi_mandate",
"name": "com.tatvapay.upi_mandate",
"requires_delegate_payment": true,
"requires_pci_compliance": false,
"psp": "tatvapay",
"config": {
"merchant_id": "agt_…",
"delegate_payment_url": "https://api.tatvapay.com/acp/agentic_commerce/delegate_payment",
"instrument_type": "tatvapay_mandate",
"credential_type": "tatvapay_delegated_token",
"rails": ["upi_reserve_pay", "upi_otm"]
}
}
POST /acp/checkout_sessions/{id} updates items; GET reads; …/cancel cancels (405 once completed or canceled).
Delegated payment (PSP side)
POST /acp/agentic_commerce/delegate_payment
Authorization: Bearer tpp_<buyer key>
{
"payment_method": {"type": "tatvapay_mandate", "mandate_id": "mdt_…"},
"allowance": {
"reason": "one_time",
"max_amount": 150000,
"currency": "inr",
"checkout_session_id": "cs_…",
"merchant_id": "agt_…",
"expires_at": "2026-10-01T12:00:00Z"
},
"risk_signals": [],
"metadata": {}
}
The answer is 201 with {"id": "vt_…", "created", "metadata"}. The token:
- pays once, for that session and merchant, up to
max_amount, untilexpires_at(at most 7 days); - fits inside the mandate's per-payment cap and seller list;
- can be revoked in the consent centre.
No mandate yet? Leave out mandate_id. TatvaPay makes a one-time mandate scoped exactly to the allowance; metadata.status is pending_authorisation and metadata.authorization_url is where the person authorises it, once. Cards are refused (invalid_card): India's two-factor rules make a silent card-on-file charge non-compliant, while a UPI mandate is authorised once and then debited within its limits.
Complete
{"payment_data": {"handler_id": "tatvapay_upi_mandate",
"instrument": {"type": "tatvapay_mandate",
"credential": {"type": "tatvapay_delegated_token", "token": "vt_…"}}}}
| Session status | Meaning |
|---|---|
completed |
paid; order has the id and a link; metadata.tatvapay_receipt_id the receipt |
pending_approval |
above the always-ask threshold: a person approves in the app, then GET shows completed |
ready_for_payment with a payment_declined message |
the rules refused it; ask for a new token |
Errors are ACP's flat shape: {"type", "code", "message", "param"}, for example allowance_exceeded, token_used, token_session_mismatch, requires_buyer_review (402: the person has not authorised yet).
A reference client
examples/acp-client/acp_client.py in the engine repository is an outside assistant written with plain HTTP and a crypto library: checkout, token, complete, wait for a person if asked, then it fetches the receipt with its buyer key and verifies it offline.
Last updated 1 October 2026
Something unclear or wrong? Tell us