# Errors & idempotency ## The envelope Every `/api/v1` error is: ```json { "error": "", "details": { "message": "human explanation", … } } ``` Branch on `error` — it is stable API contract. `details.message` is for humans and may change; other `details` fields are documented per code below. ## Error catalog | Code | Status | Meaning | Recovery | |---|---|---|---| | `missing_token` | 401 | No `Authorization: Bearer` header | Send the token | | `invalid_token` | 401 | Token not recognized | Check the value; mint a new token if lost | | `revoked_token` | 401 | Token was revoked | Mint a new token | | `insufficient_scope` | 403 | Token scope too narrow (e.g. `read` on a mutation) | Use a `write` token | | `not_found` | 404 | Domain not in your workspace's portfolio, or unknown route | Check the domain and workspace | | `bad_request` | 400 | Body/params failed validation | Fix per `details` | | `price_changed` | 409 | `confirm_price` ≠ live price | Re-quote; retry with `details.current_price` if acceptable | | `insufficient_balance` | 402 | Wallet can't cover the price | Top up, or pass `payment_method` / `shared_payment_token`; `details.required` and `details.available` are in cents | | `payment_failed` | 402/422 | Pay-as-you-go charge declined | 402 = card declined (`details.kind`); fix the instrument and retry | | `email_taken` | 409 | Signup email already registered | Sign in / use the existing account | | `no_hosted_zone` | 409 | Zone operation on a domain with external nameservers | Manage DNS at the delegated provider, or switch to `ns1/ns2.ingram.tech` | | `delete_failed` | 422 | Registry rejected the delete | See `details.message` (often TLD policy) | | `registration_failed` / `renew_failed` / `transfer_failed` | 422 | Registry rejected the operation | `details.message` carries the registry's reason — often user-actionable (e.g. contact field policy) | | `internal` | 500 | Unexpected registry/server error | Retry with the same `Idempotency-Key` | Registry mutations are **asynchronous**: a success response with registry status `Pending` means the registry accepted the operation and is processing it. The change (new expiry date, transferred status, …) becomes visible when the registry completes — usually seconds to minutes, up to days for transfers. ## Idempotency Every mutating route accepts an `Idempotency-Key` header (any unique string; a UUID is conventional): ```bash -H 'idempotency-key: 0e8f3b7c-renew-example-com-2027' ``` Semantics: - The first request with a key executes; its response is stored. - A replay with the same key returns the **stored response** without re-executing — a network timeout can never double-charge or double-register. - Successes (2xx) and rejections (4xx) are cached; **5xx responses are not**, so retrying a transient failure with the same key re-executes safely. The CLI generates a fresh key per mutation automatically. For direct API use, derive the key from your own operation identity (order id, task id) so your retries collapse naturally.