Source · GitHub · Verified November 1, 2026
GitHub webhook source guide
Short answer: A Hooks GitHub endpoint verifies the X-Hub-Signature-256 HMAC on the raw body, stores the delivery, and normalizes each webhook into <event>.<action> (for example issues.opened) before any destination filter runs.
GitHub is the source most agent workflows start with: labeled issues, @mention comments, and pull request events. Hooks keeps the GitHub signature scheme, the X-GitHub-Delivery id, and the repository full name, so filters and templates can match the same identifiers your repository already uses.
Set up the webhook in GitHub
- In the repository, open Settings → Webhooks → Add webhook and paste the endpoint's ingestion URL.
- Set Content type to application/json and paste the same signing secret the endpoint uses.
- Choose “Let me select individual events” and enable Issues, Issue comments, Pull requests, Pushes, Releases, and Workflow runs as needed.
- Open or label an issue to send the first delivery, then check the delivery log.
Signature verification
| Header | X-Hub-Signature-256: sha256=<hex> |
|---|---|
| Algorithm | HMAC-SHA256 over the raw request body, compared in constant time. |
| Replay window | No timestamp header, so there is no clock-window check. |
| Delivery id | X-GitHub-Delivery, deduplicated per endpoint. |
- The event type comes from X-GitHub-Event; the action comes from the body.
- application/x-www-form-urlencoded deliveries are supported: the adapter extracts the embedded
payload=JSON. pingdeliveries are acknowledged and stored but filtered out of delivery to targets.
Events Hooks normalizes
| event.type | subject | What it means |
|---|---|---|
issues.opened | issue | Issue created with title, body, URL, labels, and state. |
issues.labeled | issue | Label added; the label list is available for filtering. |
issues.closed | issue | Issue closed; state changes to closed. |
issue_comment.created | issue_comment | Issue conversation comment; pull request comments keep kind pull_request_comment. |
pull_request.opened | pull_request | Pull request opened; state becomes merged once merged. |
pull_request_review.submitted | pull_request_review | Review submitted on a pull request. |
push.pushed | push | Push received; the subject title is the head commit's first line. |
release.published | release | Release published with tag name and body. |
workflow_run.completed | workflow_run | Actions workflow run finished; status and conclusion are mapped. |
ping.received | ping | GitHub health check. Stored for audit but never forwarded. |
Normalized payload
Every adapter produces this shape before filters or templates run. The raw payload and sanitized headers stay attached for audit, but filters and templates read the normalized fields.
Normalized event
{
"id": "evt_01j9m5k3x8",
"source": "github",
"type": "issues.opened",
"action": "opened",
"deliveryId": "72d3162e-cc78-11e3-81ab-4c9367dc0958",
"occurredAt": "2026-11-01T08:12:44.000Z",
"actor": {
"login": "octocat",
"id": "583231",
"type": "User",
"association": "MEMBER"
},
"repo": "acme/app",
"subject": {
"kind": "issue",
"id": "2603006563",
"title": "Login fails on Safari",
"url": "https://github.com/acme/app/issues/7",
"body": "Signing in on Safari 18 returns a 500 after the redirect.",
"labels": ["bug", "agent"],
"state": "open"
}
}Filter and template examples
Filter: only labeled issues
{
"allow": [
{
"field": "event.type",
"op": "in",
"value": ["issues.opened", "issues.labeled"]
},
{ "field": "event.subject.labels", "op": "contains", "value": "agent" }
],
"senders": {
"allow": { "associations": ["OWNER", "MEMBER", "COLLABORATOR"] }
}
}Destination: Grok Bot task
{
"routineUrl": "https://grok.example.com/routines/abc123",
"key": "{{secret.GROKBOT_KEY}}",
"task": "{{event.subject.title}}\n\n{{event.subject.body | truncate:2000}}\n{{event.subject.url}}"
}Retries and the delivery log
408, 425, 429, and 5xx responses retry with backoff and honor Retry-After; other 4xx responses fail permanently with a named code. Every attempt keeps the rendered request and the target response, so a failure points at one field or one status instead of a guess.

Common errors
| Symptom | Fix |
|---|---|
SIGNATURE_INVALID | The secret in GitHub and the endpoint secret differ, or a proxy rewrote the body. Copy the same secret to both sides and compute the HMAC over raw bytes. |
Ping event accepted but nothing delivered | Expected: ping.received is stored for audit and filtered before delivery. Open or label an issue instead. |
Pull requests never match an issue filter | PRs produce pull_request.* types, not issues.*. Add pull_request.opened to the filter or subscribe to both. |
Pair with a target
Reference
- Receiving eventshttps://hooks.events/docs/receive-events.md
- Verify signatureshttps://hooks.events/docs/signatures.md
- Payload templateshttps://hooks.events/docs/templates.md
Other sources
Stripe-Signature as HMAC-SHA256 over <timestamp>.<raw body> with a five-minute tolerance, then normalizes each event to <resource>.<action> such as invoice.payment_failed.SlackA Hooks Slack endpoint verifies X-Slack-Signature as HMAC-SHA256 over v0:<timestamp>:<raw body>, answers the url_verification handshake itself, and normalizes messages to app_mention.received or message.<channel_type>.LinearA Hooks Linear endpoint verifies Linear-Signature as hex HMAC-SHA256 over the raw body, rejects payloads whose webhookTimestamp is more than 60 seconds old, and normalizes events to issue.create, issue.update, and comment.create.SentryA Hooks Sentry endpoint verifies Sentry-Hook-Signature as hex HMAC-SHA256 with the Integration's Client Secret, deduplicates by Request-ID, and normalizes events to issue.created or event_alert.triggered.GitLabGitLab project webhooks cover pushes, merge requests, pipelines, issues, notes, and releases.ShopifyShopify app webhooks send topic-scoped events such as orders/create, products/update, and refunds/create.IntercomIntercom sends conversation and contact notification topics, signed with the app client secret.ZendeskZendesk webhooks cover ticket, user, and organization events, signed with a timestamp plus body HMAC.JiraJira Cloud webhooks cover issue creation, updates, comments, and sprint changes.PagerDutyPagerDuty v3 webhooks cover incident trigger, acknowledge, resolve, and annotation events.Standard WebhooksStandard Webhooks deliveries carry webhook-id, webhook-timestamp, and base64 v1 signatures.SvixSvix Play and the Svix dispatcher use the Standard Webhooks headers with svix- prefixes.VercelVercel webhooks cover deployment, integration, project, and domain events.TwilioTwilio status callbacks cover messages, calls, and recordings, signed over the URL and form parameters.DiscordDiscord interaction webhooks use Ed25519 signatures plus a PING handshake.Generic HMACGeneric sources sign timestamp.deliveryId.body with HMAC-SHA256 and send the signature in x-signature.BitbucketBitbucket Cloud repository webhooks cover pushes, pull request changes, and issue events.