Bank accounts & withdrawals
Register a payout destination for an end user, then move INR out of OroPocket into it.
Two capabilities are involved and they are granted separately: bank manages payout destinations, and withdraw moves money. A token can hold one without the other.
List bank accounts
/partner/users/:user_code/bank-accountsbankcurl https://api.oropocket.com/partner/users/usr_xxx/bank-accounts \
-H "Authorization: Bearer oro_live_xxx"Returns the user's user_code plus a bank_accounts array. Each entry:
| Field | Type | Description |
|---|---|---|
| id | number | Pass this as bank_account_id when creating a withdrawal. |
| accountHolderName | string | Name on the account, as submitted. |
| accountType | string | "savings" or "current". |
| accountNumber | string | MASKED account number — only the last 4 digits are visible. The full number is never returned. |
| ifscCode | string | IFSC code, upper-cased. |
| bankName | string | Resolved from the IFSC — you don't submit it. |
| branchName | string | Also resolved from the IFSC. |
| upiId | string | null | UPI handle, when one was supplied. |
| isVerified | boolean | Whether the account has passed verification. |
| isPrimary | boolean | The user's default payout account. |
| status | string | Active/inactive state of the account record. |
| createdAt | string | ISO-8601 timestamp. |
Add a bank account
/partner/users/:user_code/bank-accountsbankSubmit the holder name, account type, account number and IFSC. The bank and branch are resolved from the IFSC for you. A UPI handle can be stored alongside.
| Field | Type | Description | |
|---|---|---|---|
| account_holder_name | string | required | Name on the bank account. At least 2 characters. |
| account_type | string | required | Either "savings" or "current". |
| account_number | string | required | 8–20 digits. |
| ifsc_code | string | required | 11-character IFSC in the form ABCD0123456 (4 letters, a zero, then 6 alphanumerics). bank_name and branch are auto-resolved from it. |
| upi_id | string | optional | Optional UPI handle, e.g. anya@okhdfcbank, stored alongside the account. |
curl -X POST https://api.oropocket.com/partner/users/usr_xxx/bank-accounts \
-H "Authorization: Bearer oro_live_xxx" \
-d '{
"account_holder_name": "Anya Sharma",
"account_type": "savings",
"account_number": "50100123456789",
"ifsc_code": "HDFC0000123"
}'{
"success": true,
"data": {
"user_code": "usr_xxx",
"bank_account": {
"id": 4821,
"accountHolderName": "Anya Sharma",
"accountType": "savings",
"accountNumber": "**********6789",
"ifscCode": "HDFC0000123",
"bankName": "HDFC Bank",
"branchName": "Banjara Hills",
"upiId": null,
"isVerified": false,
"isPrimary": true,
"createdAt": "2026-08-08T09:22:00.000Z"
}
}
}Remove an account
/partner/users/:user_code/bank-accounts/:idbankThe account is deactivated, not erased — it drops out of the list immediately and can no longer be used as a withdrawal destination, but past withdrawals keep pointing at the account they actually paid out to. Scoped to the user in the path, so you cannot deactivate an account belonging to someone else; an id that isn't theirs returns 404 BANK_ACCOUNT_NOT_FOUND.
curl -X DELETE https://api.oropocket.com/partner/users/usr_xxx/bank-accounts/4821 \
-H "Authorization: Bearer oro_live_xxx"Create a withdrawal
/partner/users/:user_code/withdrawalswithdraw| Field | Type | Description | |
|---|---|---|---|
| amount_inr | number | required | ₹100 minimum, ₹5,00,000 maximum per transaction. |
| bank_account_id | number | required | The id of one of the user's registered bank accounts. |
Withdrawals require approved KYC. If the user hasn't finished verification the call fails with 403 KYC_REQUIRED — run them through the KYC endpoints first. Buys, sells and receiving gifts have no such requirement.
curl -X POST https://api.oropocket.com/partner/users/usr_xxx/withdrawals \
-H "Authorization: Bearer oro_live_xxx" \
-H "Idempotency-Key: <uuid>" \
-d '{"amount_inr": 2500, "bank_account_id": 4821}'The response carries the user's code and a withdrawal object:
| Field | Type | Description |
|---|---|---|
| transaction_id | string | Reference for this payout. Quote it in support requests and match it against webhooks. |
| amount_inr | number | Gross amount requested. |
| bank_account_id | number | Destination account. |
| status | string | Always "pending" at creation. It moves on asynchronously — listen on webhooks rather than polling. |
| created_at | string | ISO-8601 timestamp. |
List withdrawals
/partner/users/:user_code/withdrawalswithdraw| Field | Type | Description | |
|---|---|---|---|
| limit | number | optional | Page size, 1–100. Defaults to 20; a value outside the range or not a number falls back to 20 (over 100 becomes 100). |
| offset | number | optional | Zero-based count of rows to skip. Defaults to 0. There is no cursor or page number. |
Returns user_code, a withdrawals array and a pagination object. Rows are ordered newest first by created_at, with ties broken by an internal id, so the order is stable. Sandbox and live return the same fields. Each row:
| Field | Type | Description |
|---|---|---|
| withdrawal_id | string | The withdrawal's id — the same value the withdrawal.* webhooks carry in data.withdrawal_id. Key on this. |
| transaction_id | string | The same value as withdrawal_id. |
| amount_inr | number | Gross amount requested. |
| fee_inr | number | Payout fee. |
| net_amount_inr | number | What reaches the bank account. |
| status | string | "pending", "processing", "completed", "failed" or "cancelled". |
| bank_account_id | number | null | Destination account. |
| refunded | boolean | true for failed and cancelled withdrawals: the amount went back to the user's INR balance. |
| failure_reason | string | null | Set on failed and cancelled withdrawals, when a reason was recorded. |
| utr | string | null | Bank reference, set once completed. |
| created_at | string | ISO-8601 timestamp. |
| completed_at | string | null | Set once completed. |
| updated_at | string | Last status change. |
pagination is { total, limit, offset, has_more }. total counts every withdrawal the user has, whatever its status; nothing is dropped from the history. You have read to the end when has_more is false. An offset past the end returns an empty array with has_more: false.
curl "https://api.oropocket.com/partner/users/usr_xxx/withdrawals?limit=20&offset=0" \
-H "Authorization: Bearer oro_live_xxx"withdrawal_id, and to recover statuses read from offset 0 until you reach rows you already hold in a final state. The withdrawal.* webhooks tell you about each change as it happens.Sandbox behaviour
In sandbox the payout rail is simulated but the state machine is real. Any amount whose paise end in .01 deterministically fails and refunds the user's test INR balance — so 2500.01 exercises your failure path on demand. Every other amount starts pending and moves to completed shortly after. Sandbox KYC still has to be approved first, which the magic PAN and Aadhaar values make instant.