Document translation APIs are asynchronous, so completion has to reach you somehow — and the answer is not webhooks or polling, it is webhooks and a reconciliation poller. Callbacks give you low latency on the common path; the poller catches the jobs whose callback was dropped, delayed past your patience, or delivered to an endpoint that was mid-deploy. Build only the webhook handler and you will ship a system that silently strands documents in
pending forever, with the translated file sitting complete on the vendor's side.
This guide covers the job lifecycle, why both mechanisms are required, and the specific defects — duplicate delivery, out-of-order events, forged callbacks, lost state on restart — that decide whether your integration is reliable at three in the morning.
Real Documents Do Not Fit Inside an HTTP Request
A two-hundred-page credit agreement is not translating inside a thirty-second gateway timeout, and no vendor is going to hold the connection open while it works. So the shape of every serious document API is the same: POST the file, receive a job ID immediately, and collect the output later.
That makes the job — not the request — the unit your system reasons about. A job moves through states: accepted, processing, succeeded, failed, sometimes partially_succeeded for a multi-file submission, sometimes expired when the output is purged before you fetch it. Ask which states exist and which are terminal, because your code has to enumerate them.
Latency therefore stops being a number and becomes a distribution. A ten-page memo and a data room export do not finish on the same timescale, as users in this r/software thread on translating a 100-page Word file discover. Design around "we will tell you when it is ready", not around a spinner.
Callbacks and Polling Solve Different Halves of the Problem
A webhook is a push: the vendor calls your endpoint when a job reaches a terminal state. It is fast, it costs nothing while idle, and it is the right primary mechanism.
It is also delivery over the public internet to an endpoint you operate. Your endpoint will be down during a deploy. A TLS certificate will renew badly one Sunday. A firewall rule will change. The vendor's retry schedule gives up after some finite number of attempts — ask what it is, and whether failed deliveries can be replayed.
Polling is a pull: you ask about jobs you know exist. It is slower and noisier, but it depends only on your ability to make an outbound request, which is a far smaller failure surface than your ability to receive an inbound one.
Used together, they are complementary rather than redundant. The webhook handles the ninety-nine percent and keeps latency low. The poller exists for the one percent, and the one percent is where documents go missing.
The Reconciliation Gap and the Sweeper That Closes It
The specific failure worth naming: a job completes on the vendor's side, the callback never lands, and your record stays processing indefinitely. Nothing errors. No alert fires, because nothing failed. A translated agreement simply never arrives, and you find out when a fee earner asks where it is.
The sweeper that closes this is unglamorous and short. On a schedule, select every job in a non-terminal state older than some threshold, call the vendor's status endpoint, and apply whatever it says. Two rules keep it cheap: back off as jobs age, checking a five-minute-old job often and a five-hour-old job rarely; and cap the age at which a job is escalated to a human rather than polled forever.
Set the threshold above your realistic completion time so the sweeper is not racing the webhook. And make it the same code path as the webhook handler — one function that applies a status to a job — so the two mechanisms cannot drift into disagreeing about what succeeded means.
Delivery Is At-Least-Once, and Order Is Not Guaranteed
Webhook systems retry, which means duplicates. Your endpoint will receive the same completion event twice, occasionally seconds apart, occasionally hours apart after a delivery backlog drains. If your handler downloads the output and writes it to the matter folder, twice means two files.
Ordering is the subtler defect. A processing event emitted before a succeeded event can arrive after it, and a batch of jobs completes in whatever order the work finished, not the order you submitted. Anything that assumes submission order — a loop that expects document one back before document two — is already broken.
Both are fixed by the same design. Treat the payload as a claim about state, not an instruction to act. Deduplicate on the event or delivery ID the vendor provides, and make the job's state machine monotonic: a terminal state never moves back to an earlier one, and an event describing a state you have already passed is acknowledged and discarded. Then a duplicate is a no-op and a late event is harmless.
Verifying That the Callback Came From the Vendor
Your webhook endpoint is a publicly reachable URL that causes your system to fetch files and mark legal work complete. Treat it as an authentication boundary.
Ask how callbacks are signed. The usual pattern is an HMAC over the raw request body with a shared secret, delivered in a header alongside a timestamp. Four things matter in the implementation: compute the signature over the raw bytes rather than a re-serialised object; use a constant-time comparison; reject requests whose timestamp falls outside a short window, which is what stops replay of a captured legitimate callback; and support two active secrets at once so rotation is not an outage.
If signing is not offered, the fallbacks are mutual TLS, an IP allowlist, or an unguessable per-tenant callback path — the last being the weakest, since URLs leak into logs. Secret storage and rotation are exactly the kind of control an ISO/IEC 27001 programme will ask you to evidence, so do it properly the first time.
Payloads Should Carry Identifiers, Not Content
A callback that includes the translated text, the client name, the matter description or a long-lived unauthenticated download link has moved confidential material into a channel you did not design for it — through your reverse proxy logs, your APM traces, and any middlebox in between.
Keep payloads to identifiers: job ID, state, event ID, timestamp. On receipt, your handler fetches the output over an authenticated request you initiate. That inverts the trust direction and keeps the content on a path where you control retention.
This is also a data-protection point rather than a stylistic one. Under GDPR, data minimisation applies to transfers between systems, and webhook payloads that carry personal data end up copied into log stores with their own retention schedules that nobody has ever reviewed. Ask the vendor what a callback body contains, whether it can be reduced, and whether download URLs are short-lived and single-use.
Durable Job State, Written Before the Request
If job state lives in memory, a restart loses it, and a job whose ID you have forgotten is a job you cannot reconcile. The store is the system of record; the vendor is a source of updates to it.
Write the row before you submit, not after. It holds your own job identifier, the idempotency key, matter and document references, target language, current state, attempt count, timestamps for each transition, and the vendor job ID once known. Committed first, it means a crash between request and response leaves a recoverable trace instead of an orphan.
Then make delivery to downstream systems its own tracked step. "Vendor job succeeded" and "output filed in the DMS and the requester notified" are different facts; a job can be complete on one axis and incomplete on the other. Recording them separately is what lets a restarted worker finish the half that did not happen without redoing the half that did.
Monitoring the Backlog, Not the Endpoint
The instinct is to alert on webhook errors. Do that, but understand it only catches deliveries that arrived. The failure this guide is about produces no request at all, so there is nothing to error on.
The metric that catches it is the age of the oldest job in a non-terminal state. If that number climbs past your normal completion time, something is stuck — vendor-side backlog, a dead worker, or callbacks going nowhere — and it climbs regardless of which. Alert on it, and record how many jobs were resolved by the sweeper rather than by a callback: a sudden rise in sweeper resolutions means your webhook path has quietly stopped working while everything still appears fine.
Firms in scope for DORA will need to evidence detection and reporting for ICT third-party disruptions anyway, and "we noticed within minutes because the backlog metric moved" is the answer that survives the follow-up question.
Exercising the Failure Paths Before Production Does
Four deliberate tests, none of which requires vendor cooperation.
Replay a captured callback. Post the same signed payload twice and confirm one output file, one DMS entry, one notification.
Deliver events backwards. Send succeeded, then processing. The job must remain succeeded.
Black-hole the webhook. Firewall your own endpoint, submit ten jobs, and confirm the sweeper resolves all ten within its threshold. This is the single most valuable test here, and the one most often skipped.
Kill the worker mid-flight. Terminate the process between submission and response, restart, and confirm recovery from your own store with no duplicate job. Combined with a stable idempotency key, that should be uneventful.
Run all four against the same document translation API configuration you intend to deploy, and keep them as integration tests. Async bugs return whenever someone refactors the handler.
Sources and Further Reading
Best tool to translate a 100-page Word doc — practitioners weighing long-document turnaround and what waiting on a job actually looks like
Related Reading
Choosing a Document Translation API for Automated Legal Workflows
Designing for Burst Throughput in Batch Document Translation
Last reviewed 24 August 2026 by the Bluente document engineering team, who build and test the pipeline described here. We update these guides when the underlying standards, regulations or file formats change.
A job that finished is not a job that arrived — build the sweeper. Try BluTranslate free.