# Set up webhooks

<Lead>
Webhooks let the platform notify your own systems in real time: for every subscribed event (for example a new appointment booking or a website inquiry) it sends a signed POST request to a URL of your choice. Ideal for CRM sync, Zapier/Make automations or your own scripts.
</Lead>

## Prerequisites

- Managing webhooks requires the corresponding permission (owners and admins by default).
- You need an HTTPS endpoint that accepts POST requests and responds with a 2xx status.

## Create an endpoint

<Steps>
  <Step title="Open the webhook management">
    Go to **Settings > Webhooks** and click **Add endpoint**.
  </Step>
  <Step title="Set the URL and events">
    Enter the target URL and pick the events from the catalog. You can subscribe to individual events, or receive every current and future event with **All events**.
  </Step>
  <Step title="Store the signing secret">
    After creation the signing secret (format `whsec_...`) is shown exactly once. Copy it immediately into your password manager or your receiver's configuration. It cannot be viewed again afterwards.
  </Step>
  <Step title="Send a test event">
    Open the endpoint's detail page and send a test event. The delivery appears in the log shortly after. This verifies reachability and your signature check before real events flow.
  </Step>
</Steps>

## Which events exist?

The event catalog is grouped by domain. An overview:

| Group | Examples |
|---|---|
| Properties | `property.created`, `property.updated`, `property.sync.completed` |
| Contacts | `contact.sync.completed` |
| Tools | `tool.installed`, `tool.uninstalled`, `tool.run.completed`, `tool.run.failed` |
| Credentials vault | `credential.created`, `credential.revealed`, `credential.external_shared` |
| DB-Leads | `db-lead.created`, `db-lead.moved`, `db-lead.scan.completed` |
| Homestaging | `homestaging.generated` |
| Website | `website_lead.received`, `website_page.published`, `website_domain.verified` |
| Appointment booking | `booking.created`, `booking.cancelled`, `booking.rescheduled`, `booking.updated` |
| Workspace and billing | `tenant.plan_changed`, `tenant.credit_pack_purchased`, `tenant.seat_subscribed` |

The complete, always current list with descriptions and example payloads is shown directly in the event picker when creating an endpoint.

## What your endpoint receives

Every delivery is a POST with a JSON body:

```json
{
  "id": "5f0f9c3a-...",
  "type": "booking.created",
  "createdAt": "2026-07-21T09:30:00.000Z",
  "data": {
    "appointmentId": "a1b2c3d4-...",
    "eventTypeId": "b2c3d4e5-...",
    "startsAt": "2026-08-03T09:30:00.000Z"
  }
}
```

Plus three headers following the Standard Webhooks format:

| Header | Content |
|---|---|
| `webhook-id` | Unique ID of the delivery (identical across retries) |
| `webhook-timestamp` | Unix timestamp in seconds |
| `webhook-signature` | `v1,<base64 HMAC>` |

## Verify the signature

Verify the signature on every delivery so that only genuine platform events are accepted:

1. Build the string `{webhook-id}.{webhook-timestamp}.{raw request body}`.
2. Compute HMAC-SHA256 with your signing secret (base64-decode the part after `whsec_`).
3. Compare the base64 result with the value after `v1,` in the `webhook-signature` header.

Standard Webhooks libraries for common languages handle these steps for you.

## Delivery, retries and pausing

- If your endpoint does not respond with 2xx, the platform retries automatically: after 1, 5, 30, 120 and 720 minutes (up to 5 attempts in total).
- The delivery log on the detail page shows every attempt with status, response time and the first 2,048 characters of the response. Individual deliveries can be **redelivered** manually.
- After 15 consecutive failed deliveries the endpoint pauses automatically. Once you fix the problem, re-enable it in the management view.

<Info title="Custom headers">
Per endpoint you can define additional HTTP headers (for example your own API key for the target system). The platform's signature headers cannot be overridden.
</Info>

## Rotate the secret

If you suspect a leaked secret: open the detail page and use **Rotate secret**. The old secret becomes invalid immediately; the new one is again shown exactly once. Update your target system right away, otherwise its signature checks will fail.

## Frequently asked questions

<Faq>
  <FaqItem q="Do I receive events of other workspaces?">
    No. Every endpoint belongs to exactly one workspace and receives only its events.
  </FaqItem>
  <FaqItem q="In what order do events arrive?">
    Usually in creation order. Retries after failures can shift the order. Use `createdAt` from the body when ordering matters.
  </FaqItem>
  <FaqItem q="Can I receive an event twice?">
    Yes, in rare cases (for example when your response arrived after a timeout). Use the `webhook-id` as an idempotency key and process each ID only once.
  </FaqItem>
  <FaqItem q="Why is my endpoint paused?">
    After 15 consecutive failed deliveries the endpoint pauses automatically so no queue builds up. Fix the cause (check the log) and re-enable it.
  </FaqItem>
</Faq>
