Skip to content

Errors

One envelope for every response, a stable code in it, and what to do about each one.

Every response, success or failure, is the same envelope. Branch on the HTTP status and on code; message is for people and can change.

 {
  "success": true,
  "code": "OK",
  "message": "Transaction accepted",
  "data": { "reference": "txn_01a0d8dfa6b87d10a3e54c2b9f86d1e4", "status": "queued" }
}

An error has success: false, the code and a message. data is null:

 {
  "success": false,
  "code": "WALLET_INSUFFICIENT_FUNDS",
  "message": "Insufficient wallet balance: available balance (0.00 NGN) is less than required amount (100.00 NGN)",
  "data": null
}

A validation error names each field that failed in data.errors:

 {
  "success": false,
  "code": "VALIDATION_FAILED",
  "message": "Invalid request",
  "data": {
    "errors": [
      {
        "field": "destination",
        "message": "must be a valid 11-digit Nigerian phone number (e.g. 08031234567 or +2348031234567)"
      }
    ]
  }
}

Every response also carries an X-Request-Id header. Quote it when you ask support about a request.

Codes

CodeStatusWhat it meansWhat to do
OK2xxSuccess.
VALIDATION_FAILED400A field is missing or wrong, the body is not one JSON object, or it has a field the endpoint does not take. data.errors says which.Fix the request. Retrying it unchanged gets the same answer.
IDEMPOTENCY_KEY_MISSING400A vend was sent without an Idempotency-Key header.Send one. See idempotency.
AUTH_REQUIRED401No API key, an invalid, revoked or expired key, or a missing or bad request signature on a key that requires one.Check the key and the mode it belongs to.
WALLET_INSUFFICIENT_FUNDS402The wallet's available balance does not cover the price. Nothing was created.Fund the wallet, then send the order with a new key.
PERMISSION_DENIED403The request came from an address outside the key's IP allowlist.Call from an allowed address, or change the allowlist.
NOT_FOUND404No such transaction, product or route for this key's business and mode.Check the reference and that the key's mode matches.
CONFLICT409The client_reference is already used by another transaction; the message names it.Do not resend. Look the existing transaction up.
IDEMPOTENCY_IN_PROGRESS409A request with this key is still running.Retry shortly with the same key.
REQUEST_TOO_LARGE413The body is over the size limit.Send less, for example fewer metadata keys.
IDEMPOTENCY_FINGERPRINT_MISMATCH422The key was already used with a different request.A different order needs a different key.
RATE_LIMITED429Too many requests for the key or the business. See rate limits.Wait Retry-After seconds, then retry.
INTERNAL_ERROR500Something failed on our side. The message never says more.Retry with the same Idempotency-Key.
SERVICE_UNAVAILABLE503A dependency or provider is unavailable.Retry with the same Idempotency-Key.

The rest of the registry never comes back from /api/v1. MFA_REQUIRED, VERIFICATION_REQUIRED, ACCOUNT_LOCKED, INVALID_PIN, PIN_LOCKED, PIN_NOT_SET, WEAK_PIN, INVALID_PHONE_NUMBER, SIM_ALREADY_REGISTERED, SIM_UNAUTHORIZED and TRANSACTION_INVALID_TRANSITION belong to the dashboard. ROUTING_NO_CANDIDATES is a transaction's failure_code, below.

When a vend fails after 202

A vend that was accepted and then did not deliver is not an HTTP error: the transaction ends failed, the hold is released, and transaction.failed carries a failure_code:

failure_codeMeaning
ROUTING_NO_CANDIDATESNo line or provider could take the request. In live mode, check that a connected line in a group sells the plan and has the stock for it.
PROVIDER_REJECTEDThe channel refused it outright, for example an invalid destination.
PROVIDER_FAILEDThe channel failed it, or a status query found it failed.
RETRIES_EXHAUSTEDEvery route the plan allowed was tried without success.

One code appears on a transaction that has not failed. PENDING_TIMEOUT marks a transaction still pending when its status queries ran out: it stays pending, in manual review with the hold kept, until staff settle it one way or the other.