Deposits
Collect mobile money from a customer and credit their Deriv account.
A deposit collects money from the customer's phone and credits their Deriv account through a payment agent transfer. The API answers at once with a transaction. Settlement happens afterwards, so follow the transaction to a final state.
Create a deposit
POST /api/v2/deposits
Idempotency-Key: dep_user_1842_order_0001Scope: deposits. Rate limit: 30 per minute. An idempotency key is required.
curl https://deriapi.site/api/v2/deposits \
-H "Authorization: Bearer $DERIPAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: dep_user_1842_order_0001" \
-d '{
"connection_id": "con_6720f1a2b3c4d5e6f7a8",
"country": "UG",
"amount": 50000,
"phone_number": "0772123456",
"operator": "mtn"
}'| Field | Required | Type | Description |
|---|---|---|---|
connection_id |
yes | string | The customer's con_… id. |
country |
no | string | Market code from GET /markets. Default KE. |
amount |
yes | integer | What the customer pays, in whole units of the market currency. |
phone_number |
yes | string, 6-20 | The paying mobile number, 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 to credit, 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:
- Call
GET /rates?country=…and keepamountbetweendeposit_limits.minanddeposit_limits.max. - If
deposits_availableisfalse, do not offer deposits in that market.
Response: 201 with a transaction. A replay with the same idempotency key returns 200, the same transaction and the header Idempotent-Replayed: true. See Idempotency.
{
"success": true,
"data": {
"id": "txn_6720f9e8d7c6b5a4f3e2",
"object": "transaction",
"type": "deposit",
"state": "pending",
"status": "DEPOSIT_HONEYCOIN_PENDING",
"status_message": null,
"connection_id": "con_6720f1a2b3c4d5e6f7a8",
"country": "UG",
"currency": "UGX",
"operator": "mtn",
"amount": 50000,
"amount_usd": 12.82,
"phone_number": "2567•••456",
"customer_action": null,
"terminal": false,
"created_at": "2026-11-03T09:00:00.000Z",
"updated_at": "2026-11-03T09:00:01.000Z"
}
}Store data.id before you show the next screen.
Kenya: M-Pesa prompt
In Kenya the customer gets an M-Pesa STK prompt on their phone straight away. Tell them to check their phone and enter their M-Pesa PIN. You do nothing else. The transaction goes from pending to processing once M-Pesa confirms, then to completed once Deriv has been credited.
Tanzania and Uganda: customer actions
Outside Kenya the provider can ask the customer for an extra step. The transaction then has state: "action_required" and a customer_action object:
{
"state": "action_required",
"status": "DEPOSIT_HONEYCOIN_ACTION_REQUIRED",
"customer_action": {
"step": "otp",
"redirect_url": null,
"instructions": ["message: Enter the code sent to your phone"]
}
}customer_action.step |
What to do |
|---|---|
otp |
Ask the customer for the code their network sent them and submit it with POST /deposits/{id}/otp. |
redirect |
Send the customer to redirect_url (always an https URL from the provider) to approve the payment, then keep following the transaction. |
ussd |
Show instructions so the customer can dial the code on their phone. Keep following the transaction. |
unsupported |
A step the API cannot complete. Show instructions if present and contact support. |
instructions is a list of short strings from the provider. It can be empty.
The step can change while the state stays action_required. Read customer_action from the latest transaction every time. Finishing a step is not proof of payment: wait for completed.
You receive a transaction.action_required webhook when a deposit enters this state.
Submit an OTP
POST /api/v2/deposits/{id}/otpScope: deposits. Rate limit: 30 per minute. No idempotency key.
curl https://deriapi.site/api/v2/deposits/txn_6720f9e8d7c6b5a4f3e2/otp \
-H "Authorization: Bearer $DERIPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "otp": "012345" }'| Field | Required | Description |
|---|---|---|
otp |
yes | The code from the customer's network, 3 to 12 characters. Send it as a string so leading zeros survive. |
Returns 200 with the current transaction. It can still be action_required or pending. Keep following it.
- A badly formed code returns
400 DEPOSIT_OTP_INVALID. - A code sent when the deposit is not waiting for one, or for a withdrawal, returns
409 DEPOSIT_OTP_INVALID. - Too many tries returns
429 DEPOSIT_OTP_INVALID. The provider allows at most five submissions per deposit. - If the deposit has not reached the payment network yet, you get
409 TRANSACTION_NOT_READY. Fetch it again in a moment.
Do not resend a code automatically after a timeout. Fetch the transaction first.
Deposit states
state |
Meaning | What to show |
|---|---|---|
pending |
Created, or waiting for the customer to approve on their phone. | "Check your phone to approve the payment." |
action_required |
Waiting for an OTP, redirect or USSD step. | The step from customer_action. |
processing |
Payment received. Deriv is being credited. | "Payment received, crediting your account." |
completed |
Deriv account credited. Final. | Success. |
failed |
The deposit did not go through. Final. | status_message, and offer to try again. |
The raw status values behind each state are listed in Transactions.
Errors
| Code | Status | Meaning |
|---|---|---|
IDEMPOTENCY_KEY_REQUIRED |
400 | No Idempotency-Key. |
IDEMPOTENCY_KEY_REUSED |
409 | The key was used for a different request. |
CONNECTION_NOT_FOUND |
404 | Unknown connection_id. |
MARKET_NOT_AVAILABLE |
400 | country is not open. |
INVALID_PHONE_NUMBER |
400 | The number does not belong to the market. |
OPERATOR_REQUIRED |
400 | The market needs operator, or the code is not one of its operators. |
AMOUNT_OUT_OF_RANGE |
400 | Outside the current limits. error.details has min, max and currency. |
DEPOSITS_PAUSED |
503 | Deposits in this market are temporarily off. |
DEPOSIT_NICKNAME_NOT_FOUND |
412 | The customer has no Deriv nickname for the transfer. They must set one in Deriv. |
DEPOSIT_DERIV_RECONNECT_REQUIRED |
401 | The customer must reconnect Deriv. |
UPSTREAM_UNAVAILABLE |
502 | No answer in time. Retry with the same idempotency key. |
The full list is in Errors.