Webhooks
Add an endpoint
In the dashboard, open an agent's Settings → Webhooks, enter your URL and pick the events. Use an https:// address that's reachable from the internet (http://localhost works while you develop). Copy the endpoint's signing secret to your server, and press Test to send a sample.
Using Zapier, Make or n8n? Point the webhook at their "catch hook" URL, or use the Zapier card on the Deploy page.
Events
| Event | Fires when | payload |
|---|---|---|
| leads.submit | A visitor leaves contact details (Collect Leads), in the form or by telling the agent. A second submission in the same chat fires again only when it adds a new email or phone. | _id, name, email, phone, conversationId, source, createdAt, customFields |
| data.submit | The agent finishes collecting the details you asked for (Collect Data). | _id, values, fields (key → label), actionName, conversationId, source, createdAt |
| message.feedback | A visitor or teammate gives an answer a thumbs up or down. | messageId, conversationId, feedback (up / down), source, visitorId, userId |
| conversation.completed | Once per conversation, after the agent's first reply: a new conversation is under way. | conversationId, source, model, visitorId, userId |
source is the channel: widget, help, email, whatsapp, api…
The request
Every event is a POST with a JSON body: event, payload, agentId, sentAt (milliseconds) and deliveryId. Test deliveries also have "test": true.
POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-IntelliDesk-Event: leads.submit
X-IntelliDesk-Delivery: delivery_mujd3k2a9f…
X-IntelliDesk-Signature: sha256=5c5effd2b8fd5fc3e468019749e10198729fbb184a8394e45abea5b6f0609524
{
"event": "leads.submit",
"payload": {
"_id": "leads_…",
"agentId": "agents_…",
"conversationId": "conversations_…",
"name": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+44 20 7946 0000",
"source": "widget",
"createdAt": 1790470400742,
"customFields": { "company": "Analytical Engines Ltd" }
},
"agentId": "agents_…",
"sentAt": 1790470400817,
"deliveryId": "delivery_mujd3k2a9f…"
}| Header | What it is |
|---|---|
| X-IntelliDesk-Event | The event name, same as in the body. |
| X-IntelliDesk-Signature | sha256= + the hex HMAC-SHA256 of the raw body, keyed with the endpoint's signing secret. |
| X-IntelliDesk-Delivery | The delivery's id. Retries of the same event keep it, so you can skip repeats. |
| X-IntelliDesk-Attempt | 2, 3 or 4 on retries. Absent on the first try. |
Verify the signature
Anyone can post to a public URL, so check the signature before trusting a request. Compute it over the exact bytes you received (parsing and re-serializing the JSON changes them) and compare in constant time.
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const app = express();
const seen = new Set<string>(); // use your database in production
// The signature covers the exact bytes sent, so read the raw body (not re-serialized JSON).
app.post("/webhooks/intellidesk", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + createHmac("sha256", process.env.INTELLIDESK_WEBHOOK_SECRET!).update(req.body).digest("hex");
const given = String(req.header("X-IntelliDesk-Signature") || "");
if (given.length !== expected.length || !timingSafeEqual(Buffer.from(given), Buffer.from(expected))) {
return res.status(401).end();
}
const delivery = String(req.header("X-IntelliDesk-Delivery") || "");
if (delivery && seen.has(delivery)) return res.status(200).end(); // a retry you already handled
seen.add(delivery);
const { event, payload } = JSON.parse(req.body.toString("utf8"));
if (event === "leads.submit") {
// add payload.email to your CRM…
}
res.status(200).end(); // answer fast; do slow work in a queue
});import hashlib, hmac, json, os
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/webhooks/intellidesk")
def intellidesk_webhook():
raw = request.get_data() # the exact bytes that were signed
expected = "sha256=" + hmac.new(os.environ["INTELLIDESK_WEBHOOK_SECRET"].encode(), raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get("X-IntelliDesk-Signature", "")):
abort(401)
body = json.loads(raw)
if body["event"] == "leads.submit":
pass # add body["payload"]["email"] to your CRM…
return "", 200Responses and retries
- Answer with any
2xxstatus within 15 seconds. Do slow work after answering. - When your server can't be reached, times out, or answers
408,429or5xx, the same request is sent again after 30 seconds, 2 minutes and 10 minutes, then dropped. Other4xxanswers aren't retried. - A retry has the same body, signature and
X-IntelliDesk-Delivery, so a receiver that saves delivery ids handles each event once. - Settings → Webhooks shows each endpoint's latest status, and when a retry is due.
Pull instead of push
REST API
The REST API lists leads and conversations whenever you ask: handy for a nightly sync, or to fill in anything your server missed.