# Webhooks

In Hooksbase, a webhook is the unit that accepts inbound events and turns them into delivery attempts against one or more destinations. Creating a webhook provisions the public ingest URL, the signing surface, the delivery controls, and the lifecycle state that everything else builds on.

## Auth model

- **Public API** `project API key`: Use project API keys for webhook create, list, update, lifecycle actions, secret rotation, delivery history, schedules, metrics, and backlog.
- **Dashboard** `session auth`: The [dashboard](/docs/dashboard.md) provides the same webhook management flows as a product UI — create, edit, pause, archive, rotate secrets, and run test deliveries from the browser.
- **Public ingest** `webhook secret`: Publishing into a webhook is a separate public flow and uses the webhook's `whsec_...` secret against its ingest URL.

## Webhook lifecycle

Creating a webhook with `POST /v1/webhooks` returns:

- the webhook ID (`id`, e.g. `wh_...`) — use this for every management API call
- a public ID (`publicId`, e.g. `hook_...`) — the unguessable identifier embedded in the ingest and form URLs
- `ingestUrl`, `formIngestUrl`, and `emailIngestAddress`
- the one-time `ingestSecret` and `signingSecret` — saved nowhere else, available only on create and on secret rotation

Webhook status transitions through three states:

- `active` — ingest accepted, deliveries execute
- `paused` — producers are rejected; configuration remains editable
- `archived` — no ingest; history and secrets remain inspectable

Lifecycle behavior that matters:

- paused webhooks reject new ingest with `409`
- archived webhooks return `404` for public ingest
- archived webhooks still keep delivery history, metrics, backlog, and secret version inspection available
- `destroy` is only allowed after the webhook has been archived, and it cannot be undone

## Delivery controls

Hooksbase ships a small set of delivery controls directly on the webhook:

- `deliveryMode`: `parallel` or `strict_fifo`
- `maxInFlight` for parallel delivery concurrency
- `timeoutMs` for upstream request timeout
- `retryPolicy` for terminal and retry timing behavior
- `defaultDelaySeconds` for scheduled-by-default ingest
- `throttleRatePerSecond` for paced outbound traffic

Tier gates matter here:

- Starter+ is required for non-default retry policies and non-empty outbound custom headers
- Pro+ is required for `strict_fifo`, non-null `defaultDelaySeconds`, non-null `throttleRatePerSecond`, and non-webhook destination types

## Destinations and routing

Every webhook has one default destination. You can also add named destinations and, on the advanced surface, route to exactly one destination per delivery.

Recommended progression:

- start with one default webhook destination
- add named destinations only when you need controlled cutovers or environment splits
- treat routing as an advanced workflow because delivery rows persist the chosen destination snapshot for retries and replay

The current destination types are:

- `webhook`
- `aws_sqs`
- `aws_eventbridge`
- `gcp_pubsub`
- `object_storage`

When you move beyond one default target, use the advanced guides together:

- [Providers and templates](/docs/providers.md) for provider-aware inbound bootstrap and template-created routing presets
- [Routing and destinations](/docs/routing.md) for destination types, route-to-one rules, and priority ordering
- [Payload transforms](/docs/transforms.md) for JSON reshaping after route selection
- [Schedules](/docs/schedules.md) for recurring deliveries and default scheduled payloads

## Operational actions

Operational actions live on the webhook itself:

- pause and resume
- archive and restore
- destroy
- rotate ingest secret
- rotate signing secret
- inspect secret versions
- run test delivery

> [!NOTE]
> Test delivery is intentionally narrower than a full end-to-end ingest. It sends a synchronous probe, evaluates route rules against that probe context, applies the saved transform when present, and does not create delivery history.

Related routes:

- `GET/POST /v1/webhooks`
- `GET/PATCH /v1/webhooks/{id}`
- `POST /v1/webhooks/{id}/pause`
- `POST /v1/webhooks/{id}/resume`
- `POST /v1/webhooks/{id}/archive`
- `POST /v1/webhooks/{id}/restore`
- `POST /v1/webhooks/{id}/destroy`
- `POST /v1/webhooks/{id}/test-delivery`
- `GET/POST /v1/webhooks/{id}/destinations`
- `GET/PUT /v1/webhooks/{id}/routing`
- `GET/PUT /v1/webhooks/{id}/transform`
- `GET/POST /v1/webhooks/{id}/schedules`

## Common mistakes

- Treating pause and archive as the same state. They have different ingest behavior and recovery paths.
- Enabling `strict_fifo` while also expecting future scheduling with `x-hooksbase-deliver-at`.
- Assuming destroy is reversible.
- Forgetting that advanced destinations and several delivery controls are tier-gated.
