Every error has the same shape: {"error": {"code", "message", "docs"}}. Branch your code on code, never on message.
The same marker can carry two statuses depending on the route. The tables below say which.
| Marker | Status | When | What to do |
|---|
API_KEY_MISSING | 401 | Authorization header absent or empty | send Authorization: Bearer fpk_... |
API_KEY_INVALID | 401 | secret not recognised | check the secret. It may have been rotated |
API_KEY_REVOKED | 403 | the key was revoked | create a new one |
API_KEY_EXPIRED | 403 | the key is past its date | create a new one. An expired key cannot be rotated |
API_KEY_IP_NOT_ALLOWED | 403 | call from outside the address allowlist | add the address, or check where the call starts from |
API_ACCOUNT_DISABLED | 403 | the account is no longer available | contact support |
API_SCOPE_MISSING:<scope> | 403 | the key lacks that permission | add the permission the suffix names |
API_ACTION_NOT_ALLOWED:<action> | 403 | provider action the platform does not classify, or one with no public permission | see the action table in Managing services |
API_KEY_SPENDING_CAP_EXCEEDED | 403 | the charge would pass the 30-day cap | raise the cap, then retry with a new idempotency key |
API_RATE_LIMITED | 429 | a rate counter is full | wait Retry-After seconds |
API_HEADER_NOT_ALLOWED | 400 | X-Acting-Account header sent | remove it |
API_KEY_IN_QUERY | 400 | key passed in the URL | send it in the header, and rotate it: the URL is already in logs |
| Marker | Status | When |
|---|
VALIDATION_ERROR:<field> | 400 | field or parameter absent, mistyped or outside its enum. The suffix names it |
IDEMPOTENCY_KEY_REQUIRED | 400 | header absent on a route that needs it |
IDEMPOTENCY_KEY_INVALID | 400 | key blank or beyond 128 characters |
IDEMPOTENCY_KEY_REUSED | 409 | same key, different request |
IDEMPOTENCY_IN_PROGRESS | 409 | same key while the first call is running. Retry-After |
INTERNAL_ERROR | 500 | on our side. Quote the X-Request-Id to support |
The VALIDATION_ERROR suffixes used across the surface: limit, cursor, status, page, action, command, method, subject, message, category, priority, payment.method, customerNote, lines.product, lines.quantity, lines.addons.quantity.
| Marker | Status | Routes | When |
|---|
SERVICE_NOT_FOUND | 404 | every service route | unknown code, another account's service, or outside the key's restriction |
SERVICE_NOT_ACTIVE | 409 | /power, /console/command, /actions/{action}, /renew | service suspended or cancelled |
SERVICE_NOT_ACTIVE | 400 | /backups, /backups/restore, /reinstall | the same, on the delegated routes |
SERVICE_IN_DUNNING | 400 | /backups, /backups/restore, /reinstall | unpaid invoice in dunning |
INSTANCE_NOT_PROVISIONED | 409 | /status, /metrics/history, /power, /actions/{action} | the infrastructure does not exist yet |
INSTANCE_NOT_PROVISIONED | 400 | /backups, /backups/restore, /reinstall | the same, on the delegated routes |
ACTION_UNSUPPORTED | 400 | /power, /console/command | kill on a VPS, console on a VPS |
INVALID_ACTION | 400 | /actions/{action} | action empty or starting with _ |
CUSTOM_NAME_TOO_LONG | 400 | PATCH /services/{code} | beyond 32 characters |
RENEWAL_CANCELLATION_NOT_AVAILABLE_VIA_API | 400 | PATCH /services/{code} | autoRenew: false |
NOTE_TOO_LONG | 400 | POST /backups | note beyond 200 characters |
MISSING_FIELDS | 400 | /backups/restore, /reinstall | a required field is missing |
BACKUP_NOT_FOUND | 400 | DELETE /backups/{id}, /backups/restore | unknown backup id |
BACKUP_IN_PROGRESS | 400 | /backups, /backups/restore | a backup operation is already running |
LIMIT_REACHED | 409 | POST /backups | backup quota reached |
INVALID_OS, OS_FAMILY_MISMATCH | 400 | /reinstall | os outside the catalogue, or family and version mismatched |
INVALID_AUTH_TYPE | 400 | /reinstall | authType outside PASSWORD and SSH_KEY |
| Marker | Status | When | What to do |
|---|
INSUFFICIENT_BALANCE | 402 | the balance does not cover the amount | top up, then retry with the same idempotency key |
NO_PAYMENT_METHOD | 402 | account_default with no usable method | register a method, then retry with the same key |
PAYMENT_METHOD_ID_REQUIRED | 400 | saved_method without paymentMethodId | fix the body, retry with the same key |
PAYMENT_METHOD_NOT_FOUND | 404 | that method is not on the account | same |
PAYMENT_METHOD_NOT_ACTIVE | 400 | orders and invoices | method expired or blocked |
PAYMENT_METHOD_NOT_ACTIVE | 402 | /renew | the same case, on a renewal |
PAYMENT_METHOD_DECRYPTION_FAILED, PAYMENT_METHOD_VAULT_ID_MISSING | 400 | the stored method is unusable | contact support |
PAYMENT_DENIED | 402 | the bank refused | change method. Replaying the same key returns the same refusal |
RENEWAL_FAILED | 402 | /renew | the charge failed for another reason |
PAYMENT_FAILED | 400 | settlement failed with no more precise code | |
PAYMENT_RECORDED_MISMATCH | 409 | reconciliation anomaly | do not retry. Contact support with the X-Request-Id |
PAYMENT_IN_PROGRESS | 409 | another payment of the same document is running | wait, then retry with the same key |
PAYMENT_METHOD_REQUIRED | 400 | the document needs a payment method | |
ORDER_NOT_FOUND | 404 | unknown order number | |
ORDER_NOT_PAYABLE, ORDER_ALREADY_SETTLED, ORDER_ALREADY_PAID, AMOUNT_INVALID | 400 | there is nothing to pay on that order | |
ORDER_CONCURRENT_UPDATE | 409 | the order changed during the write | retry |
NO_ITEMS, TOO_MANY_ITEMS | 400 | 0 lines, or more than 20 | |
PRODUCT_NOT_FOUND:<x>, PRODUCT_NOT_ORDERABLE:<x>, RECURRENCE_REQUIRED:<x>, RECURRENCE_NOT_OFFERED:<x> | 400 | the line does not match the catalogue | re-read GET /catalog/products |
MISSING_REQUIRED:<key>, INVALID_METADATA_KEY:<key>, INVALID_METADATA_VALUE:<key> | 400 | the line's configuration | re-read the product's characteristics |
ADDON_NOT_AVAILABLE:<x>, ADDON_MAX_QUANTITY:<x> | 400 | the add-on asked for | re-read the product's addons |
RENEWAL_IN_PROGRESS | 409 | a renewal of the same service is running | wait, then retry with the same key |
RENEWAL_TOO_FREQUENT | 429 | less than a minute since the previous one | wait a minute, retry with the same key |
| Marker | Status | When |
|---|
INVOICE_NOT_FOUND | 404 | unknown invoice code |
CREDIT_NOTE_NOT_FOUND | 404 | unknown credit note code |
INVOICE_ALREADY_PAID, INVOICE_VOID, INVOICE_REFUNDED, INVOICE_NOT_ISSUED, INVOICE_BALANCE_ZERO | 400 | nothing to pay on that document |
CURRENCY_UNSUPPORTED | 400 | currency not supported |
PDF_NOT_AVAILABLE | 503 | the PDF is not rendered yet. Retry-After: 5 |
| Marker | Status | When |
|---|
TICKET_NOT_FOUND | 404 | unknown ticket number |
SUBJECT_TOO_SHORT | 400 | subject too short |
TICKET_REJECTED, MESSAGE_REJECTED | 400 | support refused the creation or the message |
SSH_KEY_NOT_FOUND | 404 | unknown SSH key id |
SSH_KEY_FORMAT_INVALID | 400 | not a public key line |
DUPLICATE_KEY:<id> | 400 | the key is already on the account |
PRODUCT_NOT_FOUND | 404 | GET /catalog/products/{shortname} on an unknown shortname |
The action gateway relays the infrastructure's refusals.
| Answer | Status | Meaning |
|---|
a provider marker, e.g. FIREWALL_INVALID_ARGUMENT, RDNS_INVALID_ARGUMENT | 409 | the provider understood the request and refused it. The marker is its own |
ACTION_FAILED | 502 | the provider failed, or returned nothing usable |
PROVIDER_TIMEOUT | 502 | the provider did not answer in time |
| Status | Retry? |
|---|
400, 404 | not before fixing the request |
401, 403 | no. It is the key or its permissions |
402 | after clearing the cause. See the "what to do" column above |
409 | depends on the marker: PAYMENT_IN_PROGRESS and ORDER_CONCURRENT_UPDATE yes, PAYMENT_RECORDED_MISMATCH no |
429 | yes, after Retry-After |
500, 502, 503 | yes, with exponential backoff. On an idempotent route, keep the same key |