API Reference —
Transactions, Payouts & Webhooks
Integration guide for partners using the UPS payments API. Collect payments over UPI, bank transfer or cash, send money out through payouts, and receive a callback for every terminal state change.
Conventions#
Money
All money values are decimal strings in INR — for example "490.00".
Timestamps
Timestamps are ISO-8601 with microseconds and a trailing Z (UTC), e.g. 2026-04-29T14:43:10.245112Z.
Base URL
Replace {{BASE}} with the API base URL given to you during onboarding.
Authentication#
Every request carries one of two headers. Most endpoints use your long-lived company key; the hosted-pay polling endpoint uses the short-lived payment session key returned when a transaction is created.
Transactions (collect a payment)#
Collect a payment. Create an intent with initiate_trx, show the customer the QR / intent link or destination account, then learn the outcome by polling the status endpoints or by receiving the transaction webhook.
1.1 Initiate a transaction#
/api/transactions/initiate_trx/
Auth · X-UPS-KEY
Create a new payment intent.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
| amount | float | Yes | Amount the customer pays. |
| reference_id | string | Yes | Your idempotency / order id. Echoed back in status responses and webhooks as reference_id. |
| account_type | string | No | UPI (default), RTGS_IMPS_NEFT, or CASH. |
| source | string | No | Free-form tag (default "API"). |
| name | string | No | End-user name (default "API"). |
| redirect_url | string | No | URL the user is sent to after payment. |
api_key in the response is the payment session key (X-API-KEY) used by the polling endpoint in §1.3. It expires in 10 minutes.
For account_type=CASH / RTGS_IMPS_NEFT the response also returns account_name, account_number, account_ifsc and bank_name so you can show the destination account to the customer.
Errors
Bad Request — no eligible account is currently available, e.g. {"message": "No active account ... please try again later."}
{
"amount": 500,
"reference_id": "ORD-1029",
"account_type": "UPI",
"source": "checkout-web",
"name": "Riya Sharma"
}
{
"trx_id": 4711,
"qr_code": "data:image/png;base64,iVBORw0KGgo...",
"upi_intent": "upi://pay?pa=...",
"amount": 500,
"api_key": "9c4e1c2b9d3f8a1e6f02d4c2a7e6c0a3df9b5f17",
"expires_at": "2026-04-29T14:49:16.000Z",
"expire_in": 10,
"auto_approve_enable": true,
"redirect_url": "{{BASE}}/pay/9c4e1c2b9d3f8a1e6f02d4c2a7e6c0a3df9b5f17"
}
1.2 Transaction status by reference#
/api/transactions/{reference_number}/status/
Auth · X-UPS-KEY
Pull the status of a transaction by your reference_id.
Response fields
| Field | Notes |
|---|---|
| id | Our transaction id (the trx_id from §1.1). |
| trx_status | One of pending approved rejected |
| is_verified | Whether the payment was verified against the bank feed. |
| utr | Bank UTR for the credit, null until settled. |
| trx_fees | Fee deducted, decimal string in INR. |
| net_amount | Amount credited to you after fees. |
GET /api/transactions/ORD-1029/status/
X-UPS-KEY: Api-Key <company key>
{
"status": "success",
"message": "success",
"data": {
"id": 4711,
"amount": 500.0,
"is_verified": true,
"source": "checkout-web",
"rejected": false,
"updated_at": "2026-04-29T14:43:10.245112Z",
"created_at": "2026-04-29T14:39:16.821000Z",
"reference_id": "ORD-1029",
"utr": "123456789012",
"trx_status": "approved",
"account_type": "UPI",
"currency": "INR",
"trx_fees": "10.00",
"net_amount": "490.00"
}
}
1.3 Transaction status by trx_id#
/api/transactions/trx_status/
Auth · X-API-KEY
Same status payload as §1.2, keyed by trx_id instead of reference id. Used by hosted-pay polling.
This endpoint authenticates with the payment session key (X-API-KEY) from §1.1 — not your company key. The session key expires 10 minutes after the transaction is created.
{ "trx_id": 4711 }
{ "status": "success" } // or "rejected" or false
Payouts (send money out)#
Send money out over UPI, bank transfer or cash. Create the request with initiate_payout_request, then reconcile with the status endpoint or the payout webhook.
2.1 Initiate a payout request#
/api/payout-requests/initiate_payout_request/
Auth · X-UPS-KEY
Create a payout request. The required fields depend on requested_type.
IP allowlist. If your account has an IP allowlist configured, this endpoint only accepts requests originating from one of those IP addresses. Requests from any other IP are rejected with 403 Forbidden {"detail": "IP address <ip> is not allowed to access this API."}. Share the static IP(s) of your servers during onboarding. If no allowlist is configured, requests are accepted from any IP.
Common fields
| Field | Type | Required | Notes |
|---|---|---|---|
| requested_type | string | Yes | One of UPI, RTGS_IMPS, CASH. |
| amount | float | Yes | Must be > 0. |
| first_name | string | Yes | Beneficiary first name. |
| account_holder_name | string | Yes | Beneficiary full name on the receiving account. |
| reference_id | string | Optional | Your unique reference id. Auto-generated if omitted; must be unique. |
| bank_name | string | Optional | Free text. |
| note | string | Optional | Up to 1000 chars. |
Per-type required fields
In addition to the common block above.
| requested_type | Required extra fields | Forbidden fields |
|---|---|---|
| UPI | upi_address | account_number, ifsc_code |
| RTGS_IMPS | account_number, ifsc_code | upi_address |
| CASH | (none) | upi_address, account_number, ifsc_code |
A payout is always settled on the rail you originally requested — if you ask for UPI, you will receive a UPI settlement; the transfer_type on the webhook will match requested_type.
Success · 201 Created
Returns the full payout row. It includes a reference_id (e.g. "PO-7313D4583375") — a unique identifier we generate on our end for this payout. Use it to reconcile the webhook callback with the row you created. The row also returns is_transferred=false, transfer_type=null, reference_number=null, trx_fees="0" and net_amount="0". Those are filled in once the payout is settled and a webhook is delivered (see §3.2).
Validation errors
If you submit a field that does not belong to the chosen requested_type, the error for that field is "Not allowed for requested_type=<...>."
{
"requested_type": "UPI",
"first_name": "Aditya",
"account_holder_name": "Aditya Verma",
"upi_address": "aditya@okicici",
"amount": 1500
}
{
"requested_type": "RTGS_IMPS",
"first_name": "Aditya",
"account_holder_name": "Aditya Verma",
"account_number": "0123456789012",
"ifsc_code": "HDFC0000123",
"bank_name": "HDFC Bank",
"amount": 25000
}
{
"requested_type": "CASH",
"first_name": "Aditya",
"account_holder_name": "Aditya Verma",
"amount": 800,
"note": "Office walk-in"
}
{
"id": 911,
"reference_id": "PO-7313D4583375",
"requested_type": "UPI",
"first_name": "Aditya",
"account_holder_name": "Aditya Verma",
"upi_address": "aditya@okicici",
"amount": 1500,
"is_transferred": false,
"transfer_type": null,
"reference_number": null,
"trx_fees": "0",
"net_amount": "0"
}
{
"upi_address": ["This field is required for requested_type=UPI."],
"amount": ["Must be greater than 0."]
}
2.2 Payout status#
/api/payout-requests/status/
Auth · X-UPS-KEY
Pull the current status of a payout request. Look it up by either your reference_id or the payout_request_id (the id returned at creation). Supply at least one.
Query parameters
| Parameter | Notes |
|---|---|
| reference_id | The payout reference_id, e.g. PO-7313D4583375. |
| payout_request_id | The numeric id returned when the payout was created. |
payout_status is one of pending transferred rejected
Errors
Bad Request — neither reference_id nor payout_request_id supplied — {"status": "failed", "message": "Provide reference_id or payout_request_id."}
Not Found — no payout matches for your account — {"status": "failed", "message": "Payout request not found"}
GET /api/payout-requests/status/?reference_id=PO-7313D4583375
X-UPS-KEY: Api-Key <company key>
GET /api/payout-requests/status/?payout_request_id=911
X-UPS-KEY: Api-Key <company key>
{
"status": "success",
"message": "success",
"data": {
"id": 911,
"reference_id": "PO-7313D4583375",
"amount": 1500.0,
"trx_fees": "15.00",
"net_amount": "1515.00",
"transfer_type": "UPI",
"reference_number": "123456789012",
"is_transferred": true,
"is_rejected": false,
"note": "Settled",
"payout_status": "transferred",
"currency": "INR",
"original_request": {
"reference_id": "PO-7313D4583375",
"requested_type": "UPI",
"amount": "1500.0",
"first_name": "Aditya",
"account_holder_name": "Aditya Verma",
"upi_address": "aditya@okicici",
"account_number": null,
"ifsc_code": null,
"bank_name": null,
"reference_number": "123456789012",
"mobile_number": null,
"location": null
},
"created_at": "2026-04-29T14:39:16.821000Z",
"updated_at": "2026-04-29T14:43:10.245112Z"
}
}
Webhooks#
The platform calls a partner-supplied URL for each terminal status change. Webhook URLs are configured per type during onboarding — three URLs for inbound transactions (UPI / RTGS_IMPS_NEFT / CASH) and three for payouts.
HTTP details
Method & body
Method: POST
Body: application/json
Timeouts
Connect timeout: 2 s · Read timeout: 5 s
Retries
Success = any 2xx response. Anything else is retried once after 20 s, and then marked as failed.
Signing
Webhooks are not signed today — host the receiving endpoint over HTTPS at a secret URL.
3.1 Transaction webhook#
<your URL for the transaction's account_type>
Fires when a transaction reaches a terminal state (approved or rejected). Routed to the URL configured for the transaction's account_type.
trx_status is one of approved rejected pending — pending transactions are not webhooked.
{
"id": 4711,
"amount": 500.0,
"is_verified": true,
"source": "checkout-web",
"rejected": false,
"updated_at": "2026-04-29T14:43:10.245112Z",
"created_at": "2026-04-29T14:39:16.821000Z",
"reference_id": "ORD-1029",
"utr": "123456789012",
"trx_status": "approved",
"account_type": "UPI",
"currency": "INR",
"trx_fees": "10.00",
"net_amount": "490.00"
}
{
"id": 4712,
"amount": 1200.0,
"is_verified": false,
"source": "checkout-web",
"rejected": true,
"updated_at": "2026-04-29T14:50:01.500000Z",
"created_at": "2026-04-29T14:49:30.000000Z",
"reference_id": "ORD-1031",
"utr": null,
"trx_status": "rejected",
"account_type": "UPI",
"currency": "INR",
"trx_fees": "0.00",
"net_amount": "0.00"
}
3.2 Payout webhook#
<your URL for the payout's transfer_type>
Fires when a payout is settled (transferred) or rejected. Routed to the URL configured for the payout's transfer_type.
The payload includes a top-level reference_id (the unique id we returned when you created the payout) plus an original_request block that echoes the values you sent in §2.1, so you can match the callback to the row you created without having to track our id.
payout_status is one of transferred rejected pending — pending payouts are not webhooked.
{
"id": 911,
"reference_id": "PO-7313D4583375",
"amount": 1500.0,
"trx_fees": "15.00",
"net_amount": "1515.00",
"transfer_type": "UPI",
"reference_number": "BANKREF-998877",
"is_transferred": true,
"is_rejected": false,
"note": "Sent via PhonePe",
"payout_status": "transferred",
"currency": "INR",
"original_request": {
"requested_type": "UPI",
"amount": "1500.00",
"first_name": "Aditya",
"account_holder_name": "Aditya Verma",
"upi_address": "aditya@okicici",
"account_number": null,
"ifsc_code": null,
"bank_name": null
},
"created_at": "2026-04-29T13:20:01.000000Z",
"updated_at": "2026-04-29T14:11:12.500000Z"
}
{
"id": 912,
"reference_id": "PO-92D2D976BE49",
"amount": 25000.0,
"trx_fees": "62.50",
"net_amount": "25062.50",
"transfer_type": "RTGS_IMPS",
"reference_number": "NEFT202604290003",
"is_transferred": true,
"is_rejected": false,
"note": null,
"payout_status": "transferred",
"currency": "INR",
"original_request": {
"requested_type": "RTGS_IMPS",
"amount": "25000.00",
"first_name": "Aditya",
"account_holder_name": "Aditya Verma",
"upi_address": null,
"account_number": "0123456789012",
"ifsc_code": "HDFC0000123",
"bank_name": "HDFC Bank"
},
"created_at": "2026-04-29T12:50:00.000000Z",
"updated_at": "2026-04-29T13:55:04.000000Z"
}
{
"id": 913,
"reference_id": "PO-AE9C274DF63C",
"amount": 500.0,
"trx_fees": "0.00",
"net_amount": "0.00",
"transfer_type": "UPI",
"reference_number": null,
"is_transferred": false,
"is_rejected": true,
"note": "Beneficiary VPA invalid",
"payout_status": "rejected",
"currency": "INR",
"original_request": {
"requested_type": "UPI",
"amount": "500.00",
"first_name": "Riya",
"account_holder_name": "Riya Sharma",
"upi_address": "riya@xyz",
"account_number": null,
"ifsc_code": null,
"bank_name": null
},
"created_at": "2026-04-29T13:00:00.000000Z",
"updated_at": "2026-04-29T13:30:00.000000Z"
}
Errors#
| HTTP | Body shape | Meaning |
|---|---|---|
| 400 | {"<field>": ["<message>"]} or {"message": "..."} | Validation failure. |
| 401 | {"detail": "..."} | Missing or invalid auth header. |
| 403 | {"detail": "..."} | The endpoint does not allow your auth scheme. |
| 404 | {"detail": "...not found"} | Resource does not exist. |
| 5xx | {"message": "Please try again later."} | Transient server error — safe to retry idempotently using the same reference_id. |
Status values#
trx_status
Transactions · §1.2, §1.3, §3.1
pending Awaiting payment — never webhooked.
approved Terminal · payment received and verified.
rejected Terminal · payment failed or was declined.
payout_status
Payouts · §2.2, §3.2
pending Queued for settlement — never webhooked.
transferred Terminal · money sent on the requested rail.
rejected Terminal · see note for the reason.