Migrating from Twilio Programmable Voice to Teler

A technical guide to twilio migration covering the exact steps to move from Twilio Programmable Voice to Teler's WebSocket-based voice infrastructure. Covers audio format requirements, bidirectional media stream setup, barge-in...
Twilio migration switch flipped from Twilio Voice to FreJun Teler WebSocket media transport
In this article

AI Summary: This article is a technical walkthrough of twilio migration from Twilio Programmable Voice to Teler, written for backend and API integration engineers evaluating the switch in 2026. Twilio’s bidirectional Media Streams require outbound audio as raw 8 kHz mu-law, base64-encoded, with no container headers, a constraint that shapes every downstream codec and resampling decision. Engineers must decide whether to bridge the existing Twilio call path or cut over entirely to Teler’s WebSocket transport. Teler handles carrier connectivity, raw media streaming, and interruption signaling so the application layer focuses on STT, LLM, and TTS rather than telephony plumbing.

Scope: This walkthrough covers the twilio migration path from Twilio Programmable Voice, specifically its REST call control, TwiML webhooks, and bidirectional Media Streams, to Teler’s WebSocket-based voice infrastructure, verified against Twilio’s API documentation as of September 2026 and Teler’s published specification. It does not cover Twilio Flex, Twilio Studio, or Twilio Conversations.

Quick Answer: Migrating from Twilio Programmable Voice to Teler in 2026 means replacing TwiML webhooks with Teler’s WebSocket call events, swapping Twilio’s <Connect><Stream> bidirectional stream for Teler’s real-time media socket, and ensuring all audio injected back into the call is 8 kHz, base64-encoded, and sent in chunks of at least 500 ms.

A twilio migration to Teler is primarily an audio pipeline swap: the carrier leg moves from Twilio’s infrastructure to Teler’s edge-distributed points of presence, and the WebSocket message contract changes from Twilio’s Connected, Start, Media, Mark, Clear, Stop sequence to Teler’s equivalent transport layer.

What does Twilio Programmable Voice do, and where does it stop?

Twilio Programmable Voice is a cloud voice API that makes, receives, routes, and manages phone calls across web browsers, SIP domains, and mobile applications. It occupies the carrier and call-control layer: Twilio owns the PSTN (Public Switched Telephone Network) leg, handles SIP (Session Initiation Protocol) signaling, and exposes call state through a REST API at base URL 2010-04-01 plus selected voice.twilio.com/v1 and v2 resources. What it does not do is run your speech recognition, your language model, or your text-to-speech engine. Those are your responsibility. For real-time audio access, Twilio exposes Media Streams over WebSocket (RFC 6455), but your server must handle the framing, codec conversion, and buffer management. Twilio does not provide a built-in VAD (Voice Activity Detection) contract or an automatic resampling path between its 8 kHz mu-law stream and the 24 kHz audio that models like OpenAI Realtime expect.

Why is twilio migration harder than swapping credentials?

The surface area of a twilio migration looks small until you hit the audio path. Twilio’s bidirectional Media Streams deliver audio in 20 ms chunks of raw 8 kHz mu-law (ITU-T G.711), base64-encoded, with no container headers. Any audio you send back must match that format exactly: raw mu-law at 8000 Hz, base64-encoded, no WAV or other container bytes, framed in 160-byte chunks representing 20 ms each. Sending the wrong format produces garbled audio at the start of every utterance, a failure mode reported by Deepgram’s integration team in August 2026.

Beyond the codec constraint, three operational gaps make the migration non-trivial. First, Twilio’s Clear message flushes queued outbound audio, but your application must also cancel the upstream TTS job simultaneously or the old sentence keeps playing. Second, Twilio sends media in 20 ms chunks while a downstream VAD needs roughly 300 to 500 ms to detect speech start, so forwarding chunks immediately drops the first syllable. Third, WebSocket connections that sit idle during silence can hit inactivity timeouts, and Twilio’s documentation does not specify a keepalive contract, so your server must emit heartbeat frames independently.

What does the audio path look like end to end?

Twilio migration paths: bridge the existing Twilio call leg into Teler, or cut over fully to Teler WebSocket

