---
title: "Build a CallSphere-Style Multi-Agent for a Dental Practice"
description: "Dental practices miss 20–35% of business-hour calls. Build a 4-agent specialist setup (intake, scheduling, recall, billing) that books straight into Dentrix or OpenDental."
canonical: https://callsphere.ai/blog/vw3h-build-callsphere-style-multi-agent-for-dental-practice
category: "AI Voice Agents"
tags: ["Dental", "Multi-agent", "Dentrix", "OpenDental", "Tutorial"]
author: "CallSphere Team"
published: 2026-04-18T00:00:00.000Z
updated: 2026-05-08T17:25:15.406Z
---

# Build a CallSphere-Style Multi-Agent for a Dental Practice

> Dental practices miss 20–35% of business-hour calls. Build a 4-agent specialist setup (intake, scheduling, recall, billing) that books straight into Dentrix or OpenDental.

> **TL;DR** — Practices like Arini, Viva and Dentina ship single-tenant agents. Build the same outcome multi-tenant with 4 specialists (intake, scheduling, recall, billing) talking to Dentrix or OpenDental, plus a shared FAQ. Booking refs follow a `DT-YYYYMMDD-###` pattern modelled on CallSphere's salon convention.

## What you'll build

A 4-agent dental front desk: triage → (new-patient intake | scheduling | recall reactivation | billing). Reads/writes to OpenDental via `mod_xfer`, sends SMS confirmations, and emits structured ref IDs.

## Prerequisites

1. OpenDental or Dentrix instance with API enabled.
2. OpenAI Realtime + Twilio Voice + SMS.
3. Python 3.11, `openai-agents[voice]`, `fastapi`.
4. A list of recurring intents from your last 200 calls.
5. SMS opt-in language reviewed by office manager.

## Architecture

```mermaid
flowchart TB
  C[Caller] --> TR[Triage]
  TR --> IN[Intake]
  TR --> SC[Scheduling]
  TR --> RC[Recall]
  TR --> BL[Billing]
  IN --> ODT[(OpenDental)]
  SC --> ODT
  RC --> ODT
  BL --> ODT
```

## Step 1 — Triage

```python
triage = RealtimeAgent(
    name="triage",
    instructions="""Greet warmly. Within 2 exchanges, classify into
    new-patient, schedule-existing, recall, or billing. Hand off.""",
    handoffs=[intake, scheduling, recall, billing],
)
```

## Step 2 — Scheduling specialist

```python
@function_tool
async def find_dentist_slots(provider_id: str, day: str) -> list[dict]:
    return await odt.appointments.open_slots(provider_id, day)

@function_tool
async def book_dental(patient_id: str, provider_id: str, slot_iso: str,
                      reason: str) -> dict:
    appt = await odt.appointments.create(patient_id=patient_id,
        provider_id=provider_id, slot=slot_iso, op=reason)
    ref = f"DT-{slot_iso[:10].replace('-','')}-{appt.id:03d}"
    await sms.send(appt.patient.phone, f"Confirmed {slot_iso}. Ref {ref}")
    return {"ref": ref, "appt_id": appt.id}
```

## Step 3 — New-patient intake

```python
@function_tool
async def create_patient(name: str, dob: str, phone: str, email: str,
                         insurance_payer: str | None) -> dict:
    p = await odt.patients.create(name=name, dob=dob, phone=phone,
                                  email=email, insurance=insurance_payer)
    return {"patient_id": p.id}
```

## Step 4 — Recall reactivation (outbound)

```python
@function_tool
async def list_due_recalls(weeks_ahead: int = 4) -> list[dict]:
    return await odt.recall.due_within(weeks_ahead)

# Outbound run loop

async def run_recall_campaign():
    for r in await list_due_recalls(4):
        twilio.calls.create(
            to=r.phone, from_=BUSINESS_NUMBER,
            twiml=f'',
        )
```

## Step 5 — Billing specialist

```python
@function_tool
async def lookup_balance(patient_id: str) -> dict:
    return await odt.billing.balance(patient_id)

@function_tool
async def send_payment_link(patient_id: str, amount_cents: int) -> dict:
    pl = stripe.PaymentLink.create(line_items=[{"price_data": {
        "currency": "usd", "product_data": {"name": "Dental balance"},
        "unit_amount": amount_cents}, "quantity": 1}])
    p = await odt.patients.get(patient_id)
    await sms.send(p.phone, f"Pay your balance: {pl.url}")
    return {"sent": True}
```

## Step 6 — Twilio bridge

Standard Realtime bridge — pass `patient_id` and `intent` as URL params; the triage agent uses them to skip classification when known.

## Step 7 — Reporting

