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"
  }
}

unauthorized

401

Missing or invalid API key

No Authorization header, a malformed token, or a key that has been revoked, whose business was deleted, whose owner's account was deleted, or whose business is no longer headless.

Fix: Send `Authorization: Bearer mt_live_…`. Mint a key at /api-keys or run `madethis login`. Keys are shown exactly once — if you lost it, mint a new one and revoke the old.

forbidden_scope

403

Key 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

404

No 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

400

Request 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

429

Too 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

402

Out 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

402

Plan 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

202

Held 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

409

Idempotency-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

500

Something 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.