Se você desenvolve no CRM daHubSpot por tempo suficiente, cedo ou tarde esbarra no mesmo trio de problemas: retentativas (retries), duplicidades e visibilidade. Webhooks do HubSpot são poderosos, mas a diferença entre uma integração frágil e outra à prova de bala está em como você projeta idempotência, backoff, filas/DLQ e observabilidade.
Este guia foca em webhooks do HubSpot em duas direções:
- Outbound (HubSpot ➜ seus sistemas)
• via Webhooks API (assinaturas em um app público/privado)
• via Workflows → "Enviar um webhook" - Inbound (seus sistemas ➜ HubSpot)
• via gatilho de workflow "Quando um webhook é recebido" (funcionalidade mais recente)
Vamos cobrir as peças móveis, trazer exemplos de código práticos e mostrar padrões que “sobrevivem a falhas” sem vazar duplicidades ou perder eventos.
As três formas de webhooks do HubSpot (e quando usar cada uma)
1) Webhooks API (assinaturas de app)
Crie um app público ou privado e assine eventos de objetos do CRM (por exemplo, contact.creation, deal.propertyChange). O HubSpot envia lotes de eventos em JSON para sua URL alvo sempre que os eventos assinados ocorrem. Excelente para streaming do CRM para seu data plane ou middleware.
Comportamento de entrega. O HubSpot reitenta entregas que falham na Webhooks API em até 10 vezes ao longo de ~24 horas, com atrasos randomizados para reduzir “thundering herds”. Essa mudança foi introduzida para melhorar a confiabilidade e suavizar picos de retentativas.
Características do payload. As mensagens incluem identificadores como eventId, subscriptionId, portalId, objectId e um contador attemptNumber — úteis para idempotência e diagnóstico. (A documentação do HubSpot referencia eventId e attemptNumber nas tabelas de payload do webhook.)
2) Workflows — “Enviar um webhook” (não requer app)
Dentro de um workflow, adicione Enviar um webhook e selecione POST ou GET. Você pode incluir todas as propriedades ou customizar o corpo com campos específicos (mais valores estáticos), além de escolher a autenticação (cabeçalho de assinatura de requisição usando um App ID, ou um API key/cabeçalho). Há também uma configuração de rate limit (BETA) para estrangular execuções.
Lógica de retentativa do workflow. Se seu endpoint falhar, o HubSpot reitenta por até três dias, começando ~1 minuto após a primeira falha, com intervalos crescentes (máximo de 8 horas entre tentativas). Workflows não reintentam em respostas 4xx, exceto 429 (seguem o cabeçalho Retry-After em milissegundos). Isso é crítico para seu plano de tratamento de erros.
3) “Quando um webhook é recebido” (dispare workflows a partir de apps externos)
Agora é possível inscrever registros quando o HubSpot recebe um webhook de um sistema de terceiros. Você define uma propriedade de correspondência única e mapeia campos do JSON recebido; o content-type deve ser application/json. Isso fecha o ciclo para webhooks entrantes que disparam automações no HubSpot.
Segurança primeiro: verificando assinaturas v3 do HubSpot
Para requisições saindo do HubSpot (HubSpot ➜ você), o HubSpot assina com v3, usando os cabeçalhos X-HubSpot-Signature-v3 e X-HubSpot-Request-Timestamp. A verificação é direta:
- Rejeite a requisição se o timestamp for mais antigo que ~5 minutos.
- Monte a string: requestMethod + requestUri + requestBody + timestamp.
- Calcule o HMAC-SHA256 com o segredo do app e Base64-encode o resultado.
- Compare com X-HubSpot-Signature-v3.
O Enviar um webhook de Workflows também pode anexar um cabeçalho de assinatura (você informa o App ID, então verifica com o segredo do app no seu servidor).
Idempotência e deduplicação com webhooks do HubSpot
A regra de ouro: processar cada evento exatamente uma vez — mesmo quando o HubSpot reenviar ou seu código rodar duas vezes.
Onde ancorar chaves de idempotência:
- eventId é o ID canônico do evento disparador. Use-o como chave de idempotência.
- attemptNumber (começa em 0) aumenta nas retentativas; não deve criar trabalho novo — registre para telemetria.
O HubSpot referencia um attempt ID derivado de subscriptionId + eventId + attemptNumber no monitoramento; ainda assim, sua idempotência deve se basear em eventId.
Padrão de armazenamento. Mantenha um store rápido (Redis, DynamoDB, Postgres com índice único) de eventId processados. Insira no primeiro processamento bem-sucedido; em duplicatas, acuse recebimento e pule.
Segurança a jusante. Se seu handler cria/atualiza registros em outros sistemas, faça essas chamadas idempotentes também (por exemplo, upserts por uma chave natural como email ou um ID externo) para evitar efeitos colaterais duplicados nos seus sistemas.
Políticas de retentativa & backoff (HubSpot e você)
Workflows (HubSpot ➜ seu endpoint)
- Reintentam por até 3 dias, começando em ~1 minuto, com gap máximo de 8 horas.
- Não reintentam em 4xx, exceto 429 (honram Retry-After).
- Dica: Projete seu endpoint para responder 2xx rápido assim que aceitar o job na fila, e use 429 quando quiser que o HubSpot recue segundo seu orçamento de taxa.
Webhooks API (assinaturas de app) (HubSpot ➜ seu endpoint)
- Até 10 retentativas dentro de 24 horas, com atraso randomizado para espalhar carga. Se não puder processar agora, retorne um não‑2xx para o HubSpot reiterar; quando aceitar o job, responda 200 rapidamente.
Suas retentativas (seu receiver ➜ HubSpot/outros serviços)
Se seu receiver chamar APIs do HubSpot ou de terceiros, adote exponential backoff com jitter do seu lado e evite loops de feedback. Respeite a semântica de HTTP 429 do HubSpot se re‑chamar a API a partir do handler.
Arquitetura que sobrevive a falhas: fila e DLQ
Padrão: ack rápido ➜ fila ➜ worker ➜ DLQ (dead‑letter queue)
- Receba o webhook, verifique a assinatura, faça validação leve.
- Persista o evento (ex.: S3) e enfileire um job pequeno referenciando eventId + ponteiro para o payload.
- Ack 200 imediatamente para o HubSpot.
- Processe assíncrono em workers.
- Em falhas repetidas acima do limite, dead‑letter o job para investigação e replay manual.
O que isso te dá:
- Throughput e controle de backpressure (você define concorrência e limites).
- Idempotência natural (workers checam seu store de idempotência antes do trabalho).
Reprocessamento (você pode replay a partir do DLQ/arquivos sem pedir reenvio ao HubSpot).
Exemplo prático: receptor Express (Node.js) com assinatura v3 + idempotência no Redis
Adapte para qualquer stack; o ponto é o formato, não a lib específica.
<p>import crypto from "crypto";</p>
<p>import express from "express";</p>
<p>import bodyParser from "body-parser";</p>
<p>import { createClient } from "redis";<br><br>const app = express();</p>
<p><br>// Preserve raw body for signature verification<br>app.use(<br> bodyParser.json({<br> verify: (req, res, buf) => {<br> req.rawBody = buf;<br> },<br> })<br>);<br><br>const APP_SECRET = process.env.HUBSPOT_APP_SECRET;<br>const redis = createClient({ url: process.env.REDIS_URL });<br>await redis.connect();<br><br>function verifyV3(req) {<br> const sig = req.get("X-HubSpot-Signature-v3");<br> const ts = req.get("X-HubSpot-Request-Timestamp"); // ms since epoch<br> if (!sig || !ts) return false;<br><br> // Reject stale<br> const ageMs = Math.abs(Date.now() - Number(ts));<br> if (ageMs > 5 * 60 * 1000) return false;<br><br> const base =<br> req.method +<br> req.originalUrl +<br> (req.rawBody?.toString() || "") +<br> ts;<br><br> const hmac = crypto<br> .createHmac("sha256", APP_SECRET)<br> .update(base)<br> .digest("base64");<br><br> return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(sig));<br>}<br><br>app.post("/webhooks/hubspot", async (req, res) => {<br> if (!verifyV3(req)) return res.sendStatus(401);<br><br> // HubSpot sends batches (array of events)<br> const events = Array.isArray(req.body) ? req.body : req.body?.data || [];<br><br> for (const evt of events) {<br> const key = `hubspot:event:${evt.eventId}`;<br> const inserted = await redis.set(key, "1", {<br> NX: true,<br> EX: 7 * 24 * 3600, // 7-day TTL<br> });<br><br> if (!inserted) continue; // duplicate; already processed<br><br> // enqueue lightweight job (pseudo)<br> await enqueue({<br> id: evt.eventId,<br> type: evt.subscriptionType,<br> objectId: evt.objectId,<br> attempt: evt.attemptNumber,<br> payload: evt,<br> });<br> }<br><br> // Ack fast so HubSpot uses its success path<br> res.sendStatus(200);<br>});<br><br>app.listen(3000);</p>
Por que funciona: eventId é sua chave de idempotência; attemptNumber é telemetria. O HubSpot assina com v3, então tráfego antigo ou adulterado é descartado. Lotes são suportados iterando o array antes de enfileirar.
Workflows — “Enviar um webhook”: payload, auth, rate limit & retentativas
Ao enviar webhooks a partir de um workflow:
- Método & URL: suporte a POST e GET; URL deve ser HTTPS.
- Corpo: todas as propriedades ou corpo customizado com propriedades selecionadas e campos estáticos. (Prefira campos estáveis se ações posteriores dependerem da resposta.)
- Autenticação:
• Assinatura de requisição (cabeçalho) usando seu App ID (verifique com o segredo do app).
• API key em query ou header; ou OAuth token guardado como secret se você for chamar APIs do HubSpot. - Rate limit (BETA): configure execuções por janela para proteger sistemas a jusante. A ação pausa e retoma automaticamente quando voltar ao limite.
- Testes: use Test action para inspecionar request/response, ou envie para webhook.site para inspeção externa rápida.
Retentativas: até 3 dias; sem retentativa em 4xx (exceto 429, respeitando Retry‑After). Projete seu receiver considerando isso.
Exemplo de corpo (customizado) para webhook de Negócio/Deal:
<p>{</p>
<p><span style="color: #000000;"> </span>"dealId":<span style="color: #000000;"> </span>"",</p>
<p><span style="color: #000000;"> </span>"dealName":<span style="color: #000000;"> </span>"",</p>
<p><span style="color: #000000;"> </span>"amount":<span style="color: #000000;"> </span>"",</p>
<p><span style="color: #000000;"> </span>"stage":<span style="color: #000000;"> </span>"",</p>
<p><span style="color: #000000;"> </span>"companyDomain":<span style="color: #000000;"> </span>"",</p>
<p><span style="color: #000000;"> </span>"staticSource":<span style="color: #000000;"> </span>"hubspot-workflow"</p>
<p>}</p>
Playbook de integração (exemplo OMS):
- Workflow: Enviar webhook no estágio Closed Won.
- Autenticação: cabeçalho de assinatura de requisição (verifique com o segredo do app).
- Rate limit: 60 execuções/min (BETA) para blindar o OMS.
- Receiver do OMS: verifique assinatura v3 (rejeite se >5 min), coloque o payload na fila deals e responda 200 imediatamente.
- Worker: idempotência por externalId = "hubspot:" + dealId + ":" + closedDate (ou, se usando Webhooks API, use o eventId).
Ação: chamar /orders/upsert idempotente no OMS.
Checklist de implementação (copiar/colar)
Configuração no HubSpot
- Escolha o mecanismo certo: Webhooks API (nível de app), webhooks de Workflow, ou gatilhos de webhook de entrada.
- Para webhooks de Workflow, configure autenticação, corpo e (opcionalmente) rate limit (BETA).
- Documente as expectativas de retentativa para as partes interessadas (3 dias em Workflows vs 24 horas em assinaturas).
Receiver
- Verifique assinatura v3 e registre eventId em store de idempotência.
- Enfileire trabalho leve e ack rápido (2xx).
- Implemente backoff com jitter nas chamadas a APIs a jusante (incluindo o próprio HubSpot).
- Arquive payloads brutos para replay e tenha uma DLQ.
Observabilidade
- Monitore tentativas/retentativas (incluindo attemptNumber), latência, taxa de sucesso e erros por tipo de assinatura.
- Correlacione eventId entre o HubSpot e seus logs.
Alerta para picos de 429 e falhas 5xx.
“Gotchas” frequentes
Os webhooks do HubSpot sempre enviam o objeto completo?
Webhooks de Workflow podem incluir todas as propriedades (do tipo do objeto) ou seu corpo customizado; seja intencional para evitar payloads pesados. Webhooks de assinaturas de app enviam payloads focados no evento (você pode buscar mais via API usando objectId).
Os webhooks chegam em ordem?
Não conte com ordem estrita. Preserve ordem você mesmo se precisar (ex.: filas FIFO por objeto) e faça handlers idempotentes.
Juntando tudo
Projetar webhooks confiáveis no HubSpot não é “colocar uma URL e torcer”. É:
- Idempotente por padrão (eventId/chaves únicas).
- Fila‑first com DLQ e caminho de replay.
- Backoff‑aware sobre as janelas de retentativa do HubSpot e seus próprios limites.
- Observável de ponta a ponta (logs do HubSpot + sua telemetria).
- Seguro com assinatura v3 e janela de tempo apertada.
Faça isso e suas integrações vão sobreviver às falhas — sem duplicidades, eventos perdidos ou panes misteriosas.
Referências (comportamentos e setup)
- Workflows — "Enviar um webhook": setup, auth, rate limit (BETA), testes e política de retentativa de 3 dias com nuances de 4xx/429.
- Gatilho de entrada “Quando um webhook é recebido”: inscrição, mapeamento e application/json.
- Webhooks API: assinaturas e campos de payload de eventos (incluindo eventId, attemptNumber).
- Lógica de retentativa da Webhooks API: 10 retentativas em 24 horas com randomização de atrasos.
- Assinatura v3: X-HubSpot-Signature-v3, X-HubSpot-Request-Timestamp, HMAC‑SHA256 + Base64.
Pro tip #1: trate “webhooks do HubSpot” na sua documentação interna e mensagens de commit como você trataria uma API — nomes claros, contratos explícitos e um plano para o dia em que algo sair do trilho. Seu “eu do futuro” agradece.
Pro tip #2: com webhooks do HubSpot, também é possível disparar ações com base em valores de propriedades. Cada evento entregue inclui atributos como propertyName e propertyValue. Validando esses valores contra requisitos específicos, você consegue acender sua cadeia de regras de negócio e garantir que sistemas a jusante reajam apenas quando as condições certas forem atendidas.