A failed request returns a machine-readable code, a human-readable message, and optional metadata. Branch on code; messages can change, codes stay stable.
{ " code " : " PROJECT_NOT_FOUND " , " message " : " Project not found " , " meta " : { " projectId " : 123 } }
Field
Type
Meaning
code
string
Machine-readable code
message
string
Human-readable description
meta
object
Optional context, varies by error
When the body, params or query fail schema validation, the API returns 422 with code and meta and no message. Only the first failing field is reported.
{ " code " : " FIELD_REQUIRED " , " meta " : { " field " : " name " } }
Code
Cause
meta fields
FIELD_REQUIRED
A required field is missing
field
FIELD_TOO_SHORT
Below the minimum length
field, minLength
FIELD_TOO_LONG
Above the maximum length
field, maxLength
VALIDATION_ERROR
Wrong type, format, pattern, range, or enum value
field, plus one of expectedType, expectedFormat, expectedPattern, minimum, maximum, allowedValues
Status
Meaning
400
The request body or parameters are invalid
401
Missing or invalid authentication
403
Valid credentials but insufficient permissions
404
The resource does not exist
409
The request conflicts with the current state of a resource
413
An uploaded file exceeds the size limit
422
Schema validation failed
429
Rate limit exceeded
500
Unexpected server error
502
An upstream provider (billing, LLM) failed
504
An upstream provider timed out
The status column lists every status a code is sent with.
Code
Status
When
AUTHENTICATION_REQUIRED
401
No valid session or API key
UNAUTHORIZED
401
Authentication was attempted and failed
INVALID_CREDENTIALS
401, 400
Email or password is incorrect. 400 when confirming your current password
INVALID_OR_EXPIRED_TOKEN
400
A reset or verification token is invalid or expired
API_KEY_INVALID
401
API key is malformed or does not exist
API_KEY_DISABLED
401
API key has been deactivated
API_KEY_EXPIRED
401
API key has passed its expiration date
API_KEY_PERMISSIONS_REVOKED
401
The key’s creator no longer holds the permissions it was granted
EMAIL_NOT_VERIFIED
403
Verify the email address before using the API
USER_BLOCKED
403
The account has been blocked
RECENT_SIGN_IN_REQUIRED
403
Sign in again before performing this action
Code
Status
When
FORBIDDEN
403
Access denied. The most common permission failure
INSUFFICIENT_PERMISSION
403
The role lacks the permission this action requires
NO_PROJECT_ACCESS
403, 404
No access to this project. 404 when the user is not assigned to it
NOT_ORGANIZATION_MEMBER
403, 404, 400
Not a member of the organization. 403 when it is the caller, 404 when it is the target user
ROLE_HIERARCHY_VIOLATION
403, 400
Cannot assign or modify a role above your own. 400 when a project role would exceed the organization role
INVALID_ROLE
403
The role is not valid for this action
IMPERSONATION_FORBIDDEN
403
Not allowed while impersonating
Code
Status
When
NOT_FOUND
404
Generic resource not found
USER_NOT_FOUND
404
User does not exist
ORGANIZATION_NOT_FOUND
403, 404
Organization does not exist. 403 when the caller is not a member
PROJECT_NOT_FOUND
403, 404
Project does not exist. 403 when the caller has no access to it
API_KEY_NOT_FOUND
404
API key does not exist
LOCALE_NOT_FOUND
400, 404
Locale does not exist. 400 when it arrives as an invalid input code
TRANSLATION_KEY_NOT_FOUND
404
Translation key does not exist
NAMESPACE_NOT_FOUND
404
Namespace does not exist
WEBHOOK_NOT_FOUND
404
Webhook does not exist
WEBHOOK_DELIVERY_NOT_FOUND
404
Webhook delivery does not exist
INVITATION_NOT_FOUND
404
Invitation does not exist or has already been used
SUBSCRIPTION_NOT_FOUND
404
No subscription found
PLAN_NOT_FOUND
404
The billing plan does not exist
Most “already exists” conditions are 400, not 409.
Code
Status
When
CONFLICT
409
The request conflicts with current state
TRANSACTION_CONFLICT
409
The database rolled the transaction back. Retry the request as-is
EMAIL_ALREADY_USED
409
An account with this email already exists
LOCALE_ALREADY_EXISTS
409
The locale is already added to the project
SUBSCRIPTION_MANUALLY_MANAGED
409
The plan is admin-managed, so self-serve billing changes are refused
SUBSCRIPTION_ALREADY_EXISTS
409
The organization already has a live subscription. Fix or cancel it instead of starting a new checkout
UNIQUE_CONSTRAINT
400
A name that must be unique is already taken
USER_ALREADY_IN_ORGANIZATION
400
User is already a member of this organization
INVITATION_ALREADY_SENT
400
An invitation has already been sent to this email
TRANSLATION_KEY_ALREADY_EXISTS
400
A key with this name already exists in the namespace
PASSWORD_ALREADY_SET
400
Password has already been set for this account
Code
Status
When
VALIDATION_ERROR
422, 400, 409
Schema validation at 422. Business-rule failures use 400; a concurrent-edit conflict uses 409
FIELD_REQUIRED
422
A required field is missing
FIELD_TOO_SHORT
422
Below the minimum length
FIELD_TOO_LONG
422
Above the maximum length
INVALID_INPUT
400
Input data is invalid
SYNTAX_ERROR
400
Request body is not valid JSON
Code
Status
When
FILE_TOO_LARGE
413
Uploaded file exceeds the size limit
FILE_TYPE_NOT_ALLOWED
400
File format is not supported
INVALID_FILE_STRUCTURE
400
File content does not match the expected structure
TOO_MANY_KEYS
400
Import exceeds the maximum number of translation keys
ZIP_BOMB_DETECTED
400
Compressed file detected as potentially malicious
JSON_BOMB_DETECTED
400
JSON file detected as potentially malicious
IMPORT_SESSION_EXPIRED
401
The import session timed out. Start the import again
IMPORT_SESSION_MISMATCH
403
Import confirmation does not match the session that started it
Code
Status
When
TOO_MANY_REQUESTS
429
Any limit the rate-limit plugin enforces: the global limit, every per-route override, or repeated failed authentication. From the global limiter, meta.expiresIn is a human-readable wait such as "1 minute". From the failed-authentication limiter, meta.retryAfter is a number of seconds and there is no expiresIn
RATE_LIMIT_EXCEEDED
429
A route-specific limit the handler enforces itself, such as type generation or the import session bucket
Code
Status
When
READ_ONLY_MODE
403
The organization is in read-only mode. Every write endpoint can return this
LIMIT_EXCEEDED
403
Plan limit reached, such as max keys, projects, or members. meta carries metric, current, and limit
FEATURE_NOT_AVAILABLE
403
Feature not available on the current plan
ACTIVE_SUBSCRIPTION_REQUIRED
400
A paid subscription is still linked, so this change is refused
CANNOT_CANCEL_FREE_PLAN
400
Cannot cancel a free plan
BILLING_NOT_INITIALIZED
500
The organization has no billing record
Code
Status
When
CANNOT_REMOVE_LAST_OWNER
400
Cannot remove or demote the last owner
OWNED_FREE_ORG_LIMIT_EXCEEDED
403
Free plan users can own at most one organization
INVITATION_EXPIRED
400
The invitation has expired
INVALID_INVITATION
403
The invitation was sent to a different email address
CANNOT_DELETE_LAST_PROVIDER
400
Cannot remove the only authentication provider
Code
Status
When
CANNOT_REMOVE_DEFAULT_LOCALE
400
Cannot remove the source language from a project
LOCALE_NOT_ENABLED
400
The locale is not enabled for this project
NOT_ENOUGH_KEYS
422
Too few keys to generate an AI context draft
FAILED_TO_CREATE_PROJECT
500
Internal error during project creation
Code
Status
When
WEBHOOK_URL_NOT_ALLOWED
400
The target URL is not a permitted destination
WEBHOOK_RETRY_NOT_ALLOWED
400
Only deliveries with failed status can be retried
Code
Status
When
INTERNAL_ERROR
500, 502, 504
Unexpected internal error. 502 when an upstream provider failed, 504 when it timed out
INTERNAL_SERVER_ERROR
500
General server error
CACHE_INVALIDATION_FAILED
500
A permission change was refused because its cache flush failed
UNKNOWN
500, 413
An error Comvi did not raise itself, with the status the server gave it: 413 for a body over the route’s limit, otherwise 500. In production a 5xx message is replaced with Internal server error
Header
Meaning
Retry-After
Seconds to wait before retrying
X-RateLimit-Limit
Maximum requests in the window
X-RateLimit-Remaining
Requests remaining in the window
Every limit the rate-limit plugin enforces sends these headers: the global limit and every per-route override, including machine translation, CDN deploys, AI context, and the auth and account routes. RATE_LIMIT_EXCEEDED, raised by handlers, and the 429 from repeated failed authentication carry the wait in meta instead.