> ## 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.

# Proxy Agents

> WebSocket proxy agents for distributed deployments

<Note>Requires the websocket extra: `uv add "pipecat-ai[websocket]"`</Note>

## WebSocketProxyClient

Forwards bus messages to a remote agent over WebSocket. Connects to a WebSocket URL and forwards messages between a local agent and a remote agent.

The proxy does not take a `bus` in its constructor. It receives the shared bus when registered with the runner via `add_workers()`.

```python theme={null}
from pipecat.workers.proxy.websocket.client import WebSocketProxyClient
```

```python theme={null}
proxy = WebSocketProxyClient(
    "proxy",
    url="ws://remote-server:8765/ws",
    remote_worker_name="worker",
    local_worker_name="voice",
)

@proxy.event_handler("on_connected")
async def on_connected(worker, websocket):
    logger.info("Connected to remote server")

@proxy.event_handler("on_disconnected")
async def on_disconnected(worker, websocket):
    logger.info("Disconnected from remote server")

await runner.add_workers(proxy)
```

### Configuration

<ParamField path="name" type="str" required>
  Unique name for this agent.
</ParamField>

<ParamField path="url" type="str" required>
  The WebSocket URL to connect to.
</ParamField>

<ParamField path="remote_worker_name" type="str" required>
  Name of the agent on the remote server. Only messages targeted at this agent
  are forwarded.
</ParamField>

<ParamField path="local_worker_name" type="str" required>
  Name of the local agent that should receive responses. Only inbound messages
  targeted at this agent are accepted.
</ParamField>

<ParamField path="forward_messages" type="tuple[type[BusMessage], ...]" default="()">
  Additional message types to forward from the local agent (e.g.
  `(BusFrameMessage,)` for frame routing). These are forwarded based on source
  agent name only, regardless of target.
</ParamField>

<ParamField path="headers" type="dict[str, str] | None" default="None">
  Optional HTTP headers sent with the WebSocket handshake (e.g. for
  authentication).
</ParamField>

<ParamField path="serializer" type="MessageSerializer | None" default="None">
  Serializer for bus messages. Defaults to
  [`JSONMessageSerializer`](/api-reference/server/bus/serializers#jsonmessageserializer).
</ParamField>

<ParamField path="active" type="bool" default="False">
  Whether the agent starts active. Defaults to `False` because `on_activated`
  opens the WebSocket connection, which is usually a deliberate action triggered
  by an upstream event (e.g. the local client connecting). Pass `True` to
  connect as soon as the agent starts.
</ParamField>

### Event Handlers

| Event             | Arguments             | Description                                        |
| ----------------- | --------------------- | -------------------------------------------------- |
| `on_connected`    | `(worker, websocket)` | Fired when the WebSocket connection is established |
| `on_disconnected` | `(worker, websocket)` | Fired when the WebSocket connection is closed      |

## WebSocketProxyServer

Receives bus messages from a remote client over WebSocket. Accepts a FastAPI/Starlette WebSocket connection and forwards messages between the remote client and a local agent.

Like the client, the server does not take a `bus` in its constructor; it receives the shared bus when registered with the runner via `add_workers()`.

```python theme={null}
from pipecat.workers.proxy.websocket.server import WebSocketProxyServer
```

```python theme={null}
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    proxy = WebSocketProxyServer(
        "gateway",
        websocket=websocket,
        worker_name="worker",
        remote_worker_name="voice",
    )

    @proxy.event_handler("on_client_connected")
    async def on_client_connected(worker, websocket):
        logger.info("Client connected")

    @proxy.event_handler("on_client_disconnected")
    async def on_client_disconnected(worker, websocket):
        logger.info("Client disconnected")

    await runner.add_workers(proxy)
```

### Configuration

<ParamField path="name" type="str" required>
  Unique name for this agent.
</ParamField>

<ParamField path="websocket" type="WebSocket" required>
  An accepted FastAPI/Starlette WebSocket connection.
</ParamField>

<ParamField path="worker_name" type="str" required>
  Name of the local agent to route messages to/from. Only messages from this
  agent are forwarded to the client.
</ParamField>

<ParamField path="remote_worker_name" type="str" required>
  Name of the agent on the remote client. Only outbound messages targeted at
  this agent are sent. Only inbound messages targeted at the local agent are
  accepted.
</ParamField>

<ParamField path="forward_messages" type="tuple[type[BusMessage], ...]" default="()">
  Additional message types to forward from the local agent (e.g.
  `(BusFrameMessage,)` for frame routing). These are forwarded based on source
  agent name only, regardless of target.
</ParamField>

<ParamField path="serializer" type="MessageSerializer | None" default="None">
  Serializer for bus messages. Defaults to
  [`JSONMessageSerializer`](/api-reference/server/bus/serializers#jsonmessageserializer).
</ParamField>

### Event Handlers

| Event                    | Arguments             | Description                                                     |
| ------------------------ | --------------------- | --------------------------------------------------------------- |
| `on_client_connected`    | `(worker, websocket)` | Fired when the WebSocket client connects and the proxy is ready |
| `on_client_disconnected` | `(worker, websocket)` | Fired when the WebSocket client disconnects                     |
