API reference

Errors

Every error code the v2 API can return, with its HTTP status, meaning and what to do.

Every error has the same shape:

json
{
  "success": false,
  "error": {
    "code": "AMOUNT_OUT_OF_RANGE",
    "message": "Deposits in Kenya must be a whole number between 10 and 250000 KES.",
    "details": { "min": 10, "max": 250000, "currency": "KES" },
    "request_id": "req_9f1c2b3a4d5e6f70"
  }
}
  • Branch on code. Never branch on message or details text.
  • message is safe to log and usually safe to show.
  • Quote request_id (also in the X-Request-Id header) when you contact support.
  • When a Retry-After header is present, wait that many seconds before trying again.

How to react

Kind of error Retry?
400 validation (INVALID_REQUEST, AMOUNT_OUT_OF_RANGE, …) No. Fix the request.
401 / 403 key or scope errors No. Fix the key.
Reconnect errors (DERIV_RECONNECT_REQUIRED, …) No. Send the customer through Deriv OAuth again, or ask for a new personal access token.
409 state conflicts No. Fetch the transaction and follow its state.
429 Yes, after Retry-After.
502, 503, 504 Yes, with backoff. For deposits and withdrawals use the same idempotency key.

Never start a new deposit or withdrawal because a request timed out. Retry with the same key, or fetch the transaction.

Gateway errors

These come from the Deripay v2 API itself.

Code Status Meaning What to do
AUTH_MISSING_API_KEY 401 No API key on the request. Send Authorization: Bearer <secret key> from your server.
AUTH_INVALID_API_KEY 401 The key is unknown, revoked or malformed. Check the key in the dashboard; rotate it if it leaked.
AUTH_INSUFFICIENT_SCOPE 403 The key lacks the scope this endpoint needs. Create a key with the required scope.
AUTH_IP_NOT_ALLOWED 403 The request came from an IP outside the key's allowlist. Add your server IP to the key or clear the allowlist.
ACCOUNT_SUSPENDED 403 Your Deripay developer account is suspended. Contact support.
RATE_LIMITED 429 Too many requests for this key. Back off for Retry-After seconds.
INVALID_REQUEST 400 The body or query failed validation. Fix the fields listed in error.details.
IDEMPOTENCY_KEY_REQUIRED 400 A money-moving request was sent without Idempotency-Key. Send a unique key per logical attempt and reuse it on retries.
IDEMPOTENCY_KEY_REUSED 409 The idempotency key was already used for a different request. Use a new key for a new attempt.
DERIV_APP_NOT_REGISTERED 400 client_id is not one of your registered Deriv apps. Add the Deriv OAuth client ID under Deriv apps in the dashboard.
CONNECTION_NOT_FOUND 404 No connection with this ID belongs to your account. Use the con_… ID returned when the customer connected.
TRANSACTION_NOT_FOUND 404 No transaction with this ID belongs to your account. Use the txn_… ID returned when the transaction was created.
MARKET_NOT_AVAILABLE 400 The country is not an open market. Call GET /markets and offer only the countries it returns.
INVALID_PHONE_NUMBER 400 The phone number is not a valid mobile number for the market. Collect a number in the market's own country.
OPERATOR_REQUIRED 400 The market has several mobile money networks and none was chosen. Also returned when operator is not one of the market's codes. Send operator using a code from GET /markets.
AMOUNT_OUT_OF_RANGE 400 The amount is outside the market's current limits. details has the limits. Read limits from GET /rates?country= before submitting.
DEPOSITS_PAUSED 503 Deposits in this market are temporarily unavailable. Disable the deposit button and try again later.
UPSTREAM_UNAVAILABLE 502 Deripay's payment network did not answer in time. Retry with the same idempotency key, with backoff.
INTERNAL_ERROR 500 Unexpected gateway error. Retry; contact support with the request_id if it persists.
TRANSACTION_NOT_READY 409 An OTP, resend or confirm was sent before the transaction reached the payment network. Fetch the transaction again shortly, then retry.
UPSTREAM_ERROR 502 The payment network failed without a specific code. Retry with backoff; contact support if it persists.
API_VERSION_RETIRED 410 A request was sent to /api/v1. Move to /api/v2 with a new key. See Changelog.

Payment network errors

These come from Deripay's payment network and are passed through unchanged, with their own HTTP status. Transaction errors carry the prefix of the operation: DEPOSIT_ or WITHDRAWAL_. Connection errors are not prefixed. The same base code can appear in either form, for example DERIV_RECONNECT_REQUIRED and DEPOSIT_DERIV_RECONNECT_REQUIRED. Handle both.

