Skip to navigation

The Style Agent Terminology API reads and writes the same terminology you manage in the console: entries, terms, languages, statuses, domains and fields. If you have scripts or integrations built on the Acrolinx Terminology API v9, such as a termbase sync, term contribution or nightly backups, this page shows how to change each call.

Entries are still exchanged as ACTIF 1.0 XML, and categories and fields are still referred to by name, so most integrations only change the host, the authentication and a few paths. New integrations can also use the JSON endpoints in this section, which address terms and categories by id.

What changes

Acrolinx Terminology API v9Style Agent Terminology API
Base URLhttps://<your-host>/iq/services/v9/rest/terminologyhttps://api.markup.ai/style-agent/terminology
AuthenticationAn auth token, then a session from requestSession, sent on every call and released afterwardsAn API key, sent as Authorization: Bearer <API key> on every request
Entry formatsACTIF XML, ACTIF JSON, MTFACTIF 1.0 XML
Search resultsJSONJSON
PublishingdeployTerminology, synchronous triggerPOST /publish, a background job you poll

Paths on the rest of this page are relative to the base URL.

Authenticate

There are no sessions to request or release. Create an API key in the console and send it on every request. The key belongs to your organization, so every call reads and writes that organization’s terminology.

TOKEN=$(curl -s -X POST "https://<your-host>/iq/services/v3/rest/core/authToken" \
-H "username: $USER_NAME" -H "encryptedOrPlaintextPassword: $PASSWORD")
SESSION=$(curl -s -X POST "https://<your-host>/iq/services/v3/rest/core/requestSession" \
-H "authToken: $TOKEN" -H "Content-Type: application/json" \
-d '{"sessionType": "TERMINOLOGY", "clientSignature": "<signature>", "clientInfo": {"name": "sync"}}')
curl "https://<your-host>/iq/services/v9/rest/terminology/schema" \
-H "Authorization: session $SESSION"
curl -X DELETE "https://<your-host>/iq/services/v3/rest/core/releaseSession/$SESSION"

The Python samples below use this api client.

Call by call

Taskv9Now
Read the schemaGET /schemaGET /schema
Search termsPOST /searchTerms, GET /searchTerms/{sessionId}/{query}GET /terms
Search entriesPOST /searchEntriesGET /terms with domain
Get entriesGET /entries/{entryIDorUUIDs}GET /entries/{entry}
Create or update entriesPOST /entriesPOST /entries
Delete an entryDELETE /entries/{entryUUID}DELETE /entries/{entry}
Propose a termPOST /candidatesPOST /terms/contribute
DeployPUT /deployTerminologyPOST /publish, then GET /jobs/{job_id}
Back upGET /dump/{sessionId}, POST /dump/request/{sessionId}GET /export/xml-dump
RestorePUT /dump/restorePOST /import/xml-dump, then GET /jobs/{job_id}
Export ACTIFPOST /exportTerminology/{sessionId}GET /export/actif
Upload or download an imagePOST, GET /media/{fileName}POST /images, GET /images/{filename}

Read the schema

GET /schema returns an ACTIF document with only the <schema> element: your languages, statuses, domains, other category types and fields, by name. These are the names an entry you post can use.

curl "https://<your-host>/iq/services/v9/rest/terminology/schema" \
-H "Authorization: session $SESSION" \
-H "Accept: application/vnd.acrolinx.actif+xml"

Search terms

GET /terms replaces both search calls. Filter by name with language, status and domain; repeat status or domain to match any of several. A domain also matches its sub-domains.

match says how query is compared, ignoring case:

  • contains (the default): query appears anywhere in the term.
  • exact: the whole term equals query.
  • wildcard: as in v9, % matches any run of characters and _ matches one. Put \ before either to match it literally.

Results are paged with page and page_size rather than offset and limit. Each term carries its entry as conceptuuid and conceptid, which you pass to GET /entries. To list the entries in some domains, as searchEntries did, pass domain without a query and collect the conceptuuid values, or pass group_by_concept=true to get every term of each matching entry.

curl -X POST "https://<your-host>/iq/services/v9/rest/terminology/searchTerms" \
-H "Authorization: session $SESSION" \
-H "Content-Type: application/json" \
-d '{"criteria": {"name": "data%", "language": "en", "domains": ["Product"]},
"pagination": {"offset": 0, "limit": 100}}'

An unknown language, status or domain name returns 400 naming it, instead of an empty result.

Get entries

GET /entries/{entry} returns whole entries as one ACTIF document. Pass up to 100 entry uuids or entry ids, separated by commas; each value is tried as a uuid first, then as an id. The document holds every language and the <schema>, and returns 404 if none of the values match.

curl "https://<your-host>/iq/services/v9/rest/terminology/entries/$ENTRY_UUID,$OTHER_ENTRY_UUID" \
-H "Authorization: session $SESSION" \
-H "Accept: application/vnd.acrolinx.actif+xml"

Values come back as stored, without default values filled in, so a document you fetch can be posted back without changing anything.

Create or update entries

