Skip to main content

O que são webhooks?

Webhooks permitem que a SocialSell envie notificações automáticas para o seu sistema quando eventos ocorrem — como um novo contato criado, um negócio ganho ou uma mensagem recebida. Em vez de fazer polling constante na API, você recebe os dados imediatamente.

Eventos disponíveis

EventoDescrição
contact.createdUm novo contato foi criado
contact.updatedUm contato foi atualizado
deal.createdUm novo negócio foi criado
deal.stage.changedUm negócio mudou de etapa no funil de venda
deal.wonUm negócio foi marcado como ganho
deal.lostUm negócio foi marcado como perdido
message.receivedUma nova mensagem foi recebida de um contato
Mais eventos serão adicionados em versões futuras. Acompanhe o roadmap em socialsell.canny.io.

Criando uma assinatura

Requer o escopo write:webhooks e plano Growth ou superior.
curl -X POST https://api.socialsell.ai/v1/webhooks \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Notificações ERP",
    "url": "https://meusistema.com/webhook/socialsell",
    "events": ["contact.created", "deal.won", "deal.stage.changed"]
  }'
A resposta inclui o campo secret — armazene-o com segurança para validar as entregas:
{
  "data": {
    "id": "664f1a2b3c4d5e6f78901234",
    "name": "Notificações ERP",
    "url": "https://meusistema.com/webhook/socialsell",
    "events": ["contact.created", "deal.won", "deal.stage.changed"],
    "status": "active",
    "secret": "whsec_a1b2c3d4e5f6...",
    "created_at": "2026-06-10T15:00:00.000Z"
  }
}
O campo secret é retornado apenas na criação. Não é possível recuperá-lo depois. Armazene-o como variável de ambiente.

Formato do payload

Cada evento entregue tem o seguinte formato:
{
  "id": "evt_01jx8kz3m4n5p6q7r8s9t0u1v",
  "event": "deal.won",
  "organization_id": "664a1b2c3d4e5f6789012345",
  "created_at": "2026-06-10T15:30:00.000Z",
  "data": {
    "id": "664f1a2b3c4d5e6f78901234",
    "title": "Proposta Empresa XYZ",
    "value": 5000,
    "status": "won",
    "pipeline": { "id": "...", "name": "Funil Principal" },
    "stage": { "name": "Fechamento" },
    "assigned_to": { "id": "...", "name": "João Silva" }
  }
}

Validando a assinatura

Cada requisição inclui o header X-SocialSell-Signature com um HMAC-SHA256 do corpo usando seu secret:
X-SocialSell-Signature: sha256=a1b2c3d4e5f6...
Sempre valide a assinatura antes de processar o payload:
const crypto = require('crypto');

function validateWebhookSignature(body, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(typeof body === 'string' ? body : JSON.stringify(body))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// No seu handler Express:
app.post('/webhook/socialsell', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-socialsell-signature'];
  const secret = process.env.SOCIALSELL_WEBHOOK_SECRET;
  
  if (!validateWebhookSignature(req.body, signature, secret)) {
    return res.status(401).send('Assinatura inválida');
  }
  
  const event = JSON.parse(req.body);
  
  switch (event.event) {
    case 'deal.won':
      // processar negócio ganho
      break;
    case 'contact.created':
      // processar novo contato
      break;
  }
  
  res.status(200).send('OK');
});

Política de retentativas

A SocialSell tenta entregar cada evento com as seguintes regras:
  • Timeout: 10 segundos para o endpoint responder
  • Sucesso: resposta HTTP 2xx
  • Falha: qualquer outro status ou timeout
  • Retentativas: após falha, tenta novamente com backoff exponencial
  • Desativação automática: após múltiplas falhas consecutivas, a assinatura é pausada automaticamente (campo consecutive_failures)
Responda com 200 OK o mais rápido possível e processe o evento de forma assíncrona (ex: em uma fila). Isso evita timeouts causados por processamento lento.

Gerenciamento de assinaturas

Idempotência

Eventos podem ser entregues mais de uma vez em casos de falha. Use o campo id do evento para garantir idempotência no processamento:
// Armazene IDs já processados para evitar duplicatas
const processedEvents = new Set();

function handleWebhook(event) {
  if (processedEvents.has(event.id)) return; // já processado
  processedEvents.add(event.id);
  
  // processar...
}