# Routing And Destinations

Routing is where a single webhook becomes a small event gateway. Hooksbase keeps one default destination, lets you add named destinations, and then evaluates an ordered route-to-one rule set that chooses exactly one destination per accepted delivery.

## Routing model

The routing model is intentionally narrow:

- exactly one default destination per webhook
- optional named destinations for cutovers, canaries, or sink-type changes
- first enabled matching rule wins by ascending `priority`
- unmatched traffic falls back to the default destination
- no fan-out, no header mutation, and no URL rewriting from the routing layer

Accepted deliveries persist the resolved destination snapshot. Retries, replay, DLQ re-drive, and bulk replay keep using that saved destination config even after the live routing table changes.

## Auth model

- **Public API** `project API key`: Use `/v1/webhooks/{id}/destinations` and `/v1/webhooks/{id}/routing` for the canonical route and destination surface.
- **Dashboard** `session auth`: The [dashboard](/docs/dashboard.md) provides the same destination and routing controls as a UI — add destinations, reorder rules, and preview matches from the browser.
- **SDK / CLI**: Routing and destination management are not wrapped by the first-party SDK or CLI. Use raw HTTP for this surface.

## Destinations

Every webhook starts with one default destination. The current destination types are:

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

Operational rules:

- the default destination cannot be deleted
- deleting a destination that is still referenced by a routing rule returns `409`
- non-webhook destination types are Pro+ features
- paused or missing resolved destinations fail ingest admission with `400`

**Replace the routing table**

```bash
curl https://api.hooksbase.com/v1/webhooks/wh_123/routing \
  -X PUT \
  -H "Authorization: Bearer swk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "defaultDestinationId": "dest_default",
    "rules": [
      {
        "name": "priority-orders",
        "enabled": true,
        "priority": 1,
        "matchMode": "all",
        "destinationId": "dest_orders",
        "conditions": [
          { "source": "headers.x-tenant", "operator": "eq", "value": "acme" },
          { "source": "payload.kind", "operator": "eq", "value": "order.created" }
        ]
      }
    ]
  }'
```

## Route-to-one rules

Each rule compares condition `source`s against literal values. The supported sources are:

- `contentType` — the request's `Content-Type` header
- `headers.<name>` — any inbound header, lower-cased
- `payload.<path>` — dot-path into parsed JSON, including numeric array indexes (e.g. `payload.items.0.sku`)
- `provider.name`, `provider.sourceId`, `provider.eventType`, and `provider.verified` — normalized metadata from a configured provider pack

Supported operators:

- `eq`, `neq` — strict equality
- `contains`, `starts_with` — substring on strings
- `exists` — value is present (no `value` needed)
- `in` — value appears in the array supplied as `value`
- `gt`, `gte`, `lt`, `lte` — numeric comparison

Other limits that matter:

- up to `200` rules per webhook
- up to `25` conditions per rule
- `matchMode` is `all` (AND) or `any` (OR)
- omitted priorities default from submitted array order, but final priorities must still be unique

Routing always evaluates the original source payload before any [payload transform](/docs/transforms.md) runs. If a rule references `payload.<path>` and the body is not JSON, the rule simply does not match.

## Related routes

- `GET /v1/webhooks/{id}/destinations`
- `POST /v1/webhooks/{id}/destinations`
- `PATCH /v1/webhooks/{id}/destinations/{destinationId}`
- `DELETE /v1/webhooks/{id}/destinations/{destinationId}`
- `GET /v1/webhooks/{id}/routing`
- `PUT /v1/webhooks/{id}/routing`

## Common mistakes

- Expecting routing to fan out to multiple destinations. Routing is route-to-one only.
- Forgetting that the first matching enabled rule wins, even if a later rule is more specific.
- Assuming replay will use today's routing table instead of the saved destination snapshot.
- Pointing destinations back at Hooksbase ingest or form endpoints instead of a real downstream target.
