SalesMessage Webhook Handler v4
Enhanced inbound gateway for SalesMessage (buyer-side SMS) events. Orchestrates 24 event types across calls, voicemails, conversations, messages, and AI draft approvals. Drives buyer qualification, dispo response generation, and deal auto-creation for the acquisitions pipeline.
Endpoint
| Path | Provider | Status |
|---|---|---|
https://webhook.reri.co/webhook/salesmessage | SalesMsg | ✅ approved |
https://webhook.reri.co/webhooks/salesmessage | SalesMsg (alt spelling) | ✅ approved |
Port: 18793 (systemd unit: salesmsg-webhook.service)
Tunnel: Cloudflare Tunnel reri-api → webhook.reri.co (canonical)
Auth method
Query-parameter token — ?secret=<SALESMSG_WEBHOOK_SECRET>. This is NOT HMAC. SalesMsg sends the secret as a plain query param; the handler performs constant-time string comparison against the env var. This is intentional per SalesMsg’s API design — do not switch to HMAC without verifying live event headers first (per CLAUDE.md FUNNEL-REGISTRY governance).
Payload shape
24 event types across 5 categories:
| Category | Event Types |
|---|---|
call_events | Calls, voicemails |
webhook_events | All raw system events |
conversations | Lead tracking, hot scoring |
messages | All inbound/outbound text |
message_approvals | AI-generated draft queue |
Dispo inboxes routed specially: 128279 (Dispo A) · 143887 (End Buyer CA) · 160031 (End Buyer FL)
Downstream dispatch
salesmessage-handler-v4-complete.js
├─ checkCompliance() + checkQuietHours() → compliance-gate.js (runs BEFORE any send)
├─ classifyMessage() → deal-parser.js → classifyBuyerResponse() → response-intelligence.js
├─ extractDeals() → deal-parser.js → createDealFromParsed() → hubspot-deal-creator.js
├─ gatherBuyerContext() → workspace-dispo/lib/context-gatherer.js
├─ generateDispoResponse() → workspace-dispo/lib/draft-generator.js
├─ postApprovalCard() → workspace-dispo/lib/message-approval-card.js (queues for human approval)
├─ evaluateAfterMessage() → lib/rules-evaluator.js
├─ recordBlastResponse() → blast-safety.js (saturation tracking)
├─ cancelFollowups() → follow-up-engine.js (cancels pending follow-ups on response)
├─ handleBroadcastResponse() → lib/broadcast-response-handler.js
├─ startQualification() / continueQualification() → lib/showing-qualifier.js
└─ buildAcquisitionDealCard() → acquisition-deal-card.js → Discord #acquisitions
Graceful shutdown: On SIGTERM, cancels all _pendingTimeouts (delayed send queue) before exit — prevents duplicate sends across service restarts.
Dedup strategy
Uses checkDedup() from scripts/lib/webhook-dedup.js — 24-hour TTL in processed_webhook_events Supabase table. Failed/unprocessable payloads written to data/dead-letters/ directory.
Audit trail
webhook_audit_log— non-blocking write on each handled event.- Dead letter queue:
data/dead-letters/— failed events for manual replay. - Discord: Deal card notifications to
#acquisitions(channel1479157035668078786). - Compliance gate: Every outgoing message passes
checkCompliance+checkQuietHours— rejections are logged with reason.
Related
- webhook-architecture — Cross-handler governance, dedup pattern, FUNNEL-REGISTRY invariants; note: SalesMsg uses query-param NOT HMAC
- salesmsg — SalesMsg API reference, inbox IDs, event shape, 24-event type catalog
- _summary — Aurora drives buyer response generation via this handler
- _summary — Deal auto-creation pipeline, blast safety, follow-up cancellation
- cron-timer-registry —
salesmsg-webhook.servicesystemd unit - backend-model-map — LLM routing for draft generation via Portkey proxy