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.