Term lists
The API reference lists every endpoint with its request and response schema, rendered from the OpenAPI document; this page explains how the calls behave. The term list calls are panel calls rather than part of the published reference, so they are listed here.
Term list calls
A term list is a client term base loaded as one object with an identity: a name, the file it came from, the client's export date, the language column that was read, a scope and a severity of its own. Every finding it produces names all of that in its evidence. A row with only a source term is a do-not-translate entry (the term must stay as it is in the target); a row with a second column is a glossary entry (the target must use that form, suffixes allowed). Lists are unioned by name up the scope hierarchy (org, account, division, language, profile); the most specific scope wins on a name collision. /api/terms/parse takes a workspace API key with a qa:read or qa:write scope, or the panel session. The term-list calls that store a list use the panel session only, and a shared scope needs the PM role.
| Endpoint | What it does |
|---|---|
POST /api/terms/parse | Multipart file (.txt, .csv, .tsv, .xlsx, .xls; max 10 MB). Column 1 is the term, column 2 the optional expected target; a header row is dropped. Returns count (the terms read), terms (the flat first column, at most 2,000: the size of the Brands and terms to keep list), terms_truncated and terms_dropped (how many did not fit in terms), entries ([{source, target?}], de-duplicated case-insensitively, capped at 20,000), has_targets, dnt_count, glossary_count. A target equal to its source is dropped, so the row stays a do-not-translate entry. |
GET /api/profiles/{name}/term-lists | The profile's effective lists, own and inherited, without entries: {lists: [{name, scope, severity, origin, exported_at, column, loaded_at, loaded_by, dnt_count, glossary_count, count}], count}. |
POST /api/profiles/{name}/term-lists | JSON body: name (required, the list's identity), entries (from the parse call; omit to change only the metadata of an existing list), severity (error / warning / info / off; default warning), origin (the client file name), exported_at, column, inflection (optional bool; unset lets the target language decide), target (profile, account, division:<division>, account_lang:<lang>, division_lang:<division>/<lang>; default profile). A list of the same name at that scope is replaced, which is how a client's newer export goes in. Stamps scope, loaded_at and loaded_by. Returns {ok, scope, list}. |
DELETE /api/profiles/{name}/term-lists/{list}?target= | Removes one list from the scope the GET reports for it. 404 when no list of that name is stored at that scope. target takes the short form (account_lang:tr) or the stored label the GET reports (account_lang:acme/tr). |
GET /api/profiles/{name}/term-lists/{list}/entries?target= | The entries of one stored list at its scope: {name, scope, entries: [{source, target?}], count}. |
DELETE /api/profiles/{name}/term-lists/{list}/entries?target=&source= | Removes ONE entry by its source term (exact) and keeps the rest; the list keeps its identity and dates. 404 when the list or the term is not there. An emptied list is removed. Returns {ok, removed, list, scope, list_removed}. |
How a list is matched: the source term must be present in the source segment as a whole word, with its case; a hyphen next to it continues the word (Acme One does not match Acme One-Year). On the target side bidi marks are stripped; a do-not-translate term keeps its case; a glossary form is compared without regard to case; each word may carry a suffix on inflecting targets; a word of three characters or fewer is matched whole; edge punctuation is direction-neutral in an Arabic string; a Turkish compound may drop the prescribed possessive when the next word carries one. A do-not-translate entry wholly inside a longer glossary entry's span is that entry's, so a client ruling on the longer term is never contradicted by the shorter one. On an RTL target a glossary miss is at most a warning.
curl -s -X POST https://app.stillneedsahuman.com/api/terms/parse -H "Authorization: Bearer snah_..." \
-F "file=@acmecare_td_tr_glossary.csv"
# -> {"count": 157, "entries": [{"source": "carry-in repair", "target": "bizzat teslim"}, ...], ...}
# storing the list needs a signed-in panel session (its cookie), not an API key
curl -s -X POST https://app.stillneedsahuman.com/api/profiles/acme_tr/term-lists -b "ww_session=$SESSION" \
-H "Content-Type: application/json" \
-d '{"name": "AcmeCare TD (Turkish)", "origin": "acmecare_td_tr_glossary.csv", "exported_at": "2026-09-16",
"column": "Turkish", "severity": "warning", "target": "division_lang:acmecare/tr", "entries": [...]}'
# -> {"ok": true, "scope": "division_lang:acme/acmecare/tr", "list": {"name": "AcmeCare TD (Turkish)", "count": 157, ...}}