Publish Translation Task
Promote a fully-translated task's strings to the project level — writing into translation_strings_aggregated (project-level aggregated strings) and tm_records_aggregated (TM index). Equivalent to the "Publish" button on the task detail page.
Highlights
- Async: API returns
job_id+execution_id+trace_idimmediately; real publish runs in a GCP Workflow in the background - Both success and failure are delivered via callbacks:
task.publish.completed/task.publish.failed - Pollable: environments that cannot receive webhooks can poll Query Publish Status with the
job_iduntil a terminal state - No sub-step events: only terminal events are emitted
- Independent trace_id: each publish is its own call + its own callback stream, disjoint from the original
/tasks/createtrace
When to call
Typical integration flow:
POST /tasks/create→ task enters translation- Receive the
task.completedcallback (or poll task status untilcompleted) → task strings are translated but not yet project-level (SDK / other APIs can't read them yet) - Call this endpoint → triggers the PUBLISH_TASK workflow
- Receive
task.publish.completed(or poll publish status untilcompleted) → strings are now project-level; only then will/export/inireflect this publish
Endpoint
POST {BASE_URL}/api/open/v1/tasks/publishAuthentication Headers
Identical to Create Translation Task.
Body
| Param | Type | Required | Description |
|---|---|---|---|
task_id | string | Yes | Task to publish; must be an open-platform task under the same App Key |
callback_url | string | No | Per-invocation callback URL override. Empty → use the callback_url registered with /tasks/create |
callback_events | string[] | No | Subscription whitelist: task.publish.completed / task.publish.failed. Empty → both |
Source language is read from the task (task.source_languages[0]); not a parameter.
Target languages always use the task's full target_languages set; not a parameter. Subset selection and conflict resolution are driven by the task itself — this endpoint just publishes whatever is in the task.
Response (sync)
Success (HTTP 200):
{
"success": true,
"code": 0,
"msg": "ok",
"trace_id": "b4e82a1c...",
"data": {
"task_id": "68a12cb7e1f9a88b4ea23c77",
"job_id": "publish_open_...",
"accepted_at": "2026-04-23T09:15:22.117Z",
"source_language": "zh-cn",
"target_languages": ["en", "ja"]
}
}job_id: PUBLISH_TASK workflow's identifieraccepted_at: UTC time the server accepted the requestsource_language: source language used (= task's source language)target_languages: languages being published (= task'starget_languagesin full)
Callbacks
task.publish.completed (success)
Fired at workflow completion. detail carries stats (rows processed, success / fail counts, etc.).
{
"event": "task.publish.completed",
"task_id": "68a12cb7e1f9a88b4ea23c77",
"project_id": "...",
"trace_id": "b4e82a1c...",
"delivery_id": "...",
"timestamp": "2026-04-23T09:16:05.442Z",
"status": "SUCCEEDED",
"detail": {
"message": "Publish task workflow completed successfully"
},
"error": null
}task.publish.failed (failure)
{
"event": "task.publish.failed",
"task_id": "68a12cb7e1f9a88b4ea23c77",
"trace_id": "b4e82a1c...",
"status": "FAILED",
"detail": {
"error": "…backend error detail…"
},
"error": "…human-readable reason…"
}Signing, retry schedule (4 inline attempts: immediate / 16s / 32s / 64s), and SSRF guard are identical to the /tasks/create callbacks.
Error Codes
| code | Description |
|---|---|
| 400 | Invalid params (missing task_id / bad callback_url / callback_events has unknown event) |
| 401 | Signature invalid / timestamp expired |
| 403 | Task does not belong to this App Key |
| 404 | Task not found |
| 422 | Task has no source language configured / no target_languages |
| 500 | Internal error (sync phase) |
Failures in the async phase (workflow trigger / execution errors) do not flip the sync response's success: true — they are only reported via task.publish.failed.
Examples
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}"'"}'
SIGN=$(printf '%s' "${TIMESTAMP}${BODY}${APP_SECRET}" | md5)
curl -s -X POST "${BASE_URL}/api/open/v1/tasks/publish" \
-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
BODY='{
"task_id": "68a12cb7e1f9a88b4ea23c77",
"callback_url": "https://my-hooks.example.com/loxily/publish",
"callback_events": ["task.publish.completed", "task.publish.failed"]
}'Console Parity
- The "Publish" button on the task detail page triggers the same GCP Workflow (
WORKFLOW_PUBLISH,job_type=PUBLISH_TASK) publish_typeis fixed tosimplein this API; if the task has publish conflicts (some strings already published by another task), the backend's default strategy (overwrite with latest) applies- For explicit conflict resolution (choosing which version to keep), use the console UI
Idempotency
There is no idempotency key. Calling the API multiple times on the same task launches independent PUBLISH_TASK workflows, each with its own job_id + trace_id. Subsequent publishes overwrite the previous result.
We recommend waiting for the previous task.publish.completed callback before triggering the next publish to avoid concurrent PUBLISH_TASK workflows.