Skip to content

API fundamentals

Errors

The error envelope, every error code, request ids and what to do about each kind of error.

Every response that is not a success has the same envelope, whatever went wrong. The HTTP status tells you the class of problem, type how to react, and code exactly what happened.

The error envelope

{
  "error": {
    "type": "invalid_request_error",
    "code": "amount_too_small",
    "message": "The minimum amount is 50.00 DA (5000 centimes).",
    "param": "amount",
    "request_id": "req_8f3a1c9d2b7e4a60"
  }
}
typestring

The category of the error. It decides how you react.

codestring

A stable, machine-readable identifier. Branch on this, never on the message.

messagestring

An explanation for developers. Do not show it to payers, and do not parse it.

paramstringnullable

The request parameter the error is about, such as amount or customer.email. Nested names use dots and brackets: items[0].quantity.

request_idstring

The id of the request, also in the Rais-Request-Id response header.

Request ids

Every response, errors included, carries a Rais-Request-Id header such as req_8f3a1c9d2b7e4a60c5d1, and the Rais-Version header. Log the request id of every call that fails, and quote it when you contact support: it finds the exact request on our side. Raïs logs each request with its id but never its headers, query string or body. The id is the same in the header and in error.request_id.

Types

TypeStatusWhat to do
authentication_error401Fix the key. See Authentication.
permission_error403The key is valid but not allowed: a missing scope. (A suspended store, or live mode before verification, answer 401 instead: see Authentication.)
invalid_request_error400, 405, 409Fix the request. The param names the field. Do not retry as it is.
not_found_error404The object does not exist for this key. See Authentication.
idempotency_error409, 422The Idempotency-Key conflicts with another request. See Idempotency and retries.
rate_limit_error429Wait for the Retry-After header, then retry. See Rate limits.
payment_error402, 502The payment gateway or the bank refused. Nothing was changed on your side of the line.
api_error500, 503A problem on our side or the gateway's. Retry later with the same Idempotency-Key.

Error codes

CodeHTTPTypeMeaning
amount_mismatch400invalid_request_errorThe amount is not an integer, is outside 5000...999999999999 centimes, or disagrees with `items`.
amount_too_large400invalid_request_errorThe amount is not an integer, is outside 5000...999999999999 centimes, or disagrees with `items`.
amount_too_small400invalid_request_errorThe amount is not an integer, is outside 5000...999999999999 centimes, or disagrees with `items`.
currency_not_supported400invalid_request_errorOnly `DZD`.
customer_not_found400invalid_request_errorA field is invalid (see `param`).
insufficient_balance400invalid_request_errorThe refund is more than what is left on the payment, or than your balance can cover (the refund plus its fee).
invalid_amount400invalid_request_errorThe amount is not an integer, is outside 5000...999999999999 centimes, or disagrees with `items`.
invalid_cursor400invalid_request_errorThe header or the `starting_after` value is invalid.
invalid_email400invalid_request_errorA field is invalid (see `param`).
invalid_idempotency_key400invalid_request_errorThe header or the `starting_after` value is invalid.
invalid_json400invalid_request_errorThe request body or query is malformed.
invalid_metadata400invalid_request_errorA field is invalid (see `param`).
invalid_parameter400invalid_request_errorThe request body or query is malformed.
invalid_url400invalid_request_errorA field is invalid (see `param`).
parameter_missing400invalid_request_errorThe request body or query is malformed.
parameter_unknown400invalid_request_errorThe request body or query is malformed.
refund_exceeds_balance400invalid_request_errorThe refund is more than what is left on the payment, or than your balance can cover (the refund plus its fee).
invalid_api_key401authentication_errorThe key is missing, unknown, revoked, expired, not allowed from this IP address, or its store cannot use it.
refund_declined402payment_errorThe bank refused the refund.
account_disabled403permission_errorThe store is suspended or disabled.
insufficient_scope403permission_errorThe key lacks the scope the endpoint needs.
live_mode_not_enabled403permission_errorComplete business verification to take live payments.
event_not_found404not_found_errorNothing there (or it belongs to another store or environment).
payment_not_found404not_found_errorNothing there (or it belongs to another store or environment).
refund_not_found404not_found_errorNothing there (or it belongs to another store or environment).
route_not_found404not_found_errorNothing there (or it belongs to another store or environment).
method_not_allowed405invalid_request_errorWrong HTTP method.
idempotency_in_progress409idempotency_errorThe first request with this key is still running.
payment_not_cancellable409invalid_request_errorThe payment is not in a state that allows it.
payment_not_refundable409invalid_request_errorThe payment is not in a state that allows it.
idempotency_key_reused422idempotency_errorThe key was used with different parameters.
rate_limit_exceeded429rate_limit_errorToo many requests (60 per second, bursts of 120, per key) or too many failed authentications.
internal_error500api_errorOur fault. Quote the `request_id`.
gateway_rejected502payment_errorThe payment gateway refused to create the payment.
gateway_unavailable503api_errorThe gateway cannot be reached; retry later. Nothing was created or refunded.

More codes can appear while a request is being read. They are produced by the validation of the request itself, so they are not tied to one endpoint:

CodeHTTP statusMeaning
invalid_request_body400The body is missing or is not a JSON object, for example an array.
parameter_out_of_range400A number is below or above what the parameter accepts, for example limit=0 or expires_in: 299. param names it.
parameter_too_long400A text or a list is longer than allowed, for example a reference of 65 characters or 51 items. param names it.
parameter_too_short400A text or a list is shorter than required.
invalid_request400A request that is invalid in a way no other code describes. Read message.

Handling errors

async function rais(path, init) {
  const response = await fetch(`https://api.rais.app/v1${path}`, init);
  const body = await response.json();
  if (response.ok) return body;

  const { error } = body;
  console.error("Raïs error", error.code, error.request_id); // always log the request id

  if (error.type === "rate_limit_error" || error.type === "api_error") {
    // transient: retry with backoff, reusing the same Idempotency-Key
    throw Object.assign(new Error(error.code), { retryable: true, retryAfter: response.headers.get("Retry-After") });
  }
  throw Object.assign(new Error(error.code), { retryable: false, param: error.param });
}

Retry guidance

SituationRetry?
Network error or timeoutYes, with the same Idempotency-Key. The first request may have succeeded.
429 rate_limit_exceededYes, after Retry-After seconds.
500 internal_errorYes, with backoff, and the same Idempotency-Key.
503 gateway_unavailableYes. Nothing was created or refunded. Wait the Retry-After seconds.
409 idempotency_in_progressYes, after Retry-After seconds: the first request is still running.
502 gateway_rejectedNot as it is: the gateway refused the payment. Read message.
402 refund_declinedNo. The bank refused. Do not send it again unchanged.
Other 4xxNo. Fix the request.

Back off exponentially (1 s, 2 s, 4 s ...) with some random jitter, and give up after a few attempts. Never retry a POST without an Idempotency-Key.