AllyOneCRM / Webhooks

Webhooks

Recibe eventos del CRM en la URL de tu sistema, firmados con HMAC-SHA256 — sin polling.

Registrar

POST/v1/webhooks
{
  "name": "ERP — producción",
  "url": "https://erp.tuempresa.com/webhooks/allyone",
  "events": ["contact.created", "deal.won", "form.submitted"]
}

Exige el permiso integrations:manage (owner y admin, por defecto). La URL debe ser pública: se rechazan las direcciones internas, de loopback y de metadatos de nube. La respuesta 201 trae el secret usado en la firma — guárdalo en tu gestor de secretos.

Eventos

EventoCuándo se disparadata
contact.createdContacto creado por la API o el panel.id, email, first_name, tags
form.submittedSe envió un formulario del CRM.form_id, form_name, contact_id, data
deal.stage_changedUn negocio cambió de etapa.deal_id, title, stage_id
deal.wonUn negocio se marcó como ganado.deal_id, title, value
deal.lostUn negocio se marcó como perdido.deal_id, title
contact.updatedContacto modificado por la API o el panel.id, fields (campos modificados)
deal.createdSe creó un negocio.deal_id, title, value, stage_id, contact_id
survey.respondedSe respondió una encuesta (NPS/CSAT).survey_id, contact_id, score
campaign.completedCampaña concluida (fin del envío, límite alcanzado, audiencia vacía o conclusión manual).campaign_id, name, sent, failed, reason
journey.enrolledContacto inscrito en un journey.journey_id, contact_id, enrollment_id
journey.completedEl contacto llegó al final del journey.journey_id, contact_id, enrollment_id
contact.unsubscribedBaja por el enlace (por canal), "SAIR" en WhatsApp o eliminación a pedido del titular (LGPD).contact_id, channel, reason
score.rule_firedSe aplicó una regla de puntuación a un contacto.regla y contacto

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": "Plan anual — ACME", "value": 18000 }
}

El tipo de evento está en el campo event del cuerpo. Cada entrega tiene su propio id (también en el header X-AllyOne-Delivery), igual en todos los intentos — úsalo para descartar repeticiones.

Validar la firma

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 es el HMAC-SHA256 del cuerpo bruto con el secret del webhook. Calcúlalo sobre los bytes recibidos, antes de parsear el JSON, y compara en tiempo constante:

import crypto from 'node:crypto'

// rawBody: el cuerpo EXACTO recibido (Buffer/string), antes de cualquier 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 y fallos

  • Éxito es cualquier respuesta 2xx en hasta 10 segundos. No se siguen redirecciones.
  • Ante un fallo, el CRM reintenta enseguida: hasta 3 intentos en total, con 1 s y 2 s de intervalo. Después no hay nuevos reintentos.
  • Cada entrega queda registrada en GET /v1/webhooks/:id/deliveries (las 50 más recientes, con estado HTTP, número de intentos y éxito) — úsalo para conciliar lo que falló.
  • Responde 2xx rápido y procesa en una cola de tu lado: un endpoint lento consume los 3 intentos en segundos.

Gestionar

RutaQué hace
GET /v1/webhooksLista los webhooks del tenant.
GET /v1/webhooks/eventsEventos aceptados en la suscripción.
PATCH /v1/webhooks/:idCambia name, url, events o active.
POST /v1/webhooks/:id/rotate-secretGenera un secret nuevo (el anterior deja de valer al instante).
GET /v1/webhooks/:id/deliveriesHistorial de entregas.
DELETE /v1/webhooks/:idElimina.
Webhooks — AllyOneCRM