Send real-time event data from Synaptiq to any endpoint with signed webhook payloads.
Webhooks let Synaptiq push event data to your own systems in real time. When something happens — a lead is created, a conversation ends, a meeting is booked — Synaptiq sends an HTTP POST request to a URL you specify with a JSON payload describing the event.
This is the most flexible integration method. If you can receive an HTTP request, you can integrate with Synaptiq.
lead.qualified)2xx status code| Event | Triggered When |
|-------|---------------|
| lead.created | A new lead is identified (email captured for the first time) |
| lead.qualified | A lead meets your qualification criteria during conversation |
| lead.updated | A lead's information or status changes |
| conversation.started | A new conversation session begins |
| conversation.completed | A conversation ends (timeout, lead leaves, or AI concludes) |
| conversation.handoff | The AI transfers the conversation to a human agent |
| meeting.booked | A meeting is successfully booked through the calendar integration |
| meeting.cancelled | A previously booked meeting is cancelled |
| meeting.rescheduled | A meeting time is changed |
Go to /admin/settings > Integrations > Webhooks and click Add Webhook.
Fill in:
https://api.yourcompany.com/synaptiq/events)You can create multiple webhooks pointing to different URLs for different events.
After saving, Synaptiq generates a webhook signing secret — a random string used to verify that incoming requests genuinely came from Synaptiq. Copy it and store it securely (environment variable, secrets manager, etc.).
whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
You'll need this to verify webhook signatures (covered below).
Every webhook request is an HTTP POST with a JSON body. The structure is consistent across all events:
{
"id": "evt_8f3a2b1c4d5e6f7a",
"type": "lead.qualified",
"created_at": "2026-04-05T14:32:18.000Z",
"data": {
"lead_id": "lead_9k2m4n6p8q0r",
"email": "alex.chen@acmecorp.com",
"name": "Alex Chen",
"company": "Acme Corp",
"phone": "+1-555-0142",
"lead_score": 82,
"qualification_status": "qualified",
"tags": ["enterprise", "high-intent"],
"custom_fields": {
"industry": "SaaS",
"company_size": "200-500",
"use_case": "sales automation"
},
"conversation_id": "conv_3j5l7n9p1q3s",
"agent_id": "agent_default"
},
"metadata": {
"workspace_id": "ws_abc123",
"environment": "production",
"api_version": "2026-01-15"
}
}
meeting.booked includes additional scheduling data:
{
"id": "evt_7e2d1a3c5b4f6g8h",
"type": "meeting.booked",
"created_at": "2026-04-05T14:35:22.000Z",
"data": {
"lead_id": "lead_9k2m4n6p8q0r",
"email": "alex.chen@acmecorp.com",
"name": "Alex Chen",
"meeting": {
"meeting_id": "mtg_4r6t8v0x2z4b",
"title": "Sales Demo — Acme Corp",
"start_time": "2026-04-08T15:00:00.000Z",
"end_time": "2026-04-08T15:30:00.000Z",
"timezone": "America/New_York",
"calendar_provider": "cal.com",
"calendar_event_id": "cal_evt_abc123",
"assigned_rep": "sarah.jones@yourcompany.com"
},
"conversation_id": "conv_3j5l7n9p1q3s"
}
}
conversation.completed includes the full transcript:
{
"id": "evt_2a4c6e8g0i2k",
"type": "conversation.completed",
"created_at": "2026-04-05T14:40:05.000Z",
"data": {
"conversation_id": "conv_3j5l7n9p1q3s",
"lead_id": "lead_9k2m4n6p8q0r",
"duration_seconds": 247,
"message_count": 14,
"outcome": "meeting_booked",
"transcript": [
{
"role": "assistant",
"content": "Hi! Welcome to Acme. What brings you here today?",
"timestamp": "2026-04-05T14:35:58.000Z"
},
{
"role": "user",
"content": "I'm looking for a sales automation tool for my team.",
"timestamp": "2026-04-05T14:36:12.000Z"
}
]
}
}
Always verify that incoming webhooks are actually from Synaptiq. Every request includes a signature header that you check against your signing secret.
Synaptiq includes these headers on every webhook request:
X-Synaptiq-Signature: sha256=a1b2c3d4e5f6...
X-Synaptiq-Timestamp: 1712327538
X-Synaptiq-Event: lead.qualified
X-Synaptiq-Delivery-Id: evt_8f3a2b1c4d5e6f7a
The signature is an HMAC-SHA256 hash of the timestamp concatenated with the raw request body, using your signing secret as the key.
Node.js example:
const crypto = require("crypto");
function verifyWebhookSignature(req, signingSecret) {
const signature = req.headers["x-synaptiq-signature"];
const timestamp = req.headers["x-synaptiq-timestamp"];
const body = req.rawBody; // raw request body as string
// Reject requests older than 5 minutes to prevent replay attacks
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - parseInt(timestamp) > 300) {
throw new Error("Webhook timestamp too old");
}
// Compute expected signature
const payload = `${timestamp}.${body}`;
const expected = crypto
.createHmac("sha256", signingSecret)
.update(payload)
.digest("hex");
const expectedHeader = `sha256=${expected}`;
// Constant-time comparison to prevent timing attacks
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedHeader)
)) {
throw new Error("Invalid webhook signature");
}
return true;
}
The same approach works in any language: concatenate the timestamp and body with a . separator, compute the HMAC-SHA256 using your signing secret, and compare the result to the signature header using a constant-time comparison function.
If your endpoint doesn't return a 2xx response, Synaptiq retries with exponential backoff:
| Attempt | Delay After Failure | |---------|-------------------| | 1st retry | 30 seconds | | 2nd retry | 2 minutes | | 3rd retry | 10 minutes | | 4th retry | 1 hour | | 5th retry | 6 hours |
After 5 failed retries, the webhook delivery is marked as failed and no further attempts are made for that event. Failed deliveries are visible in the webhook logs at /admin/settings > Webhooks > Delivery Log.
Your endpoint must respond within 10 seconds. If it takes longer, Synaptiq treats it as a failure and queues a retry. For long-running processing, accept the webhook immediately (return 200), then process the payload asynchronously.
Retries mean your endpoint may receive the same event more than once. Use the id field in the payload (e.g., evt_8f3a2b1c4d5e6f7a) to deduplicate. Store processed event IDs and skip any you've already handled.
/admin/settings > WebhooksDuring development, your local server probably isn't publicly accessible. Use webhook.site for a quick temporary URL that logs requests, or ngrok (ngrok http 3000) to tunnel your local server to a public HTTPS URL.
Every webhook delivery is logged at /admin/settings > Webhooks > Delivery Log with the event type, timestamp, response status code, response body (first 1KB), request duration, and retry count.
200 OK immediately and process the payload in a background jobid to deduplicate, since retries can deliver the same event twiceWas this page helpful?