Criou uma tarefa de provisionamento e recebeu um 202 em troca: um ID de motor e status: "in_progress". O agente de IA está agora a percorrer as suas fontes e a aplicar vozes da marca, itens de glossário e instruções a esse motor, em segundo plano. Isto pode demorar um instante ou algum tempo, consoante o número de ligações que tiver de percorrer. Até podia manter um WebSocket em direto aberto e acompanhar o processo – mas prefere não deixar uma ligação aberta só para saber quando o agente terminou e o que construiu.
É precisamente para isso que serve o webhook. Quando fornece uma callbackUrl ao criar a tarefa, a Lingo envia por POST o resultado final para esse URL no exato momento em que a tarefa termina – fica a saber quando o motor está pronto, com o inventário do que foi criado. Uma tarefa concluída chega como provisioning.completed, com o resumo de cada registo que a IA criou. Uma tarefa falhada chega como provisioning.failed, com o motivo. Em qualquer dos casos, o seu fluxo de configuração é informado sem ter de ir perguntar.
Esta página cobre os dois payloads e a forma de os tratar. A entrega é assinada e reenviada em caso de falha – esse mecanismo é partilhado com a localização e está documentado na página de verificação da assinatura do webhook, para a qual remetemos sempre que for preciso.
Nesta página
- Como funciona a entrega
- O payload completed
- O payload failed
- Como processar um webhook
- Quando a entrega não é a ferramenta certa
Como funciona a entrega#
Uma tarefa de provisionamento termina exatamente uma vez. No instante em que atinge um estado terminal – com todas as fontes percorridas e analisadas, ou com a execução abandonada – o resultado é entregue à sua callbackUrl num único POST. Um grupo de localização desdobra-se numa tarefa por idioma de destino, cada uma com o seu próprio callback; uma tarefa de provisionamento é uma só tarefa, por isso corresponde a uma só entrega.
Defina o destino com callbackUrl quando criar a tarefa. Há dois formatos de payload a circular, distinguidos pelo respetivo campo type: provisioning.completed e provisioning.failed. Ambos identificam a jobId e o engineId a que pertencem, para que um único handler possa encaminhar com base em type e atualizar o registo certo.
Só HTTPS
callbackUrl tem de usar HTTPS. Um URL HTTP é rejeitado quando cria a tarefa – o webhook é assinado, e enviar um payload assinado em texto simples anula completamente o propósito.
Trate tipos de evento desconhecidos com naturalidade
Neste momento, a ligação transporta provisioning.completed e provisioning.failed. Trate esse conjunto como aberto – faça branching apenas para os tipos que conhece e ignore os restantes, para que um tipo de evento futuro não quebre um handler já em produção.
O payload completed#
Quando a tarefa termina, o payload traz o summary – o mesmo inventário que obteria ao consultar a tarefa, mas enviado diretamente para si em vez de ter de o ir pedir. Identifica cada voz da marca, item de glossário e instrução que a IA criou no seu motor, e lista também quaisquer falhas por item encontradas pelo 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 | A tarefa de provisionamento que terminou (prefixo pjb_) |
engineId | O motor que configurou (prefixo eng_) |
summary | O que a IA criou no motor – contagens e IDs por componente, além das falhas por item em errors |
O summary é o mesmo objeto que a tarefa contém, e o significado de cada campo – o que é cada componente, como os itens são mapeados para idiomas, o que vai parar a errors – está documentado uma única vez em O que a IA extrai. Aqui, basta saber que o payload completed lhe entrega os IDs de tudo o que o agente criou, para que o seu handler os possa registar ou apresentar no seu dashboard sem voltar a ir buscar a tarefa.
Um array errors não vazio continua a chegar como completed.
Falhas por item não fazem a tarefa falhar. Se uma única fonte não puder ser percorrida ou um registo não puder ser criado, isso vai parar a summary.errors e tudo o resto continua a ser aplicado ao motor – e o payload continua a ser provisioning.completed, não provisioning.failed. O evento completed significa que a tarefa correu até ao fim; leia errors para perceber o que precisa de corrigir. Um payload provisioning.failed só é enviado quando a execução não produz qualquer motor utilizável.
O payload failed#
Uma tarefa de provisionamento falha quando a execução não produz nada com que trabalhar – por exemplo, quando todas as fontes falham ao ser percorridas e o agente fica sem conteúdo para analisar. Quando isso acontece, continua a ser informado. O tipo de payload é provisioning.failed e, em vez do resumo, traz uma string error:
{
"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 | A tarefa de provisionamento que falhou |
engineId | O motor que foi criado, mas ficou por configurar |
error | Motivo, em linguagem legível, pelo qual a tarefa não pôde ser concluída |
Aqui está a parte que um leitor cético tem toda a razão em questionar: se a tarefa falhou, também perdi o motor? Não. O engineId neste payload é o mesmo motor que recebeu no 202 – continua a existir, criado no momento em que fez a chamada, apenas sem a configuração que a execução falhada teria acrescentado. Uma falha custa-lhe a extração, nunca o motor. Ajuste o que submeteu e tente novamente, ou configure o motor manualmente a partir do dashboard. Quando uma tarefa falha durante o rastreio, o problema costuma estar nas fontes – Tipos de fonte explica o que faz com que uma fonte valha a pena.
Como processar um webhook#
O primeiro pensamento de um leitor cético aqui está certíssimo: o meu handler faz trabalho real – escreve na base de dados, envia uma notificação, atualiza o dashboard – por isso não vai manter a ligação aberta tempo suficiente para o webhook expirar?
Vai, por isso não faça a Lingo esperar. Devolva 200 primeiro, depois processe. Acuse a receção e só depois faça o trabalho a sério, já depois de a resposta ter sido enviada. O contrato completo de entrega – porque deve confirmar primeiro, e qual é o calendário de novas tentativas se não o fizer – está na página assinatura e entrega; o handler abaixo mostra o formato que isso assume 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 peça que esta página não define. Cada entrega é assinada segundo a especificação Standard Webhooks – três cabeçalhos, um HMAC sobre o corpo raw e um segredo whsec_ gerado na primeira vez que submete uma tarefa com callback. Os callbacks de provisionamento e de localização usam exatamente esse esquema, por isso ele está documentado uma única vez em verificação da assinatura do webhook. Ligue esse middleware antes de confiar em qualquer payload – um corpo não verificado é um corpo não autenticado.
Verifique antes de confiar no corpo
O seu endpoint é um URL público; qualquer pessoa pode fazer POST para ele. Verifique a assinatura com base no corpo raw do pedido antes de agir sobre qualquer payload – antes de marcar um motor como pronto ou de guardar os IDs que ele diz ter criado. O processo – os cabeçalhos, o HMAC, o segredo whsec_ – está na página de verificação da assinatura.
Quando a entrega não é a ferramenta certa#
O webhook é uma conveniência de push, não o sistema de registo. Há dois casos em que faz mais sentido outra abordagem, e ambos estão à distância de um clique.
Se o seu endpoint estiver em baixo quando o resultado for entregue, a plataforma volta a tentar segundo o mesmo calendário usado por todos os webhooks da Lingo – e o resultado não fica preso no callback. Os registos que a IA criou são a configuração real do motor; o resumo completed é um relatório de trabalho que já aconteceu num motor real, não a única cópia dessa informação. Por isso, um período de indisponibilidade custa-lhe uma notificação, nunca o motor. O próprio calendário de novas tentativas está na página assinatura e entrega.
E se o que procura é progresso em direto enquanto o motor está a ser configurado – um estado de rastreio seguido de configuração na UI, em vez de um único callback para o seu servidor quando tudo termina – então o que quer é o WebSocket da tarefa de provisionamento, não o webhook. Esse transmite um snapshot ao ligar e eventos de progresso à medida que a execução avança, e pode ligar-se em qualquer momento, mesmo depois de a tarefa já ter terminado.
