GitHub source
Turn GitHub webhooks into EventSend events with no code — setup steps, the default event mapping, and how to test it.
A GitHub source turns repository activity into events you can route anywhere. Paste one URL and one secret into your repository's webhook settings, and pushes, pull requests, releases, new stars and failed CI runs start arriving as events — no GitHub Action, no integration code.
Setup
Unlike most providers, you choose GitHub's webhook secret. That makes setup a single visit: create the source here first, copy both values, then paste them into GitHub.
In EventSend, open Sources, create a source, and choose GitHub. Click Generate next to the signing secret (or type your own strong random value), pick the events you want, and save. The next screen shows your endpoint URL and the secret side by side — that is the only time the secret is shown, so copy it before you leave. If you do lose it, nothing is broken: open the source again and set a new secret.
Then, in GitHub:
- In your repository (or organization) go to Settings → Webhooks → Add webhook.
- Paste this source's URL as the Payload URL.
- Set Content type to
application/json. - Paste the same signing secret into GitHub's Secret field.
- Choose Let me select individual events and tick the ones you enabled in EventSend — anything else is safely ignored.
- Save. GitHub immediately sends a
ping; the source's Last received timestamp confirms the connection. A ping does not create an event and is not metered.
GitHub's own webhook documentation is at docs.github.com/webhooks.
An organization-level webhook works too: one source then covers every repository in the org, and each message names the repository it came from.
What each GitHub event becomes
| GitHub event | Event name | Level | Icon |
|---|---|---|---|
push |
code-pushed |
default (warning when forced) |
📦 |
issues.opened |
issue-opened |
warning |
🐛 |
issues.closed |
issue-closed |
success |
✅ |
pull_request.opened |
pr-opened |
default |
🔀 |
pull_request.closed (merged) |
pr-merged |
success |
🎉 |
pull_request.closed (not merged) |
pr-closed |
default |
🚪 |
release.published |
release |
success |
🚀 |
star.created |
new-star |
success |
⭐ |
workflow_run.completed (failed) |
ci-failed |
error |
❌ |
Two of these are one checkbox covering several outcomes. pull_request.closed becomes pr-merged or
pr-closed depending on whether the pull request was actually merged. workflow_run.completed only
produces an event when the run genuinely failed (failure, timed_out or startup_failure) — a
successful or cancelled run creates nothing, which keeps a busy repository from spending your quota on
green builds. If you do want green-build notifications, send them from the workflow itself with
deploy notifications.
A push is reported four different ways, because "pushed" covers four different things: commits
landing, a branch or tag being created, a ref being deleted, and history being force-pushed (which is
raised to warning).
GitHub events that aren't in this table are ignored — EventSend answers 200, and nothing is recorded
or metered.
Because the names are predictable, a topic pattern of pr-* catches every pull-request event, and a
minimum level of error catches only CI failures. See routing rules.
The payload
EventSend does not forward the raw GitHub object. Each event carries a small structured payload with just the useful fields, so webhook destinations get something machine-readable and chat messages stay short:
{
"provider": "github",
"delivery_id": "72d3162e-cc78-11e3-81ab-4c9367dc0958",
"repo": "acme/app",
"sender": "ada",
"ref": "main",
"head_sha": "a1b2c3d4",
"commits": 3,
"url": "https://github.com/acme/app/compare/aaa000...bbb111"
}
Fields GitHub didn't supply are omitted, and the exact keys depend on the event: issues and pull
requests carry number, title and url; releases carry tag; workflow runs carry workflow,
branch and conclusion. Every url is a browser link, never an API endpoint. Note that commits
counts the commits included in the delivery — GitHub caps that list at 2,048 for a very large push.
Security and duplicates
Every request is verified against your signing secret before it is decoded, using the scheme GitHub
documents: HMAC-SHA256 over the raw request body, sent as X-Hub-Signature-256. Only that scheme is
accepted; the legacy SHA-1 X-Hub-Signature header is ignored. A bad signature returns 401 and
increments the source's failure counter, which is visible on the source page.
Every delivery carries a GUID, and GitHub reuses it when you press Redeliver. EventSend uses that
GUID as the event's idempotency key, so a redelivery is recognized and answered 200 without
producing a second event or a second notification. That makes Redeliver safe to use freely on any
delivery still in your history.
That protection is bound to your plan's retention window, though: once the original event is pruned, nothing durable remains to recognize the GUID by, and redelivering that same payload would produce a fresh event and a fresh notification.
When a delivery fails
GitHub does not automatically retry a failed delivery. This is the one important difference from providers like Stripe. If a delivery fails, open the webhook's Recent Deliveries tab and press Redeliver.
That is also why a GitHub source requires its signing secret up front: a source that cannot verify would reject deliveries that never come back on their own.
One case Redeliver cannot fix: EventSend caps webhook bodies at 512KB, and a larger delivery is
rejected with 413. Redelivering sends the identical oversized payload, so it fails the same way.
This is rare — it takes a very large push, since GitHub embeds per-commit file lists — but if you hit
it, reduce the size of the push, or have a workflow send a compact custom event through the
ingest API instead.