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

# Sandbox webhooks

> Register sandbox webhooks, verify signed HTTPS deliveries, inspect outcomes, and disable delivery.

Register sandbox webhooks to receive signed HTTPS notifications for machine, account, billing, and job events. Webhook management requires customer admin scope.

## Register an endpoint

Save this as `webhook.json`, using your public HTTPS receiver:

```json theme={null}
{"url":"https://receiver.example/events","events":["sandbox.ready","sandbox.deleted"]}
```

```bash theme={null}
bead api POST /webhooks --input webhook.json
```

Save the one-time returned `secret` in your receiver's secret store. Listing does not reveal it. Select event names from [events](https://docs.bead.run/platform/events.md).

To receive events only for selected machines, register the endpoint with `"machine_only": true` and set `metadata["bead.webhookEndpoint"]` to its UUID in each machine’s create API request. The endpoint must be enabled and belong to your organization.

Receivers must use HTTPS port 443 without URL userinfo or fragments. Every resolved address must be public; private, loopback, link-local, and metadata destinations are rejected. Redirects count as delivery failures.

## Verify signatures

Requests include these headers:

```text theme={null}
bead-event-id: <event UUID>
Idempotency-Key: <same event UUID>
bead-signature: t=<unix-seconds>,v1=<hex-HMAC-SHA256>
```

The signed message is exactly `<timestamp>.<raw-body>`. Use the returned secret's bytes as the HMAC key and preserve the body before parsing it.

This Python helper follows the wire format:

```python theme={null}
import hashlib
import hmac
import time


def verify(secret: str, signature: str, raw_body: bytes) -> bool:
    try:
        fields = dict(part.split("=", 1) for part in signature.split(","))
        timestamp = fields["t"]
        if abs(time.time() - int(timestamp)) > 300:
            return False
        message = timestamp.encode() + b"." + raw_body
        expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
        return hmac.compare_digest(expected, fields["v1"])
    except (KeyError, ValueError):
        return False
```

After verification, require the body ID to match the idempotency header. Atomically save that event ID with your business effects. Return 2xx for an already processed valid ID without applying effects again. Delivery is at least once; retries use the same event ID and a fresh signature.

## Inspect failures

```bash theme={null}
bead api GET /webhooks
bead api GET /webhooks/WEBHOOK_UUID/deliveries
```

The delivery log records HTTP statuses or null for transport failures. Attempts back off from a two-second base up to one hour, with at most 12 attempts per event. Twelve consecutive failures disable an endpoint. Earlier undelivered events block later ones for the same machine stream.

## Disable or replace

```bash theme={null}
bead api DELETE /webhooks/WEBHOOK_UUID
```

Deletion disables new delivery after any in-flight attempt and preserves the delivery log. Register a new endpoint to rotate the secret or re-enable delivery.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.