Pipecat ships a built-in development runner (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.
pipecat.runner.run) that handles the server-side glue most bots need during development: creating Daily rooms, accepting WebRTC offers, terminating telephony WebSockets, and serving a prebuilt UI to talk to your bot. You write the bot; the runner does everything around it.
This page covers the canonical shape: a bot file with a single async entry point, run via pipecat.runner.run.main().
The bot entry point
Every bot is a Python file with an asyncbot() function that takes a RunnerArguments. The runner calls this function once per session, passing in everything the bot needs to connect (room URL, token, WebRTC connection, WebSocket, etc.) as fields on the argument object.
bot.py
runner_args. This is the property that makes the same bot file portable across the development runner, Pipecat Cloud, and most production self-hosting setups.
Choosing a transport
RunnerArguments has transport-specific subclasses; the runner instantiates the right one based on how the session was initiated. A typical multi-transport bot pattern-matches:
transport value in the /start request (webrtc, daily, twilio, telnyx, plivo, or exotel); -t/--transport only restricts the local runner to one transport and sets that default.
For working files demonstrating each transport, see examples/runner-examples/ — the 01- through 04- files step from a single-transport bot through to a factory-driven multi-transport setup.
Installing and running
localhost:7860, serves the prebuilt client UI at /client, and lets clients start sessions through POST /start. Clients can request the transport in that start request; -t/--transport is available when you want to restrict the local runner to one transport.
For the full /start request shape, transport-specific behavior, and CLI flags, see the Development Runner guide.
What the runner does for you
Behinduv run bot.py the runner is doing a fair amount of work depending on the transport the client requests:
- WebRTC (
smallwebrtc) — mounts a prebuilt UI at/client, acceptsPOST /api/offer(with ICE candidates viaPATCH /api/offer), and bridges the resultingSmallWebRTCConnectionto yourbot()function. Also exposes aPOST /startendpoint that returns asessionId, mimicking Pipecat Cloud’s start API. - Daily — calls Daily’s REST API to create a room and issue tokens, then either redirects the browser to the room (for the
GET /flow) or returns the room URL + token viaPOST /start. - Telephony — returns the carrier-specific XML stub (TwiML for Twilio, the equivalent for Telnyx/Plivo/Exotel) on
POST /, then accepts the bidirectional media WebSocket at/wsand hands it to your bot wrapped in aWebSocketRunnerArguments. - Daily PSTN dial-in (
--dialin) — handles Daily’s pinless dial-in webhook, creates a SIP-enabled room, and dispatches the bot with full dial-in context.
POST /start and /sessions/{id}/... endpoints exposed in WebRTC and Daily modes are deliberately shaped the same way as Pipecat Cloud’s session API. That means a client built against the development runner works against PCC unchanged, and a custom production dispatcher can offer the same contract if you want clients to remain portable.
Adding your own routes
The runner exports its FastAPI app as a module-level attribute, so you can add custom routes before callingmain():
Containerizing for deployment
A minimal Dockerfile for a bot file is unsurprising:dailyco/pipecat-base) is what most of the examples build from. It’s also a reasonable starting point for self-hosted deployments — it provides a sensible runtime environment for a bot file, and you can use it without using PCC.