Skip to main content

API Reference

Complete reference for the patter Python SDK (v0.3.0).

Patter

The main SDK client class.

Constructor

Patter(
    mode: str = "local",
    twilio_sid: str = "",
    twilio_token: str = "",
    telnyx_key: str = "",
    telnyx_connection_id: str = "",
    openai_key: str = "",
    elevenlabs_key: str = "",
    deepgram_key: str = "",
    phone_number: str = "",
    webhook_url: str = "",
    pricing: dict | None = None,
)
ParameterTypeDefaultDescription
modestr"local"Operating mode. Can be omitted — auto-detected when provider keys are given.
twilio_sidstr""Twilio Account SID.
twilio_tokenstr""Twilio Auth Token.
telnyx_keystr""Telnyx API key.
telnyx_connection_idstr""Telnyx Call Control App ID.
openai_keystr""OpenAI API key.
elevenlabs_keystr""ElevenLabs API key.
deepgram_keystr""Deepgram API key.
phone_numberstr""Phone number in E.164 format.
webhook_urlstr""Public hostname (no scheme).
pricingdict | NoneNoneOverride default provider pricing. See Metrics & Cost Tracking.

Methods

call()

async def call(
    to: str,
    on_message: Callable[[IncomingMessage], Awaitable[str]] | None = None,
    first_message: str = "",
    from_number: str = "",
    agent_id: str | None = None,
    agent: Agent | None = None,
    machine_detection: bool = False,
    on_machine: Callable[[dict], Awaitable[None]] | None = None,
    voicemail_message: str = "",
) -> None
Make an outbound call. Raises:
  • PatterConnectionError if agent is not provided.
  • ValueError if to is not in E.164 format.

agent()

def agent(
    system_prompt: str,
    voice: str = "alloy",
    model: str = "gpt-4o-mini-realtime-preview",
    language: str = "en",
    first_message: str = "",
    tools: list[dict] | None = None,
    provider: str = "openai_realtime",
    stt: STTConfig | None = None,
    tts: TTSConfig | None = None,
    variables: dict | None = None,
    guardrails: list | None = None,
) -> Agent
Create an Agent configuration. Validates provider keys in local mode. Raises: ValueError if provider is invalid or required keys are missing.

serve()

async def serve(
    agent: Agent,
    port: int = 8000,
    recording: bool = False,
    on_call_start: Callable[[dict], Awaitable[None]] | None = None,
    on_call_end: Callable[[dict], Awaitable[None]] | None = None,
    on_transcript: Callable[[dict], Awaitable[None]] | None = None,
    on_message: Callable[[dict], Awaitable[str]] | str | None = None,
    on_metrics: Callable[[dict], Awaitable[None]] | None = None,
    voicemail_message: str = "",
    dashboard: bool = True,
    dashboard_token: str = "",
    tunnel: bool = False,
) -> None
Start the embedded server. Blocks until stopped. Raises:
  • TypeError if agent is not an Agent instance.
  • ValueError if port is out of range.

test()

async def test(
    agent: Agent,
    on_message: Callable[[dict], Awaitable[str]] | None = None,
    on_call_start: Callable[[dict], Awaitable[None]] | None = None,
    on_call_end: Callable[[dict], Awaitable[None]] | None = None,
) -> None
Start an interactive terminal test session. Simulates a phone call without telephony, STT, or TTS — pure text input/output. When no on_message handler is provided and an openai_key is configured, the built-in LLM loop is used. See Test Mode for details. Raises:
  • TypeError if agent is not an Agent instance.

disconnect()

async def disconnect() -> None
Disconnect from Patter. Stops the embedded server.

Static Methods

Patter.deepgram()

@staticmethod
def deepgram(api_key: str, language: str = "en") -> STTConfig
Create a Deepgram STT configuration.

Patter.whisper()

@staticmethod
def whisper(api_key: str, language: str = "en") -> STTConfig
Create a Whisper (OpenAI) STT configuration.

Patter.elevenlabs()

@staticmethod
def elevenlabs(api_key: str, voice: str = "rachel") -> TTSConfig
Create an ElevenLabs TTS configuration.

Patter.openai_tts()

@staticmethod
def openai_tts(api_key: str, voice: str = "alloy") -> TTSConfig
Create an OpenAI TTS configuration. Audio is auto-resampled from 24kHz to 16kHz.

Patter.guardrail()

@staticmethod
def guardrail(
    name: str,
    blocked_terms: list[str] | None = None,
    check: Callable[[str], bool] | None = None,
    replacement: str = "I'm sorry, I can't respond to that.",
) -> dict
Create an output guardrail dictionary.

Patter.tool()

@staticmethod
def tool(
    name: str,
    description: str = "",
    parameters: dict | None = None,
    handler: object = None,
    webhook_url: str = "",
) -> dict
Create a tool definition for use with agent(tools=[...]).
ParameterTypeDefaultDescription
namestrrequiredTool name (visible to the LLM).
descriptionstr""What the tool does (visible to the LLM).
parametersdict | NoneNoneJSON Schema for tool arguments.
handlerCallable | NoneNoneAsync or sync callable `(arguments, context) -> strdict`. Called in-process.
webhook_urlstr""URL to POST to when the LLM invokes the tool.
Either handler or webhook_url must be provided. Raises: ValueError if neither handler nor webhook_url is provided.

Data Classes

All data classes are frozen (immutable) dataclasses.

Agent

