Pular para o conteúdo principal

Webhooks

Um webhook entrega um POST JSON, assinado, no endereço que você cadastrou, quando um evento acontece. Cadastre pelo console (Configurações → Webhooks), pela API (POST /v1/webhooks) ou pelo kadu.yaml.

Corpo​

{
"event": "execution.completed",
"event_id": "5b0e…",
"delivered_at": "2026-10-31T12:04:51.210Z",
"organization_id": "1d7a…",
"workspace_id": "9c3f…",
"data": { "…": "o recurso do evento, em snake_case" }
}
CampoO que é
evento nome do evento (lista em Webhooks)
event_idid do evento: o mesmo em todas as tentativas de entrega dele
delivered_atquando esta entrega saiu
organization_ida organização
workspace_ido workspace do evento, ou null num evento da organização
datao recurso do evento. custom_metadata e metadata vão como foram gravados.

Um webhook com workspace recebe só os eventos daquele workspace. Um webhook sem workspace recebe todos.

Headers​

HeaderConteúdo
X-Kobana-Appkadu
X-Kobana-Environmentproduction ou staging. Confira antes de processar: uma entrega de staging nunca deve tocar dados de produção.
X-Kobana-Webhook-Ido webhook que gerou a entrega
X-Kobana-Evento nome do evento
X-Kobana-Event-Ido id do evento
X-Kobana-Delivery-Ido id desta entrega
X-Kobana-Timestampsegundos Unix em que a entrega foi assinada
X-Kobana-Signaturea assinatura
webhook-id, webhook-timestamp, webhook-signatureos mesmos três valores, no layout Standard Webhooks
Authorization ou o header configuradoa credencial que você configurou no webhook (Bearer, Basic ou header)

Conferir a assinatura​

A assinatura segue o Standard Webhooks: qualquer biblioteca de verificação compatível serve.

  • Signing secret: whsec_ seguido de 32 bytes em base64. A chave do HMAC são os bytes decodificados depois do prefixo.
  • Conteúdo assinado: <delivery_id>.<timestamp>.<corpo cru>, com o X-Kobana-Delivery-Id (= webhook-id) e o X-Kobana-Timestamp.
  • Header: v1,<base64(HMAC-SHA256)>. Durante uma rotação, o header traz uma assinatura por secret ainda válido, separadas por espaço. Aceite a entrega se qualquer uma casar.
  • Janela: recuse uma entrega com timestamp a mais de 5 minutos do seu relógio, para limitar replay.
import base64, hashlib, hmac, time

def verificar(secret: str, delivery_id: str, timestamp: str, corpo: bytes, assinatura: str) -> bool:
if abs(time.time() - int(timestamp)) > 300:
return False
chave = base64.b64decode(secret.removeprefix("whsec_"))
conteudo = f"{delivery_id}.{timestamp}.".encode() + corpo
esperado = base64.b64encode(hmac.new(chave, conteudo, hashlib.sha256).digest()).decode()
return any(
hmac.compare_digest(token.split(",", 1)[1], esperado)
for token in assinatura.split()
if token.startswith("v1,")
)

Use o corpo cru, antes de qualquer parse de JSON: reserializar muda os bytes e quebra a assinatura.

:::note Header legado X-Kobana-Signature-Sha256 é o esquema antigo do Kadu (sha256=<hex> de timestamp.corpo) e está depreciado. Use X-Kobana-Signature. :::

Signing secret e rotação​

O signing secret aparece uma vez, na criação do webhook. Para trocar, use POST /v1/webhooks/{id}/rotate-secret: a resposta traz o novo secret, e o anterior continua assinando por 24 horas (previous_signing_secret_expires_at), para você trocar a chave no receptor sem perder entregas.

Entregas e falhas​

  • Responda 2xx em até 10 segundos e processe depois. Qualquer outra resposta, ou o timeout, conta como falha.
  • O mesmo evento pode chegar mais de uma vez: use o event_id para descartar duplicatas.
  • Depois de 5 falhas seguidas, o webhook é desativado e os e-mails cadastrados nele são avisados.
  • GET /v1/webhook_deliveries lista as 50 entregas mais recentes, com status e resposta, e POST /v1/webhook_deliveries/{id}/replay reenvia uma entrega.
  • Entregas ficam guardadas por 90 dias.

A URL de destino precisa ser pública e https. Endereços privados, de loopback ou em http são recusados.