# Webhook endpoints

Create, update and inspect webhook endpoints with the API.

# Webhook endpoints

Everything the dashboard can do with webhook endpoints is available through the API, so you can provision endpoints per environment from your own tooling.

## Endpoints

| Method | Path | Scope | Purpose |
|---|---|---|---|
| `GET` | `/v1/webhook_endpoints` | `webhooks:read` | List endpoints (newest first). |
| `POST` | `/v1/webhook_endpoints` | `webhooks:write` | Create an endpoint. |
| `GET` | `/v1/webhook_endpoints/{id}` | `webhooks:read` | Retrieve one endpoint. |
| `PATCH` | `/v1/webhook_endpoints/{id}` | `webhooks:write` | Update url, description, events or enabled. |
| `POST` | `/v1/webhook_endpoints/{id}` | `webhooks:write` | Same as `PATCH`, for clients without `PATCH`. |
| `DELETE` | `/v1/webhook_endpoints/{id}` | `webhooks:write` | Delete an endpoint. |
| `POST` | `/v1/webhook_endpoints/{id}/rotate_secret` | `webhooks:write` | Rotate the signing secret. |
| `GET` | `/v1/webhook_endpoints/{id}/deliveries` | `webhooks:read` | Delivery log of one endpoint. |

Secret keys only — webhook management is never allowed from the browser.

## Create

```bash
curl https://api.southbill.com/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/southbill/webhook",
    "description": "Production",
    "enabled_events": ["checkout.session.completed", "invoice.paid"]
  }'
```

```json
{
  "id": "…",
  "object": "webhook_endpoint",
  "url": "https://example.com/southbill/webhook",
  "enabled": true,
  "enabled_events": ["checkout.session.completed", "invoice.paid"],
  "secret": "whsec_…",
  "secret_last4": "a91f",
  "created": 1767225600
}
```

- `secret` is returned **only** by create and `rotate_secret`. Store it — list and retrieve return `secret_last4` only.
- Omit `enabled_events` (or send `["*"]`) to receive every event.
- Only public `https` URLs are accepted; loopback and private network addresses are rejected with `400`.

## Rotating the secret

`POST /v1/webhook_endpoints/{id}/rotate_secret` returns a new secret and keeps the previous one valid for **24 hours**. During that window every delivery is signed with both secrets, so you can deploy the new secret without dropping events. See [Verify signatures](/docs/webhooks/signature-verification).

## Delivery log

```bash
curl "https://api.southbill.com/v1/webhook_endpoints/{id}/deliveries?status=failed&limit=20" \
  -H "Authorization: Bearer sk_live_..."
```

Each entry contains `event`, `event_type`, `status` (`pending`, `delivered`, `failed`), `attempts`, `response_code`, `error` and `next_retry_at`. Failed deliveries are retried automatically with backoff; to push an event again yourself use `POST /v1/events/{id}/replay`.

## Rate limits

300 reads/min and 60 writes/min per API key, like the rest of the API.