Connections and Deriv authorization

Code Status Meaning What to do
DERIV_AUTH_FAILED 401 or 403 (varies) Deriv did not accept the access token, or it lacks a required scope such as payment. Send the customer through Deriv OAuth again, or ask for a new personal access token, with trade account_manage payment.
DERIV_RECONNECT_REQUIRED (or prefixed) 401 Deriv has rejected the stored token. Reconnect the customer, then retry.
DERIV_SESSION_EXPIRED (or prefixed) 401 Older form of DERIV_RECONNECT_REQUIRED. Treat it the same way.
DERIV_REFRESH_UNAVAILABLE (or prefixed) 503 Token state is temporarily unavailable. Retry with backoff. Do not reconnect.
DERIV_ACCOUNT_MISMATCH 400 preferred_account_id was not among the customer's Deriv accounts. Fix the account id and connect again.
DERIV_ACCOUNT_NOT_ELIGIBLE (or prefixed) 400 or 412 No real USD Deriv account is available. Ask the customer to connect with a real USD account.
NICKNAME_NOT_FOUND, DEPOSIT_NICKNAME_NOT_FOUND 412 Deriv has not given the customer a nickname, which the payment agent transfer needs. Ask the customer to set up their nickname in Deriv, then retry.
DEPOSIT_SESSION_APP_MISMATCH, WITHDRAWAL_SESSION_APP_MISMATCH 401 The stored connection cannot be used for this operation. Reconnect the customer.
DERIV_CONNECTION_NOT_FOUND (or prefixed) 404 The payment network no longer knows this connection. Reconnect the customer with POST /connections.

Deposits

Code Status Meaning What to do
DEPOSIT_INVALID_DEPOSIT_AMOUNT 400 The amount was refused by the payment network. Refresh limits from GET /rates and ask again.
DEPOSIT_INVALID_REQUEST 400 Required data was missing or malformed. Fix the request. Do not retry blindly.
DEPOSIT_OTP_INVALID 400 The OTP is badly formed. Ask the customer for the code again.
DEPOSIT_OTP_INVALID 409 The deposit is not waiting for an OTP, or the transaction is a withdrawal. Fetch the transaction and follow customer_action.
DEPOSIT_OTP_INVALID 429 Too many OTP attempts (at most five per deposit). Stop. Fetch the transaction; it will end in failed or move on.

Other provider failures on a deposit can use other DEPOSIT_… codes. Fetch the transaction before deciding what to tell the customer.

Withdrawals

Code Status Meaning What to do
WITHDRAWAL_ALREADY_ACTIVE 409 The customer, Deriv login or phone already has a withdrawal in progress. Fetch the existing withdrawal and wait for it to finish.
WITHDRAWAL_COOLDOWN 429 A cooldown applies after a recent withdrawal. Wait for Retry-After, then retry the same request.
WITHDRAWAL_VELOCITY_LIMIT 429 Too many withdrawals in a rolling window. Wait for Retry-After. Do not switch phone or account to get round it.
WITHDRAWAL_INVALID_WITHDRAWAL_AMOUNT 400 The amount was refused by the payment network, for example above the main wallet balance. Check GET /connections/{id}/balance and GET /rates.
WITHDRAWAL_INVALID_OTP varies The Deriv verification code is wrong or expired. Fetch the transaction. If it is awaiting_code, let the customer re-enter or resend the code.
WITHDRAWAL_INVALID_TRANSACTION_STATE 409 The withdrawal cannot do that from its current state, or the transaction is a deposit. Fetch the transaction and update your screen.
WITHDRAWAL_INVALID_REQUEST 400 Required data was missing or malformed. Fix the request. Do not retry blindly.
WITHDRAWAL_FORBIDDEN 403 The transaction does not belong to this connection. Check the ids you stored.

Errors in the event stream

GET /transactions/{id}/events can send an error event inside a 200 stream:

text
event: error
data: {"code":"MONITOR_UPSTREAM_ERROR","message":"Live status is unavailable. Poll GET /api/v2/transactions/{id}."}

Stop streaming and poll GET /transactions/{id}. See Transactions.

Asynchronous failures

A transaction can fail after the API has answered 201. That is not an error response: the transaction moves to state: "failed" with a status_message, and you receive transaction.failed. Show status_message to the customer.