This quick start gives you the minimum end-to-end plugin flow: authenticate, create project and jobs, monitor progress, and download translated output.
At a glance
You will implement this sequence:
Authenticate
Create project and job
Monitor progress
Export and set DELIVERED
Prerequisites
Phrase account with TMS access.
Phrase Platform API token.
Platform base URL and TMS API base URL for your tenant/region.
At least one TMS project template available in your org.
A sample source file to upload.
Basic REST API familiarity.
Set base URLs once and reuse them in examples:
PLATFORM_BASE_URL = "https://<your-region>.phrase.com"
TMS_BASE_URL = "https://<your-tms-host>/web"
Do not hardcode EU endpoints unless your tenant is on EU infrastructure.
Step 1: Authenticate
Exchange your API token for a short-lived access token and send it as Authorization: Bearer <token>.
curl --request POST "${ PLATFORM_BASE_URL }/idm/oauth/token" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode 'subject_token=YOUR_API_TOKEN'
Use this token exchange flow for all subsequent API calls. Refresh before expiry or on a single retry after 401.
See Platform authentication and OAuth token endpoint .
Step 2: Create a project and job
List project templates: GET /api2/v1/projectTemplates
Create project from template: POST /api2/v2/projects/applyTemplate/{templateUid}
Create job in project: POST /api2/v1/projects/{projectUid}/jobs
curl --request POST "${ TMS_BASE_URL }/api2/v2/projects/applyTemplate/${ TEMPLATE_UID }" \
--header "Authorization: Bearer ${ ACCESS_TOKEN }" \
--header 'Content-Type: application/json' \
--data '{"name":"My Plugin Quick Start Project"}'
Persist projectUid, job.uid, and any returned asyncRequest.id. You need these IDs later.
Reference docs:
Step 3: Monitor progress
Use one of these methods:
Simple start: poll GET /api2/v1/projects/{projectUid}/jobs/{jobUid}/parts
Async operation status: poll GET /api2/v1/async/{asyncRequestId} when relevant
Production approach: webhook events with polling fallback
curl --request GET "${ TMS_BASE_URL }/api2/v1/projects/${ PROJECT_UID }/jobs/${ JOB_UID }/parts" \
--header "Authorization: Bearer ${ ACCESS_TOKEN }"
Treat terminal failure statuses (CANCELLED, REJECTED) as non-deliverable outcomes.
Step 4: Export and mark delivered
Start async download: PUT /api2/v3/projects/{projectUid}/jobs/{jobUid}/targetFile
Wait until async request is complete.
Download target file: GET /api2/v2/projects/{projectUid}/jobs/{jobUid}/downloadTargetFile/{asyncRequestId}
Mark delivered: POST /api2/v1/projects/{projectUid}/jobs/{jobUid}/setStatus with requestedStatus: DELIVERED
{
"requestedStatus" : "DELIVERED" ,
"notifyOwner" : true ,
"propagateStatus" : true
}
Reference docs:
Optional: implementation patterns (pseudocode)
Use these patterns when moving from sample calls to production code. You can skip this section for a first pass.
Auth caching and one-time 401 retry
CACHE = { token: null, expiresAt: 0 }
function getAccessToken():
if CACHE.token exists and now < CACHE.expiresAt - safetyWindow:
return CACHE.token
tokenResponse = POST ${PLATFORM_BASE_URL}/idm/oauth/token
CACHE.token = tokenResponse.access_token
CACHE.expiresAt = now + tokenResponse.expires_in
return CACHE.token
function authRequest(method, url, body):
token = getAccessToken()
response = HTTP(method, url, header={"Authorization": "Bearer " + token}, body=body)
if response.status == 401:
CACHE.token = null
token = getAccessToken()
response = HTTP(method, url, header={"Authorization": "Bearer " + token}, body=body)
return response
Async polling with bounded backoff
function waitForAsync(asyncRequestId, timeout):
delay = 2s
until timeout:
status = GET /api2/v1/async/{asyncRequestId}
if status.asyncResponse exists:
if status.asyncResponse.errorCode exists:
fail(status.asyncResponse)
return status.asyncResponse
sleep(delay + jitter)
delay = min(delay * 2, 60s)
Export completion flow
start = PUT /api2/v3/projects/{projectUid}/jobs/{jobUid}/targetFile
waitForAsync(start.asyncRequest.id)
file = GET /api2/v2/projects/{projectUid}/jobs/{jobUid}/downloadTargetFile/{start.asyncRequest.id}
importToSourceSystem(file)
POST /api2/v1/projects/{projectUid}/jobs/{jobUid}/setStatus {"requestedStatus":"DELIVERED"}
Next steps
Full Integration Workflow Add production-grade behaviors and resilience.
Live Preview Add contextual preview support for translators.
Error Handling & Limits Harden retries, limits, and failure behavior.