# Errors & status codes
Source: https://paylinky.vercel.app/docs/errors

Error format and the full list of error codes.

# Errors

Errors always use this shape:

```json
{ "error": "invalid_amount", "message": "amount must be a positive number." }
```

Some errors include extra fields (for example `reference` and `ussd_code` on `otp_required`, or `transaction_id` on provider errors).

## HTTP status codes

| Status | Meaning | Action |
| - | - | - |
| `200` | OK / final result / idempotent replay | Read `status` |
| `201` | Resource created (payment link) | — |
| `202` | Accepted, pending | Wait for webhook |
| `400` | Invalid request | Fix the request |
| `401` | Missing/invalid/revoked key | Check your key |
| `403` | Not allowed (activation, KYC, suspended) | See `error` |
| `404` | Not found | Check the id |
| `409` | Reference conflict | Use a unique reference |
| `429` | Rate limit | Back off and retry |
| `5xx` | Provider or server problem | Check the status before retrying |

## Error codes

| `error` | HTTP | Description |
| - | - | - |
| `unauthorized` | 401 | Bad key |
| `key_not_activated` | 403 | Key awaiting admin activation |
| `kyc_required` | 403 | Live access needs approved KYC |
| `account_suspended` | 403 | Account suspended |
| `sandbox_only` | 403 | Endpoint requires a sandbox key |
| `method_disabled` | 403 | Method disabled by an administrator |
| `invalid_json` | 400 | Body is not valid JSON |
| `invalid_amount` / `amount_too_low` / `amount_too_high` | 400 | Amount rules |
| `invalid_currency` | 400 | Currency doesn't match the country |
| `invalid_country` / `country_not_supported` | 400 | Unknown or disabled country |
| `invalid_operator` / `operator_not_supported` | 400 | Unknown or disabled operator |
| `invalid_phone` | 400 | Phone number invalid |
| `invalid_customer` | 400 | Crypto customer fields missing |
| `asset_not_supported` | 400 | Crypto asset inactive |
| `invalid_title` / `invalid_url` | 400 | Payment link validation |
| `otp_required` | 400 | OTP flow — see [Payment flows](/docs/payment-flows) |
| `invalid_otp` / `invalid_reference` | 400 | Wrong OTP or unknown session reference |
| `reference_conflict` | 409 | Reference reused with different data |
| `not_found` | 404 | Unknown resource or endpoint |
| `rate_limited` | 429 | Too many requests |
| `maintenance` | 503 | Live API in maintenance |
| `provider_unavailable` / `provider_error` | 502 | Payment provider issue |
| `upstream_timeout` | 504 | Provider timeout — verify before retrying |
| `internal_error` | 500 | Unexpected error |

## Retrying safely

Do not blindly retry payment creation after a timeout. Save your `reference`, then call `GET /transaction/{reference}`. Retrying `POST /collect` with the **same** `reference` is safe: you get the original payment back.
