Destinations

Updated May 02, 2026

What is a destination?

A destination is anywhere a captured webhook gets forwarded — your local handler, a staging server, a queue, another tool. Each endpoint fans out to as many destinations as your plan allows, independently.

Destinations are independent. If one returns 500, the others still receive the webhook. Splithook never short-circuits fan-out on a single failure.

Creating a destination

From an endpoint's page: Add destination → choose HTTP or Tunnel → configure → Save.

Plan limits:

Plan Max destinations per endpoint
Free 1
Pro 5
Team 25

Destination types

HTTP destination

A plain HTTPS URL. Splithook POSTs the webhook body with the same headers the provider sent, plus optional signing headers.

https://staging.acme.dev/webhooks/stripe

HTTP destinations require HTTPS unless your workspace has allow_http_destinations enabled (only available in dev/self-hosted configurations).

Tunnel destination

Connects to a running splithook tunnel CLI process. The URL field takes a local address:

http://localhost:3000/webhooks/stripe

When you save a tunnel destination, Splithook issues a tunnel token (sht_…). You see it once — copy it before closing the dialog. The CLI uses this token to authenticate the Mercure subscription.

Filters

A filter is an ExpressionLanguage expression evaluated against every incoming webhook. Only webhooks where the expression is true are forwarded. Default: no filter (forward everything).

provider == 'stripe' and body.type matches '^charge\.'

Variables available in filter expressions:

Variable Type Example
provider string 'stripe', 'github', 'unknown'
event_type string 'charge.failed', 'push'
headers map headers['x-github-event']
body map body.data.object.amount
method string 'POST'

The filter is evaluated asynchronously in the forwarding worker — it does not block the capture response.

See Filters & expressions for the full operator reference.

Signing modes

Mode What Splithook sends When to use
passthrough Original signature header, byte-for-byte Live traffic, same secret as provider
strip No signature header Internal services, analytics workers
re_sign Fresh signature with your dev secret Replays, staging, local development

To use re_sign, attach a Signing secret to the destination. Go to Settings → Signing secrets → New, then select it in the destination editor.

Re-signing uses your secret, not the provider's

Upload the secret your staging handler verifies with — not the one Stripe/GitHub configured on their end. These are different keys.

Retry policy

On failure (5xx or connection error), Splithook retries with exponential backoff:

Attempt Delay
1st retry 30s
2nd retry 5m
3rd retry 30m

After 3 failed attempts the delivery moves to dead-letter. You can replay dead-lettered deliveries manually from the Replay Log, or in bulk from the destination settings.

Every attempt — including retries — is logged in the ReplayLog with status, latency, and the first 4 KB of the response body.

Circuit breaker

If a destination's failure rate exceeds 90% over a 5-minute rolling window, the circuit opens automatically and delivery is paused. The dashboard shows the destination as circuit open. Fix the underlying issue, then manually resume from the destination settings.

Idempotency

Every delivery attempt sends the same splithook-delivery-id header. Use it to deduplicate in your handler:

splithook-delivery-id: dlv_01j3k9...
splithook-attempt: 2
splithook-endpoint: ab3dkf7z

Toggling a destination

Pause/resume a destination from its settings without deleting it. Paused destinations accumulate no dead-letter entries — events are simply skipped.

Rotating the tunnel token

If a tunnel token is leaked: destination settings → Rotate token. The old token is invalidated immediately; any connected CLI process disconnects and must re-authenticate with the new token.

Deleting a destination

Deletes the destination and all its ReplayLog entries. Captured webhooks on the parent endpoint are unaffected.