Você criou um job de provisionamento e recebeu um 202 de volta: um ID de engine e status: "in_progress". Agora, o agente de IA está rastreando suas fontes e aplicando vozes da marca, itens do glossário e instruções a esse engine em segundo plano. Isso pode levar alguns instantes ou demorar mais, dependendo de quantos links ele precisa rastrear. Você até poderia manter um WebSocket ativo aberto e acompanhar tudo em tempo real — mas provavelmente não quer deixar uma conexão aberta só para saber quando o agente terminou e o que ele criou.
É exatamente para isso que serve o webhook. Quando você informa uma callbackUrl ao criar o job, a Lingo envia o resultado final para essa URL via POST no momento em que o job termina — você fica sabendo quando o engine está pronto, junto com o inventário do que foi criado. Um job concluído chega como provisioning.completed, com o resumo de cada registro que a IA criou. Um job com falha chega como provisioning.failed, com o motivo. Em ambos os casos, seu fluxo de setup é avisado sem precisar consultar nada.
Esta página cobre os dois payloads e como lidar com eles. A entrega é assinada e reenviada em caso de falha — esse mecanismo é compartilhado com a localização e está na página de verificação de assinatura do webhook, com links nos pontos em que você vai precisar dele.
Nesta página
- Como a entrega funciona
- O payload completed
- O payload failed
- Como processar um webhook
- Quando a entrega não é a ferramenta certa
Como a entrega funciona#
Um job de provisionamento termina exatamente uma vez. No instante em que atinge um estado terminal — com todas as fontes rastreadas e analisadas, ou com a execução abandonada — o resultado é entregue à sua callbackUrl em um único POST. Um grupo de localização se desdobra em um job por idioma de destino, cada um com seu próprio callback; já um job de provisionamento é um único job, então gera uma única entrega.
Defina o destino com callbackUrl ao criar o job. Há dois formatos de payload trafegando por essa entrega, diferenciados pelo campo type: provisioning.completed e provisioning.failed. Ambos informam a qual jobId e engineId pertencem, para que um único handler possa rotear com base em type e atualizar o registro certo.
Apenas HTTPS
callbackUrl deve usar HTTPS. Uma URL HTTP é rejeitada quando você cria o job — o webhook é assinado, e enviar um payload assinado em texto puro vai contra todo o propósito.
Trate tipos de evento desconhecidos com elegância
Hoje, essa entrega inclui provisioning.completed e provisioning.failed. Trate esse conjunto como aberto — faça branch apenas para os tipos que você conhece e ignore o restante, para que um tipo de evento futuro não quebre um handler já em produção.
O payload completed#
Quando o job termina com sucesso, o payload traz o summary — o mesmo inventário que você teria ao consultar o job, mas enviado para você em vez de exigir polling. Ele informa cada voz da marca, item do glossário e instrução que a IA criou no seu engine, além de listar qualquer falha por item encontrada no caminho.
{
"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": []
}
}| Campo | Descrição |
|---|---|
type | provisioning.completed |
jobId | O job de provisionamento concluído (prefixo pjb_) |
engineId | O engine que ele configurou (prefixo eng_) |
summary | O que a IA criou no engine — contagens e IDs por componente, além das falhas por item em errors |
O summary é o mesmo objeto que o job carrega, e seu significado campo a campo — o que é cada componente, como os itens se relacionam aos idiomas, o que entra em errors — está documentado uma única vez em O que a IA extrai. Aqui, basta saber que o payload completed entrega os IDs de tudo o que o agente criou, para que seu handler possa registrá-los ou exibi-los no dashboard sem precisar buscar o job de novo.
Um array errors não vazio ainda chega como completed.
Falhas por item não fazem o job falhar. Se uma única fonte não puder ser rastreada ou um registro não puder ser criado, isso vai para summary.errors e todo o restante ainda é aplicado ao engine — e o payload continua sendo provisioning.completed, não provisioning.failed. O evento completed significa que o job foi até o fim; leia errors para ver o que precisa ser corrigido. Um payload provisioning.failed só é enviado quando a execução não produz nenhum engine utilizável.
O payload failed#
Um job de provisionamento falha quando a execução não produz nada utilizável — por exemplo, quando todas as fontes falham no rastreamento e o agente fica sem conteúdo para analisar. Quando isso acontece, você ainda é avisado. O tipo do payload é provisioning.failed, e ele traz uma string error no lugar do resumo:
{
"type": "provisioning.failed",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"engineId": "eng_X1y2Z3a4B5c6D7e8",
"error": "All sources failed to crawl. No content available for analysis."
}| Campo | Descrição |
|---|---|
type | provisioning.failed |
jobId | O job de provisionamento que falhou |
engineId | O engine que foi criado, mas ficou sem configuração |
error | Motivo em linguagem natural pelo qual o job não pôde ser concluído |
Aqui está a pergunta que todo leitor cético tem razão em fazer: se o job falhou, eu perdi o engine também? Não. O engineId deste payload é o mesmo engine que você recebeu no 202 — ele continua existindo, criado no momento em que você fez a chamada, só que sem a configuração que a execução com falha teria adicionado. Uma falha faz você perder a extração, nunca o engine. Ajuste o que você enviou e tente de novo, ou configure o engine manualmente pelo dashboard. Quando um job falha no rastreamento, o problema geralmente está nas fontes — Tipos de fonte explica o que faz uma fonte valer a pena como entrada.
Como processar um webhook#
O primeiro pensamento de um leitor cético aqui está certíssimo: meu handler faz trabalho de verdade — grava no banco, envia uma notificação, atualiza o dashboard — então isso não vai manter a conexão aberta por tempo suficiente para o webhook expirar?
Vai. Então não faça a Lingo esperar por isso. Retorne 200 primeiro e processe depois. Confirme o recebimento e só então faça o trabalho real, depois que a resposta já tiver sido enviada. O contrato completo de entrega — por que você confirma primeiro e qual é a cadência de tentativas se não fizer isso — está na página de assinatura e entrega; o handler abaixo mostra como isso fica para um payload de provisionamento.
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" },
});
}
});O middleware verifyWebhook é a única parte que esta página não define. Toda entrega é assinada de acordo com a especificação Standard Webhooks — três cabeçalhos, um HMAC sobre o corpo bruto e um segredo whsec_ gerado na primeira vez que você envia um job com callback. Os callbacks de provisionamento e de localização usam esse mesmo esquema sem mudanças, por isso ele fica centralizado em verificação de assinatura do webhook. Configure o middleware antes de confiar em qualquer payload — um corpo não verificado é um corpo não autenticado.
Verifique antes de confiar no corpo
Seu endpoint é uma URL pública; qualquer pessoa pode fazer POST para ela. Verifique a assinatura com base no corpo bruto da requisição antes de agir sobre qualquer payload — antes de marcar um engine como pronto ou armazenar os IDs que ele diz ter criado. O processo — os cabeçalhos, o HMAC, o segredo whsec_ — está na página de verificação de assinatura.
Quando a entrega não é a ferramenta certa#
O webhook é uma conveniência de envio, não a fonte da verdade. Há dois cenários em que outra ferramenta faz mais sentido — e ambos estão a um clique de distância.
Se o seu endpoint estava fora do ar quando o resultado foi entregue, a plataforma tenta de novo na mesma cadência usada por todos os webhooks da Lingo — e o resultado não fica preso no callback. Os registros que a IA criou são a configuração real do engine; o resumo de conclusão é um relatório de um trabalho que já aconteceu em um engine de verdade, não a única cópia dessas informações. Então, um período de indisponibilidade faz você perder uma notificação, nunca o engine. A própria cadência de tentativas está na página de assinatura e entrega.
E se o que você quer é progresso em tempo real enquanto o engine é configurado — um status de rastreamento seguido de configuração em uma UI, em vez de um único callback para seu servidor quando tudo termina — então a ferramenta certa é o WebSocket do job de provisionamento, não o webhook. Ele transmite um snapshot na conexão e eventos de progresso à medida que a execução avança, e você pode se conectar a qualquer momento, inclusive depois que o job terminar.
