Source Code
Ngrok Webhook Listener
Start a public webhook endpoint via ngrok. Incoming webhooks are auto-routed to matching skills or presented to the user for manual handling.
Prerequisites
cd skills/ngrok-unofficial-webhook-skill
npm install
Environment Variables
Set in the skill's .env file (copy from .env.example).
Required:
NGROK_AUTHTOKENโ ngrok auth token from https://dashboard.ngrok.com
Optional:
NGROK_DOMAINโ stable ngrok domain for consistent URLsWEBHOOK_PORTโ local port (default:4040)WEBHOOK_PATHโ webhook path (default:/webhook)OPENCLAW_BINโ path to openclaw binary (default:openclaw)OPENCLAW_NOTIFY_CHANNELโ notification channel (default:whatsapp)OPENCLAW_NOTIFY_TARGETโ phone number / target for notifications
Usage
Start the webhook listener
Run as a background process:
cd skills/ngrok-unofficial-webhook-skill
node scripts/webhook-server.js
The server prints its public URL to stderr:
NGROK_URL=https://xxxx.ngrok-free.app
Webhook endpoint: https://xxxx.ngrok-free.app/webhook
For long-running use, launch with nohup:
nohup node scripts/webhook-server.js >> /tmp/ngrok-webhook.log 2>&1 &
What happens when a webhook arrives
- The server immediately responds 200 OK to the sender
- It discovers installed skills that declare
webhookEventsin theirskill.json - Auto-routing (no user intervention needed):
- If a matching skill has
forwardPortโ HTTP POST to the local service - If a matching skill has
webhookCommandsโ runs the configured shell command
- If a matching skill has
- Manual routing (user decides):
- If no auto-route is available, sends a WhatsApp notification with the payload and a numbered list of matching skills
- User replies with their choice
Skill discovery
Skills opt into webhook handling by adding webhookEvents to their skill.json:
{
"openclaw": {
"webhookEvents": ["meeting.rtms_started", "meeting.rtms_stopped"],
"forwardPort": 4048,
"forwardPath": "/"
}
}
For command-based auto-handling (no running service required):
{
"openclaw": {
"webhookEvents": ["recording.completed"],
"webhookCommands": {
"recording.completed": {
"command": "python3 scripts/download.py {{meeting_id}}",
"description": "Download cloud recording",
"meetingIdPath": "payload.object.id"
}
}
}
}
commandโ shell command to run;{{meeting_id}}is replaced with the extracted valuemeetingIdPathโ dot-separated path to extract the meeting ID from the webhook payloaddescriptionโ human-readable description for notifications
The ngrok skill scans all sibling skill folders for skill.json files with these fields.
Stdout output
The server also writes each webhook as a JSON line to stdout for process polling:
{
"id": "uuid",
"timestamp": "ISO-8601",
"method": "POST",
"path": "/webhook",
"query": {},
"body": {}
}
Health check
curl http://localhost:4040/health
Stop the listener
Kill the background process when done.
Integration with Zoom
Typical flow:
- Start this webhook listener โ get ngrok URL
- Configure the ngrok URL in your Zoom Marketplace app's webhook settings
- When RTMS starts, Zoom sends
meeting.rtms_startedโ auto-forwarded to the RTMS Meeting Assistant - When RTMS stops, Zoom sends
meeting.rtms_stoppedโ auto-forwarded, triggers cleanup