Skip to content

About

A reference integration between Teler and VAPI, based on Media Streaming over WebSockets.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

Repository files navigation

Teler VAPI Bridge

License: MIT Python Docker Teler Docs

A reference integration between Teler and VAPI, based on Media Streaming over WebSockets.

What is Teler?

Teler is a programmable voice API by FreJun. It handles carriers, phone numbers, and real-time audio streaming so you can connect AI models directly to phone calls. → FreJun-Teler

Overview

This repository connects phone calls on Teler to a Vapi assistant. It is a Python (FastAPI) server that places a call through the Teler API, receives the call's audio over Teler's Media Streaming Bridge, forwards it to Vapi, and streams the response audio back into the call.

Use it as a working reference: run it as-is to hear Vapi on a real phone call, then adapt the code for your own application.

What you can build

  • Phone access to a Vapi assistant identified by VAPI_ASSISTANT_ID
  • Outbound calls handled by your Vapi assistant's prompt, tools and voice
  • A Python base for testing Vapi assistants with Teler numbers

The included POST /api/v1/calls/initiate-call endpoint places outbound calls. For inbound calls, point your Teler number's voice app at this server's /api/v1/calls/flow endpoint (see Voice apps).

How it works

sequenceDiagram
    participant U as Your app
    participant B as This bridge
    participant T as Teler
    participant V as Vapi
    participant C as Callee

    U->>B: POST /api/v1/calls/initiate-call
    B->>T: Create call (flow URL, status callback)
    T->>C: Rings phone
    T->>B: POST /api/v1/calls/flow
    B-->>T: Stream action (WebSocket URL)
    T->>B: Open WebSocket /api/v1/calls/media-stream
    B->>V: Open session
    C-->>T: Speaks
    T->>B: Audio chunks
    B->>V: Caller audio
    V-->>B: Agent audio
    B->>T: Audio chunks
    T-->>C: Hears the reply
    T->>B: Call events (webhook)
Loading
  1. Your app calls POST /api/v1/calls/initiate-call. The bridge asks Teler to place the call, passing its own flow URL and webhook URL (both built from SERVER_DOMAIN).
  2. When the call connects, Teler requests /api/v1/calls/flow, and the bridge answers with a stream action that points Teler to its WebSocket.
  3. Teler opens a WebSocket to /api/v1/calls/media-stream and starts sending the caller's audio.
  4. The bridge opens a session with Vapi assistant WebSocket.
  5. Audio flows both ways: caller audio is forwarded to Vapi, and Vapi's audio is sent back to Teler.
  6. Audio is resampled where the rates differ: Vapi (16 kHz) → Teler (8 kHz).
  7. Outgoing audio is buffered (VAPI_MESSAGE_BUFFER_SIZE) so Teler receives evenly sized chunks.
  8. Call status events are delivered to /api/v1/webhooks/receiver.

More detail: How media streaming works and WebSocket protocol.

Prerequisites

  • Teler account and API key, from the FreJun Teler dashboard
  • A Teler phone number to call from
  • Vapi API key and a Vapi assistant ID
  • ngrok account and auth token (used by the Docker setup to expose the server publicly)
  • Runtime: Docker and Docker Compose (recommended). To run without Docker: Python 3.11 (the Docker image uses python:3.11-slim).
  • Public HTTPS/WSS endpoint: Teler must reach this server from the internet for the flow URL, the media WebSocket and webhooks. The Docker setup uses ngrok for this; in production, use your own domain with TLS.
  • Outbound internet access from the server to the Teler API and to the provider's API.
  • Ports: 8000 (app) and 4040 (ngrok inspector) must be free on the host when using Docker Compose.

Setup

  1. Clone and configure:

    git clone https://github.com/frejun-tech/teler-vapi-bridge.git
    cd teler-vapi-bridge
    cp env.example .env
    # Edit .env with your actual values
  2. Run with Docker:

    docker compose up -d --build

Run without Docker (local development)

python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000

In a second terminal, expose the server with ngrok http 8000. Outside Docker the app cannot auto-detect the ngrok URL (it looks for the ngrok container), so set SERVER_DOMAIN in .env to the ngrok hostname, without https://, before starting the server.

Verify it is running

curl http://localhost:8000/health
curl http://localhost:8000/ngrok-status

Then place a test call with the request in Call Initiation Example.

Environment Variables

Variable Description Default
VAPI_API_KEY Your VAPI API key Required
VAPI_ASSISTANT_ID Your VAPI assistant ID Required
VAPI_SAMPLE_RATE Audio sample rate 16000
VAPI_MESSAGE_BUFFER_SIZE Messages to buffer before relay 50
TELER_API_KEY Your Teler API key Required
NGROK_AUTHTOKEN Your ngrok auth token Required

Required vs optional settings

These values are taken from the code (app/core/config.py). Where this table and the table above differ, the code is what runs.

