Civic Outreach & Donor Operations Platform
Campaign CRM with smart dialer, AI voice, and finance data explorer

End-to-end (Frontend, Backend APIs, Dialer Logic, Voice AI, Infrastructure)
Political campaigns and nonprofits need to manage thousands of contacts, make compliant outreach calls, track donor history, and coordinate teams—but most CRMs are too generic or lack calling infrastructure.
This platform combines contact intelligence (CRM) with a smart dialer, optional AI voice assistance, and donor finance data exploration. Built for political campaigns, consultants, and nonprofits, it handles contact operations (activities, tasks, tags, lists), compliant calling (progressive + power dialer, DNC management, call recording), email outreach (Resend), file management (cloud sync), and subscriptions (Stripe). The system uses Twilio for telephony, ElevenLabs for AI voice, and WebSocket media servers for real-time audio processing.
The Challenge
Campaign teams juggle dozens of tools: a CRM for contacts, a dialer for calls, a spreadsheet for donor tracking, and manual research for giving history. Most dialers are clunky, require VoIP setup, and don't integrate with modern web apps. AI voice is promising but often violates compliance rules (TCPA, state-level consent laws). The goal was to build an all-in-one platform with a built-in dialer, optional AI voice (with human handoff), integrated finance data, and multi-tenant support for campaign teams and consultants.
Technical Architecture
Frontend
- Next.js 15 (App Router)
- React 18
- TypeScript
- TailwindCSS
- shadcn/ui
- Framer Motion
- TanStack Table
Backend & APIs
- Next.js Route Handlers (App Router)
- Twilio (Voice, Recordings, Media Streams)
- ElevenLabs (Conversational AI, TTS)
- OpenAI (Whisper STT, GPT for conversation logic)
Data & Storage
- Supabase (PostgreSQL + RLS + Storage)
- Vercel Blob (Export artifacts)
- Migrations for contacts, activities, tasks, call queues, agent states
Realtime & Media
- WebSocket Media Server (Node + ws + Socket.IO, Fly.io)
- Twilio Media Streams (audio bridging for STT/AI)
Infrastructure
- Vercel (Web app + APIs)
- Fly.io (WebSocket server)
- Railway (Background worker)
- Supabase (DB + Storage)
Integrations
- Clerk (Auth)
- Stripe (Subscriptions)
- Resend (Email)
- Knock (Notifications)
- Mapbox (Geo viz)
Key Features Built
- CRM: Contacts with activities, tasks, comments, tagging, lists, duplicate detection, DNC management
- Smart Dialer: Progressive and power dialer flows with buffer countdowns, agent state transitions (READY/BUFFER/ON_CALL), call queue management, dispositions
- Twilio Integration: Inbound/outbound calling, call recording, status callbacks, conference bridging
- AI Voice (Optional): ElevenLabs TTS and outbound AI call flows, OpenAI Whisper STT, conversational AI service with human handoff orchestration
- Finance Data Explorer: State contributions and IRS 8872 Schedule A APIs with filtering, pagination, and guardrails
- Email: Resend integration, templates fetch/send, attachments, stored artifacts
- Files/Drive: Folder/file APIs, search/tags, upload/download, Google Drive sync plumbing
- Notifications: Knock test/sync endpoints, in-app feed plumbing
- Billing: Stripe subscription lifecycle (create/update/cancel), product details
- Admin/Cron: Sync queue, recording processing, pending task reminders, webhook cleanups
Dialer & Voice AI Flow
The dialer supports two modes: progressive (agent-paced) and power (automated queue). Progressive: agent clicks 'Ready', system fetches next contact from queue, dials, and transitions agent to ON_CALL; after call ends, agent enters post-call workflow (disposition, notes, schedule follow-up), then buffer countdown before next call. Power: system dials multiple contacts in parallel, connects answered calls to available agents. For AI voice: ElevenLabs ConvAI triggers outbound call, system updates call queue and agent state, and handles human handoff via webhook. Inbound calls use Twilio TwiML for IVR (4-digit extension), then either dial a configured number or stream audio to WebSocket media server for AI bridge. All calls are recorded, transcribed (via Twilio Intelligence), and cached in Supabase Storage.
Technical Challenges & Solutions
Agent state race conditions (multiple calls updating state).
Used database-level locking with SELECT FOR UPDATE and idempotent state transitions.
Twilio Media Streams audio buffering causing latency.
Tuned WebSocket server buffer sizes and added heartbeat pings; reduced latency from 2s to <500ms.
AI voice compliance (TCPA requires consent).
Added opt-in/opt-out tracking, DNC screening, and human handoff button in UI; logged all consent events.
Large contact exports (100K+ rows) timing out.
Moved export to background worker (Railway) that writes to Blob and emails download link via Resend.
Multi-tenant isolation for campaign teams.
Added org_id to all tables, enforced RLS policies, and scoped all queries via Supabase helpers.
Learnings & Tradeoffs
This project taught me the complexity of real-time telephony. Twilio's Media Streams API is powerful but requires careful buffer management and heartbeat tuning to avoid audio dropouts. I also learned to treat agent state as a finite state machine—early designs allowed invalid transitions (e.g., READY → BUFFER without ON_CALL), which caused bugs. Adding idempotent state transitions and database-level locking fixed this. The AI voice feature was high-risk: compliance teams were concerned about TCPA violations, so we added explicit opt-in tracking, DNC screening, and human handoff. The tradeoff was reduced automation (agents still had to manually approve calls), but it was necessary for legal reasons. If I rebuilt this, I'd invest in more robust WebSocket health checks and add feature flags to toggle AI voice per org.