ZapAPI Docs
Get StartedConceptsAI GatewayGuidesPayment GatewaySDKAPI ReferenceResources
Concepts

Errors

The ZapAPI error envelope — deterministic HTTP status codes, machine-readable error codes and field-level validation messages.

ZapAPI uses a single, deterministic error format across every endpoint. You can write one error handler and use it everywhere.

The error envelope

Every non-2xx response uses the same envelope as successful responses, with status: "error" and an errors array of field-level messages:

{
  "status": "error",
  "statusCode": 422,
  "message": "Validation failed",
  "errors": ["amount must be a positive integer"],
  "timestamp": "2026-10-25T00:00:00.000Z",
  "path": "/api/v1/invoices"
}

For machine-readable branching, many errors also carry a code:

{
  "status": "error",
  "statusCode": 403,
  "code": "missing_scope",
  "message": "Missing required scope: invoices:write",
  "timestamp": "2026-10-25T00:00:00.000Z",
  "path": "/api/v1/invoices"
}

HTTP status codes

ZapAPI uses conventional HTTP status codes:

CodeMeaningWhen you see it
400Bad RequestMalformed JSON, invalid enum value, missing required field.
401UnauthorizedMissing, invalid, expired or revoked API key.
403ForbiddenValid key, but missing a required scope.
404Not FoundThe resource does not exist (or belongs to another project).
409ConflictIdempotency-key replay with a different body; state conflict.
422Unprocessable EntitySemantic validation failed (e.g., amount out of range).
429Too Many RequestsRate limit exceeded — see Retry-After header.
500Internal Server ErrorSomething broke on our side — retried automatically when safe.
502 / 503Bad Gateway / Service UnavailableUpstream (LNbits, node) issue — retry with backoff.

Machine-readable codes

Common code values you can branch on:

CodeHTTPMeaning
invalid_api_key401Key is malformed, revoked or expired.
key_expired401Key passed its expiresAt.
missing_scope403Key lacks the required scope.
resource_not_found404Resource does not exist.
idempotency_conflict409Same key, different body.
validation_failed422One or more fields failed validation.
rate_limited429Rate limit exceeded.
upstream_unavailable503LNbits / Lightning node unavailable.

The set of codes is additive — new codes may be introduced, but existing codes will not change meaning. Branch on the codes you care about and treat unknown codes as generic errors.

Handling errors in the SDK

The TypeScript SDK throws a typed ZapAPIError with helpers for the common cases:

import { ZapAPI, ZapAPIError } from '@zapapi/sdk';

try {
  await zap.invoices.create(params);
} catch (err) {
  if (err instanceof ZapAPIError) {
    err.status;          // 400, 401, 403, 409, 429, …
    err.code;            // 'invalid_api_key', 'missing_scope', …
    err.payload;         // raw API error body
    err.isUnauthorized;  // 401
    err.isForbidden;     // 403
    err.isNotFound;      // 404
    err.isConflict;      // 409 — idempotency conflict
    err.isRateLimited;   // 429
  }
}

Retrying safely

  • Idempotent retries — for any request that used an Idempotency-Key, retrying with the same key is always safe. See Idempotency.
  • 429s — respect the Retry-After header and back off exponentially.
  • 5xxs — retry with exponential backoff; if you used an idempotency key, the retry is safe.
  • 409s — do not retry blindly; a conflict means the original request succeeded with a different body.

Validation errors

422 responses include a field-level errors array. Each entry is a human-readable message naming the field and the problem:

{
  "statusCode": 422,
  "message": "Validation failed",
  "errors": [
    "amount must be a positive integer",
    "currency must be one of: sat, USD, EUR, GBP, INR, BRL, JPY, CAD, AUD"
  ]
}

Surface these to your users directly — they are written to be end-user friendly.

On this page