This guide shows how to build an integration workflow in Phrase Orchestrator: a workflow that receives an event from a third-party system, fetches content through the Connectors API, translates it with Phrase Language AI or Phrase TMS, and sends the translation back. The examples use the Knak integration, but the same building blocks apply to any connector that exposes download, upload, and (optionally) preview operations.
When to use this pattern
Use an Orchestrator integration workflow when a third-party system can notify Phrase about new content (usually through a webhook) and you want translation to happen automatically, without anyone exporting or importing files by hand. There are two common shapes:
The rest of this guide builds the machine translation pattern step by step, then shows how the TMS pattern differs.
Prerequisites
Key concepts
Use the dedicated Phrase Connector API actions wherever one exists for your connector. They authenticate with the Connectors API for you, so you don’t need a token step. Search the action picker for the connector’s name (for example
knak): several connectors have an action with the same name, such as Upload raw content synchronously. Phrase Language AI has no actions yet, so its calls use Send HTTP Request v2.The example workflow
The machine translation workflow for Knak looks like this. Each block is explained in the steps below.1. Receive the event with a webhook trigger
- In the workflow editor, open Triggers, search for
webhook, and add the Webhook trigger. - Copy the trigger’s webhook URL and register it in the third-party system. For Knak this is a webhook for the
translation_request.createdevent. - Open Edit webhook and paste an example payload. Orchestrator uses it as a schema, so later steps can pick fields from the trigger. Without it, the trigger’s fields aren’t available in the field picker.
In Knak, a translation request only reaches Phrase (and fires
translation_request.created) when Phrase is the translation source. In Request Translation → Source Selection, choose Manual Translation and check that Phrase appears on the confirmation step. AI Translation is translated by Knak itself and sends no event.2. Extract the values you need
Add a Transform JSON with jq action right after the trigger, so later steps can use short names instead of long payload paths.-
Input type:
JSON -
Input:
{{ @raw }}, with the Webhook trigger as the source -
jq:
{{ $.id }}, {{ $.lang }} and {{ $.src }} with this step as their source.
Limit the workflow with a condition
A third-party webhook is often account-wide: it fires for every asset, not just the ones you’re testing with. Add a condition to this jq step so the workflow only continues for the content you expect. For example, on the Conditions tab:3. Download the source file
Add the connector’s Download raw content synchronously action (search forknak):
X-ResponseType: OBJECT makes the action return the file itself (for Knak, an XLIFF 2.0 file) instead of a storage ID, so later steps can use it directly as {{ @file.path }}.
4. Translate with Phrase Language AI
Translating a file with Phrase Language AI takes three calls: log in, start the file translation, and download the result. Language AI has no Orchestrator actions yet, so use Send HTTP Request v2 for all three.Log in
-
Method:
POST -
URL:
https://eu.phrase.com/smt/api/v1/auth/login -
Body: raw JSON:
token. Use it as the bearer token ({{ $.token }}) for the next two calls.
Start the file translation
-
Method:
POST -
URL:
https://eu.phrase.com/smt/api/v1/fileTranslations -
Authentication: Bearer token
{{ $.token }}(source: the login step) -
Body: form data with two parts:
The
metadatapart, with the jq step as its source:
mtSettings.profile.uid. Content Groups (contentGroup.uid) only apply to AI Translation Agent profiles; use them for rules such as formality or how to treat links and merge tags.
The response contains the translation’s uid.
Wait, then download the result
File translation is asynchronous. The simplest approach is a Sleep action (for example 90 seconds for a short email) before downloading:- Method:
GET - URL:
https://eu.phrase.com/smt/api/v1/fileTranslations/{uid}/MT_GENERIC_PRETRANSLATE/{lang} - Path parameters:
uid={{ $.uid }}(source: the translation step),lang={{ $.lang }}(source: the jq step) - Header:
Accept: application/octet-stream
5. Validate the translation
Machine translation can occasionally drop or change markup, for example the closing tag of a link. Before sending a translation back, check that every unit kept the same HTML tags and merge tags as its source, and fail the run if not. Add a Convert XML to JSON Format action with the downloaded translation ({{ @file.path }}) as input, and this jq expression:
title attributes) and the {{merge tags}}, and fails with the IDs of any units that changed or are empty. A failed run stops before anything is uploaded, so broken content never reaches the third-party system.
6. Send the translation back
Uploading a translation through the Connectors API is a two-step process: store the file, then tell the connector where to put it.Store the translated file
Add Upload file to file storage synchronously (Phrase Connector API):- file:
{{ @file.path }}from the step that downloaded the translated file
uid.
Upload
Add the connector’s Upload raw content synchronously action (search forknak):
Each field has its own source, so you don’t need an extra step to combine values. The translation now appears in the third-party system.
Test and publish
- Publish the workflow. Webhook-triggered workflows only receive calls while published.
- Create a real request in the third-party system, or send an example payload to the webhook URL yourself.
- Open Executions, select the run, and click each block to see its Input, Output, Headers and Response Status.
- Fix the first failing block, publish again, and resend. Each publish creates a new revision; you can export any revision under Revisions → Export revision as file as a backup.
Changing an existing workflow
- To add a block between two others, hover over the arrow between them and click +.
- To remove a block in the middle, delete it, then reconnect the chain: deleting a block attaches the blocks below it directly to the trigger. Drag from the small handle at the bottom of the block above the gap onto the block below it. To remove a leftover connection, click it and then click the circle in its middle.
- To replace a block, add the new block first, point the next block’s fields to it, and only then delete the old one.
The TMS pattern
When translations need people (translators, reviewers, or a full TMS workflow), replace the Language AI steps with a Phrase TMS project and job. Because human translation takes longer than a single workflow run, this pattern uses two workflows. The native Phrase TMS actions authenticate to TMS for you, so no login step is needed. Workflow 1: request to TMS job, with live preview- Webhook trigger, jq step, and condition, as in steps 1–2.
- Download raw content synchronously, as in step 3.
-
Create project (Phrase TMS action):
templateUidset to your project template,namefor exampleKnak {{ $.id }},sourceLang{{ $.src }}, and onetargetLangsitem{{ $.lang }}(all from the jq step). -
Generate preview package synchronously (Connector API action): the same
credentialsandpathas the download, andX-ResponseTypeOBJECT, so the action returns the preview ZIP itself. -
Upload Job Preview Package (Phrase TMS action):
projectUid{{ $.uid }}from the project step, andfile{{ @file.path }}from the preview step. The output contains afileUid. -
Create job (Phrase TMS action):
fileUid from the preview package upload, not its top-level uid.
Configure the file import settings once in the project template rather than per job. For Knak’s XLIFF 2.0 files, enabling the HTML subfilter and converting
{{...}} merge tags to Phrase tags (regular expression \{\{[^}]+\}\}) makes HTML and merge tags appear as protected tags in the editor. Changing the template, rather than the organization’s default import settings, keeps the change limited to these projects.- Trigger: on the Triggers tab, search for
joband add the Phrase TMSjob.status.changedtrigger. Add filters so it only runs for completed jobs in the projects this integration creates, for example a project name that starts withKnak. Without filters, the workflow runs for every job status change in the organization. - Job info (Transform JSON with jq, input
{{ @raw }}from the trigger): extract the job UID, the project UID, the target language, whether the job is at the last workflow step, and the third-party request ID from the job’s filename (knak-<id>.xlf). The exact field names come from the trigger’s payload; check them in the trigger’s output under Executions. - Prepare target file (Phrase TMS action):
projectUidandjobUidfrom Job info. Add a condition so it only runs when the job is at the last workflow step (for example{{ $.lastStep }}equalstrue). A job with several workflow steps (translation, then review) is completed once per step, and only the last one should send the translation back. - Wait with a Sleep action. Preparing the target file is asynchronous; a short wait (for example 20 seconds for an email) is enough for small files. Increase it for larger content.
- Download target file (Phrase TMS action):
projectUidandjobUidfrom Job info, andasyncRequestId{{ $.asyncRequest.id }}from the prepare step. - Upload file to file storage synchronously:
fileis{{ @file.path }}from the download step. - Upload raw content synchronously, as in step 6, with
path → idandtargetLocalefrom Job info ({{ $.id }},{{ $.lang }}) andstorageIdfrom the file storage step ({{ $.uid }}).
Calling the Connectors API without an action
If your connector has no Connector API actions yet, call its endpoints with Send HTTP Request v2 and a Connectors API token:- Get a token with an IDM token exchange:
POST https://eu.phrase.com/idm/oauth/token, URL-encoded body withgrant_type=urn:ietf:params:oauth:grant-type:token-exchangeandsubject_token= a Phrase platform token stored as a workflow variable. The response contains anaccess_token. - Call the connector, for example
POST https://eu.phrase.com/connectors/<connector>/v1/sync/download-raw-file, with Bearer token{{ $.access_token }}and the same JSON fields the actions use (credentials,path, and so on) as a raw JSON body. - To combine values from several steps into one request body, use Transform JSON with jq v2, which takes two inputs as
.[0]and.[1].