Withdrawals
Take USD from a customer's Deriv account and pay it out to their phone.
A withdrawal has two steps. You start it with an amount in USD. Deriv sends the customer a verification code. You submit the code, Deriv releases the money, and Deripay pays it out to the customer's phone in the local currency.
1. Start a withdrawal
POST /api/v2/withdrawals
Idempotency-Key: wd_user_1842_req_0001Scope: withdrawals. Rate limit: 20 per minute. An idempotency key is required.
curl https://deriapi.site/api/v2/withdrawals \
-H "Authorization: Bearer $DERIPAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: wd_user_1842_req_0001" \
-d '{
"connection_id": "con_6720f1a2b3c4d5e6f7a8",
"country": "KE",
"amount_usd": 10,
"phone_number": "0712345678"
}'| Field | Required | Type | Description |
|---|---|---|---|
connection_id |
yes | string | The customer's con_… id. |
country |
no | string | Market the payout goes to. Default KE. |
amount_usd |
yes | number | Amount to take from Deriv, in USD, at most two decimals. |
phone_number |
yes | string, 6-20 | The number to pay out to, in the market's own country. |
operator |
conditionally | string | Operator code from GET /markets. Required when the market has requires_operator: true. Ignored in Kenya. |
account_login_id |
no | string | Deriv login id, for example CR1234567. Default: the connection's selected account. |
idempotency_key |
conditionally | string | Use this only if you cannot send the Idempotency-Key header. |
Before you call it:
- Read
withdraw_limits_usdfromGET /rates?country=…. - Read the main wallet balance from
GET /connections/{id}/balance. The amount must not be more thanbalance. - Read the payout number back to the customer. Most networks cannot check the recipient's name.
Response: 201 with a transaction in state: "awaiting_code". amount is the estimated local payout and amount_usd is what leaves Deriv. Store data.id before you ask for the code.
{
"success": true,
"data": {
"id": "txn_6720f9e8d7c6b5a4f3e2",
"object": "transaction",
"type": "withdrawal",
"state": "awaiting_code",
"status": "WITHDRAWAL_EMAIL_SENT",
"status_message": null,
"connection_id": "con_6720f1a2b3c4d5e6f7a8",
"country": "KE",
"currency": "KES",
"operator": "mpesa",
"amount": 1250,
"amount_usd": 10,
"phone_number": "2547•••678",
"customer_action": null,
"terminal": false,
"created_at": "2026-11-03T09:00:00.000Z",
"updated_at": "2026-11-03T09:00:02.000Z"
}
}2. The customer gets a code
Deriv sends a verification code to the contact registered on the customer's Deriv account, usually by email. Ask the customer to enter it in your form. Keep the code as a string: it can start with zero. Deriv codes are currently six digits.
Resend the code
POST /api/v2/withdrawals/{id}/resendScope: withdrawals. Rate limit: 10 per minute. No body.
curl -X POST https://deriapi.site/api/v2/withdrawals/txn_6720f9e8d7c6b5a4f3e2/resend \
-H "Authorization: Bearer $DERIPAY_API_KEY"Works while the state is awaiting_code. Returns the same transaction. Use the newest code. Once the withdrawal has moved on, resend returns 409 WITHDRAWAL_INVALID_TRANSACTION_STATE. Respect Deriv's own limits on how often a code can be sent.
3. Confirm with the code
POST /api/v2/withdrawals/{id}/confirmScope: withdrawals. Rate limit: 20 per minute.
curl https://deriapi.site/api/v2/withdrawals/txn_6720f9e8d7c6b5a4f3e2/confirm \
-H "Authorization: Bearer $DERIPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "verification_code": "048213" }'| Field | Required | Description |
|---|---|---|
verification_code |
yes | The code Deriv sent the customer, as a string. |
Returns 200 with the transaction, usually in state: "processing". The money is not on the customer's phone yet. Show success only when the state is completed.
If the code is wrong or expired you get an error such as WITHDRAWAL_INVALID_OTP. Fetch the transaction: if it is back in awaiting_code, let the customer re-enter the code or resend it, on the same transaction. Never apply a code to a different transaction.
If confirm times out, fetch the transaction before retrying. Confirming a withdrawal that already completed returns the completed transaction.
Processing until paid out
After a correct code the withdrawal stays in processing while Deriv releases the funds and the mobile money payout is sent. A payout that fails on the first try is retried by Deripay. That shows as raw status WITHDRAWAL_B2C_FAILED, which is still processing, not final.
Do not start a new withdrawal to "fix" a slow one. Once Deriv has released the money, a second withdrawal would take money from the customer again. Wait for completed or failed, and contact support with the transaction id if it stays in processing longer than you expect.
One active withdrawal at a time
Deripay allows one active withdrawal per customer, Deriv login and payout number. A different idempotency key does not get round it.
| Status | Code | What to do |
|---|---|---|
| 409 | WITHDRAWAL_ALREADY_ACTIVE |
Fetch the withdrawal you already have and wait for it to finish. Do not start another. |
| 429 | WITHDRAWAL_COOLDOWN |
Wait for the Retry-After header (seconds), then try again. |
| 429 | WITHDRAWAL_VELOCITY_LIMIT |
Too many withdrawals in a short period. Wait for Retry-After. Do not change the phone or account to get round it. |
When one of these refuses a request, nothing was created, so you can retry the same logical request later with the same idempotency key.
Withdrawal states
state |
Meaning | What to show |
|---|---|---|
awaiting_code |
Waiting for the customer's Deriv code. | The code form, and a resend link. |
processing |
Code accepted. Deriv release and payout under way. | "Your withdrawal is on its way." |
completed |
Money sent to the phone. Final. | Success. |
failed |
The withdrawal did not go through. Final. | status_message. |
pending |
The request was not confirmed by the payment network yet (raw status SUBMISSION_PENDING). |
A spinner. Retry POST /withdrawals with the same idempotency key. |
Errors
| Code | Status | Meaning |
|---|---|---|
IDEMPOTENCY_KEY_REQUIRED |
400 | No Idempotency-Key. |
IDEMPOTENCY_KEY_REUSED |
409 | The key was used for a different request. |
AMOUNT_OUT_OF_RANGE |
400 | Outside the current USD limits. error.details has min_usd and max_usd. |
INVALID_PHONE_NUMBER |
400 | The number does not belong to the market. |
OPERATOR_REQUIRED |
400 | The market needs operator. |
WITHDRAWAL_ALREADY_ACTIVE |
409 | Another withdrawal is in progress. |
WITHDRAWAL_COOLDOWN |
429 | Wait for Retry-After. |
WITHDRAWAL_INVALID_OTP |
varies | Wrong or expired code. |
WITHDRAWAL_INVALID_TRANSACTION_STATE |
409 | The withdrawal cannot do that from its current state. |
DERIV_RECONNECT_REQUIRED |
401 | The customer must reconnect Deriv. Also sent in a prefixed form. |
The full list is in Errors.