> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meetdarwin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook events and payloads

> Every Darwin webhook event type: lead.captured, handoff.requested, outcome.submitted, personal_link.opened, meeting.booked and the rest, when each fires and an example payload for each.

Each event is one JSON body with the envelope described in [What Darwin sends](https://docs.meetdarwin.ai/api-reference/webhooks.md#what-darwin-sends). `data.object` is the thing the event is about.

## Events

| Type | Sent when | `data.object` is |
| - | - | - |
| `lead.captured` | A visitor gave their name and email in the chat | The contact, plus `data.conversation` |
| `handoff.requested` | A visitor asked to talk to a person (once per conversation) | The conversation, with its transcript |
| `outcome.submitted` | A visitor sent one of your [outcome forms](https://www.meetdarwin.ai/help/outcomes/what-are-outcomes), such as a quote request, with every answer | The outcome request, plus `data.conversation` |
| `personal_link.opened` | Someone opened one of your [personal links](https://www.meetdarwin.ai/help/conversion-studio/personal-links), whether or not they chat. Once per browser and link within 12 hours; your team's and test clicks never send it. | The link visit, plus `data.link` and `data.contact` |
| `meeting.booked` | A meeting was booked from the chat | The meeting |
| `meeting.rescheduled` | A meeting booked from the chat moved to a new time | The meeting |
| `meeting.canceled` | A meeting booked from the chat was canceled | The meeting |
| `conversation.started` | A visitor sent their first message. High volume: subscribe only if you mirror every chat. | The conversation |
| `conversation.completed` | A conversation went quiet and Darwin graded it: intent, buying stage and score | The conversation, with its transcript and `evaluation` |
| `experiment.started` | An opening test started showing variants to visitors | The experiment |
| `experiment.winner_found` | An opening test found a winner | The experiment |
| `knowledge.source_failed` | A page the agent answers from could not be read | The knowledge page, plus `data.error` |

`data.object` has the same shape as the REST API's answer for that object, so code that reads `GET /conversations/{id}` also reads the webhook. Webhooks always include names and emails: an endpoint exists to get the lead into your CRM.

## Example payloads

> **lead.captured**
>
> ```json theme={null}
> {
>   "id": "evt_2b8f0c1d6e4a9937",
>   "object": "event",
>   "type": "lead.captured",
>   "created_at": "2026-10-12T09:30:00.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "id": "ct_4f8k2m9q1x7z3c5v",
>       "object": "contact",
>       "email": "jordan@example.com",
>       "first_name": "Jordan",
>       "last_name": "Lee",
>       "name": "Jordan Lee",
>       "company": null,
>       "phone": null,
>       "external_id": null,
>       "attributes": {},
>       "identity_verified": false,
>       "source": "chat",
>       "first_seen_at": "2026-10-12T09:27:41.000Z",
>       "last_seen_at": "2026-10-12T09:30:00.000Z",
>       "created_at": "2026-10-12T09:30:00.000Z",
>       "updated_at": "2026-10-12T09:30:00.000Z"
>     },
>     "conversation": {
>       "id": "conv_c-1760261261000-k3j9",
>       "object": "conversation",
>       "page_url": "https://example.com/pricing",
>       "page_path": "/pricing",
>       "outcome": "lead",
>       "message_count": 6,
>       "preview": "Do you offer annual billing for teams of 20?",
>       "contact": "ct_4f8k2m9q1x7z3c5v",
>       "lead": { "first_name": "Jordan", "last_name": "Lee", "email": "jordan@example.com", "captured_at": "2026-10-12T09:30:00.000Z" },
>       "visitor": { "device": "desktop", "country": "US", "source": "google", "utm": {}, "ai_referred": false },
>       "test": false
>     }
>   }
> }
> ```
>
> The conversation here is shortened; it carries every field of a conversation, without the transcript.

> **handoff.requested and conversation.completed**
>
> ```json theme={null}
> {
>   "id": "evt_9c0d2e7f1a3b5c6d",
>   "object": "event",
>   "type": "handoff.requested",
>   "created_at": "2026-10-12T10:02:13.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "id": "conv_c-1760262900000-p2r8",
>       "object": "conversation",
>       "page_url": "https://example.com/support",
>       "outcome": "handoff",
>       "handoff": { "requested_at": "2026-10-12T10:02:13.000Z", "reason": "Wants to change the billing contact on their account" },
>       "lead": { "first_name": "Sam", "last_name": null, "email": "sam@example.com", "captured_at": "2026-10-12T10:01:50.000Z" },
>       "evaluation": null,
>       "messages": [
>         { "id": "m1", "role": "user", "text": "Can I talk to someone about our invoice?", "created_at": "2026-10-12T10:01:02.000Z", "tools": [] },
>         { "id": "m2", "role": "assistant", "text": "I've passed this to the team. They'll email you at sam@example.com.", "created_at": "2026-10-12T10:02:13.000Z", "tools": ["asked for a person"] }
>       ]
>     }
>   }
> }
> ```
>
> `conversation.completed` has the same shape. Its `evaluation` is filled in: `intent`, `buying_stage`, `qualification_score` and `meeting_intent` (0 to 10), `understood`, `failure_category`, `abandonment_reason` and `recommended_improvement`. `conversation.started` carries the conversation without `messages`.

> **outcome.submitted**
>
> ```json theme={null}
> {
>   "id": "evt_9c8d7e6f5a4b3c2d",
>   "object": "event",
>   "type": "outcome.submitted",
>   "created_at": "2026-10-12T10:40:00.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "id": "oreq_6f1c2a9e0b3d4e5f8a7b6c5d4e3f2a1b",
>       "object": "outcome_request",
>       "outcome": { "id": "out_3k9d2m7q1x8w", "name": "Request a quote" },
>       "status": "sent",
>       "fit": true,
>       "fit_reasons": [],
>       "answers": [
>         { "key": "what_do_you_need_a_quote_for", "question": "What do you need a quote for?", "value": "Brand refresh" },
>         { "key": "when_do_you_want_it_done", "question": "When do you want it done?", "value": "Within a month" }
>       ],
>       "fields": {
>         "what_do_you_need_a_quote_for": "Brand refresh",
>         "when_do_you_want_it_done": "Within a month"
>       },
>       "contact": { "name": "Priya Shah", "email": "priya@example.com", "phone": null },
>       "contact_id": "ct_7q2w9e4r1t6y3u8i",
>       "conversation": "conv_c-1760265600000-k3p9",
>       "page_url": "https://example.com/services",
>       "billing": "counted",
>       "submitted_at": "2026-10-12T10:40:00.000Z",
>       "created_at": "2026-10-12T10:38:12.000Z",
>       "test": false
>     },
>     "conversation": {
>       "id": "conv_c-1760265600000-k3p9",
>       "object": "conversation",
>       "page_url": "https://example.com/services",
>       "page_path": "/services",
>       "outcome": "lead",
>       "message_count": 4,
>       "preview": "How much would a brand refresh cost?",
>       "contact": "ct_7q2w9e4r1t6y3u8i",
>       "test": false
>     }
>   }
> }
> ```
>
> The conversation here is shortened; it carries every field of a conversation, with its transcript in `messages`. Each answer's `key` comes from its question and stays the same while the question does; `GET /outcomes` lists every form's keys. `fit` is `false` when an answer matched the form's fit rules, and `fit_reasons` names those questions. `billing` is `counted`, `not_billable`, `voided` or `none`.

> **personal\_link.opened**
>
> ```json theme={null}
> {
>   "id": "evt_4a8c2e6b0d1f5539",
>   "object": "event",
>   "type": "personal_link.opened",
>   "created_at": "2026-10-12T14:01:31.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "id": "lv_3f9a0c1d2b4e5f60718293a4b5c6d7e8",
>       "object": "link_visit",
>       "link": { "id": "lk_q4x7mz", "name": "Q4 outbound" },
>       "greeting": "gr_m3k8wq",
>       "page": "/pricing",
>       "person": { "email": "jane@example.com", "first_name": "Jane", "last_name": "Cooper", "company": "Acme", "title": "VP of Operations" },
>       "contact": "ct_7q2w9e4r1t6y3u8i",
>       "conversations": [],
>       "created_at": "2026-10-12T14:01:30.000Z",
>       "last_seen_at": "2026-10-12T14:01:30.000Z",
>       "test": false
>     },
>     "link": {
>       "id": "lk_q4x7mz",
>       "object": "personal_link",
>       "name": "Q4 outbound",
>       "page": "/pricing",
>       "greeting": "gr_m3k8wq",
>       "status": "active",
>       "url": "https://example.com/pricing?dw=lk_q4x7mz",
>       "results": { "days": 30, "visits": 42, "chats": 11, "meetings": 3 }
>     },
>     "contact": {
>       "id": "ct_7q2w9e4r1t6y3u8i",
>       "object": "contact",
>       "email": "jane@example.com",
>       "first_name": "Jane",
>       "last_name": "Cooper",
>       "company": "Acme",
>       "source": "link"
>     }
>   }
> }
> ```
>
> The link and contact here are shortened; they carry every field `GET /personal_links/{id}` and `GET /contacts/{id}` return. `person` is what the link's address carried, so a forwarded email can carry someone else's details. `contact` is null when the link had no email. The visit arrives within seconds of the click, before any chat; `GET /link_visits/{id}` later lists the `conversations` that browser started.

> **meeting.booked, meeting.rescheduled and meeting.canceled**
>
> ```json theme={null}
> {
>   "id": "evt_5e6f7a8b9c0d1e2f",
>   "object": "event",
>   "type": "meeting.booked",
>   "created_at": "2026-10-12T11:15:00.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "id": "mtg_n4f8a2b1c9d0e7f6a5b4c3d2e1f0a9b8c",
>       "object": "meeting",
>       "status": "booked",
>       "verified": true,
>       "provider": "calendly",
>       "meeting_type": "30 minute demo",
>       "host": "Alex Rivera",
>       "start_at": "2026-10-14T16:00:00.000Z",
>       "end_at": "2026-10-14T16:30:00.000Z",
>       "booked_at": "2026-10-12T11:15:00.000Z",
>       "reschedules": 0,
>       "join_url": "https://zoom.us/j/0000000000",
>       "reschedule_url": "https://calendly.com/reschedulings/...",
>       "cancel_url": "https://calendly.com/cancellations/...",
>       "conversation": "conv_c-1760267400000-x8m2",
>       "contact": "ct_7q2w9e4r1t6y3u8i",
>       "invitee": { "name": "Priya Shah", "email": "priya@example.com" },
>       "updated_at": "2026-10-12T11:15:00.000Z",
>       "test": false
>     }
>   }
> }
> ```
>
> `verified` is `true` when the calendar provider confirmed the booking. On `meeting.rescheduled`, `start_at`, `end_at` and `reschedules` change; on `meeting.canceled`, `status` is `canceled`.

> **experiment.started and experiment.winner\_found**
>
> ```json theme={null}
> {
>   "id": "evt_1a2b3c4d5e6f7a8b",
>   "object": "event",
>   "type": "experiment.winner_found",
>   "created_at": "2026-10-12T12:00:00.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "id": "exp_4c1f0a2b9d3e7a10",
>       "object": "experiment",
>       "name": "Pricing page opening",
>       "status": "winner_found",
>       "pages": ["/pricing"],
>       "metric": "bookings",
>       "winner": "var_...",
>       "variants": [
>         { "id": "var_...", "object": "variant", "name": "Current", "control": true, "headline": "Questions about pricing?", "results": { "views": 1840, "rate": 0.021, "chance_beats_current": null } }
>       ]
>     }
>   }
> }
> ```
>
> Shortened. The experiment has the shape of `GET /experiments/{id}`.

> **knowledge.source\_failed**
>
> ```json theme={null}
> {
>   "id": "evt_8b7a6f5e4d3c2b1a",
>   "object": "event",
>   "type": "knowledge.source_failed",
>   "created_at": "2026-10-12T03:10:00.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "id": "src_142",
>       "object": "knowledge_source",
>       "url": "https://example.com/docs/security",
>       "section": "/docs/",
>       "title": "Security",
>       "status": "failed",
>       "error": "The page answered 404.",
>       "characters": 5120,
>       "chunks": 4,
>       "read_at": "2026-10-05T03:10:00.000Z",
>       "created_at": "2026-09-20T14:00:00.000Z"
>     },
>     "error": "The page answered 404."
>   }
> }
> ```
>
> A page that fails keeps its last good content, so the agent keeps answering from it until you fix or remove it.

> **webhook.test**
>
> ```json theme={null}
> {
>   "id": "evt_0f1e2d3c4b5a6978",
>   "object": "event",
>   "type": "webhook.test",
>   "created_at": "2026-10-12T09:31:00.000Z",
>   "api_version": "2026-10-12",
>   "data": {
>     "object": {
>       "endpoint": "we_7h3k9m2p4q8r1s5t",
>       "message": "This is a test event from Darwin. Your endpoint works if it answered 2xx."
>     }
>   }
> }
> ```
>
> Sent only by **Send test event**. It is signed like a real event, sent once and not retried, and does not appear in `GET /events`.

If the object no longer exists when Darwin builds the payload, `data.object` is `null`. If the person was [erased](https://docs.meetdarwin.ai/api-reference/privacy.md), `data` is `{ "erased": true }`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.