Webhooks flip the usual client-polls-server model on its head — instead of repeatedly asking “has anything changed yet?”, the server proactively pushes a notification the moment something actually happens.
from flask import Flask, request
import hmac, hashlib
app = Flask(__name__)
@app.route('/webhooks/payment', methods=['POST'])
def handle_payment_webhook():
# ALWAYS verify the signature - anyone can POST to a public endpoint otherwise
signature = request.headers.get('X-Signature')
expected = hmac.new(WEBHOOK_SECRET.encode(), request.data, hashlib.sha256).hexdigest()
if not hmac.compare_digest(signature, expected):
return "Invalid signature", 401
event = request.json
if event['type'] == 'payment.succeeded':
mark_order_paid(event['data']['order_id'])
return "", 200 # respond quickly - do heavy processing asynchronously, not inline
Signature Verification Is Not Optional
Without verifying the signature, any attacker who discovers your webhook URL can POST fake events — a fake “payment succeeded” webhook could mark an unpaid order as paid. Every reputable webhook provider (Stripe, GitHub, Twilio) signs its payloads with a shared secret specifically so you can verify authenticity before acting on the event.
Respond Fast, Process Slow
Webhook senders typically retry if they don’t get a fast response (often within a few seconds) — doing heavy processing inline inside the webhook handler risks timeouts and duplicate deliveries. Acknowledge receipt immediately, then hand the actual work off to a background queue.