How to Build an After-Hours Virtual Receptionist Avatar for Healthcare Intake in FastAPI

Build a FastAPI after-hours healthcare intake flow with session state, realtime streaming, triage rules, and a synced avatar.
Introduction
After-hours healthcare intake is a good fit for a virtual receptionist because the workflow is structured, time-sensitive, and repetitive: answer the phone, identify the patient, collect a limited set of details, route urgent cases, and create a clean handoff for staff in the morning. The hard part is not generating a “smart” conversation; it is keeping the interaction low-latency, auditable, and constrained enough that it behaves like an intake system rather than a free-form chat bot.
In this post, I’ll show how to design that flow in FastAPI and how to connect it to a realtime avatar so the caller sees and hears a synchronized receptionist instead of a silent voice agent. By the end, you should understand the architecture, the main API boundaries, and the practical concerns that matter in production: session state, streaming audio/video, fallback behavior, and data handling.
Start with the intake workflow, not the avatar
For healthcare intake, the avatar is presentation. The actual product is a narrow state machine with a few well-defined outputs:
Patient identity and callback number
Reason for call or symptom summary
Urgency classification and escalation trigger
Insurance, provider, and appointment context if needed
A structured handoff record for clinic staff
This is important because realtime voice UX makes it easy to over-generalize. A receptionist bot should not “chat”; it should collect fields in order, confirm them, and minimize branching. In practice, a simple intake schema and a deterministic conversation policy will outperform a larger, looser prompt.
A useful pattern is to model the call as a finite set of states:
Each state should have:
what the agent is allowed to ask
what fields are required before advancing
what conditions force escalation to a human
For example, if the caller mentions chest pain, shortness of breath, uncontrolled bleeding, or other emergency language, the system should stop intake and direct the caller to emergency services or a human operator. That logic belongs in your application layer, not in a prompt alone.
Build the FastAPI backend around session state
FastAPI is a good fit because it gives you a clean place to manage authentication, scheduling, and persistent call state while leaving the realtime media path to the voice/streaming layer. The backend usually needs three pieces:
an endpoint to create or initialize a session
a store for intake state keyed by session ID
an endpoint to receive the completed handoff
A minimal skeleton looks like this:
In a real deployment, you would store the conversation transcript separately from the structured intake data. That makes it easier to review the call, audit decisions, and build a morning callback queue. It also reduces the temptation to treat raw model output as authoritative.
Stream the conversation, but keep the policy narrow
The realtime part of the system is what lets the receptionist feel responsive. Under the hood, voice agents typically consume audio frames, run speech-to-text, generate a response, synthesize audio, and stream it back with low end-to-end latency. If you add an avatar, you also need lip-sync alignment so the video face tracks the generated speech closely enough to avoid uncanny timing gaps.
The main engineering constraints are:
Latency budget: if response time is too high, users interrupt or hang up.
Barge-in handling: callers must be able to interrupt the agent naturally.
Turn detection: the system should know when the caller is done speaking.
State consistency: partial answers should not overwrite confirmed fields.
Because healthcare intake is structured, you can keep the model prompt short and push most logic into deterministic validators. For example, if the caller gives an insurance card number in the wrong format, do not ask the model to “figure it out”; re-prompt for the specific field. Likewise, if the caller refuses to provide a required field, record the refusal and move to the next permissible step rather than stalling.
Use FastAPI to separate media orchestration from business rules
One of the biggest mistakes is coupling the avatar session lifecycle directly to the intake logic. Instead, treat the avatar session as a transport concern and the intake workflow as business logic. That separation gives you flexibility if you later swap voice providers, add a phone gateway, or fall back to a plain audio-only experience during an incident.
A practical split is:
Avatar session: created when the caller connects; tied to a realtime conversation
Intake session: the durable record in your application database
Conversation turn: a transient exchange of speech, transcript, and assistant output
In FastAPI, that means your realtime event handler should update the intake state only after validating the extracted fields. If you are using WebSocket or WebRTC callbacks, keep those handlers lightweight: acknowledge the event, persist the minimal state, and offload longer processing to a background task or queue.
This is also where timeouts matter. An after-hours receptionist should fail closed in predictable ways:
If the agent cannot hear the caller, offer a callback number and end cleanly.
If the model or TTS service is slow, keep a short apology and retry once.
If the system detects emergency symptoms, bypass intake and escalate immediately.
Do not rely on a long free-form conversation to “eventually” gather the right information. A good receptionist is brief, repeatable, and boring.
Where Protoface fits
This is the layer where a realtime avatar is actually useful: it gives the voice agent a synchronized face without forcing you to build the video pipeline yourself. With Protoface, you can keep your FastAPI app focused on intake logic while the avatar/session side handles the visual presence. For Python backends, the Python SDK is the cleanest way to create and manage avatars or realtime sessions programmatically; the exact request/response fields are documented in the docs.
A simplified example might look like this:
If you are using LiveKit for the voice agent, the plugin path is even simpler because it drops the avatar into the existing agent rather than forcing a separate video orchestration layer. The relevant integration is documented in the plugin repository and package, including the LiveKit Agents surface used by developer teams building conversational voice systems. See the plugin examples in the GitHub organization if you want to wire an avatar into an existing agent stack.
Practical concerns: PHI, observability, and fallback modes
Healthcare intake introduces a few non-negotiables. First, be careful with protected health information. Keep transcripts and structured intake data in your own systems, minimize what you send to third-party services, and make sure you understand the retention and access patterns of every component in the chain. If you can avoid storing audio, do so. If you must store it, use a retention policy.
Second, instrument the flow. You want to know:
where callers drop off
which prompts trigger misunderstandings
how often escalation is invoked
response latency at each turn
Those metrics are usually more valuable than generic “conversation quality” scores. In an intake setting, the core question is whether the caller completed the minimum viable handoff with enough accuracy for staff to act on it.
Third, design a fallback path. If the avatar session cannot be established, the same FastAPI workflow should still work with audio-only or even SMS/email follow-up. A resilient design treats the avatar as an enhancement, not a dependency for collecting the intake record.
Conclusion
An after-hours virtual receptionist works when the conversational layer is constrained by a real workflow: collect a few specific fields, detect urgent cases, and produce a handoff staff can trust. FastAPI is a solid control plane for that system because it cleanly separates session management, validation, and persistence from the realtime media path. The avatar then becomes the presentation layer on top of a structured intake engine.
If you are implementing this for a live call flow, start with the state machine, wire the backend endpoints, and only then add the realtime avatar. For integration details, docs, and examples, begin at docs.protoface.com and the relevant quickstarts in the GitHub ecosystem. That will get you to a working prototype quickly without blurring the line between product logic and media plumbing.
