How to build an AI contact center with FreeSWITCH and the OpenAI Realtime API

Connect FreeSWITCH to the OpenAI Realtime API via a Node.js WebSocket bridge that uses FreeSWITCH ESL to stream PCM audio bidirectionally. FreeSWITCH handles SIP/WebRTC call termination and media processing; the Node.js bridge relays audio frames to the OpenAI Realtime WebSocket; GPT-4o processes voice input and streams audio responses back with sub-500ms end-to-end latency.

What is the OpenAI Realtime API for voice?

The OpenAI Realtime API enables bidirectional audio streaming with GPT-4o over a persistent WebSocket connection — accepting live audio input and streaming audio output in real time without separate ASR or TTS steps. This makes it suitable for building conversational AI phone agents with natural, low-latency dialogue.

FreeSWITCHOpenAI RealtimeAI Contact CenterVoice AIESLGPT-4o

Building an AI Contact Center with FreeSWITCH and OpenAI Realtime API

The OpenAI Realtime API (launched October 2024) changes the economics of AI voice agents fundamentally. By streaming audio directly to GPT-4o over a WebSocket — no separate ASR transcription, no TTS synthesis — response latency drops to 300–600ms end-to-end. That is conversational quality. And FreeSWITCH, handling 10,000+ concurrent calls on commodity hardware, is the telephony engine that makes it production-scale.

This guide covers the complete architecture: FreeSWITCH ESL audio streaming, the Node.js WebSocket bridge, OpenAI Realtime API integration, AI tool calls for CRM lookups, human agent handoff, interruption handling, and a real cost model vs hosted AI voice platforms. CelloIP has deployed this stack in production — here is exactly what works.

By Kaushik Parmar— Founder & VoIP Architect, CelloIP Technologies·24 min read·June 9, 2026·≈ 2,700 words

<500ms

End-to-end AI response latency

GPT-4o

Realtime voice model used

10k+

Concurrent calls on FreeSWITCH

70%

Cost savings vs hosted AI voice

FreeSWITCH OpenAI Realtime API AI contact center architecture — four layer diagram showing SIP phone calls processed by FreeSWITCH media server, Node.js ESL audio bridge streaming 16kHz PCM to OpenAI GPT-4o Realtime API, with CRM tool calls and human agent handoff
Fig 1: Four-layer AI contact center stack. Old ASR→LLM→TTS pipeline: 720ms–1.4s. OpenAI Realtime API direct audio streaming: 250–450ms end-to-end. FreeSWITCH handles 10,000+ concurrent SIP sessions.

Why FreeSWITCH for Production AI Voice?

Hosted AI voice platforms (Twilio ConversationalAI, Vonage AI Studio, Bland AI) bundle telephony and AI into one managed service. That is convenient for prototypes. For production at scale, FreeSWITCH wins on every dimension that matters to a serious contact centre:

10,000+

Concurrency

Concurrent calls on a single FreeSWITCH server (8-core). Hosted platforms charge per concurrent channel slot.

Any LLM

Model Portability

Swap OpenAI Realtime → Anthropic → Gemini → local Llama without changing your telephony layer.

On-Premise

Data Sovereignty

Healthcare, finance, and government deployments can keep all audio and transcripts on-premise or in a private cloud.

70% cheaper

Cost

vs hosted AI voice platforms at 10,000+ minutes/month. Infrastructure cost scales sublinearly with volume.

Native

SIP Integration

Any SIP trunk, any PBX, any carrier. No gateway fees, no PSTN markup, direct SIP termination.

Full

Codec Control

PCMU, PCMA, G.729, Opus, iLBC. Transcode precisely where needed. Minimise audio quality loss.

OpenAI Realtime API: What Changed in 2024

Before the Realtime API, building a voice AI agent required chaining three separate services: ASR (Whisper) → LLM (GPT-4) → TTS (ElevenLabs). Each hop added 150–400ms latency. Total pipeline: 600–1,200ms — noticeable, bordering on jarring for natural conversation.

Before: ASR → LLM → TTS Chain

Audio capture+20ms
Whisper ASR transcription+200–400ms
GPT-4 ChatCompletion+300–600ms
ElevenLabs TTS synthesis+200–400ms
Audio playback startTotal: 720ms–1.4s

