Este documento é para quem quer “ligar” automações sem precisar ser técnico.
- Entrada de Leads: você cola uma URL/“senha” no Hotmart/n8n/Make e os leads entram no seu funil automaticamente.
- Follow-up: quando o lead muda de etapa, o CRM avisa seu sistema (n8n/Make/WhatsApp).
Acesso: configurações de Webhooks são admin-only.
- Vá em
Configurações → Integrações → Webhooks. - Você verá dois cards:
- Entrada de Leads (Webhook) (inbound)
- Follow-up (Webhook de saída) (outbound)
Na UI você consegue:
- Editar (board/etapa do inbound, URL do outbound)
- Ativar/Desativar
- Excluir
- Copiar URL e Copiar secret
- Clique em Ativar entrada de leads e escolha:
- qual funil (Board)
- qual etapa (Estágio)
- Copie:
- URL do webhook
- Secret (a “senha”)
- No Hotmart/n8n/Make:
- crie um passo “Enviar para URL (HTTP Request)”
- cole a URL
- cole o Secret no header
X-Webhook-Secret(ouAuthorization: Bearer <secret>) - envie o lead com pelo menos e-mail ou telefone
-
Clique em Conectar follow-up e cole a URL do seu destino (n8n/Make/etc).
-
Pronto: o CRM vai avisar sua URL quando o lead mudar de etapa.
-
No seu destino, valide o header
X-Webhook-Secret(ouAuthorization: Bearer ...) — é a “senha” do aviso.
- URL correta?
- Secret correto?
- Você testou com um lead real?
- No Follow-up: você moveu o lead de etapa? (só dispara quando muda)
Quando você cria a “Entrada de Leads”, o CRM gera um source_id e a URL fica no formato:
POST {SUPABASE_URL}/functions/v1/webhook-in/<source_id>
A UI monta essa URL a partir do
NEXT_PUBLIC_SUPABASE_URL.
Você precisa enviar o header:
X-Webhook-Secret: <secret>- (alternativa)
Authorization: Bearer <secret>
- (alternativa)
Esse secret é o “token” do webhook. Trate como senha.
Content-Type: application/jsonX-Webhook-Secret: ...(ouAuthorization: Bearer ...)
Campos aceitos (todos opcionais, mas recomenda-se enviar pelo menos email ou phone):
- Campos do “Novo Negócio” (recomendado):
deal_title(string): nome do negóciodeal_value(number|string): valor estimado do negóciocompany_name(string): empresa do clientecontact_name(string): nome do contato principal
external_event_id(string): opcional (recomendado apenas para integrações “de evento” com retry, ex.: Hotmart)name(string): (legado) nome do contatoemail(string)phone(string)source(string): ex."hotmart","n8n","make"notes(string)company_name(string)
Ao receber o POST, o handler (supabase/functions/webhook-in/index.ts):
- valida
X-Webhook-Secret - registra auditoria em
webhook_events_inquandoexternal_event_idexiste (idempotência para retry) - faz upsert de contato por
emaile/ouphone(na mesmaorganization_id) - cria ou atualiza um deal em aberto no board configurado (para evitar duplicidade em reenvio de “cadastro”)
- (se enviar
company_name) cria/vincula a empresa emcrm_companiese liga no contato/deal viaclient_company_id(best-effort) - grava metadados em
deals.custom_fields:inbound_source_idinbound_external_event_id
curl -X POST 'https://SEU-PROJETO.supabase.co/functions/v1/webhook-in/<source_id>' \
-H 'Content-Type: application/json' \
-H 'X-Webhook-Secret: <secret>' \
-d '{
"deal_title": "Contrato Anual - Acme",
"deal_value": 12000,
"company_name": "Empresa Ltd",
"contact_name": "Lead Teste",
"email": "teste@exemplo.com",
"phone": "+5511999999999",
"source": "webhook"
}'O endpoint retorna algo como:
{
"ok": true,
"message": "Recebido! Criamos um novo negócio no funil configurado.",
"action": { "contact": "created|updated|none", "company": "created|linked|none", "deal": "created|updated" },
"organization_id": "...",
"contact_id": "...",
"deal_id": "..."
}Atualmente, o inbound não possui “regenerar secret” na UI. Para trocar o secret:
- Exclua a configuração de “Entrada de Leads”
- Crie novamente (isso gera um novo
source_ide um novosecret)
O CRM dispara quando um deal muda de etapa (deals.stage_id muda).
Implementação: trigger no Postgres (notify_deal_stage_changed) via migration
supabase/migrations/20251226010000_integrations_webhooks_product.sql.
Você informa na UI uma URL (ex.: n8n/Make/WhatsApp provider webhook) e o CRM faz:
POST <sua_url>
O CRM envia o header:
X-Webhook-Secret: <secret do endpoint>
Se você regenerar o secret na UI, você precisa atualizar também no seu sistema (n8n/Make/etc).
O payload do outbound (em webhook_events_out.payload) tem este formato:
{
"event_type": "deal.stage_changed",
"occurred_at": "2025-12-26T00:00:00.000Z",
"deal": {
"id": "...",
"title": "...",
"value": 0,
"board_id": "...",
"board_name": "...",
"from_stage_id": "...",
"from_stage_label": "...",
"to_stage_id": "...",
"to_stage_label": "...",
"contact_id": "..."
},
"contact": {
"name": "...",
"phone": "...",
"email": "..."
}
}O envio é feito via pg_net (async):
- a entrega é registrada em
webhook_deliveries webhook_deliveries.request_idguarda o id donet.http_post(...)- em caso de exceção no disparo, a delivery é marcada como
failed
MVP: não existe retry/backoff automático no banco.
As tabelas principais:
integration_inbound_sources: configurações de inbound (admin-only)integration_outbound_endpoints: configurações de outbound (admin-only)webhook_events_in: auditoria de eventos inboundwebhook_events_out: auditoria de eventos outboundwebhook_deliveries: tentativas de entrega outbound
Últimos inbound recebidos:
select id, received_at, status, external_event_id, error
from webhook_events_in
order by received_at desc
limit 50;Últimos outbound gerados:
select id, created_at, event_type, deal_id
from webhook_events_out
order by created_at desc
limit 50;Entregas outbound:
select d.id, d.attempted_at, d.status, d.request_id, d.response_status, d.error
from webhook_deliveries d
order by d.attempted_at desc
limit 50;- Nunca exponha o
secretem client-side público/landing pages. - No seu endpoint (n8n/Make/servidor), valide o header
X-Webhook-Secret. - Gere
external_event_idno provedor de origem para garantir idempotência. - Se suspeitar de vazamento:
- inbound: recrie a configuração (gera novo
source_ide secret) - outbound: use Regenerar secret e atualize o destino
- inbound: recrie a configuração (gera novo