Páginas da documentação
Guia
Desenvolvedores · Guia · v1.2.0
Recebendo webhooks
Os avisos que o seu sistema recebe, o corpo de cada um e como conferir a assinatura.
Eventos
Cadastre a URL do seu sistema em POST /webhooks (ou no painel, em API) e receba um POST em JSON a cada evento. Responda 2xx em até 15 segundos; o processamento pesado deve ficar para depois da resposta.
| Evento | Quando | Em data |
|---|---|---|
| campaign.published | A campanha chegou à plataforma (em análise, ativa ou pausada). | {campaign, previous_status} |
| campaign.failed | A publicação falhou; o motivo vem em error.message. Corrija e use POST /campaigns/{id}/retry. | {campaign, previous_status} |
| campaign.status_changed | O status mudou depois de publicada (aprovada, pausada, reprovada, concluída, excluída na Meta...). | {campaign, previous_status} |
| campaign.deleted | A campanha foi excluída (pela API, pelo painel ou descartada). | {campaign} |
| ad.approved | Um anúncio passou na revisão da Meta (ou teve a reprovação revertida). | {campaign_id, ad} |
| ad.rejected | Um anúncio foi reprovado pela Meta; o motivo vem em rejection_reason. | {campaign_id, ad} |
| alert.created | Algo novo pede atenção na conta de anúncio: pagamento, problema de entrega, verba no limite ou gasto sem resultado. | {alert} |
| ad_account.status_changed | A situação da conta de anúncio mudou (ativa, restrita, desativada ou encerrada). | {ad_account, previous_status, status} |
| lead.created | Chegou um lead novo de um formulário instantâneo. | {lead} |
| lead.status_changed | O status de um lead mudou (contatado, qualificado, venda, perdido...), pela API ou pelo painel. | {lead, previous_status} |
| lead.scored | A IA deu a nota do lead (quente, morno ou frio) com o motivo e a mensagem sugerida. | {lead} |
| brain.report_ready | O Gestor IA terminou uma análise: aguardando aprovação, sem ação ou já executada no automático. | {report} (com actions) |
| media.ready | A mídia enviada por URL foi baixada e está pronta para os criativos. | {media} |
| media.failed | Não deu para baixar a mídia enviada por URL; o motivo vem em error. | {media} |
| webhook.test | Envio de teste (botão "Testar" ou POST /webhooks/{id}/test). | {message} |
Cada objeto em data vem no mesmo formato da rota GET correspondente (campaign como GET /campaigns/{id} sem os conjuntos, lead como GET /leads/{id} sem o histórico). Os eventos lead.* exigem que quem cadastra o webhook possa ver os leads, e brain.report_ready, que possa ver o Gestor IA.
{
"id": "evt_01jbz9a1c5f9j3m7q1t5x9b3e7",
"type": "campaign.published",
"api_version": "v1",
"created_at": "2026-10-28T15:02:11+00:00",
"account_id": "01jbw8r5x9a3d7g1k5n9s3w7b1",
"data": {
"campaign": {
"id": "01jby0q4c8m2d6t9v3k7w5r1xa",
"name": "Promoção de outubro",
"goal": "traffic",
"goal_label": "Tráfego",
"status": "pending_review",
"status_label": "Em análise",
"created_via": "api",
"budget": {
"level": "campaign",
"type": "daily",
"amount_cents": 5000,
"currency": "BRL"
},
"schedule": {
"starts_at": "2026-10-28T03:00:00+00:00",
"ends_at": null
},
"error": null,
"platform": "meta",
"platform_campaign_id": "120212345678900001",
"published_at": "2026-10-28T15:02:11+00:00",
"synced_at": "2026-10-28T15:40:00+00:00",
"created_at": "2026-10-28T15:00:00+00:00",
"updated_at": "2026-10-28T15:02:11+00:00",
"links": {
"panel": "https://app.leadgo.com.br/app/campaigns/01jby0q4c8m2d6t9v3k7w5r1xa"
}
},
"previous_status": "creating"
}
}
Conferindo a assinatura
Cada aviso traz X-Leadgo-Event, X-Leadgo-Event-Id (o mesmo em todas as tentativas), X-Leadgo-Timestamp e X-Leadgo-Signature. A assinatura é v1= seguido do HMAC-SHA256, em hexadecimal, de "{timestamp}.{corpo}" com o segredo do webhook. No seu receptor:
- Leia o corpo cru, exatamente como chegou, antes de converter o JSON.
- Calcule v1= + HMAC-SHA256 (hex) de "{X-Leadgo-Timestamp}.{corpo}" com o segredo do webhook.
- Compare com X-Leadgo-Signature em tempo constante (hash_equals, crypto.timingSafeEqual, hmac.compare_digest).
- Recuse avisos com mais de 5 minutos de diferença entre o timestamp e o seu relógio.
- Descarte eventos repetidos pelo X-Leadgo-Event-Id.
# corpo.json: o corpo cru do aviso, byte a byte.
# TIMESTAMP e ASSINATURA: os cabeçalhos X-Leadgo-Timestamp e X-Leadgo-Signature.
ESPERADA="v1=$( { printf '%s.' "$TIMESTAMP"; cat corpo.json; } \
| openssl dgst -sha256 -hmac "$LEADGO_WEBHOOK_SECRET" | sed 's/^.* //')"
[ "$ESPERADA" = "$ASSINATURA" ] && echo "assinatura válida" || echo "recusar"
Para testar o receptor, peça um aviso webhook.test assinado:
curl -X POST "https://leadgo.com.br/api/v1/webhooks/01jbz8e2h6k0n4r8u2x6b0f4jm/test" \ -H "Authorization: Bearer $LEADGO_TOKEN" \ -H "Accept: application/json"
Entregas e novas tentativas
- Sem resposta 2xx, tentamos de novo depois de 1, 5, 15 e 60 minutos.
- Depois de 10 avisos perdidos seguidos, o webhook é desligado e quem administra a API é avisado no painel.
- Os avisos podem chegar fora de ordem: o corpo é uma foto do momento (veja previous_status). Na dúvida, consulte GET /campaigns/{id}.
- O endereço precisa ser https público; não seguimos redirecionamentos.
- Até 5 webhooks por conta.