Errors
One error envelope for every failure, a stable set of machine-readable codes, and a request id on every response for support.
Every failure — at every status code — returns the same JSON shape. You write one error handler and it covers the whole API.
The error envelope
{
"error": {
"code": "validation_failed",
"message": "MINIMUM_EFT_AMOUNT must be numeric.",
"requestid": "8f3c1a9b2d7e4f60"
}
}| Field | Use it for |
|---|---|
code | Control flow. A stable machine-readable token. Branch on this. |
message | Humans and logs. Readable and safe to surface, but the wording may change — never branch on its text. |
requestid | Support. Correlates to the X-Request-Id response header and the server logs. Quote it when reporting a problem. |
The message never carries internal detail — no stack traces, no SQL, no connection strings, no
tokens, and no confirmation that another tenant's record exists.
Error codes
Branch on code. These strings are part of the published contract: new ones may be added, but
existing ones are never renamed or repurposed.
code | Typical status | Meaning |
|---|---|---|
unauthorized | 401 | No valid bearer token (missing, malformed, disabled, or suspended account). Every auth failure is identical. |
forbidden | 403 | Authenticated, but not permitted to perform that action. |
not_found | 404 | The merchant or nested resource does not exist, isn't yours, or is deleted — indistinguishable by design. |
validation_failed | 400 | A field or the request body failed validation (bad type, disallowed setting key, illegal status transition, unreadable JSON). |
source_out_of_scope | 400 | A copy_settings_from source names a merchant outside your scope. Distinct from not_found because you named it explicitly. |
rate_limited | 429 | The per-credential rate limit was exceeded. Honour the Retry-After header. See Rate limits. |
gateway_unavailable | 503 | FNB Smart Blocked data is backed by the gateway, which is not currently reachable. Retry with backoff. |
internal_error | 500 | An unexpected server-side fault. Not your request's fault — retry, and if it persists quote the requestid. |
Status codes at a glance
400— you sent something invalid. Fix the request; retrying unchanged will not help.401— authentication failed. See Authentication.403— authenticated but not permitted.404— the resource isn't visible to you (may not exist, or isn't yours).429— slow down; obeyRetry-After.500— server fault. Safe to retry with backoff.503— a dependency (the gateway) is down. Retry with backoff.
Distinguish 500 from 401. A 500 on a call that normally works is a server fault, not a
statement that your token is invalid — do not discard the token or re-authenticate in a loop. Retry
with backoff and quote the requestid if it persists.
Correlation with X-Request-Id
Every response carries an X-Request-Id header. You may set it on a request to tie it to your
own logs (letters, digits, -, _, ., up to 64 characters); if you don't, the API mints one. The
same id appears in the requestid field of any error body and in the server-side logs — it is the
single thing support needs to trace a specific call.