Bulk Upload Terms
Bulk-write glossary terms into the project termbase. Async endpoint — like Create Translation Task, the API returns an upload_id immediately; the actual writes happen in the background; completion / failure is reported via callback_url (term_upload.completed / term_upload.failed).
Highlights
- JSON body only — no file URL option
- Up to 1000 terms per call
- Upserted by
(team, project, content, source_language): re-uploading the same content+source merges over the existing record - Both success and failure are delivered via callbacks
- Vector sync queued (non-blocking) after write
Endpoint
POST {BASE_URL}/api/open/v1/terms/createAuthentication Headers
Identical to Create Translation Task.
Body
| Param | Type | Required | Description |
|---|---|---|---|
terms | array | Yes | Terms, up to 1000 |
callback_url | string | No | http(s) URL where the completion event will be delivered. Empty → no callback. |
callback_events | string[] | No | Subscription subset (term_upload.completed / term_upload.failed). Empty → both. |
terms[] item fields
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | The term text in the source language (non-empty after trim) |
source_language | string | Yes | Source language code; must be in the platform's supported set (matches /languages) |
category | string | No | Category label, e.g. Item / Character |
pic | string | No | Image URL for the term (optional business field) |
translations | object | No | Target language map, e.g. { "en": "Combat Power", "ja": "戦闘力" }; keys must be supported language codes; empty string clears that language |
Response (sync)
Success (HTTP 200):
{
"success": true,
"code": 0,
"msg": "ok",
"trace_id": "d3f98b2c...",
"data": {
"upload_id": "d3f98b2c...",
"accepted_at": "2026-04-23T08:00:01.234Z",
"terms_count": 42
}
}upload_id==trace_id, used on your side to correlate upload + callbacksterms_count: number of items accepted (shape-validated, not yet upserted)
Callbacks (async)
term_upload.completed (success)
{
"event": "term_upload.completed",
"task_id": "",
"project_id": "...",
"trace_id": "d3f98b2c...",
"delivery_id": "...",
"timestamp": "2026-04-23T08:00:03.891Z",
"status": "SUCCEEDED",
"detail": {
"upload_id": "d3f98b2c...",
"terms_count": 42,
"upserted_count": 35,
"modified_count": 7
},
"error": null
}upserted_count: newly inserted termsmodified_count: existing terms with at least one field changedterms_count - upserted_count - modified_count: existing, unchanged
term_upload.failed (failure)
{
"event": "term_upload.failed",
"trace_id": "d3f98b2c...",
"status": "FAILED",
"detail": {
"upload_id": "d3f98b2c...",
"terms_count": 42,
"failure_code": 500
},
"error": "..."
}Callback signing
Identical to /tasks/create callbacks — MD5 signature in X-Loxily-Sign, 4 inline attempts (immediate / 16s / 32s / 64s), any 2xx counts as delivered.
task_id is an empty string because this event is not tied to a specific task.
Error Codes
| code | Description |
|---|---|
| 400 | Missing / invalid params (terms > 1000 / bad shape / source_language not whitelisted / bad callback_url / bad translations) |
| 401 | Signature invalid / timestamp expired |
| 404 | appKey unknown |
| 403 | App Secret not configured |
| 500 | Internal error (sync phase) |
Note: failures in the background phase do not flip the sync response's success: true — they are only reported via term_upload.failed.
Example
APP_KEY="5685414646a54423c891d87194d87f3f"
APP_SECRET="<your App Secret>"
BASE_URL="https://api.loxily.com"
TIMESTAMP=$(date +%s)
BODY=$(cat <<'EOF'
{
"terms": [
{
"content": "战斗力",
"source_language": "zh-cn",
"category": "Item",
"translations": { "en": "Combat Power", "ja": "戦闘力" }
},
{
"content": "兔窝工头",
"source_language": "zh-cn",
"category": "Character",
"translations": { "en": "Rabbit Den Foreman" }
}
],
"callback_url": "https://your.hook/terms-upload"
}
EOF
)
SIGN=$(printf '%s' "${TIMESTAMP}${BODY}${APP_SECRET}" | md5)
curl -s -X POST "${BASE_URL}/api/open/v1/terms/create" \
-H "Content-Type: application/json" \
-H "X-Loxily-AppKey: ${APP_KEY}" \
-H "X-Loxily-Timestamp: ${TIMESTAMP}" \
-H "X-Loxily-Sign: ${SIGN}" \
--data "${BODY}"Idempotency
There is no idempotency key — re-uploading the same terms is safe (upsert merges), but each call produces a new upload_id + callback. De-duplicate on your side if you want to avoid duplicate background work.