After: OpenAI Realtime API

Audio capture + VAD+20–50ms
Speech end detection+50–100ms
GPT-4o Realtime processing+150–300ms
Audio stream begins+30ms (first chunk)
User hears responseTotal: 250–450ms

The Realtime API also adds built-in VAD (voice activity detection) with interrupt handling — if the caller speaks during the AI response, the AI stops and listens. This is the feature that makes voice AI feel like a real conversation rather than a robot reading a script.

Full System Architecture

The architecture has four layers. Each is independently scalable:

Layer 1: SIP/Media Layer

FreeSWITCH 1.10.x — SIP registration, media handling, codec transcoding

Accepts inbound SIP calls from carrier SIP trunks or WebRTC browsers. Transcodes audio to 16kHz mono PCM (required by OpenAI Realtime API). Handles 10,000+ concurrent sessions.

Layer 2: Event & Audio Bridge

Node.js service using ESL (modesl) + mod_audio_stream

Subscribes to FreeSWITCH ESL events. When a call arrives in the AI IVR dialplan context, opens a WebSocket audio stream between FreeSWITCH and the OpenAI Realtime API. Handles the bidirectional audio relay.

Layer 3: AI Processing

OpenAI Realtime API (GPT-4o model) · Tool call handlers (CRM, KB, ticketing)

Processes inbound audio via VAD, generates conversational responses, streams audio back. Executes tool calls when the AI needs to look up account info, check order status, or route to a department.

Layer 4: Business Logic

CRM API (Salesforce, HubSpot, Zendesk) · Knowledge base (RAG vector store) · Human agent queue (ACD)

Tool call endpoints that the AI agent calls to retrieve customer info, look up answers, create tickets, and trigger human escalation. Completely decoupled from the telephony layer.

FreeSWITCH Configuration for AI IVR

The dialplan routes inbound calls to the AI IVR context. We configure a dedicated context that answers the call and immediately passes it to the Node.js bridge via a socket application or mod_audio_stream WebSocket endpoint.

xmldialplan/default.xml — AI IVR context
<!-- Route inbound DID to AI agent context -->
<context name="public">
  <extension name="ai_agent_did">
    <!-- Match your DID number -->
    <condition field="destination_number" expression="^(18005551234)$">
      <!-- Set audio params for OpenAI Realtime (16kHz mono required) -->
      <action application="set" data="media_bug_answer_req=true"/>
      <action application="set" data="record_sample_rate=16000"/>

      <!-- Answer the call -->
      <action application="answer"/>

      <!-- Play a brief hold tone while bridge connects (optional) -->
      <action application="playback" data="silence_stream://500"/>

      <!-- Stream audio to our Node.js AI bridge via mod_audio_stream -->
      <!-- mod_audio_stream sends raw PCM to ws://localhost:3001/audio -->
      <action application="audio_stream" data="wss://localhost:3001/audio async full"/>

      <!-- If bridge hangs up cleanly, execute hangup -->
      <action application="hangup"/>
    </condition>
  </extension>
</context>

<!-- Sofia SIP profile — accept inbound SIP trunk calls -->
<!-- In conf/sip_profiles/external.xml: -->
<!-- <param name="context" value="public"/> -->

Install mod_audio_stream from the community repository. It streams raw 16kHz PCM audio frames over WebSocket to your bridge application:

bashInstall mod_audio_stream for FreeSWITCH
# Install build dependencies
apt-get install -y libjson-c-dev libwebsockets-dev

# Clone and build mod_audio_stream
git clone https://github.com/nicholasgasior/mod_audio_stream
cd mod_audio_stream
make
make install

# Load in modules.conf.xml
# <load module="mod_audio_stream"/>

# Reload modules in fs_cli
freeswitch> reload mod_audio_stream
FreeSWITCH to OpenAI Realtime API audio bridge flow diagram — SIP caller audio streams through FreeSWITCH mod_audio_stream to Node.js WebSocket server, relayed to OpenAI Realtime WebSocket, VAD detects speech end, GPT-4o processes voice and streams audio response back to caller in under 500ms
Fig 2: Audio bridge data flow — FreeSWITCH mod_audio_stream → Node.js relay → OpenAI Realtime WebSocket → GPT-4o VAD + processing → audio response streams back. Total round-trip: <500ms.

