AI outbound sales · insurance · compliance-first

A voice agent that sells insurance by the rules, and won't make things up.

Eastwood takes purchased or inbound leads, decides who may lawfully be called and who is worth calling, then has Imogen, a realtime AI voice assistant, run the discovery call. She screens health, presents deterministic quotes, and books the prospect with a licensed human agent who closes. Every step is gated by code, not by asking the model nicely.

Pre-launch: built and test-verified, awaiting production credentials and DNC registry data
453automated tests passing
2services: Python core + TypeScript agent
12tools Imogen can call, each gated
17Kafka topics on the event bus
34Prometheus metrics
51states + DC with local calling windows
How it works

From a row in a spreadsheet to a booked appointment

Each lead passes through the same pipeline. The green steps are hard gates: if one fails, the lead stops there, and nothing downstream can override it.

  1. Ingest

    CSV or PDF lead exports with fuzzy column mapping, an HTTP ingest API, signed Facebook Lead Ads webhooks, and web forms.

  2. Normalize

    Phones to E.164, product lines, age from DOB, and consent records. Unmapped columns are kept for audit.

  3. Compliance gates

    TCPA 8am–9pm local window, National DNC clearance (fail-closed), prior opt-outs, express written consent.

  4. Triage & score

    Medical knockouts, severity scoring, wealth and fit signals, then a route: auto-dial, request consent, nurture, or suppress.

  5. Dial queue

    A concurrency cap, a 4/24/32/56/80-hour retry ladder, max attempts, and idempotent handling of Twilio status callbacks.

  6. Imogen's call

    Discloses she is an AI, confirms identity, runs needs discovery and health screening, and captures only what she clearly heard.

  7. Quote

    Unlocks only after discovery is complete. The engine picks three options; the model only explains them.

  8. Book & hand off

    A real Calendly booking, a full prospect profile persisted, and an SMS brief to the licensed agent. Outcome-aware follow-ups run after.

The voice agent

Meet Imogen: silence is never an answer

Speech-to-speech models predict the likeliest next line of a sales call, which is "Perfect, thanks" followed by the next question, even when the prospect only coughed. Eastwood takes the decision to speak away from voice detection and rules on what was actually said first.

Illustrative excerpt · turn gate in action
IMOGEN
And do you currently use any tobacco or nicotine products?
PROSPECT
[half-second of line noise]
turn_gate → not_heard · noise_only · slot tobacco stays UNKNOWN · "Perfect, thanks" blocked
IMOGEN
Sorry, I didn't catch that. Was that a yes or a no?
PROSPECT
No, quit years ago.
transcript final · confidence 0.94 · answers yes_no → slot tobacco CONFIRMED = "no"
IMOGEN
Good to know. Have you been diagnosed with any heart conditions in the last two years?

Three-state slots

Every answer moves UNKNOWN → HEARD → CONFIRMED. Only confirmed values reach the quote engine or the handoff brief. "Unknown" is an acceptable outcome; a wrong value is not.

Transcript grounding

To store a value, the model must quote what it heard. That quote is matched against finalized transcripts of the prospect's own speech and scored with per-token logprob confidence.

A discovery state machine

Purpose → beneficiary → burial or cremation → coverage → rating sex → tobacco → health. Code owns which question is next; the persona owns the wording.

Turn-taking that holds up on a real phone line

Barge-in cancels speech and flushes queued audio. A watchdog nudges a stalled model, then ends the call cleanly. After three unheard turns she names the line problem instead of looping.

Everything it can do

Capabilities

Grouped by subsystem. Every item below exists in the code today; the core paths are covered by the 453 automated tests.

01 · Intake

Lead ingestion

  • Any vendor CSV, with fuzzy column-to-field mapping
  • PDF lead exports (tables extracted page by page)
  • POST /api/leads/ingest HTTP API
  • Facebook Lead Ads webhook, signature-verified
  • Browser lead uploader and a 60-second "risk scan" funnel
  • Lead batches tracked by vendor and cost for ROI
02 · Scoring

Enrichment, scoring & routing

  • Optional OpenFEC donor and NPI provider lookups
  • Home-value wealth tiers and per-product age-fit bands
  • Consent freshness, state fit, recent-touch penalty
  • Hot / warm / cold bands with configurable thresholds
  • Routes to auto_dial, request_consent_sms, nurture, manual_review, or suppress
03 · Triage

