Skip to main content
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:
  1. Authenticate
  2. Create project and job
  3. Monitor progress
  4. 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

  1. List project templates: GET /api2/v1/projectTemplates
  2. Create project from template: POST /api2/v2/projects/applyTemplate/{templateUid}
  3. 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

  1. Start async download: PUT /api2/v3/projects/{projectUid}/jobs/{jobUid}/targetFile
  2. Wait until async request is complete.
  3. Download target file: GET /api2/v2/projects/{projectUid}/jobs/{jobUid}/downloadTargetFile/{asyncRequestId}
  4. 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.