Most application routes translate known failures to a JSON object containing error (a user-facing message) and code (a machine-readable category). An ApiError can also add a bounded details object, such as account-link guidance. HTTP status and code have different purposes: status classifies the request outcome, while code selects a concrete recovery path.
{
"error": "Your workspace changed. Refresh and try again.",
"code": "revision_conflict"
}| Status | Typical meaning | Client response |
|---|---|---|
| 400 | Invalid object, field, enum, identifier, or cursor | Fix the request; repeating identical invalid input will not help. |
| 401 | Missing, invalid, expired, or required identity | Refresh/sign in through the correct identity provider. |
| 403 | Origin mismatch or resource/role permission failure | Check app origin and current membership; do not retry with another workspace ID to evade access checks. |
| 404 | Missing or inaccessible resource | Refresh resource selection; a form capability can intentionally conceal unpublished forms. |
| 409 | Revision, state, dependency, or concurrency conflict | Reload and review the relevant state before retrying. |
| 413 | Endpoint body or file size limit exceeded | Reduce payload size within that endpoint’s limit. |
| 415 | Unsupported content type | Use JSON or multipart as required by the route. |
| 422 | Confirmation, rule, provider capability, form-value, or generated-layout issue | Resolve the specific requirement before resubmitting. |
| 429 | Request/actor concurrency limit | Back off, reduce parallel requests, and allow active work to finish. |
| 500 | Unexpected server failure | Show the safe error; use operator logs for diagnosis without exposing internals. |
| 502 / 503 | Provider/service unavailable or bounded queue full | Inspect the feature state and retry appropriately after recovery. |
Generic server failures return a safe server_error message. Known validation errors use validation_error; confirmation-related engine errors may use 422 rather than 400. Not every endpoint emits every status or provides Retry-After. Do not invent a retry delay from a missing header.
Match the conflict to its resource#
| Code | Resource that must be refreshed |
|---|---|
| revision_conflict | Workspace or reviewed proposal revision |
| page_changed | Record paging snapshot; restart at the first page |
| draft_dependencies_changed | Source data used by a saved AI draft; regenerate for review |
| invalid_draft_state | AI job state: only eligible completed/unapplied drafts can be edited |
| resume_context_changed | Saved failed-job request/context; preserve exact input or start a new request |
| email_draft_changed | Latest recipients/body and updatedAt for an email draft |
| email_categories_conflict / email_categories_stale | The actor’s separate category revision |
| email_sync_busy | An actively updating mailbox; wait before organizing it again |
Retry identities belong to specific operations#
Durable AI creation uses clientRequestId; email draft creation and sending use their own clientRequestId values; template installation uses requestId; public forms use submissionId. These are not interchangeable or universal idempotency headers. Preserve a logical request’s key across a lost response, and create a new key only for a new intended operation. Reusing a key with changed content can be rejected.
HTTP 202 on email sending means queued, not delivered. Draft statuses include queued, preparing, sending, sent, unknown, and failed. An unknown outcome needs reconciliation against provider evidence; blindly constructing a new send request can create a duplicate message. Likewise, cancellation of an AI request should be judged by returned/saved state rather than a closed browser connection.
Account for format exceptions#
- OAuth callbacks return 303 redirects with bounded result query parameters even on failure.
- Download routes return bytes on success but can return a JSON error body; inspect status before writing a file.
- NDJSON interpretation can emit an error event after response streaming has begun; HTTP status alone cannot describe that late failure.
- MCP auth failures can use JSON-RPC errors and WWW-Authenticate; early host/origin guards have their own response. A tool-level isError result differs from a failed HTTP request.
- AI availability can be expressed as a proposal with no operations and aiUnavailable even when the immediate interpretation HTTP response is 200.