|
Documentation
Book a DemoPlatform
PlatformMCPCLIAPI
Workflows
GuidesChangelog

Welcome

  • Overview
  • Authentication
  • Errors & status codes
  • Webhook signatures

Localization

  • Overview
  • Create jobs
  • Lock non-translatable keys
  • Track a job group
  • Get a single job
  • List jobs
  • Webhook delivery
  • Live progress (WebSocket)

Pipeline

  • Overview
  • Pre-localization AI edit
  • Human review
  • AI review (post-edit)
  • Rephrase for natural copy
  • Back-translation check
  • Configure the pipeline
  • Observe pipeline runs

Provisioning

  • Overview
  • Create a provisioning job
  • Source types
  • What the AI extracts
  • Webhook delivery
  • Live progress (WebSocket)

Synchronous

  • Localize
  • Recognize

Engine management

  • Engine Suggestions

Provisioning live progress

You created a provisioning job and got back a pjb_ job ID and an eng_ engine ID in milliseconds. The engine is usable already, but it is still filling in: an AI agent is crawling your sources and writing brand voices, glossary items, and instructions onto it. While that runs you want to show the work – a "Crawling your style guide… configuring the engine… done" line, the way an install wizard does, instead of a spinner that says nothing.

The WebSocket gives you exactly that feed. Connect to the job and the server pushes a snapshot of the current state, then a provisioning.progress event each time the workflow moves to a new step. And because the server sends the current state on connect and closes a finished job right after, you can connect any time, even after it finishes – there is no window you have to catch.

text
GET /jobs/provisioning/:jobId/ws

The jobId is the pjb_ value from the create call. New to async provisioning? Start with the Overview for the mental model.

On this page

  • Message types
  • Snapshot on connect
  • Progress events
  • Connecting after the job finishes
  • Wiring it into your UI
  • Keep your API key server-side

Message types#

Two message types travel over the socket. The first arrives once, on connect; the second arrives repeatedly, as the job advances.

TypeWhenKey fields
provisioning.snapshotOn initial connectionjobId, status, errorMessage
provisioning.progressAs each workflow step starts or completesjobId, step, detail

This is a liveness feed, not a results feed: it tells you where the job is and whether it failed, not which records the AI created. The summary of everything provisioned – the brand-voice, glossary, and instruction IDs – arrives separately, in the completion webhook or by reading the job once it is done. Keep the socket for the progress bar; reach for the webhook for the payload.

Snapshot on connect#

The instant you connect, the server reads the job's current state from the database and sends it. No progress event is required first – the snapshot stands on its own.

json
{
  "type": "provisioning.snapshot",
  "jobId": "pjb_A1b2C3d4E5f6G7h8",
  "status": "in_progress",
  "errorMessage": null
}
FieldDescription
statusin_progress, completed, or failed.
errorMessageThe failure description when status is failed, otherwise null.

The snapshot is the one message you are guaranteed to receive. If the job is still running you will get progress events after it; if the job has already finished you will get the snapshot and nothing more (see below).

Progress events#

As the workflow runs, the server broadcasts a provisioning.progress event each time it enters a new step. Each event names the step and carries a human-readable detail.

json
{
  "type": "provisioning.progress",
  "jobId": "pjb_A1b2C3d4E5f6G7h8",
  "step": "crawling",
  "detail": "Crawling source URLs..."
}
stepWhenExample detail
crawlingSource URLs are being fetched"Crawling source URLs..." or "Retrying crawl (attempt 2)..."
configuringThe AI agent is analyzing content and writing engine config"AI agent analyzing content and configuring engine..." or "Retrying configuration (attempt 2)..."
completedThe job finished successfully"Provisioning complete"
failedThe job failedAn error message describing the failure

A retry is not a failure

The crawling and configuring steps can fire more than once – a transient fetch or analysis error retries, and the retry surfaces as a progress event with a detail like "Retrying crawl (attempt 2)...". That is the job recovering, not the job failing. Treat only the failed step as terminal; its detail carries the actual reason.

Handle steps you do not recognize

New step values may be added over time. Switch on the steps you know, treat completed and failed as the two that close the socket, and ignore anything else as informational – a forward-compatible client keeps working without an update.

Connecting after the job finishes#

The hard question with any progress socket is what happens if you connect late – after the crawl is done, after a deploy reconnected the tab, after the job has already failed. Here the answer is built into how the snapshot works.

If the job has already reached completed or failed, the server sends the snapshot with that final status (and errorMessage, if it failed) and closes the connection immediately. There are no progress events to replay, because the final state is the snapshot. A job still in flight keeps the connection open and streams progress; a finished job hands you the outcome and hangs up.

Either way, the first message tells you where things stand. Connect any time, even after it finishes – you cannot connect too early and you cannot connect too late.

Wiring it into your UI#

Open the socket against the pjb_ job ID, read the snapshot to set your initial state, then update on each progress event and close when the job reaches completed or failed:

javascript
import WebSocket from "ws";

const jobId = "pjb_A1b2C3d4E5f6G7h8";
const ws = new WebSocket(
  `wss://api.lingo.dev/jobs/provisioning/${jobId}/ws`,
  { headers: { "X-API-Key": process.env.LINGO_API_KEY } }
);

ws.on("message", (raw) => {
  const event = JSON.parse(raw);

  switch (event.type) {
    case "provisioning.snapshot":
      console.log(`status: ${event.status}`);
      break;

    case "provisioning.progress":
      console.log(`${event.step}: ${event.detail}`);
      if (event.step === "completed" || event.step === "failed") {
        ws.close();
      }
      break;
  }
});

Run against a job that crawls cleanly and that prints the configuration happening, step by step:

text
status: in_progress
crawling: Crawling source URLs...
configuring: AI agent analyzing content and configuring engine...
completed: Provisioning complete

That is the whole arc on screen: the job opens in_progress, you watch it crawl and then configure, and completed tells you the engine is fully provisioned. The same loop is correct on a late connect – a finished job sends one snapshot with its final status and the socket closes, so the code that handles the live run handles the replay without a special case.

Keep your API key server-side#

The socket authenticates with your API key – the same organization-scoped key the REST endpoints use. That key reaches every engine in your organization, so the browser is the wrong place to open the connection: anyone who views source would see it.

Connect from your backend, not the browser

Open the WebSocket from your server, where the key already lives, then forward the progress to the browser over your own channel – a WebSocket or server-sent events stream you control. Your frontend shows the engine configuring; your key never leaves your infrastructure.

This mirrors the webhook model: the connection that touches Lingo.dev runs server-side, and what reaches the user is whatever your own app chooses to forward.

Where this fits#

The WebSocket is the live view – it is bound to one job and closes when that job is done. For a durable, server-to-server record of the result that survives a closed tab or a deploy, pair it with the completion webhook: the socket drives the progress bar while the job is on screen, the webhook delivers the summary of everything the AI created the moment it lands. Wire both from the same create call.

Webhook delivery
The completion and failure payloads, with the full summary of brand voices, glossary items, and instructions - plus signature verification.
Create a provisioning job
POST /jobs/provisioning - where the pjb_ job ID you connect to here comes from.
Translate with your new engine
Once the job completes, fan content out to every locale through the async Localization API.

Was this page helpful?

Max PrilutskiyMax Prilutskiy·Updated 3 days ago·6 min read