Error envelope
Every error response uses the same JSON shape:| Field | Required | Description |
|---|---|---|
type | Yes | High-level category. Stable across versions. |
message | Yes | Human-readable description. Subject to change. Do NOT pattern-match it programmatically. |
code | No | Machine-readable sub-code. Use this for branching logic. |
param | No | Field name for parameter-specific errors |
Error types
type | Meaning |
|---|---|
authentication_error | Missing, invalid, or expired API key |
invalid_request_error | Malformed body, missing required field, wrong shape |
not_found_error | Resource does not exist or is not owned by the caller |
rate_limit_error | Per-minute rate limit or regen cap |
quota_exceeded | Monthly generation quota exhausted |
server_error | Internal error |
Full code reference
| HTTP | type | code | Meaning |
|---|---|---|---|
| 400 | invalid_request_error | idempotency_key_required | Idempotency-Key header missing on launch |
| 400 | invalid_request_error | (varies) | Malformed request body or invalid field |
| 401 | authentication_error | (none) | Missing or invalid API key |
| 404 | not_found_error | course_not_found | Course ID not found or not owned by caller |
| 404 | not_found_error | preset_not_found | Preset ID not found or not owned by caller |
| 409 | invalid_request_error | already_launched (varies) | Conflict, e.g. course already launched or in wrong state for the action |
| 429 | rate_limit_error | retry_after_<n> | Per-minute rate limit exceeded. <n> is seconds to wait. |
| 429 | rate_limit_error | regen_cap_reached | Account artifact regen cap exhausted; note still recorded |
| 429 | quota_exceeded | quota_exceeded | Monthly generation quota exhausted |
| 500 | server_error | (none) | Internal server error; safe to retry |
Handling
Always branch on type first, then code
type is stable, code is more specific. Pattern:
Never pattern-match message
The text is subject to change. Use type and code.
Retry policy
429 rate_limit_errorwithRetry-After: wait the indicated seconds, then retry.500 server_error: exponential backoff with jitter, max 3 retries.400,401,404,409,429 quota_exceeded: do NOT retry. Surface to your caller.
Privacy: 404 instead of 403
If you request a course ID that exists but belongs to a different account, the API returns404 not_found_error / course_not_found, not 403. This is intentional, to avoid leaking the existence of resources you do not own.