POST /entries takes an ACTIF 1.0 document of up to 500 entries and 10 MB, with Content-Type set to application/vnd.acrolinx.actif+xml, application/xml or text/xml.

  • An entry whose entry/uuid matches a stored entry replaces it. Any other entry is created, keeping the entry/uuid and term/uuid values it carries.
  • Terms are matched by term/uuid. A stored term the entry leaves out is deleted, and a field the entry leaves empty is cleared.
  • The term in <head> becomes the entry’s head term.
  • Dates, users, frequency and history events in the document are ignored; the server records them.

The document is stored entirely or not at all. The response lists each stored entry with its uuid, id and whether it was created.

curl -X POST "https://<your-host>/iq/services/v9/rest/terminology/entries" \
-H "Authorization: session $SESSION" \
-H "Content-Type: application/vnd.acrolinx.actif+xml" \
--data-binary @entries.xml

A minimal document with one new entry:

entries.xml
<actif xmlns="http://www.acrolinx.com" version="1.0">
<data>
<entry>
<head>
<term>
<name>data center</name>
<language>en</language>
<value field="term/status">preferred</value>
<value field="term/domain">Product</value>
</term>
</head>
<term>
<name>datacenter</name>
<language>en</language>
<value field="term/status">deprecated</value>
<value field="term/domain">Product</value>
</term>
</entry>
</data>
</actif>

Change part of an entry

Because a posted entry replaces the stored one, change an entry the way v9 integrations already do: fetch it, edit the document and post it back. This adds a synonym; changing a status or a domain is the same edit on term/status or term/domain.

Python
NS = "http://www.acrolinx.com"
ET.register_namespace("", NS)
response = api.get(f"/entries/{entry_uuid}", headers={"Accept": "application/vnd.acrolinx.actif+xml"})
response.raise_for_status()
document = ET.fromstring(response.content)
entry = document.find(f".//{{{NS}}}entry")
term = ET.SubElement(entry, f"{{{NS}}}term")
ET.SubElement(term, f"{{{NS}}}name").text = "data centre"
ET.SubElement(term, f"{{{NS}}}language").text = "en"
ET.SubElement(term, f"{{{NS}}}value", field="term/status").text = "admitted"
api.post(
"/entries",
content=ET.tostring(document),
headers={"Content-Type": "application/vnd.acrolinx.actif+xml"},
).raise_for_status()

Errors

Names are resolved against GET /schema. A language, status, domain, field or list value that isn’t there is an error, not created on the fly. A 400 lists every problem in the document at once, and nothing is stored:

{
"detail": "The entries were not stored",
"status": 400,
"request_id": "...",
"errors": ["entry 1, term 'data center': unknown domain 'Prodcut'"]
}

Delete an entry

DELETE /entries/{entry} deletes an entry and all of its terms and returns 204.

curl -X DELETE "https://<your-host>/iq/services/v9/rest/terminology/entries/$ENTRY_UUID" \
-H "Authorization: session $SESSION"

Propose a term

POST /terms/contribute replaces /candidates. It creates a new entry with the proposed status for a terminologist to review, and records the comment in the term’s history. It takes the language and at least one domain as category ids, which GET /category-types and GET /categories look up from names. It returns 403 if your organization has term contribution turned off.

curl -X POST "https://<your-host>/iq/services/v9/rest/terminology/candidates" \
-H "AUTHTOKEN: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"surface": "edge node", "language": "en", "comment": "Used in the 4.2 release notes"}'

Publish

Changes reach checks once you publish, as they did after deployTerminology. POST /publish returns a job right away; poll GET /jobs/{job_id} until its status is succeeded or failed. Publishing takes from a few seconds to several minutes.

curl -X PUT "https://<your-host>/iq/services/v9/rest/terminology/deployTerminology" \
-H "Authorization: session $SESSION"

Back up and restore

GET /export/xml-dump downloads all of your terminology as an XML dump, and POST /import/xml-dump restores one. GET /export/actif downloads the ACTIF document that publishing makes live.

An import replaces all of your organization’s terminology with the dump.

The import runs in the background like a publish: it returns a job to poll. While it runs, other writes return 409.

curl "https://<your-host>/iq/services/v9/rest/terminology/dump/$SESSION"
# -> {"dumpFileUrl": "...", "isReady": true, ...}

What behaves differently

  • Entries are complete. A posted entry replaces the stored one. Fetch, edit and post back to change only what you edit.
  • Unknown names are errors. Nothing is created implicitly; add new languages, statuses, domains or fields first, in the console or with the category and custom field endpoints.
  • Status codes.
    • POST /entries returns 201 if any entry was new and 200 otherwise. There is no 204 for an unchanged entry.
    • DELETE /entries/{entry} of an unknown entry returns 404, not 410.
    • Unknown names and invalid documents return 400 with an errors list.
  • Publishing and importing are jobs. Both return immediately. Only one runs per organization at a time; a second returns 409.
  • Formats. Entries and the schema are ACTIF 1.0 XML only. ACTIF JSON and MTF are not accepted.

Not available

  • Sessions (requestSession, releaseSession): send the API key on every request instead.
  • Saved filters (GET /filters and criteria.filter): pass the filter’s conditions to GET /terms.
  • findTerms: use GET /terms, then GET /entries/{entry} for the full documents.
  • lookupLinkedTerms: use GET /terms/{term_id}/linked.
  • importTerminology and exportTerminology with saved configurations: use POST /entries for partial updates, and the XML dump or ACTIF export for whole termbases.