> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://platform.bctrl.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://platform.bctrl.ai/_mcp/server.

# Webhooks

> Send signed BCTRL events to an HTTPS endpoint and manage delivery attempts.

Webhooks let BCTRL notify your server when automation changes state. Create an HTTPS endpoint, subscribe it to event types, and process each delivery after verifying its signature.

## Create an endpoint

```ts
const webhook = await bctrl.webhooks.create({
  name: "automation-events",
  url: "https://example.com/bctrl/webhook",
  events: ["run.completed", "run.failed", "recording.ready"],
});

console.log(webhook.id);
console.log(webhook.secret); // save it now; it is returned only once
```

The URL must use HTTPS and cannot contain embedded credentials. `name` is optional. `events` must contain at least one event type.

The create response includes the signing `secret`. Store it securely. `get()` and `list()` return the endpoint configuration but never return the secret again.

## Event types

| Event                  | Sent when                      |
| ---------------------- | ------------------------------ |
| `run.started`          | A Run starts.                  |
| `run.completed`        | A Run completes successfully.  |
| `run.failed`           | A Run fails.                   |
| `tool_input.requested` | A Tool requests human input.   |
| `tool_input.responded` | Human input is submitted.      |
| `tool_input.expired`   | A human-input request expires. |
| `view.created`         | A View is created.             |
| `view.revoked`         | A View is revoked.             |
| `recording.ready`      | A recording becomes available. |

A delivery body is a webhook envelope:

| Field          | Type             | Always present | Description                          |
| -------------- | ---------------- | -------------- | ------------------------------------ |
| `id`           | `string`         | Yes            | Event identifier.                    |
| `type`         | `string`         | Yes            | Event type, such as `run.completed`. |
| `createdAt`    | `string`         | Yes            | Event timestamp.                     |
| `subaccountId` | `string \| null` | Yes            | Subaccount that produced the event.  |
| `data`         | `object`         | Yes            | Event-specific payload.              |

Verify the delivery signature with the secret before using `data`. Keep the raw request body available to your verification code; do not parse and reserialize it before verification. The SDK manages webhook endpoints and delivery records; your HTTP server is responsible for receiving and verifying incoming requests.

## Manage an endpoint

```ts
const all = await bctrl.webhooks.list();
const current = await bctrl.webhooks.get(webhook.id);

await bctrl.webhooks.update(webhook.id, {
  events: ["run.completed", "run.failed"],
  enabled: false,
});

await bctrl.webhooks.delete(webhook.id);
```

Update any combination of `name`, `url`, `events`, and `enabled`. Deleting an endpoint does not remove its historical delivery records.

### Webhook fields

| Field                    | Type                 | Always present | Description                                  |
| ------------------------ | -------------------- | -------------- | -------------------------------------------- |
| `id`                     | `string`             | Yes            | Endpoint identifier used by the SDK methods. |
| `subaccountId`           | `string \| null`     | Yes            | Subaccount context for the endpoint.         |
| `name`                   | `string \| null`     | Yes            | Human-readable label.                        |
| `url`                    | `string`             | Yes            | HTTPS destination.                           |
| `events`                 | `WebhookEventType[]` | Yes            | Events sent to the endpoint.                 |
| `enabled`                | `boolean`            | Yes            | Whether new deliveries are sent.             |
| `createdAt`, `updatedAt` | `string`             | Yes            | Resource timestamps.                         |

## Rotate the signing secret

Rotation replaces the current secret immediately. The new secret is returned only in the rotation response.

```ts
const rotated = await bctrl.webhooks.rotateSecret(webhook.id);
console.log(rotated.id, rotated.secret); // save the new secret
```

Update your receiver to use the new secret before rotating in production, or be prepared for deliveries signed with the new secret immediately after rotation.

## Test and troubleshoot deliveries

Send a signed test event and inspect the resulting delivery record:

```ts
const testDelivery = await bctrl.webhooks.test(webhook.id);
console.log(testDelivery.id, testDelivery.status);

const deliveries = await bctrl.webhooks.deliveries.list(webhook.id);
for await (const delivery of bctrl.webhooks.deliveries.iter(webhook.id)) {
  console.log(delivery.eventType, delivery.status, delivery.lastError);
}
```

When the receiver is fixed, queue another attempt for a failed delivery:

```ts
await bctrl.webhooks.deliveries.redeliver(webhook.id, delivery.id);
```

### Delivery fields

| Field                    | Type                                                          | Always present | Description                               |
| ------------------------ | ------------------------------------------------------------- | -------------- | ----------------------------------------- |
| `id`                     | `string`                                                      | Yes            | Delivery identifier used for redelivery.  |
| `webhookId`              | `string \| null`                                              | Yes            | Endpoint that received the delivery.      |
| `eventId`                | `string`                                                      | Yes            | Event identifier in the webhook envelope. |
| `eventType`              | `string`                                                      | Yes            | Event type being delivered.               |
| `status`                 | `"pending" \| "sending" \| "sent" \| "failed" \| "cancelled"` | Yes            | Delivery state.                           |
| `attemptCount`           | `number`                                                      | Yes            | Number of attempts made.                  |
| `responseStatus`         | `number \| null`                                              | Yes            | HTTP response status from your endpoint.  |
| `nextAttemptAt`          | `string \| null`                                              | Yes            | Next scheduled attempt, if any.           |
| `sentAt`                 | `string \| null`                                              | Yes            | Successful send timestamp.                |
| `lastError`              | `string \| null`                                              | Yes            | Most recent delivery error.               |
| `createdAt`, `updatedAt` | `string`                                                      | Yes            | Delivery timestamps.                      |

## Next

* [Runs](/sdk/runs) — inspect the automation events that webhooks reference
* [Views](/sdk/views) — create live or replayable automation views
* [Account and organization](/sdk/account) — scope endpoints and manage API keys