Architecture, configuration, and reference for the OpenAI Realtime API + Twilio phone system.
To PLACE calls, manage prospects, and run campaigns: pair this skill with your own outbound dialer / campaign layer. This skill is about the real-time call infrastructure itself.
The call flow, session config format, and audio path below were debugged through many iterations. Do not restructure without reading this entire skill.
javascript
// CORRECT:
{ type: "session.update", session: {
modalities: ["text", "audio"], voice: "cedar",
turn_detection: { type: "semantic_vad", eagerness: "high", create_response: true, interrupt_response: true },
input_audio_format: "g711_ulaw", output_audio_format: "g711_ulaw",
}}
// WRONG (API rejects): session: { type: "realtime", audio: { input: { format: ... } } }
Callee picks up, says hello, THEN the agent responds. No forced greeting. Semantic VAD with create_response: true handles it automatically.
Audio deltas from OpenAI are already base64 g711_ulaw. Forward directly to Twilio. No PCM conversion, no gain control, no resampling.
conversation.item.create (user message) + response.create. NOT response.create with instructions. Trigger on session.updated, NOT session.created.
Must point to /twiml. Verify: check Twilio API, not assumptions.
"high" first turn, "medium" after. Configurable.response.done.response.create during pre-warm warms pipeline without audio.Before adding patches when calls fail:
systemctl status <your-service>, pm2 status, or your equivalent)lsof -i :<PORT>session.updated in logs. error after session.created = wrong config format.Do not pile patches. If it worked before and doesn't now, check infrastructure first.
Whatever process supervisor you use, the correct sequence is:
text
stop the websocket server
→ kill any orphaned listeners on the websocket port (lsof -i :<PORT> -t | xargs kill)
→ start the websocket server
Always stop → kill orphans → start. A bare restart can leave a stale listener holding the port.
Keep a known-good copy of sessionManager.ts (the file most affected by tuning) in a snapshots directory alongside the source. To restore:
text
copy snapshots/sessionManager-TUNED-<date>.ts → src/sessionManager.ts
restart using the procedure above
WhatPathsessionManager.tswebsocket-server/src/sessionManager.tsserver.tswebsocket-server/src/server.tsSnapshotswebsocket-server/snapshots/Service unityour process supervisor unit file (systemd user unit, pm2 ecosystem file, etc.)Logswherever you configured (stdout + journald, /var/log/..., pm2 logs, etc.).envwebsocket-server/.env (contains PORT)
All reference docs in {baseDir}/docs/:
FileContent{baseDir}/docs/01-overview.mdModel landscape, changelog{baseDir}/docs/02-session-config.mdsession.update reference + defaults{baseDir}/docs/03-prewarm-outbound.mdPre-warm: buffer, fallback, edge cases{baseDir}/docs/04-inbound-modes.mdAI IVR, Receptionist, CSR with DB{baseDir}/docs/05-async-tools.mdAsync tool calling{baseDir}/docs/06-latency-tuning.mdAll latency levers{baseDir}/docs/07-twilio-integration.mdPCMU format, edge, AMD, stream events{baseDir}/docs/08-known-issues.mdBugs, workarounds, watch-later{baseDir}/docs/09-openclaw-config.mdConfig + install/publish
Load the relevant doc before answering architecture or config questions.
gpt-realtime-1.5 (flagship), gpt-realtime-mini (cost-sensitive)wss://api.openai.com/v1/realtime?model=gpt-realtime-1.5semantic_vad with eagerness: "high" is the tested defaultsession.updated, not session.created.