โ† Back to AI & LLMs
AI & LLMs by @tanchunsiong

ngrok-unofficial-webhook-skill

Start an ngrok tunnel

0
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:

Optional:

  • NGROK_DOMAIN โ€” stable ngrok domain for consistent URLs
  • WEBHOOK_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

  1. The server immediately responds 200 OK to the sender
  2. It discovers installed skills that declare webhookEvents in their skill.json
  3. 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
  4. 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 value
  • meetingIdPath โ€” dot-separated path to extract the meeting ID from the webhook payload
  • description โ€” 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:

  1. Start this webhook listener โ†’ get ngrok URL
  2. Configure the ngrok URL in your Zoom Marketplace app's webhook settings
  3. When RTMS starts, Zoom sends meeting.rtms_started โ†’ auto-forwarded to the RTMS Meeting Assistant
  4. When RTMS stops, Zoom sends meeting.rtms_stopped โ†’ auto-forwarded, triggers cleanup