System Overview
The Billing Dialer is an AI-powered outreach system that manages the full 180-day payment lifecycle across 5 client segments. It uses automated voice calls (Retell AI agent "Grace"), templated SMS (HeyMarket), and live agent transfers (Talkdesk) to guide clients from a missed payment through resolution — or, when necessary, through suspension and cancellation.
Retell AI
Voice Agent
AI voice agent Grace handles outbound calls, voicemail detection, and live agent transfers
HeyMarket
SMS Platform
Templated SMS with dynamic variable injection — separate inboxes for billing vs. resurrection
Dialflow
Agent Availability
Real-time agent availability API — gates batch release until enough agents are free
Databricks
Data Warehouse
Source of eligible clients via SQL query; receives call history write-back on campaign stop
Daily Workflow
A configurable SQL query runs against the Databricks warehouse to pull eligible clients. Each row includes client ID, name, phone, segment, days past due, and ring group.
Uses @databricks/sql with a Personal Access Token. The query itself is stored in the system settings so it can be updated without a deploy.
Each row is matched to its cadence day and segment config. The system resolves SMS templates, voicemail prompts, live-call prompts, transfer numbers, and action type (SMS, Call, or Both).
Rows are enriched with per-segment transfer numbers (billing vs. resurrection) and inserted into Neon Postgres via Prisma in batches of 100.
While the campaign is running, the same queue query is re-executed periodically. The returned client IDs are cross-referenced against the stored queue — any queued client not in the result is marked INELIGIBLE and skipped.
Runs on a configurable interval (default 30 minutes). Prevents calling clients who resolved their balance mid-campaign.
The orchestrator groups queued items by ring group, checks live-agent availability via the Dialflow API, and releases batches when enough agents are available.
Batch size and release threshold are configurable per campaign. If no agents are available, the loop polls every 5 seconds until availability opens up.
Each batch item triggers an outbound call through Retell AI. The AI agent "Grace" receives dynamic variables — client name, past-due amount, cadence-specific prompts, and the correct transfer number.
POST /v2/create-phone-call with override_agent_id and retell_llm_dynamic_variables. Supports live-call prompts, voicemail prompts, and warm transfers.
When a call ends, Retell fires a webhook. The system maps the disconnection reason to an outcome: answered, voicemail, transferred, no-answer, or failed.
call_ended fires immediately; call_analyzed provides voicemail vs. answered disambiguation. Outcomes update queue item status and campaign counters.
SMS-only cadence items fire at a randomized time (10 AM – 1 PM). "Both" items send a follow-up SMS after a voicemail or no-answer call outcome.
POST /v1/message/send with dynamic templates. Resurrection-phase messages route to a separate HeyMarket inbox for specialized agent handling.
When a live client wants to speak with an agent, the Retell voice agent initiates a warm transfer to the Talkdesk phone number configured for that segment.
Transfer numbers are per-segment (billing vs. resurrection). The webhook logs the transfer and marks the queue item as TRANSFERRED.
When the campaign stops (manually or at end-of-day), all call outcomes are written back to Databricks for analytics and reporting.
INSERT INTO analytics.billing.dialer_call_history with full outcome data. Queue items are then cleaned up from Postgres.
Tech Stack Breakdown
Next.js 16
Framework
- App Router with server & client components
- API routes for campaign control and webhooks
- Standalone output for container deployment
Neon Postgres + Prisma
Database
- Serverless Postgres via @prisma/adapter-neon
- Models: Campaign, QueueItem, Cadence, Schedule, Config
- Batch inserts (100 at a time) for queue loading
Databricks SQL
Data Warehouse
- Daily queue pull via configurable SQL query
- Periodic revalidation for eligibility changes
- History write-back for analytics on campaign stop
Retell AI
Voice Agent
- AI agent "Grace" with dynamic LLM variables
- Voicemail detection & live-call prompts per cadence day
- Webhook-driven outcome resolution (call_ended / call_analyzed)
HeyMarket
SMS Platform
- Template-driven SMS with dynamic variable injection
- Separate billing and resurrection inboxes
- Randomized send window (10 AM – 1 PM) for SMS-only items
Talkdesk
Contact Center
- Per-segment transfer numbers for live agent routing
- Billing ring group pre-suspension, resurrection post
- Warm transfer initiated by Retell voice agent
Dialflow
Agent Availability
- Real-time agent availability checks per ring group
- Gates batch release — calls only fire when agents are ready
- Polled every 5s during the release loop
In-Process Orchestrator
Batch Engine
- Async release loop with configurable batch size
- 2-minute safety timeout per batch
- SMS scheduler with randomized daily send time
TypeScript + Zod + Pino
DX & Reliability
- End-to-end type safety with TypeScript 5
- Runtime validation with Zod on API inputs
- Structured logging with Pino for observability
| Segment | Past Due | Suspension | Cancellation | Touchpoints | Notes |
|---|---|---|---|---|---|
| RED Initial | Day 1 | Day 60 | Day 180 | ~50 | Standard billing cadence for initial RED payments |
| RED Recurring | Day 1 | Day 60 | Day 180 | ~50 | Same cadence as RED Initial — same billing segment |
| Reso Initial | Day 1 | Day 90 | Day 180 | ~50 | Resolution plan first payment — 30 extra days before suspension |
| Reso Recurring | Day 14 | Day 90 | Day 180 | ~50 | 14-day grace period built into the cadence |
| Reso Servicing | Day 31 | Day 122 | Day 180 | ~50 | Monterey Financial. Longer grace and billing window. |
RED Initial / Recurring
Day 0
Payment missed — friendly SMS
Day 1 — Past Due
Account is past due — soft intro outreach begins
Soft Intro
Friendly check-ins, no amounts cited
Billing Escalation
Past-due amounts, urgency increasing, suspension warnings
Day 60 — Suspension
Account suspended — work paused
Resurrection
"We're still here" — empathetic re-engagement, ready when you are
Pre-Cancellation
Cancellation risk warnings, urgent final outreach
Day 180 — Cancellation
Case cancelled — final communication
Pre-Suspension: Billing Tone
Escalating urgency. Past-due amounts cited. Account risk warnings. Goal is to collect before suspension.
Soft Intro (Days 1-10)
“Just a friendly reminder — we still have a payment that needs your attention.”
Past Due (Days 12-30)
“Your overdue payment is holding things up. We want to help get this resolved and keep things moving.”
Pre-Suspension (Days 31+)
“Your account is at risk of suspension. Suspension will halt our work on your resolution options.”
Post-Suspension: Resurrection Tone
Empathetic re-engagement. “We're still here.” No pressure — ready when they are. Cancellation risk introduced later.
Re-engagement (Post-Suspension)
“Your account is suspended but we're still here for you. When you're ready, give us a call or text us.”
Direct (Mid Re-engagement)
“We're still here and want to help you move toward resolution. Give us a call — let's talk.”
Pre-Cancellation (Day 150+)
“We don't want to see your case cancelled. Please call us so we can discuss keeping your resolution options alive.”
Routing Changes at Suspension
Call transfers route to the resurrection ring group and transfer number instead of the standard billing line
SMS replies route to a dedicated resurrection inbox in HeyMarket for specialized agent handling
SMS
Sends a templated SMS via HeyMarket with dynamic variables (client name, past-due amount, days past due, payment link).
Call
AI voice agent "Grace" calls the client via Retell. If answered, uses the live-call prompt. If voicemail, leaves a contextual message. Can transfer to a live agent.
Both
Sends SMS and places a call on the same cadence day. Used for milestones and high-priority touchpoints to maximize reach.