Error reference
Every error returns the same envelope. The code is stable and safe to branch on — the message is written for humans and agents and may change.
{
"error": {
"code": "validation_failed",
"message": "priceAmountCents must be an integer",
"docs_url": "https://madethis.com/docs/api/errors#validation_failed"
}
}forbidden_scope
403Key lacks the required scope
The key is valid but was minted without the scope this route requires. Scopes are read, write, money, and ads.
Fix: Mint a new key with the scope you need. The `money` scope (checkout and payouts) is owner-only — a business admin cannot grant it to themselves.
not_found
404No such route or resource
Either the path isn't a /v1 route, or the id belongs to a different business. Cross-business reads return 404 rather than 403 so ids can't be probed.
Fix: Check the path against the OpenAPI document, and confirm the id belongs to the business this key is scoped to (`GET /v1/business`).
validation_failed
400Request body or query failed validation
A field is missing, the wrong type, or out of range. The message names the offending field.
Fix: Fix the named field. Prices are integer cents (2900 = $29.00), currency is a 3-letter ISO code, and `limit` is 1–100.
rate_limited
429Too many requests
A per-key (or, on public pairing routes, per-IP) bucket is exhausted. Reads and writes have separate budgets.
Fix: Wait the number of seconds in the `Retry-After` response header, then retry. Batch reads rather than polling in a tight loop.
credits_exhausted
402Out of credits
Every write runs a credits gate, metered or not. Reads are never gated, so a business out of credits can still be inspected.
Fix: Top up, then retry. Nothing was written — this fires before the handler runs.
plan_required
402Plan does not include this capability
The account's plan doesn't cover the route being called.
Fix: Upgrade the plan, or use a capability your current plan includes.
confirmation_required
202Held for confirmation (reserved)
Reserved for the spend-policy gate. When that ships, an over-threshold call returns a SUCCESS-shaped 202 carrying a confirmation id — not this error envelope. No route emits this code today.
Fix: Nothing to do yet. When spend policy lands, confirm or reject the parked action and the original call executes exactly once.
idempotency_conflict
409Idempotency-Key reused with a different request
This key was already used for a different method, path, or body — or a first request carrying it is still in flight. Replaying the SAME request instead returns the original response with `Idempotency-Replayed: true`.
Fix: Use a fresh Idempotency-Key for a genuinely new request. If a previous attempt may have succeeded, retry it byte-identically to get the replay rather than a second write.
internal
500Something broke on our side
An unhandled failure. The response carries no internal detail.
Fix: Retry with backoff. If it persists, contact support with the approximate timestamp and the route you called.