Webhooks let you receive real-time notifications when events happen in your chat widget. When a visitor starts a chat, submits a form, or sends a message, Chatim sends an HTTP POST request to your URL with the event data.
With webhooks you can:
After creation, you'll see a Webhook Secret — copy and save it immediately. This secret is used to verify that incoming requests are from Chatim. It is only shown once.
| Event | Triggered When |
|---|---|
chat.started | A visitor opens the chat widget and begins a new session |
chat.message.received | A visitor sends a text message |
chat.form.submitted | A visitor submits a form in the chatbot flow |
chat.closed | A chat session is closed |
chat.assigned | A support agent joins the conversation |
chat.handoff | The chatbot transitions from bot mode to live chat |
Every webhook delivery is an HTTP POST request with a JSON body.
{
"event": "chat.started",
"eventId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2025-01-15T10:30:00.000Z",
"projectUuid": "your-project-uuid",
"chatUuid": "visitor-chat-uuid",
"data": { }
}Includes visitor info, page URL, location, and user agent.
{
"event": "chat.started",
"data": {
"visitor": {
"uuid": "visitor-uuid",
"name": "John Doe",
"email": "[email protected]",
"phone": "+1234567890"
},
"page": {
"url": "https://example.com/pricing",
"title": "Pricing - Example"
},
"location": { "country": "US", "city": "Los Angeles" },
"userAgent": { "browser": "Chrome", "os": "Windows", "device": "Desktop" }
}
}{
"event": "chat.message.received",
"data": {
"messageUuid": "message-uuid",
"message": "Hi, I have a question about your pricing.",
"visitor": { "uuid": "visitor-uuid", "name": "John Doe" }
}
}{
"event": "chat.form.submitted",
"data": {
"formNodeId": "node-id",
"formName": "Contact Form",
"fields": {
"name": "John Doe",
"email": "[email protected]",
"phone": "+1234567890",
"message": "I'd like a product demo."
},
"visitor": { "uuid": "visitor-uuid", "name": "John Doe" }
}
}Includes who closed the chat (agent, visitor, system, or timeout), duration in seconds, message count, and transcript.
{
"event": "chat.closed",
"data": {
"closedBy": "agent",
"status": "closed",
"duration": 345,
"messageCount": 12,
"visitor": { "uuid": "visitor-uuid", "name": "John Doe" }
}
}Sent when a support agent joins the conversation.
{
"event": "chat.assigned",
"data": {
"assignedTo": { "uuid": "agent-uuid", "name": "Jane Smith" },
"visitor": { "uuid": "visitor-uuid", "name": "John Doe" }
}
}Sent when the chatbot transitions to live chat mode.
{
"event": "chat.handoff",
"data": {
"reason": "Visitor requested human support",
"fromNode": "node-id",
"visitor": { "uuid": "visitor-uuid", "name": "John Doe" }
}
}Every webhook request includes a signature so you can verify it came from Chatim. The signature is computed as HMAC-SHA256(secret, timestamp + "." + payload) and sent in the X-Chatim-Signature header.
| Header | Description |
|---|---|
X-Chatim-Signature | HMAC-SHA256 signature of the payload |
X-Chatim-Timestamp | Unix timestamp of when the request was sent |
X-Chatim-Event | The event type |
X-Chatim-Delivery-Id | Unique delivery identifier |
const crypto = require('crypto');
function verifyWebhook(req, secret) {
const signature = req.headers['x-chatim-signature'];
const timestamp = req.headers['x-chatim-timestamp'];
const body = JSON.stringify(req.body);
// Reject requests older than 5 minutes
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
if (age > 300) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}import hmac, hashlib, time
def verify_webhook(headers, body, secret):
signature = headers.get('X-Chatim-Signature', '')
timestamp = headers.get('X-Chatim-Timestamp', '')
if abs(time.time() - int(timestamp)) > 300:
return False
expected = 'sha256=' + hmac.new(
secret.encode(),
f'{timestamp}.{body}'.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)If your endpoint is unavailable or returns an error, Chatim automatically retries.
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 24 hours |
After 5 failed retries, the delivery is marked as failed. 2xx responses are treated as success, 4xx as permanent failures (no retry), 5xx and timeouts (30s) trigger retries.
If a webhook accumulates 10 consecutive failures, it is automatically disabled. You can re-enable it from the dashboard after fixing the issue.
Click Test on any webhook to send a sample chat.started event to your URL. This lets you verify your endpoint before going live.
Click on a webhook to view recent deliveries with event type, timestamp, HTTP status code, and error messages. History is retained for 30 days.
If your webhook secret is compromised, click Regenerate Secret, save the new secret, and update your endpoint. The old secret is immediately invalidated.
Subscribe to chat.form.submitted and chat.started to automatically create contacts and leads in your CRM.
Subscribe to chat.assigned and chat.handoff to alert your team when a visitor needs live support.
Subscribe to all events to build a complete picture of chat engagement, form conversion rates, and support response times.
Subscribe to chat.form.submitted to trigger welcome emails, follow-up sequences, or confirmation messages.
eventId to handle duplicate deliveriestimestamp + "." + raw_body (not parsed JSON)Get started