> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pipecat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# DailyTransport

> WebRTC transport implementation using Daily for real-time audio/video communication

## Overview

`DailyTransport` provides real-time audio and video communication using Daily's hosted WebRTC platform. It handles bidirectional media streams, participant management, transcription, recording, and telephony features without requiring your own WebRTC infrastructure. Daily manages all the complexity of WebRTC connections, NAT traversal, and global media routing.

<CardGroup cols={2}>
  <Card title="DailyTransport API Reference" icon="code" href="https://reference-server.pipecat.ai/en/latest/api/pipecat.transports.daily.transport.html">
    Transport methods and configuration options
  </Card>

  <Card title="Example Implementation" icon="play" href="https://github.com/pipecat-ai/pipecat/blob/main/examples/transports/transports-daily.py">
    Complete Daily transport voice agent example
  </Card>

  <Card title="Daily API Documentation" icon="book" href="https://docs.daily.co/reference/rest-api">
    Official Daily REST API and platform documentation
  </Card>

  <Card title="Daily Dashboard" icon="external-link" href="https://dashboard.daily.co/u/signup?pipecat=true">
    Sign up for Daily API access and room management
  </Card>
</CardGroup>

## Installation

To use `DailyTransport`, install the required dependencies:

```bash theme={null}
uv add "pipecat-ai[daily]"
```

## Prerequisites

### Daily Account Setup

Before using DailyTransport, you need:

1. **Daily Account**: Sign up at [Daily Dashboard](https://dashboard.daily.co/u/signup?pipecat=true)
2. **API Key**: Generate a Daily API key from your dashboard
3. **Room Creation**: Daily rooms must be created before connecting (see Usage section)

### Required Environment Variables

* `DAILY_API_KEY`: Your Daily API key for room creation and management

## Key Features

* **Hosted WebRTC**: No infrastructure setup required - Daily handles all WebRTC complexity
* **Multi-participant Support**: Handle multiple participants with individual audio/video tracks
* **Multi-track Audio/Video**: Publish multiple custom audio and video tracks simultaneously with per-track configuration, including built-in support for Daily's `screenVideo` destination
* **Built-in Transcription**: Real-time speech-to-text with Deepgram integration
* **Telephony Integration**: \[Dial-in/dial-out support for phone numbers via [SIP](/pipecat/telephony/twilio-daily-sip)/[PSTN](/pipecat/telephony/daily-pstn)
* **Recording & Streaming**: Built-in call recording and live streaming capabilities
* **Global Infrastructure**: Daily's edge network ensures low latency worldwide
* **Advanced Controls**: Participant management, permissions, and media routing

## Configuration

### DailyTransport

<ParamField path="room_url" type="str" required>
  URL of the Daily room to connect to.
</ParamField>

<ParamField path="token" type="str" default="None">
  Authentication token for the room. Required for private rooms or when specific
  permissions are needed.
</ParamField>

<ParamField path="bot_name" type="str" required>
  Display name for the bot in the call.
</ParamField>

<ParamField path="params" type="DailyParams" default="DailyParams()">
  Transport configuration parameters. See [DailyParams](#dailyparams) below and
  [TransportParams](/api-reference/server/services/transport/transport-params)
  for inherited base parameters.
</ParamField>

<ParamField path="input_name" type="str" default="None">
  Optional name for the input transport processor.
</ParamField>

<ParamField path="output_name" type="str" default="None">
  Optional name for the output transport processor.
</ParamField>

### DailyParams

Inherits all parameters from [TransportParams](/api-reference/server/services/transport/transport-params) (audio, video, VAD settings) with these additional fields:

<ParamField path="api_url" type="str" default="https://api.daily.co/v1">
  Daily API base URL.
</ParamField>

<ParamField path="api_key" type="str" default="">
  Daily API authentication key.
</ParamField>

<ParamField path="audio_in_user_tracks" type="bool" default="True">
  Receive users' audio in separate tracks rather than a mixed stream.
</ParamField>

<ParamField path="dialin_settings" type="DailyDialinSettings" default="None">
  Settings for dial-in functionality. See
  [DailyDialinSettings](#dailydialinsettings) below.
</ParamField>

<ParamField path="camera_out_enabled" type="bool" default="True">
  Whether to enable the main camera output track.
</ParamField>

<ParamField path="camera_out_send_settings" type="Dict[str, Any]" default="None">
  Camera output track publishing settings. This dict is passed verbatim to the
  Daily client's camera publishing settings, allowing full control over
  encoding, codec, bitrate, and framerate. See [Daily
  VideoPublishingSettings](https://reference-python.daily.co/types.html#videopublishingsettings).
</ParamField>

<ParamField path="custom_audio_track_params" type="Mapping[str, DailyCustomAudioTrackParams]" default="None">
  Per-destination configuration for custom audio tracks. See
  [DailyCustomAudioTrackParams](#dailycustomaudiotrackparams) below.
</ParamField>

<ParamField path="custom_video_track_params" type="Mapping[str, DailyCustomVideoTrackParams]" default="None">
  Per-destination configuration for custom video tracks. See
  [DailyCustomVideoTrackParams](#dailycustomvideotrackparams) below.
</ParamField>

<ParamField path="microphone_out_enabled" type="bool" default="True">
  Whether to enable the main microphone track.
</ParamField>

<ParamField path="transcription_enabled" type="bool" default="False">
  Whether to enable Daily's built-in speech transcription (powered by Deepgram).
</ParamField>

<ParamField path="transcription_settings" type="DailyTranscriptionSettings" default="DailyTranscriptionSettings()">
  Configuration for the transcription service. See
  [DailyTranscriptionSettings](#dailytranscriptionsettings) below.
</ParamField>

### DailyDialinSettings

Settings for Daily's dial-in (SIP) functionality.

<ParamField path="call_id" type="str" default="">
  Call ID (UUID) representing the session ID in the SIP network.
</ParamField>

<ParamField path="call_domain" type="str" default="">
  Call domain (UUID) representing your Daily domain on the SIP network.
</ParamField>

### DailyTranscriptionSettings

Configuration for Daily's built-in transcription service (Deepgram).

<ParamField path="language" type="str" default="en">
  ISO language code for transcription.
</ParamField>

<ParamField path="model" type="str" default="nova-2-general">
  Deepgram transcription model to use.
</ParamField>

<ParamField path="profanity_filter" type="bool" default="True">
  Whether to filter profanity from transcripts.
</ParamField>

<ParamField path="redact" type="bool" default="False">
  Whether to redact sensitive information.
</ParamField>

<ParamField path="endpointing" type="bool" default="True">
  Whether to use endpointing to determine speech segments.
</ParamField>

<ParamField path="punctuate" type="bool" default="True">
  Whether to add punctuation to transcripts.
</ParamField>

<ParamField path="includeRawResponse" type="bool" default="True">
  Whether to include raw response data from Deepgram.
</ParamField>

<ParamField path="extra" type="Mapping[str, Any]" default="{&#x22;interim_results&#x22;: True}">
  Additional parameters passed to the Deepgram transcription service.
</ParamField>

### DailyCustomAudioTrackParams

Configuration for a custom audio track. If `send_settings` is not provided, the track will use the default audio publishing settings (bitrate, channel config, etc.).

<ParamField path="sample_rate" type="int" default="None">
  Audio sample rate in Hz. Defaults to transport's output sample rate.
</ParamField>

<ParamField path="channels" type="int" default="1">
  Number of audio channels.
</ParamField>

<ParamField path="send_settings" type="Dict[str, Any]" default="None">
  Optional Daily sendSettings dict for this track. See [Daily
  AudioPublishingSettings](https://reference-python.daily.co/types.html#audiopublishingsettings).
</ParamField>

### DailyCustomVideoTrackParams

Configuration for a custom video track. If `send_settings` is not provided, the track will use the default video publishing settings (framerate, bitrate, codec, etc.).

<ParamField path="width" type="int" default="1024">
  Video width in pixels.
</ParamField>

<ParamField path="height" type="int" default="768">
  Video height in pixels.
</ParamField>

<ParamField path="color_format" type="str" default="RGB">
  Video color format (e.g., "RGB", "RGBA", "BGRA").
</ParamField>

<ParamField path="send_settings" type="Dict[str, Any]" default="None">
  Optional Daily sendSettings dict for this track. See [Daily
  VideoPublishingSettings](https://reference-python.daily.co/types.html#videopublishingsettings).
</ParamField>

## Usage

DailyTransport connects your Pipecat bot to Daily rooms where it can communicate with participants through audio, video, and data channels. Rooms must be created using the Daily API before your bot can join.

The transport integrates with Pipecat's pipeline to process participant audio through your STT, LLM, and TTS services, then send responses back to participants.

See the [complete example](https://github.com/pipecat-ai/pipecat/blob/main/examples/transports/transports-daily.py) for a full implementation including:

* Daily room creation and token management
* Transport configuration with transcription and VAD
* Pipeline integration with participant event handling
* Advanced features like recording and dial-out

## Notes

### Screen Video Destination

DailyTransport includes built-in support for Daily's `screenVideo` destination. When `"screenVideo"` is included in the `video_out_destinations` parameter, a dedicated screen video track is automatically created at join time:

```python theme={null}
params = DailyParams(
    video_out_enabled=True,
    video_out_is_live=True,
    video_out_width=1280,
    video_out_height=720,
    video_out_destinations=["screenVideo"]
)

# Route frames to the screen video track
frame = OutputImageRawFrame(...)
frame.transport_destination = "screenVideo"
```

### Camera Publishing Settings

The `camera_out_send_settings` parameter provides full control over the camera track's publishing configuration:

```python theme={null}
params = DailyParams(
    camera_out_send_settings={
        "maxQuality": "high",
        "encodings": {
            "high": {"maxBitrate": 2_000_000, "maxFramerate": 30}
        },
    },
)
```

<Warning>
  The `TransportParams.video_out_bitrate` parameter is deprecated for Daily. Use
  `DailyParams.camera_out_send_settings` instead to configure camera publishing
  encodings (bitrate, framerate, codec, etc.).
</Warning>

## Event Handlers

DailyTransport provides event handlers for room lifecycle, participant management, messaging, telephony, and recording. Register handlers using the `@event_handler` decorator on the transport instance.

### Events Summary

| Event                         | Description                       |
| ----------------------------- | --------------------------------- |
| `on_joined`                   | Bot joined the room               |
| `on_connected`                | Bot connected to the room         |
| `on_left`                     | Bot left the room                 |
| `on_before_leave`             | About to leave the room (sync)    |
| `on_error`                    | Transport error occurred          |
| `on_call_state_updated`       | Call state changed                |
| `on_first_participant_joined` | First participant joined the room |
| `on_participant_joined`       | A participant joined              |
| `on_participant_left`         | A participant left                |
| `on_participant_updated`      | A participant's state updated     |
| `on_client_connected`         | A participant connected           |
| `on_client_disconnected`      | A participant disconnected        |
| `on_active_speaker_changed`   | Active speaker changed            |
| `on_app_message`              | App message received              |
| `on_transcription_message`    | Transcription message received    |
| `on_recording_started`        | Recording started                 |
| `on_recording_stopped`        | Recording stopped                 |
| `on_recording_error`          | Recording error occurred          |
| `on_dialin_connected`         | Dial-in call connected            |
| `on_dialin_ready`             | Dial-in SIP endpoint ready        |
| `on_dialin_stopped`           | Dial-in call stopped              |
| `on_dialin_error`             | Dial-in error                     |
| `on_dialin_warning`           | Dial-in warning                   |
| `on_dialout_answered`         | Dial-out call answered            |
| `on_dialout_connected`        | Dial-out call connected           |
| `on_dialout_stopped`          | Dial-out call stopped             |
| `on_dialout_error`            | Dial-out error                    |
| `on_dialout_warning`          | Dial-out warning                  |
| `on_dtmf_event`               | DTMF keypad tone received         |

### Room Lifecycle

#### on\_joined

Fired when the bot successfully joins the Daily room.

```python theme={null}
@transport.event_handler("on_joined")
async def on_joined(transport, data):
    print(f"Bot joined the room: {data}")
```

**Parameters:**

| Parameter   | Type             | Description                |
| ----------- | ---------------- | -------------------------- |
| `transport` | `DailyTransport` | The transport instance     |
| `data`      | `dict`           | Join event data from Daily |

#### on\_connected

Fired when the bot connects to the Daily room. This is an alias for `on_joined` that provides a consistent event name across all transport types.

```python theme={null}
@transport.event_handler("on_connected")
async def on_connected(transport, data):
    print(f"Bot connected to the room: {data}")
```

**Parameters:**

| Parameter   | Type             | Description                |
| ----------- | ---------------- | -------------------------- |
| `transport` | `DailyTransport` | The transport instance     |
| `data`      | `dict`           | Join event data from Daily |

#### on\_left

Fired when the bot leaves the Daily room.

```python theme={null}
@transport.event_handler("on_left")
async def on_left(transport):
    print("Bot left the room")
```

**Parameters:**

| Parameter   | Type             | Description            |
| ----------- | ---------------- | ---------------------- |
| `transport` | `DailyTransport` | The transport instance |

#### on\_before\_leave

Fired synchronously just before the bot leaves the room. Use this for cleanup that must happen before disconnection, such as stopping transcription.

```python theme={null}
@transport.event_handler("on_before_leave")
async def on_before_leave(transport):
    print("About to leave the room...")
```

**Parameters:**

| Parameter   | Type             | Description            |
| ----------- | ---------------- | ---------------------- |
| `transport` | `DailyTransport` | The transport instance |

<Note>
  This is a **synchronous** event — the bot will not leave the room until all
  handlers complete. Keep handlers fast.
</Note>

#### on\_error

Fired when a transport-level error occurs.

```python theme={null}
@transport.event_handler("on_error")
async def on_error(transport, error):
    print(f"Transport error: {error}")
```

**Parameters:**

| Parameter   | Type             | Description            |
| ----------- | ---------------- | ---------------------- |
| `transport` | `DailyTransport` | The transport instance |
| `error`     | `str`            | Error message          |

#### on\_call\_state\_updated

Fired when the call state changes (e.g., joining, joined, leaving, left).

```python theme={null}
@transport.event_handler("on_call_state_updated")
async def on_call_state_updated(transport, state):
    print(f"Call state: {state}")
```

**Parameters:**

| Parameter   | Type             | Description            |
| ----------- | ---------------- | ---------------------- |
| `transport` | `DailyTransport` | The transport instance |
| `state`     | `str`            | The new call state     |

### Participants

#### on\_first\_participant\_joined

Fired when the first participant (other than the bot) joins the room. This is commonly used to start the conversation.

```python theme={null}
@transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant):
    await task.queue_frame(TTSSpeakFrame("Hello! How can I help you today?"))
```

**Parameters:**

| Parameter     | Type             | Description                 |
| ------------- | ---------------- | --------------------------- |
| `transport`   | `DailyTransport` | The transport instance      |
| `participant` | `dict`           | Participant data from Daily |

#### on\_participant\_joined

Fired when any participant joins the room.

```python theme={null}
@transport.event_handler("on_participant_joined")
async def on_participant_joined(transport, participant):
    print(f"Participant joined: {participant['id']}")
```

**Parameters:**

| Parameter     | Type             | Description                 |
| ------------- | ---------------- | --------------------------- |
| `transport`   | `DailyTransport` | The transport instance      |
| `participant` | `dict`           | Participant data from Daily |

<Note>
  When a participant joins, both `on_participant_joined` and
  `on_client_connected` fire. Use `on_first_participant_joined` if you only need
  to react to the first participant.
</Note>

#### on\_participant\_left

Fired when a participant leaves the room.

```python theme={null}
@transport.event_handler("on_participant_left")
async def on_participant_left(transport, participant, reason):
    print(f"Participant {participant['id']} left: {reason}")
```

**Parameters:**

| Parameter     | Type             | Description                 |
| ------------- | ---------------- | --------------------------- |
| `transport`   | `DailyTransport` | The transport instance      |
| `participant` | `dict`           | Participant data from Daily |
| `reason`      | `str`            | Reason the participant left |

#### on\_participant\_updated

Fired when a participant's state changes (e.g., audio/video tracks enabled/disabled).

```python theme={null}
@transport.event_handler("on_participant_updated")
async def on_participant_updated(transport, participant):
    print(f"Participant updated: {participant['id']}")
```

**Parameters:**

| Parameter     | Type             | Description                         |
| ------------- | ---------------- | ----------------------------------- |
| `transport`   | `DailyTransport` | The transport instance              |
| `participant` | `dict`           | Updated participant data from Daily |

#### on\_client\_connected / on\_client\_disconnected

Transport-agnostic aliases that fire alongside `on_participant_joined` and `on_participant_left` respectively, for compatibility with other transports. Same parameters as their counterparts.

```python theme={null}
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, participant):
    print(f"Client connected: {participant['id']}")
```

#### on\_active\_speaker\_changed

Fired when the active speaker in the room changes.

```python theme={null}
@transport.event_handler("on_active_speaker_changed")
async def on_active_speaker_changed(transport, participant):
    print(f"Active speaker: {participant['id']}")
```

**Parameters:**

| Parameter     | Type             | Description                     |
| ------------- | ---------------- | ------------------------------- |
| `transport`   | `DailyTransport` | The transport instance          |
| `participant` | `dict`           | Active speaker participant data |

### Messaging

#### on\_app\_message

Fired when an app message is received from a participant.

```python theme={null}
@transport.event_handler("on_app_message")
async def on_app_message(transport, message, sender):
    print(f"Message from {sender}: {message}")
```

**Parameters:**

| Parameter   | Type             | Description                 |
| ----------- | ---------------- | --------------------------- |
| `transport` | `DailyTransport` | The transport instance      |
| `message`   | `Any`            | The message content         |
| `sender`    | `str`            | The sender's participant ID |

#### on\_transcription\_message

Fired when a transcription message is received from Daily's built-in transcription service.

```python theme={null}
@transport.event_handler("on_transcription_message")
async def on_transcription_message(transport, message):
    print(f"Transcription: {message['text']}")
```

**Parameters:**

| Parameter   | Type             | Description                                                                               |
| ----------- | ---------------- | ----------------------------------------------------------------------------------------- |
| `transport` | `DailyTransport` | The transport instance                                                                    |
| `message`   | `dict`           | Transcription message with `text`, `participantId`, `timestamp`, and `rawResponse` fields |

### Recording

#### on\_recording\_started / on\_recording\_stopped / on\_recording\_error

Events for monitoring Daily's built-in recording feature.

```python theme={null}
@transport.event_handler("on_recording_started")
async def on_recording_started(transport, status):
    print(f"Recording started: {status}")

@transport.event_handler("on_recording_error")
async def on_recording_error(transport, stream_id, message):
    print(f"Recording error for {stream_id}: {message}")
```

**Parameters:**

| Event                  | Parameters                                      |
| ---------------------- | ----------------------------------------------- |
| `on_recording_started` | `transport`, `status` (str)                     |
| `on_recording_stopped` | `transport`, `stream_id` (str)                  |
| `on_recording_error`   | `transport`, `stream_id` (str), `message` (str) |

### Telephony: Dial-in

Events for monitoring incoming phone calls. See the [telephony guides](/pipecat/telephony/overview) for setup details.

#### on\_dialin\_ready

Fired when the dial-in SIP endpoint is ready to receive calls. If `dialin_settings` are configured, Pipecat automatically calls the Daily `pinlessCallUpdate` API.

```python theme={null}
@transport.event_handler("on_dialin_ready")
async def on_dialin_ready(transport, sip_endpoint):
    print(f"Dial-in ready at: {sip_endpoint}")
```

**Parameters:**

| Parameter      | Type             | Description            |
| -------------- | ---------------- | ---------------------- |
| `transport`    | `DailyTransport` | The transport instance |
| `sip_endpoint` | `str`            | The SIP endpoint URI   |

#### on\_dialin\_connected / on\_dialin\_stopped / on\_dialin\_error / on\_dialin\_warning

Lifecycle events for dial-in calls. All receive `(transport, data)` where `data` is a `dict` with event details.

```python theme={null}
@transport.event_handler("on_dialin_connected")
async def on_dialin_connected(transport, data):
    print(f"Dial-in connected: {data}")

@transport.event_handler("on_dialin_error")
async def on_dialin_error(transport, data):
    print(f"Dial-in error: {data}")
```

### Telephony: Dial-out

Events for monitoring outgoing phone calls. All receive `(transport, data)` where `data` is a `dict` with event details.

| Event                  | Description                |
| ---------------------- | -------------------------- |
| `on_dialout_answered`  | Dial-out call was answered |
| `on_dialout_connected` | Dial-out call connected    |
| `on_dialout_stopped`   | Dial-out call stopped      |
| `on_dialout_error`     | Dial-out error occurred    |
| `on_dialout_warning`   | Dial-out warning           |

```python theme={null}
@transport.event_handler("on_dialout_answered")
async def on_dialout_answered(transport, data):
    print(f"Dial-out answered: {data}")

@transport.event_handler("on_dialout_error")
async def on_dialout_error(transport, data):
    print(f"Dial-out error: {data}")
```

### Telephony: DTMF

#### on\_dtmf\_event

Fired when a DTMF (dual-tone multi-frequency) keypad tone is received from a phone caller. The transport automatically pushes an `InputDTMFFrame` into the pipeline, enabling your bot to react to keypad presses.

```python theme={null}
@transport.event_handler("on_dtmf_event")
async def on_dtmf_event(transport, data):
    tone = data["tone"]
    print(f"Received DTMF tone: {tone}")
```

**Parameters:**

| Parameter   | Type             | Description                                           |
| ----------- | ---------------- | ----------------------------------------------------- |
| `transport` | `DailyTransport` | The transport instance                                |
| `data`      | `dict`           | DTMF event data from Daily containing `tone` (string) |

<Note>
  DTMF tones are automatically converted to `InputDTMFFrame` and pushed into the
  pipeline. You can handle these frames in your processors to implement IVR
  menus or other telephony interactions.
</Note>

## Additional Resources

* [Events Overview](/api-reference/server/events/overview) - Overview of all events in Pipecat
* [Daily REST Helper Utility](/api-reference/server/utilities/daily/rest-helper)
* [Pipecat Development Runner's Transport Utilities](/api-reference/server/utilities/runner/transport-utils)
* [Client SDK Integration](/api-reference/client/js/transports/daily)