Variable Required Default Description
TELER_API_KEY Required — Teler API key, used to place calls through the Teler API
VAPI_API_KEY Required — Vapi API key
VAPI_ASSISTANT_ID Required — Vapi assistant ID
VAPI_SAMPLE_RATE Optional 16000 Vapi audio sample rate
VAPI_MESSAGE_BUFFER_SIZE Optional 50 Vapi messages buffered before relaying to Teler
NGROK_AUTHTOKEN Required with Docker — ngrok auth token, read by the ngrok container in docker-compose (not by the app itself)
SERVER_DOMAIN Optional auto-detected Public hostname of this server, without https://. Auto-detected from ngrok when it is running; set it yourself when deploying without ngrok
SERVER_HOST Optional 0.0.0.0 Interface the server binds to
SERVER_PORT Optional 8000 Port the server listens on
LOG_LEVEL Optional INFO Log verbosity
FROM_NUMBER Optional placeholder Fallback caller number used by the call-initiation code
TO_NUMBER Optional placeholder Fallback destination number used by the call-initiation code

API Endpoints

  • GET / - Health check with server domain
  • GET /health - Service status
  • GET /ngrok-status - Current ngrok status and URL
  • POST /api/v1/calls/initiate-call - Start a new call with dynamic phone numbers
  • POST /api/v1/calls/flow - Get call flow configuration
  • WebSocket /api/v1/calls/media-stream - Audio streaming
  • POST /api/v1/webhooks/receiver - Teler webhook receiver

Call Initiation Example

curl -X POST "http://localhost:8000/api/v1/calls/initiate-call" \
  -H "Content-Type: application/json" \
  -d '{
    "from_number": "+1234567890",
    "to_number": "+0987654321"
  }'

Features

  • Bi-directional media streaming - Bridges audio between Teler and VAPI (Voice API) over WebSockets.
  • Real-time audio handling - Receives live audio chunks from Teler, processes them, and forwards to VAPI; streams responses back to Teler.
  • Dockerized setup - Comes with Dockerfile and docker-compose.yml for easy local development and deployment.
  • Dynamic ngrok URL detection - Automatically detects current ngrok domain
  • Audio buffering - Buffers VAPI messages before relaying to Teler
  • Sample rate conversion - Converts VAPI 16kHz audio to Teler 8kHz

Troubleshooting

initiate-call returns an authentication error from Teler. Check TELER_API_KEY in .env, then restart the container so it picks up the change. Keys are managed in the Teler dashboard.

The phone rings, but there is silence after answering. Teler could not reach the flow URL or the WebSocket. Open /ngrok-status and confirm a public URL is listed. If you are not using ngrok, set SERVER_DOMAIN to your public hostname without https://.

/ngrok-status shows no tunnel. Check that NGROK_AUTHTOKEN is set and look at the ngrok container logs (docker compose logs ngrok). The free ngrok plan gives a new URL on every restart, so place calls only after the tunnel is up.

The connection to Vapi fails or closes immediately. Check the Vapi credentials in .env (see Required vs optional settings) and the app logs (docker compose logs app) for the provider's error message.

Audio is choppy or delayed. Tune VAPI_MESSAGE_BUFFER_SIZE. Smaller values reduce latency; larger values smooth playback.

Running outside Docker, the app reports no server domain. The Python app looks for ngrok at http://ngrok:4040, which only exists inside Docker Compose. Set SERVER_DOMAIN manually.

Bind for 0.0.0.0:8000 failed: port is already allocated. Another process is using port 8000 or 4040. Stop it, or change the host port mapping in docker-compose.

Security

  • Keep secrets out of git. Store keys only in .env, which is listed in .gitignore. Never commit real keys, and use placeholders in examples and issues.
  • Rotate keys if you suspect a key was exposed, and use separate keys for development and production.
  • Protect the call-initiation endpoint. POST /api/v1/calls/initiate-call has no authentication in this reference code, so anyone who can reach your public URL can place calls billed to your Teler account. Before exposing the server, add authentication (an API key header, for example) or restrict access by IP.
  • The flow, media and webhook endpoints are not verified in this reference code. In production, keep the server behind your own domain and validate incoming requests.
  • Use ngrok only for development. In production, deploy behind your own domain with TLS (https:// and wss://).
  • Call recording is on by default in this bridge. Make sure you have the consent your jurisdiction requires, or turn recording off.
  • Logs can contain call details such as phone numbers and transcripts. Set LOG_LEVEL appropriately and handle logs as personal data.
  • Report security issues privately to support@frejun.com rather than in a public GitHub issue.

Contributing

  • Report a bug or request a feature: open an issue at github.com/frejun-tech/teler-vapi-bridge/issues. Include steps to reproduce, the relevant logs (with keys removed) and your environment.
  • Submit a change: fork the repository, create a branch, make your change, and open a pull request that describes what changed and how you tested it. Keep pull requests focused on one change.
  • For questions about your Teler account, contact support@frejun.com.

Related repositories

Same integration in Node.js (TypeScript): teler-vapi-node-bridge

Python bridges: cartesia, deepgram, devnagri, elevenlabs, gemini, openai, ultravox

Node.js bridges: cartesia, deepgram, devnagri, elevenlabs, gemini, openai, sarvam, ultravox, vapi

Other Teler references: teler-wav-bridge, teler-livekit-outbound, teler-openai-sip-trunking

SDKs: teler-py (Python), teler-node (Node.js)

Documentation

License

This project is licensed under the MIT License. See LICENSE for details.

About

A reference integration between Teler and VAPI, based on Media Streaming over WebSockets.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages