Payments
Hosted Checkout
Hosted Checkout is for a payout — money leaving your corporate account — that a real person on your side reviews and authorizes. It is not a customer-facing payment-collection product, and it is not for unattended, no-human submission. For fully machine-to-machine bulk submission, use Create Payments above instead.
1. Prerequisites
- Generate a partner API key with the
paymentsscope — API Keys section, this portal. - Register a webhook subscribed to the payment lifecycle events — Webhooks section, this portal.
- Have at least one approved maker user and one approved beneficiary on your corporate account.
2. Create a checkout session — server-to-server only
/v1/partner/payout-checkout/sessionsmakerUserIdstringrequiredSee the callout below — must be a real, approved human maker, never a system user.
amount.valueintegerrequiredInteger paise. ₹1,250.00 = 125000. Never send a decimal.
transactionReferencestringrequiredYour own unique reference. Safe to retry — the same Idempotency-Key returns the same session.
beneficiaryIdstringrequiredMust already be an approved beneficiary. Frozen for the whole session — the maker cannot redirect the payment elsewhere.
redirectUrl / cancelUrlstring (HTTPS URL)optionalWhere the maker's browser returns to after success or cancellation. Must use https://. Registered-domain enforcement is deferred and is not provided by this release.
curl -X POST https://<bank-api-host>/v1/partner/payout-checkout/sessions \
-H "x-api-key: cms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: order-88213-attempt-1" \
-H "Content-Type: application/json" \
-d '{
"makerUserId": "user-1780460729021-815",
"amount": { "value": 125000, "currency": "INR" },
"transactionReference": "order-88213",
"beneficiaryId": "bene-acme-vendor-001",
"redirectUrl": "https://your-erp.example.com/payments/return",
"cancelUrl": "https://your-erp.example.com/payments/cancelled",
"metadata": { "orderId": "88213", "department": "procurement" }
}'{
"sessionId": "pcs_live_5f2a9c1e4b3d4a2e9f6c7d8e1a2b3c4d",
"token": "9f86d081884c7d659a2feaa0c55ad015",
"hostedUrl": "https://checkout.example-bank.com/payout/9f86d081884c7d659a2feaa0c55ad015",
"expiresAt": 1785400200000
}The maker named by makerUserId is the person who receives the one-time verification code on the hosted page and authorizes the payment. It must be the user ID of an approved corporate user holding the maker (transaction.make) permission — the same kind of user who creates payments in the corporate web app today.
It must notbe a system/service account, an integration user, or any ID that doesn't correspond to a real person who can receive and enter an OTP. An unknown, unapproved, or non-maker ID is rejected outright with maker_not_authorized — there is no fallback identity.
3. Redirect the maker's browser
// your frontend / ERP, after calling your own backend // which in turn called the API above window.location.assign(session.hostedUrl);
4. What the maker does — no work required from you
- Reviews the frozen amount, beneficiary, and your reference.
- Picks a package, debit account, and payment method — automatically filtered to what their role permits.
- Requests and enters a one-time code sent to their own registered email.
- Is redirected to your
redirectUrl(orcancelUrlif they cancel).
5. Reconcile — reuse your existing webhook integration
/v1/partner/payout-checkout/sessions/:sessionId{
"checkout": {
"sessionId": "pcs_live_5f2a9c1e4b3d4a2e9f6c7d8e1a2b3c4d",
"status": "submitted",
"expiresAt": "2026-08-01T10:30:00.000Z",
"completedAt": "2026-08-01T10:18:42.000Z"
},
"transaction": {
"transactionId": "batch-pcs-5f2a9c1e4b3d",
"transactionReference": "order-88213",
"status": "pending_approval"
}
}{
"id": "evt_...",
"type": "transaction.paid",
"created": 1785400300000,
"data": {
"object": {
"object": "transaction",
"transactionId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"jobId": null,
"apiRefNumber": "order-88213",
"reference": "order-88213",
"status": "paid",
"source": "payout_checkout",
"amount": { "value": 125000, "currency": "INR" },
"utr": "HDFCN2026080100778",
"bankReference": "BANKALPHA-DISP-6BA7B810...",
"timestamps": { "paidAt": 1785400300000 }
}
}
}Quick reference
~15 minutes, single-use.
Integer paise, always.
makerUserId = a real, approved, human maker — never a system user.
transaction.transactionId after submission — returned by polling and webhooks.
Still subject to your corporate's normal checker approval matrix.
Common errors
{
"type": "invalid_operation_error",
"title": "This action can't be performed in the object's current state.",
"detail": "This payment session has expired. Start a new payment from your app.",
"status": 409
}| Condition | type | HTTP | Meaning |
|---|---|---|---|
| Maker not authorized | insufficient_permissions_error | 403 | makerUserId isn't an approved human maker in this corporate. |
| Beneficiary not found | object_not_found_error | 404 | beneficiaryId doesn't exist or isn't approved. |
| Idempotency key reused | idempotency_key_already_used_error | 409 | Same Idempotency-Key, different payload — use a new key. |
| Idempotency in progress | idempotency_key_in_progress_error | 409 | A request with this key is still processing — retry shortly. |
| Session expired | invalid_operation_error | 409 | The ~15-minute session window passed — create a new session. |
| Session already submitted | invalid_operation_error | 409 | Already submitted — check the checkout session status. |
| Incorrect OTP | invalid_parameters_error | 400 | Wrong verification code entered on the hosted page. |
| Locked or expired OTP | invalid_operation_error | 409 | Too many wrong codes or code expired — maker starts a new payment. |
| OTP delivery unavailable | invalid_operation_error | 409 | No email on file for maker — contact administrator. |
| Role selection denied | insufficient_permissions_error | 403 | Maker's role does not permit the selected package or debit account. |
| Amount limit exceeded | invalid_operation_error | 409 | Over the per-transaction or daily limit for the account. |
| Payout creation failed | internal_server_error | 500 | Unexpected failure during payout execution. |