@dataclass(frozen=True)
class Agent:
    system_prompt: str
    voice: str = "alloy"
    model: str = "gpt-4o-mini-realtime-preview"
    language: str = "en"
    first_message: str = ""
    tools: list[dict] | None = None
    provider: str = "openai_realtime"
    stt: STTConfig | None = None
    tts: TTSConfig | None = None
    variables: dict | None = None
    guardrails: list | None = None
FieldTypeDefaultDescription
system_promptstrrequiredAgent instructions.
voicestr"alloy"TTS voice name.
modelstr"gpt-4o-mini-realtime-preview"AI model ID.
languagestr"en"BCP-47 language code.
first_messagestr""Greeting spoken at call start.
toolslist[dict] | NoneNoneTool definitions for function calling.
providerstr"openai_realtime""openai_realtime", "elevenlabs_convai", or "pipeline".
sttSTTConfig | NoneNoneSTT config for pipeline mode.
ttsTTSConfig | NoneNoneTTS config for pipeline mode.
variablesdict | NoneNoneDynamic variable substitutions for {placeholder} in system_prompt. Values limited to 500 chars.
guardrailslist | NoneNoneOutput guardrails.

CallEvent

@dataclass(frozen=True)
class CallEvent:
    call_id: str
    caller: str = ""
    callee: str = ""
    direction: str = ""
FieldTypeDefaultDescription
call_idstrrequiredUnique call identifier.
callerstr""Caller phone number.
calleestr""Callee phone number.
directionstr"""inbound" or "outbound".

IncomingMessage

@dataclass(frozen=True)
class IncomingMessage:
    text: str
    call_id: str
    caller: str
FieldTypeDescription
textstrThe user’s transcribed message.
call_idstrUnique call identifier.
callerstrCaller phone number.

STTConfig

@dataclass(frozen=True)
class STTConfig:
    provider: str
    api_key: str
    language: str = "en"
FieldTypeDefaultDescription
providerstrrequiredSTT provider name ("deepgram", "whisper").
api_keystrrequiredProvider API key.
languagestr"en"BCP-47 language code.
Methods: to_dict() -> dict

TTSConfig

@dataclass(frozen=True)
class TTSConfig:
    provider: str
    api_key: str
    voice: str = "alloy"
FieldTypeDefaultDescription
providerstrrequiredTTS provider name ("elevenlabs", "openai").
api_keystrrequiredProvider API key.
voicestr"alloy"Voice name or ID.
Methods: to_dict() -> dict

Guardrail

@dataclass(frozen=True)
class Guardrail:
    name: str
    check: object = None
    blocked_terms: list | None = None
    replacement: str = "I'm sorry, I can't respond to that."
FieldTypeDefaultDescription
namestrrequiredGuardrail identifier.
checkCallable[[str], bool] | NoneNoneCustom check function.
blocked_termslist[str] | NoneNoneCase-insensitive blocked terms.
replacementstr"I'm sorry, I can't respond to that."Replacement text when blocked.

CallControl

class CallControl:
    call_id: str
    caller: str
    callee: str
    telephony_provider: str
Passed as the second argument to on_message handlers. Allows dynamic call management during a conversation.
MethodDescription
await transfer(number: str)Transfer the call to another phone number.
await hangup()End the call.
PropertyTypeDescription
is_transferredboolWhether a transfer was requested.
is_hung_upboolWhether a hangup was requested.
endedboolTrue if transferred or hung up.

CallMetrics

@dataclass(frozen=True)
class CallMetrics:
    call_id: str
    duration_seconds: float
    turns: tuple[TurnMetrics, ...]
    cost: CostBreakdown
    latency_avg: LatencyBreakdown
    latency_p95: LatencyBreakdown
    provider_mode: str
    stt_provider: str = ""
    tts_provider: str = ""
    llm_provider: str = ""
    telephony_provider: str = ""
Frozen metrics for a completed call. See Metrics & Cost Tracking for usage.

CostBreakdown

@dataclass(frozen=True)
class CostBreakdown:
    stt: float = 0.0
    tts: float = 0.0
    llm: float = 0.0
    telephony: float = 0.0
    total: float = 0.0
Per-component cost in USD.

LatencyBreakdown

@dataclass(frozen=True)
class LatencyBreakdown:
    stt_ms: float = 0.0
    llm_ms: float = 0.0
    tts_ms: float = 0.0
    total_ms: float = 0.0
Per-component latency in milliseconds.

TurnMetrics

@dataclass(frozen=True)
class TurnMetrics:
    turn_index: int
    user_text: str
    agent_text: str
    latency: LatencyBreakdown
    stt_audio_seconds: float = 0.0
    tts_characters: int = 0
    timestamp: float = 0.0
Per-turn metrics for a single conversation turn.

Exceptions

All exceptions inherit from PatterError.
PatterError
└── PatterConnectionError

PatterError

Base exception for all Patter SDK errors.
from patter import PatterError

try:
    await phone.serve(agent)
except PatterError as e:
    print(f"Patter error: {e}")

PatterConnectionError

Raised for connection failures or missing required parameters.
from patter import PatterConnectionError

try:
    await phone.serve(agent)
except PatterConnectionError as e:
    print(f"Connection error: {e}")

Exports

All public symbols exported from the patter package:
from patter import (
    Patter,
    Agent,
    CallControl,
    CallEvent,
    CallMetrics,
    CostBreakdown,
    Guardrail,
    LatencyBreakdown,
    STTConfig,
    TTSConfig,
    TurnMetrics,
    PatterError,
    PatterConnectionError,
)