Webhooks are GitHub's way of saying "hey, something happened" by sending an HTTP POST to your server. Instead of hammering their API every 30 seconds asking "anything new?" like some needy ex, you get notified the moment shit goes down.
What the docs won't tell you: webhook deliveries get delayed when GitHub's having issues, and you'll find out when users start bitching that CI didn't trigger. I've seen deployments stuck for hours because a webhook got lost in the ether during one of GitHub's "minor service interruptions."
The Setup (That Usually Works)
You configure a webhook by pointing GitHub at your server endpoint and picking which events you care about. When someone pushes code or opens a PR, GitHub fires an HTTP POST with a JSON payload containing all the juicy details.
The payload comes with headers like X-GitHub-Event
(tells you what happened), X-GitHub-Delivery
(unique ID for this specific webhook), X-GitHub-Hook-ID
(your webhook's ID), and X-Hub-Signature-256
for signature verification. Get that signature check wrong and you'll spend 3 hours debugging why valid payloads are getting rejected - the HMAC-SHA256 calculation is finicky as hell, especially with character encoding edge cases.
The complete webhook payload structure varies by event type, but every payload includes standard fields like repository info, sender details, and event-specific data. If you want to do this properly in production, their webhook creation guide actually has useful examples (shocking, I know), and the validation best practices will save your ass when signature verification breaks.
What Events You Can Actually Catch
GitHub supports 60+ event types, from the obvious stuff like pushes and PRs to weird edge cases like when someone stars your repo (yeah, people actually use that). They keep adding new ones as GitHub evolves - recently added events for code scanning alerts, custom properties, and Dependabot activities.
The ones you'll actually use:
- push - Someone committed code (triggers 90% of your CI nightmares)
- pull_request - PRs opened, closed, merged (the bread and butter)
- issues - Bug reports and feature requests (prepare for chaos)
- release - Tagged releases (usually works fine until it doesn't)
- repository - Repo created/deleted (surprisingly useful for automation)
The ones that will surprise you:
- ping - GitHub's health check (your endpoint better respond or they'll disable your webhook)
- workflow_run - GitHub Actions finished (meta-webhooks, very inception-like)
- code_scanning_alert - Security alerts from code analysis (new in 2024)
- dependabot_alert - Dependency vulnerability notifications (because your npm packages are probably fucked)
- custom_property_values - Organization custom properties changed (enterprise features creeping in)
You can configure webhooks at the repo level, organization level, or for GitHub Apps. Org-level webhooks are great until you realize you're getting events for that one test repo someone forgot about that triggers 500 events per day.
The 25MB Payload Limit Will Bite You
Had this nightmare where webhooks just stopped working. Turns out they have a 25MB limit - sounds like a lot until some idiot pushes a massive file. I think it was like 600MB of logs or some shit? Maybe more? Either way, your webhook dies silently. No error, no notification, just... nothing. Took us hours to figure out why CI went quiet.
What People Actually Use Webhooks For
CI/CD (The Obvious One)
Every push triggers your build pipeline. Works great until GitHub shits the bed with "elevated error rates" and your deploys just... don't happen. Always have a manual trigger button - learned this the hard way during a 3AM production fix when GitHub was having issues.
Common patterns that work:
- Jenkins builds on every push (set a 30-second timeout or prepare for hanging builds)
- Deploy to staging on PR merge (until someone merges a breaking change at 5 PM Friday)
- Run tests on every commit (until the test suite takes 45 minutes and blocks everything)
Before you deploy this stuff, read GitHub's best practices guide - it's actually helpful. Hookdeck also has a solid tutorial on handling the reliability issues that will definitely bite you.
Slack Notifications (The Noisy One)
Everyone wants GitHub activity in Slack until channel #dev becomes unusable. You'll start with every PR and issue, then gradually filter it down to just the stuff that matters. Takes about 2 weeks to find the right balance.
Word of advice: don't send webhook failures to Slack. When GitHub goes down, you'll get 500 "webhook delivery failed" messages in 30 seconds and your team will hate you.
Jira Integration (The Corporate One)
Auto-create Jira tickets from GitHub issues. Sounds great in theory. In practice, you get tickets like "Fix typo in README" that waste more time in triage than just fixing the damn typo.
Why Webhooks Beat Polling (Usually)
You're Not Wasting API Calls
Instead of asking "what's new?" every 30 seconds like a clingy coworker, webhooks tell you when shit actually happens. GitHub's rate limit is 5,000 requests/hour for authenticated users - sounds like a lot until you're polling 50 repos every minute and hitting the limit by lunch.
Faster Response Times
Webhooks fire within seconds of events happening. Perfect for emergency deployments when the site is down and every second counts. Just don't rely on this during GitHub incidents when webhook deliveries get queued for 20+ minutes.
It Actually Scales
One webhook handles unlimited events. Polling doesn't scale - monitoring 500 repos means 500 API calls every check. Do the math on that rate limit.
The Production Reality Check
Retry Logic is Aggressive as Hell
When your endpoint fails, GitHub will retry. And retry. And retry. If your server is down for 5 minutes, you'll get hammered with retry attempts when it comes back up. Make sure your error handling doesn't create a retry storm that crashes your server again.
GitHub's retry system will absolutely hammer your dead server with hundreds of requests. If you're running enterprise stuff, you might want something like Hookdeck to handle the retry storms.
Delivery Delays Happen
"Near real-time" is bullshit marketing speak. When GitHub's having a bad day, webhooks get delayed by 20+ minutes. Had CI pipelines sitting there waiting for webhooks that showed up like an hour later. Maybe longer? Point is, don't bet your prod deployments on instant webhook delivery.
IPv6 is Coming
GitHub's /meta API already lists IPv6 ranges for webhook deliveries. If your firewall only allows IPv4, you might start missing webhooks when they flip the switch. Check your infrastructure now.
If you're paranoid about security (you should be), set up IP allowlisting with GitHub's IP ranges, and definitely implement webhook signature validation because any script kiddie can POST JSON to your endpoint otherwise.
Setting This Shit Up
Step 1: Go to your repo Settings → Webhooks. Click "Add webhook" and try not to break anything.
Step 2: Paste your server URL. Make sure it's HTTPS or GitHub will reject it. Yes, even for testing. No, localhost doesn't work unless you use ngrok.
Step 3: Pick events. Start with just "push" and "pull_request". Don't get fancy with 40 event types until you've confirmed the basics work.
Step 4: Set a secret token. Write it down somewhere that isn't your commit history. I've seen production secrets in README files more times than I should admit.
Step 5: Save and pray. Check the "Recent Deliveries" tab in 5 minutes to see if it actually worked.
The First-Time Gotchas That Will Waste Your Afternoon
Your endpoint returns 500? Webhook disabled. Returns 404? Disabled. Takes longer than 10 seconds to respond? You guessed it - disabled.
The signature validation will break if you're not comparing the raw body bytes. JSON parsing changes the string encoding and your HMAC won't match. Spent 3 hours debugging "invalid signature" errors because I was being clever and parsing JSON first. Don't be clever.
Test with curl
first:
curl -X POST YOUR_WEBHOOK_URL \
-H "X-GitHub-Event: ping" \
-d '{"zen":"Keep it logically awesome."}'
Replace YOUR_WEBHOOK_URL
with your actual webhook endpoint. If that works, GitHub webhooks should work. If it doesn't, fix your server before blaming GitHub.