Medical field underwriting

  • L0 compliance and contactability gate
  • L1 universal knockouts: hospice, dementia, ALS, and more
  • Guaranteed-issue floor conditions
  • Severity scoring with comorbidity bumps and lookback windows
  • Medication proxies (insulin → diabetes, Eliquis → cardiac)
  • CALL_NOW / WORKABLE / DEAD buckets, a versioned ruleset
04 · Placement

Carrier placement engine

  • Ranks carriers for each lead and the rate tier each would likely offer: level, graded, modified, or GI
  • Lists the exact questions still needed per carrier
  • 9 profiles: American Amicable, Mutual of Omaha, Royal Neighbors, Transamerica, Prosperity Life, Aetna / CVS, Humana
05 · Voice

Imogen, the realtime voice agent

  • OpenAI Realtime speech-to-speech over Twilio Media Streams
  • Discloses she is an AI up front and whenever asked
  • Tier-1 screening, plus Tier-2 follow-ups added when a condition is confirmed
  • Turn gate, slot state, transcript grounding, barge-in, watchdog
  • Six product scripts: final expense, life, annuity, Medicare Advantage, Medigap, ACA
06 · Tools

12 tools, unlocked by data

  • update_lead_field, add_followup_questions
  • get_quote, select_quote_option
  • get_booking_slots, book_appointment
  • capture_consent, capture_objection, capture_dnc
  • transfer_live, transfer_to_human, end_call
  • Availability is derived from what's been captured. After a do-not-call request, only end_call remains.
07 · Quoting

Deterministic three-option quotes

  • Engine selects options C / B / A (minimum, recommended, maximum within budget)
  • Adapters: CMS Medicare data API, carrier rate tables, formula estimates
  • Honesty labels on every quote: quote_type, rate_source, carrier_rate, bindable
  • Carrier name hidden on estimates
  • Premium-per-$1k sanity bands veto implausible quotes
08 · Guardrails

Live policy engine

  • Blocks "guaranteed", "can't be denied", "no questions asked"
  • Blocks implying Medicare plans are a government benefit (CMS)
  • Blocks reading back SSNs or bank and routing numbers
  • Blocks vague "$60 to $80" ranges once exact quotes exist
  • Every decision counted in agent_policy_decisions_total
09 · Handoff

Booking & licensed-agent handoff

  • Calendly Scheduling API: offers real availability and books the slot
  • Idempotent bookings, with honest failure if Calendly is down
  • Full prospect profile persisted in handoff_profiles
  • SMS brief: name, time, purpose, beneficiary, budget, health, quote, motivation, objections, open questions
  • Outcome SMS after every connected call; a live-transfer alert for hot prospects
10 · Follow-up

Outcome-aware sequences

  • Missed call, voicemail left, interested-but-busy, no contact
  • Quoted-not-closed, price objection, spouse objection
  • Application not completed, payment failed
  • Mixes SMS and callbacks with personalized templates
  • A do-not-call request cancels every pending touch
11 · Compliance

Regulatory controls

  • TCPA calling window per state time zone (all 50 states + DC)
  • Official FTC National DNC dataset: checksummed, freshness-limited, SAN stored only as a fingerprint
  • Opt-out suppressions honored on re-import
  • Consent evidence packs with SHA-256 hashes
  • Boot guards: production refuses to start in an unsafe configuration
12 · Operations

Operator APIs & observability

  • Closer cockpit: best-first queue with placement and a suggested opener
  • Batch ROI and lead-source ROI, computed live
  • Call-outcome tagging and a dashboard summary
  • Prometheus + Grafana, per-turn latency reports, call tracing
  • Answering-machine detection and an abandoned-session reaper
Boundaries

What it does, and what it deliberately doesn't

The right column matters as much as the left. Most of these refusals are enforced in code, at the point where the action would happen.

Eastwood does

  • Pre-qualify and educate. Discovery, health screening, and carrier fit before a human spends a minute.
  • Present real numbers it can stand behind. Deterministic options, labeled as estimates or carrier rates.
  • Book the licensed agent, with a complete, factual brief and a persisted profile.
  • Honor opt-outs instantly. A mid-call "stop calling" suppresses the number and cancels redials.
  • Keep evidence. Consent text verbatim, hashed evidence packs, a field-level capture log.
  • Measure itself. Turn latency, gate verdicts, tool vetoes, session leaks, duplicate events.

