IdealBoxPay

Documentation

Take your first payment.

Everything below is the real contract: the routes, the statuses, the failure codes, and the rules a correct integration follows.

The error envelope

Every failed request answers with the same shape. Branch on error.code, which is stable, and show error.message, which is prose.

{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_missing",
    "message": "That payment does not exist.",
    "requestId": "req_01JAY7BQ2M4X8ND6VKQ0R3PZ5W"
  }
}

Request errors

These mean the request was wrong or could not be accepted. They are your problem to fix and they arrive with an HTTP status.

HTTPcodeWhat it means
400invalid_requestThe body failed validation. Unknown fields are refused rather than ignored — a typo’d amount field must not silently charge a different figure.
401unauthenticatedNo key, a malformed key, a revoked key, or a source address the key does not permit.
403permission_deniedThe key authenticated but its scopes do not cover this operation.
404resource_missingNo such object under your merchant. Somebody else’s id answers this too, so the endpoint cannot be used to test whether an id exists.
409resource_conflictThe object is not in a state that allows this — already confirmed, already expired, or another attempt is in flight.
412idempotency_key_reusedThe same idempotency key arrived with a different body. That is a client bug, and answering with the first result would charge the wrong figure silently.
422unprocessable_entityWell-formed, but the platform cannot act on it.
429rate_limit_exceededThe key’s request budget is exhausted. Honour the retry-after header.
503processor_unavailableNo provider route is available for this corridor right now.
500api_errorSomething failed on our side. The message never carries internal detail; quote the requestId.

Payment failures

These are different: the request worked and the payment did not. The code is canonical across every provider, which is what makes decline analytics possible and what lets you write retry logic once.

failureCodeWhat happenedRetry
INSUFFICIENT_FUNDSThe account or card had too little money.Worth retrying
INVALID_ACCOUNTWrong number, or an account that does not exist.Permanent
ACCOUNT_INACTIVEThe account exists but cannot transact.Permanent
LIMIT_EXCEEDEDA per-transaction or per-period ceiling was hit.Worth retrying
EXPIRED_INSTRUMENTThe card or mandate has lapsed.Permanent
AUTHENTICATION_FAILEDA wrong PIN, or 3-D Secure was not completed.Worth retrying
DECLINED_BY_ISSUERThe issuer refused without saying why.Permanent
USER_CANCELLEDThe customer declined the prompt.Worth retrying
TIMEOUTThe customer never responded.Worth retrying
DUPLICATEThe provider recognised this as a repeat of a payment it already took.Permanent
FRAUD_SUSPECTEDBlocked by a risk check.Permanent
CURRENCY_NOT_SUPPORTEDThat rail cannot settle this currency.Permanent
PROVIDER_UNAVAILABLEThe provider was unreachable or down.Worth retrying
PROVIDER_ERRORThe provider answered with an error of its own.Worth retrying
CONFIGURATION_ERROROur credentials or setup are wrong. Do not retry — this needs an engineer.Permanent

Retrying

A retryable failure is worth prompting the customer again. A permanent one is not — asking somebody to re-enter a closed card number four times is a worse experience than telling them once.