Create a fully configured localization engine from your existing content. Submit links to your website, style guides, or raw text — an AI agent crawls the sources, extracts brand voice, glossary terms, and translation instructions, then applies them to a new engine. One POST returns a 202 in milliseconds. The configured engine is ready to use when the job completes.
On this page
Authentication#
All provisioning endpoints require API key authentication. Pass your API key in the X-API-Key header. API keys are scoped to an organization and have access to all localization engines within it. See API Keys for details.
How it works#
Submit sources
POST to /jobs/provisioning with a name for your new engine and an array of sources — URLs to crawl or raw content to analyze. The API creates the engine immediately and returns 202 with the engine ID and job ID.
AI extraction
The platform crawls link sources in parallel, then an AI agent analyzes all content to extract brand voice profiles, glossary terms (custom translations and non-translatable terms), and translation instructions. Each extracted item is applied to the new engine as it's identified.
Engine ready
When extraction completes, the engine is fully configured and ready to use with the localization API. Results are delivered via webhook or WebSocket, including a summary of everything the AI created.
Create a provisioning job#
POST /jobs/provisioning| Parameter | Type | Description |
|---|---|---|
engine.name | string | Name for the new localization engine |
engine.description | string (optional) | Engine description |
locales | string[] (optional) | Array of BCP-47 target locales (e.g., ["es", "ja", "de"]) |
sources | array (optional) | Up to 10 link or content sources to analyze |
callbackUrl | string (optional) | HTTPS webhook URL to receive provisioning results |
Request#
const response = await fetch("https://api.lingo.dev/jobs/provisioning", {
method: "POST",
headers: {
"X-API-Key": process.env.LINGO_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
engine: {
name: "Acme Corp Engine",
description: "Production localization engine for acme.com",
},
locales: ["de", "fr", "ja", "es"],
sources: [
{ type: "link", payload: "https://acme.com/brand-guidelines" },
{ type: "link", payload: "https://acme.com/docs/style-guide" },
{
type: "content",
payload:
"Brand name 'Acme' is never translated. Use formal tone in German (Sie-form). Product names: AcmeFlow, AcmeSync, AcmeVault — always keep in English.",
},
],
callbackUrl: "https://your-app.com/webhooks/provisioning",
}),
});
const { jobId, engineId, status } = await response.json();
// 202 back in milliseconds.
// status: "in_progress" — AI is analyzing your sources.
console.log(engineId); // "eng_X1y2Z3a4B5c6D7e8"Response (202 Accepted)#
{
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"engineId": "eng_X1y2Z3a4B5c6D7e8",
"status": "in_progress"
}| Field | Description |
|---|---|
jobId | Provisioning job ID (pjb_ prefix). Use to poll status or connect via WebSocket. |
engineId | The new engine's ID (eng_ prefix). Available immediately — configuration is applied as the job runs. |
status | in_progress when sources are provided, completed when no sources (engine created with defaults). |
When no sources are provided, the engine is created with default model configurations and returned immediately with status: "completed".
Source types#
Each source in the sources array has a type and payload:
| Type | Payload | Use case |
|---|---|---|
link | URL to crawl | Websites, documentation, style guides, brand pages |
content | Raw text or markdown | Brand guidelines, terminology lists, translation rules, product descriptions |
Link sources are crawled in parallel and converted to markdown before analysis. Content sources are passed directly to the AI agent.
Provide meaningful sources
The quality of the extracted configuration depends on the quality of your input. Link sources should point to pages with useful context — brand guidelines, style guides, product documentation, glossaries. Raw content sources should contain concrete terminology, tone guidance, or translation rules. Generic marketing pages or login screens produce little useful configuration.
What the AI extracts#
The AI agent analyzes all sources and creates three types of engine configuration:
| Component | What it looks for | Example |
|---|---|---|
| Brand voices | Tone, style, formality level, writing conventions | "Use formal German (Sie-form). Keep sentences concise and direct." |
| Glossary items | Product names, technical terms, brand-specific translations, non-translatable terms | "Acme" → non-translatable, "workspace" → "Arbeitsbereich" (de) |
| Instructions | Formatting rules, cultural conventions, domain-specific guidelines | "Always format dates as DD.MM.YYYY in German translations." |
Brand voices and instructions use * as the target locale when they apply across all languages. Glossary items are created per locale pair, with non-translatable terms using * to apply globally.
Output summary#
When a provisioning job completes, the output summary reports everything the AI created:
{
"brandVoices": {
"count": 3,
"ids": ["bv_A1b2C3d4", "bv_B2c3D4e5", "bv_C3d4E5f6"]
},
"glossaryItems": {
"count": 12,
"ids": ["gi_A1b2C3d4", "gi_B2c3D4e5", "..."]
},
"instructions": {
"count": 5,
"ids": ["ins_A1b2C3d4", "ins_B2c3D4e5", "..."]
},
"errors": []
}Individual item creation failures don't fail the overall job. Failed items appear in the errors array, while successfully created items are applied to the engine.
Webhooks#
When a provisioning job completes or fails, the platform POSTs the result to your callback URL.
HTTPS required
Webhook URLs must use HTTPS. The platform rejects HTTP callback URLs.
Payload#
When the job completes:
{
"type": "provisioning.completed",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"engineId": "eng_X1y2Z3a4B5c6D7e8",
"summary": {
"brandVoices": { "count": 3, "ids": ["bv_A1b2C3d4", "bv_B2c3D4e5", "bv_C3d4E5f6"] },
"glossaryItems": { "count": 12, "ids": ["gi_A1b2C3d4", "..."] },
"instructions": { "count": 5, "ids": ["ins_A1b2C3d4", "..."] },
"errors": []
}
}When the job fails:
{
"type": "provisioning.failed",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"engineId": "eng_X1y2Z3a4B5c6D7e8",
"error": "All sources failed to crawl. No content available for analysis."
}Signature verification#
Provisioning webhooks use the same Standard Webhooks signature scheme as localization job webhooks. The three headers (webhook-id, webhook-timestamp, webhook-signature) and HMAC-SHA256 verification process are identical.
Retry behavior#
If your endpoint returns a non-2xx status code or is unreachable, the platform retries with exponential backoff up to 4 attempts. After all retries are exhausted, the webhook is marked as failed. The engine and its configuration are still available — only the notification delivery failed.
WebSocket#
For real-time progress updates, connect a WebSocket to the provisioning job. The platform sends two types of messages: an initial snapshot of the current job state, then progress events as the workflow advances.
GET /jobs/provisioning/:jobId/wsMessage types#
| Type | When | Key fields |
|---|---|---|
provisioning.snapshot | On initial connection | jobId, status, errorMessage |
provisioning.progress | As each workflow step starts or completes | jobId, step, detail |
Snapshot (on connect)#
The server immediately sends the current job state from the database:
{
"type": "provisioning.snapshot",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"status": "in_progress",
"errorMessage": null
}| Field | Description |
|---|---|
status | in_progress, completed, or failed |
errorMessage | Error description when status is failed, otherwise null |
If the job has already finished (completed or failed), the server sends the snapshot and closes the connection immediately. This means you can connect at any point — even after the job finishes — and get the final state.
Progress events#
As the workflow runs, the server broadcasts progress events:
{
"type": "provisioning.progress",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"step": "crawling",
"detail": "Crawling source URLs..."
}step | When | Example detail |
|---|---|---|
crawling | Source URLs are being fetched | "Crawling source URLs..." or "Retrying crawl (attempt 2)..." |
configuring | AI agent is analyzing content and creating engine config | "AI agent analyzing content and configuring engine..." or "Retrying configuration (attempt 2)..." |
completed | Job finished successfully | "Provisioning complete" |
failed | Job failed | Error message describing the failure |
Example#
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(`Job status: ${event.status}`);
break;
case "provisioning.progress":
console.log(`${event.step}: ${event.detail}`);
if (event.step === "completed" || event.step === "failed") {
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.