The table below maps each hop in the Twilio path against the equivalent Teler hop, so you can see exactly where the ownership boundary shifts during a twilio migration.

HopTwilio pathTeler pathFormat / transportOwner
Carrier to platformTwilio PSTN ingressTeler PSTN ingressRTP / G.711 mu-lawPlatform
Platform to your serverWebSocket, <Connect><Stream>WebSocket, Teler media socketJSON frames, base64 audioYour server
Your server to STTRaw 8 kHz mu-law, decodedRaw 8 kHz or 16 kHz PCMHTTP or WebSocketYour server
LLM inferenceYour choiceYour choice, model-agnosticHTTP or WebSocketYour server
TTS to platform8 kHz mu-law, base64, no headers8 kHz base64, at least 500 ms chunksWebSocket JSON frameYour server
Platform to carrierTwilio PSTN egressTeler PSTN egressRTP / G.711 mu-lawPlatform

The critical difference is the resampling requirement. If you use OpenAI Realtime, its native sample rate is 24 kHz. Teler streams at 8 kHz by default (configurable to 16 kHz). You must resample in both directions: downsample from 24 kHz to 8 kHz before sending to the telephony layer, and upsample from 8 kHz to 24 kHz before sending to the model. This hop is where most teams discover their latency problem, because a naive resampling implementation adds 40 to 80 ms per direction.

What do you need before starting the migration?

  • A Teler account with API key, available at platform.frejun.ai (prototyping credits available without a credit card)
  • At least one phone number provisioned through Teler’s number API (50+ countries supported)
  • Your existing Twilio TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN for the parallel-run phase
  • A WebSocket server reachable over TLS, because both Twilio and Teler require wss:// endpoints
  • An audio resampling library: audioop (Python stdlib, deprecated in 3.13) or resampy / librosa for Python, or node-audioworklet for Node.js
  • A mu-law encoder if your TTS engine outputs PCM: audioop.lin2ulaw in Python or an equivalent
  • Network access from your WebSocket server to Teler’s edge nodes and to your STT, LLM, and TTS providers
  • Familiarity with Teler’s WebSocket message contract, documented at Teler API docs

How do you execute the twilio migration step by step?

Step 1: Provision a Teler number and configure the WebSocket endpoint

Provision a DID (Direct Inward Dialing) number through Teler’s number API and point its inbound webhook at your WebSocket server. Teler will POST a call event to your HTTP endpoint when a call arrives, then upgrade to WebSocket for the media stream. This replaces Twilio’s TwiML webhook plus <Connect><Stream> verb pair.

import asyncio
import websockets
import json
import base64

async def handle_call(websocket):
    async for message in websocket:
        event = json.loads(message)
        event_type = event.get("type")
        if event_type == "start"
            stream_id = event["stream_id"]
            print(f"Stream started: {stream_sid}")
        elif event_type == "audio":
            # Incoming audio: 8 kHz mu-law, base64-encoded
            audio_bytes = base64.b64decode(event["data"]["audio_b64"])
            await process_audio(audio_bytes)

async def main():
    async with websockets.serve(handle_call, "0.0.0.0", 8765):
        await asyncio.Future()

asyncio.run(main())

Watch the event_type field: Teler’s event names differ from Twilio’s Connected / Start sequence, so map them explicitly rather than assuming string equality.

Step 2: Decode inbound audio and buffer before forwarding to STT

Teler streams inbound audio as base64-encoded 8 kHz mu-law by default. Decode it, convert to linear PCM, then buffer at least 400 ms before forwarding to your STT engine. Forwarding 20 ms chunks immediately causes the first syllable to be dropped because the VAD needs 300 to 500 ms to detect speech start, as reported by Callstack.tech in January 2026.

BUFFER_MS = 400
BYTES_PER_MS = 16  # 8000 Hz x 2 bytes (16-bit PCM)
buffer = bytearray()

def process_audio(pcm_bytes: bytes) -> bytes | None:
    global buffer
    buffer.extend(pcm_bytes)
    if len(buffer) >= BUFFER_MS * BYTES_PER_MS:
        chunk = bytes(buffer)
        buffer = bytearray()
        return chunk
    return None

The audioop.ulaw2lin call takes a width argument in bytes per sample; use 2 for 16-bit PCM. If your STT engine expects 16 kHz, resample the PCM output before sending.

