Event reference
Look up every webhook event, when it fires and the exact payload your endpoint receives.
An endpoint receives only the events it subscribed to. The event name arrives in the X-Transglot-Event header and again as event in the body.
| Event | Fires when | Delivery |
|---|---|---|
batch.completed | A translation run finished: completed or completed_with_errors | Once per run |
batch.failed | A translation run ended failed or cancelled | Once per run |
key.created | Keys were created | Coalesced, 30 s |
key.updated | Keys changed name, description, plural flag or length limit | Coalesced, 30 s |
key.deleted | Keys were deleted | Coalesced, 30 s |
translation.updated | Translation values were written | Coalesced, 15 s |
translation.reviewed | Translations were approved | Coalesced, 30 s |
language.added | A target language was added | Once |
language.removed | A target language was removed | Once |
glossary.updated | Glossary terms changed | Coalesced, 30 s |
qa.completed | A quality check finished scoring translations | Coalesced, 60 s |
source_drift.repaired | Translations were repaired after their source changed | Coalesced, 60 s |
The envelope
Every payload carries these fields:
Prop
Type
The examples on this page are formatted for reading. The body you receive is compact JSON and its keys can come in a different order, so read fields by name.
Coalesced events
A coalesced event is not delivered once per occurrence. The first occurrence opens a window (15, 30 or 60 seconds, depending on the event). Every further occurrence in that window adds to a counter. When the window closes, your endpoint receives one delivery for the whole window, and the next occurrence opens a new window.
Prop
Type
samples is for display and logging. To act on exactly what changed, use the event as a trigger and fetch the current state, for example with GET /v1/pull or POST /v1/translations/batch.
batch.completed
A translation run reached a successful final state: completed, or completed_with_errors when some pairs failed. Delivered exactly once per run, whatever started it (a push, the console, POST /v1/translate, a language being added, a retry).
batch has the same shape as GET /v1/batches/{batch}.
{
"event": "batch.completed",
"delivered_at": "2026-10-12T09:41:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"batch": {
"id": 4821,
"status": "completed",
"trigger": "push",
"total": 240,
"completed": 240,
"failed": 0,
"skipped": 12,
"skipped_reasons": { "human_value_stale": 12 },
"tm_served": 37,
"language_breakdown": { "fr": 120, "de": 120 },
"started_at": "2026-10-12T09:39:13+00:00",
"finished_at": "2026-10-12T09:41:00+00:00"
}
}Prop
Type
batch.failed
A translation run ended failed, or was cancelled from the console or with POST /v1/batches/{batch}/cancel. Delivered exactly once per run, with the same payload shape as batch.completed.
{
"event": "batch.failed",
"delivered_at": "2026-10-12T09:41:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"batch": {
"id": 4821,
"status": "failed",
"trigger": "push",
"total": 240,
"completed": 191,
"failed": 49,
"skipped": 0,
"skipped_reasons": null,
"tm_served": 37,
"language_breakdown": { "fr": 120, "de": 120 },
"started_at": "2026-10-12T09:39:13+00:00",
"finished_at": "2026-10-12T09:41:00+00:00"
}
}To see which pairs failed and why, call GET /v1/batches/{batch}?include=failures.
key.created
Keys were created in the console or by a source-locale push. Coalesced over 30 seconds. count is the number of keys created; samples holds key names, and push:<locale> for a push.
{
"event": "key.created",
"delivered_at": "2026-10-12T09:50:31+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 14,
"samples": ["push:en"],
"window_seconds": 30
}key.updated
Keys changed name, description, plural flag or length limit: an edit in the console, a bulk rename (sample bulk-rename), or a source push whose metadata sidecar changed descriptions or length limits (sample push-metadata:<locale>). Coalesced over 30 seconds.
{
"event": "key.updated",
"delivered_at": "2026-10-12T10:12:30+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 2,
"samples": ["checkout.submit", "checkout.error.declined"],
"window_seconds": 30
}key.deleted
Keys were deleted: one at a time in the console (sample: the key name), in bulk (sample bulk), or by a source push with mode=sync_removals (sample push:<locale>). Coalesced over 30 seconds.
{
"event": "key.deleted",
"delivered_at": "2026-10-12T10:20:04+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 31,
"samples": ["bulk"],
"window_seconds": 30
}translation.updated
Translation values were written. Coalesced over 15 seconds. count counts written values only: a pair a translation run left alone, because a person had already edited or reviewed it or because its source moved during the run, writes nothing and is not counted.
| Source of the write | Sample |
|---|---|
| An edit in the console editor | <key>:<locale>, for example checkout.submit:fr |
| A push (source or target) | push:<locale> |
| A translation run | batch:<batch id>:<locale> |
| Copying the source into a locale in bulk | bulk-copy-source:<locale> |
{
"event": "translation.updated",
"delivered_at": "2026-10-12T09:41:12+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 240,
"samples": ["batch:4821:fr", "batch:4821:de"],
"window_seconds": 15
}Writes through the API
A value written with PUT /v1/translations does not fire translation.updated. If your own integration writes values that way, you already know about the change.
translation.reviewed
Translations were approved in the review workflow. Coalesced over 30 seconds. samples holds <key>:<locale> for a single approval and bulk:<locale> for a bulk approval.
{
"event": "translation.reviewed",
"delivered_at": "2026-10-12T11:02:45+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 37,
"samples": ["checkout.submit:fr", "bulk:de"],
"window_seconds": 30
}language.added
A target language was added to the project. Delivered once, as soon as the language exists. A translation run to fill it may follow and reports through the batch.* events.
{
"event": "language.added",
"delivered_at": "2026-10-12T12:00:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"language": { "locale": "pt-BR" }
}language.removed
A target language was removed from the project. Every translation in that locale and its version history were deleted with it, and neither can be recovered through the product. Delivered once. React to it by dropping the locale from your builds, your CDN configuration or your CMS.
{
"event": "language.removed",
"delivered_at": "2026-10-12T12:30:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"language": { "locale": "pt-BR", "translations_deleted": 1204 }
}language.translations_deleted tells you whether the locale held content or was an empty placeholder.
glossary.updated
Glossary terms were created, edited, approved, rejected, imported or deleted in the console. Coalesced over 30 seconds. samples holds source terms, and import for a file import, whose count is the number of terms it created or updated.
{
"event": "glossary.updated",
"delivered_at": "2026-10-12T13:05:30+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 3,
"samples": ["Checkout", "Acme"],
"window_seconds": 30
}Changes made through the glossary API endpoints do not fire this event.
qa.completed
A quality check finished scoring translations. A check is processed in groups, and the 60-second window collapses a whole check into one delivery. count is the number of translations checked. samples holds flagged <key>:<locale> pairs, at most one per group, so an empty samples means nothing was flagged. The findings themselves are on the translations: read them with GET /v1/translations or in the editor.
{
"event": "qa.completed",
"delivered_at": "2026-10-12T14:01:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 74,
"samples": ["checkout.error.declined:de"],
"window_seconds": 60
}source_drift.repaired
Source strings changed, and the translations that depended on them were checked again and repaired, either as a suggestion or applied directly, according to the project's source-drift setting. Coalesced over 60 seconds. count is the number of translations repaired; samples holds <key>:<locale> pairs.
{
"event": "source_drift.repaired",
"delivered_at": "2026-10-12T15:10:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 6,
"samples": ["checkout.submit:fr", "checkout.submit:de"],
"window_seconds": 60
}