Webhooks
Assine eventos e receba notificações em tempo real na sua URL, com cada entrega assinada via HMAC.
https://app.syncro.chat/api/v1AuthX-API-Key: crm_SUA_CHAVE_AQUIListar assinaturas
/webhookswebhooks:readLista as assinaturas de webhook da conta.
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",
"..."
]
}Criar assinatura
/webhookswebhooks:writeCria uma assinatura apontando para a sua URL e escolhe quais eventos receber.
Parâmetros do body
target_urlstring (URL)obrigatórioPOSTeventsarray(string)obrigatóriosourcestringopcionalapi (padrão) ou n8nsecretstringopcionalGuarde o secret: você vai usá-lo para validar a assinatura 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"
]
}Detalhe de uma assinatura
/webhooks/1webhooks:readRetorna os dados de uma assinatura 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
}Atualizar assinatura
/webhooks/1webhooks:writeAtualize a URL, os eventos ou ative/desative. Reativar (is_active: true) zera o contador de falhas.
Parâmetros (body, todos opcionais)
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
}Remover assinatura
/webhooks/1webhooks:writeRemove uma assinatura de webhook.
curl -X DELETE "https://app.syncro.chat/api/v1/webhooks/1" \ -H "X-API-Key: crm_SUA_CHAVE_AQUI"
{
"success": true
}Como a entrega funciona
Quando um evento assinado ocorre, o Syncro faz um POST (JSON) na sua target_url.
Envelope
Todo evento chega neste formato:
{
"event": "lead.created",
"tenant_id": 1,
"emitted_at": "2026-06-30T10:00:00Z",
"data": {
"...": "payload específico do evento"
}
}
Headers da entrega
| Header | Valor |
|---|---|
Content-Type |
application/json |
User-Agent |
Syncro-CRM-Webhook/1.0 |
X-Syncro-Event |
nome do evento (ex.: lead.created) |
X-Syncro-Delivery |
ID único da entrega |
X-Syncro-Signature |
sha256=<hmac> — assinatura HMAC do corpo |
Validando a assinatura (HMAC)
A assinatura é o HMAC-SHA256 do corpo bruto da requisição, usando o secret da assinatura como chave. O header vem como sha256=<hash_hexadecimal>.
Calcule o HMAC sobre o corpo bruto recebido (não re-serialize o JSON) e compare com o valor após 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'] ?? '');
Responda com 2xx para confirmar o recebimento. Qualquer outro status é tratado como falha.
Reentregas e retry
- O Syncro tenta entregar até 4 vezes, com espera crescente: 10s, 1min, 5min, 15min.
- Respostas 5xx, 408 e 429 são reenviadas. Respostas 4xx (exceto 408/429) não são reenviadas (erro do receptor).
- Após 10 falhas consecutivas, a assinatura é desativada automaticamente (reative com
PUT /webhooks/{id}eis_active: true). - Faça o seu endpoint idempotente (pode receber a mesma entrega mais de uma vez); use o
X-Syncro-Deliverypara deduplicar.
Eventos disponíveis
| Evento | Quando dispara |
|---|---|
lead.created |
Lead criado |
lead.updated |
Lead atualizado |
lead.stage_changed |
Lead mudou de etapa |
lead.won |
Lead marcado como ganho |
lead.lost |
Lead marcado como perdido |
lead.deleted |
Lead excluído |
lead.assignment_changed |
Responsável do lead mudou |
lead.merged |
Leads mesclados |
lead.tags_changed |
Tags do lead mudaram |
sale.created |
Venda registrada |
note.added |
Nota adicionada a um lead |
nps.answered |
Pesquisa NPS respondida |
task.created |
Tarefa criada |
task.updated |
Tarefa atualizada |
task.completed |
Tarefa concluída |
conversation.message_received |
Reservado (ainda não emitido) |
Exemplos de payload
Exemplos do 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 — o mesmo objeto de lead, mais:
{
"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.mergedelead.tags_changedseguem o mesmo envelope, com odataespecífico do evento (incluindo o lead de contexto quando aplicável).