What is a Webhook? Explained Simply for Beginners (2026)
Webhooks are how apps talk to each other in real time — but the explanation is always confusing. Here's what a webhook actually is, how it works, how it differs from APIs, and real DevOps examples.
Webhooks are mentioned everywhere — GitHub webhooks, Slack webhooks, Stripe webhooks — but the explanation is always weirdly complicated. Here's the simple version.
The Problem Webhooks Solve
Imagine you ordered a pizza. You have two ways to know when it's ready:
Option 1: Polling — Call the restaurant every 5 minutes and ask "Is my pizza ready?"
Option 2: Webhook — Give the restaurant your phone number. They call you when it's ready.
Polling wastes time and resources. Webhooks are efficient — the event triggers the notification.
In software:
- API polling: Your app calls another service every few seconds asking "Did anything change?"
- Webhook: The other service calls your app when something changes
What is a Webhook?
A webhook is an HTTP callback. When an event happens in System A, it sends an HTTP POST request to a URL you specified in System B.
GitHub Your CI/CD Server
│ │
│ Event: code pushed to main │
│ │
│──── POST /webhook ──────────────→ │
│ { │
│ "ref": "refs/heads/main", │ Your server receives
│ "commits": [...], │ this and triggers
│ "repository": {...} │ a pipeline build
│ } │
│ │
That's it. A webhook is just:
- You give Service A a URL
- When something happens, Service A sends a POST request to your URL
- Your server does whatever it needs to do with that data
Webhook vs API — The Key Difference
| API (Pull) | Webhook (Push) | |
|---|---|---|
| Who initiates? | You call the service | The service calls you |
| When? | Whenever you poll | When the event happens |
| Efficiency | Low (wasted calls) | High (only when needed) |
| Latency | Depends on poll interval | Near real-time |
| Complexity | Easier to set up | Need a server to receive |
| Use case | "Get me user #123" | "Tell me when a payment succeeds" |
Real DevOps Examples
1. GitHub Webhook → Trigger CI/CD Pipeline
When you push to GitHub, GitHub sends a webhook to your Jenkins/GitLab/CI server.
Setting up in GitHub:
- Go to Repository → Settings → Webhooks → Add webhook
- Payload URL:
https://ci.yourcompany.com/github-webhook/ - Content type:
application/json - Events: "Just the push event" (or select specific events)
- Click Add webhook
GitHub will now POST to your CI server every time code is pushed.
2. AlertManager Webhook → Slack Notification
When Prometheus fires an alert, AlertManager sends a webhook to Slack:
# alertmanager.yml
receivers:
- name: 'slack-notifications'
slack_configs:
- api_url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXX'
channel: '#alerts'
text: "{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}"That api_url is a Slack webhook URL. AlertManager POSTs to it when alerts fire.
3. ArgoCD Webhook → Faster Git Sync
By default ArgoCD polls your Git repo every 3 minutes. You can add a webhook so it syncs immediately when code changes:
# Get your ArgoCD webhook URL
echo "https://argocd.yourcompany.com/api/webhook"
# Add this URL to your GitHub repo webhooks
# Events: Push events
# Secret: your-webhook-secretNow ArgoCD deploys within seconds of a git push instead of waiting up to 3 minutes.
4. Stripe Webhook → Payment Processing
When a payment succeeds (or fails), Stripe POSTs to your application:
POST https://yourapp.com/webhooks/stripe
{
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_xxx",
"amount": 2000,
"currency": "inr"
}
}
}
Your server processes the payment, activates the subscription, sends a receipt.
How to Build a Simple Webhook Receiver
Here's a basic webhook receiver in Python (FastAPI):
from fastapi import FastAPI, Request, Header, HTTPException
import hashlib
import hmac
import json
app = FastAPI()
WEBHOOK_SECRET = "your-secret-key"
@app.post("/webhook/github")
async def github_webhook(
request: Request,
x_hub_signature_256: str = Header(None)
):
body = await request.body()
# Verify the webhook is actually from GitHub
expected_sig = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(),
body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected_sig, x_hub_signature_256):
raise HTTPException(status_code=401, detail="Invalid signature")
payload = json.loads(body)
# Handle the event
if payload.get("ref") == "refs/heads/main":
trigger_deployment(payload)
return {"status": "ok"}
def trigger_deployment(payload):
commit = payload["after"]
print(f"Deploying commit: {commit}")
# Your deployment logic hereKey point: Always verify the webhook signature. This proves the request actually came from the service (not an attacker).
Webhook Security
Anyone who knows your webhook URL can send fake requests to it. Services use webhook secrets to prevent this:
- You generate a secret key
- You share it with the service (GitHub, Stripe, etc.)
- The service signs every webhook payload with that key (HMAC-SHA256)
- Your server verifies the signature before processing
# GitHub signs with HMAC-SHA256
# Signature is in the X-Hub-Signature-256 header
# Format: sha256=<hex digest>
# Verify in bash
echo -n "$payload" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET"Never skip signature verification in production. Without it, anyone can trigger your deployment pipeline by POSTing to your webhook URL.
Common Webhook Problems
My webhook isn't firing
- Is the URL publicly accessible? Localhost URLs don't work — services can't reach your laptop.
- Check the service's webhook delivery logs (GitHub shows each delivery with the response code)
- Is your server returning 200 OK? If not, the service may retry or disable the webhook.
For local development: Use ngrok to expose your localhost temporarily:
ngrok http 3000
# Gives you: https://abc123.ngrok.io → forwards to localhost:3000Webhook fires but nothing happens
- Check your server logs for errors
- The service expects a 200 response within ~5 seconds — don't do slow processing synchronously
- Use a queue (Redis, RabbitMQ) for heavy processing
@app.post("/webhook")
async def webhook(request: Request):
payload = await request.json()
# Don't do heavy work here — add to queue
queue.push(payload)
return {"status": "queued"} # Return 200 immediatelyDuplicate webhook deliveries
Services retry if they don't get a 200 response. Implement idempotency — if you process the same event twice, the second time should be a no-op.
processed_events = set() # Use Redis in production
@app.post("/webhook")
async def webhook(request: Request):
event_id = request.headers.get("X-GitHub-Delivery")
if event_id in processed_events:
return {"status": "already processed"}
processed_events.add(event_id)
# Process the event...Webhooks in Kubernetes
Kubernetes itself uses webhooks internally:
- Admission Webhooks: Review and modify (or reject) API requests before objects are created
- Conversion Webhooks: Convert between CRD versions
# MutatingWebhookConfiguration — Kubernetes calls your server for every pod creation
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: pod-mutator
webhooks:
- name: pod-mutator.example.com
clientConfig:
service:
name: pod-mutator-svc
namespace: default
path: /mutate
rules:
- operations: ["CREATE"]
resources: ["pods"]This is how tools like Istio inject sidecars — a webhook intercepts every pod creation and adds the Envoy container.
Webhooks are one of those concepts that seem mysterious until you understand the pattern: when this happens, call that URL. Once you understand that, the rest is just implementation details.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
How to Migrate from Ingress-NGINX to Kubernetes Gateway API in 2026
Step-by-step guide to migrating from Ingress-NGINX to Kubernetes Gateway API. Includes YAML examples, implementation choices, testing strategy, and cutover plan.
How to Set Up GitLab CI/CD from Scratch (2026 Complete Tutorial)
A practical step-by-step guide to setting up GitLab CI/CD pipelines from zero — covering runners, pipeline stages, Docker builds, deployment to Kubernetes, and best practices.
How to Set Up Kubernetes Gateway API to Replace Ingress (2026 Guide)
The Kubernetes Ingress API is being replaced by the Gateway API. Here's a complete step-by-step guide to setting it up with Nginx Gateway Fabric and migrating from Ingress.