Webhooks
SalesInt sends HTTP POST callbacks to your endpoint when events occur — lead captured, message received, post published, and more. Subscribe only to the events you actually handle.
Best Practices
- Subscribe only to the events you actually handle.
- Treat each delivery as an event notification, not a full sync.
- Use the webhook event ID as your deduplication key.
- Verify the
X-SalesInt-Signature header when a webhook secret is configured. - Acknowledge fast — return a 2xx immediately, then process async.
Delivery Flow
- Create a webhook endpoint in Dashboard → Webhooks.
- Choose the events you want to subscribe to.
- Receive a POST request from SalesInt whenever one of those events occurs.
- Return a
2xx response after accepting the payload. - Use Test Webhook and Webhook Logs to validate your integration.
Delivery Retries
A delivery is successful when your endpoint returns a 2xx within 5 seconds. Any other outcome triggers a retry on an exponential backoff schedule capped at 24 hours. Up to 7 attempts are made per event.
| Attempt | Delay Before | Cumulative Time |
|---|
| 1 | Immediate | 0 |
| 2 | 10s | ~10s |
| 3 | 1m 40s | ~1m 50s |
| 4 | 16m 40s | ~18m 30s |
| 5 | 2h 46m | ~3h 5m |
| 6 | 24h (capped) | ~27h 5m |
| 7 | 24h (capped) | ~51h 5m |
⚠️After the 7th attempt fails, the event is moved to a dead-letter queue and no longer retried automatically. Keep your handler fast — acknowledge immediately, process in background.
Idempotency
Webhook deliveries use at-least-once semantics — the same event may arrive more than once. Your handler must be idempotent. Use the payload.id field (also sent as X-SalesInt-Event-Id header) as your deduplication key.
Signature Verification
When a webhook secret is configured, every delivery includes an X-SalesInt-Signature header — the lowercase hex HMAC-SHA256 of the raw request body keyed by your secret.
javascript
import crypto from 'crypto';
export const POST = async (req) => {
const sig = req.headers.get('X-SalesInt-Signature');
if (!sig) return new Response('No signature', { status: 401 });
const secret = process.env.SALESINT_WEBHOOK_SECRET;
const rawBody = await req.text();
const computed = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
if (sig !== computed) {
return new Response('Invalid signature', { status: 400 });
}
const payload = JSON.parse(rawBody);
// Handle the event
switch (payload.event) {
case 'lead.captured':
await handleNewLead(payload.lead);
break;
case 'message.received':
await handleInboundMessage(payload.message);
break;
case 'post.published':
await handlePostPublished(payload.post);
break;
}
return new Response('OK', { status: 200 });
};
Available Events
| Event | Description |
|---|
| lead.captured | Fired when a new lead is captured by the AI chatbot on any channel. |
| lead.qualified | Fired when a lead crosses your intent score threshold. |
| conversation.started | Fired once when a new conversation begins between your account and a contact. |
| message.received | Fired when a new inbound message is received across any channel. |
| message.sent | Fired when an outgoing message is sent from the inbox. |
| message.delivered | Fired when an outgoing message is delivered (WhatsApp, Messenger). |
| message.read | Fired when an outgoing message is read (WhatsApp, Messenger, Instagram). |
| message.failed | Fired when an outgoing message fails to deliver (WhatsApp only). |
| comment.received | Fired when a new comment is received on a tracked post. |
| post.published | Fired when a scheduled post is successfully published. |
| post.failed | Fired when a post fails to publish on all target platforms. |
| post.partial | Fired when a post publishes on some platforms and fails on others. |
| post.scheduled | Fired when a post is scheduled for future publishing. |
| account.connected | Fired when a social account is successfully connected. |
| account.disconnected | Fired when a connected social account becomes disconnected. |
| broadcast.sent | Fired when a broadcast is sent to all recipients. |
| campaign.sent | Fired when an email campaign delivery completes. |
| review.new | Fired when a new review is posted on a connected Google Business account. |
| review.updated | Fired when a review is edited or a reply is added. |
| webhook.test | Fired when sending a test webhook to verify endpoint configuration. |
Sample Payload — lead.captured
json
{
"id": "evt_01j9xyz...",
"event": "lead.captured",
"timestamp": "2026-06-05T10:22:00Z",
"lead": {
"id": "lead_abc123",
"name": "Jane Smith",
"email": "jane@company.com",
"phone": "+1-555-0100",
"channel": "website",
"websiteId": "web_xyz",
"intentScore": 82,
"stage": "hot"
}
}
Sample Payload — message.received
json
{
"id": "evt_02k9abc...",
"event": "message.received",
"timestamp": "2026-06-05T10:23:15Z",
"message": {
"id": "msg_def456",
"text": "Hi, I'd like to know more about your pricing",
"platform": "whatsapp",
"from": "+1-555-0200"
},
"conversation": {
"id": "conv_ghi789",
"contactId": "contact_jkl012"
},
"account": {
"id": "acc_mno345",
"platform": "whatsapp"
}
}