Skip to main content

Prerequisites

  • Python 3.12+
  • Docker (for Postgres + Redis)
  • Twilio account with a phone number
  • OpenAI API key (or Ollama for local LLM)
  • Deepgram API key (free at console.deepgram.com)
  • ngrok (for local development)

Setup

1

Clone and install

git clone https://github.com/kobikis/turncall.git
cd turncall
python -m venv .venv
source .venv/bin/activate
make dev
2

Configure environment

cp env.example .env
Edit .env with your credentials:
.env
DATABASE_URL=postgresql+asyncpg://turncall:turncall@localhost:5432/turncall
REDIS_URL=redis://localhost:6379/0
TWILIO_ACCOUNT_SID=ACxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxx
OPENAI_API_KEY=sk-xxxxxxxx
DEEPGRAM_API_KEY=xxxxxxxx
3

Start infrastructure

make docker-up    # Postgres + Redis containers
make migrate      # Create database tables
4

Start the server

make run          # Starts on http://localhost:8090
5

Expose via ngrok

ngrok http 8090
# Copy the https://xxxx.ngrok.io URL
6

Run the example

python examples/receptionist/setup.py \
  --twilio-number "+15559876543" \
  --twilio-number-sid "PNxxxxxxxx" \
  --server-url "https://xxxx.ngrok.io"
7

Call your number

The receptionist agent will answer, understand your intent, and route accordingly.

What You Just Built

Twilio opens a media-stream WebSocket to /ws/media-stream; the pipeline runs until you hang up, then the call is marked completed.

Manual Setup via API

If you prefer to set things up step by step:

Create a project

curl -X POST http://localhost:8090/v1/projects \
  -H "Content-Type: application/json" \
  -d '{"name": "my-project"}'

Create an API key

curl -X POST "http://localhost:8090/v1/api-keys?project_id=PROJECT_ID" \
  -H "Content-Type: application/json" \
  -d '{"name": "dev-key", "role": "admin"}'
# Save the raw_key from the response

Create an agent

curl -X POST http://localhost:8090/v1/agents \
  -H "Authorization: Bearer tc_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-agent",
    "config": {
      "system_prompt": "You are a helpful assistant.",
      "first_message": "Hello! How can I help?",
      "stt": {"provider": "deepgram", "model": "nova-2", "language": "en"},
      "llm": {"provider": "openai", "model": "gpt-4o-mini"},
      "tts": {"provider": "deepgram", "voice": "aura-2-helena-en"}
    }
  }'

Publish the agent

curl -X POST http://localhost:8090/v1/agents/AGENT_ID/publish \
  -H "Authorization: Bearer tc_xxx"

Bind a phone number

curl -X POST http://localhost:8090/v1/phone-numbers \
  -H "Authorization: Bearer tc_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "external_number_sid": "PNxxxxxxxx",
    "e164_number": "+15559876543",
    "routing_target_type": "agent",
    "routing_target_id": "AGENT_ID"
  }'

Configure Twilio webhooks

Set your Twilio number’s webhook URLs:
  • Voice URL: https://xxxx.ngrok.io/webhooks/twilio/voice/inbound (POST)
  • Status URL: https://xxxx.ngrok.io/webhooks/twilio/status (POST)

Call your number

That’s it — call the number and talk to your agent.

Environment Variables

VariableRequiredDescription
DATABASE_URLYesPostgreSQL connection string
REDIS_URLYesRedis connection string
TWILIO_ACCOUNT_SIDYesTwilio account SID
TWILIO_AUTH_TOKENYesTwilio auth token
OPENAI_API_KEYYes*OpenAI API key. *Not required if using Ollama
DEEPGRAM_API_KEYYesDeepgram API key
ELEVENLABS_API_KEYNoElevenLabs API key
CARTESIA_API_KEYNoCartesia API key
ANTHROPIC_API_KEYNoAnthropic API key (for Claude)
GOOGLE_API_KEYNoGoogle API key (for Gemini Live S2S)
OPENROUTER_API_KEYNoOpenRouter API key (multi-model + fallback routing)
HEYGEN_LIVE_AVATAR_API_KEYNoHeyGen video avatar (LiveAvatar key, app.liveavatar.com)
TAVUS_API_KEYNoTavus video avatar (platform.tavus.io)
See Providers for provider-specific configuration and Video Avatar for avatars.