Roll your own receiver | Master AI Automation in 4 hours Master AI Automation in 4 hours Course About Ayush Modules Sample chapter Toolbox The Microcap Minute Classroom / Module 06: Webhooks & Automation / Chapter 4 Roll your own receiver Watch first, then read. Same lesson, your pace. What you will learn – Receiving webhooks in your own Python server – Signature verification, properly – Exposing localhost to the world safely with tunnels Twenty lines of ownership Automation platforms are great until they’re not, limits, pricing, niche logic. Owning a receiver is cheaper than you fear. Inside api-lab ‘s venv: uv pip install flask receiver.py : from flask import Flask, request, jsonify import hmac, hashlib, os app = Flask(__name__) WEBHOOK_SECRET = os.environ[“WEBHOOK_SECRET”] # from .env, naturally @app.route(“/doorbell”, methods=[“POST”]) def doorbell(): sig = request.headers.get(“X-Signature”, “”) expected = hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): return jsonify(error=”bad signature”), 401 data = request.get_json() print(“Event arrived:”, data) return jsonify(status=”received”), 200 app.run(port=5001) Run it, then ring it: curl -X POST http://localhost:5001/doorbell -H “Content-Type: application/json” -H “X-Signature: ” -d ‘{“event”:”test”}’ Read what you built: one route listening on /doorbell ; signature verification first (the sender signs payloads with a shared secret; hmac.compare_digest checks theirs against yours, Chapter 1’s promise kept); only then does JSON get trusted. The localhost problem Your receiver works but lives at localhost:5001 , invisible to Razorpay or GitHub, who can’t reach your laptop . Enter tunnels : programs that give your local port a temporary public URL. With ngrok: ngrok http 5001 # โ https://random-name.ngrok-free.app That random URL now reaches your machine. Paste it into any service’s webhook settings and events arrive at your desk. (Free tiers rotate URLs on restart, fine for learning; production uses fixed domains.) Security stays non-negotiable even here: verify signatures always, keep secrets in .env , kill tunnels when done. A tunnel is a real door in your wall. Try it yourself Get receiver.py running. Test the rejection path first: POST without a signature header โ watch 401 defend the door. Then set WEBHOOK_SECRET=test123 in .env , sign a payload with Python ( hmac.new(b”test123″, body, hashlib.sha256).hexdigest() ), and enjoy the 200. Finally install ngrok, expose 5001, and hit your public URL from your phone’s browser-turned-curl or a friend’s curl. Screenshot the arrival log into learn/my-receiver.png . Key takeaways – A receiver = one route + signature check + JSON handling; Flask makes it ~20 lines. – Verify signatures before trusting anything; reject strangers with 401. – Tunnels (ngrok) make localhost temporarily public, power and responsibility included. – Own receivers beat platform limits once your flows outgrow them. Download the exercise sheet (PDF) Module workbook (PDF) โ Prev: Build a real pipeline Next: The always-on assistant โ Classroom / Module 06: Webhooks & Automation / Chapter 4 Roll your own receiver What you will learn – Receiving webhooks in your own Python server – Signature verification, properly – Exposing localhost to the world safely with tunnels Twenty lines of ownership Automation platforms are great until they’re not, limits, pricing, niche logic. Owning a receiver is cheaper than you fear. Inside api-lab ‘s venv: uv pip install flask receiver.py : from flask import Flask, request, jsonify import hmac, hashlib, os app = Flask(__name__) WEBHOOK_SECRET = os.environ[“WEBHOOK_SECRET”] # from .env, naturally @app.route(“/doorbell”, methods=[“POST”]) def doorbell(): sig = request.headers.get(“X-Signature”, “”) expected = hmac.new(WEBHOOK_SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): return js
Roll your own receiver
Written by
in