|
Knowledgebase
EnterprisePlatform
PlatformAPI
React (MCP)React (Lingo Compiler)
Alpha
CLIIntegrations
GuidesChangelog

Synchronous

  • How it works
  • Localize
  • Recognize
  • ProvisionEnterprise

AsynchronousEnterprise

  • How it works
  • Queue
  • Webhooks
  • WebSocket

WebSocket

Max PrilutskiyMax Prilutskiy·Updated 1 day ago·3 min read

For real-time progress updates, connect a WebSocket to a job group. Every message includes a full state snapshot, so clients never need to maintain local state or worry about missed events.

text
GET /jobs/localization/groups/:groupId/ws

Message types#

TypeWhenKey fields
snapshotOn initial connectionFull group state
job.completedA job finishes successfullyjobId, locale, plus full group state
job.failedA job failsjobId, locale, error, plus full group state
group.completedAll jobs are donegroupId, status, plus full group state

Every message contains a snapshot object with the current group state: totalJobs, completedJobs, failedJobs, and a jobs map with per-job locale and status. The type field tells you what just happened. The snapshot tells you where things stand.

Message payloads#

On connect, the server sends the current state:

json
{
  "type": "snapshot",
  "snapshot": {
    "groupId": "ljg_A1b2C3d4E5f6G7h8",
    "totalJobs": 3,
    "completedJobs": 1,
    "failedJobs": 0,
    "jobs": {
      "ljb_A1b2C3d4E5f6G7h8": { "locale": "de", "status": "completed" },
      "ljb_B2c3D4e5F6g7H8i9": { "locale": "fr", "status": "processing" },
      "ljb_C3d4E5f6G7h8I9j0": { "locale": "ja", "status": "queued" }
    }
  }
}

As jobs finish, each event includes the updated snapshot:

json
{
  "type": "job.completed",
  "jobId": "ljb_B2c3D4e5F6g7H8i9",
  "locale": "fr",
  "snapshot": {
    "groupId": "ljg_A1b2C3d4E5f6G7h8",
    "totalJobs": 3,
    "completedJobs": 2,
    "failedJobs": 0,
    "jobs": {
      "ljb_A1b2C3d4E5f6G7h8": { "locale": "de", "status": "completed" },
      "ljb_B2c3D4e5F6g7H8i9": { "locale": "fr", "status": "completed" },
      "ljb_C3d4E5f6G7h8I9j0": { "locale": "ja", "status": "processing" }
    }
  }
}
json
{
  "type": "job.failed",
  "jobId": "ljb_C3d4E5f6G7h8I9j0",
  "locale": "ja",
  "error": "Model timeout after 30 seconds",
  "snapshot": { "..." : "..." }
}

When every job has resolved, the server sends a final event and closes the connection:

json
{
  "type": "group.completed",
  "groupId": "ljg_A1b2C3d4E5f6G7h8",
  "status": "partial",
  "snapshot": { "..." : "..." }
}

The status field is completed when all jobs succeeded, partial when some failed, or failed when all failed.

Example#

javascript
import WebSocket from "ws";

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

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

  switch (event.type) {
    case "snapshot":
      // Full state on connect - initialize your progress UI
      console.log(`${snapshot.completedJobs}/${snapshot.totalJobs} complete`);
      break;
    case "job.completed":
      console.log(`${event.locale} ready (${snapshot.completedJobs}/${snapshot.totalJobs})`);
      break;
    case "job.failed":
      console.error(`${event.locale} failed: ${event.error}`);
      break;
    case "group.completed":
      console.log(`All translations done: ${event.status}`);
      ws.close();
      break;
  }
});

Keep your API key server-side

WebSocket connections require API key authentication. Connect from your backend, then proxy events to your frontend over your own WebSocket or server-sent events channel. This keeps the API key out of client-side code.

Wiring into your UI#

The job group provides a natural progress model. Store the groupId when you create jobs, then update your UI as WebSocket events arrive. A progress indicator ("3 of 14 languages ready") that updates in real time gives users confidence that translations are in progress.

javascript
// Content saved - kick off translations
async function onContentSaved(content) {
  const response = await fetch("https://api.lingo.dev/jobs/localization", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.LINGO_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      sourceLocale: "en",
      targetLocales: ["de", "fr", "ja", "ko", "pt-BR"],
      data: {
        title: content.title,
        steps: content.steps,
        metadata: content.metadata,
      },
      callbackUrl: "https://your-app.com/webhooks/translations",
      idempotencyKey: `${content.id}-v${content.version}`,
    }),
  });

  const { groupId, jobs } = await response.json();

  // Store for progress tracking in your UI
  await db.translationProgress.create({
    contentId: content.id,
    groupId,
    totalLanguages: jobs.length,
    completedLanguages: 0,
  });
}

Next Steps#

Queue
Create localization jobs and poll status
Webhooks
Receive results via webhook callbacks
How it works
Overview of the async localization API

Was this page helpful?