Skip to main content
Find it in Darwin Profile menu → Workspace settings → API → Webhooks Open this screen ↗
Webhooks tell your systems the moment something happens in the chat: a visitor leaves their email, asks for a person, or books a meeting. Darwin sends each event as a signed POST to your HTTPS address and retries for about three days if your server doesn’t answer. Every event is something Darwin’s server recorded, never something a browser claimed. Your own preview chats send no events; use Send test event to try an endpoint.

Add an endpoint

1

Open Webhooks

Open Settings → API. Owners and admins see Add endpoint beside Webhooks.
2

Enter your URL

Select Add endpoint and enter an HTTPS URL on a public hostname, for example https://example.com/webhooks/darwin. It must answer 2xx within 10 seconds.
3

Choose events

Under Events, keep Choose events and tick what you need. lead.captured, handoff.requested and meeting.booked are ticked for you. All events subscribes to every type, including ones Darwin adds later.
4

Copy the signing secret

Select Add endpoint. Darwin shows the endpoint’s Signing secret (whsec_…). Store it with your receiver’s other secrets. Owners and admins can see it again later with Show signing secret.
5

Send a test event

Open the endpoint’s ⋯ menu and select Send test event. It worked if Darwin says The endpoint answered 200 (or another 2xx). Otherwise it shows why, for example HTTP 401 or No answer within 10 seconds.
A workspace can have 16 endpoints. A new endpoint receives events that happen after it was created; for older ones, read GET /events.

Events

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.

What Darwin sends

The body is one event: Darwin builds the payload a few seconds after the event and keeps it: a retry or redelivery sends the same body. For the latest state, read the object from the API by its id.

Example payloads

The conversation here is shortened; it carries every field of a conversation, without the transcript.
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.
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.
Shortened. The experiment has the shape of GET /experiments/{id}.
A page that fails keeps its last good content, so the agent keeps answering from it until you fix or remove it.
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, data is { "erased": true }.

Verify the signature

Darwin signs every request the Standard Webhooks way, so you can use an existing library or a few lines of your own. Always verify before you trust a payload: anyone can send a POST to your URL.
  1. Take the raw request body exactly as received. Parsing and re-serialising the JSON changes the bytes and breaks the signature.
  2. Reject the request if webhook-timestamp is more than five minutes from your clock.
  3. Build the signed content: `${webhook-id}.${webhook-timestamp}.${body}`.
  4. Compute HMAC-SHA256 over it. The key is your signing secret without the whsec_ prefix, base64-decoded.
  5. Base64-encode the result and compare it, in constant time, with each v1, entry in webhook-signature. The header can hold several entries separated by spaces (during a secret rotation); accept if any matches.

Use a Standard Webhooks library

Any Standard Webhooks library verifies Darwin’s signatures, including Svix’s. Pass the raw body, the headers and the secret as Darwin shows it:

Answer quickly

  • Answer with any 2xx status within 10 seconds. Anything else counts as a failure: a 3xx (Darwin does not follow redirects), a 4xx, a 5xx, a timeout or a refused connection.
  • Answer first and do slow work (CRM calls, Slack posts) afterwards in a queue. A receiver that waits on another service is the most common cause of timeouts.
  • Darwin reads at most the first 64 KB of your answer and keeps the first 500 characters in the delivery log. Don’t return anything secret.

Handle duplicates and order

  • At least once. Darwin may deliver the same event more than once, for example when your server handled it but the answer was lost. webhook-id (the event id) is the same on every retry and redelivery. Record the ids you’ve processed and skip repeats.
  • No ordering. Events can arrive out of order, especially after retries. A meeting.rescheduled can arrive before its meeting.booked. Use created_at, or the object’s updated_at, to decide which is newer, or read the current object from the API.

Retries

If a delivery fails, Darwin tries again after: That is nine attempts over about three days. After the last one the delivery is marked Failed. You can still send it again by hand for 30 days.

Endpoints that keep failing are switched off

If an endpoint has failures and not one successful delivery for three days, and is still failing, Darwin switches it off and shows why under its address in Settings → API: Deliveries have failed for three days without one success. Fix the receiver, then switch the endpoint back on. An endpoint that answers 410 Gone is switched off at once: that is how a receiver (Zapier, for one) says the subscription is over. Its badge turns from Failing to Off, and GET /webhook_endpoints/{id} shows "status": "disabled" with a disabled_reason. While an endpoint is off, Darwin does not queue new events for it. To recover:
  1. Fix your receiver and check it with Send test event.
  2. Open the endpoint’s ⋯ menu and select Switch on (or PATCH it with {"status": "enabled"}). Deliveries that were still retrying continue.
  3. Send the events you missed: use Send again in Deliveries, or read GET /events for the gap and redeliver them.
You can also select Switch off yourself, for example during maintenance. Events stay in the event log for 30 days.

Check deliveries and send again

Open the endpoint’s ⋯ menu and select Deliveries. Each row shows the event type, whether it was Delivered, is Retrying or Failed, the number of attempts, your server’s last HTTP status or error, and the next try. Send again puts that delivery back in the queue; it goes out within a few seconds. The endpoint row also summarises this week: how many were delivered, failed and are retrying. With the API:
The redeliver response says how many deliveries were queued. Only enabled endpoints subscribed to the event’s type receive it.

Catch up with the event log

Every event from the last 30 days is in GET /events (scope events:read), newest first. It is the pull twin of webhooks: use it after downtime, or instead of webhooks if you would rather poll.
Each item is the same event body a webhook sends.

Rotate the signing secret

Open the endpoint’s ⋯ menu, select Rotate signing secret and confirm. Darwin shows the new secret once. For the next 24 hours every delivery carries two signatures, one per secret, so your receiver keeps working while you switch it to the new secret. After 24 hours only the new secret signs. With the API: POST /v1/webhook_endpoints/{id}/rotate_secret returns the endpoint with its new secret.

Requirements for your URL

  • HTTPS on a public hostname. Plain HTTP, IP addresses, localhost and .local or .internal names are refused, and so is a hostname that resolves to a private network address.
  • No username or password before the host. To authenticate requests, verify the signature, or put a token in the path or query string.
  • Darwin does not publish a fixed list of sending IP addresses. Verify signatures rather than allowlisting IPs.

Change or remove an endpoint

  • Change its URL, events or description with PATCH /v1/webhook_endpoints/{id}.
  • Delete in the ⋯ menu (or DELETE /v1/webhook_endpoints/{id}) stops all deliveries and removes the endpoint’s delivery log.

Troubleshooting

Check that you verify the raw body, not parsed and re-serialised JSON, and that you base64-decode the secret after removing whsec_. Frameworks that parse JSON automatically need a raw-body route, like express.raw above.
Your URL answered with a 3xx. Use the final address, including https:// and the exact path, often with or without a trailing slash.
Answer 200 before doing the work. Check that your server is reachable from the public internet and not behind a login or firewall.
Preview chats in Darwin never send events. Use Send test event, or have someone use the chat on your live website.
Send handoffs to Slack → · Sync leads to a CRM →