Outreach Stage 1

Stage 1 is the first-touch SMS from RERI to a wholesaler or contract holder (CH) for a newly-ingested deal. It is the entry point of the acquisitions conversation — all subsequent follow-up (Stages 2-4) depends on this send. Stage 1 is OpenPhone-only (not SalesMsg). Every send passes all 5 compliance gates defined in compliance-gates before any message leaves. Read this hub after reading compliance-gates. Owner: acquisitions agent.

Critical rules — do not skip:

  • NEVER bypass the compliance gate (all 5 gates required on every first-touch send)
  • NEVER run without —dry-run during audit (use --dry-run to validate without sending)
  • NEVER send without explicit Henry auth (per feedback_never_send_without_auth)
  • Stage 1 only — this skill sends first-touch only. Follow-ups (Stage 2+) go to followup-stages-2-3-4

Quick reference

FieldValue
StagesDeal ingest → Approval gate → 5-gate compliance → Template select → Mentor voice compose → OpenPhone send → Quo note
Primary agent_summary
Supporting agents_summary, _summary
Agent handoff chaindeal-ingestion → checkApprovalGate → unified-outreach-engine → gate-computer → compliance-gate → blast-safety → thread-context → response-generator → openphone-sms → quo-note-builder → outreach-stage1 audit → [if reply received] followup-stages-2-3-4
Compliance gates listgate-computer, compliance-gate, blast-safety, thread-context, response-generator
Skills invokedacquisitions-outreach, messaging-compliance-gate
Success metricsopenphone_events row with event_type='outbound_sms'; Quo internal note posted; deal qualification_status transitions logged
Cost per stage~0.0075/segment)
Throughput~50-200 deals/day evaluated; ~20-80 Stage 1 sends/day (after gate filtering)
Last run resultSee openphone_events most recent event_type='outbound_sms' row
Failure modesGate trips → no send; approval gate red-zone → CLI-only; lineId missing → no note (fail-closed); OpenPhone 429 → rotate line

Architecture

acquisition_deals row (newly ingested, qualification_status='pending')
        │
        ▼
[checkApprovalGate(deal)] — B7 three-zone gate
        │
        ├─ 🟢 Green: reri_arv_flag=null or 'ok' → auto-approve
        ├─ 🟡 Yellow: reri_arv_flag='pre_negotiation' → 4h hold
        └─ 🔴 Red: reri_arv_flag='manual_review_required' → CLI only
        │ approved
        ▼
[unified-outreach-engine.js] — coordinator
        │
        ├─→ [gate-computer.js] — select gate 1-5 + template scenario
        ├─→ [compliance-gate.js] — quiet hours, TCPA, 24h dedup
        ├─→ [blast-safety.js] — cross-deal dedup, suppression, line saturation
        ├─→ [thread-context.js] — prior message history, lineId resolution
        ├─→ [mentor-voice.js] — touch count → mentor selection
        └─→ [response-generator.js] — v3 Dispo Conversion Agent compose
        │ all 5 gates pass
        ▼
[openphone-sms.js] — OpenPhone send (acq is OpenPhone-only)
        │
        ▼
[quo-note-builder.js] — §4.1 canonical 7-line Quo internal note
        │
        ▼
openphone_events (Supabase CCP audit trail)
        │
        ▼
→ [[wiki/_hubs/processes/followup-stages-2-3-4]] (when CH replies OR 4h passes)

B7 Approval Gate (three zones)

Checked before every first-touch send via checkApprovalGate(deal):

ZoneConditionBehavior
🟢 Greenreri_arv_flag is null or 'ok'Auto-approve; engine picks up immediately
🟡 Yellowreri_arv_flag='pre_negotiation'Hold 4h; deal-outreach-auto-approver.js (every 30 min) flips qualification_status='approved' after window
🔴 Redreri_arv_flag='manual_review_required'CLI only: node deal-outreach-worker.js --deal-id=X --approve

qualification_status='approved' overrides any flag. qualification_status='rejected' blocks permanently.

SMS template scenarios

Gate result maps to template scenario via resolveStage(deal, gateResult):

ScenarioTemplate fileTrigger
initial-outreachdeal-qualification/templates/sms/initial-outreach.jsonGate 1, no pre-negotiation flag
detail-requestdeal-qualification/templates/sms/detail-request.jsonGate 2-3
gap-filldeal-qualification/templates/sms/gap-fill.jsonGate 4
pre-negotiationdeal-qualification/templates/sms/pre-negotiation.jsonreri_pre_negotiation_triggered=true (tight spread)
offer-inquirydeal-qualification/templates/sms/offer-inquiry.jsonGate 5 / qualifying deal

reri_pre_negotiation_triggered=true always routes to pre-negotiation regardless of gate.

Mentor voice cadence

feedback_acq_mentor_voice_cadence — touch count determines which mentor’s voice is applied:

TouchMentorVoice style
T1-2Mike FerryDirect, confident, one question, no filler
T3-6Mandeep DamjiRelational, warm, references their situation
T7-13David NortonCertain, anchors commitment, no hedging
T14-20Grant CardoneUrgency, scarcity framing, action-oriented
T21+Sean TerryObjection-counter, floor-probing

One question per send (Mike Ferry rule). feedback_acq_no_pass_and_pivot — never use pass+pivot. feedback_acq_thread_history_aware_gates — skip gates already satisfied by thread history.

Quo internal note format (§4.1)

