# MeetStream Guide: Test Webhooks Locally with ngrok or Cloudflare Tunnel When you’re building a MeetStream integration, you’ll often want to test **webhooks locally** (on your laptop) before deploying a server. This guide shows how to: - run a local webhook server - expose it to the internet using **ngrok** or **Cloudflare Tunnel (cloudflared)** - paste the public URL into MeetStream as your `callback_url` --- ## What you’re building 1. You run a local HTTP server (example: `http://localhost:3000/webhooks/meetstream`) 2. You create a tunnel that gives you a public URL (example: `https://abc123.ngrok-free.app/webhooks/meetstream`) 3. You use that public URL as MeetStream’s webhook `callback_url` --- ## 0) Create a simple local webhook endpoint Pick any stack. Here are two minimal examples. ### A) Node.js (Express) ```bash mkdir meetstream-webhook-test && cd meetstream-webhook-test npm init -y npm i express ``` Create `server.js`: ```js import express from "express"; const app = express(); app.use(express.json({ type: "*/*" })); app.post("/webhooks/meetstream", (req, res) => { console.log("✅ Webhook received"); console.log("Headers:", req.headers); console.log("Body:", req.body); res.status(200).send("ok"); }); app.listen(3000, () => console.log("Listening on http://localhost:3000")); ``` Run it: ```bash node server.js ``` Your local endpoint is now: - `http://localhost:3000/webhooks/meetstream` --- ### B) Python (FastAPI) ```bash pip install fastapi uvicorn ``` Create `app.py`: ```py from fastapi import FastAPI, Request app = FastAPI() @app.post("/webhooks/meetstream") async def webhook(req: Request): body = await req.json() print("✅ Webhook received") print("Headers:", dict(req.headers)) print("Body:", body) return {"ok": True} ``` Run it: ```bash uvicorn app:app --port 3000 --reload ``` Endpoint: - `http://localhost:3000/webhooks/meetstream` --- ## 1) Option A — Use ngrok (quickest) ### Step 1: Install ngrok - macOS (Homebrew): ```bash brew install ngrok/ngrok/ngrok ``` Or download from ngrok website and follow install steps. ### Step 2: Authenticate ngrok ```bash ngrok config add-authtoken ``` ### Step 3: Start a tunnel to your local port If your local server is running on `localhost:3000`: ```bash ngrok http 3000 ``` ngrok will print a public HTTPS URL like: - `https://abc123.ngrok-free.app` ### Step 4: Use the public URL as callback_url If your route is `/webhooks/meetstream`, your final webhook URL becomes: - `https://abc123.ngrok-free.app/webhooks/meetstream` Use this in your Create Bot payload: ```json { "callback_url": "https://abc123.ngrok-free.app/webhooks/meetstream" } ``` --- ## 2) Option B — Use Cloudflare Tunnel (cloudflared) Cloudflare Tunnel is great if you already use Cloudflare or want a stable tunnel. ### Step 1: Install cloudflared - macOS (Homebrew): ```bash brew install cloudflare/cloudflare/cloudflared ``` ### Step 2: Start a quick (temporary) tunnel If your local server is on port `3000`: ```bash cloudflared tunnel --url http://localhost:3000 ``` cloudflared will output a public URL like: - `https://something.trycloudflare.com` ### Step 3: Use it as callback_url Final webhook URL: - `https://something.trycloudflare.com/webhooks/meetstream` --- ## 3) Verify your local webhook is receiving events ### A) Add the URL while creating the bot Include in Create Bot / Create Agent payload: ```json { "callback_url": "https:///webhooks/meetstream" } ``` ### B) Trigger a bot lifecycle You should see incoming webhook payloads for events like: - `bot.joining` - `bot.inmeeting` - `bot.stopped` --- ## 4) Common issues & fixes ### Webhook not arriving? - Confirm your local server is running and the endpoint path matches exactly. - Ensure your tunnel is pointing to the right port (e.g., 3000). - Make sure your webhook handler returns **2xx** quickly. ### “502 Bad Gateway” in ngrok / Cloudflare - Your local server is not reachable on that port. - Try opening locally: - `curl -X POST http://localhost:3000/webhooks/meetstream -H "Content-Type: application/json" -d '{}'` ### Missing JSON body - Ensure you have JSON parsing enabled: - Express: `app.use(express.json({ type: "*/*" }))` - FastAPI already parses JSON with `await req.json()` --- ## 5) Production best practices (when you deploy) - Make webhook handling **idempotent** (avoid double-processing). - Verify webhook signatures (if configured). - Log webhook events + store minimal event history for debugging. - Don’t run tunnels in production — use a hosted endpoint. --- If you tell me your preferred runtime (Node / Python / Bun / Go), I can add a “copy‑paste ready” webhook handler with signature verification + de-duplication.