Step 3: Inject TTS audio back into the call

Teler expects outbound audio as base64-encoded 8 kHz mu-law, sent in chunks of at least 500 ms. Strip any WAV or container headers from your TTS output before encoding. Sending container bytes produces garbled audio at the start of every utterance. The chunk size is configurable from 20 to 2000 ms in multiples of 20, with a default of 400 ms, but FreJun recommends sending back at least 500 ms to avoid choppy playback.

import audioop, base64, json, uuid

def encode_for_teler(pcm_16k_bytes: bytes) -> tuple[str, str]:
    pcm_8k, _ = audioop.ratecv(pcm_16k_bytes, 2, 1, 16000, 8000, None)
    chunk_id = uuid.uuid4().hex
    message = json.dumps({
        "type": "audio",
        "audio_b64": base64.b64encode(pcm_8k).decode("utf-8"),
        "chunk_id": chunk_id,
    })
    return message, chunk_id

async def send_audio(websocket, pcm_bytes: bytes) -> str:
    message, chunk_id = encode_for_teler(pcm_bytes)
    await websocket.send(message)
    return chunk_id

async def send_audio(websocket, pcm_bytes: bytes, stream_sid: str):
    message = encode_for_teler(pcm_bytes, stream_sid)
    await websocket.send(message)

Confirm that audioop.ratecv receives the correct state argument on subsequent calls; passing None resets the resampler state and introduces a click at chunk boundaries.

Step 4: Implement barge-in and interruption handling

When the caller speaks while the agent is playing audio, you must flush Teler’s outbound buffer, cancel the upstream TTS job, and drop any late frames already in flight. Teler’s {"type": "clear"} message flushes queued playback on the telephony side. Sending only the clear message without canceling the TTS generator lets the old sentence keep playing, a failure mode reported in three separate sources including the OpenAI Community forum in January 2026.

import asyncio

tts_task: asyncio.Task | None = None

async def handle_barge_in(websocket):
    global tts_task
    # 1. Cancel the TTS generator
    if tts_task and not tts_task.done():
        tts_task.cancel()
    # 2. Flush Teler's outbound audio buffer
    await websocket.send(json.dumps({"type": "clear"}))
    # 3. Reset transcript state to avoid stale context
    reset_transcript_state()

The order matters: cancel the TTS task before sending clear, so no new frames arrive at the WebSocket after the flush.

Step 5: Add WebSocket keepalive and reconnect logic

WebSocket connections that sit idle during silence can hit inactivity timeouts. Twilio’s documentation does not specify a keepalive interval, and Teler’s specification does not publish one either. Emit a heartbeat frame every 10 seconds and implement client-side reconnect with replay logic to avoid mid-call stream drops, a pattern reported by Callstack.tech in January 2026.

async def keepalive(websocket):
    while True:
        await asyncio.sleep(10)
        try:
            await websocket.ping()  # standard WebSocket ping; Teler has no JSON ping message
        except websockets.exceptions.ConnectionClosed:
            break

Run keepalive as a concurrent task alongside your main message loop, and cancel it when the call ends.

The reference implementation for Twilio Media Streams is available at github.com/twilio/media-streams, with Python, Node.js, Java, and Ruby examples. The repository was last updated on 11 September 2026 according to search metadata. Use it to validate your Twilio-side audio handling before cutting over to Teler. Teler’s own integration patterns are documented at Teler’s developer documentation.

Most teams discover their latency problem is not the model, it is the resampling hop they added between the carrier and the STT engine. A naive implementation of 24 kHz to 8 kHz conversion adds 40 to 80 ms per direction, and that compounds with every other hop in the pipeline. Fix the resampler first, then tune the buffer.

– Subhash Kalluri, Founder & CEO, FreJun

What breaks in production after a twilio migration?

