curl --request POST \
--url https://studio.weaverse.io/api/v1/content/projects/{projectId}/translations/{operation} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"targetLocale": "fr-fr",
"units": [
{
"id": "Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ",
"sourceGeneration": "sha256:i5yxt6P8d6YAZqFxmfpJwVd1EG3A-S_s04MP3XTKlHs",
"targetGeneration": null,
"translatedValue": "Acheter maintenant"
}
]
}
'{
"object": "translation_apply",
"projectId": "clz8k2m4n0001qwer7tyu8iop",
"targetLocale": "fr-fr",
"requested": 1,
"applied": 1,
"unchanged": 0,
"readback": [
{
"id": "Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ",
"status": "applied",
"value": "Acheter maintenant",
"generation": "sha256:jrrmwDqYx6bx8u4zNin5AXBzTiYGEzO8XIsX0eh8SYQ"
}
]
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "STALE_TRANSLATION",
"message": "The target of unit Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ changed since it was read; nothing was written. Re-read the inventory and retry.",
"status": 409,
"details": {
"unitId": "Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ",
"side": "target"
}
}Validate or apply a translation batch
Submits a batch of 1-50 translation units read from GET /projects/{projectId}/translation-units. {operation} is one of:
validate: runs every check (unit membership, value shape, placeholders, source and target generations, duplicates) and writes nothing. Returnsobject: "translation_validation"with a per-unitoutcomeofapplyorunchanged.apply: runs the same checks under the project lock, then writes onlyTranslationrows, atomically. Returnsobject: "translation_apply"withapplied/unchangedcounts and an exact primaryreadbackof every submitted unit. The cache is invalidated once; if that fan-out fails the write still stands and the response carriescacheDelivery: "degraded".
Any other {operation} value returns 404.
Each unit echoes id, sourceGeneration and targetGeneration exactly as listed (targetGeneration: null when no target was stored), plus translatedValue:
- Text units take a string (max 20,000 characters).
{{name}}and{name}placeholders in the source must appear unchanged in the translation. - Image units take
{ url, alt?, width?, height? };urlmust behttps://or root-relative. nullclears the overlay so the source shows through. An empty string is rejected. Clearing an absent target, or re-clearing a cleared one, reportsunchanged.
Concurrency and atomicity:
- Any stale source or target refuses the whole batch with 409
STALE_TRANSLATION(details.unitId,details.side) and writes nothing. Re-read the inventory and resubmit. - A unit outside this project’s writable content for the locale returns 404
TRANSLATION_UNIT_NOT_FOUND(details.unitId) and writes nothing. - A unit whose target already holds the requested value is
unchangedregardless of the generation it carries, so an identical retry converges. - On
apply, every submitted unit (including ones the preflight found unchanged) is re-proven against locked source and target state; a unit that moved between preflight and lock fails the whole batch. - Under the same lock,
applyre-proves each unit’s stored-schema approval (the field must still be a declared text input, or image input for an image unit, of its component type; otherwise 409) and the target’s authority (the locale must still resolve to the same configured, non-default language; otherwise 400 or 409). Nothing is written in either case.
Authentication: accepts shop API keys, scoped API keys and delegated MCP OAuth tokens that hold the scope, and project-bound translation agent tokens (wv_agent_…, Authorization header only). Agent tokens are accepted only on the translation-unit routes.
Required scope: translations:write
curl --request POST \
--url https://studio.weaverse.io/api/v1/content/projects/{projectId}/translations/{operation} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"targetLocale": "fr-fr",
"units": [
{
"id": "Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ",
"sourceGeneration": "sha256:i5yxt6P8d6YAZqFxmfpJwVd1EG3A-S_s04MP3XTKlHs",
"targetGeneration": null,
"translatedValue": "Acheter maintenant"
}
]
}
'{
"object": "translation_apply",
"projectId": "clz8k2m4n0001qwer7tyu8iop",
"targetLocale": "fr-fr",
"requested": 1,
"applied": 1,
"unchanged": 0,
"readback": [
{
"id": "Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ",
"status": "applied",
"value": "Acheter maintenant",
"generation": "sha256:jrrmwDqYx6bx8u4zNin5AXBzTiYGEzO8XIsX0eh8SYQ"
}
]
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "UNAUTHORIZED",
"message": "<string>",
"status": 123,
"details": "<unknown>"
}{
"object": "error",
"code": "STALE_TRANSLATION",
"message": "The target of unit Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ changed since it was read; nothing was written. Re-read the inventory and retry.",
"status": 409,
"details": {
"unitId": "Y2x6OGszZDJlMDAwOHBvaXU3eXRyNmV3cQBkYXRhLmJ1dHRvblRleHQ",
"side": "target"
}
}Authorizations
Bearer authentication: a shop API key (WEAVERSE_API_KEY), a scoped API key, a delegated MCP OAuth token, or (translation-unit routes only) a project-bound translation agent token. Scopes: content:read — Read projects, pages, theme settings, versions, drafts and translation units. content:write — Save drafts and make non-destructive edits (page items, metadata, SEO, assignments, languages, glossary). Never publishes or deletes. content:publish — Publish drafts, schedule/reschedule/cancel publications, restore versions, publish global sections. content:delete — Delete pages, global sections, languages, static keys, override reverts; required with project:manage for project deletion. project:manage — Create/copy/delete projects, settings, variants, hierarchy, backups, restore-as-new, previews, hostname policy. translations:read — Read translation units only (no general content access). translations:write — Apply translation units only; never mutates page content. A legacy unscoped API key keeps read/write/translation baseline access and every pre-scope endpoint, but never gains publish/delete/project-manage implicitly.
Path Parameters
validate, apply Body
Response
translation_validation or translation_apply result
The response is of type object.
Was this page helpful?