Appearance
Webhook Ingest
About 484 wordsAbout 2 min
The ingest endpoint accepts raw webhooks from any provider. The source's path_token acts as authentication — no Bearer header is required.
Receive a webhook
POST /ingest/{token}Authentication: {token} path parameter (the source's path_token, prefixed src_)
Max body size: 5 MB
Path parameters
| Parameter | Type | Description |
|---|---|---|
token | string | The source's path_token (e.g. src_a1b2c3d4e5f6) |
Request headers (optional)
| Header | Description |
|---|---|
Content-Type | Passed through to the destination |
X-Event-Type | Event type tag (e.g. payment.succeeded). Used for route matching. |
X-Webhook-Id | Idempotency key. Duplicate IDs are silently accepted without re-queuing. |
X-Event-Id | Alternative idempotency key |
Idempotency-Key | Alternative idempotency key |
X-Request-Id | Alternative idempotency key |
Sensitive headers are stripped
Authorization and Cookie headers are never stored.
Request body
Any raw body is accepted — JSON, form-encoded, XML, plain text. GetHook treats it as an opaque bytes and persists it as-is.
Response 202 Accepted
{
"data": {
"id": "e1e2e3e4-e5f6-7890-abcd-ef1234567890",
"status": "queued"
}
}Duplicate detection
If the same X-Webhook-Id (or X-Event-Id / Idempotency-Key) is sent twice, the second request returns:
{
"data": {
"status": "accepted",
"note": "duplicate"
}
}This is still a 202 — idempotent behavior.
Errors
| Status | Cause |
|---|---|
404 | Token not found or source is inactive |
401 | Signature verification failed (when auth_mode: "shared_secret") |
413 | Body exceeds 5 MB limit |
curl examples
# Basic ingest
curl -s -X POST http://localhost:8080/ingest/src_a1b2c3d4e5f6 \
-H "Content-Type: application/json" \
-d '{"type": "payment.succeeded", "amount": 2000}' | jq .# With event type tag (enables route filtering)
curl -s -X POST http://localhost:8080/ingest/src_a1b2c3d4e5f6 \
-H "Content-Type: application/json" \
-H "X-Event-Type: payment.succeeded" \
-d '{"id": "evt_abc123", "amount": 2000}' | jq .# With idempotency key (safe to retry on network error)
curl -s -X POST http://localhost:8080/ingest/src_a1b2c3d4e5f6 \
-H "Content-Type: application/json" \
-H "X-Webhook-Id: evt_stripe_abc123" \
-d '{"id": "evt_stripe_abc123", "type": "charge.succeeded"}' | jq .# Simulating a Stripe webhook (with signature header)
STRIPE_SECRET="whsec_your_stripe_signing_secret"
TIMESTAMP=$(date +%s)
PAYLOAD='{"id":"evt_test","type":"payment_intent.succeeded"}'
SIG="t=${TIMESTAMP},v1=$(echo -n "${TIMESTAMP}.${PAYLOAD}" | openssl dgst -sha256 -hmac "$STRIPE_SECRET" -binary | xxd -p -c 256)"
curl -s -X POST http://localhost:8080/ingest/src_a1b2c3d4e5f6 \
-H "Content-Type: application/json" \
-H "Stripe-Signature: $SIG" \
-d "$PAYLOAD" | jq .Event lifecycle after ingest
POST /ingest/{token}
→ Event created with status: queued
→ Worker polls (every 5s by default)
→ status: delivering
→ HTTP POST to destination
→ status: delivered (success)
or: retry_scheduled (HTTP 5xx / timeout)
→ retry after backoff
→ ... up to 5 attempts
→ status: dead_letter (all attempts failed)Configuring providers
Stripe
Point your Stripe webhook endpoint to:
https://your-domain/ingest/src_your_tokenIn Stripe Dashboard → Developers → Webhooks → Add endpoint. GetHook verifies the Stripe-Signature header when auth_mode: "shared_secret" is configured.
GitHub
https://your-domain/ingest/src_your_tokenContent type: application/json. Add a secret in GitHub's webhook settings to match verification_config.
Generic provider
Any provider that sends HTTP POST webhooks is compatible. Set X-Event-Type or map it in your route's event_type_pattern.