Webhook ingress.
The way in for anything that already speaks webhook — Stripe, GitHub, Calendly, your own backend. The URL is the source: a unique token per source, verified at the gate before the proxy touches storage.
URL shape
One per source.
# Each webhook source has its own URL, of the form: https://in.ingestlayer.com/whk/<token> # the token is opaque, rotatable, and unique per source. # you find it under the source's row in /sources.
Webhook sources are created automatically for each org at bootstrap; additional ones can be created at /sources/new with the webhook.in kind. The token is regeneratable from the source detail page — old token stops working immediately.
Auth modes
HMAC, bearer, or open.
Each source declares one auth mode in its config. The proxy rejects every request that doesn't satisfy it.
signed · HMAC-SHA256 (default)
# verify with HMAC-SHA256 over the raw request body # signing_secret is shown once when you create the source X-Ingest-Signature: sha256=<hex> # the proxy: # 1. computes hmac(body, signing_secret) # 2. timing-safe compares against the header # 3. accepts on match, 401s on mismatch
bearer · static token
# alternative: simple bearer token Authorization: Bearer <bearer_token>
Useful for senders that don't support HMAC signing. Rotate the token if you suspect it leaked — same effect as rotating an API key.
none · open
Testing only. Anyone who knows the token can post. Useful for getting started, not for production traffic.
Event shape
Body in, event out.
# the request body becomes the event payload as-is.
# if it has a string "type" field, that becomes event.type.
# otherwise the proxy stamps a default: "<source-kind>.received"
$ curl -X POST https://in.ingestlayer.com/whk/$TOKEN \
-H "Authorization: Bearer $BEARER" \
-H "Content-Type: application/json" \
-d '{
"type": "github.push",
"ref": "refs/heads/main",
"head_commit": { "id": "abc123", "message": "ship it" }
}'Once auth passes, the webhook is rate-limited per source so one bursting endpoint doesn't starve the others. The event's type comes from the body's type field if it's a string, otherwise we stamp <source-kind>.received (e.g. stripe.received). The source is attached to the event automatically. A 202 in response means it's durable.
Idempotency
Same key as the SDK.
Pass X-Ingest-Idempotency-Key in the request headers. Same dedupe semantics as the SDK: identical key within the window is a no-op (returns 202 without writing a second event row).