§ 01Case study
inboundSelect
Real-time call routing and billing for US insurance agencies
A multi-tenant pay-per-call platform. Live calls reach exactly one licensed, available agent within seconds, and every connected call is billed through Stripe Connect. As the only engineer, I built the whole product: the React app, the backend and the database.
- Role
- Full Stack Engineer · Contract
- Timeline
- Nov 2025 – Jul 2026
- Core stack
- React, TanStack Query, Node.js
- AMlicensed FL · available
- RKlicensed FL · available
- JTlicensed TX
- SPon another call
UPDATE calls SET agent_id = $1 WHERE id = $2 AND agent_id IS NULL
→ wallet debited once · idempotency key ok · stripe connect

Fig. 1Two agents accept the same call; a conditional UPDATE lets exactly one win.
01
The problem
Insurance agencies buy inbound calls: someone dials a tracked number because they want a quote. The call is only worth something if a licensed agent picks it up in the next few seconds, while the caller is still on the line. After that, the agency needs to know who took it, how long it lasted, and who pays.
inboundSelect does this for many agencies at once, each with its own agents, phone numbers and Stripe account. I was the only engineer: I worked with the client on what to build, designed the UI, and built the React app and the backend on my own over about nine months.
02
Routing a live call
When a call arrives, Twilio hits a webhook. The backend works out which agency owns the number, then runs one SQL query for agents who can take it: available, approved, phone-verified, on a paid plan, licensed in the caller's state, and in that agency. The caller hears hold music while the call appears on every eligible agent's dashboard over Socket.IO.
The first agent to accept gets it. Accepting is a conditional UPDATE that only succeeds if nobody has taken the call yet, so when two agents click at the same moment, one wins and the other gets a 409. Before connecting, the backend checks the agent's wallet and concurrent-call limit and asks Twilio whether the caller is still there. Calls nobody accepts expire after 30 seconds.
All of this happens while a person waits on hold, so I added partial indexes for the routing queries and a short-lived cache for routing settings. Together they removed two to four database round-trips from every call.
03
The app agents work in
Agents and agency admins spend their day in a React 19 app of 146 components. Incoming calls appear on every eligible agent's dashboard over Socket.IO, and wallet balances and call status update live.
TanStack Query keeps server data cached and fresh, React Router handles each role's screens, agents top up their wallets and pay for subscriptions through Stripe's embedded checkout, and admins track calls, spend and agent performance in Recharts dashboards.
04
Money that moves exactly once
Each agency has its own Stripe Connect account, and agents' payments go straight to it. Agents prepay into a wallet and are charged when a call ends, but only if it lasted as long as their plan requires. The charge is a single UPDATE that fails if the balance is too low, so two calls ending together can't overdraw a wallet.
Stripe sometimes delivers the same webhook twice, so events are recorded by ID and skipped if they've been seen. Every charge the platform makes carries an idempotency key, so a retry never charges twice. Agencies pay the platform through subscriptions with proration, a fee per connected call, and a cut of calls resold on the marketplace.
05
Jobs, webhooks and recovery
Nine scheduled jobs run on Bull and Redis: invoices, fee collection, re-billing Twilio usage, listing unworked calls on the marketplace, and clearing calls that got stuck. They retry with exponential backoff, and if Redis is down they fall back to timers inside the process.
Call recordings move from Twilio to S3 on their own queue. Every webhook, from Twilio, Stripe and AWS SNS, checks its signature before doing anything, and bounced emails are suppressed automatically through SES.
06
What I'd do differently
I'd write tests around billing from the first week. Most of my confidence in routing came from a simulator I built for it, and nothing like that existed for the money side until much later.
By the numbers
- REST endpoints
- 380+
- Postgres tables
- ~60
- scheduled jobs
- 9
- verified webhooks
- 9