Skip to content

Routing strategies overview

import { Aside } from ‘@astrojs/starlight/components’

When a webhook arrives at your Hookman endpoint, the proxy needs to decide which deployment to forward it to. This decision is called routing.

A project’s routing is a list of rules, each with a condition that reads part of the request. Hookman evaluates them in two tiers, by priority (lower first) within each tier:

Tier 1 Dynamic rules (registered by the CLI / GitHub Action)
Tier 2 Manual rules (created in the dashboard)
↓ no rule matched
Active deployment (the default target)
+ fan-out copies (mirrored to any fan-out deployments)

A rule’s condition reads a header, a query param, or a JSON body field and matches it against a value. The first rule that matches wins, and the request goes only to that rule’s deployment. If no rule matches, the request falls through to the active deployment (plus fan-out — see below). If no rule matches and there’s no active deployment, Hookman returns 422 no_route_resolved.

Configuring routing

Set up routing in your project settings (dashboard or API). You can configure multiple strategies simultaneously — they’re evaluated in the order above.

Project settings → Routing
☐ Routing header: x-hookman-branch
☐ Query parameter: branch
☐ Payload path: metadata.branch

Leave all three blank to use manual switch only.

Strategy comparison

HeaderQuery paramPayloadManual switch
Requires body parseNoNoYesNo
Caller must include routing keyYesYesYesNo
Works with any providerDependsDependsDependsYes
Fully automaticYesYesYesNo

Manual switch

The simplest strategy. You explicitly set which deployment receives webhooks by marking it as active. No routing key needed from the caller.

Good for: solo developers, prototyping, any situation where you want direct control.

Manual switch documentation →

Header routing

Hookman reads a specific request header and matches its value against your deployment labels.

Incoming header: x-hookman-branch: feature/checkout
Deployment label: feature/checkout
→ Routes to that deployment

Good for: internal tooling, platforms that let you inject custom headers, webhook forwarders.

Header routing documentation →

Payload routing

Hookman parses the JSON body and reads a value at a dot-notation path.

// Incoming body
{ "metadata": { "branch": "feature/checkout" } }
// Config: payload path = "metadata.branch"
// Resolved value: "feature/checkout"
// → Routes to matching deployment

Good for: platforms that include environment metadata in their payload (some CI platforms, internal event buses, custom webhook senders).

Payload routing documentation →

Dynamic rules

Rules registered by the CLI or GitHub Action are dynamic rules. They behave like any other rule but:

  • They’re evaluated first (Tier 1), before any rules you create in the dashboard.
  • They go live in KV immediately on registration — they don’t sit in a draft waiting for you to publish.

This is what makes hookman register from CI safe and instant: a preview branch starts receiving its routed webhooks the moment the action runs.

Fall-through and the default

Rules are tried in order. A rule that matches forwards the request to its deployment and stops. A rule that doesn’t match is simply skipped — Hookman moves to the next rule.

If no rule matches, the request goes to the active deployment (the default). If there’s no active deployment either, Hookman returns 422 no_route_resolved — the response includes a trace showing how each rule was evaluated.

Fan-out

Deployments can be marked fan-out. When a webhook falls through to the default (active) deployment, Hookman also mirrors a copy to every fan-out deployment.

no rule matched
→ active deployment (main)
+ fan-out deployment (staging)
+ fan-out deployment (teammate's branch)

By default fan-out applies to the fall-through only — a webhook that matched a rule goes only to that rule’s deployment, and fan-out is skipped.

Individual rules can opt back in. Each rule has a fan-out toggle (the checkbox on the rule form, fanout on the API) meaning when this rule matches, still copy to the fan-out deployments as well as my own target:

matched a rule with fan-out off
→ that rule's deployment only
matched a rule with fan-out on
→ that rule's deployment (pr-42)
+ fan-out deployment (staging)

A deployment that is both the resolved target and marked fan-out receives one copy, not two. Each fan-out delivery is logged and counts toward your monthly quota.