Sie haben einen Provisioning-Job erstellt und ein 202 zurückbekommen: eine Engine-ID und status: "in_progress". Der KI-Agent durchsucht jetzt im Hintergrund Ihre Quellen und wendet Markenstimmen, Glossareinträge und Anweisungen auf diese Engine an. Je nachdem, wie viele Links gecrawlt werden müssen, kann das schnell gehen oder etwas dauern. Sie könnten eine offene WebSocket-Verbindung halten und den Fortschritt live verfolgen – aber Sie möchten nicht nur deshalb eine Verbindung offenhalten, um zu erfahren, wann der Agent fertig ist und was er erstellt hat.
Genau dafür gibt es den Webhook. Wenn Sie beim Erstellen des Jobs eine callbackUrl übergeben, sendet Lingo das Endergebnis per POST an diese URL, sobald der Job abgeschlossen ist – Sie erfahren sofort, wann die Engine bereit ist, inklusive einer Übersicht über alles, was erstellt wurde. Ein erfolgreich beendeter Job kommt als provisioning.completed mit der Zusammenfassung aller Datensätze an, die die KI erstellt hat. Ein fehlgeschlagener Job kommt als provisioning.failed mit dem Grund an. So wird Ihr Setup-Flow in jedem Fall informiert, ohne aktiv nachfragen zu müssen.
Diese Seite behandelt die beiden Payloads und den richtigen Umgang damit. Die Zustellung ist signiert und wird bei Bedarf erneut versucht – dieser Mechanismus wird auch für Lokalisierung verwendet und ist auf der Seite Verifizierung von Webhook-Signaturen dokumentiert, auf die wir an den relevanten Stellen verlinken.
Auf dieser Seite
- So funktioniert die Zustellung
- Die completed-Payload
- Die failed-Payload
- So verarbeiten Sie einen Webhook
- Wann Zustellung das falsche Werkzeug ist
So funktioniert die Zustellung#
Ein Provisioning-Job endet genau einmal. In dem Moment, in dem er einen Endzustand erreicht – entweder wurden alle Quellen gecrawlt und analysiert oder der Lauf wurde abgebrochen –, wird das Ergebnis als einzelnes POST an Ihre callbackUrl zugestellt. Eine Lokalisierungsgruppe verzweigt in je einen Job pro Zielsprache, die jeweils ihren eigenen Callback ausliefern; ein Provisioning-Job ist genau ein Job und deshalb genau eine Zustellung.
Das Ziel legen Sie mit callbackUrl fest, wenn Sie den Job erstellen. Übertragen werden zwei Payload-Formate, unterschieden durch das Feld type: provisioning.completed und provisioning.failed. Beide nennen die zugehörige jobId und den zugehörigen engineId, sodass ein einzelner Handler anhand von type routen und den richtigen Datensatz aktualisieren kann.
Nur HTTPS
callbackUrl muss HTTPS verwenden. Eine HTTP-URL wird beim Erstellen des Jobs abgelehnt – der Webhook ist signiert, und eine signierte Payload über unverschlüsseltes HTTP würde den Zweck verfehlen.
Unbekannte Ereignistypen robust behandeln
Aktuell werden über die Schnittstelle provisioning.completed und provisioning.failed übertragen. Betrachten Sie diese Menge als offen – verzweigen Sie nach den Typen, die Sie kennen, und ignorieren Sie den Rest, damit ein zukünftiger Ereignistyp einen bereits ausgerollten Handler nicht aus dem Tritt bringt.
Die completed-Payload#
Wenn der Job abgeschlossen ist, enthält die Payload die summary – dieselbe Übersicht, die Sie auch beim Auslesen des Jobs erhalten würden, nur dass sie Ihnen aktiv zugestellt wird, statt dass Sie sie abfragen müssen. Sie nennt jede Markenstimme, jeden Glossareintrag und jede Anweisung, die die KI auf Ihrer Engine erstellt hat, und listet außerdem alle elementbezogenen Fehler auf, die unterwegs aufgetreten sind.
{
"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": []
}
}| Feld | Beschreibung |
|---|---|
type | provisioning.completed |
jobId | Der abgeschlossene Provisioning-Job (Präfix pjb_) |
engineId | Die konfigurierte Engine (Präfix eng_) |
summary | Was die KI auf der Engine erstellt hat – Anzahlen und IDs pro Komponente sowie elementbezogene Fehler in errors |
Das summary ist dasselbe Objekt, das auch im Job enthalten ist. Die Bedeutung der einzelnen Felder – was jede Komponente ist, wie Elemente auf Sprachen abgebildet werden und was in errors landet – ist zentral auf Was die KI extrahiert dokumentiert. Hier reicht es zu wissen, dass Ihnen die completed-Payload die IDs von allem liefert, was der Agent erstellt hat, sodass Ihr Handler sie speichern oder in Ihrem Dashboard anzeigen kann, ohne den Job erneut abrufen zu müssen.
Auch ein nicht leeres errors-Array kommt als completed an.
Elementbezogene Fehler lassen den Job nicht fehlschlagen. Wenn eine einzelne Quelle nicht gecrawlt werden konnte oder ein Datensatz nicht erstellt werden konnte, landet das in summary.errors und alles andere wird trotzdem auf die Engine angewendet – die Payload bleibt also provisioning.completed und wird nicht zu provisioning.failed. Das completed-Ereignis bedeutet, dass der Job bis zum Ende durchgelaufen ist; lesen Sie errors, um zu sehen, was behoben werden muss. Eine Payload vom Typ provisioning.failed wird gesendet, wenn der Lauf überhaupt keine nutzbare Engine hervorgebracht hat.
Die failed-Payload#
Ein Provisioning-Job schlägt fehl, wenn der Lauf nichts Verwertbares hervorbringt – zum Beispiel, wenn keine Quelle gecrawlt werden kann und der Agent deshalb keinen Inhalt zur Analyse hat. Auch dann werden Sie informiert. Der Payload-Typ ist provisioning.failed und sie enthält anstelle der Zusammenfassung einen error-String:
{
"type": "provisioning.failed",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"engineId": "eng_X1y2Z3a4B5c6D7e8",
"error": "All sources failed to crawl. No content available for analysis."
}| Feld | Beschreibung |
|---|---|
type | provisioning.failed |
jobId | Der fehlgeschlagene Provisioning-Job |
engineId | Die erstellte, aber unkonfiguriert gebliebene Engine |
error | Menschenlesbarer Grund, warum der Job nicht abgeschlossen werden konnte |
Hier stellt sich zu Recht die skeptische Frage: Wenn der Job fehlgeschlagen ist, habe ich dann auch die Engine verloren? Nein. Die engineId in dieser Payload ist dieselbe Engine, die Sie im 202 erhalten haben – sie existiert weiterhin, wurde in dem Moment erstellt, in dem Sie den Aufruf gemacht haben, nur ohne die Konfiguration, die der fehlgeschlagene Lauf sonst hinzugefügt hätte. Ein Fehlschlag kostet Sie die Extraktion, nie die Engine. Passen Sie Ihre Eingaben an und versuchen Sie es erneut, oder konfigurieren Sie die Engine im Dashboard von Hand. Wenn ein Job bereits beim Crawling scheitert, liegt die Ursache meist bei den Quellen – Quelltypen erklärt, was eine Quelle sinnvoll macht.
So verarbeiten Sie einen Webhook#
Die erste skeptische Reaktion ist hier genau die richtige: Mein Handler macht echte Arbeit – einen Datenbank-Write, eine Benachrichtigung, ein Dashboard-Refresh – hält das die Verbindung nicht lange genug offen, damit der Webhook in ein Timeout läuft?
Doch – also lassen Sie Lingo nicht darauf warten. Geben Sie zuerst 200 zurück und verarbeiten Sie dann. Bestätigen Sie den Empfang und erledigen Sie die eigentliche Arbeit erst, nachdem die Antwort gesendet wurde. Der vollständige Zustellungsvertrag – warum Sie zuerst bestätigen sollten und welcher Retry-Plan greift, wenn Sie es nicht tun – steht auf der Seite Signatur und Zustellung; der Handler unten zeigt, wie das für eine Provisioning-Payload aussieht.
app.post("/webhooks/provisioning", verifyWebhook, async (req, res) => {
// Acknowledge first - the job ends once, so this fires once.
res.status(200).send("ok");
const { type, jobId, engineId } = req.body;
if (type === "provisioning.completed") {
const { summary } = req.body;
await db.engines.update({
where: { engineId },
data: {
status: "ready",
brandVoiceCount: summary.brandVoices.count,
glossaryCount: summary.glossaryItems.count,
instructionCount: summary.instructions.count,
},
});
}
if (type === "provisioning.failed") {
console.error(`Provisioning failed: ${jobId} (${engineId})`, req.body.error);
await db.engines.update({
where: { engineId },
data: { status: "needs_configuration" },
});
}
});Die verifyWebhook-Middleware ist der eine Baustein, den diese Seite nicht selbst definiert. Jede Zustellung wird nach der Spezifikation Standard Webhooks signiert – mit drei Headern, einem HMAC über den rohen Body und einem whsec_-Secret, das beim ersten Einreichen eines Jobs mit Callback erzeugt wird. Provisioning- und Lokalisierungs-Callbacks verwenden dieses Schema unverändert, daher ist es zentral auf Verifizierung von Webhook-Signaturen dokumentiert. Binden Sie die Middleware ein, bevor Sie einer Payload vertrauen – ein nicht verifizierter Body ist kein authentifizierter Body.
Verifizieren Sie den Body, bevor Sie ihm vertrauen
Ihr Endpunkt ist eine öffentliche URL; jede Person kann ein POST dorthin senden. Verifizieren Sie die Signatur anhand des rohen Request-Bodys, bevor Sie auf eine Payload reagieren – bevor Sie eine Engine als bereit markieren oder die IDs speichern, die sie angeblich erstellt hat. Wie das funktioniert – die Header, das HMAC, das whsec_-Secret – steht auf der Seite Signaturverifizierung.
Wann Zustellung das falsche Werkzeug ist#
Der Webhook ist eine praktische Push-Lösung, aber nicht das System of Record. In zwei Fällen ist etwas anderes die bessere Wahl – und beides ist nur einen Link entfernt.
Wenn Ihr Endpunkt nicht verfügbar war, als das Ergebnis zugestellt wurde, versucht die Plattform die Zustellung nach demselben Plan erneut, den jeder Lingo-Webhook verwendet – und das Ergebnis steckt nicht im Callback fest. Die Datensätze, die die KI erstellt hat, sind die tatsächliche Konfiguration der Engine; die completed-Zusammenfassung ist ein Bericht über Arbeit, die bereits auf einer realen Engine stattgefunden hat, nicht ihre einzige Kopie. Selbst eine längere Downtime kostet Sie also höchstens eine Benachrichtigung, nie die Engine. Der Retry-Plan selbst ist auf der Seite Signatur und Zustellung beschrieben.
Und wenn Sie Live-Fortschritt möchten, während die Engine konfiguriert wird – also einen UI-Status für Crawling und anschließende Konfiguration statt eines einzelnen Callbacks an Ihren Server, wenn alles abgeschlossen ist –, dann ist dafür der WebSocket des Provisioning-Jobs da, nicht der Webhook. Er streamt beim Verbinden einen Snapshot und während des Laufs Fortschrittsereignisse, und Sie können sich jederzeit verbinden – auch nachdem der Job bereits abgeschlossen ist.
