Error handling
How Lumeo reports failures, when to retry, and how idempotency keys interact with errors.
Lumeo uses conventional HTTP status codes. This page is the reference for what each range means, the error codes you'll actually encounter, and how retries and idempotency interact when something fails.
The error response shape below is assembled from what the current NestJS exception filters actually return, not from a single canonical schema — some endpoints (e.g. confirm, payment list filters) use untyped request handling and can surface a generic 500 for cases a typed DTO would normally reject with 400. Treat unfamiliar error codes defensively rather than assuming an exhaustive enum.
Status code ranges
| Range | Meaning | What to do |
|---|---|---|
2xx | Success | — |
400 | Bad request — validation failure or invalid state transition | Fix the request; do not retry unchanged. |
401 | Missing or invalid Bearer token | Refresh or reissue the API key. |
403 | Signature or permission failure (e.g. webhook HMAC mismatch) | Check signing secret / scope. |
404 | Resource not found, or not owned by the authenticated user | Verify the ID and that it belongs to this account. |
429 | Rate limited | Back off and retry after the window resets. |
5xx | Internal error | Safe to retry with the same Idempotency-Key where supported. |
Error response shape
Most 4xx errors from validated endpoints return:
{
"statusCode": 404,
"message": "Payment not found",
"error": "ERR_PAYMENT_NOT_FOUND"
}Known error codes seen across the API today:
| Code | Where | Meaning |
|---|---|---|
ERR_PROFILE_NOT_FOUND | Accounts | The user has no profile record yet. |
ERR_PAYMENT_NOT_FOUND | Payments | Payment ID doesn't exist or isn't owned by this user. |
ERR_WALLET_NOT_FOUND | Payments | The funding vaultId supplied to confirm doesn't exist. |
Endpoints built on untyped inline request bodies (see the confirm, link-wallet, and payment list-filter endpoints in Payments and Accounts) don't run through a class-validator DTO. Malformed input to these can surface as a 500 rather than a 400 — plan your error handling to treat unexpected 5xx from these specific endpoints as a possible input problem, not purely a server fault.
Rate limits
Rate limits are enforced per-endpoint, per-user:
| Endpoint | Limit |
|---|---|
POST /payments/initiate | 10 requests / minute |
POST /payments/confirm | 5 requests / minute |
A 429 response does not include a Retry-After header today — use exponential backoff starting at 1 second, matching the pattern in Handle webhook retries.
Idempotency and retries
initiate and confirm both accept an Idempotency-Key header (see Authentication). This changes how you should react to different failure types:
- On a
5xxor timeout: retry with the sameIdempotency-Key. If the original request actually succeeded server-side despite the timeout, you get back the original result instead of creating a duplicate payment. - On a
4xx: do not retry with the same key unchanged — the request itself was rejected, and Lumeo will keep rejecting it. Fix the input first. - Keys expire after 24 hours, scoped to your API key. A retry after expiry is treated as a brand-new request.
Two payment identifiers exist and are not interchangeable: id (the payment's primary key, returned from initiate/confirm) and txnId (derived from the Idempotency-Key you sent). GET /payments/:id/status expects txnId; POST /payments/:id/retry and GET /payments/:id/form15ca expect id. Passing the wrong one returns a 404, not a helpful error explaining the mismatch — see Payments for exactly which route expects which.
What's safe to retry blindly
| Call | Safe to retry without checking state first? |
|---|---|
GET requests (status, list, balance) | Yes — always safe. |
initiate / confirm with the same Idempotency-Key | Yes — idempotent by design. |
initiate / confirm with a new key | No — will create a duplicate payment. |
retry on a payment | Only if the payment is still FAILED or PENDING — check status first. |
Read next
- Handle webhook retries — the same retry/backoff pattern applied to inbound webhook delivery.
- Authentication — idempotency key scoping and API key rotation.