Node.js Audio Bridge: FreeSWITCH ↔ OpenAI Realtime

The bridge is the heart of the system. It accepts WebSocket connections from FreeSWITCH (audio stream), opens a connection to the OpenAI Realtime API, and relays audio frames bidirectionally. It also handles tool calls and the human escalation logic.

javascriptNode.js — FreeSWITCH ↔ OpenAI Realtime audio bridge
const WebSocket = require('ws');
const OpenAI = require('openai');

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// WebSocket server — accepts connections from FreeSWITCH mod_audio_stream
const wss = new WebSocket.Server({ port: 3001, path: '/audio' });

wss.on('connection', async (fsSocket) => {
  console.log('[Bridge] FreeSWITCH connected');

  // Open OpenAI Realtime session
  const realtimeWs = new WebSocket(
    'wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01',
    {
      headers: {
        Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
        'OpenAI-Beta': 'realtime=v1',
      },
    }
  );

  realtimeWs.on('open', () => {
    console.log('[Bridge] OpenAI Realtime connected');

    // Configure the session
    realtimeWs.send(JSON.stringify({
      type: 'session.update',
      session: {
        modalities: ['text', 'audio'],
        instructions: `You are a helpful customer service agent for Acme Corp.
Be concise — phone responses should be under 30 words unless the caller asks for detail.
You have access to tools: lookup_account, create_ticket, transfer_to_agent.`,
        voice: 'alloy',
        input_audio_format: 'pcm16',
        output_audio_format: 'pcm16',
        input_audio_transcription: { model: 'whisper-1' },
        turn_detection: {
          type: 'server_vad',
          threshold: 0.5,
          prefix_padding_ms: 300,
          silence_duration_ms: 600,
        },
        tools: [
          {
            type: 'function',
            name: 'lookup_account',
            description: 'Look up a customer account by phone number',
            parameters: {
              type: 'object',
              properties: { phone: { type: 'string', description: 'Caller phone number' } },
              required: ['phone'],
            },
          },
          {
            type: 'function',
            name: 'transfer_to_agent',
            description: 'Transfer the call to a human agent when the caller requests it or the AI cannot help',
            parameters: {
              type: 'object',
              properties: { reason: { type: 'string' }, department: { type: 'string' } },
              required: ['reason'],
            },
          },
        ],
      },
    }));
  });

  // ── FreeSWITCH → OpenAI: relay incoming audio ────────────────
  fsSocket.on('message', (audioChunk) => {
    if (realtimeWs.readyState === WebSocket.OPEN) {
      realtimeWs.send(JSON.stringify({
        type: 'input_audio_buffer.append',
        audio: audioChunk.toString('base64'),
      }));
    }
  });

  // ── OpenAI → FreeSWITCH: relay AI audio response ─────────────
  realtimeWs.on('message', async (data) => {
    const event = JSON.parse(data.toString());

    switch (event.type) {
      case 'response.audio.delta':
        // Stream AI audio back to FreeSWITCH
        if (fsSocket.readyState === WebSocket.OPEN) {
          fsSocket.send(Buffer.from(event.delta, 'base64'));
        }
        break;

      case 'response.function_call_arguments.done':
        // Handle tool calls
        await handleToolCall(event, realtimeWs, fsSocket);
        break;

      case 'input_speech_started':
        // Caller is speaking — interrupt AI audio
        console.log('[Bridge] Caller interruption detected');
        break;

      case 'error':
        console.error('[Bridge] Realtime API error:', event.error);
        break;
    }
  });

  fsSocket.on('close', () => {
    console.log('[Bridge] FreeSWITCH disconnected — closing OpenAI session');
    realtimeWs.close();
  });
});

async function handleToolCall(event, realtimeWs, fsSocket) {
  const args = JSON.parse(event.arguments);

  if (event.name === 'transfer_to_agent') {
    console.log(`[Bridge] Transferring to human agent: ${args.reason}`);
    // Signal FreeSWITCH to bridge to agent queue via ESL (out-of-band)
    // Your ESL client would issue: api uuid_transfer <uuid> 1001 xml default
    realtimeWs.send(JSON.stringify({
      type: 'conversation.item.create',
      item: {
        type: 'function_call_output',
        call_id: event.call_id,
        output: JSON.stringify({ status: 'transferring', estimated_wait: '2 minutes' }),
      },
    }));
    realtimeWs.send(JSON.stringify({ type: 'response.create' }));
  }
}

