Sonoria — an AI voice assistant & booking platform
A voice AI platform for service businesses: customers call a number and an AI books, reschedules, cancels, and takes payment end-to-end — no human on the line. Built on the OpenAI Realtime API, Twilio, Django Channels, and Stripe Connect.
- Voice AI
- SaaS
- Django
- Twilio
- Stripe
Sonoria is an AI voice assistant and booking platform for service businesses — salons, clinics, studios, trades. A customer dials a number, and an AI answers, handles the entire conversation, and books, reschedules, or cancels an appointment while collecting payment where needed. There is no human on the line and no IVR menu to fight through. The whole thing runs at roughly $0.06 per minute of voice, which is what makes it viable to leave running 24/7 for a small business.
I built Sonoria at Pseudobytes, a studio I co-founded. Below is how the system actually works — the real-time voice pipeline, the per-business prompt assembly, and the billing that keeps it economical.
The problem
Service businesses lose money at the phone. Calls come in during a haircut, after hours, or at a peak the front desk can't cover, and each missed call is a booking that walks to a competitor. Hiring a receptionist to cover every hour is expensive and still leaves gaps. Existing "phone tree" automation is worse than a person — it frustrates callers into hanging up.
The goal was an agent good enough that a caller doesn't need to know or care that it isn't human: it listens, answers questions about services and pricing, finds a slot, takes a deposit, and sends a confirmation — all inside one phone call, with a clean handoff to a real person when the situation actually needs one.
Architecture
The hard constraint is latency. A phone conversation collapses the moment the response gap gets long, so the audio path has to stay tight from the caller's mouth to the model and back. Twilio bridges the PSTN call into a WebSocket; audio streams bidirectionally as G.711 μ-law frames to the OpenAI Realtime API, which runs server-side voice activity detection and calls back into the Django backend via function calling whenever it needs to actually do something.
The voice pipeline
Twilio delivers the call as a media stream over a WebSocket. Frames arrive as G.711 μ-law — 8 kHz, 8-bit, the ancient but universal telephony codec — and I forward them straight to the OpenAI Realtime API over its own WebSocket. Server-side VAD lives on the model, so it decides when the caller has stopped speaking and when to start talking back. That removes an entire class of turn-taking bugs I'd otherwise have to hand-tune with silence thresholds.
The interesting part is that the AI doesn't just talk — it acts. Mid conversation it issues function calls back into Django to book a slot, reschedule, cancel, fire an SMS confirmation, or transfer the call to a human when the caller asks for one or the request falls outside its rules. Each tool runs against live data and returns a result the model folds into the next sentence, so the caller hears "you're booked for Thursday at 3" only after the row actually exists.
{
"type": "function",
"name": "book_appointment",
"description": "Reserve a slot for a service and send an SMS confirmation.",
"parameters": {
"type": "object",
"properties": {
"service_id": { "type": "string" },
"start_time": { "type": "string", "format": "date-time" },
"customer": { "type": "object" },
"deposit": { "type": "boolean" }
},
"required": ["service_id", "start_time", "customer"]
}
}When a caller genuinely needs a person — an edge case, a complaint, a request outside the booking rules — the agent doesn't dead-end. It performs a live transfer to a human fallback, so the AI is a first line that handles the routine 90% and escalates the rest cleanly.
Dynamic prompt engineering
Every business on Sonoria gets its own agent with zero training— just configuration. There's no fine-tuning step and no per-customer model. Instead, the prompt is assembled at call time from that business's database record: services, pricing, hours, FAQs, team members, and booking rules all get composed into a context-aware system prompt. Change your prices in the dashboard and the next caller's agent already knows.
Real-time layer
The backend runs as an ASGI app on Django Channels behind Daphne, which is what lets a single Django process hold long-lived WebSocket connections for the audio stream alongside the ordinary DRF request/response API. The same channel layer pushes booking updates so the dashboard reflects a call in progress without a refresh.
Payments & subscriptions
Billing runs on Stripe with Stripe Connect, since each business collects its own customer deposits. There are 9 plan combinations, and because voice has a real per-minute cost, usage is metered by the minute against the plan. I built progressive failure warnings at 80% and 100%of allotted usage so a business is told it's approaching its limit before the agent stops answering, plus a 30-day trial to let owners hear it work on their own line before paying.
- Stripe Connect for per-business customer deposits
- Minute-based voice metering tied to the subscription
- Progressive 80% / 100% usage warnings
- 9 plan combinations and a 30-day trial
Outcomes
For a business, Sonoria removes roughly 15–20 hours a week of manual call handling and gives 100% call coverage— nothing is missed after hours or at peak, because there's no queue and no voicemail. Automated SMS confirmations cut down on no-shows, which is where the economics actually land for owners: fewer empty chairs, not just fewer missed calls.
The design bet was that a caller shouldn't have to know it's an AI. Everything — the tight audio path, server-side VAD, mid-call function calling, and a clean human handoff — exists to keep that illusion intact for the whole call.
Stack
- Backend: Django, Django REST Framework, Django Channels (ASGI / Daphne)
- Voice: OpenAI Realtime API, Twilio, G.711 μ-law over WebSockets, server-side VAD
- Payments: Stripe, Stripe Connect
- Frontend: Next.js 15, TypeScript, Tailwind CSS
- Data: PostgreSQL