Skip to content

Members

View .md

Members give people access to organizations and projects. Invite by email, assign roles, restrict languages, and remove access.

Every endpoint takes a session cookie, except GET /v1/invitations/{token}, which is public. A Bearer key returns 401 AUTHENTICATION_REQUIRED; see Sessions. Organization endpoints need organization.manage_members (owners and managers); project endpoints need project.manage_members on the project. Common errors: 401 AUTHENTICATION_REQUIRED, 403 FORBIDDEN, 403 READ_ONLY_MODE on writes. The full list is under Errors.

Role Grants
owner Full control, including billing and member management
manager Manage projects, members, and settings
editor Edit translations and keys
translator Translate content in assigned languages
viewer Read-only access

Owner and manager carry every project permission. At the organization level a manager lacks organization.delete and organization.manage_billing, so deleting the organization and every billing route are owner-only. Ownership moves only through POST /v1/organizations/{orgId}/transfer-ownership.

Organization roles set a baseline; a project role can restrict but not elevate, and a project role above the organization role is rejected with 400 ROLE_HIERARCHY_VIOLATION. You cannot assign a role above your own, modify a member above you, or change your own role: 403 ROLE_HIERARCHY_VIOLATION or 403 FORBIDDEN.

List organization members with their roles. An organization with no members returns an empty array.

Parameter In Type Required Meaning
orgId path integer Yes Organization ID
Terminal window
curl -b cookies.txt https://api.comvi.io/v1/organizations/1/members
[{ "userId": 43, "email": "bob@example.com", "name": "Bob Johnson", "role": "translator", "joinedAt": "2025-02-15T14:30:00Z", "accessScope": "project", "projectCount": 2 }]
Field Type Meaning
role string Organization role
accessScope string organization for all projects, project for a chosen set
projectCount integer Projects the member can access. Optional

POST /v1/organizations/{orgId}/members/invite

Section titled “POST /v1/organizations/{orgId}/members/invite”

Invite by email. An email with a Comvi account is added immediately and no invitation is created; otherwise an invitation is stored and emailed. Invitations expire after 7 days and cannot be resent once expired, see Recovering an expired invitation.

Parameter In Type Required Meaning
orgId path integer Yes Organization ID
email body string Yes Address to invite
role body string Yes One of the five roles
projectId body integer | null No Grant one project instead of the whole organization
languageRestrictions body string[] | null No Locale codes, such as ["fr", "de"]
Terminal window
curl -X POST -b cookies.txt -H "Content-Type: application/json" \
-d '{"email": "translator@example.com", "role": "translator", "projectId": 5, "languageRestrictions": ["fr", "de"]}' \
https://api.comvi.io/v1/organizations/1/members/invite

Returns 200 OK in both cases with the same shape. The invitation token is never returned: it reaches the invited address only in the invitation email. To tell the two cases apart, list the pending invitations; a new invitation appears there.

{ "id": 12, "email": "translator@example.com", "role": "translator", "projectId": 5, "languageRestrictions": ["fr", "de"], "expiresAt": "2025-04-01T08:00:00Z", "createdAt": "2025-03-25T08:00:00Z" }
Field New user Existing user
id Invitation ID User ID
expiresAt When the invitation expires The current time. Ignore it

GET /v1/organizations/{orgId}/members/{userId}/access

Section titled “GET /v1/organizations/{orgId}/members/{userId}/access”

A member’s full access picture, for populating an edit form before PUT …/access.

Parameter In Type Required Meaning
orgId path integer Yes Organization ID
userId path integer Yes Member’s user ID
Terminal window
curl -b cookies.txt https://api.comvi.io/v1/organizations/1/members/43/access
{ "userId": 43, "accessScope": "project", "role": "translator", "projectIds": [5, 8], "languageRestrictions": ["fr", "de"], "projectAccessEntries": [], "hasProjectOverrides": false, "hasMixedProjectRoles": false, "hasMixedLanguageRestrictions": false, "requiresProjectLevelManagement": false }
Field Type Meaning
projectIds integer[] Assigned projects
languageRestrictions string[] | null Locale codes, or null for all
projectAccessEntries object[] Per-project role and restrictions
hasProjectOverrides boolean Some project gives a role other than the organization role
hasMixedProjectRoles boolean Per-project roles differ
hasMixedLanguageRestrictions boolean Per-project restrictions differ
requiresProjectLevelManagement boolean Too varied to edit here; PUT …/access refuses with 409. Edit each project instead

PUT /v1/organizations/{orgId}/members/{userId}/access

Section titled “PUT /v1/organizations/{orgId}/members/{userId}/access”

Set scope, role, project assignments and language restrictions in one call.

Parameter In Type Required Meaning
orgId path integer Yes Organization ID
userId path integer Yes Member’s user ID
accessScope body string Yes organization or project
role body string Yes Role to apply
projectIds body integer[] When project Duplicates fail schema validation with 422; an empty array returns 400
languageRestrictions body string[] | null No Locale codes, or null for all
Terminal window
curl -X PUT -b cookies.txt -H "Content-Type: application/json" \
-d '{"accessScope": "project", "role": "translator", "projectIds": [5, 8], "languageRestrictions": ["fr", "de"]}' \
https://api.comvi.io/v1/organizations/1/members/43/access

Returns 204 No Content.

PATCH /v1/organizations/{orgId}/members/{userId}

Section titled “PATCH /v1/organizations/{orgId}/members/{userId}”

Change a member’s organization role, leaving project assignments alone. Demoting the last owner returns 400 CANNOT_REMOVE_LAST_OWNER; demoting an owner while a paid subscription is linked returns 400 ACTIVE_SUBSCRIPTION_REQUIRED.

Parameter In Type Required Meaning
orgId path integer Yes Organization ID
userId path integer Yes Member’s user ID
role body string Yes New role
Terminal window
curl -X PATCH -b cookies.txt -H "Content-Type: application/json" -d '{"role": "editor"}' \
https://api.comvi.io/v1/organizations/1/members/43

Returns 204 No Content.

POST /v1/organizations/{orgId}/transfer-ownership

Section titled “POST /v1/organizations/{orgId}/transfer-ownership”

Hand ownership to another member; the current owner becomes a manager. Only the current owner may call it, although the route checks organization.manage_members, and the new owner must already have organization-wide access.

Parameter In Type Required Meaning
orgId path integer Yes Organization ID
newOwnerId body integer Yes User ID of the new owner
Terminal window
curl -X POST -b cookies.txt -H "Content-Type: application/json" -d '{"newOwnerId": 43}' \
https://api.comvi.io/v1/organizations/1/transfer-ownership

Returns 204 No Content.

Status and code When
400 INVALID_INPUT newOwnerId is the caller
403 FORBIDDEN The caller is not the current owner
409 CONFLICT The target has project-scoped access, or is already an owner

DELETE /v1/organizations/{orgId}/members/{userId}

Section titled “DELETE /v1/organizations/{orgId}/members/{userId}”

Remove a user from the organization and every project in it. You cannot remove yourself or the last owner.

Terminal window
curl -X DELETE -b cookies.txt https://api.comvi.io/v1/organizations/1/members/43

Returns 204 No Content.

List invitations not yet accepted. Expired ones are included, so check expiresAt.

Terminal window
curl -b cookies.txt https://api.comvi.io/v1/organizations/1/invitations
[{ "id": 12, "email": "translator@example.com", "role": "translator", "invitedByUserId": 42, "invitedByName": "Jane Smith", "projectId": 5, "projectName": "my-web-app", "languageRestrictions": ["fr", "de"], "expiresAt": "2025-04-01T08:00:00Z", "createdAt": "2025-03-25T08:00:00Z" }]

What an invitation is for, so the recipient can see who invited them and to what before signing up. Public: the 64-character token is the only credential. An expired token returns 400 INVITATION_EXPIRED and deletes the invitation, so anyone opening an expired link removes it; afterwards it is absent from the list and cancel and resend return 404 INVITATION_NOT_FOUND. To recover, invite again: the duplicate check finds no row, so the re-invite succeeds with a fresh token and a new 7-day window.

Parameter In Type Required Meaning
token path string Yes Token from the email link, 64 characters
Terminal window
curl https://api.comvi.io/v1/invitations/a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90
{ "email": "translator@example.com", "organizationName": "Acme Corp", "role": "translator", "inviterName": "Jane Smith", "projectName": "my-web-app", "languageRestrictions": ["fr", "de"], "expiresAt": "2025-04-01T08:00:00Z" }
Status and code When
400 INVITATION_EXPIRED Expired. The invitation is deleted
404 INVITATION_NOT_FOUND Unknown or already used token

Join the organization the invitation names. The caller must be signed in as the invited email address. No body.

Terminal window
curl -X POST -b cookies.txt https://api.comvi.io/v1/invitations/a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90/accept
{ "organizationId": 1, "organizationName": "Acme Corp", "role": "translator" }
Status and code When
400 INVITATION_EXPIRED Expired. The invitation is deleted, so the token cannot be tried again
400 USER_ALREADY_IN_ORGANIZATION Already a member. The invitation is deleted
403 INVALID_INVITATION Signed in with a different email than the invitation was sent to

POST /v1/organizations/{orgId}/invitations/{invitationId}/resend

Section titled “POST /v1/organizations/{orgId}/invitations/{invitationId}/resend”

Send the invitation email again with the same token; the expiry is not extended. An expired invitation returns 400 INVITATION_EXPIRED and nothing is sent.

Terminal window
curl -X POST -b cookies.txt https://api.comvi.io/v1/organizations/1/invitations/12/resend

Returns 204 No Content.

DELETE /v1/organizations/{orgId}/invitations/{invitationId}

