Saltar para o conteúdo principal

Webhooks (Saída)

Webhooks permitem que o SalesOS chame o seu sistema quando algo acontece (um lead é ofertado, um ranking é publicado, uma mensagem é disparada). Você nos dá uma URL HTTPS e um segredo; nós fazemos POST de um payload JSON assinado sempre que um evento assinado dispara.
Esta página cobre webhooks de saída (SalesOS → seu endpoint). Para enviar dados para dentro do SalesOS, veja Atividades e API Keys.

Configurar um webhook no Dashboard

Vá em Integrações → Webhooks → Novo Webhook e preencha o formulário.
1

Básico

  • Nome (obrigatório) — ex.: Notificar CRM.
  • Chave — um identificador estável (ex.: notify_crm).
  • Descrição — opcional.
2

Eventos (gatilhos)

Escolha uma Categoria e selecione os eventos que disparam este webhook — esta é a sua assinatura (veja o catálogo). Deixe vazio para um webhook que você só dispara manualmente (botão Testar) ou via workflow.
3

Destino

  • MétodoPOST (padrão), PUT ou PATCH.
  • Timeout (segundos) — quanto esperamos pelo seu 2xx (padrão 30).
  • URL (obrigatório) — seu endpoint. Precisa ser https:// e público (hosts internos/loopback são bloqueados).
  • Autenticação — escolha HMAC para assinar cada entrega e, em Configuração de Autenticação (JSON), informe o segredo:
    { "secret": "whsec_seu_segredo" }
    
    (Outros esquemas disponíveis: Bearer, API Key, Basic, OAuth2.)
4

Avançado

  • Headers Customizados (JSON) — headers extras enviados em cada entrega.
  • Template do Payload (JSON com variáveis {{ }}) — molda o data do envelope a partir do contexto do evento, ex.: { "id": "{{event.id}}", "type": "{{event.type}}", "to": "{{event.to}}" }.
Os valores do template são interpolados em JSON. Use campos planos e escalares. Injetar um objeto aninhado via "{{event.data}}" pode não renderizar limpo hoje — confira sempre o Preview / envie um Teste antes.
5

Preview, testar e salvar

O formulário mostra um Preview ao vivo (headers + corpo). Clique em Salvar webhook e use Testar Webhook para enviar uma entrega de exemplo e confirmar que seu endpoint recebe — e verifica — o payload.

O envelope

No caminho de evento, cada entrega é um único objeto JSON:
{
  "id": "evt_2KWPBgLlAfxdpx2AI54pPJ85f4W",
  "type": "ranking.weekly.published",
  "version": "1",
  "ts": "2026-06-03T12:00:00.000Z",
  "tenant": "9b1f0c2e-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
  "data": { /* específico do evento — veja o catálogo */ }
}
CampoDescrição
idId único do evento, estável entre retentativas → sua chave de idempotência.
typeTipo do evento (veja o catálogo).
versionVersão do schema de data. Evolui de forma aditiva.
tsQuando o evento ocorreu (ISO 8601, UTC).
tenantTenant de origem (uuid).
dataPayload específico do evento.

Headers

HeaderDescrição
X-SalesOS-EventTipo do evento — roteie sem ler o corpo.
X-SalesOS-Event-IdIgual ao id; estável entre retentativas (idempotência).
X-SalesOS-Delivery-IdId da tentativa (muda a cada retry; para debug).
X-SalesOS-TimestampUnix em segundos — parte da assinatura (anti-replay).
X-SalesOS-TenantId do tenant de origem.
X-SalesOS-Signaturesha256=<hmac> — veja Verificar.

Verificar a assinatura

Sempre verifique a assinatura antes de confiar numa entrega. Sem ela, qualquer um que descobrir sua URL poderia forjar eventos.
No caminho de evento, a assinatura cobre {id}.{timestamp}.{rawBody} (padrão Standard Webhooks), então autentica o payload e o timestamp (anti-replay). Recompute o HMAC com o seu segredo, compare em tempo constante e rejeite se o timestamp estiver mais de 300s fora.
import crypto from "node:crypto";

// raw = o corpo cru exato da requisição (NÃO faça JSON.parse antes)
export function verify(req, secret) {
  const id  = req.headers["x-salesos-event-id"];
  const ts  = req.headers["x-salesos-timestamp"];
  const sig = req.headers["x-salesos-signature"]; // "sha256=<hex>"
  const raw = req.rawBody;

  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // janela de replay

  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(`${id}.${ts}.${raw}`).digest("hex");

  const a = Buffer.from(sig), b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Entregas de teste (o botão “Testar Webhook”) e gatilhos legados de workflow usam hoje um esquema mais simples: o corpo é o template renderizado cru (não o envelope) e o header de assinatura é X-Signature: <prefixo><hmac(corpo)>sem o prefixo id.timestamp., então sem anti-replay. Prefira o caminho de evento acima em produção.

Catálogo de eventos

typeQuando disparadata
lead.offer_createdUm lead é ofertado a um corretorpayload do lead/oportunidade
lead.acceptedUm corretor aceita um leadpayload do lead/oportunidade
lead.offer_expiredUma oferta expira sem aceitepayload do lead/oportunidade
mission.completedUma missão é concluídapayload da missão
ranking.weekly.publishedRanking semanal publicado (agendado)leaderboard (linhas por email)
ranking.consolidated.publishedRanking consolidado publicadoleaderboard (linhas por email)
message.dispatchMensagem disparada a um usuário/grupo{ to, m }
notification.push.dispatchPush disparado a um usuário/grupo{ to, n }
Exemplo de data para ranking.weekly.published:
{
  "p": { "type": "weekly", "label": "Semana 23/2026", "start": "2026-05-25", "end": "2026-05-31" },
  "org_unit": null,
  "totals": { "users": 843, "points": 1820400, "missions": 5120 },
  "rows": [
    { "rank": 1, "delta": 2, "email": "maria@loja.com", "points": 4820, "missions": 37, "org_unit": "ou_12" }
  ]
}
Os destinatários são chaveados por email — nomes e ids não são enviados; enriqueça do seu lado se precisar.

Confiabilidade

Responda rápido. Retorne um status 2xx rapidamente (antes de processamento pesado) para confirmar o recebimento.
Sua respostaO que o SalesOS faz
2xxMarca a entrega como concluída. Sem retry.
4xx (400, 401, 422…)Tratado como rejeição permanente. Sem retry (exceto 429, que respeita o backoff).
5xx / timeout / erro de conexãoReentrega com backoff exponencial (2^n minutos, até 5 tentativas), depois dead-letter.
Idempotência. O mesmo evento pode ser entregue mais de uma vez (retentativas). Deduplique por X-SalesOS-Event-Id — ele permanece o mesmo entre retentativas.
Entregas falhas aparecem no painel Entregas (Dashboard → Integrações → Webhooks), onde você inspeciona status, tentativas e o último erro, e pode reenviar a partir da fila de dead-letter.