Skip to content

Webhooks — Event-Driven API Communication Guide

DodaTech Updated 2026-06-24 5 min read

In this tutorial, you'll learn about Webhooks. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Webhooks are HTTP callbacks that enable real-time event-driven communication between web applications by sending automated notifications when specific events occur, eliminating the need for polling.

What You'll Learn

By the end of this tutorial, you'll understand Webhook architecture, how to implement Webhook senders and receivers, verify payloads with signatures, and handle retries and idempotency.

Why Webhooks Matter

Polling — repeatedly checking for updates — wastes bandwidth and server resources. Webhooks push data the moment an event happens. DodaTech's Durga Antivirus Pro uses Webhooks to notify partner systems about threat detections in real time instead of requiring partners to poll for updates.

Webhook Architecture

flowchart LR
    subgraph "Webhook Flow"
        A["Event Occurs\n(e.g., payment received)"] --> B["Sender Server\n(your app)"]
        B --> C["Register Webhook\n(POST /webhook)"]
        C --> D["Send Payload\n(HTTP POST)"]
        D --> E["Receiver\n(partner API)"]
        E --> F["Process & Respond\n(200 OK)"]
        F -->|"Failure"| G["Retry Queue\n(3-5 attempts)"]
        G --> D
    end
    style B fill:#dbeafe,stroke:#2563eb
    style E fill:#dbeafe,stroke:#2563eb

Implementing a Webhook Sender

import requests
import JSON
import hmac
import hashlib
from datetime import datetime

Webhook_SECRET = "whsec_your_secret_key_here"

def send_Webhook(URL, event_type, payload):
    headers = {
        "Content-Type": "application/JSON",
        "X-Webhook-Event": event_type,
        "X-Webhook-Timestamp": str(int(datetime.utcnow().timestamp())),
    }
    body = JSON.dumps(payload).encode("utf-8")
    signature = hmac.new(
        Webhook_SECRET.encode("utf-8"), body, hashlib.sha256
    ).hexdigest()
    headers["X-Webhook-Signature"] = f"sha256={signature}"

    try:
        resp = requests.post(URL, data=body, headers=headers, timeout=10)
        if resp.status_code == 200:
            print(f"Webhook delivered: {event_type}")
        else:
            print(f"Webhook failed: {resp.status_code}")
    except requests.Timeout:
        print("Webhook timed out, queuing for retry")

Output:

Webhook delivered: payment.completed

Implementing a Webhook Receiver

from Flask import Flask, request, jsonify
import hmac
import hashlib

app = Flask(__name__)
Webhook_SECRET = "whsec_your_secret_key_here"

def verify_signature(payload, signature_header, timestamp):
    expected = hmac.new(
        Webhook_SECRET.encode("utf-8"), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature_header)

@app.route("/Webhook", methods=["POST"])
def handle_Webhook():
    payload = request.get_data()
    signature = request.headers.get("X-Webhook-Signature", "")
    event_type = request.headers.get("X-Webhook-Event", "unknown")

    if not verify_signature(payload, signature, request.headers.get("X-Webhook-Timestamp", "")):
        return jsonify({"error": "Invalid signature"}), 401

    data = request.get_JSON()
    print(f"Processing {event_type}: {JSON.dumps(data, indent=2)}")
    return jsonify({"status": "received"}), 200

Output:

Processing payment.completed: {
  "amount": 49.99,
  "currency": "USD",
  "customer_email": "user@example.com"
}

Retry and Idempotency

import time
from functools import wraps

def idempotent_Webhook_handler(f):
    processed = set()
    @wraps(f)
    def wrapper(Webhook_id, *args, **kwargs):
        if Webhook_id in processed:
            print(f"Skipping duplicate: {Webhook_id}")
            return {"status": "already_processed"}
        result = f(Webhook_id, *args, **kwargs)
        processed.add(Webhook_id)
        return result
    return wrapper

@idempotent_Webhook_handler
def handle_payment(Webhook_id, amount, customer):
    print(f"Processing payment {Webhook_id}: ${amount} from {customer}")
    time.sleep(1)

Common Errors

1. Not Verifying Signatures

Without signature verification, anyone can send fake Webhooks to your endpoint. Always verify HMAC signatures using a shared secret.

2. Blocking on Webhook Responses

Webhook senders expect fast responses (2-5 seconds). Queue heavy processing to a background task and return 200 immediately.

3. Ignoring Idempotency

Network failures cause duplicate Webhook deliveries. Use idempotency keys to detect and skip duplicates.

4. No Retry Logic

Webhooks fail — networks drop, servers restart. Implement exponential backoff with 3-5 retry attempts.

5. Not Returning Proper Status Codes

Always return 200 on success. Returning 4xx or 5xx tells the sender to retry, which may cause duplicate processing.

Practice Questions

1. What is the main advantage of Webhooks over polling?

Webhooks push data in real-time when events occur, eliminating the need for clients to repeatedly poll the server. This reduces bandwidth, latency, and server load.

2. How do you verify a Webhook payload is authentic?

Use HMAC signature verification. The sender signs the payload with a shared secret, and the receiver computes the expected signature and compares it using hmac.compare_digest to prevent timing attacks.

3. What HTTP status code should a Webhook receiver return on success?

200 OK. Any 4xx or 5xx status signals failure and may trigger retries.

4. Challenge: Design a Webhook system for a file scanning service like Durga Antivirus Pro. When a scan completes, the service must notify the user's dashboard via WebSocket and an external SIEM system via Webhook. Handle retries, idempotency, and signature verification.

Use a dual-delivery pattern: publish scan results to an internal event bus, which fans out to a WebSocket broadcaster (for the dashboard) and a Webhook dispatcher (for the SIEM). The Webhook dispatcher signs each payload with a per-partner secret, retries up to 3 times with exponential backoff, and uses scan IDs for idempotency.

Mini Project: Webhook Receiver with Retry

Build a Flask Webhook receiver that accepts GitHub-style Webhooks, verifies signatures, processes events in background threads, and returns 202 Accepted with a location header for status checking. Include a retry Queue using Redis for failed deliveries.

FAQ

What is the difference between Webhooks and APIs?

APIs follow a request-response pattern — you ask, you receive. Webhooks follow an event-callback pattern — when something happens, the server notifies you. APIs are pull-based; Webhooks are push-based.

Can Webhooks send binary data?

Yes, Webhooks can send any content type, but JSON is most common. Use multipart/form-data or base64-encoded payloads for binary data.

What happens if my Webhook endpoint is down?

The sender retries with exponential backoff (typically 3-5 attempts over 24-72 hours). After exhausting retries, the event is logged as failed for manual inspection.

How do Webhooks scale?

Webhooks scale horizontally behind a load balancer. The sender distributes Webhooks across registered URLs. Use a message Queue (RabbitMQ, Redis) to buffer Webhook deliveries during traffic spikes.

Related Concepts

API Gateway
AsyncAPI Specification
Server-Sent Events

What's Next

Learn AsyncAPI specification for documenting event-driven APIs, then explore API gateways for managing Webhook delivery at scale.


Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-24.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro