SatLane
Documentation

Error codes

Errors

All errors return the same shape:

{
  "error": {
    "code": "no_active_xpub",
    "message": "Store has no active xpub for this environment...",
    "request_id": "b9fc7e29-587f-4dda-b220-86d7144893fe"
  }
}

Include the request_id when contacting support. It correlates to our server logs.

Errors POST /v1/invoices can return

The most-called endpoint. Plan for these.

401 — authentication

CodeWhen
api_key_invalidMissing Authorization header, malformed, or the key doesn't exist. Check the header is Authorization: Bearer sl_live_… or sl_test_….
api_key_revokedKey was revoked from the dashboard or by an admin. Mint a new one.
api_key_wrong_envCalling a live store with a test key, or vice versa. Use the key that matches the store's mode.

403 — authenticated but blocked

CodeWhen
auth_account_suspendedVendor account suspended by an admin. No invoice creation until reinstated. Contact support.

404 — resource missing

CodeWhen
not_found (Store)The store the API key belongs to was archived. Restore it or use a different store.

409 — conflict / not configured

CodeWhenResolution
no_active_xpubStore has no active xpub for this environment. Live invoices need a mainnet xpub; test invoices need any active xpub.Add an xpub at app.satlane.com/stores/<id>/xpubs.
gap_limit_exceededWallet's gap limit is within 5 of being reached and we haven't seen recent funding.Bump the gap limit in your Electrum wallet (recommend 100+) or rotate xpubs. Address recycling absorbs some of this for expired-unpaid invoices.
idempotency_conflictThe same Idempotency-Key was reused with a different request body.Either reuse the key with the original body (we'll return the cached response) or generate a new key.

400 — validation

CodeCause
validation_errorZod rejected the body. Common: missing both (amount + currency) and amount_sats, providing both, expires_in_minutes outside [5, 120], invalid callback_url, metadata value > 255 chars. The message field names the offending field.
validation_errorIdempotency-Key header > 255 chars or empty.
invalid_currencyCurrency code not in our supported list (only USD at MVP).
invalid_amountSats amount ≤ 0, or fiat amount rounds to zero sats at the current rate.

429 — rate limited

CodeWhenResolution
rate_limitedMore than 100 invoice creations per minute on one API key.Honor the Retry-After header (seconds).

503 — temporary infrastructure issue (retry safe)

These mean the call would have succeeded if not for an infra condition. Retry with exponential backoff.

CodeWhen
chain_syncingThe Bitcoin node is in initial block download (verificationprogress < 0.999). We refuse new invoices to avoid issuing payable addresses against a stale tip. Usually only happens right after a fresh deploy.
disk_fullThe platform host is critically low on disk. We block writes to avoid losing webhook delivery state. Operator gets paged automatically; resolves itself.
database_unavailablePostgres unreachable. Rare.

Recommended client retry policy

HTTPAction
200 / 201Use the response.
400, 401, 403, 404, 409Stop. These are caller bugs or configuration errors. Log and surface to the user.
429Backoff using Retry-After, then retry.
503Exponential backoff (e.g. 1s → 2s → 4s → 8s → 16s, max 5 tries). Show "Service temporarily unavailable" if exhausted.
Other 5xxTreat as a bug on our side. Log request_id, escalate.

Always send an Idempotency-Key when retrying. We cache the response for 24 hours per key, so retries after a transient network error return the same invoice instead of creating duplicates.

Errors from other endpoints

A non-exhaustive selection (see packages/shared/src/errors.ts for the full catalog):

  • auth_required (401) — session cookie missing on dashboard endpoints
  • auth_totp_required (401) — 2FA-gated endpoint, prompt for code and call /v1/auth/totp/verify
  • auth_email_not_verified (403) — vendor email not yet verified
  • invoice_not_cancellable (409) — invoice already paid / late_paid / cancelled / expired
  • invoice_expired (410) — payment flow hit a fully-expired invoice
  • invoice_already_paid (409) — duplicate paid transition attempt
  • not_found — UUID doesn't match anything you own
  • gone (410) — resource intentionally removed