TatvaPay
Docs menu· ACP checkout

Agentic Commerce Protocol (ACP)

Next

Sell 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, until expires_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