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

PathProviderStatus
https://webhook.reri.co/webhook/salesmessageSalesMsg✅ approved
https://webhook.reri.co/webhooks/salesmessageSalesMsg (alt spelling)✅ approved

Port: 18793 (systemd unit: salesmsg-webhook.service) Tunnel: Cloudflare Tunnel reri-apiwebhook.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:

CategoryEvent Types
call_eventsCalls, voicemails
webhook_eventsAll raw system events
conversationsLead tracking, hot scoring
messagesAll inbound/outbound text
message_approvalsAI-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 (channel 1479157035668078786).
  • Compliance gate: Every outgoing message passes checkCompliance + checkQuietHours — rejections are logged with reason.
  • 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-registrysalesmsg-webhook.service systemd unit
  • backend-model-map — LLM routing for draft generation via Portkey proxy