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

# Webhook Testing

> Assert that your application sends the correct webhooks

Empirical lets you verify your application sends the correct webhooks. It
integrates with [webhook.site](https://webhook.site) to capture and query
webhook payloads, and includes a custom Playwright assertion for use in tests.

All test repositories managed by Empirical have this built-in — no additional
setup is required.

## Usage

### Create a webhook URL

Use `getWebhookUrl` to generate a unique webhook.site URL for your test.
Configure your application to send webhooks to this URL.

```ts theme={null}
import { getWebhookUrl } from "@empiricalrun/playwright-utils";

const webhookUrl = await getWebhookUrl();
// e.g. "https://webhook.site/9981f9f4-657a-4ebf-be7c-1915bedd4775"

// Optionally set expiry in seconds (default: 604800 = 7 days)
const webhookUrl = await getWebhookUrl({ expiry: 3600 }); // expires in 1 hour
const webhookUrl = await getWebhookUrl({ expiry: null }); // never expires
```

### Custom assertion

Use the `toHaveReceivedWebhook` assertion to verify that a webhook was received.
Pass any webhook.site URL to `expect()` — either one generated with `getWebhookUrl`
or an existing URL — and specify content to match against the body.

```ts theme={null}
import { getWebhookUrl } from "@empiricalrun/playwright-utils";
import { expect } from "@empiricalrun/playwright-utils/test";

// Works with a newly generated URL
const webhookUrl = await getWebhookUrl();
await expect(webhookUrl).toHaveReceivedWebhook({
  content: orderId,
});

// Or with an existing webhook.site URL
await expect("https://webhook.site/your-token-id").toHaveReceivedWebhook({
  content: [roomId, "recording.started"],
});
```

The assertion polls webhook.site until a matching webhook is found or the
timeout is reached. When multiple content strings are provided, all of them
must be present in the webhook body.

### Querying webhooks

Use `queryWebhookRequests` to fetch matching webhook requests directly. This
is useful when you need to inspect the payload for further assertions.

```ts theme={null}
import { getWebhookUrl, queryWebhookRequests } from "@empiricalrun/playwright-utils";

const webhookUrl = await getWebhookUrl();
const requests = await queryWebhookRequests(webhookUrl, [roomId, "recording.started"]);

// Access the parsed payload
const body = JSON.parse(requests[0].content);
expect(body.status).toBe("completed");
```

Each request in the returned array is a `WebhookRequest` with the following
properties:

| Property     | Type                       | Description                                                  |
| ------------ | -------------------------- | ------------------------------------------------------------ |
| `content`    | `string`                   | Raw request body (parse with `JSON.parse` for JSON payloads) |
| `method`     | `string`                   | HTTP method (e.g. `POST`)                                    |
| `headers`    | `Record<string, string[]>` | Request headers                                              |
| `url`        | `string`                   | Full request URL                                             |
| `created_at` | `string`                   | Timestamp of when the request was received                   |

## Assertion options

| Option    | Type                 | Default  | Description                                                                             |
| --------- | -------------------- | -------- | --------------------------------------------------------------------------------------- |
| `content` | `string \| string[]` | Required | One or more terms to match against the webhook body. Multiple terms are AND'd together. |
| `timeout` | `number`             | `300000` | Maximum time in milliseconds to wait for the webhook (default: 5 minutes)               |
