AllyOneCRM / Webhooks

Webhooks

Receba eventos do CRM na URL do seu sistema, assinados com HMAC-SHA256 — sem polling.

Cadastrar

POST/v1/webhooks
{
  "name": "ERP — produção",
  "url": "https://erp.suaempresa.com/webhooks/allyone",
  "events": ["contact.created", "deal.won", "form.submitted"]
}

Exige a permissão integrations:manage (owner e admin, por padrão). A URL precisa ser pública: endereços internos, de loopback e de metadados de nuvem são recusados. A resposta 201 traz o secret usado na assinatura — guarde-o no seu cofre de segredos.

Eventos

EventoQuando disparadata
contact.createdContato criado pela API ou pelo painel.id, email, first_name, tags
form.submittedFormulário do CRM enviado.form_id, form_name, contact_id, data
deal.stage_changedNegócio mudou de etapa.deal_id, title, stage_id
deal.wonNegócio marcado como ganho.deal_id, title, value
deal.lostNegócio marcado como perdido.deal_id, title
contact.updatedContato alterado pela API ou pelo painel.id, fields (campos alterados)
deal.createdNegócio criado.deal_id, title, value, stage_id, contact_id
survey.respondedPesquisa (NPS/CSAT) respondida.survey_id, contact_id, score
campaign.completedCampanha concluída (fim do envio, limite atingido, audiência vazia ou conclusão manual).campaign_id, name, sent, failed, reason
journey.enrolledContato inscrito numa jornada.journey_id, contact_id, enrollment_id
journey.completedContato chegou ao fim da jornada.journey_id, contact_id, enrollment_id
contact.unsubscribedDescadastro pelo link (por canal), "SAIR" no WhatsApp ou eliminação a pedido do titular (LGPD).contact_id, channel, reason
score.rule_firedRegra de pontuação aplicada a um contato.regra e contato

Payload

{
  "id": "5b0d2c8e-7f1a-4c3e-9a51-2e6c0b7d4f10",
  "event": "deal.won",
  "timestamp": "2026-10-01T14:03:11.204Z",
  "tenant_id": "4f1c...",
  "data": { "deal_id": "9a2e...", "title": "Plano anual — ACME", "value": 18000 }
}

O tipo do evento está no campo event do corpo. Cada entrega tem um id próprio (também no cabeçalho X-AllyOne-Delivery), igual em todas as tentativas — use-o para descartar repetições.

Validar a assinatura

Content-Type: application/json
X-AllyOne-Signature: sha256=<hex>
X-AllyOne-Event: deal.won
X-AllyOne-Delivery: 5b0d2c8e-7f1a-4c3e-9a51-2e6c0b7d4f10
User-Agent: AllyOne-CRM-Webhook/1.0

X-AllyOne-Signature é o HMAC-SHA256 do corpo bruto com o secret do webhook. Calcule sobre os bytes recebidos, antes de fazer parse do JSON, e compare em tempo constante:

import crypto from 'node:crypto'

// rawBody: o corpo EXATO recebido (Buffer/string), antes de qualquer JSON.parse
function isValid(rawBody, header, secret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  const a = Buffer.from(expected), b = Buffer.from(header ?? '')
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}
import hmac, hashlib

def is_valid(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header or "")

Entrega e falhas

  • Sucesso é qualquer resposta 2xx em até 10 segundos. Redirecionamentos não são seguidos.
  • Em falha, o CRM tenta de novo na hora: até 3 tentativas no total, com 1 s e 2 s de intervalo. Depois disso não há nova tentativa.
  • Cada entrega fica registrada em GET /v1/webhooks/:id/deliveries (as 50 mais recentes, com status HTTP, número de tentativas e sucesso) — use para conciliar o que falhou.
  • Responda 2xx rápido e processe numa fila do seu lado: um endpoint lento consome as 3 tentativas em segundos.

Gerenciar

RotaO que faz
GET /v1/webhooksLista os webhooks do tenant.
GET /v1/webhooks/eventsEventos aceitos na assinatura.
PATCH /v1/webhooks/:idAltera name, url, events ou active.
POST /v1/webhooks/:id/rotate-secretGera um secret novo (o antigo deixa de valer na hora).
GET /v1/webhooks/:id/deliveriesHistórico de entregas.
DELETE /v1/webhooks/:idRemove.
Webhooks — AllyOneCRM