PolicyJar CRM

Full-featured insurance CRM and call center dialer, now being productized as a self-serve SaaS targeting Medicare/ACA insurance agencies.

Tech Stack

  • Framework: Next.js 16, TypeScript, Tailwind CSS 4, shadcn/ui
  • Database: Supabase (Postgres + Auth)
  • Voice/SMS: Telnyx (WebRTC softphone, call control API, SMS, IVR)
  • Billing: Stripe (subscriptions + metered minutes)
  • Hosting: Vercel (production) — stays on Vercel, NOT migrating to Cloudflare

URLs & Repos

SaaS Pricing

Plan Price Minutes
Starter $29/agent/mo $0.018/min
Growth $49/agent/mo $0.015/min
Agency $79/agent/mo $0.012/min

Key Features

  • Softphone — Telnyx WebRTC with real-time call control
  • IVR builder — Visual IVR flow editor
  • Lead CRM — Assignment, routing, tracking, dispositions
  • Queues — Call queue management, skill-based routing
  • Live wallboard — Real-time agent/queue metrics
  • Voicemail — Voicemail inbox, playback, voicemail drop
  • Call recording — Automatic recording + transcription-ready
  • Compliance — DNC list, calling hours, holiday scheduling
  • Reports/Analytics — Call metrics, lead conversion, campaign performance
  • Team management — Invites, roles, permissions
  • Agent presence — Persistent agent status (30s heartbeat, 60s expiry)
  • Stripe billing — Self-serve checkout, customer portal, metered usage

Architecture

  • Next.js App Router with Supabase RLS for multi-tenant isolation
  • tenant_id on all tables — full data isolation per agency
  • Telnyx Call Control API (CCA) for call routing and bridging
  • Webhook-based call event processing
  • is_internal = TRUE on the me@coy.gg tenant — bypasses all billing gates

Multi-Tenant Schema

  • tenants table: plan, seats_purchased, status, stripe_customer_id, stripe_subscription_id, trial_ends_at, is_internal
  • All tables have tenant_id foreign key
  • RLS policies enforce per-tenant isolation

Stripe Billing (PR #59)

Branch: feature/stripe-billing-saas

File Purpose
src/lib/stripe.ts Stripe singleton (null if key not set)
src/lib/seats.ts Seat availability check
src/lib/usage.ts Metered minute reporting
src/app/api/billing/checkout/route.ts Stripe Checkout session
src/app/api/billing/portal/route.ts Stripe Customer Portal
src/app/api/billing/webhook/route.ts Subscription lifecycle events
src/app/(app)/settings/billing/page.tsx Billing UI
supabase/migrations/015_stripe_billing.sql Stripe columns on tenants
supabase/migrations/016_internal_account.sql is_internal flag

Required env vars (add to Vercel before merging):

STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_STARTER_PRICE_ID=price_...
STRIPE_GROWTH_PRICE_ID=price_...
STRIPE_AGENCY_PRICE_ID=price_...

Competitor: EnrollHere (enrollhere.com)

Enterprise-focused insurance dialer (150+ agencies, Medicare/senior market, sales-led, no public pricing). PolicyJar differentiates via: self-serve signup, transparent pricing, Quote & Enroll in-call.

SaaS Roadmap

Phase Features Status
Phase 1 org_id isolation ✅, Stripe billing (PR #59), self-serve signup, auto-provision Telnyx DID In progress
Phase 2 Outbound dialer, lead import, recording, reporting, voicemail drop Planned
Phase 3 Quote & Enroll (CMS Plan Finder + HealthSherpa API), commissions, TCPA compliance Planned

Cron Jobs

Job Schedule Action
pr-auto-merge Every 1h (Sonnet) Auto-merges approved PRs
nightly backup 3 AM EST Database backup

Deployment

Platform: Vercel (auto-deploy from GitHub master)

PR Workflow: branch → PR → Greptile review → auto-merge at 4/5+ → deploy

Known Issues / Tech Debt

  • SMS callback not implemented
  • Rate limiter is in-memory (needs Redis for scale)
  • Stripe billing not merged to master yet (awaiting price ID setup)