Skip to main content
The CallClient class is the primary interface for joining calls, managing media, and controlling call features.

Constructor

Creates a new CallClient instance.
from daily import CallClient, EventHandler

class MyEventHandler(EventHandler):
    def on_participant_joined(self, participant):
        print(f"Participant joined: {participant['id']}")

client = CallClient(event_handler=MyEventHandler())

Parameters

event_handler
Optional[EventHandler]
default:"None"
An optional event handler instance to receive call events. See EventHandler for details.

Returns

A new CallClient instance.

Connection Management

join()

Joins a Daily room.
client.join(
    meeting_url="https://your-domain.daily.co/room-name",
    meeting_token="your-meeting-token",
    client_settings={
        "inputs": {
            "camera": {"isEnabled": False},
            "microphone": {"isEnabled": True}
        }
    },
    completion=lambda data, error: print(f"Joined: {data}")
)

Parameters

meeting_url
str
required
The Daily room URL to join.
meeting_token
Optional[str]
default:"None"
Optional meeting token for authentication and permissions.
client_settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary of client settings (inputs, publishing, subscription profiles, etc.).
completion
Optional[Callable[[Optional[Mapping[str, Any]], Optional[str]], None]]
default:"None"
Optional callback invoked when join completes. Receives (data, error) where data contains join information on success, or error contains an error message on failure.

Returns

This method does not return a value. Use the completion callback for async results.

leave()

Leaves the current Daily room.
client.leave(
    completion=lambda error: print("Left call" if not error else f"Error: {error}")
)

Parameters

completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when leave completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

release()

Releases all resources associated with the CallClient. Should be called when you’re done with the client.
client.release()

Returns

This method does not return a value.

Participant Management

participants()

Gets the current participants in the call.
participants = client.participants()
for participant_id, participant_info in participants.items():
    print(f"{participant_id}: {participant_info['info']['userName']}")

Returns

A dictionary mapping participant IDs to participant information objects.

participant_counts()

Gets the current participant counts.
counts = client.participant_counts()
print(f"Present: {counts['present']}, Hidden: {counts['hidden']}")

Returns

A dictionary containing participant count information (present, hidden, etc.).

active_speaker()

Gets information about the current active speaker.
speaker = client.active_speaker()
if speaker:
    print(f"Active speaker: {speaker['participantId']}")

Returns

A dictionary containing active speaker information.

set_user_name()

Sets the user name for the local participant.
client.set_user_name("Bot User")

Parameters

user_name
str
required
The display name for the local participant.

Returns

This method does not return a value.

update_remote_participants()

Updates settings for remote participants.
client.update_remote_participants(
    remote_participants={
        "participant-id": {
            "setAudio": False,
            "setVideo": False
        }
    },
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

remote_participants
Mapping[str, Any]
required
Dictionary mapping participant IDs to their settings updates.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when update completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

eject_remote_participants()

Ejects one or more participants from the call.
client.eject_remote_participants(
    ids=["participant-id-1", "participant-id-2"],
    completion=lambda error: print("Ejected" if not error else f"Error: {error}")
)

Parameters

ids
List[str]
required
List of participant IDs to eject from the call.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when eject completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Media Input Management

inputs()

Gets the current input settings (camera, microphone).
inputs = client.inputs()
print(f"Camera enabled: {inputs['camera']['isEnabled']}")
print(f"Microphone enabled: {inputs['microphone']['isEnabled']}")

Returns

A dictionary containing current input settings.

update_inputs()

Updates input settings for camera and microphone.
client.update_inputs(
    input_settings={
        "camera": {
            "isEnabled": True,
            "settings": {
                "deviceId": "my-camera"
            }
        },
        "microphone": {
            "isEnabled": True,
            "settings": {
                "deviceId": "my-microphone"
            }
        }
    },
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

input_settings
Mapping[str, Any]
required
Dictionary containing input configuration (camera, microphone settings).
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when update completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Publishing Management

publishing()

Gets the current publishing settings.
publishing = client.publishing()
print(f"Camera publishing: {publishing['camera']['isPublishing']}")
print(f"Microphone publishing: {publishing['microphone']['isPublishing']}")

Returns

A dictionary containing current publishing settings.

update_publishing()

Updates publishing settings for camera, microphone, and custom tracks.
client.update_publishing(
    publishing_settings={
        "camera": {"isPublishing": True},
        "microphone": {"isPublishing": True}
    },
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

publishing_settings
Mapping[str, Any]
required
Dictionary containing publishing configuration.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when update completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Subscription Management

subscriptions()

Gets the current subscription settings.
subscriptions = client.subscriptions()
for participant_id, settings in subscriptions.items():
    print(f"{participant_id}: {settings}")

Returns

A dictionary containing current subscription settings for participants.

update_subscriptions()

Updates subscription settings for specific participants or using profiles.
client.update_subscriptions(
    participant_settings={
        "participant-id": {
            "media": "subscribed"
        }
    },
    profile_settings={
        "base": {
            "camera": "subscribed",
            "microphone": "subscribed"
        }
    },
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

participant_settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary mapping participant IDs to subscription settings.
profile_settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary of subscription profile settings.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when update completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

subscription_profiles()

Gets the current subscription profiles.
profiles = client.subscription_profiles()
print(f"Profiles: {profiles}")

Returns

A dictionary containing subscription profile configurations.

update_subscription_profiles()

Updates subscription profile settings.
client.update_subscription_profiles(
    profile_settings={
        "high-quality": {
            "camera": {
                "subscriptionState": "subscribed",
                "settings": {"maxQuality": "high"}
            }
        }
    },
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

profile_settings
Mapping[str, Any]
required
Dictionary containing subscription profile configurations.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when update completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Permissions

update_permissions()

Updates permissions for participants in the call.
client.update_permissions(
    permissions={
        "hasPresence": True,
        "canSend": ["camera", "microphone"],
        "canAdmin": False
    },
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

permissions
Mapping[str, Any]
required
Dictionary containing permission settings.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when update completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Custom Audio Tracks

add_custom_audio_track()

Adds a custom audio track to the call.
from daily import CustomAudioSource, CustomAudioTrack

audio_source = CustomAudioSource(sample_rate=16000, channels=1)
audio_track = CustomAudioTrack(audio_source)

client.add_custom_audio_track(
    track_name="music-track",
    audio_track=audio_track,
    ignore_audio_level=False,
    completion=lambda error: print("Added" if not error else f"Error: {error}")
)

Parameters

track_name
str
required
Name identifier for the custom audio track.
audio_track
CustomAudioTrack
required
The custom audio track instance to add.
ignore_audio_level
Optional[bool]
default:"None"
If True, audio level detection is disabled for this track.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

update_custom_audio_track()

Updates an existing custom audio track.
client.update_custom_audio_track(
    track_name="music-track",
    audio_track=new_audio_track,
    ignore_audio_level=True,
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

track_name
str
required
Name identifier of the custom audio track to update.
audio_track
CustomAudioTrack
required
The new custom audio track instance.
ignore_audio_level
Optional[bool]
default:"None"
If True, audio level detection is disabled for this track.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

remove_custom_audio_track()

Removes a custom audio track from the call.
client.remove_custom_audio_track(
    track_name="music-track",
    completion=lambda error: print("Removed" if not error else f"Error: {error}")
)

Parameters

track_name
str
required
Name identifier of the custom audio track to remove.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Recording

start_recording()

Starts recording the call.
client.start_recording(
    streaming_settings={
        "width": 1280,
        "height": 720,
        "fps": 30
    },
    stream_id="my-recording",
    force_new=False,
    completion=lambda stream_id, error: print(f"Recording started: {stream_id}" if not error else f"Error: {error}")
)

Parameters

streaming_settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing recording configuration (width, height, fps, etc.).
stream_id
Optional[str]
default:"None"
Optional identifier for the recording stream.
force_new
Optional[bool]
default:"None"
If True, forces creation of a new recording even if one exists.
completion
Optional[Callable[[str, Optional[str]], None]]
default:"None"
Optional callback invoked when recording starts. Receives (stream_id, error).

Returns

This method does not return a value. Use the completion callback for async results.

stop_recording()

Stops an active recording.
client.stop_recording(
    stream_id="my-recording",
    completion=lambda error: print("Stopped" if not error else f"Error: {error}")
)

Parameters

stream_id
Optional[str]
default:"None"
Optional identifier of the recording stream to stop. If not provided, stops the default recording.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

update_recording()

Updates settings for an active recording.
client.update_recording(
    update_settings={
        "layout": {"preset": "speaker-active"}
    },
    stream_id="my-recording",
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

update_settings
Mapping[str, Any]
required
Dictionary containing recording settings to update.
stream_id
Optional[str]
default:"None"
Optional identifier of the recording stream to update.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Live Streaming

start_live_stream_with_endpoints()

Starts live streaming to specified endpoints.
client.start_live_stream_with_endpoints(
    endpoints=["https://example.com/stream"],
    streaming_settings={
        "width": 1920,
        "height": 1080,
        "fps": 30
    },
    stream_id="my-stream",
    force_new=False,
    completion=lambda error: print("Started" if not error else f"Error: {error}")
)

Parameters

endpoints
Optional[List[str]]
required
List of streaming endpoint URLs.
streaming_settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing streaming configuration.
stream_id
Optional[str]
default:"None"
Optional identifier for the stream.
force_new
Optional[bool]
default:"None"
If True, forces creation of a new stream even if one exists.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

start_live_stream_with_rtmp_urls()

Starts live streaming to RTMP URLs.
client.start_live_stream_with_rtmp_urls(
    rtmp_urls=["rtmp://a.rtmp.youtube.com/live2/your-stream-key"],
    streaming_settings={
        "width": 1920,
        "height": 1080,
        "fps": 30
    },
    stream_id="youtube-stream",
    force_new=False,
    completion=lambda error: print("Started" if not error else f"Error: {error}")
)

Parameters

rtmp_urls
Optional[List[str]]
required
List of RTMP streaming URLs.
streaming_settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing streaming configuration.
stream_id
Optional[str]
default:"None"
Optional identifier for the stream.
force_new
Optional[bool]
default:"None"
If True, forces creation of a new stream even if one exists.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

stop_live_stream()

Stops an active live stream.
client.stop_live_stream(
    stream_id="my-stream",
    completion=lambda error: print("Stopped" if not error else f"Error: {error}")
)

Parameters

stream_id
Optional[str]
default:"None"
Optional identifier of the stream to stop.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

update_live_stream()

Updates settings for an active live stream.
client.update_live_stream(
    update_settings={
        "layout": {"preset": "grid"}
    },
    stream_id="my-stream",
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

update_settings
Mapping[str, Any]
required
Dictionary containing streaming settings to update.
stream_id
Optional[str]
default:"None"
Optional identifier of the stream to update.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

add_live_streaming_endpoings()

Adds endpoints to an active live stream.
client.add_live_streaming_endpoings(
    endpoints=["https://example.com/new-endpoint"],
    stream_id="my-stream",
    completion=lambda error: print("Added" if not error else f"Error: {error}")
)

Parameters

endpoints
List[str]
required
List of streaming endpoint URLs to add.
stream_id
Optional[str]
default:"None"
Optional identifier of the stream.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

remove_live_streaming_endpoings()

Removes endpoints from an active live stream.
client.remove_live_streaming_endpoings(
    endpoints=["https://example.com/endpoint-to-remove"],
    stream_id="my-stream",
    completion=lambda error: print("Removed" if not error else f"Error: {error}")
)

Parameters

endpoints
List[str]
required
List of streaming endpoint URLs to remove.
stream_id
Optional[str]
default:"None"
Optional identifier of the stream.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Transcription

start_transcription()

Starts real-time transcription of the call.
client.start_transcription(
    settings={
        "language": "en",
        "model": "nova-2",
        "profanity_filter": True
    },
    completion=lambda error: print("Started" if not error else f"Error: {error}")
)

Parameters

settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing transcription configuration.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

stop_transcription()

Stops active transcription.
client.stop_transcription(
    completion=lambda error: print("Stopped" if not error else f"Error: {error}")
)

Parameters

completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

update_transcription()

Updates transcription settings.
client.update_transcription(
    participants=["participant-id-1", "participant-id-2"],
    instance_id="transcription-instance",
    completion=lambda error: print("Updated" if not error else f"Error: {error}")
)

Parameters

participants
Optional[List[str]]
default:"None"
Optional list of participant IDs to include in transcription.
instance_id
Optional[str]
default:"None"
Optional transcription instance identifier.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

SIP and Dialout

start_dialout()

Starts a SIP dialout to connect an external phone number.
client.start_dialout(
    settings={
        "phoneNumber": "+1234567890",
        "callerId": "+0987654321"
    },
    completion=lambda participant_id, error: print(f"Dialout started: {participant_id}" if not error else f"Error: {error}")
)

Parameters

settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing dialout configuration (phone number, caller ID, etc.).
completion
Optional[Callable[[str, Optional[str]], None]]
default:"None"
Optional callback invoked when dialout starts. Receives (participant_id, error).

Returns

This method does not return a value. Use the completion callback for async results.

stop_dialout()

Stops an active SIP dialout connection.
client.stop_dialout(
    participant_id="sip-participant-id",
    completion=lambda error: print("Stopped" if not error else f"Error: {error}")
)

Parameters

participant_id
Optional[str]
default:"None"
Optional participant ID of the SIP connection to stop.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

send_dtmf()

Sends DTMF tones during a SIP call.
client.send_dtmf(
    settings={
        "participantId": "sip-participant-id",
        "tones": "1234#"
    },
    completion=lambda error: print("Sent" if not error else f"Error: {error}")
)

Parameters

settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing DTMF settings (participant ID, tones).
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

sip_call_transfer()

Transfers a SIP call to another number.
client.sip_call_transfer(
    settings={
        "participantId": "sip-participant-id",
        "transferTo": "+1234567890"
    },
    completion=lambda error: print("Transferred" if not error else f"Error: {error}")
)

Parameters

settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing transfer settings.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

sip_refer()

Sends a SIP REFER request.
client.sip_refer(
    settings={
        "participantId": "sip-participant-id",
        "referTo": "sip:user@example.com"
    },
    completion=lambda error: print("Sent" if not error else f"Error: {error}")
)

Parameters

settings
Optional[Mapping[str, Any]]
default:"None"
Optional dictionary containing REFER settings.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Messaging

send_app_message()

Sends an application message to participants.
client.send_app_message(
    message={"type": "custom", "data": "hello"},
    participant_id="specific-participant",
    serialize_none=True,
    completion=lambda error: print("Sent" if not error else f"Error: {error}")
)

Parameters

message
Any
required
The message payload to send (can be any JSON-serializable object).
participant_id
Optional[str]
default:"None"
Optional participant ID. If not provided, message is sent to all participants.
serialize_none
bool
default:"True"
If True, serializes None values in the message.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

send_prebuilt_chat_message()

Sends a chat message in Daily Prebuilt format.
client.send_prebuilt_chat_message(
    message="Hello everyone!",
    user_name="Bot User",
    completion=lambda error: print("Sent" if not error else f"Error: {error}")
)

Parameters

message
str
required
The chat message text to send.
user_name
Optional[str]
default:"None"
Optional user name to display with the message.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Media Renderers

set_audio_renderer()

Sets a callback to receive raw audio data from a participant.
def audio_callback(participant_id: str, audio_data: AudioData, source: str):
    print(f"Received {audio_data.num_audio_frames} frames from {participant_id}")
    # Process audio_data.audio_frames (bytes)

client.set_audio_renderer(
    participant_id="participant-id",
    callback=audio_callback,
    audio_source="microphone",
    sample_rate=16000,
    callback_interval_ms=20
)

Parameters

participant_id
str
required
The participant ID to receive audio from.
callback
Callable[[str, AudioData, str], None]
required
Callback function that receives (participant_id, audio_data, audio_source).
audio_source
str
default:"microphone"
Audio source to receive (“microphone” or custom track name).
sample_rate
int
default:"16000"
Desired sample rate in Hz for the audio data.
callback_interval_ms
int
default:"20"
Interval in milliseconds between callback invocations.

Returns

This method does not return a value.

set_video_renderer()

Sets a callback to receive raw video frames from a participant.
def video_callback(participant_id: str, video_frame: VideoFrame, source: str):
    print(f"Received frame {video_frame.width}x{video_frame.height} from {participant_id}")
    # Process video_frame.buffer (bytes)

client.set_video_renderer(
    participant_id="participant-id",
    callback=video_callback,
    video_source="camera",
    color_format="RGBA"
)

Parameters

participant_id
str
required
The participant ID to receive video from.
callback
Callable[[str, VideoFrame, str], None]
required
Callback function that receives (participant_id, video_frame, video_source).
video_source
str
default:"camera"
Video source to receive (“camera” or “screenVideo”).
color_format
str
default:"RGBA"
Desired color format for video frames (“RGBA”, “RGB”, “BGRA”, “BGR”).

Returns

This method does not return a value.

Network Configuration

get_network_stats()

Gets current network statistics.
stats = client.get_network_stats()
print(f"Bitrate: {stats['stats']['latest']['videoSendBitsPerSecond']}")

Returns

A dictionary containing network statistics.

set_proxy_url()

Configures a proxy server for network connections.
client.set_proxy_url(
    proxy_url="http://proxy.example.com:8080",
    completion=lambda error: print("Set" if not error else f"Error: {error}")
)

Parameters

proxy_url
Optional[str]
default:"None"
Proxy server URL, or None to clear proxy settings.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

set_ice_config()

Configures ICE (Interactive Connectivity Establishment) settings.
client.set_ice_config(
    ice_config={
        "iceServers": [
            {"urls": "stun:stun.l.google.com:19302"},
            {"urls": "turn:turn.example.com:3478", "username": "user", "credential": "pass"}
        ]
    },
    completion=lambda error: print("Set" if not error else f"Error: {error}")
)

Parameters

ice_config
Optional[Mapping[str, Any]]
default:"None"
ICE configuration dictionary, or None to use default settings.
completion
Optional[Callable[[Optional[str]], None]]
default:"None"
Optional callback invoked when operation completes. Receives an optional error message.

Returns

This method does not return a value. Use the completion callback for async results.

Complete Example

from daily import Daily, CallClient, EventHandler, LogLevel
import time

class MyEventHandler(EventHandler):
    def on_participant_joined(self, participant):
        print(f"Participant joined: {participant['info']['userName']}")
    
    def on_participant_left(self, participant, reason):
        print(f"Participant left: {participant['info']['userName']} ({reason})")
    
    def on_call_state_updated(self, state):
        print(f"Call state: {state}")

# Initialize SDK
Daily.init(worker_threads=2, log_level=LogLevel.Info)

# Create virtual devices
mic = Daily.create_microphone_device("bot-mic", sample_rate=16000)
camera = Daily.create_camera_device("bot-camera", width=1280, height=720)

# Create client with event handler
client = CallClient(event_handler=MyEventHandler())

# Configure inputs
client.update_inputs({
    "microphone": {"isEnabled": True, "settings": {"deviceId": "bot-mic"}},
    "camera": {"isEnabled": True, "settings": {"deviceId": "bot-camera"}}
})

# Join call
def on_joined(data, error):
    if error:
        print(f"Join error: {error}")
    else:
        print(f"Joined as {data['participants']['local']['info']['userName']}")

client.join(
    meeting_url="https://your-domain.daily.co/room-name",
    client_settings={"userName": "My Bot"},
    completion=on_joined
)

# Keep the bot running
try:
    while True:
        # Write media to virtual devices
        # mic.write_frames(audio_data)
        # camera.write_frame(video_data)
        time.sleep(0.1)
except KeyboardInterrupt:
    pass

# Clean up
client.leave()
client.release()
Daily.deinit()

Build docs developers (and LLMs) love