After every outbound send, quo-note-builder.js posts a 7-line emoji note to the Quo conversation. Fail-closed: if lineId cannot be resolved from fromNumber, no note is posted. No hardcoded fallbacks.

📍 City / State / Zip
📈 Stage: <label>

🔗 IL URL
📁 HubSpot link

👤 N total deals from this CH on file
🗺️ STATE · STATE
⏳ Only deal on file from this CH   (or +N more...)

Entry points (CLI)

Bulk worker

node /home/opsadmin/.openclaw/workspace/scripts/workers/unified-outreach-engine.js \
  [--state=FL]           # filter to deals in this state
  [--limit=N]            # max deals per run
  [--dry-run]            # gate + compose, NO send — ALWAYS run dry-run during audit
  [--phase=new|followup|all]  # filter by action type

Single-deal trigger

node /home/opsadmin/.openclaw/workspace/scripts/workers/deal-outreach-worker.js \
  --deal-id=<acquisition_deals.id> \
  [--approve]            # flip qualification_status='approved' (red-zone CLI override)
  [--dry-run]

Yellow-zone auto-approver (background timer)

node /home/opsadmin/.openclaw/workspace/scripts/workers/deal-outreach-auto-approver.js \
  [--dry-run]
  [--hours=4]            # review window (default 4)

Runs automatically via deal-outreach-auto-approver.timer (every 30 min).

OpenPhone line routing

Stage 1 sends are OpenPhone-only. Line selection uses STATE_LINES map in unified-outreach-engine.js:

StateLine
CACA line
NVNV line
FLFL line
GAGA line
AZAZ line

10DLC note: All OpenPhone lines are subject to A2P 10DLC carrier compliance. Until osil-twilio-10dlc-resubmission-2026-05-03 B14 resolves (campaign resubmission approved), carrier filtering may reduce deliverability. Monitor delivery rates in openphone_events.

Components

  • workspace/scripts/workers/unified-outreach-engine.js — coordinator (canonical entry point)
  • workspace/scripts/workers/deal-outreach-worker.js — single-deal trigger
  • workspace/scripts/workers/deal-outreach-auto-approver.js — yellow-zone 4h timer
  • workspace/scripts/workers/smart-outreach-worker.js — batch legacy (superseded by engine)
  • workspace/scripts/lib/gate-computer.js — 5-gate qualification progression
  • workspace/scripts/lib/compliance-gate.js — TCPA / quiet hours (see compliance-gates)
  • workspace/scripts/lib/blast-safety.js — dedup + suppression (see compliance-gates)
  • workspace/scripts/lib/thread-context.js — prior message history (see compliance-gates)
  • workspace/scripts/lib/response-generator.js — v3 Dispo Conversion Agent
  • workspace/scripts/lib/mentor-voice.js — touch→mentor cadence
  • workspace/scripts/lib/quo-note-builder.js — §4.1 Quo internal note
  • workspace/scripts/lib/openphone-sms.js — OpenPhone send wrapper
  • deal-qualification/templates/sms/ — 5 SMS template files
  • workspace/knowledge-base/openphone/OUTREACH-RULES.md — 10 compliance rules
  • workspace/knowledge-base/openphone/MENTORS.md — mentor profiles + anti-patterns

How it’s used

  • Trigger: new deal created (via deal-ingestion) AND qualification_status transitions to approved or auto-approved; OR Henry instructs “send initial outreach for deal X”
  • Workflow: approval gate → 5-gate compliance check → template select → mentor compose → OpenPhone send → Quo note → audit log
  • Agents involved: _summary (orchestrator), _summary (monitoring + Quo note via gateway)
  • Handoff: after Stage 1 send, deal waits for reply; if no reply in 4h → followup-stages-2-3-4 picks up NO_REPLY_4H trigger
  • Success criteria: openphone_events row with event_type='outbound_sms'; acquisition_deals.last_outreach_at updated; Quo conversation has 7-line note

Agents that touch this

  • _summary — primary orchestrator; runs engine, evaluates approval gate
  • _summary — monitoring; posts Quo internal notes via gateway
  • _summary — deal field audit before approval gate
  • _summary — Solara variant uses separate follow-up-scheduler (not Stage 1)

Skills that invoke this

Plans that govern this

Feedback rules

KB / source docs

  • README — OpenPhone API, line routing, quiet hours
  • README — 10DLC carrier compliance (B14 blocker)

System maps

HubRole
compliance-gatesPrerequisite — all 5 gates defined here; outreach-stage1 depends on this
followup-stages-2-3-4Downstream — picks up after Stage 1 send (4h no-reply or inbound reply)
openphone-quoOpenPhone send channel + inbound webhook (:18792)
twilioCarrier compliance, 10DLC B14 blocker
salesmsgSalesMsg handles follow-up SMS (Stage 2+ via handler :18793)
deal-ingestionUpstream — deal must exist before Stage 1 can fire

Open issues / TODOs

  • B14: 10DLC campaign resubmission pending — carrier deliverability impacted until resolved. Monitor OpenPhone delivery rates.
  • Yellow-zone auto-approver (deal-outreach-auto-approver.timer) — verify timer is running: systemctl --user status deal-outreach-auto-approver.timer
  • smart-outreach-worker.js is legacy (superseded by unified-outreach-engine.js) — retirement pending Phase cleanup
  • Mentor voice touch counter (acquisition_deals.touch_count) vs actual openphone_events send count — verify parity monthly

Recent activity

  • 2026-05-03: hub created (W1-S8)