SymptomRoot causeFixFrequency
Garbled audio at utterance startContainer/header bytes sent in media payload, or non-mu-law codecStrip headers, base64-encode raw mu-law only, frame to 160-byte 20 ms chunksReported once (Deepgram, Aug 2026)
First syllable clipped20 ms chunks forwarded immediately; VAD needs 300 to 500 ms to detect speechBuffer 400 ms minimum before forwarding to STTReported once (Callstack.tech, Jan 2026)
Old sentence keeps playing after barge-inOnly one side of the pipeline signaled: Teler buffer cleared but TTS not canceled, or vice versaCancel TTS task first, then send {"type": "clear"}, then reset transcript stateReported three times
Stream drops mid-call during silenceWebSocket inactivity timeout, no keepaliveEmit heartbeat every 10 s, implement reconnect with replayReported once (Callstack.tech, Jan 2026)
Duplicate chunks or stale transcript stateRace condition between stream callbacks and STT/agent events on reconnectAdd processing guard, make downstream actions idempotent, reset state on barge-inReported twice
Choppy or robotic audioPacket loss or jitter in the RTP path between endpointsMove off lossy Wi-Fi, prioritize RTP traffic, consider larger jitter buffersReported twice (Twilio Support, Telphi Consulting)

The barge-in failure catches the most teams because it requires coordinating three separate systems: the telephony buffer, the TTS generator, and the transcript state machine. Teams that fix only the Teler clear message without canceling the TTS job find that the caller hears the tail of the previous response even after interrupting. The fix is sequential: cancel first, clear second, reset third.

One honest limitation of Teler worth stating: the transport layer targets sub-100ms packet delivery as a design specification, but the documentation does not publish a measured benchmark methodology or a per-region latency table. If your application has a hard latency SLA, you should run your own end-to-end timing tests before committing to production traffic. See why latency is a critical factor in voice API integration deployment for the variables that affect that number in practice.

Which architecture should you choose for the twilio migration?

Twilio migration in four steps: provision numbers, point webhook to Teler, map audio format, switch traffic
ApproachMedia terminates atLatency costOperational burdenLock-in
TwiML webhooks only (no streams)Twilio / Teler platformOne HTTP round trip per turnLow: stateless webhook handlerPlatform TTS and routing verbs
Unidirectional stream (listen only)Your server (analysis sidecar)One extra hop for analyticsMedium: streaming processing pipelinePlatform owns PSTN leg
Bidirectional stream (full agent)Your WebSocket serverResampling plus buffer managementHigh: codec, buffer, barge-in, keepalivePlatform owns PSTN leg only
SIP trunking to your media serverYour SIP / RTP infrastructureSIP signaling overheadHigh: SIP, RTP, codec, PBX or SBC opsLowest: you own the media path

For most AI voice agent workloads, bidirectional streaming is the right choice because it gives your server full control over what the caller hears and when. Choose SIP trunking instead if you already operate a PBX or SBC (Session Border Controller) and want to own the RTP media path end to end, as described in SIP Trunking vs Elastic SIP Trunking: What’s the Difference and Why It Matters. Choose the webhook-only approach if your use case is IVR routing or simple call deflection with no real-time audio processing. Teler supports all four patterns, so the architecture decision is yours rather than a platform constraint.

For teams building AI phone agents at scale, the bidirectional streaming pattern combined with Teler’s model-agnostic transport is the most future-proof path. The top CPaaS providers in the US comparison covers how Teler’s approach differs from other platforms on this axis.

Frequently asked questions

Does Teler accept the same audio format as Twilio Media Streams?

Both platforms use base64-encoded 8 kHz mu-law audio over WebSocket for the telephony leg, so the codec requirement is the same. The difference is in the message envelope: Teler’s JSON frame structure and event type names differ from Twilio’s Connected, Start, Media, Mark, Clear, Stop sequence. You must remap your message handlers explicitly rather than assuming string equality between the two contracts.

What happens if I send audio with WAV headers to Teler?

The call audio will be garbled at the start of every utterance. Teler, like Twilio, expects raw mu-law bytes with no container headers. Strip the WAV header before base64-encoding. Deepgram’s integration team documented this failure mode in August 2026 specifically for Twilio, and the same constraint applies to any platform that expects raw telephony audio.

Should I use SIP trunking or WebSocket streaming for the migration?

Use WebSocket streaming if you are building an AI voice agent that needs to inject synthesized audio into the live call. Use SIP trunking if you already operate a media server or PBX and want to own the RTP (Real-time Transport Protocol, RFC 3550) path. Teler supports both, with SIP credential or IP authentication and IP access control lists for the SIP path.

