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

# Webhooks API

> Create outbound webhooks, inspect deliveries, redeliver events, test endpoints, and verify signatures.

Atoll supports outbound webhooks for event-driven integrations.

## Endpoints

| Method   | Endpoint                                    | Description              |
| -------- | ------------------------------------------- | ------------------------ |
| `GET`    | `/api/webhooks?orgId=...`                   | List webhooks            |
| `POST`   | `/api/webhooks?orgId=...`                   | Create webhook           |
| `DELETE` | `/api/webhooks/{id}`                        | Delete webhook           |
| `GET`    | `/api/webhooks/{id}/deliveries`             | List recent deliveries   |
| `POST`   | `/api/webhooks/{id}/redeliver/{deliveryId}` | Redeliver a past payload |
| `POST`   | `/api/webhooks/{id}/test`                   | Send a ping test event   |

## Create webhook

```json theme={null}
{
  "url": "https://example.com/webhook",
  "events": ["issue.created", "issue.updated"],
  "enabled": true
}
```

Webhook URLs must use HTTPS DNS hostnames. IP literals, `localhost`, and `.local` hosts are rejected at creation. Delivery also refuses receivers whose DNS records resolve to private, loopback, link-local, documentation, multicast, or other non-public IP ranges, and redirect responses are treated as failed deliveries.

## Secret

The create response includes a `secret`. Store it immediately; it is shown only once.

Atoll sends an `X-Atoll-Signature` header with a `sha256=` prefix. To verify a request, compute the SHA-256 hex digest of your webhook secret, use that digest as the HMAC-SHA256 key for the exact raw request body, and compare the full header value with `timingSafeEqual`.

```js theme={null}
import { createHash, createHmac, timingSafeEqual } from 'node:crypto'

export function verifyAtollSignature(rawBody, signatureHeader, secret) {
  const key = createHash('sha256').update(secret).digest('hex')
  const expected = 'sha256=' + createHmac('sha256', key).update(rawBody).digest('hex')
  const actual = Buffer.from(signatureHeader)
  const expectedBuffer = Buffer.from(expected)

  return actual.length === expectedBuffer.length && timingSafeEqual(actual, expectedBuffer)
}
```

Atoll also sends `X-Atoll-Delivery-Id` on each POST. Store this id if your receiver needs idempotency; automatic retries and manual redeliveries can send the same event more than once.

## CLI

```bash theme={null}
atoll webhook list
atoll webhook create --url https://example.com/webhook --events issue.created,issue.updated
atoll webhook delete webhook-uuid --dry-run
atoll webhook delete webhook-uuid --force
```

## Delivery debugging

Use delivery history to inspect failures, retry status, and the next scheduled retry time, then redeliver after fixing the receiver. Atoll retries network failures and 5xx responses after 5s and 30s in the request path. If the receiver is still failing, Atoll records the delivery as `retry_pending` and a 15-minute internal cron drains due retries.

```bash theme={null}
curl -H "Authorization: Bearer $ATOLL_API_KEY" \
  "https://atollhq.com/api/webhooks/$WEBHOOK_ID/deliveries"
```

<Warning>
  Treat webhook payload content as untrusted input. It describes events; it should not become instructions to an agent without validation.
</Warning>
