GitHub webhooks are one of the most powerful tools for automating development workflows, enabling real-time notifications, and building integrations that respond to repository events. Whether you're building CI/CD pipelines, automating code reviews, managing issues, or monitoring security alerts, webhooks provide the event-driven foundation that keeps your team informed and your processes automated.
This comprehensive guide covers everything you need to know about GitHub webhooks—from basic setup to advanced automation patterns. We'll explore pull request workflows, issue management, CI/CD events, security alerts, and show you both custom implementation approaches and no-code alternatives using MagicBell workflows.
What Are GitHub Webhooks?
GitHub webhooks are HTTP callbacks that GitHub triggers when specific events occur in your repository. Instead of constantly polling GitHub's API to check for changes, webhooks push event data to your application in real time whenever something happens—like a pull request being opened, an issue being commented on, or a CI workflow completing.
Think of webhooks as GitHub's notification system for applications. When you configure a webhook, you're telling GitHub: "When X happens, send me the details at this URL." GitHub then delivers a POST request to your specified endpoint containing all relevant event data in JSON format.
Why Webhooks Matter
Webhooks enable event-driven architecture, where systems react to events as they happen rather than continuously checking for updates. This approach:
- Reduces latency: Notifications arrive instantly when events occur
- Saves resources: No need for constant polling or API rate limit consumption
- Enables automation: Trigger workflows, notifications, and integrations automatically
- Improves developer experience: Teams stay informed about repository activity in real time
GitHub supports 73+ webhook events covering everything from code changes to security alerts. This extensive event catalog makes webhooks incredibly versatile for building sophisticated automation workflows.
How GitHub Webhooks Work
Understanding the webhook flow helps you build more reliable integrations. Here's what happens when a GitHub event triggers a webhook:
The Webhook Lifecycle
- Event occurs: A developer opens a pull request, pushes code, or comments on an issue
- GitHub constructs payload: GitHub gathers all relevant event data into a JSON payload
- Signature generation: GitHub creates an HMAC signature using your webhook secret
- HTTP POST request: GitHub sends the payload to your configured webhook URL
- Your server receives: Your endpoint receives and validates the webhook
- Processing: Your application processes the event and takes action
- Response: Your server responds with a 2xx status code to acknowledge receipt
This entire process typically completes in milliseconds, enabling near-instant automation.
Webhook Payload Structure
Every GitHub webhook delivery includes these components:
Headers:
X-GitHub-Event: The event type (e.g.,pull_request,issues,push)X-GitHub-Delivery: Unique identifier for this webhook deliveryX-Hub-Signature-256: HMAC signature for payload verificationUser-Agent: Always starts withGitHub-Hookshot/
Body: JSON payload containing:
action: What happened (e.g.,opened,closed,synchronize)- Event-specific data (pull request details, issue information, commit data)
repository: Information about the repository where the event occurredsender: The GitHub user who triggered the event
Understanding this structure is crucial for parsing events and building robust webhook handlers.
Setting Up GitHub Webhooks
You can configure webhooks at the repository or organization level through GitHub's web interface or API.
Repository Webhook Configuration
- Navigate to your repository on GitHub
- Go to Settings → Webhooks → Add webhook
- Configure the webhook:
- Payload URL: Your endpoint that will receive webhook events
- Content type: Select
application/json - Secret: Generate a strong random string for signature verification
- SSL verification: Leave enabled (always use HTTPS in production)
- Events: Choose which events trigger this webhook
Event Selection Strategies
GitHub offers three options for event selection:
1. Just the push event (default)
- Simplest option, only notified of code pushes
- Good for deployment automation
2. Send me everything
- Receive all 73+ event types
- Useful during development to explore available events
- Can generate high volume—filter events in your application
3. Let me select individual events
- Recommended approach for production
- Subscribe only to events you'll actually handle
- Reduces unnecessary webhook deliveries
For most applications, selecting specific events is the best approach. You can always add more events later as your integration grows.
Webhook URL Requirements
Your webhook endpoint must:
- Use HTTPS (not HTTP) for production webhooks
- Respond within 10 seconds (GitHub's timeout)
- Return a 2xx status code to indicate successful receipt
- Be publicly accessible (GitHub can't reach localhost directly)
For local development, you'll need a tunneling tool to expose your local server—we'll cover this in the testing section.
Pull Request Webhook Events
Pull requests are the heart of collaborative development on GitHub. Automating PR workflows can dramatically improve code review efficiency and team communication.
Core Pull Request Events
GitHub provides several events for different stages of the PR lifecycle:
pull_request - The primary event with these actions:
opened: New PR createdclosed: PR closed (checkmergedfield to distinguish merged vs. closed)reopened: Previously closed PR reopenededited: PR title, description, or base branch changedsynchronize: New commits pushed to the PR branchassigned: Reviewer or assignee addedlabeled: Label added or removedready_for_review: Draft PR marked ready
pull_request_review - Code review events:
submitted: Review completed (approved, changes requested, or commented)edited: Review comment editeddismissed: Review dismissed
pull_request_review_comment - Inline code comments:
created: New comment on specific code linesedited: Comment modifieddeleted: Comment removed
Real-World PR Workflow Examples
Here's a practical example of handling PR events to automate reviewer notifications.
Scenario: When a PR is opened, notify the assigned reviewers via Slack and in-app notification.
app.post('/webhooks/github', async (req, res) => {
const event = req.headers['x-github-event'];
const payload = req.body;
if (event === 'pull_request' && payload.action === 'opened') {
const pr = payload.pull_request;
// Extract PR details
const prData = {
title: pr.title,
author: pr.user.login,
url: pr.html_url,
number: pr.number,
repository: payload.repository.full_name
};
// Notify each requested reviewer
for (const reviewer of pr.requested_reviewers || []) {
await sendNotification({
recipient: reviewer.login,
title: `New PR: ${prData.title}`,
message: `${prData.author} requests your review on PR #${prData.number}`,
action_url: prData.url,
category: 'code_review'
});
}
console.log(`Notified ${pr.requested_reviewers?.length || 0} reviewers for PR #${pr.number}`);
}
res.status(200).send('Webhook received');
});
MagicBell Workflow Alternative
Instead of writing custom code, you can use MagicBell workflows to automate PR notifications without any backend changes:
{
"key": "integration.github.pull_request.opened",
"steps": [
{
"command": "broadcast",
"input": {
"title": "New PR: {{data.pull_request.title}}",
"content": "{{data.pull_request.user.login}} requests your review",
"action_url": "{{data.pull_request.html_url}}",
"category": "code_review",
"recipients": [
{
"external_id": "{{data.pull_request.requested_reviewers[0].login}}"
}
]
}
}
]
}
This pull request workflow automatically executes when GitHub sends the pull_request.opened webhook to MagicBell. No server code required.
Advanced PR Automation Ideas
Merge conflict detection:
Monitor pull_request.synchronize events and check if mergeable is false. Notify the PR author that conflicts need resolution before merging can proceed.
Stale PR reminders:
Track when PRs remain in review status for too long. Use a scheduled job to query open PRs and send reminder notifications to reviewers after configurable time periods (e.g., 48 hours).
CI/CD integration:
Combine PR events with check run events to notify when tests pass or fail, enabling reviewers to know when a PR is actually ready for review.
Issue Webhook Events
GitHub Issues serve as the project management hub for many teams. Automating issue workflows keeps everyone aligned and ensures nothing falls through the cracks.
Core Issue Events
issues - The main event with these actions:
opened: New issue creatededited: Issue title or description changedclosed: Issue closedreopened: Issue reopeneddeleted: Issue deleted (available to org admins)assigned: Assignee addedunassigned: Assignee removedlabeled: Label addedunlabeled: Label removedlocked: Issue locked (no more comments)unlocked: Issue unlockedtransferred: Issue moved to another repositorypinned: Issue pinned to repositoryunpinned: Issue unpinned
issue_comment - Comments on issues:
created: New comment addededited: Comment editeddeleted: Comment deleted
These events also apply to pull requests (PRs are issues with code), so issue_comment fires for both issue comments and PR comments.
Practical Issue Automation
Scenario: When critical bugs are reported, immediately notify on-call engineers and create a Slack thread for coordination.
app.post('/webhooks/github', async (req, res) => {
const event = req.headers['x-github-event'];
const payload = req.body;
if (event === 'issues' && payload.action === 'opened') {
const issue = payload.issue;
const labels = issue.labels.map(l => l.name);
// Check if this is a critical bug
if (labels.includes('bug') && labels.includes('critical')) {
const issueData = {
title: issue.title,
reporter: issue.user.login,
url: issue.html_url,
number: issue.number,
body: issue.body
};
// Alert on-call team
await sendUrgentNotification({
recipients: ['oncall-team'],
title: `🚨 Critical Bug: ${issueData.title}`,
message: `Reporter: ${issueData.reporter}\n\n${issueData.body.substring(0, 200)}...`,
action_url: issueData.url,
priority: 'urgent'
});
console.log(`Alerted on-call team for critical bug #${issue.number}`);
}
}
res.status(200).send('Webhook received');
});
MagicBell Issue Workflow
The no-code approach using issue workflows:
{
"key": "integration.github.issues.opened",
"steps": [
{
"if": "{{data.issue.labels | map: 'name' | includes: 'bug'}}",
"command": "broadcast",
"input": {
"title": "🐛 Bug Report: {{data.issue.title}}",
"content": "{{data.issue.user.login}} reported a bug",
"action_url": "{{data.issue.html_url}}",
"category": "bug_report",
"recipients": [
{"external_id": "engineering-team"}
]
}
}
]
}
The if condition filters issues, only broadcasting when the bug label is present.
Issue Triage Automation
Auto-labeling based on content:
Parse issue body for keywords like "crash," "security," or "feature request" and automatically apply appropriate labels. This helps maintainers quickly categorize new issues.
Assignment routing:
When certain labels are applied, automatically assign issues to the appropriate team members or teams based on predefined rules.
Stale issue management:
Track issues that haven't been updated recently. After a configurable period (e.g., 30 days), add a "stale" label and post a comment asking if the issue is still relevant.
CI/CD and Workflow Events
GitHub Actions workflows and other CI/CD systems generate events that are crucial for keeping developers informed about build status, test results, and deployment progress.
Workflow Run Events
workflow_run - Triggered when GitHub Actions workflows execute:
completed: Workflow finished (checkconclusionfor success/failure)requested: Workflow queued for executionin_progress: Workflow actively running
workflow_job - Individual job-level events:
queued: Job added to queuein_progress: Job started runningcompleted: Job finished
These events are especially valuable for monitoring CI/CD pipelines and notifying developers when their code passes or fails tests.
Check Run Events
check_run - Status checks from GitHub Apps and Actions:
created: New check initiatedcompleted: Check finished with conclusion (success, failure, cancelled)rerequested: Check manually re-run
check_suite - Groups of checks:
completed: All checks in suite finishedrequested: New check suite created
CI/CD Notification Example
Scenario: Notify developers when their commit fails CI, so they can fix issues immediately.
app.post('/webhooks/github', async (req, res) => {
const event = req.headers['x-github-event'];
const payload = req.body;
if (event === 'workflow_run' && payload.action === 'completed') {
const workflow = payload.workflow_run;
// Only notify on failures
if (workflow.conclusion === 'failure') {
const commitAuthor = workflow.head_commit.author.name;
const branch = workflow.head_branch;
await sendNotification({
recipient: commitAuthor,
title: `❌ CI Failed: ${workflow.name}`,
message: `Your commit on ${branch} failed CI checks`,
action_url: workflow.html_url,
priority: 'high',
category: 'ci_failure'
});
console.log(`Notified ${commitAuthor} about CI failure on ${branch}`);
}
}
res.status(200).send('Webhook received');
});
MagicBell CI/CD Workflow
Using workflow run events:
{
"key": "integration.github.workflow_run.completed",
"steps": [
{
"if": "{{data.workflow_run.conclusion == 'failure'}}",
"command": "broadcast",
"input": {
"title": "CI Failed: {{data.workflow_run.name}}",
"content": "Your commit on {{data.workflow_run.head_branch}} failed CI",
"action_url": "{{data.workflow_run.html_url}}",
"category": "ci_failure",
"recipients": [
{
"external_id": "{{data.workflow_run.head_commit.author.name}}"
}
]
}
}
]
}
Deployment Events
deployment - Deployments initiated:
created: New deployment started
deployment_status - Deployment progress:
created: Deployment status updated (pending, success, failure, error)
These events help teams track releases across environments (staging, production) and notify stakeholders when deployments complete.
Security and Alert Events
GitHub provides powerful security scanning features, and webhooks ensure your team responds quickly to security issues.
Security Event Types
dependabot_alert - Vulnerability alerts for dependencies:
created: New vulnerability discovereddismissed: Alert dismissed by maintainerfixed: Vulnerability patchedreintroduced: Previously fixed vulnerability reappeared
code_scanning_alert - Code security issues:
created: New code vulnerability foundclosed_by_user: Alert manually closedfixed: Issue resolved in codereopened: Previously closed alert reopened
secret_scanning_alert - Exposed secrets detected:
created: Secret found in repositoryresolved: Secret issue addressed
security_advisory - Published security advisories:
published: New advisory releasedupdated: Advisory information updated
Security Notification Example
Scenario: When Dependabot discovers a critical vulnerability, immediately alert the security team.
app.post('/webhooks/github', async (req, res) => {
const event = req.headers['x-github-event'];
const payload = req.body;
if (event === 'dependabot_alert' && payload.action === 'created') {
const alert = payload.alert;
const vulnerability = alert.security_vulnerability;
// Check severity
if (vulnerability.severity === 'critical' || vulnerability.severity === 'high') {
await sendNotification({
recipients: ['security-team'],
title: `🔒 ${vulnerability.severity.toUpperCase()} Vulnerability Detected`,
message: `${vulnerability.package.name}: ${alert.security_advisory.summary}`,
action_url: alert.html_url,
priority: 'urgent',
category: 'security_alert'
});
console.log(`Alerted security team about ${vulnerability.severity} vulnerability`);
}
}
res.status(200).send('Webhook received');
});
Security Workflow with MagicBell
Automate Dependabot alert workflows:
{
"key": "integration.github.dependabot_alert.created",
"steps": [
{
"if": "{{data.alert.security_vulnerability.severity == 'critical'}}",
"command": "broadcast",
"input": {
"title": "🔒 Critical Vulnerability: {{data.alert.security_vulnerability.package.name}}",
"content": "{{data.alert.security_advisory.summary}}",
"action_url": "{{data.alert.html_url}}",
"category": "security_alert",
"recipients": [
{"external_id": "security-team"}
]
}
}
]
}
Repository and Code Events
These events cover fundamental repository activities like pushing code, creating branches, and publishing releases.
Push Events
The push event fires when commits are pushed to a repository. This is one of the most common webhook events.
Payload highlights:
commits: Array of commits pushedref: The branch reference (e.g.,refs/heads/main)before/after: Commit SHAs before and after pushforced: Whether this was a force push
Use cases:
- Trigger deployments when code is pushed to specific branches
- Run automated tests or code quality checks
- Update external systems with latest code changes
- Send notifications about significant commits (e.g., version bumps)
Release Events
release - Repository releases:
published: New release createdunpublished: Release removedcreated: Draft release creatededited: Release details updateddeleted: Release deletedprereleased: Pre-release publishedreleased: Pre-release changed to full release
Release events are perfect for notifying users about new versions or triggering deployment pipelines.
MagicBell Release Notification
Announce new releases to your users with a release workflow:
{
"key": "integration.github.release.published",
"steps": [
{
"command": "broadcast",
"input": {
"title": "🎉 {{data.release.name}} Released",
"content": "{{data.release.body}}",
"action_url": "{{data.release.html_url}}",
"category": "product_update",
"recipients": [
{"external_id": "all-users"}
]
}
}
]
}
Branch and Tag Events
create - Branch or tag created:
ref_type: Eitherbranchortagref: Name of branch or tag
delete - Branch or tag deleted:
ref_type: Eitherbranchortagref: Name deleted
branch_protection_rule - Branch protection changes:
created: New protection rule addededited: Existing rule modifieddeleted: Protection rule removed
Webhook Security Best Practices
Webhook endpoints are publicly accessible HTTP endpoints, making security critical. GitHub provides signature verification to ensure webhook authenticity.
HMAC Signature Verification
Every webhook delivery includes an X-Hub-Signature-256 header containing an HMAC signature. This signature proves the request came from GitHub and hasn't been tampered with.
Verification process:
- GitHub generates HMAC using your webhook secret and the payload
- GitHub includes signature in
X-Hub-Signature-256header - Your server recomputes the signature using the same secret
- Compare signatures—if they match, the webhook is authentic
Node.js Signature Verification
const crypto = require('crypto');
function verifyGitHubSignature(req, secret) {
const signature = req.headers['x-hub-signature-256'];
if (!signature) {
return false;
}
// Compute expected signature
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(JSON.stringify(req.body)).digest('hex');
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
// Use in your webhook handler
app.post('/webhooks/github', (req, res) => {
const secret = process.env.GITHUB_WEBHOOK_SECRET;
if (!verifyGitHubSignature(req, secret)) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
// Signature valid, process webhook
const event = req.headers['x-github-event'];
// ... handle event
});
Critical security notes:
- Always use
crypto.timingSafeEqual()for comparison to prevent timing attacks - Never log or expose your webhook secret
- Store secrets in environment variables, not code
- Use HTTPS for all webhook URLs
- Rotate webhook secrets periodically
Additional Security Measures
Rate limiting:
Implement rate limits on your webhook endpoint to prevent abuse. Even with signature verification, rate limiting protects against accidental webhook floods.
IP allowlisting (optional):
GitHub publishes webhook IP ranges you can allowlist. However, these ranges change, so signature verification is more reliable.
Payload validation:
After verifying the signature, validate the payload structure. Ensure required fields are present before processing.
Idempotency:
Handle duplicate webhook deliveries gracefully. GitHub may deliver the same webhook multiple times, so use the X-GitHub-Delivery header as an idempotency key.
Building a Custom Webhook Handler
Here's a production-ready Express.js webhook handler that demonstrates best practices:
const express = require('express');
const crypto = require('crypto');
const app = express();
// Raw body parsing for signature verification
app.use(express.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString('utf8');
}
}));
// Webhook secret from environment
const WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET;
// Signature verification middleware
function verifySignature(req, res, next) {
const signature = req.headers['x-hub-signature-256'];
if (!signature) {
return res.status(401).send('Missing signature');
}
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
const digest = 'sha256=' + hmac.update(req.rawBody).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest))) {
return res.status(401).send('Invalid signature');
}
next();
}
// Idempotency tracking (in-memory, use Redis/database in production)
const processedDeliveries = new Set();
// Main webhook endpoint
app.post('/webhooks/github', verifySignature, async (req, res) => {
const deliveryId = req.headers['x-github-delivery'];
const event = req.headers['x-github-event'];
const payload = req.body;
// Idempotency check
if (processedDeliveries.has(deliveryId)) {
console.log(`Duplicate delivery ${deliveryId}, ignoring`);
return res.status(200).send('Already processed');
}
// Mark as processed
processedDeliveries.add(deliveryId);
// Acknowledge receipt immediately (respond within 10 seconds)
res.status(200).send('Webhook received');
// Process webhook asynchronously
try {
await processWebhook(event, payload);
console.log(`Successfully processed ${event} event (${deliveryId})`);
} catch (error) {
console.error(`Error processing webhook ${deliveryId}:`, error);
// Don't throw—we already responded to GitHub
}
});
async function processWebhook(event, payload) {
switch (event) {
case 'pull_request':
await handlePullRequest(payload);
break;
case 'issues':
await handleIssue(payload);
break;
case 'workflow_run':
await handleWorkflowRun(payload);
break;
case 'push':
await handlePush(payload);
break;
default:
console.log(`Unhandled event type: ${event}`);
}
}
async function handlePullRequest(payload) {
const action = payload.action;
const pr = payload.pull_request;
if (action === 'opened') {
console.log(`New PR #${pr.number}: ${pr.title}`);
// Send notifications, update tracking systems, etc.
}
}
async function handleIssue(payload) {
const action = payload.action;
const issue = payload.issue;
if (action === 'opened') {
const labels = issue.labels.map(l => l.name);
console.log(`New issue #${issue.number} with labels: ${labels.join(', ')}`);
// Auto-label, assign, notify teams, etc.
}
}
async function handleWorkflowRun(payload) {
const workflow = payload.workflow_run;
if (payload.action === 'completed' && workflow.conclusion === 'failure') {
console.log(`Workflow ${workflow.name} failed on ${workflow.head_branch}`);
// Notify developers of CI failures
}
}
async function handlePush(payload) {
const branch = payload.ref.replace('refs/heads/', '');
const commits = payload.commits.length;
console.log(`${commits} commit(s) pushed to ${branch}`);
// Trigger deployments for specific branches
if (branch === 'main' || branch === 'production') {
console.log('Triggering deployment...');
// Deploy to production/staging
}
}
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`GitHub webhook handler listening on port ${PORT}`);
});
This handler demonstrates:
- Signature verification middleware
- Idempotency using delivery IDs
- Immediate response to GitHub (sub-second)
- Asynchronous event processing
- Error handling that doesn't crash the server
- Event routing to specialized handlers
Testing GitHub Webhooks Locally
Testing webhooks during development requires exposing your local server to the internet so GitHub can deliver events.
Tunneling Tools
Ngrok (popular choice):
ngrok http 3000
Ngrok provides a public URL that tunnels to localhost:3000. Use this URL as your webhook URL in GitHub settings.
Localtunnel (open source):
npm install -g localtunnel
lt --port 3000
Cloudflare Tunnel (free):
cloudflared tunnel --url http://localhost:3000
GitHub Webhook Delivery Inspection
GitHub provides excellent debugging tools in the webhook settings:
- Go to Settings → Webhooks
- Click your webhook
- Scroll to Recent Deliveries
For each delivery, you can view:
- Full request headers and payload
- Response status and body
- Timing information
- Redeliver the webhook with the same payload
Use redelivery to test your handler without triggering new GitHub events.
Testing with Webhook.site
Webhook.site provides instant webhook URLs for testing. Use it to inspect GitHub webhook payloads without writing any code:
- Visit webhook.site and copy your unique URL
- Add this URL as a GitHub webhook
- Trigger events in GitHub (open a PR, create an issue)
- View the full payload in webhook.site
This is invaluable for understanding event structure before writing handler code.
Production Best Practices
Running webhooks in production requires additional considerations beyond development environments.
Idempotency
GitHub may deliver the same webhook multiple times. Your handler must safely process duplicate deliveries without side effects.
Implementation strategies:
- Store processed delivery IDs in Redis or your database
- Check if delivery ID exists before processing
- Set TTL on delivery IDs (e.g., 24 hours) to prevent unbounded growth
// Redis idempotency example
const redis = require('redis').createClient();
async function isProcessed(deliveryId) {
const exists = await redis.exists(`webhook:${deliveryId}`);
return exists === 1;
}
async function markProcessed(deliveryId) {
// Store with 24-hour expiration
await redis.setex(`webhook:${deliveryId}`, 86400, '1');
}
Error Handling and Retries
GitHub doesn't automatically retry failed webhooks beyond the initial delivery. If your handler errors, implement your own retry logic.
Best practices:
- Acknowledge receipt immediately (return 2xx status)
- Queue events for asynchronous processing
- Implement exponential backoff for transient failures
- Alert on-call engineers if processing repeatedly fails
// Queue-based processing example
const queue = require('bull');
const webhookQueue = new queue('github-webhooks');
app.post('/webhooks/github', verifySignature, async (req, res) => {
// Add to queue instead of processing inline
await webhookQueue.add({
event: req.headers['x-github-event'],
payload: req.body,
deliveryId: req.headers['x-github-delivery']
});
res.status(200).send('Queued');
});
// Process queue jobs with retries
webhookQueue.process(async (job) => {
const { event, payload, deliveryId } = job.data;
if (await isProcessed(deliveryId)) {
return; // Already processed
}
await processWebhook(event, payload);
await markProcessed(deliveryId);
});
Monitoring and Alerting
Monitor webhook delivery success rates and processing latency:
- Track webhook processing failures
- Alert when error rates exceed thresholds
- Monitor queue depth if using async processing
- Log all webhook deliveries for debugging
GitHub also provides webhook delivery metrics in the repository settings, showing success/failure rates over time.
Rate Limits and Scaling
Popular repositories can generate high webhook volumes. Plan for scaling:
- Use load balancers to distribute webhook traffic
- Employ horizontal scaling for webhook handlers
- Consider separate handlers for different event types
- Implement backpressure mechanisms if your system can't keep up
MagicBell: No-Code Webhook Automation
Writing and maintaining custom webhook handlers requires ongoing development effort. MagicBell workflows provide a no-code alternative that handles common automation scenarios without custom code.
How MagicBell Workflows Work
- Configure your GitHub repository to send webhooks to MagicBell
- Create workflows that trigger on specific GitHub events
- Define notification logic with conditional branching and delays
- MagicBell handles webhook security, delivery, and notification sending
Setting Up MagicBell for GitHub
# 1. Save your GitHub webhook signing secret
magicbell integration save_github \
--data '{"webhook_signing_secret":"your_secret_here"}'
# 2. Use the returned ID to build your webhook URL
# https://api.magicbell.com/v2/integrations/github/webhooks/incoming/{id}
Add this URL to your GitHub webhook configuration, and MagicBell will receive all GitHub events.
Workflow Definition Example
Create a workflow that triggers on GitHub events using the key pattern integration.github.{event_type}:
{
"key": "integration.github.pull_request.opened",
"steps": [
{
"command": "broadcast",
"input": {
"title": "PR Review Needed: {{data.pull_request.title}}",
"content": "{{data.pull_request.user.login}} opened a pull request",
"action_url": "{{data.pull_request.html_url}}",
"category": "code_review",
"recipients": [
{
"external_id": "{{data.pull_request.requested_reviewers[0].login}}"
}
]
}
},
{
"command": "wait",
"input": {
"duration": 86400
}
},
{
"if": "{{data.pull_request.state == 'open'}}",
"command": "broadcast",
"input": {
"title": "PR Still Pending: {{data.pull_request.title}}",
"content": "This PR has been open for 24 hours without review",
"action_url": "{{data.pull_request.html_url}}",
"category": "reminder"
}
}
]
}
This workflow:
- Sends an immediate notification to the requested reviewer
- Waits 24 hours
- Sends a reminder if the PR is still open
All without writing any server code or managing infrastructure.
Available GitHub Workflows
MagicBell provides pre-built workflow examples for common GitHub automation scenarios:
Pull Request Workflows:
Issue Workflows:
- Issue opened - Triage notifications
- Issue commented - Discussion tracking
CI/CD Workflows:
- Workflow run completed - Build status notifications
- Check run completed - Test results
Release Workflows:
- Release published - Announce new versions
Security Workflows:
- Dependabot alert created - Vulnerability notifications
- Code scanning alert - Security findings
Browse the complete list of GitHub workflow examples to find templates for your use cases.
Troubleshooting Common Issues
Webhooks Not Delivering
Symptoms: GitHub shows failed deliveries or timeouts.
Solutions:
- Verify your endpoint URL is publicly accessible
- Check that your server responds within 10 seconds
- Ensure you're returning 2xx status codes
- Review GitHub's Recent Deliveries for specific error messages
Invalid Signature Errors
Symptoms: Your server rejects webhooks with signature verification failures.
Solutions:
- Verify your webhook secret matches GitHub configuration
- Ensure you're computing HMAC on the raw request body (before JSON parsing)
- Check that your secret doesn't have trailing whitespace
- Use constant-time comparison (
crypto.timingSafeEqual)
Missing Event Data
Symptoms: Expected fields are undefined or missing from webhook payload.
Solutions:
- Use GitHub's Recent Deliveries to inspect actual payload structure
- Check GitHub's webhook documentation for event schemas
- Remember that optional fields may be
nullor absent - Different actions within the same event type have different payloads
Webhook Floods
Symptoms: Receiving thousands of webhooks in short time periods.
Solutions:
- Uncheck "Send me everything" and select specific events
- Implement rate limiting on your endpoint
- Use queue-based processing to handle bursts
- Consider if you actually need real-time processing for all events
Conclusion
GitHub webhooks unlock powerful automation capabilities that transform how development teams work. From automated code reviews and CI/CD notifications to security alerts and release management, webhooks provide the real-time event stream that keeps modern development workflows moving.
This guide covered:
- 73+ GitHub webhook events across pull requests, issues, CI/CD, security, and repository management
- Custom webhook implementation with signature verification, idempotency, and production best practices
- MagicBell workflows as a no-code alternative for common automation scenarios
- Security best practices including HMAC verification and secret management
- Testing strategies for local development and debugging
- Production considerations like error handling, scaling, and monitoring
Whether you're building custom integrations or using MagicBell's no-code workflows, webhooks provide the foundation for event-driven automation that makes your team more productive and responsive.
For more webhook resources, check out:
- What Is a Webhook? - Webhook fundamentals
- Webhooks Tutorial - Getting started guide
- Stripe Webhooks Guide - Payment webhook automation
- GitHub Workflow Examples - Pre-built workflow templates
Sources:
