## Errors

Every error is JSON with a stable code field — safe to switch on.

### Shape

Every error response uses this JSON envelope:

```json
{
  "error":   "invalid_inputs",
  "details": [
    {
      "field":    "country",
      "code":     "not_in_enum",
      "expected": ["US", "MX"]
    }
  ]
}
```

### Status codes

| Status | Code | Meaning |
|--------|------|---------|
| 400 | `invalid_payload` | Body could not be parsed as JSON. |
| 401 | `unauthorized` | Token missing, malformed, or revoked. |
| 401 | `invalid_signature` | HMAC verification failed for a trigger. |
| 404 | `not_found` | Process, instance, or callback does not exist. |
| 409 | `callback_already_resolved` | One-shot webhook callback was already received. |
| 422 | `invalid_inputs` | Inputs failed schema validation. `details` lists each violation. |
| 422 | `invalid_definition` | Process YAML failed schema or syntax check. |
| 422 | `invalid_transition` | Tried to act on a terminal instance/step. |
| 429 | `rate_limited` | Too many requests. `Retry-After` header advises a wait. |
| 500 | `trigger_misconfigured` | Engine env var for HMAC secret is unset. |

### Retries

The engine itself retries failed `automated` steps with exponential backoff (max 3 attempts by default). The API does not retry your inbound calls — wrap idempotent calls (`POST /start`, `POST /submit`) yourself.

