Allure Connect API errors use a flat JSON envelope:
{
"error": "Human-readable message",
"code": "STABLE_MACHINE_CODE"
}Validation failures may also include details. The interactive OpenAPI reference is the endpoint-level source of truth; this page lists common cross-cutting and core delivery errors rather than inventing an exhaustive global enum.
Authentication and Access
| HTTP | Code | Meaning |
|---|---|---|
| 401 | API_KEY_REQUIRED | Missing, invalid, revoked, wrong-mode, or disallowed-scope API key |
| 402 | PAYMENT_REQUIRED | The operation requires a paid plan |
| 403 | TENANT_MISMATCH | A request named a tenant outside the authenticated key |
| 403 | DISPATCH_ORIGIN_REQUIRED | A domain-restricted dispatch did not include usable origin context |
| 403 | DISPATCH_ORIGIN_NOT_ALLOWED | The launch origin is outside the dispatch allowlist |
Use Authorization: Bearer <api_key> or X-API-Key: <api_key> on partner routes. Bearer wins when both are supplied. Customer dashboard routes under /api/customer/* require the signed-in Clerk browser session.
Request Validation
| HTTP | Code | Meaning |
|---|---|---|
| 400 | INVALID_JSON | The request body is not valid JSON |
| 400/422 | INVALID_REQUEST | The request does not match the endpoint schema |
| 400 | INVALID_BODY | The body cannot be parsed for that endpoint |
| 400 | INVALID_PAGINATION | A pagination value is invalid |
| 400 | INVALID_SESSION_ID | A session identifier is not a UUID |
| 400 | INVALID_LIMIT | The requested limit is outside the supported range |
| 400 | INVALID_STATEMENT | An xAPI statement fails validation |
| 413 | STATEMENT_TOO_LARGE | An xAPI statement exceeds the payload limit |
Packages, Sessions, and Dispatches
| HTTP | Code | Meaning |
|---|---|---|
| 404 | PACKAGE_NOT_FOUND | The package does not exist in the authenticated workspace |
| 404 | SESSION_NOT_FOUND | The session does not exist |
| 404 | UPLOAD_NOT_FOUND | The multipart upload cannot be found |
| 404 | DISPATCH_POSTBACK_NOT_FOUND | The dispatch has no postback delivery to retry |
| 409 | VERSION_CONFLICT | A session update used a stale version |
| 409 | SESSION_ID_SCOPE_MISMATCH | Launch session_id already belongs to a different package or workspace |
| 409 | DISPATCH_POSTBACK_NOT_RETRYABLE | The postback is not in a retryable state |
| 429 | RATE_LIMIT_EXCEEDED | An operation-specific limit was reached |
| 502 | PRESIGN_FAILED | Connect could not mint the upload URL |
| 502 | ARCHIVE_UPLOAD_FAILED | Direct archive upload failed |
| 502 | MULTIPART_INIT_FAILED | Multipart upload initialization failed |
| 502 | MULTIPART_COMPLETE_FAILED | Multipart completion failed |
| 502 | MULTIPART_ABORT_FAILED | Multipart abort failed |
Dispatch launch token failures may return INVALID_DISPATCH_TOKEN or EXPIRED_DISPATCH_TOKEN. A protected dispatch can also return the specific password error supplied by the launch service. Always display the error message and branch programmatically on code.
Webhooks and xAPI
| HTTP | Code | Meaning |
|---|---|---|
| 404 | STATEMENT_NOT_FOUND | The xAPI statement does not exist |
| 400 | INVALID_PAGE | The xAPI offset/page input is invalid |
| 400 | INVALID_STATEMENT | One or more xAPI statements are invalid |
Webhook endpoint creation uses INVALID_JSON and INVALID_REQUEST for invalid payloads. Delivery history and manual retry are dashboard operations, not separate public webhook-delivery endpoints.
Platform Failures
| HTTP | Code | Meaning |
|---|---|---|
| 503 | RATE_LIMIT_UNAVAILABLE | A required durable rate-limit backend is unavailable |
| 503 | BACKEND_UNAVAILABLE | A transient backend operation failed; retry when advised |
| 500 | INTERNAL_ERROR | An unexpected non-transient failure occurred |
BACKEND_UNAVAILABLE responses may include Retry-After. Rate-limited routes use X-RateLimit-*; see Rate Limiting.
Handling Pattern
const response = await fetch(url, init);
const payload = await response.json().catch(() => null);
if (!response.ok) {
const code = payload?.code ?? 'UNKNOWN_ERROR';
if (response.status === 503 && payload?.retryable === true) {
const retryAfterSeconds = Number(response.headers.get('Retry-After'));
scheduleRetry(Number.isFinite(retryAfterSeconds) && retryAfterSeconds >= 0
? retryAfterSeconds
: 5);
}
throw new Error(`${code}: ${payload?.error ?? response.statusText}`);
}