API Error Codes
Stable type URLs for developer API problem+json responses. Full reference in API docs → Error Codes.
| Code | HTTP | Description |
|---|---|---|
| MISSING_API_KEY | 401 | No Authorization header provided |
| INVALID_API_KEY_FORMAT | 401 | Key doesn't start with htai_live_ |
| INVALID_KEY_FORMAT | 401 | Alias of INVALID_API_KEY_FORMAT — malformed key shape (hash length / prefix) from key validation RPC |
| INVALID_KEY | 401 | Key not found or has been revoked |
| KEY_EXPIRED | 401 | Key has expired (rotated) |
| UNKNOWN_ERROR | 401 | Rare auth-path failure when key validation did not return a more specific code. Retry once; if it persists, rotate the key or contact support. |
| UNAUTHORIZED | 403 | Caller is not authorized for this operation |
| NO_ACTIVE_SUBSCRIPTION | 403 | No active API plan on the account (gateway auth path) |
| NO_SUBSCRIPTION | 403 | No developer-surface subscription row for this user (billing reservation path; distinct from NO_ACTIVE_SUBSCRIPTION) |
| PLAN_NOT_FOUND | 403 | Subscription plan_key has no matching plan_configs row |
| BANNED | 403 | Account is suspended |
| RISK_BLOCKED | 403 | Account restricted due to unusual activity |
| TURNSTILE_REQUIRED | 403 | Additional verification required before the request can proceed |
| SCOPE_DENIED | 403 | API key missing a required permission (humanize, detect, or tones). Enable it under Permissions or create a new key. |
| MODE_NOT_AVAILABLE | 403 | Requested mode not included in current plan |
| ENDPOINT_NOT_AVAILABLE | 403 | Endpoint not included in current plan |
| STREAMING_NOT_AVAILABLE | 403 | SSE streaming not included in current plan |
| DETECTION_NOT_AVAILABLE | 403 | AI detection not included in current plan |
| IP_NOT_ALLOWED | 403 | Request IP not in key's allowlist |
| RATE_LIMITED | 429 | Rate limit exceeded — respect Retry-After and retry |
| CONCURRENT_LIMIT | 429 | Too many in-flight humanize/detect requests for your plan concurrent cap. Wait for one to finish, then retry. |
| MISSING_TEXT | 400 | No text field in request body |
| INVALID_TEXT | 400 | Text failed validation: under 20 words, empty after normalize, disallowed characters, or blocked injection/code patterns |
| INVALID_JSON | 400 | Request body is not valid JSON |
| TEXT_TOO_LONG | 400 | Text exceeds plan's maximum words per request (see GET /v1/usage → plan.max_words_per_request) |
| INVALID_MODE | 400 | Unknown humanization mode |
| INVALID_TONE | 400 | Tone is malformed. Use a built-in name, the exact custom tone name, or the tone UUID. |
| INVALID_BEST_OF | 400 | best_of is not an integer between 1 and 5 |
| INVALID_ID | 400 | Tone id path parameter must be a UUID |
| NO_FIELDS | 400 | Tone PUT body has no updatable fields (name and/or instructions required) |
| INVALID_WORD_COUNT | 400 | Word count argument to billing reservation is missing or ≤ 0 |
| INVALID_MULTIPLIER | 400 | Cost multiplier argument to billing reservation is missing or < 1 |
| BEST_OF_NOT_AVAILABLE | 403 | best_of above 3 requires the Scale plan |
| LEGACY_PROMPT_REMOVED | 400 | Legacy "prompt" field is no longer supported — use "text" |
| INVALID_NAME | 400 | Custom tone name missing or not 1–50 characters after sanitization |
| INVALID_INSTRUCTIONS | 400 | Custom tone instructions missing or not 1–500 characters after sanitization |
| INJECTION_DETECTED | 400 | Custom tone field contains blocked injection/code patterns |
| INVALID_IDEMPOTENCY_KEY | 400 | Idempotency-Key header is malformed (must be 1–128 chars of A-Z a-z 0-9 _ - : .) |
| IDEMPOTENCY_KEY_CONFLICT | 409 | Idempotency-Key was already used under a different API key, user, or endpoint. Use a unique key per job. |
| RETRY_REQUIRED | 409 | This Idempotency-Key was already consumed but no stored result is available (expired or original attempt refunded). Mint a new key and retry. |
| INVALID_FREEZE_WORDS | 400 | freeze_words is not an array, has more than 100 entries, or an entry exceeds 100 characters. |
| TONE_NAME_CONFLICT | 409 | A custom tone with that name already exists for this account (case-insensitive). |
| TONE_NAME_RESERVED | 400 | Custom tone name collides with a built-in tone (casual, formal, professional, academic, creative). |
| INSUFFICIENT_WORDS | 402 | Not enough word balance — carries words_required and words_remaining extension fields |
| RESERVATION_FAILED | 500 | Word reservation could not complete — retry. Not a payment or balance error. |
| TONE_NOT_FOUND | 404 | No custom tone matches that name or UUID. List tones with GET /v1/tones or copy from the Custom Tones page. |
| TONE_LIMIT_REACHED | 403 | Custom tone quota for the current plan has been reached. |
| NOT_FOUND | 404 | Unknown path |
| METHOD_NOT_ALLOWED | 405 | Path exists; HTTP method not supported (see Allow header) |
| INVALID_CONTENT_TYPE | 415 | Content-Type must be application/json on write requests |
| PAYLOAD_TOO_LARGE | 413 | Request body exceeds the 128 KiB JSON body limit |
| VALIDATION_FAILED | 400 | Request failed validation (generic developer validation path) |
| CLIENT_CANCELLED | 499 | Detect-only on developer API: client disconnected before /v1/detect completed — not charged / refunded. /v1/humanize cancels collapse to AI_GENERATION_FAILED. |
| AI_TIMEOUT | 504 | Not emitted on developer /v1/humanize (collapsed to AI_GENERATION_FAILED 500). Detect timeouts collapse to DETECTION_FAILED. Present in internal classification / consumer paths. |
| AI_UNAVAILABLE | 503 | All AI providers unavailable — not charged / refunded |
| ABORTED | 504 | Request aborted mid-flight (AbortError) before a typed AI failure was classified |
| INTERNAL_ERROR | 500 | Unexpected server error |
| AI_GENERATION_FAILED | 500 | Humanize AI processing failed on /v1/humanize (timeouts, cancels, and generic AI failures collapse here) — not charged / refunded |
| DETECTION_FAILED | 500 | Detection processing failed on /v1/detect (non-cancel AI failures, including timeouts, collapse here) — not charged / refunded |