How does Teler handle high call volumes during the migration?

Teler’s edge-distributed points of presence handle carrier connectivity and media routing, so your application server is not in the path for PSTN signaling. Your WebSocket server still needs to handle concurrent connections, and you must ensure your STT, LLM, and TTS providers can absorb the concurrent request load. Teler’s documentation does not publish a concurrency cap, so test your pipeline under realistic load before cutting over production traffic. See which voice calling API providers handle platform-scale traffic for the architectural patterns that matter at scale.

What must I build myself that Twilio provided out of the box?

Both Twilio and Teler leave STT, LLM, and TTS to you. What changes in the migration is the call control vocabulary: you replace TwiML verbs like <Say>, <Gather>, and <Dial> with Teler’s WebSocket call control messages for hangup, mute, DTMF, play audio, and cold transfer. You also build your own barge-in coordinator, because neither platform provides a built-in interruption handler that spans the telephony buffer and the TTS generator simultaneously.

Can I run Twilio and Teler in parallel during the migration?

Yes. Route a subset of your DIDs to Teler while keeping the rest on Twilio, and compare call quality and latency metrics before full cutover. Keep your TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN active during the parallel-run phase. The main risk is transcript state divergence if your application shares a single state store across both call paths, so isolate state by call source during the overlap period.

Does Teler work with OpenAI Realtime, Gemini Live, and other models?

Teler is model-agnostic and documented as compatible with OpenAI Realtime, Gemini Live, ElevenLabs, Deepgram, Vapi, Ultravox, Sarvam, Devnagri, and LiveKit. The resampling requirement is model-specific: OpenAI Realtime operates at 24 kHz, so you must downsample from 24 kHz to 8 kHz before sending to Teler and upsample from 8 kHz to 24 kHz before sending to the model. See Building Bidirectional Streaming Voice Agents Using Teler with Gemini Live for a worked example.

What Opus or wideband codec options are available on the telephony leg?

The PSTN leg uses G.711 mu-law at 8 kHz in both Twilio and Teler, because that is the narrowband telephony standard defined in ITU-T G.711. Opus (RFC 6716) is not available on the PSTN leg. If you need wideband audio, configure Teler’s stream sample rate to 16 kHz, which gives you higher-fidelity audio on the WebSocket path while the carrier leg remains at 8 kHz.

What to do next with your twilio migration

A twilio migration to Teler is achievable in a single sprint if you treat it as an audio pipeline swap rather than a full platform replacement. The decision that matters most is whether you use bidirectional WebSocket streaming or SIP trunking: streaming is the right default for AI voice agents, and SIP is the right choice only if you already operate your own media infrastructure. If your use case involves real-time interruption handling, model-agnostic LLM routing, or sub-100ms latency targets, Teler’s transport layer is built for that workload. If your primary need is simple IVR routing with no raw audio access, the webhook-only approach on either platform is simpler to operate. Start with a parallel run on a subset of your numbers, validate your resampling and barge-in logic under load, then cut over.

About the Author: Subhash Kalluri is the Founder & CEO of FreJun, which he has been building since 2019. With over 8 years in voice communication and SaaS, he works on the problem of getting real-time AI onto live phone calls: raw media streaming, interruption handling, and carrier connectivity. That work became FreJun Teler, FreJun’s programmable voice API for developers building AI phone agents. Connect with him on LinkedIn.

Get started with FreJun today

In this article

Make every voice interaction count

FreJun is a real-time voice platform for automating calls and connecting AI bots.

Popular Articles
Twilio migration switch flipped from Twilio Voice to FreJun Teler WebSocket media transport
A technical guide to twilio migration covering the exact steps to move from Twilio Programmable Voice to Teler's WebSocket-based voice...
Indic-language voice agents built on the Sarvam API streaming through FreJun Teler to live calls.
This guide covers how to build Indic-language voice agents using the Sarvam API for speech recognition and synthesis, paired with...
WebSocket vs SIP shown as two equal audio transport lanes for AI voice agents on FreJun Teler.
Choosing between WebSocket and SIP for AI voice agents in 2026 comes down to where your media terminates and how...