Things you’ll need

  • An active Daily developer account with API key
  • At least one phone number purchased through Daily (covered below)
  • A server to handle webhooks (we’ll show local development with ngrok)

Phone Number Management

Before setting up dial-in or dial-out, you’ll need to purchase phone numbers through Daily.

Daily Phone Numbers Guide

Complete guide to searching, purchasing, and managing phone numbers with Daily’s REST API

Dial-in

Dial-in allows users to call your phone number and connect directly to your Pipecat bot.

How It Works

Here’s the sequence of events when someone calls your Daily phone number:
  1. Daily receives an incoming call to your phone number
  2. Daily calls your webhook server (/start endpoint)
  3. The server creates a Daily room with dial-in capabilities
  4. The server starts the bot process with the room details
  5. The caller is put on hold with music
  6. The bot joins the Daily room and signals readiness
  7. Daily forwards the call to the Daily room
  8. The caller and the bot are connected, and the bot handles the conversation

Create a pinless dial-in configuration

A pinless dial-in configuration connects your purchased phone number to a webhook URL. When someone calls your number, Daily will send a webhook to your server with call details.
Create pinless dial-in config
curl --location 'https://api.daily.co/v1' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_DAILY_API_KEY' \
--data '{
    "properties": {
        "pinless_dialin": [
            {
                "phone_number": "DAILY_PROVISIONED_NUMBER_HERE",
                "room_creation_api": "BOT_RUNNER_URL/start_bot"
            }
        ]
    }
}'

Set up your webhook server

Your server acts as the orchestrator for incoming calls. When Daily receives a call, it sends a webhook to your server with call details (callId and callDomain). Your server then:
  1. Creates a Daily room with SIP dial-in capabilities
  2. Spawns a bot process with the room details and call information
  3. Returns success to Daily, which keeps the caller on hold until the bot is ready
The server handles the coordination between Daily’s telephony infrastructure and your Pipecat bot instances. Each incoming call gets its own dedicated room and bot process.

Complete Server Implementation

See the full FastAPI server code with room creation, process management, and error handling
For local development, expose your server using ngrok:
python server.py
ngrok http 8000
# Use the ngrok URL in your dialin config
Use --subdomain to get a reusable URL for your ngrok tunnel.

Configure your Pipecat bot for dial-in

The bot receives arguments from the server process and uses them to create the Daily transport. The key components are: Bot Entry Point: The bot() method receives RunnerArguments containing:
  • room_url: Daily room URL for the call
  • token: Daily room token for authentication
  • call_id and call_domain: Call identifiers from Daily’s webhook
Transport Creation: These arguments are used to configure the DailyTransport:
bot.py
from pipecat.transports.daily import DailyTransport, DailyParams, DailyDialinSettings

async def bot(args: RunnerArguments):
    # Configure dial-in settings with webhook data
    daily_dialin_settings = DailyDialinSettings(
        call_id=args.call_id,
        call_domain=args.call_domain
    )

    # Create transport with dial-in configuration
    transport = DailyTransport(
        args.room_url,
        args.token,
        "Voice Bot",
        DailyParams(
            api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
            api_key=os.getenv("DAILY_API_KEY"),
            dialin_settings=daily_dialin_settings,
            audio_in_enabled=True,
            audio_out_enabled=True,
            video_out_enabled=False,
            vad_analyzer=SileroVADAnalyzer(),
            transcription_enabled=True,
        )
    )

    # Pass transport to your bot pipeline
    # Your bot setup and pipeline creation here...

Complete Bot Implementation

See the full bot.py with argument handling, transport setup, and pipeline configuration

Run the Example

Once you have your webhook server configured and your bot ready, you can test the complete dial-in flow:
  1. Start your server: Run your FastAPI webhook server
  2. Expose with ngrok: Make your local server publicly accessible
  3. Configure Daily: Set up the pinless dial-in configuration with your webhook URL
  4. Call your number: Dial your Daily phone number to connect with your bot

Complete Setup Instructions

See the full README with step-by-step setup, environment variables, and troubleshooting tips

Dial-out

Dial-out allows your bot to initiate calls to phone numbers. Unlike dial-in, your bot starts the call rather than waiting for incoming calls.
You must contact Daily to enable dial-out for your account. Submit a support request here.