Eastwood does not

  • Sell, bind, or apply. Imogen is not licensed. No quote is bindable, no application is submitted, and no payment is taken.
  • Pretend to be human. She opens as an AI and answers truthfully if asked.
  • Dial without clearance. No call outside 8am–9pm local, to any number not explicitly clear or exempt on the DNC registry, or without express written consent.
  • Invent answers. Unheard or unclear means the slot stays unknown, never a guess.
  • Collect sensitive identifiers. No SSNs and no bank details, on the call or in read-backs.
  • Promise approval or imply Medicare plans are a government benefit.
  • Name a carrier on an estimate, or quote before discovery is complete.
  • Bridge live transfers automatically. A hot prospect triggers an SMS alert for the closer to call in.
  • Handle inbound calls, serve multiple tenants, or ship an operator UI. It's outbound-only and single-tenant, with JSON APIs and Grafana.
Architecture

Two services, one event bus

Everything runs from a single docker compose up. Only signed Twilio webhooks and the media WebSocket are exposed at the edge; health checks, metrics, and session admin stay internal.

Edge
nginx/api · /twiml · /twilio · media WebSocket · landing pages
Services
eastwood-corePython 3.11 · FastAPI · SQLAlchemy async

Ingest, normalization, triage, scoring, routing, DNC/TCPA gates, placement, quoting, booking, dialer loop, follow-up worker, notifications, operator APIs.

ai-agentTypeScript · Node 20 · Fastify · XState

Imogen: the Twilio ⇄ OpenAI Realtime bridge, turn gate, discovery flow, slot state, tool gating, policy engine, call tracing.

Kafka (17 topics: leads.* · calls.* · agent/consent/dlq events) and bearer-authenticated HTTP
Data & telemetry
PostgreSQL 16core + agent databases, 11 migrations
Kafka 3.7KRaft mode, 3 partitions per topic
Redis 7idempotency keys, 7-day dedupe
Prometheus + Grafana34 metrics across both services
External services
Twilio Voice · SMS · Media Streams · AMD OpenAI Realtime Calendly Scheduling API CMS Data API FTC National DNC Registry OpenFEC NPPES NPI Registry Facebook Lead Ads
Technologies

The full stack

eastwood-core (Python)

FastAPI · Uvicorn
HTTP API, webhooks, background loops
Pydantic v2
Settings and validation (pydantic-settings)
SQLAlchemy 2 · asyncpg
Async ORM on PostgreSQL; Alembic available
aiokafka
Event consumers and producers
httpx · tenacity
Outbound APIs with retries
twilio
Calls, SMS, AMD, RequestValidator
phonenumbers
E.164 normalization
structlog · orjson
Structured logs, fast JSON
cryptography
Signatures and fingerprints
prometheus-client
Metrics
pytest
188 tests

ai-agent (TypeScript)

Node.js ≥ 20 · TS 5
Strict TypeScript, run with tsx
Fastify 4
HTTP + @fastify/websocket media stream
ws
WebSocket client to OpenAI Realtime
XState 5
Sales stage model
Zod
Config and event schemas
KafkaJS
Event bridge to the core
Prisma 5
Agent database client
Pino
Structured logging
prom-client
27 agent metrics
node:test
265 tests, including end-to-end call simulations

AI models

gpt-realtime-2
Speech-to-speech voice ("shimmer")
gpt-4o-transcribe
Input transcription with logprobs for confidence
Server VAD
Tuned: 900 ms silence, 0.6 threshold; gated by the turn gate

Infrastructure

Docker Compose
9 services, one command
Apache Kafka 3.7
KRaft, no ZooKeeper
PostgreSQL 16
Two databases, SQL migrations
Redis 7
Idempotency keys for the legacy pipeline
nginx 1.25
Edge proxy, forwarded-header hygiene
Prometheus · Grafana
Scraping and dashboards
Where it stands

Built and verified; not yet dialing

The application code is complete for a first controlled campaign. What remains is configuration, credentials, and data. That work is tracked in the go-live checklist.

Done

  • ✓453 tests passing; TypeScript compiles clean
  • ✓Twilio signature validation and API auth, verified on the live Docker stack
  • ✓Fail-closed DNC gate with official FTC registry provider
  • ✓Turn gate, slot state, and transcript grounding
  • ✓Idempotent booking, retries, and session cleanup
  • ✓Git history scrubbed of credentials and lead data

Before the first live dial

  • FTC registry subscription and a fresh, in-scope DNC dataset
  • Production credentials and a stable HTTPS webhook URL
  • Choose a launch quote mode: estimate-and-book or carrier rates
  • Production-safe environment values
  • A 5–10 number test campaign, one call at a time, monitored live