Glossaries
Each organization has one glossary; its terms are the editable resource. Project-scoped glossary routes under /v1/projects/{projectId}/glossary/... accept an API key and are covered in the platform glossary guide.
Every endpoint needs a signed-in session, and an API key is turned away in one of two places. The term list, the single-term read and the three exports accept either method, so a key authenticates and is then refused with 403 FORBIDDEN and the message API keys cannot access organization-scoped routes, before scopes are looked at. Every other endpoint is session-only and answers a key with 401 AUTHENTICATION_REQUIRED.
Permissions: glossary.view to read and export, glossary.create to create and import, glossary.edit to update, glossary.delete to delete. glossary.delete has no entry in the API-key scope map, so no scope grants it anywhere, including the project-scoped routes. The other three map to translations:read and translations:write.
GET /v1/organizations/{orgId}/glossary
Section titled “GET /v1/organizations/{orgId}/glossary”Read the organization glossary, creating the default record when necessary. Session only, glossary.view.
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
curl -b cookies.txt https://api.comvi.io/v1/organizations/1/glossaryGET /v1/organizations/{orgId}/glossary/terms
Section titled “GET /v1/organizations/{orgId}/glossary/terms”List terms with cursor pagination. glossary.view.
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
cursor |
query | string |
No | nextCursor from the previous page |
limit |
query | integer |
No | 1 to 100 |
search |
query | string |
No | Up to 255 characters |
termType |
query | preferred | forbidden |
No | Filter by term type |
curl -b cookies.txt "https://api.comvi.io/v1/organizations/1/glossary/terms?search=dashboard&limit=10"{ "terms": [], "nextCursor": null, "hasMore": false }POST /v1/organizations/{orgId}/glossary/terms
Section titled “POST /v1/organizations/{orgId}/glossary/terms”Create a term. Session only, glossary.create.
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
term |
body | string |
Yes | The term |
definition, termType, partOfSpeech, gender |
body | string |
No | Metadata |
isCaseSensitive, isExactMatch, isTranslatable |
body | boolean |
No | Matching flags |
curl -X POST -b cookies.txt -H "Content-Type: application/json" \ -d '{"term":"dashboard","definition":"Main analytics screen","termType":"preferred"}' \ https://api.comvi.io/v1/organizations/1/glossary/termsReturns 201 Created with the complete term, including its translations.
GET /v1/organizations/{orgId}/glossary/terms/{termId}
Section titled “GET /v1/organizations/{orgId}/glossary/terms/{termId}”Read one term. glossary.view.
PATCH /v1/organizations/{orgId}/glossary/terms/{termId}
Section titled “PATCH /v1/organizations/{orgId}/glossary/terms/{termId}”Update any subset of the fields accepted on create. Session only, glossary.edit.
DELETE /v1/organizations/{orgId}/glossary/terms/{termId}
Section titled “DELETE /v1/organizations/{orgId}/glossary/terms/{termId}”Delete a term. Session only, glossary.delete. Returns 204 No Content.
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
termId |
path | integer |
Yes | Term ID |
curl -X DELETE -b cookies.txt https://api.comvi.io/v1/organizations/1/glossary/terms/7POST /v1/organizations/{orgId}/glossary/terms/{termId}/translations
Section titled “POST /v1/organizations/{orgId}/glossary/terms/{termId}/translations”Add a translation to a term. Session only, glossary.edit.
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
termId |
path | integer |
Yes | Term ID |
localeId |
body | integer |
Yes | Target locale ID |
text |
body | string |
Yes | Translated term |
termType |
body | string |
No | preferred or forbidden |
curl -X POST -b cookies.txt -H "Content-Type: application/json" \ -d '{"localeId": 2, "text": "панель керування", "termType": "preferred"}' \ https://api.comvi.io/v1/organizations/1/glossary/terms/7/translationsPATCH /v1/organizations/{orgId}/glossary/terms/{termId}/translations/{translationId}
Section titled “PATCH /v1/organizations/{orgId}/glossary/terms/{termId}/translations/{translationId}”Update a translation by its ID. Session only, glossary.edit.
DELETE /v1/organizations/{orgId}/glossary/terms/{termId}/translations/{translationId}
Section titled “DELETE /v1/organizations/{orgId}/glossary/terms/{termId}/translations/{translationId}”Remove a translation by its ID. Session only, glossary.delete.
POST /v1/organizations/{orgId}/glossary/import
Section titled “POST /v1/organizations/{orgId}/glossary/import”Import terms from CSV. Session only, glossary.create. The body is JSON with the CSV Base64-encoded in content. The schema caps the encoded string at 10 MiB, but the route sets no body limit of its own, so Fastify’s default of 1 MiB caps the request, roughly 780 KB of decoded CSV. A larger body is refused with 413 and code: "UNKNOWN".
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
content |
body | string |
Yes | Base64-encoded CSV |
conflictStrategy |
body | skip | overwrite | merge |
Yes | What to do with an existing term |
columnMapping |
body | object |
No | Maps your headers to Comvi’s column names, such as {"language": "locale_code", "word": "term"} |
CSV columns, as the export writes them so an exported file re-imports without a mapping: concept_id, definition, subject, locale_code, term, term_type, part_of_speech, is_case_sensitive, is_exact_match, is_translatable, is_forbidden, description, gender. locale_code and term are required. A header named locale instead of locale_code is not recognised: the import answers 200 with {"imported": 0, "skipped": 0, "updated": 0, "errors": [{"row": 0, "message": "Missing locale_code column"}]} and writes nothing, and a missing term column behaves the same way. Read errors, not the status. When a row omits concept_id, each translation row must follow the source-locale row it belongs to; rows are grouped by order.
curl -X POST -b cookies.txt -H "Content-Type: application/json" \ -d '{"content":"Y29uY2VwdF9pZCxsb2NhbGVfY29kZSx0ZXJtLGRlZmluaXRpb24KMSxlbixkYXNoYm9hcmQsTWFpbiBhbmFseXRpY3Mgc2NyZWVuCjEsdWss0L/QsNC90LXQu9GMINC60LXRgNGD0LLQsNC90L3RjywK","conflictStrategy":"merge"}' \ https://api.comvi.io/v1/organizations/1/glossary/importThat content decodes to:
concept_id,locale_code,term,definition1,en,dashboard,Main analytics screen1,uk,панель керування,POST /v1/organizations/{orgId}/glossary/import/json
Section titled “POST /v1/organizations/{orgId}/glossary/import/json”Import terms from JSON. Session only, glossary.create. The body mirrors the JSON export, so a glossary round-trips between organizations. Up to 100,000 concepts; the body is capped at 10 MB.
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
version |
body | string |
Yes | "1.0" |
concepts |
body | object[] |
Yes | Up to 100,000 |
concepts[].terms |
body | object[] |
Yes | Each with localeCode and text; optional termType, description, partOfSpeech, gender, isCaseSensitive, isExactMatch |
concepts[].definition |
body | string |
No | Up to 2000 characters |
concepts[].subject |
body | string |
No | Up to 255 characters |
concepts[].isTranslatable |
body | boolean |
No | false for names never translated |
conflictStrategy |
body | skip | overwrite | merge |
Yes | What to do with an existing term |
sourceLocaleCode |
body | string |
No | Source locale of the concepts |
curl -X POST -b cookies.txt -H "Content-Type: application/json" \ -d '{"version":"1.0","sourceLocaleCode":"en","conflictStrategy":"merge","concepts":[{"definition":"Main analytics screen","isTranslatable":true,"terms":[{"localeCode":"en","text":"dashboard","termType":"preferred"},{"localeCode":"uk","text":"панель керування"}]}]}' \ https://api.comvi.io/v1/organizations/1/glossary/import/jsonBoth imports answer with the same result. A row you cannot write because of a language restriction is reported in errors as Access denied to locale: <code> and is not counted in skipped. Unknown locale codes and rows missing locale_code or term land there too, with their row number.
{ "imported": 120, "skipped": 4, "updated": 11, "errors": [{ "row": 45, "message": "Unknown locale: xx" }] }GET /v1/organizations/{orgId}/glossary/export/csv
Section titled “GET /v1/organizations/{orgId}/glossary/export/csv”GET /v1/organizations/{orgId}/glossary/export/json
Section titled “GET /v1/organizations/{orgId}/glossary/export/json”GET /v1/organizations/{orgId}/glossary/export/tbx
Section titled “GET /v1/organizations/{orgId}/glossary/export/tbx”Export the glossary as a file with a Content-Disposition: attachment header. glossary.view. Use curl -o.
| Parameter | In | Type | Required | Meaning |
|---|---|---|---|---|
orgId |
path | integer |
Yes | Organization ID |
localeIds |
query | string |
No | Comma-separated locale IDs |
| Format | Content-Type | Use it for |
|---|---|---|
csv |
text/csv; charset=utf-8 |
Spreadsheets and quick edits |
json |
application/json |
Round-tripping through the JSON import. Body: { "version": "1.0", "sourceLocaleCode": "...", "concepts": [...] } |
tbx |
application/xml; charset=utf-8 |
TBX-speaking CAT tools |
curl -b cookies.txt -o glossary.tbx "https://api.comvi.io/v1/organizations/1/glossary/export/tbx?localeIds=1,2"Errors
Section titled “Errors”Standard envelope, except 422 validation failures, which carry code and meta and no message.
| Status | When |
|---|---|
400 |
Invalid cursor, body, locale, or term shape |
401 AUTHENTICATION_REQUIRED |
No session cookie, the session is no longer valid, or an API key on a session-only endpoint |
403 FORBIDDEN |
Missing glossary permission or locale access, or an API key on an endpoint that accepts either method |
403 READ_ONLY_MODE |
The organization is over its plan limits and can only read |
404 |
Organization, term, or translation not found |
422 FIELD_REQUIRED, FIELD_TOO_LONG, FIELD_TOO_SHORT, VALIDATION_ERROR |
A field is missing, over its length, empty, or has the wrong type, format, pattern, range or enum. meta.field names it |
Next steps
Section titled “Next steps”- Glossaries in the platform to configure terminology in the dashboard
- Machine Translation for glossary-aware suggestions