A reference integration between Teler and Deepgram, based on media streaming over WebSockets.
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
This project is a reference implementation to bridge Teler and Deepgram. It enables real-time media streaming over WebSockets, facilitating live audio interactions.
- An AI phone agent that listens, thinks and speaks through Deepgram's Voice Agent API
- Outbound qualification or survey calls with a configurable agent prompt
- A Python base for experimenting with Deepgram listen/think/speak providers on real calls
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).
sequenceDiagram
participant U as Your app
participant B as This bridge
participant T as Teler
participant V as Deepgram
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)
- 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 fromSERVER_DOMAIN). - When the call connects, Teler requests
/api/v1/calls/flow, and the bridge answers with a stream action that points Teler to its WebSocket. - Teler opens a WebSocket to
/api/v1/calls/media-streamand starts sending the caller's audio. - The bridge opens a session with Deepgram Voice Agent WebSocket (
DEEPGRAM_WS_URL). - Audio flows both ways: caller audio is forwarded to Deepgram, and Deepgram's audio is sent back to Teler.
- Call status events are delivered to
/api/v1/webhooks/receiver.
More detail: How media streaming works and WebSocket protocol.
- Real-time streaming of media via WebSockets
- Bi-directional communication between Teler client and Deepgram
- Sample structure for deployment (Docker, environment variables)
- Basic error handling and connection management
Ensure you have the following installed / available:
- Docker
- Valid API credentials / access:
- Teler account / API key / endpoints (frejun account)
- Deepgram API access
- Provider credentials: Deepgram API key with Voice Agent API access
- 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) and4040(ngrok inspector) must be free on the host when using Docker Compose.
-
Clone and configure:
git clone https://github.com/frejun-tech/teler-deepgram-bridge.git cd teler-deepgram-bridge cp .env.example .env # Edit .env with your actual values
-
Run with Docker:
docker compose up -d --build
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 8000In 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.
curl http://localhost:8000/health
curl http://localhost:8000/ngrok-statusThen place a test call with the request in Call Initiation Example.
| Variable | Description | Default |
|---|---|---|
Deepgram_API_KEY |
Your Deepgram API key | Required |
TELER_API_KEY |
Your Teler API key | Required |
NGROK_AUTHTOKEN |
Your ngrok auth token | Required |
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 |
DEEPGRAM_API_KEY |
Required | — | Deepgram API key |
DEEPGRAM_WS_URL |
Optional | wss://agent.deepgram.com/v1/agent/converse |
Deepgram Voice Agent WebSocket URL |
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 |
GET /- Health check with server domainGET /health- Service statusGET /ngrok-status- Current ngrok status and URLPOST /api/v1/calls/initiate-call- Start a new call with dynamic phone numbersPOST /api/v1/calls/flow- Get call flow configurationWebSocket /api/v1/calls/media-stream- Audio streaming between teler and deepgramPOST /api/v1/webhooks/receiver- Teler → Deepgram webhook receiver
curl -X POST "https://your-domain/api/v1/calls/initiate-call" \
-H "Content-Type: application/json" \
-d '{
"from_number": "+1234567890",
"to_number": "+0987654321"
}'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 Deepgram fails or closes immediately.
Check the Deepgram credentials in .env (see Required vs optional settings) and the app logs (docker compose logs app) for the provider's error message.
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.
- 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-callhas 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://andwss://). - 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_LEVELappropriately and handle logs as personal data. - Report security issues privately to support@frejun.com rather than in a public GitHub issue.
- Report a bug or request a feature: open an issue at github.com/frejun-tech/teler-deepgram-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.
Same integration in Node.js (TypeScript): teler-deepgram-node-bridge
Python bridges: cartesia, devnagri, elevenlabs, gemini, openai, ultravox, vapi
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)
- Teler documentation
- Quickstart
- Media Streaming Bridge
- Deepgram recipe
- API reference
- FreJun Teler
- Teler dashboard
This project is licensed under the MIT License. See LICENSE for details.