Webhooks

Get events from your agent in your own app as they happen: a new lead in your CRM, collected details in your database, bad answers in your team's channel.

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

EventFires whenpayload
leads.submitA 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.submitThe agent finishes collecting the details you asked for (Collect Data)._id, values, fields (key → label), actionName, conversationId, source, createdAt
message.feedbackA visitor or teammate gives an answer a thumbs up or down.messageId, conversationId, feedback (up / down), source, visitorId, userId
conversation.completedOnce 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.

HTTP
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…"
}
HeaderWhat it is
X-IntelliDesk-EventThe event name, same as in the body.
X-IntelliDesk-Signaturesha256= + the hex HMAC-SHA256 of the raw body, keyed with the endpoint's signing secret.
X-IntelliDesk-DeliveryThe delivery's id. Retries of the same event keep it, so you can skip repeats.
X-IntelliDesk-Attempt2, 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.

Node.js (Express)
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
});
Python (Flask)
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 "", 200

Responses and retries

  • Answer with any 2xx status within 15 seconds. Do slow work after answering.
  • When your server can't be reached, times out, or answers 408, 429 or 5xx, the same request is sent again after 30 seconds, 2 minutes and 10 minutes, then dropped. Other 4xx answers 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.
Deleting an endpoint stops its deliveries at once, including retries that were still 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.