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

# ConnectorClient

> Sync SSE client. Callbacks run on a dedicated worker thread — blocking I/O is safe.

`ConnectorClient` is for synchronous pipelines (no `async/await`). The SSE stream reader
and your callback run on **separate threads**, so slow calls (vector store writes, embedding
APIs, databases) never stall the stream.

For async pipelines, use [AsyncConnectorClient](/sdk/async-client) instead.

***

## Constructor

```python theme={null}
from autoplay_sdk import ConnectorClient

client = ConnectorClient(
    url="https://your-connector.onrender.com/stream/YOUR_PRODUCT_ID",
    token="uk_live_...",
    max_queue_size=500,
)
```

<ParamField path="url" type="string" required>
  Full URL to `GET /stream/{product_id}` on the connector.
</ParamField>

<ParamField path="token" type="string" default="&#x22;&#x22;">
  Unkey API key (`uk_live_...`). Leave empty to connect without authentication.
</ParamField>

<ParamField path="max_queue_size" type="int" default="500">
  Maximum events buffered between the stream reader and worker threads.
  When full, new events are dropped and counted in `dropped_count`.
</ParamField>

***

## Callbacks

All registration methods return `self` for chaining.

### `.on_actions(fn)`

```python theme={null}
from autoplay_sdk import ActionsPayload

def handle_actions(payload: ActionsPayload):
    text = payload.to_text()
    # embed, store, process — blocking I/O is safe here

client.on_actions(handle_actions)
```

<ParamField path="fn" type="Callable[[ActionsPayload], None]" required>
  Called on a worker thread every time a batch of UI actions arrives.
</ParamField>

### `.on_summary(fn)`

```python theme={null}
from autoplay_sdk import SummaryPayload

def handle_summary(payload: SummaryPayload):
    text = payload.to_text()

client.on_summary(handle_summary)
```

<ParamField path="fn" type="Callable[[SummaryPayload], None]" required>
  Called on a worker thread every time a session summary arrives.
</ParamField>

### `.on_drop(fn)`

Called when events are dropped because the queue is full (backpressure).

```python theme={null}
def handle_drop(payload, total_dropped):
    print(f"Dropped — total: {total_dropped}")

client.on_drop(handle_drop)
```

<ParamField path="fn" type="Callable[[dict, int], None]" required>
  Called with the raw dropped payload and the running total of dropped events.
</ParamField>

***

## Lifecycle

### `.run()`

Connect and block until stopped. Reconnects with exponential backoff (1 s → 30 s).

```python theme={null}
client.on_actions(handle_actions).run()
```

<Warning>
  HTTP 401, 403, and 404 are **not** retried — they raise immediately. Check your `url` and `token`.
</Warning>

### `.run_in_background()`

Start on a daemon thread and return immediately.

```python theme={null}
thread = client.run_in_background()
do_other_work()
client.stop()
```

Returns a `threading.Thread`.

### `.stop()`

Signal the client to stop after the current event.

***

## Observability

```python theme={null}
client.dropped_count   # int — total events dropped (queue full)
client.queue_size      # int — events waiting in the queue right now
```

A non-zero `dropped_count` means your callback is slower than the event rate.
Increase `max_queue_size` or optimise the callback.

***

## Context manager

```python theme={null}
with ConnectorClient(url=URL, token=TOKEN) as client:
    client.on_actions(handle_actions).run_in_background()
    do_other_work()
# stop() called automatically
```

***

## Chained setup

```python theme={null}
ConnectorClient(url=URL, token=TOKEN) \
    .on_actions(handle_actions) \
    .on_summary(handle_summary) \
    .on_drop(handle_drop) \
    .run()
```