Section titled “DELETE /v1/organizations/{orgId}/invitations/{invitationId}”

Delete a pending invitation; its token stops working. An expired invitation can be cancelled as long as its row still exists.

Terminal window
curl -X DELETE -b cookies.txt https://api.comvi.io/v1/organizations/1/invitations/12

Returns 204 No Content, or 404 INVITATION_NOT_FOUND when the row is already gone.

Start with GET /v1/organizations/{orgId}/invitations. If the invitation is listed, cancel it, then invite again; skipping the cancel returns 400 INVITATION_ALREADY_SENT, because the duplicate check matches on email, organization and project without looking at expiresAt. If it is not listed, someone opened its link and it was deleted; invite again directly. Either way the new invitation carries a fresh token and a new 7-day window, and the old token stops working.

Everyone explicitly assigned to the project, with organization and project roles.

Parameter In Type Required Meaning
projectId path integer Yes Project ID
Terminal window
curl -b cookies.txt https://api.comvi.io/v1/projects/5/members
[{ "userId": 44, "email": "maria@example.com", "name": "Maria Garcia", "organizationRole": "translator", "projectRole": "translator", "languageRestrictions": ["es", "pt"], "addedAt": "2025-03-01T12:00:00Z" }]

languageRestrictions is null for all languages.

Add an existing organization member to a project. A role above the user’s organization role returns 400 ROLE_HIERARCHY_VIOLATION.

Parameter In Type Required Meaning
projectId path integer Yes Project ID
userId body integer Yes Organization member to add
role body string Yes Project role
languageRestrictions body string[] No Locale codes
Terminal window
curl -X POST -b cookies.txt -H "Content-Type: application/json" \
-d '{"userId": 44, "role": "translator", "languageRestrictions": ["es", "pt"]}' \
https://api.comvi.io/v1/projects/5/members

Returns 201 Created with an empty body.

PATCH /v1/projects/{projectId}/members/{userId}

Section titled “PATCH /v1/projects/{projectId}/members/{userId}”

Update a project member’s role or language restrictions.

Parameter In Type Required Meaning
projectId path integer Yes Project ID
userId path integer Yes Member’s user ID
role body string Yes New project role
languageRestrictions body string[] | null No Locale codes, or null for all
Terminal window
curl -X PATCH -b cookies.txt -H "Content-Type: application/json" \
-d '{"role": "editor", "languageRestrictions": null}' https://api.comvi.io/v1/projects/5/members/44

Returns 204 No Content.

DELETE /v1/projects/{projectId}/members/{userId}

Section titled “DELETE /v1/projects/{projectId}/members/{userId}”

Remove a user from a project, not from the organization. You cannot remove yourself, and the user must be explicitly assigned: removing someone with organization-wide access only returns 404 NO_PROJECT_ACCESS.

Terminal window
curl -X DELETE -b cookies.txt https://api.comvi.io/v1/projects/5/members/44

Returns 204 No Content.

The only 409 is CONFLICT; the “already” conditions are 400.

Status and code When
400 CANNOT_REMOVE_LAST_OWNER Removing or demoting the last owner
400 ROLE_HIERARCHY_VIOLATION A project role would exceed the organization role
400 USER_ALREADY_IN_ORGANIZATION Already a member, or already has access to the project
400 INVITATION_ALREADY_SENT An invitation is pending for this email
400 INVITATION_EXPIRED The invitation has expired
400 ACTIVE_SUBSCRIPTION_REQUIRED Removing an owner while a paid subscription is linked
400 INVALID_INPUT Empty projectIds on a project-scoped update, or transferring ownership to yourself
401 AUTHENTICATION_REQUIRED Missing session cookie, or a Bearer key
403 FORBIDDEN Missing organization.manage_members or project.manage_members, or modifying your own access
403 ROLE_HIERARCHY_VIOLATION Assigning or modifying a role above your own
403 INVALID_INVITATION The invitation was sent to a different email
403 LIMIT_EXCEEDED Member limit reached for the plan
403 READ_ONLY_MODE The organization is read-only
404 USER_NOT_FOUND The user does not exist. Project member endpoints return the same for a user outside the project’s organization
404 NOT_ORGANIZATION_MEMBER The target user is not in the organization. 403 when the caller is not
404 NO_PROJECT_ACCESS The target user is not explicitly assigned to the project
404 ORGANIZATION_NOT_FOUND The organization does not exist
404 PROJECT_NOT_FOUND The project does not exist
404 INVITATION_NOT_FOUND The invitation does not exist or was used
409 CONFLICT Ownership transfer to an ineligible member, or an access update blocked by project-level overrides
422 VALIDATION_ERROR Duplicate entries in projectIds. meta.field names the field
422 FIELD_REQUIRED A required body field is missing, such as email, role, accessScope or newOwnerId
500 CACHE_INVALIDATION_FAILED The permission cache could not be flushed first. Retry