Reference

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

GET/partner/users/:user_code/bank-accountsbank
curl 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:

FieldTypeDescription
idnumberPass this as bank_account_id when creating a withdrawal.
accountHolderNamestringName on the account, as submitted.
accountTypestring"savings" or "current".
accountNumberstringMASKED account number — only the last 4 digits are visible. The full number is never returned.
ifscCodestringIFSC code, upper-cased.
bankNamestringResolved from the IFSC — you don't submit it.
branchNamestringAlso resolved from the IFSC.
upiIdstring | nullUPI handle, when one was supplied.
isVerifiedbooleanWhether the account has passed verification.
isPrimarybooleanThe user's default payout account.
statusstringActive/inactive state of the account record.
createdAtstringISO-8601 timestamp.
Account numbers are always masked on the Partner API. The full number is never returned, on any endpoint, in either mode — so don't design a UI that expects to echo it back.

Add a bank account

POST/partner/users/:user_code/bank-accountsbank

Submit 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.

FieldTypeDescription
account_holder_namestringrequiredName on the bank account. At least 2 characters.
account_typestringrequiredEither "savings" or "current".
account_numberstringrequired8–20 digits.
ifsc_codestringrequired11-character IFSC in the form ABCD0123456 (4 letters, a zero, then 6 alphanumerics). bank_name and branch are auto-resolved from it.
upi_idstringoptionalOptional 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"
  }'
Response
{
  "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

DELETE/partner/users/:user_code/bank-accounts/:idbank

The 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"
Removing the primary account does not promote another one. If the user has other accounts, set the one you want as primary before removing the old one, or their next withdrawal has no default destination.

Create a withdrawal

POST/partner/users/:user_code/withdrawalswithdraw
FieldTypeDescription
amount_inrnumberrequired₹100 minimum, ₹5,00,000 maximum per transaction.
bank_account_idnumberrequiredThe 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:

FieldTypeDescription
transaction_idstringReference for this payout. Quote it in support requests and match it against webhooks.
amount_inrnumberGross amount requested.
bank_account_idnumberDestination account.
statusstringAlways "pending" at creation. It moves on asynchronously — listen on webhooks rather than polling.
created_atstringISO-8601 timestamp.
Withdrawals are validated against your sub-wallet claim, not the user's whole OroPocket balance. A user may hold plenty overall and still be rejected because only part of it came in through your integration — see Sub-wallets.

List withdrawals

GET/partner/users/:user_code/withdrawalswithdraw
FieldTypeDescription
limitnumberoptionalPage size, 1–100. Defaults to 20; a value outside the range or not a number falls back to 20 (over 100 becomes 100).
offsetnumberoptionalZero-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:

FieldTypeDescription
withdrawal_idstringThe withdrawal's id — the same value the withdrawal.* webhooks carry in data.withdrawal_id. Key on this.
transaction_idstringThe same value as withdrawal_id.
amount_inrnumberGross amount requested.
fee_inrnumberPayout fee.
net_amount_inrnumberWhat reaches the bank account.
statusstring"pending", "processing", "completed", "failed" or "cancelled".
bank_account_idnumber | nullDestination account.
refundedbooleantrue for failed and cancelled withdrawals: the amount went back to the user's INR balance.
failure_reasonstring | nullSet on failed and cancelled withdrawals, when a reason was recorded.
utrstring | nullBank reference, set once completed.
created_atstringISO-8601 timestamp.
completed_atstring | nullSet once completed.
updated_atstringLast 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"
Offsets count from the newest withdrawal, so a withdrawal created while you page shifts later pages by one. De-duplicate on 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.