Essentials
Errors
Every error has the same shape and a stable machine-readable code. Branch on the code, show the message, log the request ID.
The API uses HTTP status codes for the broad outcome and a code for the exact reason. Every error, from every endpoint, looks like this:
422 Unprocessable Entity
{
"message": "The wallet balance cannot cover this amount plus fees.",
"code": "insufficient_balance",
"request_id": "req_01m6d5vfycvg029wd8brjnxnby"
}| Field | Meaning |
|---|---|
code | Stable. Branch on this. A code never changes meaning. |
message | For people. Safe to show to your users or log; may be reworded at any time. |
request_id | Identifies this request. Also in the X-Request-Id header. Quote it to support. |
errors | Only on validation_failed: the failing fields, each with its messages. |
The only exception is the token endpoint, which uses the OAuth 2.0 error format.
Validation errors
When fields fail validation, errors is keyed by field name, so you can point at the exact input:
422 Unprocessable Entity
{
"message": "The amount field is required. (and 2 more errors)",
"code": "validation_failed",
"errors": {
"amount": ["The amount field is required."],
"currency": ["The currency field is required."],
"callback_url": ["The callback url field is required."]
},
"request_id": "req_01m6d5tzhnc3kwt4dba4sz8pnz"
}Error codes
| HTTP | code | What happened | What to do |
|---|---|---|---|
| 400 | bad_request | The request could not be understood. | Fix the request. |
| 400 | invalid_idempotency_key | The Idempotency-Key header is longer than 255 characters. | Use a shorter key. |
| 401 | unauthenticated | The access token is missing, invalid or expired. | Get a new token and retry once. |
| 403 | bvn_not_verified | Your Lint account has not completed BVN verification. | Complete it in the dashboard. |
| 404 | not_found | No such resource, or it belongs to another account. | Check the ID. |
| 405 | method_not_allowed | Wrong HTTP method for this path. | Check the reference. |
| 409 | invalid_session_state | The checkout session is already paid or has expired. | Create a new session. |
| 409 | idempotency_key_in_progress | A request with this Idempotency-Key is still running. | Retry shortly with the same key. |
| 422 | validation_failed | A field is missing or invalid. See errors. | Fix the fields. |
| 422 | insufficient_balance | The wallet cannot cover the amount plus fees. | Fund the wallet, or send less. |
| 422 | account_resolution_failed | The destination bank account could not be verified. | Check the account number and bank code. |
| 422 | self_transfer | The payout would go to the wallet's own account. | Choose another destination. |
| 422 | invalid_order | The bill order could not be priced (e.g. no price for the amount). | Check the product and amount. |
| 422 | account_not_ready | Your Lint wallet is not set up yet. | Complete account verification. |
| 422 | idempotency_key_reused | This Idempotency-Key was used before with a different body. | Use a new key for a new request. |
| 429 | rate_limited | Too many requests. | Wait for Retry-After seconds. |
| 502 | bill_order_failed | The biller rejected the order. The message has the order ID and the biller's reason. | Fix the input or retry; the payment is refunded. |
| 502 | provider_error | A bank, biller or identity provider failed or timed out. | Retry later with the same Idempotency-Key. |
| 500 | server_error | Something broke on our side. | Retry later; contact support with the request_id. |
Note
Resources that belong to another account return 404 not_found, never 403, so an ID cannot be used to discover what exists.
Handling errors well
- Branch on
code, not onmessageor status alone. Two different422s need different handling. - Treat unknown codes as their status. We add codes over time; an unfamiliar
4xxis a client error, an unfamiliar5xxis retryable. - Retry only what is retryable:
429,502,500and network failures. Use exponential backoff, and for anything that moves money, the sameIdempotency-Keyon every retry. - Never retry a money-moving call without an
Idempotency-Key. A timeout does not mean the money did not move. - Log
request_idnext to your own records for every failed call.