API reference

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

http
POST /api/v2/deposits
Idempotency-Key: dep_user_1842_order_0001

Scope: deposits. Rate limit: 30 per minute. An idempotency key is required.

bash
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:

  1. Call GET /rates?country=… and keep amount between deposit_limits.min and deposit_limits.max.
  2. If deposits_available is false, 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.

json
{
  "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:

json
{
  "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

http
POST /api/v2/deposits/{id}/otp

Scope: deposits. Rate limit: 30 per minute. No idempotency key.

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