CloserCore
Telnyx-backed power dialer with an AI receptionist
Role: Sole engineer — architecture, TanStack Start frontend, Telnyx WebRTC integration, Supabase schema and RLS, Edge Functions, Stripe billing, TCPA compliance logic. · Updated 2026-09-01
What's actually in it
The problem
Sales teams were paying for a dialer, a phone system, a recording tool, and a compliance spreadsheet, and none of them agreed with each other. The compliance part is the one that actually bites: calling a recipient outside their local 8am–9pm window is a TCPA violation per call, and a dialer that only knows the caller's timezone will happily generate hundreds of them.
What I built
- 01Built the whole app on TanStack Start with file-based routing — 24 navigable routes across the dialer, inbox, contacts, recordings, SMS, analytics, coaching, and an 11-route settings surface.
- 02Wired Telnyx WebRTC into a React context so the softphone is a single long-lived connection shared by every route, rather than a component that tears down when the user navigates to their contact list mid-call.
- 0313 Supabase Edge Functions on Deno handling the Telnyx side — webhooks, call actions, number search and provisioning, SMS, WebRTC credential minting, and a live transcription stream — plus Stripe checkout, portal and webhook.
- 04AI on the call record rather than in it: a post-call summary function and a voicemail note function write structured output back to the call row, so the coaching and analytics views read columns instead of re-running a model.
- 0522 tables behind 93 RLS policies, with workspace membership as the tenancy boundary and an append-only audit log for the compliance trail.
- 06Closed a real failure mode where an `incoming_calls` row stuck at status `ringing` reopened the full-screen call modal on every page refresh, in three layers: a unique index on `telnyx_call_control_id` so the webhook's upsert is idempotent instead of writing a new row per Telnyx retry, a hangup handler that relabels any still-`ringing` row to `missed`, and a two-minute freshness window on the client so a row the webhook never got to close can't resurrect the modal.
Building something in this shape? Book a 30-min scope call →
Engineering decisions
The quiet-hours module is deliberately duplicated
TCPA calling windows are enforced in both the browser and the Edge Functions, and the two implementations are separate files — Deno can't import from src/, and the browser can't import from supabase/functions/. Rather than pretend a shared package solved it, the mirror is documented as a mirror. Compliance logic that only runs client-side is decorative; a user with devtools open is not a compliance control.
Ambiguous area codes resolve to the more eastern timezone
Area-code-to-timezone mapping is imperfect — some codes span two zones. Every ambiguous code is assigned the more eastern zone on purpose, so a wrong guess blocks a borderline-late call rather than permitting a violation. Unknown codes fall through to allowed, because refusing to dial numbers you can't classify would break the product for a legal risk that isn't there.
Per-seat billing enforced in code, not in Stripe
Only the Pro and Scale tiers bill per seat. Rather than modelling that in Stripe's product catalogue, checkout reads the live workspace_members count and passes it as quantity, gated by a PER_USER_TIERS set. Seat count lives in the database that already knows it, and adding a tier is a one-line change instead of a Stripe migration.
Feature gates as a single tier table
Eight features — power dialer, coaching, analytics, API keys, HIPAA mode, unlimited recording, multi-workspace, AI receptionist — map to a minimum tier in one FEATURE_MIN_TIER object consumed through an entitlements context. No entitlement checks scattered through components, and the upgrade prompt for any gated feature knows exactly which tier to sell.
The product
Want something built the way CloserCore was?
Bring the problem, not a spec. 30-minute call — I'll tell you what it actually takes to build, what I'd cut, and what it costs. No decks, no discovery phase invoice.