# Event Drains

Event drains stream delivery-lifecycle events out of Hooksbase into external observability or journaling systems. Use them when you need a near-real-time feed of delivery state changes outside the dashboard itself.

**Available through**

Event drain management is dashboard-first, with a low-level project-authenticated route family available for raw HTTP automation.

| Surface | Status | Notes |
| --- | --- | --- |
| [Dashboard](/docs/dashboard.md) | Preferred | Create, inspect, pause, resume, edit, and delete drains from project settings. |
| [Public API](/docs/api-reference.md#event-drains) | Raw HTTP | Use project API keys against /v1/app/event-drains when you need automation. |
| [TypeScript SDK](/docs/sdks.md) | Not wrapped | The SDK does not expose drain management. |
| [CLI](/docs/cli.md) | Not wrapped | The CLI does not expose drain management. |

## Drain model

Drains subscribe to delivery lifecycle events, not arbitrary webhook payloads. The current event types are:

- `delivery.created`
- `attempt.completed`
- `delivery.succeeded`
- `delivery.failed`
- `delivery.dlq_entered`

Tiering:

- Free and Starter cannot create drains
- Pro allows one drain
- Business allows three drains
- Enterprise is effectively unbounded

You can optionally scope a drain to a subset of webhook IDs instead of the whole project.

## Where this lives

Event drain management is dashboard-first, and the dashboard is the preferred surface. Open the event drains section from project settings to:

- create a drain (pick a sink type, provide credentials, choose event types, optionally scope to specific webhooks)
- inspect the drain's current status, recent failure count, and last error
- pause or resume a drain
- edit sink configuration and credentials
- delete a drain

There is also a low-level project-authenticated route family under `/v1/app/event-drains`. It is not wrapped by the SDK or CLI and is intended for raw HTTP automation when the dashboard is not the right surface.

## Sink types and events

Supported sink types:

- `webhook` — POST each event to an HTTPS URL, optionally signed
- `axiom` — forward events to an Axiom dataset
- `datadog` — forward events to Datadog Logs
- `object_storage` — write one JSON object per event to S3-compatible storage
- `otlp_http` — emit events as OpenTelemetry Logs over OTLP/HTTP JSON

Behavior notes:

- webhook drains can include an optional signing secret; Hooksbase sends the HMAC in `x-signature-sha256`
- object-storage drains write one JSON object per drain event
- OTLP HTTP drains are logs-only, JSON-only, and do not batch or backfill

Because drains are delivery-lifecycle observers, they pair naturally with [operator alerting](/docs/operator-alerting.md): drains give you raw event streams, while operator alerting gives you customer-facing incident state and routing.

## Raw HTTP examples

Create, patch, and resume require Pro+. List, get, pause, and delete are still project-authenticated, but lower-tier projects do not process active drains.

**Create a webhook drain**

```bash
curl https://api.hooksbase.com/v1/app/event-drains \
  -H "Authorization: Bearer swk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Delivery stream",
    "sinkType": "webhook",
    "sinkConfig": {
      "url": "https://observability.example.com/hooksbase",
      "signingSecret": "drain-secret"
    },
    "eventTypes": [
      "delivery.created",
      "attempt.completed",
      "delivery.failed"
    ],
    "filterWebhookIds": ["wh_123"]
  }'
```

**201 response**

```json
{
  "id": "drain_123",
  "projectId": "proj_123",
  "name": "Delivery stream",
  "status": "active",
  "sinkType": "webhook",
  "eventTypes": [
    "delivery.created",
    "attempt.completed",
    "delivery.failed"
  ],
  "filterWebhookIds": ["wh_123"],
  "consecutiveFailures": 0,
  "lastSuccessAt": null,
  "lastFailureAt": null,
  "createdAt": 1766600000000,
  "updatedAt": 1766600000000
}
```

**Patch event types or sink config**

```bash
curl -X PATCH https://api.hooksbase.com/v1/app/event-drains/drain_123 \
  -H "Authorization: Bearer swk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "eventTypes": [
      "delivery.created",
      "attempt.completed",
      "delivery.succeeded",
      "delivery.failed",
      "delivery.dlq_entered"
    ],
    "sinkConfig": {
      "url": "https://observability.example.com/hooksbase-v2"
    }
  }'
```

**Pause, resume, and delete**

```bash
curl -X POST https://api.hooksbase.com/v1/app/event-drains/drain_123/pause \
  -H "Authorization: Bearer swk_..."

curl -X POST https://api.hooksbase.com/v1/app/event-drains/drain_123/resume \
  -H "Authorization: Bearer swk_..."

curl -X DELETE https://api.hooksbase.com/v1/app/event-drains/drain_123 \
  -H "Authorization: Bearer swk_..."
```

See [raw HTTP examples](/docs/raw-http-examples.md#event-drains) for the same route family alongside the other raw-only public routes.

## Patch and failure behavior

When editing an existing drain, the dashboard uses merge semantics for credentials:

- leaving object-storage credentials blank keeps the existing encrypted values
- optional object-storage fields can be cleared with an explicit empty value
- leaving OTLP headers blank keeps the saved headers
- clearing OTLP headers is an explicit action in the UI

Failure behavior:

- a drain moves to `failed` after `10` consecutive failures
- resume returns it to `active` and resets the consecutive-failure counter
- paused or failed drains can still be inspected, edited, resumed, or deleted
- drain failures can surface into operator issues such as `drain_paused` and `drain_degraded`

## Common mistakes

- Assuming drains stream original inbound payloads instead of delivery lifecycle events.
- Assuming event drains are wrapped by the SDK or CLI. Use the dashboard or raw HTTP against `/v1/app/event-drains`.
- Forgetting that editing a drain's credentials preserves saved values unless you explicitly clear them.
- Expecting drains to replace [operator alerting](/docs/operator-alerting.md). They serve different operator workflows.
- Treating the dashboard's browser traffic as an integration contract.
