By Sagar Shankaran, Founder of CallSphere
Hands-on tutorial for building voice agents with the OpenAI Realtime API — WebSocket setup, PCM16 audio, server VAD, and function calling.
Key takeaways
Before the Realtime API, building a voice agent meant wiring together Whisper (or Deepgram), an LLM, and a TTS service over three separate connections, then fighting a constant battle with latency and interruption handling. The Realtime API collapses all three into one WebSocket that streams audio in and audio out and surfaces a clean event model for interruptions and tool calls.
This is a hands-on tutorial for building a working voice agent on top of the Realtime API. It does not assume a telephony provider — you can run everything locally with a laptop microphone first, then swap in Twilio later.
mic ──PCM16──► Realtime API ──PCM16──► speaker
│
├── session.created
├── input_audio_buffer.speech_started
├── response.audio.delta
├── response.function_call_arguments.done
└── response.done
┌───────────────────────────────┐
│ Node.js client │
│ • sounddevice / portaudio │
│ • WebSocket to Realtime API │
│ • tool dispatcher │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ OpenAI Realtime API │
│ gpt-4o-realtime-preview- │
│ 2025-06-03 │
└───────────────────────────────┘
brew install portaudio, Linux: apt install libportaudio2).import WebSocket from "ws";
const ws = new WebSocket(
"wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2025-06-03",
{
headers: {
Authorization: "Bearer " + process.env.OPENAI_API_KEY,
"OpenAI-Beta": "realtime=v1",
},
},
);
ws.on("open", () => {
ws.send(JSON.stringify({
type: "session.update",
session: {
voice: "alloy",
instructions: "You are a friendly receptionist for Acme Clinic.",
input_audio_format: "pcm16",
output_audio_format: "pcm16",
turn_detection: { type: "server_vad", silence_duration_ms: 400, threshold: 0.5 },
tools: [
{
type: "function",
name: "check_availability",
description: "Check provider availability",
parameters: {
type: "object",
properties: {
provider_id: { type: "string" },
date: { type: "string", description: "YYYY-MM-DD" },
},
required: ["provider_id", "date"],
},
},
],
},
}));
});
import { spawn } from "child_process";
// arecord pipes PCM16 at 24kHz mono to stdout
const mic = spawn("arecord", ["-q", "-f", "S16_LE", "-r", "24000", "-c", "1", "-t", "raw"]);
mic.stdout.on("data", (chunk) => {
ws.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: chunk.toString("base64"),
}));
});
import { spawn as spawn2 } from "child_process";
const speaker = spawn2("aplay", ["-q", "-f", "S16_LE", "-r", "24000", "-c", "1"]);
ws.on("message", (raw) => {
const evt = JSON.parse(raw.toString());
if (evt.type === "response.audio.delta") {
speaker.stdin.write(Buffer.from(evt.delta, "base64"));
}
});
ws.on("message", async (raw) => {
const evt = JSON.parse(raw.toString());
if (evt.type === "response.function_call_arguments.done") {
const args = JSON.parse(evt.arguments);
let result: unknown;
if (evt.name === "check_availability") {
result = await checkAvailability(args.provider_id, args.date);
}
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: evt.call_id,
output: JSON.stringify(result),
},
}));
ws.send(JSON.stringify({ type: "response.create" }));
}
});
When the caller starts speaking mid-response, clear the output buffer and cancel the in-flight response.
flowchart LR
CALLER(["Student or Parent"])
subgraph TEL["Telephony"]
SIP["Twilio SIP and PSTN"]
end
subgraph BRAIN["Education AI Agent"]
STT["Streaming STT<br/>Deepgram or Whisper"]
NLU{"Intent and<br/>Entity Extraction"}
TOOLS["Tool Calls"]
TTS["Streaming TTS<br/>ElevenLabs or Rime"]
end
subgraph DATA["Live Data Plane"]
CRM[("CRM and Notes")]
CAL[("Calendar and<br/>Schedule")]
KB[("Knowledge Base<br/>and Policies")]
end
subgraph OUT["Outcomes"]
O1(["Enrollment captured"])
O2(["Tour scheduled"])
O3(["Counselor callback"])
end
CALLER --> SIP --> STT --> NLU
NLU -->|Lookup| TOOLS
TOOLS <--> CRM
TOOLS <--> CAL
TOOLS <--> KB
NLU --> TTS --> SIP --> CALLER
NLU -->|Resolved| O1
NLU -->|Schedule| O2
NLU -->|Escalate| O3
style CALLER fill:#f1f5f9,stroke:#64748b,color:#0f172a
style NLU fill:#4f46e5,stroke:#4338ca,color:#fff
style O1 fill:#059669,stroke:#047857,color:#fff
style O2 fill:#0ea5e9,stroke:#0369a1,color:#fff
style O3 fill:#f59e0b,stroke:#d97706,color:#1f2937
if (evt.type === "input_audio_buffer.speech_started") {
ws.send(JSON.stringify({ type: "response.cancel" }));
}
The Realtime API emits transcript deltas for both sides. Collect them for later analysis.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
if (evt.type === "conversation.item.input_audio_transcription.completed") {
console.log("user:", evt.transcript);
}
if (evt.type === "response.audio_transcript.done") {
console.log("agent:", evt.transcript);
}
CallSphere uses the OpenAI Realtime API with gpt-4o-realtime-preview-2025-06-03 as the core of its voice and chat agents. Server VAD is on, audio is PCM16 at 24kHz, and every vertical ships its own tool schema: 14 tools for healthcare (insurance verification, appointment booking, provider lookup, and more), 10 agents for real estate, 4 for salon, 7 for after-hours escalation, 10 plus RAG for IT helpdesk, and an ElevenLabs TTS pod with 5 GPT-4 specialists for sales.
Multi-agent handoffs run through the OpenAI Agents SDK so a single caller can be routed from a triage agent to a specialist mid-call without dropping audio. Post-call analytics are handled by a GPT-4o-mini pipeline that writes sentiment, intent, and lead score into per-vertical Postgres. CallSphere supports 57+ languages and keeps end-to-end response time under one second.
Yes — bridge Twilio Media Streams to your WebSocket server and forward audio in both directions.
Server VAD runs on OpenAI's side and generates speech_started events automatically. Client VAD lets you control turn-taking manually. Start with server VAD.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Send another session.update with the new voice name. Do it between turns, not during a response.
Yes — once you send the function_call_output item, the model picks up and continues speaking.
Yes. The model can emit multiple tool calls, and you should respond to each before calling response.create.
Want to see a full Realtime API deployment in production? Book a demo, explore the technology page, or browse pricing.
#CallSphere #OpenAIRealtime #VoiceAI #Tutorial #WebSocket #FunctionCalling #AIVoiceAgents
Written by
Sagar Shankaran· Founder, CallSphere
Sagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A how-to for Colombian education and tutoring SMBs to answer parents and students instantly, book trial classes 24/7 in Spanish and English, and grow enrollment with a CallSphere AI agent.
Tbilisi professional-services firms serving relocating founders and IT companies use CallSphere AI voice and chat agents to answer enquiries 24/7 in English, Georgian and Russian and book consultations.
A practical how-to for Palau eco-resorts and dive operators on capturing every high-value, multilingual enquiry with a CallSphere AI voice and chat agent, while honouring Palau’s marine-conservation commitments.
How salons, spas and wellness SMBs across the UAE, Saudi Arabia and Qatar use CallSphere AI voice and chat agents to capture every booking 24/7 in Arabic, English and expat languages, and cut no-shows.
How estate agents and property managers in Luxembourg City and across the Grand Duchy use CallSphere to capture multilingual viewing and enquiry calls 24/7, GDPR compliant.
A practical guide for Senegalese logistics, freight, legal and consulting SMBs in Dakar and Thiès to capture every enquiry 24/7 with a CallSphere AI voice and chat agent in French, Wolof and English.
© 2026 CallSphere LLC. All rights reserved.
Made within New York
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI