OpenPhone QUO Webhook Handler

High-intelligence inbound gateway for OpenPhone SMS and conversation events. This handler serves as the primary ingestion point for all communications sent to the RERI acquisition lines. It performs real-time message classification, compliance gating, and deal detection to transform raw SMS traffic into structured acquisition opportunities.

Endpoint

PathProviderStatus
https://webhook.reri.co/webhook/openphoneOpenPhone✅ approved
https://webhook.reri.co/webhook/quoOpenPhone alias✅ approved

Port: 18792 (systemd unit: openphone-webhook.service) Tunnel: Cloudflare Tunnel reri-apiwebhook.reri.co (canonical); Tailscale Funnel as fallback

Auth method

HMAC-SHA256 — OpenPhone signs requests using HMAC(secret, timestamp.body). The handler verifies this against OPENPHONE_WEBHOOK_SECRET (env var from master.env). Signature is in a request header; timestamp is also provided for anti-replay protection.

Payload shape

OpenPhone sends event objects representing message arrivals or conversation updates. Key fields:

FieldTypeDescription
typestringEvent type (e.g., message.received, call.completed)
data.fromstringSender’s phone number
data.bodystringRaw text content of the message
data.conversationIdstringUnique OpenPhone conversation identifier
data.phoneNumberIdstringOpenPhone line ID — used to identify acquisition line

Acquisition lines monitored (11 lines across CA/FL/GA/AZ/NV): PN5ZpDFnCr (RERI|CH CA primary), PNpMDy0oHA, PN2xGVs9oG, PNn2a3c4AW, PNg0Y0Un2O, PN4GfD7RX2, PNgCmeYoIb, PNtdk2ygg5, PNakeq749y, PNNvDpPHA6, PNVi9LkaNK

Downstream dispatch

quo-handler-enhanced.js
 ├─ classifyMessage() → conversation-classifier.js (intent/sentiment)
 ├─ checkCompliance() → compliance-gate.js (legal/regulatory)
 ├─ computeGate() → gate-computer.js (5-gate acquisition gate)
 ├─ parseDealFromMessages() → deal-parser.js → hubspot-deal-creator.js
 │   └─ findDealByAddress() → HubSpot dedup check
 ├─ fetchThreadContext() → thread-context.js (rep name, relationship)
 ├─ getWinningExamples() → winning-examples.js (few-shot response context)
 ├─ gatherContext() → response-generator.js → Aurora LLM response
 ├─ scoreResponse() / shouldReject() → response-scorer.js
 ├─ sendDealCard() → acquisition-deal-card.js → Discord #acquisitions
 └─ sendSmsViaOpenPhone() → openphone-sms.js (CH_LINE_PRIORITY)

Discord webhooks: #ops (OPS_WEBHOOK) + #acquisitions (DEALS_WEBHOOK)

Dead letter queue: Failed payloads written to data/dead-letters/ for manual replay.

Dedup strategy

Uses checkDedup() from scripts/lib/webhook-dedup.js — writes event ID to processed_webhook_events Supabase table with a 24-hour TTL. Duplicate events within the window return 200 OK immediately without reprocessing.

Audit trail

  • webhook_audit_log — non-blocking write via auditLog() from scripts/lib/webhook-audit.js.
  • Fallback: /tmp/webhook-audit-fallback.jsonl local append if Supabase unreachable.
  • Fatal error handler: uncaughtException + unhandledRejection log structured JSON → process exit for systemd restart.
  • webhook-architecture — Cross-handler governance, dedup pattern, audit log schema, FUNNEL-REGISTRY invariants
  • openphone-quo — OpenPhone API reference, line IDs, phone number map, HMAC signature spec
  • _summary — Aurora generates SMS responses via this handler’s dispatch chain
  • _summary — Acquisition workflow — deal detection, gate scoring, HubSpot deal creation
  • cron-timer-registryopenphone-webhook.service systemd unit, restart policy
  • backend-model-map — LLM routing for response generation via Portkey proxy