Webhooks
Subscribe to events and receive real-time notifications at your URL, with every delivery signed via HMAC.
https://app.syncro.chat/api/v1AuthX-API-Key: crm_SUA_CHAVE_AQUIList subscriptions
/webhookswebhooks:readLists the account webhook subscriptions.
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",
"..."
]
}Create subscription
/webhookswebhooks:writeCreates a subscription pointing to your URL and chooses which events to receive.
Body parameters
target_urlstring (URL)requiredPOST requestseventsarray(string)requiredsourcestringoptionalapi (default) or n8nsecretstringoptionalSave the secret: you will use it to validate the signature of each delivery.
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"
]
}Subscription detail
/webhooks/1webhooks:readReturns the data of a specific subscription.
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
}Update subscription
/webhooks/1webhooks:writeUpdate the URL, the events or activate/deactivate. Reactivating (is_active: true) resets the failure counter.
Parameters (body, all optional)
target_urlstring (URL)optionaleventsarray(string)optionalis_activebooleanoptionalcurl -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
}Delete subscription
/webhooks/1webhooks:writeRemoves a webhook subscription.
curl -X DELETE "https://app.syncro.chat/api/v1/webhooks/1" \ -H "X-API-Key: crm_SUA_CHAVE_AQUI"
{
"success": true
}How delivery works
When a subscribed event occurs, Syncro makes a POST (JSON) to your target_url.
Envelope
Every event arrives in this format:
{
"event": "lead.created",
"tenant_id": 1,
"emitted_at": "2026-06-30T10:00:00Z",
"data": {
"...": "payload específico do evento"
}
}
Delivery headers
| Header | Value |
|---|---|
Content-Type |
application/json |
User-Agent |
Syncro-CRM-Webhook/1.0 |
X-Syncro-Event |
event name (e.g.: lead.created) |
X-Syncro-Delivery |
unique delivery ID |
X-Syncro-Signature |
sha256=<hmac> — HMAC signature of the body |
Validating the signature (HMAC)
The signature is the HMAC-SHA256 of the raw body of the request, using the subscription secret as the key. The header comes as sha256=<hexadecimal_hash>.
Compute the HMAC over the raw body received (do not re-serialize the JSON) and compare with the value after 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'] ?? '');
Respond with 2xx to confirm receipt. Any other status is treated as a failure.
Redeliveries and retry
- Syncro tries to deliver up to 4 times, with increasing wait: 10s, 1min, 5min, 15min.
- Responses 5xx, 408 and 429 are resent. Responses 4xx (except 408/429) are not resent (receiver error).
- After 10 consecutive failures, the subscription is automatically deactivated (reactivate with
PUT /webhooks/{id}andis_active: true). - Make your endpoint idempotent (it may receive the same delivery more than once); use the
X-Syncro-Deliveryto deduplicate.
Available events
| Event | When it fires |
|---|---|
lead.created |
Lead created |
lead.updated |
Lead updated |
lead.stage_changed |
Lead changed stage |
lead.won |
Lead marked as won |
lead.lost |
Lead marked as lost |
lead.deleted |
Lead deleted |
lead.assignment_changed |
Lead owner changed |
lead.merged |
Leads merged |
lead.tags_changed |
Lead tags changed |
sale.created |
Sale registered |
note.added |
Note added to a lead |
nps.answered |
NPS survey answered |
task.created |
Task created |
task.updated |
Task updated |
task.completed |
Task completed |
conversation.message_received |
Reserved (not yet emitted) |
Payload examples
Examples of the data field:
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 — the same lead object, plus:
{
"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.mergedandlead.tags_changedfollow the same envelope, with the event-specificdata(including the context lead when applicable).