---
title: "Configuration"
description: "All Patter constructor parameters for local mode."
icon: "gear"
---

# Configuration

The `Patter` class is the main entry point for the SDK. Pass a carrier instance and a phone number — everything else flows through environment variables unless you override it.

## Minimal config

```python
from getpatter import Patter, Twilio

phone = Patter(carrier=Twilio(), phone_number="+15550001234")   # TWILIO_* from env
```

The carrier instance reads credentials from environment variables when you don't pass them explicitly. See [Carrier](/python-sdk/carrier) for the full list.

## Constructor parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `carrier` | `Twilio \| Telnyx \| None` | `None` | Telephony carrier instance. See [Carrier](/python-sdk/carrier). Reads credentials from env vars when arguments are omitted. |
| `phone_number` | `str` | `""` | Your phone number in E.164 format (e.g., `"+15550001234"`). Required when a carrier is set. |
| `webhook_url` | `str` | `""` | Public hostname of this server, without scheme (e.g., `"abc.ngrok.io"`). See [Tunneling](/dev-tools/tunneling) for ways to get one. |
| `tunnel` | `CloudflareTunnel \| Static \| bool \| None` | `None` | Tunnel directive. `True` is shorthand for `CloudflareTunnel()`. See [Tunneling](/dev-tools/tunneling). |
| `pricing` | `dict \| None` | `None` | Override default provider pricing estimates. See [Metrics & Cost Tracking](/python-sdk/metrics). |
| `persist` | `bool \| str \| None` | `None` (=on, platform default path) | Persist the dashboard's call history to disk so it survives process restarts. Defaults **on** since 0.6.2 — the dashboard's hydrate path requires on-disk records to recover history across restarts. Pass `False` to keep the old ephemeral-RAM-only behaviour. See [Persistent dashboard history](#persistent-dashboard-history) below. |

## Environment variables

Every credential resolves from an env var when the matching argument is omitted. See `.env.example` in the repo for a complete list.

| Env var | Used by |
|---------|---------|
| `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN` | `Twilio()` |
| `TELNYX_API_KEY`, `TELNYX_CONNECTION_ID`, `TELNYX_PUBLIC_KEY` (optional) | `Telnyx()` |
| `OPENAI_API_KEY` | `OpenAIRealtime()`, `OpenAITTS()`, `WhisperSTT()` |
| `ELEVENLABS_API_KEY`, `ELEVENLABS_AGENT_ID` | `ElevenLabsTTS()`, `ElevenLabsConvAI()` |
| `DEEPGRAM_API_KEY` | `DeepgramSTT()` |
| `CARTESIA_API_KEY` | `CartesiaSTT()`, `CartesiaTTS()` |
| `RIME_API_KEY` | `RimeTTS()` |
| `LMNT_API_KEY` | `LMNTTTS()` |
| `SONIOX_API_KEY` | `SonioxSTT()` |
| `SPEECHMATICS_API_KEY` | `SpeechmaticsSTT()` (Python only) |
| `ASSEMBLYAI_API_KEY` | `AssemblyAISTT()` |

### Runtime env vars

These tune SDK runtime behaviour (no credential lookup).

| Env var | Default | Effect |
|---------|---------|--------|
| `PATTER_LOG_DIR` | unset | Persistent dashboard root (see [Persistent dashboard history](#persistent-dashboard-history) below). |
| `PATTER_LOG_RETENTION_DAYS` | `30` | Days of disk history to retain. `0` disables cleanup. |
| `PATTER_LOG_REDACT_PHONE` | `full` | One of `full` (store raw E.164), `mask` (last 4 digits), or `hash_only` (sha256:prefix). Changed from `mask` to `full` on 2026-05-21 so the dashboard's reveal toggle has something to reconstruct from. |
| `PATTER_DASHBOARD_NOTIFY` | enabled | Set to `0`, `false`, `no`, or `off` (case-insensitive) to skip the fire-and-forget dashboard ingest POST. Use this when you embed Patter alongside your own FastAPI server on port 8000 to avoid 404 spam in your access log. |
| `PATTER_BIND_HOST` | `127.0.0.1` | Host the embedded server binds to. Set to `0.0.0.0` when running inside a container whose port must be reachable from the host (e.g. `docker run -p 8000:8000` — Docker's port-mapping cannot forward to a 127.0.0.1 listener inside the container). |

<Note>
`PATTER_BIND_HOST` defaults to loopback on purpose — exposing the embedded server on `0.0.0.0` outside a containerised deployment puts the dashboard and webhook routes on every interface. Pair `PATTER_BIND_HOST=0.0.0.0` with the `dashboard_token` argument on `serve()` (or skip the dashboard) before going public.
</Note>

<Note>
`PATTER_DASHBOARD_NOTIFY` only gates the standalone-dashboard ingest webhook. It does not disable the embedded dashboard you can serve from `phone.serve(..., dashboard=True)` — that route runs in-process and is unaffected.
</Note>

## Examples

### OpenAI Realtime on Twilio

```python
from getpatter import Patter, Twilio, OpenAIRealtime

phone = Patter(carrier=Twilio(), phone_number="+15550001234")

agent = phone.agent(
    engine=OpenAIRealtime(voice="nova"),
    system_prompt="You are a friendly receptionist.",
    first_message="Thanks for calling!",
)
```

### OpenAI Realtime on Telnyx

```python
from getpatter import Patter, Telnyx, OpenAIRealtime

phone = Patter(carrier=Telnyx(), phone_number="+15550001234")

agent = phone.agent(
    engine=OpenAIRealtime(),
    system_prompt="You are a friendly receptionist.",
)
```

### Pipeline mode

```python
from getpatter import Patter, Twilio, DeepgramSTT, ElevenLabsTTS

phone = Patter(carrier=Twilio(), phone_number="+15550001234")

agent = phone.agent(
    stt=DeepgramSTT(endpointing_ms=80),
    tts=ElevenLabsTTS(voice_id="rachel"),
    system_prompt="You are a helpful assistant.",
)
```

## Validation

Local mode enforces the following when telephony credentials are present:

- `phone_number` is required
- Both `account_sid` and `auth_token` are required when using Twilio (either via `Twilio(...)` kwargs or `TWILIO_*` env vars)

```python
# Raises ValueError — Twilio credentials missing from env and arguments.
phone = Patter(carrier=Twilio())
# ValueError: Twilio carrier requires account_sid and auth_token.
```

## Persistent dashboard history

By default the dashboard is an in-memory ring buffer — restart the process and the call list is empty. Pass `persist=` to keep per-call records (`metadata.json`, `transcript.jsonl`, `events.jsonl`) on disk and rebuild the dashboard on the next startup. No external database required.

| `persist` value | Behaviour |
|-----------------|-----------|
| omitted / `None` (default) | Reads `PATTER_LOG_DIR` if set; **otherwise falls back to the platform default location**. Persistence is **on by default** since 0.6.2 so the dashboard's hydrate path recovers history across process restarts. |
| `False` | Force-off. Disk writes are skipped even when `PATTER_LOG_DIR` is set. Use this for ephemeral RAM-only behaviour. |
| `True` | Write under the platform default location (see below). Equivalent to `PATTER_LOG_DIR=auto`. |
| `"<path>"` (string) | Write under the supplied path (`~` is expanded). Equivalent to `PATTER_LOG_DIR=<path>`. |

When `persist` is set explicitly the env var is ignored. When `persist` is `None`, `PATTER_LOG_DIR` continues to work as a deployment-time override.

### Platform default location

| Platform | Default root |
|----------|--------------|
| macOS    | `~/Library/Application Support/patter` |
| Linux    | `$XDG_DATA_HOME/patter` (falls back to `~/.local/share/patter`) |
| Windows  | `%LOCALAPPDATA%\patter` |

### Simplest opt-in

```python
from getpatter import Patter, Twilio, OpenAIRealtime

phone = Patter(
    carrier=Twilio(),
    phone_number="+15555550100",
    persist=True,                   # platform default location
)

agent = phone.agent(
    engine=OpenAIRealtime(),
    system_prompt="You are a friendly receptionist.",
)
```

### Custom path

```python
phone = Patter(
    carrier=Twilio(),
    phone_number="+15555550100",
    persist="/var/log/patter",      # explicit path; ~ is expanded
)
```

### Env-var override (deployment-time)

Leave `persist` unset in code and let ops decide per environment:

```bash
# Production: keep history under a managed volume
export PATTER_LOG_DIR=/var/log/patter

# Dev: platform default
export PATTER_LOG_DIR=auto

# CI: leave unset → no disk writes
```

```python
phone = Patter(carrier=Twilio(), phone_number="+15555550100")
# `persist` defaults to None → reads PATTER_LOG_DIR
```

### Programmatic hydration on startup

`phone.serve()` calls `MetricsStore.hydrate(log_root)` automatically when persistence is enabled, so the dashboard repopulates from disk before the first call lands. You can call it directly if you build the store yourself:

```python
from getpatter.dashboard.store import MetricsStore

store = MetricsStore()
restored = store.hydrate("/var/log/patter")
print(f"Restored {restored} calls from disk")
```

`hydrate()` is idempotent — `call_id`s already in the store are skipped, and unparseable records are logged at debug level rather than aborting.

### Retention

```bash
# Default: 30 days. Old day-directories are swept on ~2% of calls (no daemon).
export PATTER_LOG_RETENTION_DAYS=30

# Keep forever — opt out of automatic cleanup
export PATTER_LOG_RETENTION_DAYS=0
```

<Warning>
Retention defaults to **30 days**. Phone numbers in `metadata.json` default to **full** (raw E.164) since 0.6.2 so the dashboard's reveal toggle has something to reconstruct from — set `PATTER_LOG_REDACT_PHONE=mask` for setups that ship logs off-host. Gate access to the log root regardless, since `transcript.jsonl` is never redacted and may contain customer PII spoken during the call.
</Warning>

See [Call logging](/python-sdk/call-logging) for the full layout, schema, and reading patterns.

## Loading from .env

```python
import os
from dotenv import load_dotenv
from getpatter import Patter, Twilio, OpenAIRealtime

load_dotenv()

phone = Patter(
    carrier=Twilio(),                                 # reads TWILIO_* from env
    phone_number=os.environ["PHONE_NUMBER"],
    webhook_url=os.environ["WEBHOOK_URL"],
)

agent = phone.agent(
    engine=OpenAIRealtime(),                          # reads OPENAI_API_KEY
    system_prompt="You are a helpful assistant.",
)
```

<Warning>
Never hardcode API keys, tokens, or secrets in your source code. Always use environment variables or a secret manager.
</Warning>
