Error codes
Errors
All errors return the same shape:
JSON
{
"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
401 authentication
| Code | When |
|---|---|
api_key_invalid | Missing Authorization header, malformed, or the key does not exist. Use Authorization: Bearer sl_live_… or sl_test_…. |
api_key_revoked | Key was revoked from the dashboard or by an admin. Mint a new one. |
api_key_wrong_env | Calling a live store with a test key, or vice versa. Use the key that matches the store's mode. |
403 authenticated but blocked
| Code | When |
|---|---|
auth_account_suspended | Vendor account suspended by an admin. No invoice creation until reinstated. |
402 payment required (billing lock)
| Code | When |
|---|---|
billing_overdue | Live invoicing locked: unpaid plan invoice, exhausted trial, or suspended subscription. Pay or choose a plan at /billing. |
404 resource missing
| Code | When |
|---|---|
not_found (Store) | The store the API key belongs to was archived. Restore it or use a different store. |
409 conflict / not configured
| Code | When | Resolution |
|---|---|---|
no_active_xpub | Store 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_exceeded | Wallet's gap limit is within 5 of being reached and we have not seen recent funding. | Bump the gap limit in Electrum (recommend 100+) or rotate xpubs. |
idempotency_conflict | The same Idempotency-Key was reused with a different request body. | Reuse the key with the original body (cached response) or generate a new key. |
400 validation
| Code | Cause |
|---|---|
validation_error | Zod 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 names the field. |
validation_error | Idempotency-Key header > 255 chars or empty. |
invalid_currency | Currency code not supported (only USD today). |
invalid_amount | Sats amount ≤ 0, or fiat amount rounds to zero sats at the current rate. |
429 rate limited
| Code | When | Resolution |
|---|---|---|
rate_limited | More than 100 invoice creations per minute on one API key. | Back off using retry_after_seconds. |
503 temporary infrastructure (retry safe)
These mean the call would have succeeded without an infra condition. Retry with exponential backoff.
| Code | When |
|---|---|
chain_syncing | Bitcoin node is in initial block download. We refuse new invoices against a stale tip. |
disk_full | Host critically low on disk. Writes blocked to protect webhook delivery state. |
database_unavailable | Postgres unreachable. Rare. |
Recommended client retry policy
| HTTP | Action |
|---|---|
| 200 / 201 | Use the response. |
| 400, 401, 402, 403, 404, 409 | Stop. Caller bugs, billing lock, or configuration errors. Log and surface to the user. |
| 429 | Back off using retry_after_seconds, then retry. |
| 503 | Exponential backoff (1s → 2s → 4s → 8s → 16s, max 5 tries). |
| Other 5xx | Treat as a bug on our side. Log request_id, escalate. |
Always send an Idempotency-Key when retrying creates. We cache the response for 24 hours per key.
Errors from other endpoints
A non-exhaustive selection:
auth_required(401): session cookie missing on dashboard endpointsauth_totp_required(401): 2FA-gated endpoint; prompt for code and call/v1/auth/totp/verifyauth_email_not_verified(403): vendor email not yet verifiedinvoice_not_cancellable(409): invoice already paid / late_paid / cancelled / expiredinvoice_expired(410): payment flow hit a fully-expired invoiceinvoice_already_paid(409): duplicate paid transition attemptnot_found: UUID does not match anything you owngone(410): resource intentionally removed