```sql
SELECT
  date_trunc('day', occurred_at) AS day,
  agent_name,
  count(*) FILTER (WHERE outcome = 'booked') AS booked,
  count(*) FILTER (WHERE outcome = 'transferred') AS transferred
FROM call_sessions GROUP BY 1, 2 ORDER BY 1 DESC;
```

## Common pitfalls

- **Insurance verification at intake.** Use a clearinghouse tool, not the model's claim.
- **Provider preference.** Patients sometimes specify a hygienist; always pass `provider_id`.
- **Time-zone bugs.** Always store ISO with offset.

## How CallSphere does this in production

This is the dental analog of CallSphere's Salon stack — 4 ElevenLabs agents producing `GB-YYYYMMDD-###` references. Healthcare runs FastAPI :8084 with 14 HIPAA tools. OneRoof has 10 property specialists over WebRTC + Pion + NATS. 37 agents · 90+ tools · 115+ DB tables · 6 verticals. Pricing flat $149/$499/$1499 with [14-day trial](/trial). [/compare/arini](/compare/arini).

## FAQ

**HIPAA?** Yes — sign BAAs and add audit logging.

**Multi-location?** Pass `location_id` in every tool.

**Spanish-speaking patients?** Realtime supports Spanish natively.

**Insurance?** Add an eligibility tool that hits Change Healthcare or Availity.

**Recall ROI?** Practices report 25–40% reactivation lift over postcards.

## Sources

- [Arini Y Combinator](https://www.ycombinator.com/companies/arini)
- [Viva AI](https://www.getviva.ai/)
- [OpenDental API](https://www.opendental.com/manual/apicommands.html)
- [/demo](https://callsphere.ai/demo)

## How this plays out in production

If you are taking the ideas in *Build a CallSphere-Style Multi-Agent for a Dental Practice* and putting them in front of real customers, the constraint that decides everything is ASR error rates on long-tail entities (drug names, street names, SKUs) and the post-call pipeline that must reconcile what was actually heard. Treat this as a voice-first system from the first prompt: the agent's persona, its tool surface, and its escalation rules all flow from that single decision. Teams that ship fast tend to instrument the loop end-to-end before they tune any single component, because the bottleneck is rarely where intuition puts it.

## Voice agent architecture, end to end

A production-grade voice stack at CallSphere stitches Twilio Programmable Voice (PSTN ingress, TwiML, bidirectional Media Streams) to a realtime reasoning layer — typically OpenAI Realtime or ElevenLabs Conversational AI — with sub-second response as a hard SLO. Anything north of one second of perceived silence and callers either repeat themselves or hang up; that single number drives the whole architecture. Server-side VAD with proper barge-in support is non-negotiable, otherwise the agent talks over the caller and the conversation collapses. Streaming TTS with phoneme-aligned interruption keeps the cadence natural even when the user changes their mind mid-sentence. Post-call, every transcript is run through a structured pipeline: sentiment, intent classification, lead score, escalation flag, and a normalized slot extraction (name, callback number, reason, urgency). For healthcare workloads, the BAA-covered storage path, audit logs, encryption-at-rest, and PHI-safe transcript redaction are wired in from day one, not bolted on at compliance review. The end state is a system where every call produces a row of structured data, not just a recording.

## FAQ

**What changes when you move a voice agent the way *Build a CallSphere-Style Multi-Agent for a Dental Practice* describes?**

Treat the architecture in this post as a starting point and instrument it before you tune it. The metrics that matter most early on are end-to-end latency (target < 1s for voice, < 3s for chat), barge-in correctness, tool-call success rate, and post-conversation lead score distribution. Optimize whatever the data flags as the bottleneck, not whatever feels slowest in your head.

**Where does this break down for voice agent deployments at scale?**

The two failure modes that bite hardest are silent context loss across multi-turn handoffs and tool calls that succeed in dev but get rate-limited in production. Both are solvable with a proper agent backplane that pins state to a session ID, retries with backoff, and writes every tool invocation to an audit log you can replay.

**How does the salon stack (GlamBook) keep bookings clean across stylists and services?**

GlamBook runs 4 agents that handle booking, rescheduling, fuzzy service-name matching, and confirmations. Every appointment gets a deterministic reference like GB-YYYYMMDD-### so the salon, the customer, and the agent all reference the same object across SMS, email, and voice.

## See it live

Book a 30-minute working session at [calendly.com/sagar-callsphere/new-meeting](https://calendly.com/sagar-callsphere/new-meeting) and bring a real call flow — we will walk it through the live salon booking agent (GlamBook) at [salon.callsphere.tech](https://salon.callsphere.tech) and show you exactly where the production wiring sits.

---

Source: https://callsphere.ai/blog/vw3h-build-callsphere-style-multi-agent-for-dental-practice
