Error Codes
The API returns errors in a consistent JSON shape with an HTTP status
code and a machine-readable error string.
Response shape
{
"error": "invalid_template",
"error_description": "Template not found or not accessible",
"trace_id": "00-abcdef1234567890-1234567890abcdef-01"
}
trace_id is the ASP.NET Activity.Id for the
request โ include it in support tickets so we can find the exact log entry.
HTTP status codes
| Status | Meaning |
|---|---|
| 200 OK | Successful GET / synchronous operation. |
| 201 Created | Resource created (Webhooks, ProviderConfigs). |
| 202 Accepted | Async work queued (Passes, Messages, Batches). |
| 204 No Content | Resource deleted, no body. |
| 400 Bad Request | Validation / routing / template error. |
| 401 Unauthorized | Missing / invalid / expired Bearer token. |
| 403 Forbidden | Authenticated but lacking scope or status. |
| 404 Not Found | Resource not found OR cross-tenant access. |
| 409 Conflict | State conflict (revoke a revoked pass, cancel a processing batch). |
| 429 Too Many Requests | Rate-limit or batch ceiling hit. |
| 500 Internal Server Error | Unhandled โ please report with trace_id. |
Error codes
| error | HTTP | Cause |
|---|---|---|
| invalid_request | 400 | FluentValidation rejected the body (missing / malformed field, length limit hit, duplicate, etc.). |
| invalid_client | 401 | Tenant or application doesn't exist / inactive, or the secret is wrong. |
| unauthorized_client | 403 | Application is disabled. |
| insufficient_scope | 403 | JWT scope doesn't cover the route. |
| not_found | 404 | Resource not found, or it exists but belongs to another tenant. |
| invalid_template | 400 | Template doesn't exist / archived / not owned by tenant. |
| intent_mismatch | 400 | Pass intent doesn't match template type. |
| no_providers | 400 | No ApiProviderConfig usable for the tenant. |
| invalid_state | 409 | Operation incompatible with current resource state. |
| too_many_requests | 429 | Rate-limit window exhausted. |
| too_many_batches | 429 | Tenant exceeds 10 active batches. |
Idempotency
Mutating endpoints accept an Idempotency-Key request header.
A repeated request with the same key returns the original response without
creating a duplicate resource โ useful for safe retries after timeouts.
POST /passes uses the (tenant_id, external_ref)
pair as an implicit idempotency key, even when no
Idempotency-Key header is set.