How It Works

Here’s the sequence of events for dial-out calls:
  1. Your application triggers a dial-out (via API call or user action)
  2. Server creates a Daily room with dial-out capabilities enabled
  3. Bot joins the Daily room and sets up the pipeline
  4. Bot initiates the dial-out call to the target phone number
  5. Daily connects the call to the phone number
  6. The recipient answers and is connected to your bot
  7. The bot handles the conversation with the called party

Set up your server for dial-out

The dial-out server is simpler than dial-in since you’re initiating calls rather than receiving webhooks. Your server needs to:
  1. Create Daily rooms with dial-out enabled
  2. Start bot processes with the target phone number
  3. Handle API requests to trigger outbound calls

Complete Server Implementation

See the full FastAPI server code with room creation and dial-out triggering

Configure your Pipecat bot for dial-out

The dial-out bot receives the target phone number and creates a transport without dial-in settings: Bot Entry Point: The bot() method receives RunnerArguments containing:
  • room_url: Daily room URL for the call
  • token: Daily room token for authentication
  • phone_number: Target phone number to call
Transport Creation: These arguments are used to configure the DailyTransport:
bot.py
from pipecat.transports.daily import DailyTransport, DailyParams

async def bot(args: RunnerArguments):
    # Create transport for dial-out (no dial-in settings needed)
        transport = DailyTransport(
        args.room_url,
        args.token,
        "Voice Bot",
            DailyParams(
            api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
            api_key=os.getenv("DAILY_API_KEY"),
                audio_in_enabled=True,
                audio_out_enabled=True,
                video_out_enabled=False,
                vad_analyzer=SileroVADAnalyzer(),
                transcription_enabled=True,
            )
        )

    # Start the dial-out call
    await transport.start_dialout(args.phone_number)

    # Pass transport to your bot pipeline
    # Your bot setup and pipeline creation here...

Complete Bot Implementation

See the full bot.py with dial-out configuration and pipeline setup

Run the Example

To test dial-out functionality:
  1. Start your server: Run your FastAPI server
  2. Trigger a call: Make an API request to start a dial-out call
  3. Answer your phone: The bot will call the specified number
  4. Talk to your bot: Have a conversation with your AI agent

Complete Setup Instructions

See the full README with step-by-step setup, API usage, and configuration details

Call Transfers

Daily supports cold transfers, allowing you to transfer an active call to another phone number. The bot can initiate a transfer and then leave the call, connecting the caller directly to the transfer destination.

How Call Transfers Work

  1. Bot receives transfer request (via function call or user input)
  2. Bot informs the caller about the transfer
  3. Bot initiates SIP call transfer to the destination number
  4. Daily connects the transfer to the destination
  5. Bot leaves the call (cold transfer)
  6. Caller and destination continue the conversation

Implementing Call Transfers

Call transfers are typically implemented as LLM function calls that your bot can invoke:
bot.py
async def dial_operator(transport: BaseTransport, params: FunctionCallParams):
    """Function the bot can call to transfer to an operator."""
    operator_number = os.getenv("OPERATOR_NUMBER", "+1234567890")

    # Inform the user about the transfer
    content = "I'm transferring you to a supervisor now. Please hold while I connect you."
    message = {"role": "system", "content": content}
    await params.llm.push_frame(LLMMessagesAppendFrame([message], run_llm=True))

    # Execute the SIP call transfer
    transfer_params = {"toEndPoint": operator_number}
    await transport.sip_call_transfer(transfer_params)

Handling Transfer Events

Your bot should handle transfer-related events to manage the call flow:
bot.py
@transport.event_handler("on_dialout_answered")
async def on_dialout_answered(transport, data):
    logger.info(f"Transfer successful: {data}")
    # Cold transfer: bot leaves, caller and operator continue
    await task.queue_frames([EndFrame()])

Complete Transfer Implementation

See the full call transfer example with LLM function calls, event handling, and error management

Deployment

Pipecat Cloud

For production deployment without managing your own infrastructure, use Pipecat Cloud:

Self-Hosted Deployment

For self-hosted production deployment, ensure your servers are:
  • Publicly accessible with HTTPS
  • Able to handle concurrent requests
  • Properly configured with your Daily API credentials

Next Steps