Error handling
Every error response — both expected validation failures and unexpected server faults — comes back in the same envelope:
{
"code": "RESOURCE_NOT_FOUND",
"status": 404,
"message": "Inventory line not found",
"data": { "id": "65f3a2b1c8d4e9f7a6b5c4d3" }
}
Four fields, always:
| Field | Type | Meaning |
|---|---|---|
code |
string | Stable machine-readable code. Match on this. |
status |
integer | HTTP status (also on the response status line). |
message |
string | Human-readable default. Useful for logs; do not parse for client logic. |
data |
object | Per-error structured detail. Schema documented per error code. |
Common error codes
These can be returned by any endpoint:
| Code | Status | When |
|---|---|---|
BAD_REQUEST |
400 | Input validation failed, malformed Idempotency-Key header, malformed body. data describes the validation failure. |
UNAUTHORIZED |
401 | Missing, malformed, invalid, or revoked API key. data is empty by design. |
FORBIDDEN |
403 | API key lacks the scopes the endpoint requires. data lists required and granted. |
TOO_MANY_REQUESTS |
429 | Rate limit exceeded. data carries the limit value and retryAfter (seconds). See Rate limits. |
INTERNAL_SERVER_ERROR |
500 | Unexpected server fault. Quote the response's X-Request-Id header to support. |
Endpoint-specific errors (e.g. INVENTORY_LINE_NOT_FOUND on a line lookup, INSUFFICIENT_FUNDS on a payout, LISTING_ALREADY_SOLD on a purchase) are documented on the operation's reference page along with the shape of their data.
Retry semantics
| Status | Retry? |
|---|---|
| 4xx (other than 429) | No. The request is malformed or unauthorised. Fix the client; retrying doesn't help. |
| 429 | Yes, after Retry-After seconds. |
| 5xx | Yes, with exponential backoff. Cap at ~3 retries; quote X-Request-Id to support if the failure persists. |
Network errors (connection refused, TLS failure, timeout) are equivalent to a 5xx for retry purposes — same backoff, same cap.
Idempotency on writes
Write endpoints that opt in to idempotency accept an Idempotency-Key header. The header value is a free-form string (1–255 chars) that you generate per logical operation; the server caches the response for 24 hours and replays it on subsequent calls with the same key.
Use this for any operation where a network blip-then-retry could otherwise create duplicate state — orders, payouts, listing creations. The operation's reference page tells you whether it's idempotency-aware.
A typical client-side helper:
import { randomUUID } from "node:crypto"
async function createOrder(payload) {
return fetch("https://public-api.cardnexus.com/v1/orders", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": randomUUID(),
},
body: JSON.stringify(payload),
})
}
randomUUID() per logical order — not per process, not per session.
Logging + support
Every response carries an X-Request-Id header. Capture this on every error you log; if you contact support, the request id is what makes a problem locatable on our side. Without it, "the API returned 500 last Tuesday" is unactionable.