Skip to main content
Every Lumeus error carries a machine-readable code and a request_id. Branch on the HTTP status and code, never on the message text, which is for people and may change. This page lists the error shapes, every status and code, and what to do about each.

Error bodies

REST errors (Activity, Assets and Webhooks) return {"code": ..., "error": ..., "request_id": ...}. RPC errors keep the JSON-RPC 2.0 envelope and put the code in error.data. Every API response, success or error, also carries the same request_id in the X-Request-Id header (health checks excepted).
Always check the HTTP status before parsing a success schema, and check for error in every JSON-RPC reply: RPC can return HTTP 200 with a method error from the Stellar node.

HTTP statuses

Error codes

New codes may be added; treat an unknown code by its HTTP status.

Daily Testnet cap

Testnet is free up to 10,000 requests per organization per UTC day (unlimited on Enterprise). Past the cap, every Testnet request is refused until 00:00 UTC, and Retry-After counts the seconds until then. A refused request costs nothing and is not counted.
In an RPC batch, every call gets this error; a batch that would cross the cap is refused whole. A request of notifications only gets 429 with an empty body. See Testnet is free.

RPC errors

RPC errors keep the JSON-RPC 2.0 envelope. error.data is the code from the table above as a plain string, and error.request_id equals the X-Request-Id header. The Stellar SDKs read this shape: Python stellar-sdk raises SorobanRpcErrorResponse with the code in .data, and JavaScript exposes the error object as e.response.data.error. A method error from the Stellar node itself (for example a ledger outside retention) arrives with HTTP 200, the node’s own code and data, and no request_id. A rejected notification (no id) gets the real HTTP status with an empty body. submission_outcome_unknown means the transaction reached the node but its answer was lost, so it may or may not be pending. The error carries transaction_hash next to request_id:
Poll getTransaction with error.transaction_hash until it is found or the transaction’s time bounds expire. Resubmitting the same signed envelope is safe (the node answers DUPLICATE); do not build a new transaction with a new sequence number first.

Webhooks not enabled on a network

POST /v1/subscriptions can answer 503 unavailable with Retry-After: 300 and one of these messages:
  • paid webhooks are not enabled on this network yet
  • free Testnet webhooks are not enabled
  • webhook admission is not initialized on this network yet
This is a network configuration state, not a passing failure. Retrying does not help until Lumeus changes the network configuration: stop after one or two attempts, create the endpoint on the other network, or contact hello@lumeus.dev.

Unknown routes

An unknown path returns 404 not_found, and a known path with the wrong method returns 405 method_not_allowed with an Allow header, on every host. RPC answers these with the REST error body, not a JSON-RPC envelope; any POST / reaches the JSON-RPC handler. A path with an empty, . or .. segment returns 400 invalid_request. This usually means a shell variable was empty, as in /accounts//activity when $ACCOUNT is not set:
A trailing slash is a different route and returns 404 not_found.

Wrong key type

A 401 names the key you sent when it is the wrong kind: A lum_test_ key on a Mainnet host, or lum_live_ on Testnet, also returns 401. See networks.

Retry policy

  • Retry only 429, 500, 502, 503 and 504, after Retry-After when it is present. Never retry 501 not_implemented: the feature is off on that host.
  • Cap the attempts (for example 5) and the total time, and add random jitter.
  • Do not retry query_timeout unchanged: narrow the request first.
  • Do not retry a webhook create that says the endpoint kind is not enabled on this network (Retry-After: 300).
  • Do not retry 400, 401, 403 or 404 unchanged.
  • Never retry sendTransaction with a newly built transaction; see submission_outcome_unknown above.

ETags and preconditions

Only Webhooks uses ETag and If-Match. Read a subscription and save its ETag, for example "3" (behind compression it may arrive as W/"3"; send it back as is). Send it as If-Match on PATCH, DELETE, signing-key rotation and revocation, tests, replay quotes and replays. A missing If-Match returns 428; a version that no longer matches returns 412: read the subscription again and decide whether your change still applies. See manage subscriptions.

Report an issue

Send the product, network, UTC time, HTTP status, request_id, and a redacted request shape to hello@lumeus.dev. Never include API keys, bearer tokens, signing secrets, seeds or raw authorization headers.