Error Codes
All API errors return a consistent JSON structure with machine-readable codes for programmatic handling.
Error Format
{
"error": {
"message": "Invalid request body",
"statusCode": 400,
"code": "VALIDATION_ERROR",
"category": "VALIDATION",
"details": [{ "path": "events.0.type", "message": "Invalid enum value" }]
}
}
| Field | Type | Description |
|---|---|---|
message | string | Human-readable error description |
statusCode | number | HTTP status code |
code | string | Machine-readable error code |
category | string | Error category (see below) |
details | any | Additional context (validation errors, etc.) |
Error Categories
| Category | Description |
|---|---|
VALIDATION | Request body, query, or params failed schema validation |
AUTH | Authentication or authorization failure |
NOT_FOUND | Requested resource does not exist |
CONFLICT | Resource already exists or is in use |
RATE_LIMIT | Request rate exceeded allowed threshold |
EXTERNAL_SERVICE | Third-party service (Google, AI provider) failed |
INTERNAL | Unexpected server error |
Common Error Codes
Validation Errors (400)
| Code | Description |
|---|---|
VALIDATION_ERROR | Request body or parameters failed Zod schema validation |
INVALID_INVITE_TOKEN | Invite token is malformed or expired |
INVALID_RESET_TOKEN | Password reset token is malformed or expired |
MISSING_TENANT_ID | Company context could not be determined |
Authentication Errors (401)
| Code | Description |
|---|---|
AUTH_FAILED | API key is missing, invalid, revoked, or expired |
AUTH_NO_TOKEN | No JWT token found in cookie or Authorization header |
AUTH_INVALID_TOKEN | JWT token is malformed or expired |
AUTH_TOKEN_REVOKED | JWT token has been explicitly revoked |
AUTH_INVALID_CREDENTIALS | Email/password combination is incorrect |
AUTH_PROVIDER_MISMATCH | Account was registered with a different auth method (e.g., Google) |
AUTH_NO_REFRESH_TOKEN | Refresh token cookie is missing |
AUTH_INVALID_REFRESH_TOKEN | Refresh token is invalid or expired |
Authorization Errors (403)
| Code | Description |
|---|---|
FORBIDDEN | Valid authentication but insufficient permissions |
INSUFFICIENT_ROLE | User role does not have access to this operation |
Not Found Errors (404)
| Code | Description |
|---|---|
NOT_FOUND | Generic resource not found |
USER_NOT_FOUND | Referenced user does not exist |
TENANT_NOT_FOUND | Referenced company does not exist |
CONTENT_NOT_FOUND | Flow, tooltip, survey, or other content not found |
Conflict Errors (409)
| Code | Description |
|---|---|
EMAIL_EXISTS | Email address is already registered |
TENANT_EXISTS | A company with this name already exists |
MEMBER_EXISTS | User is already a member of this company |
AUDIENCE_IN_USE | Audience is referenced by active content and cannot be deleted |
Rate Limit Errors (429)
| Code | Description |
|---|---|
RATE_LIMITED | Too many requests. Check Retry-After header for when to retry |
Internal Errors (500)
| Code | Description |
|---|---|
INTERNAL_ERROR | Unexpected server error |
EXTERNAL_SERVICE_ERROR | Third-party service call failed |
REGISTRATION_FAILED | User/company registration could not be completed |
Troubleshooting
401 on SDK routes: Verify the API key is passed via the X-API-Key header (not Authorization: Bearer). Ensure the key has not been revoked in the dashboard.
400 with validation details: Check the details array for specific field paths and messages. Event payloads require a valid type field from the supported event types.
429 rate limited: SDK routes allow 600 requests/min per API key. Batch multiple events in a single POST /api/sdk/events request instead of sending them individually.
500 errors: Retry with exponential backoff. If persistent, check the API health endpoint at GET /api/health.