Errors and limits
The error format, the status codes you will meet, and the rate and usage limits.
The error format
Every error has the same JSON shape, {"detail": ...}, where detail is one of:
- A message string:
{"detail": "Lead not found."}. - A coded object, when the client may want to branch on it:
{"detail": {"message": "...", "code": "agent_paused"}}. - For
422validation errors, a list with one item per problem:{"type", "loc", "msg", "input", ...}.
An unhandled server error is 500 with the plain-text body Internal Server Error.
{
"detail": {
"message": "The Meeting copilot is paused or off. Resume it in Agents.",
"code": "agent_paused"
}
}Status codes
| Status | Meaning |
|---|---|
400 | A malformed request, e.g. a missing or invalid X-Workspace-Id with a session token. |
401 | No credential, or an invalid or expired one. The answer carries WWW-Authenticate: Bearer. |
403 | Not allowed: a missing scope, a role that is too low, a route API keys can't use, or a human-only action. |
404 | Not found, including records of another workspace. |
409 | A conflict with the current state (see the codes below). |
422 | Validation failed, or a rule refused the change (e.g. autonomy_cap). |
429 | Too many requests, or a usage limit was reached. Honour Retry-After (seconds). |
503 | A feature isn't configured on this server, or a free-tier AI budget is used up. |
Codes you may branch on
| Status | code | When |
|---|---|---|
403 | human_only | Zero, MCP or an API key asked to set the Writer or the Inbox agent to auto. Only an admin in the app can do that. |
409 | agent_paused | The agent behind the action is paused or off, e.g. preparing a meeting while the Meeting copilot is paused. |
409 | call_closed | A write to a call that already ended. |
409 | contact_denied | A compliance gate refused an outbound call. |
409 | contact_deferred | A one-off send was held back by contact pressure or a sending limit. The detail carries the reasons and next_allowed_at. |
409 | missing_postal_address | Activating a sequence with email steps before the workspace has a postal address (needed for the unsubscribe footer). |
422 | autonomy_cap | A customer-facing agent can't go above ask_first. |
422 | not_schedulable | A schedule for an agent that doesn't run on one. |
Reply drafts are different: approving or sending one with an API key or a connected app is refused with 403 and a message that tells the user to review the draft in ZeroReach.
Rate limits
| Limit | Default | Answer |
|---|---|---|
| Requests per API key (and per connector grant) | 600 per minute (API_KEY_RATE_LIMIT_PER_MINUTE) | 429 API key rate limit exceeded. Try again later. with Retry-After |
| Failed key attempts per client IP | counted on failures only | 429 with Retry-After |
| Sign-in attempts | per user and IP | 429 |
| Discovery runs per workspace per UTC day | 3 (DISCOVERY_MAX_RUNS_PER_DAY) | 429 |
| Zero (the in-app assistant) messages per workspace per day | 200 (ASSISTANT_DAILY_MESSAGES) | 429, resets at midnight UTC |
| AI credits per workspace per UTC month | 50,000 (ASSISTANT_MONTHLY_CREDITS) | 429 for the assistant until the month ends; everything else keeps working |
Sending limits never drop work
Before an email is sent or a call is dialled, the server takes a token from every limit that covers the action (per mailbox, per sending domain, per recipient domain, per workspace, per caller ID and per provider). If one is empty, the action is deferred to when it can run: the sequence runner reschedules the step, and a one-off send answers 409 contact_deferred with next_allowed_at. A limit never drops or fails a send. See Email and inbox and Compliance.
AI features fail closed
AI features run on free-tier budgets. When a budget is used up or no model is configured, the feature stops and says so (503, or a paused batch) instead of falling back to a paid provider or retrying in a loop.