Receive real-time event notifications from Synaptiq via HTTP webhooks.
Webhooks allow your application to receive real-time notifications when events occur in Synaptiq. Instead of polling the API, Synaptiq sends an HTTP POST request to your configured endpoint whenever a subscribed event fires.
You can also manage webhooks via the API:
curl -X POST https://synaptiqintel.com/api/v1/webhooks \
-H "Authorization: Bearer sk_live_abc123def456ghi789jkl012mno345" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/synaptiq",
"events": ["lead.created", "lead.qualified", "conversation.completed"],
"secret": "whsec_your_signing_secret_here"
}'
{
"id": "wh_01HQ9AAA1BBB2CCC3DDD4EEE5F",
"url": "https://your-app.com/webhooks/synaptiq",
"events": ["lead.created", "lead.qualified", "conversation.completed"],
"status": "active",
"createdAt": "2026-04-05T16:00:00.000Z"
}
| Event | Description |
|-----------------------------|---------------------------------------------------------------|
| lead.created | A new lead was captured from a conversation or the API. |
| lead.updated | A lead's fields were modified (status, score, tags, etc.). |
| lead.qualified | A lead's status changed to qualified. |
| lead.converted | A lead's status changed to converted. |
| lead.deleted | A lead was deleted. |
| conversation.started | A new AI conversation was initiated. |
| conversation.completed | A conversation was closed or expired. |
| conversation.handoff | A conversation was handed off to a human agent. |
| message.received | A new visitor message was received in a conversation. |
| message.sent | The AI agent sent a response message. |
| intent.high_value | A high-value buying intent was detected in a conversation. |
Every webhook delivery follows a consistent envelope format:
{
"id": "evt_01HQAAA1BBB2CCC3DDD4EEE5FF",
"type": "lead.created",
"apiVersion": "2026-01-15",
"createdAt": "2026-04-05T16:05:00.000Z",
"data": {
// Event-specific payload (see examples below)
}
}
| Field | Type | Description |
|--------------|--------|------------------------------------------------------|
| id | string | Unique event ID. Use for idempotency checks. |
| type | string | The event type that triggered the webhook. |
| apiVersion | string | The API version used to format the payload. |
| createdAt | string | ISO 8601 timestamp of when the event occurred. |
| data | object | The event payload. Structure varies by event type. |
Each webhook request includes the following headers:
| Header | Description |
|-------------------------|----------------------------------------------------------------|
| Content-Type | Always application/json. |
| User-Agent | Synaptiq-Webhooks/1.0. |
| X-Synaptiq-Event | The event type (e.g., lead.created). |
| X-Synaptiq-Delivery | A unique delivery ID for this attempt. |
| X-Synaptiq-Timestamp | Unix timestamp (seconds) of when the request was sent. |
| X-Synaptiq-Signature | HMAC-SHA256 signature for verifying payload authenticity. |
Every webhook delivery is signed with your webhook secret using HMAC-SHA256. Always verify the signature before processing a webhook to ensure the request is genuinely from Synaptiq.
The signature is computed over the concatenation of the timestamp and the raw request body:
signature = HMAC-SHA256(secret, timestamp + "." + body)
const crypto = require("crypto");
function verifyWebhookSignature(req, secret) {
const timestamp = req.headers["x-synaptiq-timestamp"];
const signature = req.headers["x-synaptiq-signature"];
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 (Math.abs(currentTime - parseInt(timestamp)) > 300) {
throw new Error("Webhook timestamp too old. Possible replay attack.");
}
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");
const trusted = Buffer.from(expected, "hex");
const received = Buffer.from(signature, "hex");
if (!crypto.timingSafeEqual(trusted, received)) {
throw new Error("Invalid webhook signature.");
}
return true;
}
import hmac
import hashlib
import time
def verify_webhook(payload_body, timestamp, signature, secret):
# Reject old timestamps (5-minute tolerance)
current_time = int(time.time())
if abs(current_time - int(timestamp)) > 300:
raise ValueError("Webhook timestamp too old")
expected = hmac.new(
secret.encode("utf-8"),
f"{timestamp}.{payload_body}".encode("utf-8"),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
raise ValueError("Invalid webhook signature")
return True
{
"id": "evt_01HQAAA1BBB2CCC3DDD4EEE5FF",
"type": "lead.created",
"apiVersion": "2026-01-15",
"createdAt": "2026-04-05T16:05:00.000Z",
"data": {
"lead": {
"id": "lead_01HQ3VXK9BWMN4F6T2P8R5GYSC",
"email": "sarah.chen@acmecorp.com",
"name": "Sarah Chen",
"company": "Acme Corp",
"status": "new",
"score": 42,
"source": "chat",
"createdAt": "2026-04-05T16:05:00.000Z"
},
"conversationId": "conv_01HQ7ABC2DEF3GHI4JKL5MNO6P"
}
}
{
"id": "evt_01HQBBB2CCC3DDD4EEE5FFF6GG",
"type": "lead.qualified",
"apiVersion": "2026-01-15",
"createdAt": "2026-04-05T16:10:00.000Z",
"data": {
"lead": {
"id": "lead_01HQ3VXK9BWMN4F6T2P8R5GYSC",
"email": "sarah.chen@acmecorp.com",
"name": "Sarah Chen",
"company": "Acme Corp",
"status": "qualified",
"score": 87,
"previousStatus": "new"
},
"qualificationReason": "High intent signals: pricing inquiry, demo request, team size confirmed (50+ seats)."
}
}
{
"id": "evt_01HQCCC3DDD4EEE5FFF6GGG7HH",
"type": "conversation.handoff",
"apiVersion": "2026-01-15",
"createdAt": "2026-04-05T16:15:00.000Z",
"data": {
"conversation": {
"id": "conv_01HQ7ABC2DEF3GHI4JKL5MNO6P",
"leadId": "lead_01HQ3VXK9BWMN4F6T2P8R5GYSC",
"messageCount": 14,
"status": "handed_off"
},
"handoff": {
"reason": "enterprise_inquiry",
"priority": "high",
"suggestedTeam": "enterprise-sales",
"context": "Visitor is CTO of 50-person SaaS company comparing with Drift. Budget confirmed at $5-10k/mo."
}
}
}
{
"id": "evt_01HQDDD4EEE5FFF6GGG7HHH8II",
"type": "intent.high_value",
"apiVersion": "2026-01-15",
"createdAt": "2026-04-05T16:20:00.000Z",
"data": {
"leadId": "lead_01HQ6JKL8MWPN3D5R7S9T1UVWX",
"conversationId": "conv_01HQ7QRS8TUV9WXY0ZAB1CDE2F",
"intent": "purchase_ready",
"confidence": 0.92,
"signals": [
"Asked about contract terms",
"Confirmed budget authority",
"Requested implementation timeline"
],
"estimatedDealSize": 18000
}
}
If your endpoint returns a non-2xx status code or fails to respond within 30 seconds, Synaptiq retries the delivery with exponential backoff:
| Attempt | Delay After Failure | |---------|---------------------| | 1 | Immediate | | 2 | 1 minute | | 3 | 5 minutes | | 4 | 30 minutes | | 5 | 2 hours | | 6 | 12 hours | | 7 (final) | 24 hours |
After 7 failed attempts, the delivery is marked as failed. You can view failed deliveries and manually retry them from the Webhooks page in your dashboard.
200 OK as soon as you receive the webhook. Process the event asynchronously.id and skip duplicates. Retries may deliver the same event more than once.X-Synaptiq-Signature to prevent spoofed requests.Was this page helpful?