Error Codes
Every Zihin API error returns the same JSON shape — an error code, a human-readable message, status: "error", and optional details. Codes are grouped below by category: authentication (401/403), rate limiting (429), request (400/404), provider (5xx), and session control. The HTTP status tells you the class of failure; the error code tells you the exact cause.
Error Format
{
error: string; // Error code
message: string; // Human-readable description
status: "error";
details?: object; // Additional context (optional)
}
Authentication Errors
| Code | HTTP Status | Description |
|---|---|---|
authentication_required | 401 | No authentication provided |
invalid_api_key | 401 | API key is invalid or expired |
invalid_token | 401 | JWT token is invalid or expired |
insufficient_permissions | 403 | User lacks required role |
Rate Limiting Errors
| Code | HTTP Status | Description |
|---|---|---|
rate_limit_exceeded | 429 | Too many requests |
quota_exceeded | 429 | Monthly/daily quota reached |
token_limit_exceeded | 429 | Token pack exhausted |
Request Errors
| Code | HTTP Status | Description |
|---|---|---|
invalid_request | 400 | Malformed request body |
model_not_found | 400 | Specified model doesn't exist |
agent_not_found | 404 | Agent ID not found |
tenant_not_found | 404 | Tenant ID not found |
Provider Errors
| Code | HTTP Status | Description |
|---|---|---|
provider_error | 502 | LLM provider returned an error |
provider_timeout | 504 | LLM provider didn't respond in time |
provider_unavailable | 503 | LLM provider is down |
Session Control Errors
| Code | HTTP Status | Description |
|---|---|---|
INVALID_SESSION_STATE | 409 | POST /:id/manual-message was called while the session is in control_mode='engaged'. Call /handoff or /suspend first. The response details.current_mode confirms the current mode. |
NO_TRIGGER_HISTORY | 422 | POST /:id/manual-message requires at least one previous webhook execution to know which outbound channel to use. Sessions started through SSE chat or other paths without a webhook can't accept manual messages. |
NO_CALLBACK_CONFIG | 422 | The trigger that originated the session has no outbound callback configured in trigger_config.execution.callback. Configure a callback (e.g., Twilio Messages API) and retry. |
DELIVERY_FAILED | 502 | The outbound channel (Twilio/Meta/webhook) returned an error. The response details.statusCode and details.latencyMs carry the provider response. |
Handling Errors
# Example error response
HTTP/1.1 401 Unauthorized
{
"error": "invalid_api_key",
"message": "The provided API key is invalid or expired",
"status": "error"
}
For 429 errors, check the Retry-After header for when to retry.
Common questions
How do I handle a 429?
A 429 means a rate or quota limit was hit (rate_limit_exceeded, quota_exceeded, or token_limit_exceeded). Read the Retry-After header for how many seconds to wait, then retry — see Rate Limits.
What does insufficient_permissions (403) mean?
The request authenticated successfully, but the user or API key role lacks permission for that operation. Check the role's permissions in API Keys → Role System.
What is the difference between a 4xx and a 5xx error?
4xx codes mean the request itself needs to change (bad auth, malformed body, missing resource). 5xx codes — provider_error, provider_timeout, provider_unavailable — mean an upstream LLM provider failed; these are usually transient and safe to retry.