code is stable and safe to branch on. message is for humans and may change. details
is present when we can name the offending field.
Codes
400 is for a request we cannot parse or understand. 422 is reserved for a request that
is perfectly well-formed but that we cannot serve for the requested region. Branching on
error.code is more durable than branching on the status.Region rejections
Both region codes are decided before the task is queued, so you learn immediately instead of watching a task burn its whole retry budget and fail with a symptom-shaped error.REGION_UNSUPPORTED is permanent — that engine cannot serve the requested country:
REGION_UNAVAILABLE is transient — the route exists but nothing can serve it at the
moment. Query GET /capacity to see what each region can
currently serve.
Batch failures are per-item
A well-formed batch always returns200, even if every task inside it failed. The HTTP
status describes the request; results[].success describes each task.
A batch item can fail with
REGION_UNAVAILABLE or ENQUEUE_ERROR, neither of which
appears in the per-item error enum of the async task contract this API implements. Both are
additions, not replacements — VALIDATION_ERROR and RESOURCE_ALREADY_EXISTS are unchanged.Task-level failures
A task that fails after being queued does not produce an HTTP error at all. You learn about it from the terminal status:error is a string, not the {code, message, timestamp} object used by request-level errors. It is the last engine error recorded
before the task gave up. The same task delivers a webhook with task.status === "FAILED".