Daily + Twilio SIP Voice Bot
This project demonstrates how to create a voice bot that can receive phone calls via Twilio and use Daily's SIP capabilities to enable voice conversations.
How It Works
- Twilio receives an incoming call to your phone number
- Twilio calls your webhook server (
/callendpoint) - The server creates a Daily room with SIP capabilities
- The server starts the bot process with the room details
- The caller is put on hold with music
- The bot joins the Daily room and signals readiness
- Twilio forwards the call to Daily's SIP endpoint
- The caller and bot are connected, and the bot handles the conversation
Prerequisites
- A Daily account with an API key
- A Twilio account with a phone number that supports voice
- OpenAI API key for the bot's intelligence
- Cartesia API key for text-to-speech
Setup
- Create a virtual environment and install dependencies
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
- Set up environment variables
Copy the example file and fill in your API keys:
cp .env.example .env
# Edit .env with your API keys
- Configure your Twilio webhook
In the Twilio console:
- Go to your phone number's configuration
- Set the webhook for "A Call Comes In" to your server's URL + "/call"
- For local testing, you can use ngrok to expose your local server
ngrok http 8000
# Then use the provided URL (e.g., https://abc123.ngrok.io/call) in Twilio
Running the Server
Start the webhook server:
python server.py
Testing
Call your Twilio phone number. The system should answer the call, put you on hold briefly, then connect you with the bot.
Customizing the Bot
You can customize the bot's behavior by modifying the system prompt in bot.py.
Changing the Hold Music
To change the ringing sound or hold music that callers hear while waiting to be connected to the bot, update the URL in server.py:
resp = VoiceResponse()
resp.play(
url="https://your-custom-audio-file-url.mp3",
loop=10,
)
Read Twilio's guide on how to set up an mp3 in a voice call.
Handling Multiple SIP Endpoints
The bot is configured to handle multiple on_dialin_ready events that might occur with multiple SIP endpoints. It ensures that each call is only forwarded once using a simple flag:
# Flag to track if call has been forwarded
call_already_forwarded = False
@transport.event_handler("on_dialin_ready")
async def on_dialin_ready(transport, cdata):
nonlocal call_already_forwarded
# Skip if already forwarded
if call_already_forwarded:
logger.info("Call already forwarded, ignoring this event.")
return
# ... forwarding code ...
call_already_forwarded = True
Note that normally calls only require a single SIP endpoint. If you are planning to forward the call to a different number, you will need to set up 2 SIP endpoints: one for the initial call and one for the forwarded call. IMPORTANT: ensure that your on_dialin_ready handler only handles the first call.
Daily SIP Configuration
The bot configures Daily rooms with SIP capabilities using these settings:
sip_params = DailyRoomSipParams(
display_name="phone-user", # This will show up in the Daily UI; optional display the dialer's number
video=False, # Audio-only call
sip_mode="dial-in", # For receiving calls (vs. dial-out)
num_endpoints=1, # Number of SIP endpoints to create
)
Troubleshooting
Call is not being answered
- Check that your Twilio webhook is correctly configured
- Verify your Twilio account has sufficient funds
- Check the logs of both the server and bot processes
Call connects but no bot is heard
- Ensure your Daily API key is correct and has SIP capabilities
- Check that the SIP endpoint is being correctly passed to the bot
- Verify that the Cartesia API key and voice ID are correct
Bot starts but disconnects immediately
- Check the Daily and Twilio logs for any error messages
- Ensure your server has stable internet connectivity