get started

Core concepts

The object model, how settings resolve, what each project status allows, and the asynchronous pattern behind every long-running call. Read this once: the Quickstart and the guides assume it.

The object hierarchy

Account                    your whole company on the platform
└── Organization           a division, brand, region or major client
    └── Org Unit           a department, team or client account, where projects live
        └── Project        one job, with one source language
            └── Resource   one uploaded file
                └── Work Unit   file × workflow step × target language
                    └── Task    a work unit with a person attached

Each level belongs to exactly one parent: an Organization to one Account, an Org Unit to one Organization. How you split them is up to you. Many accounts use one Org Unit per department, team or client, but there is no fixed rule.

The shape matters for two reasons: it decides what each person can see, since the level a user is added at sets how much of the account they have access to, and it decides where settings come from.

Learn more: Accounts, Organizations and Org Units · All about Organizational Units

Configuration is layered

Some settings are defined on the Account and inherited by its org units; others are set on the org unit itself. Not everything inherits, and the API has no single endpoint listing which does.

Lives onExamples
AccountPrice lists, net rate settings
OrganizationTranslation memories and glossaries (created here, then associated with org units)
Org UnitCurrency, timezone, default locales, Quote Automation, autopilot, cost logfile type, and the translation memory and glossary associations

The practical rule: read the org unit to see the effective settings, whatever their origin. GET /api/v3/client/{uuid} returns the values that will actually apply, and a null there generally means "inherited from the account":

{
  "accountUuid": "adb57880-…",
  "currency": "BRL",
  "timezone": "America/Sao_Paulo",
  "defaultSourceLanguage": "pt_br",
  "sendQuoteAuto": true,
  "projectAutomationEnabled": true,
  "netRateSettings": null
}

That netRateSettings: null is the inheritance showing: no override here, so the account's settings apply. Price lists work the same way from the other direction: GET /api/v3/price-list returns them with accountUUID set and orgUnitUuid null, because they are defined once on the account and selected per org unit.

When you create a project with inferDefaultSettings=true, it is these resolved org unit settings that get copied onto the project.

Note that the API calls org units client. The endpoint is GET /api/v3/client, and inside a project payload the same object appears twice, as client and as organizationalUnit. They are the same object.

The Organization is thin by comparison: GET /api/v2/organization/{uuid} returns little more than a name, a country and the list of org units under it. It groups org units and owns the linguistic assets; it is not where project behaviour is configured.

Work units are the multiplication

You do not create work units one per file. One call creates resources × workflows × target locales of them:

[
  {
    "projectResourceUuid": "ef7ee1da-...",
    "workflows": [
      "TRANSLATION",
      "PROOFREADING"
    ],
    "targetLocales": [
      "pt_br",
      "es_es"
    ]
  }
]

That single request produced four work units: TRANSLATION/pt_br, PROOFREADING/pt_br, TRANSLATION/es_es, PROOFREADING/es_es. Each one carries a sequence field, which is its workflow level: TRANSLATION is level 1, PROOFREADING level 2. Several download endpoints select content by workflow level, so this number matters.

Keep sequence when you create the work units. The create response reports it, and so does the work unit search; List Project Work Units leaves it out. Store it with the UUIDs you are already keeping.

For live progress, GET /project/{uuid}/process-summary is the endpoint to watch: it reports pre-translation as it happens.

Project locales

A project has one sourceLocale, set at creation and fixed afterwards.

It does not have target locales at creation: targetLocales comes back as []. Targets are derived from the work units you create. After creating the four work units above, the project reported:

"sourceLocale": "en_us",
"targetLocales": ["es_es", "pt_br"]

So "add a language to a project" means "create work units for that language".

Project status

Status controls what you are allowed to do. You change it with Change Project Status; the practical summary:

StatusWhat it meansFile/structure changesManual cost edits
DRAFTBeing assembledAllowedAllowed
PENDINGQuoted, awaiting approvalBlockedBlocked
APPROVEDWork is live, pre-translation runsBlockedBlocked
DELIVEREDWork finishedBlockedBlocked
INVOICED / CANCELLED / DELETEDTerminalBlockedBlocked

Continuous projects, which keep receiving files from a pipeline, have their own status, CONTINUOUS (see Creating projects from files).

Two things follow from that table:

  1. Add your files while the project is in DRAFT. That is the status where files, work units and workflows are editable. To add one to a project that has moved on, bring it back to DRAFT, or start a new project for it.
  2. Work starts at APPROVED. That is when pre-translation runs and when people can pick up tasks: see Quickstart, step 7.

Quote Automation changes the flow

This is the single most surprising behaviour in the API, and the reason two integrations against the same endpoints can need different code.

Quote Automation is an org unit setting, and it decides who quotes the project and who moves it out of DRAFT:

Quote AutomationWhat happensWhat your code does
ONThe platform quotes the project and moves it DRAFT → PENDING by itselfWait, re-read the status, then go to APPROVED
OFFThe project stays in DRAFTCall DRAFT → PENDING yourself: that transition is what runs the quote

With automation ON, creating work units moves the project from DRAFT straight to PENDING and calculates the cost, without you calling anything:

{
  "costsStatus": "COMPLETED",
  "costCalculationMode": "AUTOMATIC",
  "totalCost": 78.0
}

If you then ask for that same transition yourself, it is refused, because the platform already did it:

{
  "status": 422,
  "code": "BWX-002",
  "message": "BW Error: Unsupported transition: Pending -> Pending"
}

With automation OFF, that same call is the one doing the work, and calculateCosts: true on it is what triggers the quote:

{ "newStatus": "PENDING", "calculateCosts": true }

GET /api/v3/client/{uuid} shows which mode an org unit is in: look for sendQuoteAuto and projectAutomationEnabled.

Write code that works either way. Read GET /api/v3/project/{uuid} and branch on the actual status rather than the one you last set, send calculateCosts: true on the first DRAFT → PENDING (harmless if automation already ran), and treat Unsupported transition: X -> X as "already there", not as an error.

Learn more: How to use wxrks Quote Automation

Asynchronous operations

Anything that produces a file or recalculates a project is asynchronous, and the pattern is always the same: one call returns a job or request id, and you poll a status endpoint until it reports COMPLETED or FAILED.

File ingestion is the exception: it has no job id. Poll GET /api/v3/project/{uuid}/ingestion/status. It answers with just a quoted word, "COMPLETED", "COMPLETED_WARN" or "IN_PROGRESS", not an object, so strip the quotation marks before comparing.

Poll it once the work units exist. Until then it answers 200 with an empty body: the file is registered, but there is nothing yet to report on. Create the work units first and it starts reporting within seconds.

If you would rather not poll at all, register a webhook: see Stop polling, use webhooks and Webhooks.