Recovery control
Retry only when the contract permits it.
Status, stable code, Retry-After, and idempotency together determine the safe next action.
- 01ClassifyStatus + stable code
- 02Retryable?Network, 429, bounded 503
- 03PreserveBody + idempotency key
- 04WaitRetry-After or backoff
- 05TerminateAccept or escalate
| Class | Examples | Action |
|---|---|---|
| Retryable | Network, 429, bounded 503 | Wait and preserve identity |
| Correctable | 409 ceiling, 422 input | Change after a new decision |
| Terminal | 401, 402, 403, 404 | Restore the missing condition |
Stable public envelope
Operations published in the property OpenAPI contract return error-v1 for
documented failures:
{
"schema_version": "error-v1",
"error": {
"code": "invalid_request",
"message": "Request validation failed",
"details": {}
}
}
Validate it with the public schema at https://cleanedweb.com/schemas/error-v1.json. Use
error.code for program logic and retain message for diagnostics. details is
optional and can contain validation issues or canonical location candidates.
Codes and recovery
| HTTP | Stable code | Meaning | Agent behavior |
|---|---|---|---|
| 401 | authentication_required |
Session or API key missing, expired, invalid, or revoked | Stop and restore authentication. Do not retry unchanged. |
| 402 | insufficient_units |
Workspace balance cannot cover the classified delivery | Stop and request an operator-approved Unit credit. |
| 403 | access_forbidden |
Account, workspace, feature, or market is not entitled | Stop. Request access or choose an entitled market. |
| 404 | not_found |
Canonical resource is absent from entitled current data | Treat the identifier as stale or unavailable; do not fabricate a replacement. |
| 409 | max_units_exceeded |
Classified delivery exceeds the caller's spend ceiling | Narrow the search or request a new ceiling; do not silently raise it. |
| 409 | idempotency_conflict |
The key was reused for a changed request or delivery | Generate a new key only for a genuinely new request. |
| 422 | invalid_request |
Payload, range, identifier, or limit is invalid | Correct the request using OpenAPI; do not retry the same body. |
| 422 | ambiguous_location |
Location text has multiple canonical matches | Present details.candidates or ask the user to choose. |
| 422 | location_not_found |
No canonical location matches the text | Change or remove the location after user confirmation. |
| 429 | rate_limited |
Shared API-key window is exhausted | Wait for Retry-After, then retry with the same idempotency key. |
| 503 | service_unavailable |
Serving readiness or property service cannot answer safely | Retry with bounded exponential backoff and jitter; preserve the request unchanged. |
Unknown error codes must be treated as non-success. Preserve the response for diagnostics and avoid destructive follow-up actions.
Retry policy
Retry only transient failures such as 503 or a network failure where no response
was received. A reasonable client policy is 1, 2, 4, 8, then 16 seconds with jitter,
followed by a surfaced failure. Do not turn a 503 into an empty result set or an
empty capabilities manifest.
Reads can be retried safely. If a future mutation is added to the public contract, follow its published idempotency contract rather than assuming retries are safe.
Current response boundaries
GET /v1/capabilities and GET /v1/properties/locations are not currently covered
by the OpenAPI error-v1 guarantee. Their framework errors use a top-level detail
field. Check the HTTP status first and parse either documented shape without
converting an error into data.
Webhook receiver retry semantics are separate from API client retries. See
https://cleanedweb.com/docs/webhooks.md.
Standards and further reading