Skip to content

Supplement Translation

Translate an existing task into additional target languages. Equivalent to the "Supplement Translation" button in the task detail page, but:

  • TB_SCAN is skipped — the original task already scanned terms and populated the termbase; no need to re-scan
  • Already-translated languages are filtered out — targets you pass that are already translated land in skipped_target_languages; only missing ones go through TRANSLATE
  • Async semantics — same as /tasks/create: fast response, real work (CSV generation → GCS upload → TRANSLATE trigger) happens in the background fire-and-forget; progress/results are reported via the existing callbacks (tmMatching / tbMatching / ... / task.completed / task.failed)

When to call

Typical integration flow:

  1. POST /tasks/create creates a task targeting [en, ja]
  2. Task finishes (you receive task.completed)
  3. Your product later decides [ko, fr] is also needed — call this endpoint
  4. The system runs TRANSLATE directly without re-scanning terms
  5. Progress / result reported through the normal node events + terminal task.completed

The endpoint is idempotent in effect — it can be called repeatedly; each invocation launches an independent workflow with its own trace_id.

Endpoint

POST {BASE_URL}/api/open/v1/tasks/supplement-translate

Authentication Headers

Identical to Create Translation Task.

Body

ParamTypeRequiredDescription
task_idstringYesTarget task ID (must be a task created via the open platform under the same App Key)
target_languagesstring[]YesTarget languages to supplement, up to 50. Already-translated languages are filtered out automatically.
skip_tmbooleanNoSkip TM matching, default false
skip_tbbooleanNoSkip TB matching, default false (not recommended — the terms are already in the termbase)
callback_urlstringNoPer-invocation callback URL override. Empty → use the callback_url registered with the original /tasks/create. Provided → all callbacks for this supplement invocation are sent to this URL; the original task's callback_url is unaffected.
callback_eventsstring[]NoPer-invocation subscription override; each item must be in the event whitelist. Empty → inherit task-level subscription.

Source language is not a parameter — it is read from task.source_languages[0] (the one configured when the task was originally created).

Target-language filter rules

  • Input is de-duplicated and trimmed
  • Values equal to the source language are moved to skipped
  • If any row in the task already has a non-empty value for a given language column → that language is considered "already translated" and moved to skipped
  • Everything else goes into effective

If effective is empty (everything was already translated / all values equal source), no workflow is triggered, but a task.completed callback is still sent with detail.skipped=true and detail.reason="all_languages_already_translated" so integrators can handle completion uniformly.

Response

Success (HTTP 200):

json
{
  "success": true,
  "code": 0,
  "msg": "ok",
  "trace_id": "d3f98b2c...",
  "data": {
    "task_id": "68a12cb7e1f9a88b4ea23c77",
    "job_id": "supplement_translate_...",
    "accepted_at": "2026-04-23T07:12:03.441Z",
    "requested_target_languages": ["ko", "de", "fr"],
    "effective_target_languages": ["ko", "fr"],
    "skipped_target_languages": ["de"],
    "all_already_translated": false
  }
}
  • job_id: identifier for the TRANSLATE job that will run; null when all_already_translated=true
  • accepted_at: UTC time the server accepted the request
  • requested_target_languages: your input after de-dup
  • effective_target_languages: languages that will actually be translated
  • skipped_target_languages: languages filtered out (already translated / equal to source)
  • all_already_translated: if all requested languages were already translated, true and no workflow runs (but a task.completed callback is still emitted)

Callbacks

All callbacks are delivered to:

  1. callback_url passed in the body, if provided
  2. Otherwise to the callback_url registered with /tasks/create

Each callback payload carries trace_id equal to the trace_id returned in this response (distinct from the original task's trace_id), making it easy for integrators to separate "original task" and "supplement" callback streams.

Event sequence matches Create Translation Task (but term_scan.completed is not emitted, because supplement always skips TB_SCAN):

  • tmMatching / tbMatching / kbExtraction / translateBatch / lqaBatch / translateEnhanceBatch / aiScoringBatch: TRANSLATE sub-step progress
  • task.completed: supplement finished
  • task.failed: sent on any failure

When all_already_translated=true, a single task.completed is fired with a payload shaped like:

json
{
  "skipped": true,
  "reason": "all_languages_already_translated",
  "trigger": "supplement",
  "requested_target_languages": ["en", "ja"],
  "effective_target_languages": [],
  "skipped_target_languages": ["en", "ja"]
}

Error Codes

codeDescription
400Missing / invalid params (task_id / target_languages / callback_url format / callback_events contains unknown event / more than 50 languages)
401Signature invalid / timestamp expired
403Task does not belong to this App Key
404Task not found
422Task has no source strings (task_strings_aggregated is empty) or no source language configured
500Internal error
502Workflow trigger failed (reported via an async task.failed callback — this response remains 200)

Examples

Basic

bash
APP_KEY="5685414646a54423c891d87194d87f3f"
APP_SECRET="<your App Secret>"
TASK_ID="68a12cb7e1f9a88b4ea23c77"
BASE_URL="https://api.loxily.com"

TIMESTAMP=$(date +%s)
BODY='{"task_id":"'"${TASK_ID}"'","target_languages":["ko","de","fr"]}'
SIGN=$(printf '%s' "${TIMESTAMP}${BODY}${APP_SECRET}" | md5)

curl -s -X POST "${BASE_URL}/api/open/v1/tasks/supplement-translate" \
  -H "Content-Type: application/json" \
  -H "X-Loxily-AppKey: ${APP_KEY}" \
  -H "X-Loxily-Timestamp: ${TIMESTAMP}" \
  -H "X-Loxily-Sign: ${SIGN}" \
  --data "${BODY}"

With a dedicated callback_url

Route callbacks for this supplement run to a different receiver without touching the original task's registration:

bash
BODY='{
  "task_id": "68a12cb7e1f9a88b4ea23c77",
  "target_languages": ["ko", "fr"],
  "callback_url": "https://my-hooks.example.com/loxily/supplement"
}'

Console Parity

  • The "Supplement Translation" button in the Loxily console takes the same underlying TRANSLATE path (scan_terms=false) — no behavior drift
  • After your API call, the task detail page reflects progress as node events advance
  • Conversely, if the button in the console is used to trigger a supplement on an open-platform-created task, the existing task-level callback_url subscription will receive the callbacks too

Idempotency & repeated calls

  • There is no idempotency key (unlike /tasks/create's client_task_id) — every call starts a new TRANSLATE workflow with its own job_id / trace_id
  • A language already covered by a previous supplement will automatically land in skipped on subsequent calls
  • Rapid repeated calls on the same language set can spawn concurrent TRANSLATE workflows; we recommend waiting for the prior task.completed callback before triggering the next supplement to keep the callback timeline clean