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

# Webhooks

> Subscribe external systems to Breezy service request and interaction events.

# Webhooks

Webhooks let Breezy notify your systems when service request and interaction
events happen.

Webhook endpoint management requires a user token. Service API keys cannot
create, update, rotate, delete, or list webhook endpoints.

Open the [Webhooks tab](https://ai.getbreezyapp.com/integrations?tab=webhooks)
in Breezy to manage endpoint configuration from the dashboard.

## Supported events

| Event type                       | When it fires                                        |
| -------------------------------- | ---------------------------------------------------- |
| `service_request.created`        | A new service request is created.                    |
| `service_request.status_changed` | A service request status changes.                    |
| `service_request.booked`         | A service request is booked through instant booking. |
| `interaction.completed`          | A voice or chat interaction is finalized.            |

## Management flow

1. Authenticate with a user JWT.
2. `POST /integrations/webhooks` with a name, HTTPS URL, and event types.
3. Store the returned `signingSecret` immediately. It is shown only once.
4. Verify every delivery using the `breezy-signature` header.
5. Rotate a signing secret when needed. Rotation immediately invalidates the old
   secret.

Local development URLs may use `localhost`, `127.0.0.1`, or `::1`. Production
webhook URLs must use HTTPS.

## Delivery format

Breezy sends `POST` requests with a JSON envelope:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "8d7f2a70-5b57-4c08-9e7d-8d6d52f517d2",
  "type": "service_request.created",
  "createdAt": "2026-06-17T18:00:00.000Z",
  "tenantId": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "serviceRequestId": "sr_...",
    "referenceNumber": "SR-1001",
    "channel": "web_chat",
    "serviceNeeded": "Water heater repair",
    "customer": {
      "name": "Ava Customer",
      "phoneNumber": "+15555550123",
      "emailAddress": "ava@example.com"
    }
  }
}
```

Deliveries include these headers:

| Header               | Description                                        |
| -------------------- | -------------------------------------------------- |
| `breezy-event-id`    | Event id.                                          |
| `breezy-event-type`  | Event type.                                        |
| `breezy-delivery-id` | Stable delivery id for idempotency and support.    |
| `breezy-timestamp`   | ISO 8601 timestamp used in signature verification. |
| `breezy-signature`   | HMAC signature in `v1=<hex>` format.               |

Webhook payloads include operational fields needed by integrations, but do not
include raw transcripts.

## Event data

`service_request.created` data can include:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "serviceRequestId": "sr_...",
  "referenceNumber": "SR-1001",
  "channel": "web_chat",
  "leadSourceId": "lead_source_...",
  "conversationId": "conv_...",
  "serviceNeeded": "Water heater repair",
  "preferredTimeWindow": "Tomorrow morning",
  "serviceAddress": "123 Main St, Denver, CO 80202",
  "serviceAddressLine1": "123 Main St",
  "serviceAddressLine2": "Unit 4",
  "serviceCity": "Denver",
  "serviceState": "CO",
  "serviceZipCode": "80202",
  "jobTypeFreetext": "Water heater",
  "jobClass": "plumbing",
  "jobTypeId": "job_type_...",
  "availabilityWindows": [],
  "customFields": {},
  "onlineBookingFormId": "booking_form_...",
  "customer": {
    "name": "Ava Customer",
    "firstName": "Ava",
    "lastName": "Customer",
    "phoneNumber": "+15555550123",
    "emailAddress": "ava@example.com"
  }
}
```

`service_request.status_changed` data:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "serviceRequestId": "sr_...",
  "channel": "web_chat",
  "leadSourceId": "lead_source_...",
  "previousStatus": "new",
  "newStatus": "in_progress"
}
```

`service_request.booked` data:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "serviceRequestId": "sr_...",
  "referenceNumber": "SR-1001",
  "interactionId": "voice_call_...",
  "confirmedWindowStart": "2026-06-18T16:00:00.000Z",
  "confirmedWindowEnd": "2026-06-18T18:00:00.000Z",
  "providerAppointmentId": "appt_..."
}
```

`interaction.completed` data:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "interactionId": "interaction_...",
  "channel": "voice",
  "conversationOutcome": "booked"
}
```

Fields with no value are omitted from `data`.

## Signature verification

Compute the expected signature with HMAC-SHA256:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
signed_payload = breezy_timestamp + "." + raw_request_body
signature = "v1=" + hmac_sha256(signing_secret, signed_payload)
```

Compare your expected signature to `breezy-signature` using a constant-time
comparison. Reject requests with missing headers, invalid signatures, or stale
timestamps according to your integration's tolerance.

## Retries and history

Breezy treats any non-2xx response as a failed delivery. Failed deliveries are
retried up to 3 total attempts, then marked abandoned.

Deliveries are at least once. Use `breezy-delivery-id` as your idempotency key
because a delivery can be retried after network or persistence uncertainty.

Use `GET /integrations/webhooks/deliveries` to inspect recent delivery status.
Pass `endpointId` to filter history for one endpoint.