AI Tool Calls: CRM & Knowledge Base Integration

Tool calls are what elevate an AI voice agent from a novelty to a production system. The AI can look up account status, check order history, search a knowledge base, and create support tickets — all during the call, in real time. Here is a full example for CRM account lookup:

javascriptTool call handler — CRM account lookup
async function handleToolCall(event, realtimeWs) {
  const args = JSON.parse(event.arguments);
  let output;

  switch (event.name) {
    case 'lookup_account':
      try {
        // Query your CRM — replace with actual Salesforce/HubSpot API call
        const account = await crmClient.findByPhone(args.phone);
        output = account
          ? {
              found: true,
              name: account.name,
              account_id: account.id,
              plan: account.subscription_plan,
              open_tickets: account.open_tickets,
              last_interaction: account.last_contact_date,
            }
          : { found: false, message: 'No account found for this number' };
      } catch (err) {
        output = { found: false, error: 'CRM lookup failed' };
      }
      break;

    case 'create_ticket':
      const ticket = await crmClient.createTicket({
        caller_phone: args.phone,
        subject: args.subject,
        description: args.description,
        priority: args.priority || 'normal',
        channel: 'phone',
      });
      output = { ticket_id: ticket.id, estimated_resolution: '24 hours' };
      break;

    default:
      output = { error: 'Unknown tool' };
  }

  // Send tool result back to Realtime API
  realtimeWs.send(JSON.stringify({
    type: 'conversation.item.create',
    item: {
      type: 'function_call_output',
      call_id: event.call_id,
      output: JSON.stringify(output),
    },
  }));

  // Tell the model to continue the response with the tool result
  realtimeWs.send(JSON.stringify({ type: 'response.create' }));
}

Cost: FreeSWITCH + OpenAI vs Hosted Platforms

PlatformAI Model ControlData PrivacyCost/10k minSIP IntegrationOn-Premise
FreeSWITCH + OpenAI RT✓ Full (swap models)✓ BAA available~$850/mo✓ Native SIP✓ Yes
Twilio ConvAI✗ Twilio's choiceTwilio DPA only$500–$2,000/moTwilio SIP✗ No
Vonage AI Studio✗ LimitedVonage DPA$600–$1,500/moVonage SIP✗ No
LiveKit + OpenAI RT✓ Full✓ BAA available~$900/mo✓ Via SIP bridge✓ Yes
Bland AI✗ ProprietaryLimited~$0.09/minPSTN only✗ No

Rule of thumb: Use hosted AI voice platforms for <500 minutes/month or when you need zero infrastructure ops. Switch to FreeSWITCH + OpenAI RT when your monthly AI voice spend exceeds the annual cost of a FreeSWITCH server, or when you need on-premise, carrier SIP integration, or model portability.

Use Cases & Industry Applications

Customer Service IVR Replacement

Retail / E-commerce

Replace 'Press 1 for support' with a conversational AI that looks up order status, processes returns, and answers FAQs — escalating to humans for complex issues.

Healthcare Appointment Scheduling

Healthcare

HIPAA-compliant AI agent that books, reschedules, and cancels appointments by integrating with EHR scheduling APIs. On-premise FreeSWITCH ensures no PHI leaves the hospital network.

Outbound Collections & Reminders

Finance / Insurance

AI agent makes outbound calls for payment reminders, policy renewals, and debt collection — handles objections, accepts payments via DTMF, escalates disputes to humans.

Technical Support Tier 1

Telecom / SaaS

AI answers how-to questions from a knowledge base, runs guided troubleshooting scripts, creates tickets, and routes to L2 engineers when diagnosis requires hands-on access.

Hotel & Hospitality Concierge

Hospitality

AI handles room service orders, wake-up calls, housekeeping requests, and local recommendations — freeing front-desk staff for in-person guest interactions.

Government Citizen Services

Government

