> Send AI Demo Agent leads, qualified visitors, bookings and conversation summaries to Slack, Zapier or your own endpoint with signed webhooks.

Canonical: https://livedemo.ai/docs/ai-demo-agent/webhooks-and-slack/

# Webhooks & Slack

Get notified the moment something happens with your AI Demo Agent. Set this up in **Settings → Integrations → Webhooks & Slack**.

## Events

| Event | Fires when |
| --- | --- |
| `lead.created` | A visitor leaves an email (connect form, email pill, chat, `identify`, or the recap page) |
| `lead.qualified` | A visitor passes your [qualification rules](https://livedemo.ai/docs/ai-demo-agent/qualification-and-booking/) |
| `session.ended` | A conversation ends (hang-up or idle for the agent's summary time, 5 minutes by default), with its summary |
| `meeting.booked` | A visitor books through the inline calendar |
| `recap.viewed` | Someone other than the visitor opens the [recap page](https://livedemo.ai/docs/ai-demo-agent/recap-page/) |

## Slack

You don't need to install a LiveDemo app. Create an incoming webhook in your own Slack workspace:

1. Go to [api.slack.com/apps](https://api.slack.com/apps) → **Create New App** → **From scratch**, and pick your workspace.
2. Open **Incoming Webhooks** and turn it on.
3. Click **Add New Webhook to Workspace**, pick a channel, then copy the `https://hooks.slack.com/services/…` URL.
4. In LiveDemo, click **Add Slack**, paste the URL, choose the events and save.
5. Click **Send test**.

Each message shows who the visitor is, the summary, their intent, demos watched and qualification answers, with **Replay** and **Recap** links.

## Webhooks

Click **Add webhook**, enter an `https://` URL and choose the events. **Copy the signing secret** shown right after you create the webhook. It is shown only once.

### Payload

```json
{
  "id": "evt_6f1c…",
  "type": "lead.qualified",
  "createdAt": "2026-10-14T09:12:44.120Z",
  "workspaceId": "…",
  "data": {
    "session": {
      "id": "…",
      "agentId": "…",
      "agentName": "Acme demo agent",
      "startedAt": "2026-10-14T09:02:10.000Z",
      "endedAt": null,
      "durationMs": 0,
      "messageCount": 8,
      "demosOpened": 2,
      "summary": "Jane, RevOps lead at Acme (50 people), evaluated the Salesforce sync…",
      "topics": ["salesforce", "pricing"],
      "intent": "high",
      "unanswered": [],
      "qualified": true,
      "fields": { "email": "jane@acme.com", "team_size": 50, "role": "RevOps" },
      "meetingBookedAt": null,
      "pageUrl": "https://acme.com/pricing",
      "referrer": "https://google.com/",
      "utm": { "utm_campaign": "q4-salesforce" },
      "country": "United States",
      "replayUrl": "https://app.livedemo.ai/analytics?agent=…&agentSession=…",
      "recapUrl": "https://api.livedemo.ai/recaps/…"
    },
    "lead": { "id": "…", "name": "Jane Doe", "email": "jane@acme.com", "capturedVia": "chat" }
  }
}
```

### Headers

| Header | Value |
| --- | --- |
| `X-LiveDemo-Event` | The event type |
| `X-LiveDemo-Delivery` | The event id. The same id is sent on retries, so use it to ignore duplicates |
| `X-LiveDemo-Timestamp` | Unix seconds when the request was sent |
| `X-LiveDemo-Signature` | `sha256=` + HMAC-SHA256 of `timestamp + "." + raw body`, keyed with your signing secret |

### Verify the signature

Always verify the signature before you trust a webhook. Reject requests with an old timestamp (for example, older than 5 minutes).

```js
import crypto from 'crypto'

function verify(req, rawBody, secret) {
    const timestamp = req.headers['x-livedemo-timestamp']
    const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
    const received = String(req.headers['x-livedemo-signature'] || '')
    const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300
    return fresh && received.length === expected.length && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))
}
```

Compute the signature over the **raw** request body. Re-serializing parsed JSON can change the bytes and break the signature.

### Delivery and retries

- Respond with any `2xx` within 10 seconds.
- Failed deliveries are retried up to 5 times, with exponential backoff starting at 30 seconds.
- Redirects are not followed.
- Webhook URLs must be public `https://` addresses. Private and internal network addresses are rejected.
- The last status and error for each webhook are shown in Settings.

---

Back to [Conversation Insights](https://livedemo.ai/docs/ai-demo-agent/conversation-insights/)
