Authentication
Every endpoint requires authentication. Comvi has two methods, and not every endpoint accepts both.
API keys
Section titled “API keys”Create a key in app.comvi.io under Settings > API Keys, pick its scopes, and copy it once. Keys start with comvi_. Pass the key as a Bearer token.
Authorization: Bearer comvi_your_api_key_herecurl -H "Authorization: Bearer $COMVI_API_KEY" https://api.comvi.io/v1/projectGET /v1/project returns the project the key is bound to, which confirms the key works and which project it reaches. See API Keys for the response. The CLI reads the same key from .comvirc.json or COMVI_API_KEY.
Scopes
Section titled “Scopes”| Scope | Grants |
|---|---|
project:read |
Read project metadata, locales, and namespaces |
translations:read |
Read and export translation keys, values, glossaries, translation memory, comments, and history |
translations:write |
Create and update translation keys, values, namespaces, locales, glossary entries, comments, and imports. Also deletes a namespace and clears translation values |
translations:review |
Mark translations reviewed |
translations:delete |
Delete translation keys |
schema:read |
Read key schema and type-generation metadata |
Every key gets project:read, whether or not you ask for it, so the CLI, the fetch loader and the in-context editor can call GET /v1/project. You cannot grant a scope you do not hold: the attempt returns 403 FORBIDDEN with the message Cannot grant API key permissions that you do not have. Grant only the scopes an integration needs.
What a key cannot do
Section titled “What a key cannot do”A key is bound to one project and is refused on every organization-scoped route, in one of two ways.
| Routes | Refusal |
|---|---|
| Organizations, organization members, invitations (session-only) | 401 AUTHENTICATION_REQUIRED. The key never reaches the permission check |
| Organization translation-memory routes and the read-only organization glossary routes (term list, single term, three exports) | 403 FORBIDDEN, message API keys cannot access organization-scoped routes, before scopes are looked at |
| Remaining organization glossary routes (session-only) | 401 AUTHENTICATION_REQUIRED |
A key used against a project other than its own, or whose creator has since lost the underlying permission, gets 403 FORBIDDEN. A key never has more access than its creator has right now.
Sessions
Section titled “Sessions”Sign in with POST /v1/auth/login, sending email and password as JSON. The response is the current user and a session cookie. Login is rate-limited to 10 requests per minute.
curl -X POST -H "Content-Type: application/json" \ -d '{"email": "you@example.com", "password": "your-password"}' \ -c cookies.txt https://api.comvi.io/v1/auth/login
curl -b cookies.txt "https://api.comvi.io/v1/projects?organizationId=1"The cookie is sessionId, and __Host-sessionId in production. It is HttpOnly, SameSite=Strict, Path=/, with no Domain attribute, so only api.comvi.io reads it. There are no CSRF tokens; SameSite=Strict plus the host-bound cookie is the defence.
Which endpoints accept which
Section titled “Which endpoints accept which”| Rule | Accepts | Where |
|---|---|---|
| Session only | The session cookie | Organizations, members, invitations, API key management, creating and deleting projects, adding and removing locales |
| Either | Session cookie or Bearer key | Reading and updating a project, namespaces, translation keys and values, machine translation, glossaries, translation memory, comments, history, webhooks |
| API key only | Bearer key | GET /v1/project, GET /v1/project/locales, the /v1/keys/ routes, type-generation |
| API key only, enforced in the handler | Bearer key | The three CLI import/export routes under /v1/projects/{projectId}/(export|import/validate|import/commit). The middleware accepts either; the handler refuses a session |
A Bearer token sent to a session-only endpoint does not fall back. The Authorization header is never read there, so the request fails with 401 AUTHENTICATION_REQUIRED.
Errors
Section titled “Errors”| Status and code | When |
|---|---|
401 AUTHENTICATION_REQUIRED |
Missing or invalid credentials, or a Bearer token on a session-only endpoint. Message: Authentication required. Provide either a valid session or API key. A session-only route answers the shorter Authentication required |
401 API_KEY_INVALID, API_KEY_DISABLED, API_KEY_EXPIRED, API_KEY_PERMISSIONS_REVOKED |
A malformed, disabled or expired key, or one whose creator lost the granted permissions, only on API-key-only routes. Routes that accept either method collapse every key failure into AUTHENTICATION_REQUIRED |
401 UNAUTHORIZED |
No Bearer header on an API-key-only route (message API key required), or a session on a CLI import/export route (message API key authentication required) |
403 FORBIDDEN |
Valid credentials, not enough access. meta.requiredPermission names the underlying permission, not the scope; map it back through the scope table. meta.hint suggests a new key |
{ "code": "FORBIDDEN", "message": "API key does not have required permission: translation.edit", "meta": { "requiredPermission": "translation.edit", "hint": "Please create a new API key with the required permissions" }}Next steps
Section titled “Next steps”- API Keys to create and revoke keys over the API
- Roles and permissions for the permission system behind the scopes
- Errors for every code