Errors and limits

The error shape every surface returns, what each status class means, the codes worth branching on, and the plan and rate limits a call can meet.

The error shape

Every error from the REST API has the same body:

{
  "error": {
    "code": "unknown_approver",
    "message": "A sentence for a person to read.",
    "details": { "…": "whatever the refusal carries, when it carries something" }
  }
}

The `code` is the contract. Branch on it, never on the message, which is written for people and will be reworded. The SDKs raise it as DeliverdError.code, the CLI prints Error (code): message, and the MCP server returns most refusals as an error result carrying the same code.

Status classes

StatusMeansWhat to do
400The request itself is wrong.Change it. Retrying the same request fails the same way.
401The credential was not accepted.Check the key or token.
402Your plan does not include this, or a plan limit is reached.Upgrade, or free something up. Not retryable as it is.
403A refusal about you, or a rule about you.Not retryable. Read the code: a missing scope, a role, or an organisation rule.
404No such thing, or nothing this key may see.Check the id. The two are deliberately indistinguishable.
409The request was fine and the world moved.Re-read and retry: someone published first, a question was answered, a version is awaiting approval.
413, 415, 422The body is too large, the wrong type, or a scan blocked what was in it.Change the content.
429Too many requests.Slow down and retry; the SDKs do this for you.
500Ours.Retry later. A named 500 code means a write failed for a reason you could not have avoided.

Codes worth branching on

CodeStatusWhen
auth_error401 or 403The key or token is missing, malformed, revoked or expired (401), or its organisation is suspended or its agent disabled (403).
insufficient_scope403The credential is fine and lacks the scope this call needs; the message names it. Reissue the key — scopes are fixed when a key is made.
validation_error400The body does not match the schema; details lists the problems.
unknown_approver400An approver, reviewer or respondent is not someone the organisation knows.
ambiguous_approver400A name matches more than one person or team. Name it by id.
self_approval403The requester named only themselves.
refused_by_rule403An organisation rule or ethics rule refused the request. The message carries its explanation.
action_not_permitted403This agent is not permitted to make this kind of request.
agent_not_permitted403Agent keys may not do this: registering actions, writing policy.
not_permitted403Your role does not allow this, whatever the key's scopes.
agent_not_found404The agent this key acts as has been deleted.
unknown_action404The gate was asked about an action nobody registered.
invalid_input400The gate's input does not match the action's schema; problems names each fault.
question_not_found404That question is not on this approval.
already_answered409Somebody has already answered that question.
version_conflict409expectedVersion is stale: somebody published first.
approval_pending409A version of this report is awaiting approval.
upload_not_found404The upload ticket is gone — usually because the first publish used it.
scan_blocked422The publish scan found something it will not serve.
public_sharing_denied403Agent keys cannot create public links.
invalid_cursor400A list cursor this API did not issue.
idempotency_key_reused422The same Idempotency-Key with a different body.
idempotency_in_progress409The first request with this key has not finished. Retry with the same key.
rate_limited429Too many requests from this key.

The gate's refusal is not on this list, because it is not an error: a denied action answers 200 with allowed: false. See The gate.

Plan limits

A call that needs something your plan does not include, or that would take you past a limit, answers 402:

CodeStatusWhen
feature_unavailable402The plan does not include this capability — agent identities, for example.
plan_required402Organisation-wide analytics, below the plan that includes it.
plan_limit402The plan's number of live reports is reached. Archive one or upgrade.
storage_limit402Storing this version would pass the plan's storage.
seat_limit402The plan's number of seats is reached.
limit_reached402The plan's number of scheduled agents is reached.
FreeTeamBusinessEnterprise
Live reports102001,000Unlimited
Seats21050200
Storage1 GB25 GB100 GB500 GB
Scheduled agents—210100
Model screens a month——1,00010,000

Your organisation's actual limits are on Admin → Billing; an Enterprise agreement can change them.

Rate limits

Each API key or token may make up to 120 requests a minute. Past that, calls answer 429 rate_limited until the minute is up. The SDKs retry a 429 with backoff, honouring Retry-After, and their polling starts quick and slows down, so an agent waiting on a person does not spend its allowance.

Retrying safely

  • Retry 409, 429 and 5xx. Do not retry other 4xx without changing the request.
  • Send an Idempotency-Key on every POST and keep it across your retries, so a request that arrives twice is done once. The SDKs do this for you. A 5xx releases the key, so your retry is a real attempt.
  • Give requests an externalId so a restarted agent can find what it already asked rather than asking twice. The gate requires one.