Webhooks
Suscríbete a eventos y recibe notificaciones en tiempo real en tu URL, con cada entrega firmada mediante HMAC.
https://app.syncro.chat/api/v1AuthX-API-Key: crm_SUA_CHAVE_AQUIListar suscripciones
/webhookswebhooks:readLista las suscripciones de webhook de la cuenta.
curl "https://app.syncro.chat/api/v1/webhooks" \ -H "X-API-Key: crm_SUA_CHAVE_AQUI"
{
"success": true,
"data": [
{
"id": 1,
"target_url": "https://seu-sistema.com/webhooks/syncro",
"events": [
"lead.created",
"lead.updated"
],
"source": "api",
"is_active": true,
"failure_count": 0,
"last_success_at": "2026-06-30T10:05:00Z",
"last_failure_at": null,
"created_at": "2026-06-15T14:30:00Z"
}
],
"supported_events": [
"lead.created",
"lead.updated",
"lead.stage_changed",
"..."
]
}Crear suscripción
/webhookswebhooks:writeCrea una suscripción apuntando a tu URL y elige qué eventos recibir.
Parámetros del body
target_urlstring (URL)obligatorioPOSTeventsarray(string)obligatoriosourcestringopcionalapi (predeterminado) o n8nsecretstringopcionalGuarda el secret: lo usarás para validar la firma de cada entrega.
curl -X POST "https://app.syncro.chat/api/v1/webhooks" \
-H "X-API-Key: crm_SUA_CHAVE_AQUI" \
-H "Content-Type: application/json" \
-d '{
"target_url": "https://seu-sistema.com/webhooks/syncro",
"events": [
"lead.created",
"lead.won",
"task.completed"
]
}'{
"success": true,
"id": 1,
"secret": "f3a9c2...e1",
"events": [
"lead.created",
"lead.won",
"task.completed"
]
}Detalle de una suscripción
/webhooks/1webhooks:readDevuelve los datos de una suscripción específica.
curl "https://app.syncro.chat/api/v1/webhooks/1" \ -H "X-API-Key: crm_SUA_CHAVE_AQUI"
{
"success": true,
"id": 1,
"target_url": "https://seu-sistema.com/webhooks/syncro",
"events": [
"lead.created",
"lead.updated"
],
"source": "api",
"is_active": true,
"failure_count": 0,
"last_success_at": "2026-06-30T10:05:00Z",
"last_failure_at": null,
"last_failure_reason": null
}Actualizar suscripción
/webhooks/1webhooks:writeActualiza la URL, los eventos o activa/desactiva. Reactivar (is_active: true) reinicia el contador de fallos.
Parámetros (body, todos opcionales)
target_urlstring (URL)opcionaleventsarray(string)opcionalis_activebooleanopcionalcurl -X PUT "https://app.syncro.chat/api/v1/webhooks/1" \
-H "X-API-Key: crm_SUA_CHAVE_AQUI" \
-H "Content-Type: application/json" \
-d '{
"events": [
"lead.created"
],
"is_active": true
}'{
"success": true,
"id": 1,
"events": [
"lead.created"
],
"is_active": true
}Eliminar suscripción
/webhooks/1webhooks:writeElimina una suscripción de webhook.
curl -X DELETE "https://app.syncro.chat/api/v1/webhooks/1" \ -H "X-API-Key: crm_SUA_CHAVE_AQUI"
{
"success": true
}Cómo funciona la entrega
Cuando ocurre un evento suscrito, Syncro hace un POST (JSON) a tu target_url.
Envelope
Cada evento llega en este formato:
{
"event": "lead.created",
"tenant_id": 1,
"emitted_at": "2026-06-30T10:00:00Z",
"data": {
"...": "payload específico do evento"
}
}
Headers de la entrega
| Header | Valor |
|---|---|
Content-Type |
application/json |
User-Agent |
Syncro-CRM-Webhook/1.0 |
X-Syncro-Event |
nombre del evento (ej.: lead.created) |
X-Syncro-Delivery |
ID único de la entrega |
X-Syncro-Signature |
sha256=<hmac> — firma HMAC del cuerpo |
Validando la firma (HMAC)
La firma es el HMAC-SHA256 del cuerpo bruto de la solicitud, usando el secret de la suscripción como clave. El header viene como sha256=<hash_hexadecimal>.
Calcula el HMAC sobre el cuerpo bruto recibido (no vuelvas a serializar el JSON) y compara con el valor tras sha256=.
Node.js (Express)
const crypto = require('crypto');
// use o corpo BRUTO: app.use(express.raw({ type: 'application/json' }))
function verify(req, secret) {
const header = req.get('X-Syncro-Signature') || '';
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(req.body) // Buffer com o corpo bruto
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
PHP
$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
$ok = hash_equals($expected, $_SERVER['HTTP_X_SYNCRO_SIGNATURE'] ?? '');
Responde con 2xx para confirmar la recepción. Cualquier otro estado se trata como un fallo.
Reentregas y retry
- Syncro intenta entregar hasta 4 veces, con espera creciente: 10s, 1min, 5min, 15min.
- Las respuestas 5xx, 408 y 429 se reenvían. Las respuestas 4xx (excepto 408/429) no se reenvían (error del receptor).
- Tras 10 fallos consecutivos, la suscripción se desactiva automáticamente (reactívala con
PUT /webhooks/{id}eis_active: true). - Haz tu endpoint idempotente (puede recibir la misma entrega más de una vez); usa el
X-Syncro-Deliverypara deduplicar.
Eventos disponibles
| Evento | Cuándo se dispara |
|---|---|
lead.created |
Lead creado |
lead.updated |
Lead actualizado |
lead.stage_changed |
Lead cambió de etapa |
lead.won |
Lead marcado como ganado |
lead.lost |
Lead marcado como perdido |
lead.deleted |
Lead eliminado |
lead.assignment_changed |
Responsable del lead cambió |
lead.merged |
Leads fusionados |
lead.tags_changed |
Tags del lead cambiaron |
sale.created |
Venta registrada |
note.added |
Nota añadida a un lead |
nps.answered |
Encuesta NPS respondida |
task.created |
Tarea creada |
task.updated |
Tarea actualizada |
task.completed |
Tarea completada |
conversation.message_received |
Reservado (aún no emitido) |
Ejemplos de payload
Ejemplos del campo data:
lead.created / lead.updated / lead.deleted
{
"id": 123,
"name": "João Silva",
"email": "[email protected]",
"phone": "+5511999887766",
"company": "ACME",
"value": 5000.0,
"source": "site",
"status": "open",
"pipeline_id": 1,
"pipeline_name": "Vendas",
"stage_id": 5,
"stage_name": "Proposta",
"assigned_to": 12,
"owner": { "id": 12, "name": "Ana", "email": "[email protected]" },
"tags": ["vip"],
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "verao_2026",
"utm_term": null,
"utm_content": null,
"fbclid": null,
"gclid": null,
"custom_fields": { "tamanho_empresa": "100+" },
"created_at": "2026-06-30T10:00:00Z",
"updated_at": "2026-06-30T10:00:00Z"
}
lead.stage_changed / lead.won / lead.lost — el mismo objeto de lead, más:
{
"previous_stage_id": 4,
"new_stage_id": 5,
"new_stage_name": "Proposta"
}
sale.created
{
"sale_id": 1,
"lead_id": 123,
"pipeline_id": 1,
"value": 5000.0,
"closed_by": 12,
"closed_at": "2026-06-30T10:05:00Z",
"lead": {
"id": 123,
"name": "João Silva",
"email": "[email protected]",
"phone": "+5511999887766",
"company": "ACME",
"pipeline_id": 1,
"stage_id": 10,
"owner": { "id": 12, "name": "Ana", "email": "[email protected]" }
}
}
task.created / task.updated / task.completed
{
"id": 45,
"subject": "Enviar proposta",
"type": "email",
"status": "completed",
"priority": "high",
"due_date": "2026-07-01",
"due_time": "09:00",
"completed_at": "2026-06-30T11:30:00Z",
"lead_id": 123,
"assigned_to": 12,
"created_at": "2026-06-30T08:00:00Z",
"updated_at": "2026-06-30T11:30:00Z"
}
note.added,nps.answered,lead.assignment_changed,lead.mergedylead.tags_changedsiguen el mismo envelope, con eldataespecífico del evento (incluyendo el lead de contexto cuando corresponda).