Receive a GitHub webhook
A complete inbound walkthrough: take GitHub repository or organization webhooks, verify the X-Hub-Signature-256 HMAC, and fan them out to your API and a queue — losing nothing if your service is down.
What you'll build
GitHub ──▶ <ingest-domain>/github-org/events ──▶ ├─ HTTPS https://api.acme.in/ci
(acked + buffered <100ms; signature verified in processing) └─ SQS acme-ci-q1. Create the endpoint
Use the GitHub preset — it configures the X-Hub-Signature-256 HMAC-SHA256 check GitHub signs with and the fast 2xx ack GitHub expects.
curl -X POST https://api.emithook.com/v1/endpoints \
-H "Authorization: Bearer $EK_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://wh.emhk.in/acme/github-org/events",
"preset": "github",
"destinations": ["dst_acme_https", "dst_acme_sqs"]
}'// → 201 Created
{
"id": "ep_01JX9...",
"url": "https://<ingest-domain>/github-org/events",
"verification": "github",
"status": "active"
}TIP
The URL is live immediately on the shared ingest domain (<ingest-domain>, config-driven) — no DNS. Add it under Repository (or Organization) → Settings → Webhooks → Add webhook, content type application/json.
2. Add the webhook secret
Set a Secret on the GitHub webhook form and paste the same value here so Emithook can verify the X-Hub-Signature-256 header:
curl -X PUT https://api.emithook.com/v1/endpoints/ep_01JX9.../secret \
-H "Authorization: Bearer $EK_KEY" \
-d '{ "secret": "your-github-webhook-secret" }'GitHub signs the raw body with HMAC-SHA256 (hex) and sends it as X-Hub-Signature-256: sha256=<hex> — the same scheme Meta uses, so Emithook runs the one shared check. It recomputes the signature over the raw body and compares in constant time. The edge accepts and durably buffers every request in <100 ms (never dropped); verification then runs in the processing plane. A request that fails verification is quarantined — durable and inspectable, but never delivered (not a 401 at the edge, never silently dropped).
3. What an incoming request looks like
POST /github-org/events HTTP/1.1
Host: <ingest-domain>
X-GitHub-Event: push
X-Hub-Signature-256: sha256=7d38cd...
Content-Type: application/json
{ "ref": "refs/heads/main", "repository": { "full_name": "acme/api" }, "pusher": { "name": "dev" } }HTTP/1.1 200 OK
{ "received": true }Emithook acks in under 100 ms, then fans the event out to both destinations independently — each signed, retried, and logged on its own.
4. Confirm delivery
emithook logs tail --endpoint /github-org/events12:04:31 evt_01JX… push → dst_acme_https 200 142ms ✓
12:04:31 evt_01JX… push → dst_acme_sqs enqueued ✓If api.acme.in is down, Emithook retries with backoff and parks events behind the circuit breaker — when it recovers, they drain automatically. Nothing is lost.
5. Replay if needed
# replay everything that dead-lettered for this endpoint
emithook replay --dlq --endpoint /github-org/events