Guides
Errors & idempotency
The envelope
Every /api/v1 error is:
{ "error": "<machine_code>", "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):
-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.