On-premise AI voice agent handles common citizen inquiries (permit status, benefit eligibility, office hours) with zero data leaving the government network.

Frequently Asked Questions

Can FreeSWITCH integrate with the OpenAI Realtime API?

Yes. A Node.js bridge application uses FreeSWITCH ESL to access a channel's audio stream, then relays the PCM audio frames to the OpenAI Realtime WebSocket in real time. AI audio responses stream back and are injected into the call via FreeSWITCH's uuid_audio or played via a temporary audio file. The round-trip enables fully conversational AI interactions over standard SIP.

What is the OpenAI Realtime API and why does it matter for telephony?

The OpenAI Realtime API (launched October 2024) accepts live audio input and streams audio output back through a persistent WebSocket connection — no separate ASR transcription step, no TTS synthesis step. For telephony, this removes 300–800ms of latency that traditional ASR→LLM→TTS pipelines incur, bringing the total response time to 300–600ms — conversational quality.

What FreeSWITCH module do I need for audio streaming to an external API?

mod_audio_stream is the cleanest option — it streams raw PCM audio frames from a FreeSWITCH channel to an external WebSocket. Alternatively, you can use ESL's uuid_record to record audio to a named pipe (FIFO) and stream the pipe contents. For production, CelloIP recommends mod_audio_stream for its clean frame-by-frame WebSocket API.

How does the AI agent hand off to a human agent?

The AI agent is equipped with a hand_off_to_agent tool call. When triggered (by the caller requesting a human, or the AI determining it cannot help), your bridge application receives the tool call, selects an available agent from your ACD queue, and issues a FreeSWITCH ESL bridge command to transfer the call. The conversation transcript is simultaneously posted to your CRM.

What is the cost of running this stack vs a hosted AI voice platform?

At 10,000 minutes/month: OpenAI Realtime API ≈ $600 (at $0.06/min), FreeSWITCH VPS ≈ $100/month, SIP trunk ≈ $150. Total: ~$850/month. A hosted AI voice platform (Twilio ConversationalAI, Vonage AI Studio) charges $0.05–$0.20 per minute — the same 10,000 minutes costs $500–$2,000/month with no control over the underlying models or data.

Is the OpenAI Realtime API suitable for HIPAA-compliant healthcare deployments?

OpenAI offers a Business Associate Agreement (BAA) for eligible enterprise plans, which enables HIPAA-compliant use of the Realtime API. For maximum data sovereignty, consider self-hosted alternatives: Whisper for ASR, an on-premise Llama 3 / Mistral LLM, and a local TTS engine. CelloIP has deployed fully on-premise AI voice agents for healthcare clients using this stack.

Can this architecture work with WebRTC browser calls instead of SIP?

Yes. FreeSWITCH natively supports WebRTC via mod_verto or mod_rtc. Browser-based calls from your web application land in FreeSWITCH as WebRTC channels, then the same ESL audio bridge connects them to the OpenAI Realtime API. The AI agent cannot tell the difference between a SIP desk phone and a browser WebRTC call.

How do I prevent the AI from interrupting itself mid-response?

The OpenAI Realtime API includes built-in VAD (Voice Activity Detection) with interruption support. When the caller speaks while the AI is playing audio, the Realtime API sends an InputAudioBufferSpeechStarted event. Your bridge application sends an input_audio_buffer.clear and conversation.item.truncate message to the Realtime API and plays a silence frame to FreeSWITCH, cleanly interrupting the AI response.

Ready to Build Your AI Contact Center?

CelloIP has deployed production AI voice agents on FreeSWITCH with OpenAI Realtime API, Whisper, ElevenLabs, and on-premise LLM stacks. We handle the full stack: FreeSWITCH cluster, AI bridge, CRM integration, human handoff, analytics, and SIP trunk provisioning. Typical project timeline: 8–12 weeks from discovery to production.

AI IVR Proof of Concept

Working FreeSWITCH + OpenAI Realtime demo with your use case in 2 weeks. Fixed price.

Full AI Contact Center

End-to-end: FreeSWITCH cluster, AI bridge, CRM integration, agent handoff, recording, and dashboard.

On-Premise AI Stack

HIPAA/GDPR-compliant deployment using local Whisper, Llama 3, and Kokoro TTS. Zero data leaves your network.