Errors
One envelope for every error, with a stable machine-readable code and a human message.
{ "error": "entitlement_required", "message": "Guard is not enabled for this organisation", "details": { "upgrade": "/settings#plan" } }| Status | error | When |
|---|---|---|
| 400 | invalid_body | Body failed validation; details.fieldErrors names the fields |
| 400 | invalid_json · invalid_query · empty | Malformed JSON, bad query string, empty payload |
| 400 | no_agent_build · endpoint_required · no_connection | Project not ready for a run or Guard |
| 400 | invalid_cron · unknown_template · no_scenarios | Schedules and runs |
| 400 | missing_signature · invalid_signature · turnstile_failed | Webhook and public-form verification |
| 400 | config_error · invalid_provider · weak_password | Settings validation |
| 400 | restricted_key_required · invalid_key_format · stripe_key_rejected · missing_permission · mode_mismatch · account_mismatch | Connecting or rotating Stripe: live keys must be restricted; details.missing names absent permissions |
| 400 | project_required · egress_blocked · secret_required · not_configured | Observations and the access service |
| 401 | unauthorized · invalid_credentials | Missing or wrong credential |
| 402 | plan_limit_reached | Monthly runs exhausted for the plan |
| 403 | forbidden · entitlement_required · wrong_password | Role too low, capability not unlocked |
| 404 | not_found · mandate_not_found · project_not_found · connection_not_found · agent_build_not_found | Unknown or not yours |
| 409 | idempotency_key_reused · business_key_conflict | Same key different body; same effect already proposed |
| 409 | digest_mismatch · approval_expired · not_reviewable · no_pending_approval · already_decided | Approvals |
| 409 | mandate_inactive · project_paused | Authority ended, or the kill-switch is on |
| 409 | slug_taken · exists · already_enabled · already_onboarded | Conflicts |
| 409 | use_billing · billing_not_configured · already_subscribed · no_billing_account | Plan changes go through billing |
| 409 | connection_in_use | Active mandates or unfinished operations still use the connection |
| 413 | payload_too_large | Body over the limit |
| 422 | no_priced_models · no_valid_cases | AI catalog or red-team input unusable |
| 429 | rate_limited | Too many requests from this IP |
| 500 | internal_error | Unexpected; retry once, then contact support with the request time |
| 502 | sandbox_unavailable · sync_failed · stripe_unreachable | A dependency did not answer |
Decisions are not errors: a DENY is a 202 with decision: "DENY" and explicit decision_reasons. Verdicts are not errors either.