Errors and Safe Retries
Read the ActionBox error envelope, choose a retry policy, and fail closed on conflicts.
ActionBox returns a structured error for a rejected request. Preserve the HTTP
status, the stable error.code, and the X-Request-ID response header when
diagnosing a failure.
{
"error": {
"code": "ACTION_CHANGED",
"message": "This Action changed while it was being reviewed.",
"details": {}
}
}Malformed request fields use the same envelope with HTTP 422:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request did not satisfy the API contract.",
"details": {
"errors": [
{"type": "string_too_short", "location": ["body", "title"], "message": "..."}
]
}
}
}Status and code guide
| HTTP status | Common codes | Meaning |
|---|---|---|
400 | INVALID_CALLBACK_URL, INVALID_EXPIRATION, INVALID_OPEN_URL, INVALID_RESPONSE, OPTION_NOT_FOUND, RESPONSE_REQUIRED, RESPONSE_TYPE_MISMATCH, INVALID_IDEMPOTENCY_KEY | The request is understood but violates an ActionBox rule. Fix the input before retrying. |
401 | AUTH_REQUIRED, INVALID_API_KEY, SOURCE_REVOKED | The credential is missing, invalid, or no longer active. Stop and re-authenticate or rotate it. |
403 | CSRF_BLOCKED | A browser-cookie mutation was rejected because its request origin was not trusted. |
404 | ACTION_NOT_FOUND, SOURCE_NOT_FOUND, RUN_NOT_FOUND, WEBHOOK_NOT_FOUND | The resource is absent or outside the credential's scope. Confirm the ID and boundary. |
409 | ACTION_NOT_OPEN, ACTION_CHANGED, ACTION_NOT_RESOLVED, OUTCOME_ALREADY_REPORTED, IDEMPOTENCY_KEY_REUSED, DEDUPE_CONFLICT, IDEMPOTENCY_CONFLICT | State or concurrency conflict. Re-read the current resource; do not overwrite it blindly. |
413 | ACTION_PAYLOAD_TOO_LARGE | An Action create or patch body exceeds the 256 KB safety ceiling. Reduce it before retrying. |
422 | VALIDATION_ERROR | The JSON shape or field value does not satisfy the live API contract. |
429 | RATE_LIMITED | A configured IP, user, Source, or plan rate limit was reached. Honor Retry-After. |
503 | RATE_LIMIT_UNAVAILABLE | ActionBox cannot currently admit requests. Retry only after the short Retry-After delay. |
The live OpenAPI document at
/openapi.json is the authoritative
field and route reference. Error codes can include additional resource-specific
codes; do not invent a code from a message or from a different API.
Retry only when the operation is safe
| Operation | Automatic retry? | Required behavior |
|---|---|---|
GET/HEAD reads | Yes, for network errors, 429, or 5xx | Use bounded backoff and honor Retry-After. |
POST /v1/actions | Yes, only with the same Idempotency-Key and same logical body | Keep the key stable for the retry window; a changed body is a conflict. |
PATCH or POST resolve/cancel/decision | No blind retry | Re-read the Action and its action_version/fingerprint; a terminal race may already have won. |
POST /v1/actions/{id}/outcome | Exact replay is safe | Keep the same outcome fields and deterministic idempotency key; never rewrite a different outcome. |
| Webhook delivery receiver | Acknowledge with 2xx after durable processing | Verify the signature, deduplicate the event id, and return a non-2xx only when you want another delivery attempt. |
The official Python and TypeScript SDKs retry GET/HEAD requests and writes
that carry Idempotency-Key. Their default request timeout is 10 seconds and
their default retry budget is two retries. They retry transient 429 and 5xx
responses, honor Retry-After, and use bounded backoff. A client timeout does
not change an Action's state.
Fail closed on ambiguity
A timeout, connection reset, or 409 is not a rejection. Read the current
Action before taking an irreversible step. If the current version or
fingerprint differs from the reviewed snapshot, stop and require a fresh
review.
Rate-limit handling
For 429 RATE_LIMITED, wait at least the number of seconds in Retry-After
when it is present, then apply bounded exponential backoff with jitter. Do not
let many workers retry at the same instant. For 503 RATE_LIMIT_UNAVAILABLE,
pause briefly and retry a bounded number of times; if admission remains
unavailable, surface the failure rather than bypassing the API.
Every response includes a sanitized X-Request-ID. Record that ID with the
endpoint, status, and stable error code, but redact tokens, callback secrets,
Action content, and customer data.
Next: Idempotency and dedupe, Expiration and timeouts, or Troubleshooting.