Skip to content

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/create

Authentication Headers

Identical to Create Translation Task.

Body

ParamTypeRequiredDescription
termsarrayYesTerms, up to 1000
callback_urlstringNohttp(s) URL where the completion event will be delivered. Empty → no callback.
callback_eventsstring[]NoSubscription subset (term_upload.completed / term_upload.failed). Empty → both.

terms[] item fields

FieldTypeRequiredDescription
contentstringYesThe term text in the source language (non-empty after trim)
source_languagestringYesSource language code; must be in the platform's supported set (matches /languages)
categorystringNoCategory label, e.g. Item / Character
picstringNoImage URL for the term (optional business field)
translationsobjectNoTarget language map, e.g. { "en": "Combat Power", "ja": "戦闘力" }; keys must be supported language codes; empty string clears that language

Response (sync)

Success (HTTP 200):

json
{
  "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 + callbacks
  • terms_count: number of items accepted (shape-validated, not yet upserted)

Callbacks (async)

term_upload.completed (success)

json
{
  "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 terms
  • modified_count: existing terms with at least one field changed
  • terms_count - upserted_count - modified_count: existing, unchanged

term_upload.failed (failure)

json
{
  "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

codeDescription
400Missing / invalid params (terms > 1000 / bad shape / source_language not whitelisted / bad callback_url / bad translations)
401Signature invalid / timestamp expired
404appKey unknown
403App Secret not configured
500Internal 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

bash
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.