Install
openclaw skills install llc-phoneLow-latency inbound and outbound AI phone calls via the OpenAI Realtime API and Twilio, covering pre-warm and pre-accept patterns, IVR and receptionist flows, customer-service routing, VAD tuning, function calling, prompt caching, and implementation caveats.
openclaw skills install llc-phoneArchitecture, 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.
// 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:
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:
copy snapshots/sessionManager-TUNED-<date>.ts → src/sessionManager.ts
restart using the procedure above
| What | Path |
|---|---|
| sessionManager.ts | websocket-server/src/sessionManager.ts |
| server.ts | websocket-server/src/server.ts |
| Snapshots | websocket-server/snapshots/ |
| Service unit | your process supervisor unit file (systemd user unit, pm2 ecosystem file, etc.) |
| Logs | wherever you configured (stdout + journald, /var/log/..., pm2 logs, etc.) |
| .env | websocket-server/.env (contains PORT) |
All reference docs in {baseDir}/docs/:
| File | Content |
|---|---|
{baseDir}/docs/01-overview.md | Model landscape, changelog |
{baseDir}/docs/02-session-config.md | session.update reference + defaults |
{baseDir}/docs/03-prewarm-outbound.md | Pre-warm: buffer, fallback, edge cases |
{baseDir}/docs/04-inbound-modes.md | AI IVR, Receptionist, CSR with DB |
{baseDir}/docs/05-async-tools.md | Async tool calling |
{baseDir}/docs/06-latency-tuning.md | All latency levers |
{baseDir}/docs/07-twilio-integration.md | PCMU format, edge, AMD, stream events |
{baseDir}/docs/08-known-issues.md | Bugs, workarounds, watch-later |
{baseDir}/docs/09-openclaw-config.md | Config + 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.