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
| Path | Provider | Status |
|---|---|---|
https://webhook.reri.co/webhook/openphone | OpenPhone | ✅ approved |
https://webhook.reri.co/webhook/quo | OpenPhone alias | ✅ approved |
Port: 18792 (systemd unit: openphone-webhook.service)
Tunnel: Cloudflare Tunnel reri-api → webhook.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:
| Field | Type | Description |
|---|---|---|
type | string | Event type (e.g., message.received, call.completed) |
data.from | string | Sender’s phone number |
data.body | string | Raw text content of the message |
data.conversationId | string | Unique OpenPhone conversation identifier |
data.phoneNumberId | string | OpenPhone 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 viaauditLog()fromscripts/lib/webhook-audit.js.- Fallback:
/tmp/webhook-audit-fallback.jsonllocal append if Supabase unreachable. - Fatal error handler:
uncaughtException+unhandledRejectionlog structured JSON → process exit for systemd restart.
Related
- 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-registry —
openphone-webhook.servicesystemd unit, restart policy - backend-model-map — LLM routing for response generation via Portkey proxy