Skip to content

A gateway is the route from one ingest URL to one endpoint on your side. It is made of two parts, and you configure both in the same flow:

  • a destination — the endpoint that receives events: its URL, headers, authentication, timeout and retry schedule,
  • a connection — the link between a source and that destination, plus the optional filter and transformation applied on the way.

One source can feed several gateways, and one destination can be reused by several sources. Every event that passes the filter is delivered to every connected destination independently: one slow endpoint never holds up another.

  • A source with its ingest URL, and at least one event in its log. Delivering an event you can see is far easier to debug than delivering a hypothetical one.
  • A publicly reachable HTTPS endpoint on your side. Private and internal addresses are rejected when you save the destination — localhost, *.local, *.internal and private IP ranges. For local development use the CLI instead.

Open Sources, click the source, and select the Destinations tab. This is where the source’s gateways live. An empty tab has one button: Add destination.

The Destinations tab of a sourceThe Destinations tab of a source
A source's gateways: each row is one destination, its URL and the state of its circuit breaker.

The Add destination dialog has two tabs:

  • Create new — define a fresh endpoint. The dialog opens here while the workspace has no endpoint left to reuse, which is the case the first time.
  • Connect existing — reuse an endpoint the workspace already has, so two sources feed the same handler without duplicating its settings. Once at least one endpoint exists, the dialog opens on this tab.

On Create new:

  • Name — required. What you will see in the event log and in alerts. billing-api, orders-worker, slack-relay.
  • URL — required, absolute, http or https. This is where Webhooker POSTs each event.
  • Response timeout (seconds) — how long Webhooker waits for your response before the attempt counts as failed. Default 10 seconds, allowed 1 to 300. Answer fast and do the work asynchronously: a timeout is a failure, and a failure costs you a retry.

Sign outbound requests decides how your endpoint can tell that a request really came from Webhooker and not from anyone who found the URL.

MethodWhat Webhooker sendsWhen to use it
OffNothing.Only if the endpoint is already protected some other way.
HMAC signatureX-Webhooker-Signature, X-Webhooker-Timestamp, X-Webhooker-Event-Id.The default choice. Your handler recomputes the signature.
API KeyA fixed secret in the header you name, X-Api-Key by default.Your endpoint already authenticates by header.
Basic AuthAuthorization: Basic ….Your endpoint is behind basic auth.

Pick HMAC signature, generate a long random secret, paste it into Signing secret, and keep a copy — after saving, the field shows *** and the value is never returned to the browser. The verification code for your handler is in verify our signature.

Authentication headers are applied last, after any transformation, so a transformation can never overwrite or strip them.

Save. The source’s Destinations tab now lists the endpoint, and the connection between the two is created with it. The gateway is live: the next event that arrives is forwarded.

5. Tune retries and fixed headers (API only)

Section titled “5. Tune retries and fixed headers (API only)”

Two destination settings have no field in the dashboard yet and are set through the management API:

  • retry_policy — the backoff schedule as a list of delays. The default is 30s, 2m, 10m, 1h, 4h: five retries after the first attempt, spanning about five and a half hours. Up to 10 intervals, each between 1 second and 24 hours. Every delay gets ±20% jitter, so a destination that comes back up is not hit by all queued retries in the same instant.
  • custom_headers — headers sent verbatim on every delivery to this destination, for routing or tenancy hints such as X-Environment: production. Do not put secrets here; that is what authentication is for.
Terminal window
curl -X PATCH https://app.webhooker.eu/api/v1/destinations/$DESTINATION_ID \
-H "Authorization: Bearer $WEBHOOKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"retry_policy": { "intervals_seconds": [10, 60, 300, 1800] },
"custom_headers": { "X-Environment": "production" }
}'

Send a test event from the provider, or replay one you already have from the event log. Open the event and look at its delivery row:

StatusMeaning
pendingQueued, waiting for its attempt.
deliveringAn attempt is in flight.
succeededYour endpoint answered 2xx.
failedYour endpoint answered a 4xx other than 408 or 429. Not retried.
exhaustedEvery retry in the schedule failed.
filteredThe connection’s filter did not match, so nothing was sent.

Expand the row for the attempt history: attempt number, target URL, response status, response body, error message and latency for each try. This is the screen to open when a customer says “the webhook never arrived”.

7. Filter what this gateway receives (optional)

Section titled “7. Filter what this gateway receives (optional)”

By default a gateway receives every event the source accepts. To narrow it, open the Edit routing action on the row in the Destinations tab and add Filter rules: a field path, an operator and a value per row.

The connection routing screen with filter rulesThe connection routing screen with filter rules
Filter rules and header transforms are configured per connection, so the same destination can behave differently per source.

A rule addresses one value in the request by path. Two roots are available: body. for the JSON payload and headers. for the inbound headers.

{
"operator": "and",
"rules": [
{ "path": "body.type", "op": "eq", "value": "invoice.paid" },
{ "path": "body.data.object.amount", "op": "gt", "value": 5000 }
]
}

Operators: eq, ne, contains, exists, gt, lt, in. Groups combine with and or or and can be nested one level deep.

An event that does not match is recorded as filtered on that connection: it is visible in the log and never sent. Other connections on the same source are unaffected.

The same editor holds the transformation, applied per gateway just before delivery:

  • Set headers — add or overwrite a header. Values support the placeholders {{event.id}}, {{source.name}} and {{timestamp}}.
  • Rename headers — rename a header that is already being sent, keeping its value.
  • Remove headers — a comma-separated list of names to strip from the outbound request.
  • Merge body fields — JSON object of top-level fields added to a JSON body. Existing fields with the same name are overwritten; the rest of the payload is untouched.

A non-JSON body is forwarded byte for byte; header rules still apply.

Repeat from step 2 to add another destination to the same source. Each gateway keeps its own filter, transformation, signing secret, timeout, retry schedule and delivery history.

To feed an existing endpoint from a second source, use Connect existing on that source instead of recreating it. A source and a destination can only be connected once; a second attempt is rejected as a conflict.

The toggle on each row enables or disables a gateway without deleting it: disabled means events are stored and simply not forwarded here.

Two safety nets are worth setting up now:

  • Alerts — email or Telegram on delivery failure, per source or workspace wide. See alerts.
  • The circuit breaker — after 5 consecutive retryable failures, the destination’s circuit opens and deliveries pause for a cooldown that starts at 60 seconds and doubles up to 10 minutes. One probe is then sent; if it fails, the circuit re-opens. The state is shown on the destination row as closed, open or half_open. This protects a struggling endpoint from the full retry load, and it clears itself as soon as one attempt succeeds.

Deliveries that end as exhausted or failed collect in the source’s Dead letters tab, where you can replay them in bulk once the endpoint is healthy.

SymptomCause
“url must not point to a private or internal address”The endpoint is not publicly reachable. Use the CLI for localhost.
Deliveries stay pendingThe source is paused, or the destination’s circuit is open.
Everything is filteredA filter path does not match the payload. Check a real body in the event log and remember the body. / headers. prefix.
failed on the first attempt, never retriedYour endpoint answered a 4xx. Only 408 and 429 are retried.
Signature check fails in your handlerSign {timestamp}.{raw body} and use the raw bytes, not a re-serialised object.
“re-enter the secret to change it”The masked *** placeholder was submitted. Type the secret in full.