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.

company_mismatch

403

Company id disagrees with the credential

Every control-plane request names its company explicitly, and the named company must match the business this credential or grant is bound to (and the addressed resource). The mismatch is rejected before any resource lookup.

Fix: Call GET /v1/companies to see the company this credential is bound to, and send that id. A credential is never a multi-company bearer — switching companies means switching profiles or grants.

company_selection_required

403

The grant has no company selected yet

An OAuth grant binds to exactly one company on a MadeThis-owned selection step after consent. Until the owner completes it, every control call answers with this code.

Fix: Open the selection URL in the response in a browser, pick the company, then retry. The model cannot complete this step.

surface_not_available

403

Credential audience does not cover this surface

Control routes require a control-audience credential; primitive routes require a primitives-audience one. A key minted for one surface cannot call the other, whatever scopes it carries.

Fix: Pair a credential for the surface you need (`madethis login` with the matching preset). Do not widen an existing key — mint the right one.

team_member_paused

409

Team member is paused

The addressed AI team member is paused (or still a draft), so it cannot accept assignments or steering. Its history is preserved.

Fix: Resume the member (team:manage scope, or in MadeThis), then retry. Reads still work while paused.

team_member_external

409

Team member runs on your own agent

The addressed AI team member is active, but its executor is your own agent rather than MadeThis's runtime. MadeThis has no run to give it, so an assignment would be queued against nothing.

Fix: Send this work to that agent, or point the role back at MadeThis's runtime in the company's team settings, then retry. Reads and history still work.

business_suspended

409

Company is suspended

The company is suspended, so it cannot accept assignments; nothing is queued and no credits are reserved. Existing runs are not affected by this response.

Fix: Resolve the suspension in MadeThis (billing or account status), then retry. Reads still work while suspended.

capability_not_connected

409

Required capability is not connected

The work needs a capability (ad account, social account, support inbox, …) this company has not connected or has disconnected.

Fix: List capabilities via GET /v1/companies/:company_id/capabilities and complete the connection flow in a browser — connections are always user-mediated.

policy_blocked

403

Company policy refuses this action

A deterministic company policy (spend ceiling, connection rule, role authority) evaluated server-side refused the action. Natural-language instructions never override policy.

Fix: The response's hint names the policy decision. The owner can adjust policy in MadeThis; the API only proposes policy changes for owner review.

interactive_approval_required

403

Approval must happen in a MadeThis session

This credential deliberately cannot approve consequential actions. Approval happens in a fresh owner-authenticated MadeThis session, never through a model-facing credential.

Fix: Fetch the item's action URL (where offered) or open MadeThis directly; the owner approves or rejects there. Rejection and answering are available via POST …/attention/:attention_item_id/respond.

run_not_cancellable

409

Run is already terminal

The run finished, failed, or was already cancelled before the cancellation request arrived. Completed external side effects are never claimed to be reversed.

Fix: Read GET /v1/companies/:company_id/team-runs/:team_run_id for the final state, side effects, and any owner action needed.

steering_not_deliverable

409

Message cannot reach the member's current run

The addressed team member runs as a background worker with no mid-run channel, and its current run has already started. An assignment has one run, so a message parked now would never be read; nothing was saved. Chat-delivered members and runs that have not started are not affected.

Fix: Wait for the run to finish (poll GET /v1/companies/:company_id/team-runs/:team_run_id), or cancel it via POST /v1/companies/:company_id/team-runs/:team_run_id/cancel and create a new assignment whose outcome includes the change.

too_many_open_assignments

409

Member has too many open assignments

The member already has 200 or more assignments in one open state (queued, running, or awaiting attention), which is more than the duplicate-outcome guard can check a new one against, so the assignment was refused rather than risk queueing an outcome twice. Nothing was queued and no credits were reserved.

Fix: Finish, cancel, or answer some of the member's open assignments first (GET /v1/companies/:company_id/team-runs lists them), then retry.

temporarily_unavailable

503

Control plane briefly unavailable

A dependency (runtime dispatch, a connector, a settlement step) is briefly unavailable. Nothing about the request was invalid.

Fix: Retry with backoff, reusing the same Idempotency-Key on writes so the retry replays instead of duplicating.