Webhooks
Nível de acesso requerido
Admin ou Super Admin
Para que servem os Webhooks?
Webhooks permitem que o Flowi Agentic notifique sistemas externos automaticamente quando eventos ocorrem na plataforma, sem que o sistema externo precise ficar consultando (polling).
Exemplos de uso:
- Notificar um ERP quando uma instância de compra é aprovada
- Enviar dados para um dashboard externo quando uma tarefa é concluída
- Disparar automação em outro sistema quando um processo termina
Como funcionam
Quando um evento ocorre no Flowi Agentic, o sistema envia uma requisição HTTP POST para a URL cadastrada no webhook, com os dados do evento no corpo (JSON).
Cadastrando um webhook
- Acesse Integrações e automação → Webhooks
- Clique em Novo Webhook
- Preencha:
- Nome — identificação do webhook
- URL — endpoint do sistema externo que vai receber as notificações
- Eventos — marque quais eventos disparam esse webhook, ou Todos os eventos (
*). Não há campo de texto: a tela só oferece os eventos que a plataforma emite - Secret de assinatura (HMAC) — deixe em branco para a plataforma gerar um, ou digite o seu
- Clique em Criar
- A tela mostra o secret de assinatura numa janela com o botão de copiar. Copie agora e guarde no sistema que recebe o webhook: ele não é exibido de novo — nem na listagem, nem na edição, nem pela API. Se perder, gere um novo (veja abaixo)
Secret guardado no cofre
Quando a instalação tem um cofre ligado (ver Cofre externo), o campo Secret de assinatura ganha a escolha Valor guardado / Referência ao cofre. Com Referência ao cofre, informe onde o secret mora — por exemplo vault://kv/flowi/tenants/<seu-tenant>/webhooks/erp#hmac — e a plataforma lê o valor no cofre a cada entrega para assinar o HMAC.
- Não há janela de "copie agora": o secret é seu e já está no cofre. O painel do webhook mostra a referência.
- A referência é conferida ao salvar: não existe no cofre, ou está fora do prefixo do seu Tenant, e o cadastro é recusado.
- Se o cofre estiver fora do ar na hora da entrega, nada é enviado sem assinatura: a entrega fica como Falhou, nova tentativa agendada com o motivo e segue a retentativa normal.
Editando, desativando e trocando o secret
Clique no webhook na listagem (ou chegue nele com Tab e aperte Enter) para abrir o painel dele:
- Editar abre uma janela com nome, URL, eventos, Ativo e o Secret de assinatura. Com o campo do secret em branco, salvar a edição não troca o secret — o sistema receptor continua validando com o mesmo. Num webhook cujo secret mora no cofre, o campo já vem com a referência: troque-a por outra para apontar para outro segredo, ou escolha Valor guardado e digite um secret para deixar de usar o cofre.
- A chave Ativo em cada cartão da listagem liga e desliga o webhook sem abrir nada. Um webhook desligado não envia nada. Desligar é sempre permitido, mesmo que o host tenha saído dos destinos permitidos ou que o webhook esteja marcado como Nunca dispara; religar confere a lista de novo. Uma edição pela API que não mande
enabledmantém o webhook como está. - Gerar novo secret pede confirmação e mostra o novo secret uma única vez, como no cadastro. O secret anterior deixa de valer na hora: atualize o sistema receptor logo em seguida.
- Num webhook cujo secret mora no cofre, Gerar novo secret fica desabilitado, com o motivo no painel: gerar um secret aqui trocaria a referência por um valor guardado na plataforma. Gire o segredo no próprio cofre, ou edite o webhook para apontar para outra referência.
Para onde o webhook pode enviar
Cada tenant declara os destinos permitidos dos seus webhooks, em Integrações e automação → Webhooks → Destinos permitidos — um host por linha:
erp.cliente.com.br
*.parceiro.com.br*.parceiro.com.br alcança api.parceiro.com.br e não alcança parceiro.com.br. A porta é ignorada na comparação. É o mesmo formato da lista de destinos dos Agentes de IA — as duas metades do mesmo controle.
Por que isso existe: antes disso, o webhook mandava os eventos de processo do tenant para qualquer URL que um administrador tivesse digitado, e nada respondia "se este webhook disparar, para onde vão os dados" antes de ele disparar. A lista responde, e a tela mostra, para cada webhook, o host efetivo e o estado dele:
| Estado | O que significa |
|---|---|
| Destino declarado | o host está na lista — vai entregar |
| Sem lista declarada | nada limita o destino; a instalação permite, mas ninguém declarou nada |
| Fora da lista de destinos | o host não está na lista — não vai entregar |
| Nenhum destino declarado | a instalação exige lista e este tenant não tem nenhuma |
Um destino que não está na lista é recusado ao salvar, com o host no texto do erro. Se a lista mudar depois que o webhook foi cadastrado, a entrega é recusada no momento do envio e fica registrada como BLOCKED no histórico, com o motivo — não é uma falha de rede, e não é retentada.
O estado da lista é calculado a partir do host escrito na URL, e só dele. A verificação de que o endereço não é interno acontece em dois outros momentos — ao salvar o webhook e a cada envio — e não aparece nesse estado: um host que passou a resolver para um endereço interno depois do cadastro continua exibido como Destino declarado e é recusado na hora de entregar.
Endereço interno nunca é destino
localhost, 127.0.0.1, as faixas privadas (10., 172.16-31., 192.168.), link-local e os endpoints de metadados de nuvem (169.254.169.254) são recusados mesmo que você os escreva na lista. A lista amplia o alcance externo; ela não revoga essa proteção.
Instalação nova: WEBHOOKS_EGRESS_DEFAULT_POLICY=deny
Com allow (padrão, por compatibilidade), tenant sem lista entrega para qualquer endereço externo. Com deny, tenant sem lista não entrega nada, e quem integra declara onde. Gêmeo de AI_HTTP_TOOL_DEFAULT_POLICY.
O que aconteceu com os webhooks que já existiam
A atualização preencheu a lista de cada tenant com os hosts dos webhooks que ele já tinha. Nada que entregava parou de entregar, e a lista nasceu verdadeira: é o conjunto de destinos que estavam realmente em uso. Cadastrar um destino novo passou a ser dois passos — declarar o host e depois cadastrar o webhook.
Validação HMAC
Toda entrega é assinada — não existe webhook sem secret. O Flowi Agentic calcula o HMAC-SHA256 do corpo com o secret do webhook e envia a assinatura no header:
X-FlowIa-Signature: sha256=<assinatura>
X-FlowIa-Event: instance.completedNo sistema receptor, valide a assinatura usando o mesmo secret — o que a tela mostrou ao cadastrar ou ao gerar um novo: ela prova que o corpo saiu daqui e não foi alterado no caminho. Calcule o HMAC sobre o corpo exatamente como chegou, antes de reformatar o JSON.
O que a assinatura não diz é quando a requisição foi feita. Não há carimbo de tempo nem número de sequência no cabeçalho, e a retentativa reenvia o mesmo corpo com a mesma assinatura — o mesmo evento pode chegar até três vezes, todas válidas. Trate cada entrega como idempotente, usando os dados do próprio evento para reconhecer o que já foi processado.
Eventos disponíveis
| Evento | Disparado quando |
|---|---|
instance.started | Uma instância de processo é iniciada |
instance.completed | Uma instância é concluída |
instance.failed | Uma instância falha |
task.created | Uma nova tarefa é criada |
task.completed | Uma tarefa é concluída |
* | Qualquer um dos eventos acima |
task.overdue não existe
Vencimento de prazo não é um evento do motor: nada acontece no instante em que a data passa. Entregar esse evento exige uma varredura periódica das tarefas abertas, que a plataforma não faz — por isso ele não está na lista, e a API recusa um webhook cadastrado com ele.
Cadastrando pela API
Em POST e PUT /api/a/webhooks, o campo events é uma lista JSON de nomes de evento enviada como texto:
{ "name": "ERP", "url": "https://erp.cliente.com.br/hook", "events": "[\"instance.completed\", \"task.completed\"]" }A API responde 400 — dizendo o valor recusado e listando os válidos — quando events não é uma lista ("instance.completed", "task.completed,instance.started"), quando a lista está vazia ou quando traz um nome que não está na tabela acima. Sem events, o webhook assina todos (["*"]). O nome do evento é comparado inteiro: instance.completed não casa com xinstance.completed.
Webhook que nunca dispara
Antes dessa validação a API aceitava qualquer texto, e a própria tela mandava os eventos separados por vírgula — um formato que nunca casava com evento nenhum. Esses cadastros não foram apagados nem reescritos: a listagem mostra Nunca dispara no webhook, com a explicação escrita logo abaixo, e a resposta da API traz "eventsValid": false. Clique em Editar no webhook e escolha os eventos de novo (ou envie um PUT com uma lista válida) — não precisa excluir e cadastrar outra vez, e o secret continua o mesmo.
Quando a entrega acontece
O evento grava a entrega como pendente, e um processo em segundo plano a envia — em até um minuto, não no instante do evento. A chamada ao seu servidor fica de propósito fora da transação do processo: se ela acontecesse ali dentro, uma instância concluindo seguraria recursos do banco durante todo o tempo de resposta do seu endpoint, inclusive o timeout.
E se o processo desfizer a transação, a entrega desfaz junto. Uma instância que não chegou a iniciar não anuncia que iniciou.
Retentativa
Em caso de falha (servidor externo retornar erro ou não responder), o Flowi Agentic reenfileira a entrega e tenta de novo a cada minuto, até três tentativas. Depois disso a entrega fica como FAILED e não é mais tentada. O histórico fica registrado em cada webhook.
Não há backoff exponencial — o intervalo é fixo.
Monitorando entregas
Na listagem de webhooks, clique em um para ver as 50 entregas mais recentes daquele webhook: o evento, o status, o código HTTP devolvido pelo seu servidor, o número de tentativas e a data.
| Status na tela | Na API | O que significa |
|---|---|---|
| Entregue | SUCCESS | o seu servidor respondeu 2xx |
| Pendente | PENDING | aguardando o envio (até um minuto) |
| Falhou, nova tentativa agendada | RETRYING | o servidor respondeu erro ou não respondeu; vai tentar de novo |
| Falhou, tentativas esgotadas | FAILED | três tentativas sem sucesso, ou um teste que falhou; não será mais tentada |
| Bloqueado | BLOCKED | o destino não está nos destinos permitidos ou é endereço interno; não é retentada |
Clique numa entrega para ver o motivo: o trecho da resposta do seu servidor (num 401 ou 500, o que ele devolveu no corpo), a mensagem de erro de rede quando não houve resposta, ou o motivo do bloqueio. O trecho é cortado em 1000 caracteres e, se o seu servidor ecoar o secret, ele aparece como ***. O corpo enviado não é exibido nessa tela.
Testando um webhook
No painel do webhook, escolha em Evento do teste um dos eventos que ele assina e clique em Testar. A plataforma envia na hora — sem esperar o minuto da fila — um corpo no mesmo formato do evento real, assinado como qualquer entrega:
{ "event": "instance.completed", "processInstanceId": "test-3f2a…", "tenantId": "acme" }O processInstanceId começa com test-: é assim que o seu sistema reconhece que é um teste. O resultado aparece logo abaixo do botão — status, código HTTP e o motivo — e a entrega entra no histórico com o nome do evento escolhido. Se falhar, ela fica como Falhou, tentativas esgotadas na hora: um teste nunca entra na retentativa, para o seu sistema não receber o evento de mentira de novo minutos depois. Corrija o problema e clique em Testar outra vez.