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:
POST /tasks/createcreates a task targeting[en, ja]- Task finishes (you receive
task.completed) - Your product later decides
[ko, fr]is also needed — call this endpoint - The system runs TRANSLATE directly without re-scanning terms
- 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-translateAuthentication Headers
Identical to Create Translation Task.
Body
| Param | Type | Required | Description |
|---|---|---|---|
task_id | string | Yes | Target task ID (must be a task created via the open platform under the same App Key) |
target_languages | string[] | Yes | Target languages to supplement, up to 50. Already-translated languages are filtered out automatically. |
skip_tm | boolean | No | Skip TM matching, default false |
skip_tb | boolean | No | Skip TB matching, default false (not recommended — the terms are already in the termbase) |
callback_url | string | No | Per-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_events | string[] | No | Per-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):
{
"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;nullwhenall_already_translated=trueaccepted_at: UTC time the server accepted the requestrequested_target_languages: your input after de-dupeffective_target_languages: languages that will actually be translatedskipped_target_languages: languages filtered out (already translated / equal to source)all_already_translated: if all requested languages were already translated,trueand no workflow runs (but atask.completedcallback is still emitted)
Callbacks
All callbacks are delivered to:
callback_urlpassed in the body, if provided- Otherwise to the
callback_urlregistered 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 progresstask.completed: supplement finishedtask.failed: sent on any failure
When all_already_translated=true, a single task.completed is fired with a payload shaped like:
{
"skipped": true,
"reason": "all_languages_already_translated",
"trigger": "supplement",
"requested_target_languages": ["en", "ja"],
"effective_target_languages": [],
"skipped_target_languages": ["en", "ja"]
}Error Codes
| code | Description |
|---|---|
| 400 | Missing / invalid params (task_id / target_languages / callback_url format / callback_events contains unknown event / more than 50 languages) |
| 401 | Signature invalid / timestamp expired |
| 403 | Task does not belong to this App Key |
| 404 | Task not found |
| 422 | Task has no source strings (task_strings_aggregated is empty) or no source language configured |
| 500 | Internal error |
| 502 | Workflow trigger failed (reported via an async task.failed callback — this response remains 200) |
Examples
Basic
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:
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_urlsubscription will receive the callbacks too
Idempotency & repeated calls
- There is no idempotency key (unlike
/tasks/create'sclient_task_id) — every call starts a new TRANSLATE workflow with its ownjob_id/trace_id - A language already covered by a previous supplement will automatically land in
skippedon subsequent calls - Rapid repeated calls on the same language set can spawn concurrent TRANSLATE workflows; we recommend waiting for the prior
task.completedcallback before triggering the next supplement to keep the callback timeline clean