Messaging Webhook Handler Template
Purpose
Produces a production-ready webhook handler skeleton for a new messaging vendor. Every generated handler enforces the five governance requirements from webhook-security-hardening-2026-04-20.md: IP filter, signature verification, dedup via processed_webhook_events, non-blocking audit write to webhook_audit_log with local-file fallback, and dead-letter persistence.
Stops each team from reimplementing webhook hardening per vendor.
When to use
- During Phase 0B vendor onboarding after
vendor-sms-onboardingskill completes - When adding a second webhook path for an existing vendor (e.g. separate DLR endpoint)
- When a vendor changes signature verification scheme and the handler needs regeneration
Do NOT use for outbound-only vendors (no inbound webhook) or non-messaging webhooks (HubSpot, DocuSign have their own handlers).
Inputs
| Flag | Type | Required | Description |
|---|---|---|---|
--vendor | string | yes | Matches messaging_providers.name |
--auth-type | enum | yes | One of: hmac-sha256, hmac-ed25519, basic-auth, bearer |
--port | int | yes | Distinct local port per vendor (see port map below) |
--path | string | yes | Webhook URL path, e.g. /webhook/telnyx-sms |
--dedup-key-fn | string | no | JS expression to extract dedup key, e.g. event.data.id |
Outputs
workspace/webhooks/<vendor>-handler.js— handler wired to shared tools:webhook-sig-verify.js,phone-normalize.js,processed-webhook-events.js,webhook-audit-log.js~/.config/systemd/user/<vendor>-webhook.service— unit file loading master.env, auto-restart- Updated
workspace/FUNNEL-REGISTRY.mdwith new row (path, provider, port, sig-verify method, dedup, audit log, last reviewed date) - Updated
workspace/knowledge-base/security/WEBHOOK-IP-RANGES.mdunder the vendor heading - Cloudflare Tunnel ingress rule hint emitted to stdout (requires separate Cloudflare plan + approval before applying per
feedback_cloudflare_plan_before_execute.md)
Acceptance tests
- Handler file exists, passes
node --check, chmod 755 GET /<path>/healthreturns{"status":"ok","vendor":"<name>"}- POST with invalid signature returns 401
- POST with valid sig + duplicate event returns 200
{"duplicate":true} - POST with valid sig + new event returns 200 + row in
webhook_audit_log - With DB disconnected, handler still returns 200 and persists raw event to
/var/log/openclaw/webhook-fallback-<vendor>.jsonl - Systemd unit passes
daemon-reload+ boots afterenable --now - FUNNEL-REGISTRY contains the new row; next
security-audit-funnel.timerrun reports clean
Rollback behavior
systemctl --user disable --now <vendor>-webhook.service- Remove systemd unit file
- Remove Cloudflare Tunnel route via Cloudflare plan (never modify Cloudflare without a plan per governance)
- Revert FUNNEL-REGISTRY.md and WEBHOOK-IP-RANGES.md edits
- Delete handler file
- Set
messaging_providers.status = 'pending'for the rolled-back vendor
Port map
Per plan Section H.0B.1-6:
- telnyx: 18794
- bandwidth: 18795
- sent.dm: 18796
- twilio-voice: 18797 (existing)
- loopmessage: 18798
- bird: 18799
- linq: 18800
Related files
- Plan sections: G.5 and H.0B.1-6
- Shared deps:
tools/webhook-sig-verify.js,tools/phone-normalize.js,tools/processed-webhook-events.js,tools/webhook-audit-log.js - Governance:
workspace/FUNNEL-REGISTRY.md,workspace/knowledge-base/security/WEBHOOK-IP-RANGES.md - Prior-art handlers:
workspace/webhooks/salesmessage-handler-v4-complete.js,workspace/webhooks/quo-handler-enhanced.js - Webhook security plan:
/home/opsadmin/.claude/plans/webhook-security-hardening-2026-04-20.md