All Articles

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.

DevOpsBoysApr 23, 20265 min read
Share:Tweet

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:

  1. You give Service A a URL
  2. When something happens, Service A sends a POST request to your URL
  3. 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 serviceThe service calls you
When?Whenever you pollWhen the event happens
EfficiencyLow (wasted calls)High (only when needed)
LatencyDepends on poll intervalNear real-time
ComplexityEasier to set upNeed 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:

  1. Go to Repository → Settings → Webhooks → Add webhook
  2. Payload URL: https://ci.yourcompany.com/github-webhook/
  3. Content type: application/json
  4. Events: "Just the push event" (or select specific events)
  5. 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:

yaml
# 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:

bash
# 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-secret

Now 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):

python
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 here

Key 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:

  1. You generate a secret key
  2. You share it with the service (GitHub, Stripe, etc.)
  3. The service signs every webhook payload with that key (HMAC-SHA256)
  4. Your server verifies the signature before processing
bash
# 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

  1. Is the URL publicly accessible? Localhost URLs don't work — services can't reach your laptop.
  2. Check the service's webhook delivery logs (GitHub shows each delivery with the response code)
  3. 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:

bash
ngrok http 3000
# Gives you: https://abc123.ngrok.io → forwards to localhost:3000

Webhook 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
python
@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 immediately

Duplicate 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.

python
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
yaml
# 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.

Newsletter

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

Comments