Patter Python SDK
Connect AI agents to phone numbers in four lines of code
Quickstart • Features • Configuration • Voice Modes • API Reference • Contributing
Patter is the open-source SDK that gives your AI agent a phone number. Point it at any function that returns a string, and Patter handles the rest: telephony, speech-to-text, text-to-speech, and real-time audio streaming. You build the agent — we connect it to the phone.
Quickstart
pip install getpatter
Set the env vars your carrier and engine need:
export TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export TWILIO_AUTH_TOKEN=your_auth_token
export OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
Four lines of Python:
from getpatter import Patter, Twilio, OpenAIRealtime
phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(engine=OpenAIRealtime(), system_prompt="You are a friendly receptionist for Acme Corp.", first_message="Hello! How can I help?")
await phone.serve(agent, tunnel=True)
tunnel=True spawns a Cloudflare tunnel and points your Twilio number at it. In production, pass webhook_url="api.prod.example.com" to the constructor instead.
Features
| Feature | Method | Example |
|---|---|---|
| Inbound calls | phone.serve(agent) |
Answer calls as an AI |
| Outbound calls + AMD | phone.call(to, machine_detection=True) |
Place calls with voicemail detection |
| Tool calling | agent(tools=[Tool(...)]) |
Agent calls external APIs mid-conversation |
| Custom STT + TTS | agent(stt=DeepgramSTT(), tts=ElevenLabsTTS()) |
Bring your own voice providers |
| Dynamic variables | agent(variables={...}) |
Personalize prompts per caller |
| Pluggable LLM | agent(llm=AnthropicLLM()) |
5 built-in providers: OpenAI, Anthropic, Groq, Cerebras, Google |
| Custom LLM (any model) | serve(on_message=handler) |
Route to anything — local llama.cpp, internal gateways, etc. |
| Call recording | serve(recording=True) |
Record all calls |
| Call transfer | transfer_call (auto-injected) |
Transfer to a human |
| Voicemail drop | call(voicemail_message="...") |
Play message on voicemail |
| Phone-as-a-tool (external agents) | PatterTool(phone, agent).execute(...) |
Drop into LangChain / OpenAI Assistants / Hermes / MCP |
Configuration
Environment variables
Every provider reads its credentials from the environment by default. Pass api_key="..." to any constructor to override.
| Variable | Used by |
|---|---|
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN |
Twilio() carrier |
TELNYX_API_KEY, TELNYX_CONNECTION_ID, TELNYX_PUBLIC_KEY (optional) |
Telnyx() carrier |
PLIVO_AUTH_ID, PLIVO_AUTH_TOKEN |
Plivo() carrier — Auth Token doubles as the V3 webhook signature key |
OPENAI_API_KEY |
OpenAIRealtime, getpatter.stt.whisper.STT, getpatter.tts.openai.TTS |
ELEVENLABS_API_KEY, ELEVENLABS_AGENT_ID |
ElevenLabsConvAI, getpatter.tts.elevenlabs.TTS |
DEEPGRAM_API_KEY |
getpatter.stt.deepgram.STT |
CARTESIA_API_KEY |
getpatter.stt.cartesia.STT, getpatter.tts.cartesia.TTS |
RIME_API_KEY |
getpatter.tts.rime.TTS |
LMNT_API_KEY |
getpatter.tts.lmnt.TTS |
SONIOX_API_KEY |
getpatter.stt.soniox.STT |
SPEECHMATICS_API_KEY |
getpatter.stt.speechmatics.STT |
ASSEMBLYAI_API_KEY |
getpatter.stt.assemblyai.STT |
ANTHROPIC_API_KEY |
AnthropicLLM / getpatter.llm.anthropic.LLM |
GROQ_API_KEY |
GroqLLM / getpatter.llm.groq.LLM |
CEREBRAS_API_KEY |
CerebrasLLM / getpatter.llm.cerebras.LLM |
GEMINI_API_KEY (or GOOGLE_API_KEY) |
GoogleLLM / getpatter.llm.google.LLM |
cp .env.example .env
# Edit .env with your API keys
Other carriers: Telnyx and Plivo are both fully supported alternatives to Twilio. All three carriers receive equal support for inbound DTMF, transfer, AMD, status callbacks, recording, voicemail drop, and metrics. Plivo additionally supports native DTMF send over the media WebSocket — a capability Twilio Media Streams lacks. Plivo's Auth Token doubles as the V3 webhook signature key (no separate public key, unlike Telnyx Ed25519).
Voice Modes
| Mode | Latency | Quality | Best For |
|---|---|---|---|
| OpenAI Realtime | Lowest | High | Fluid, low-latency conversations |
| Pipeline (STT + LLM + TTS) | Low | High | Independent control over STT and TTS |
| ElevenLabs ConvAI | Low | High | ElevenLabs-managed conversation flow |
API Reference
Patter constructor
Patter(
carrier: Twilio | Telnyx | Plivo,
phone_number: str,
webhook_url: str = "", # Public hostname (no scheme). Mutually exclusive with tunnel=...
tunnel: CloudflareTunnel | Static | Ngrok | None = None,
pricing: dict | None = None,
)
| Parameter | Type | Description |
|---|---|---|
carrier |
Twilio / Telnyx / Plivo |
Carrier instance. Reads env vars by default. |
phone_number |
str |
Your phone number in E.164 format. |
webhook_url |
str |
Public hostname your local server is reachable on. Use instead of tunnel=. |
tunnel |
instance | CloudflareTunnel(), Static(hostname=...), or Ngrok(). |
phone.agent()
phone.agent(
system_prompt: str,
engine: OpenAIRealtime | ElevenLabsConvAI | None = None, # default OpenAIRealtime()
stt: STTProvider | None = None, # e.g. DeepgramSTT()
tts: TTSProvider | None = None, # e.g. ElevenLabsTTS()
voice: str = "alloy",
model: str = "gpt-4o-mini-realtime-preview",
language: str = "en",
first_message: str = "",
tools: list[Tool] | None = None,
guardrails: list[Guardrail] | None = None,
variables: dict | None = None,
...,
)
Pass engine= for end-to-end mode, stt= + tts= for pipeline mode. Both arguments may take plain adapter instances (e.g. DeepgramSTT()) that read their API key from the environment.
phone.serve()
await phone.serve(
agent: Agent,
port: int = 8000,
tunnel: bool = False, # shortcut for Patter(tunnel=CloudflareTunnel())
dashboard: bool = True,
recording: bool = False,
on_call_start: Callable | None = None,
on_call_end: Callable | None = None,
on_transcript: Callable | None = None,
on_message: Callable | str | None = None,
voicemail_message: str = "",
dashboard_token: str = "",
)
phone.call()
await phone.call(
to: str,
agent: Agent | None = None, # required
from_number: str = "",
first_message: str = "",
machine_detection: bool = False,
voicemail_message: str = "",
ring_timeout: int | None = None,
)
STT / TTS catalog
Flat re-exports (short form):
from getpatter import (
Twilio, Telnyx, Plivo,
OpenAIRealtime, ElevenLabsConvAI,
# STT / TTS classes live in namespaced modules — see below.
)
Namespaced imports (one module per provider):
from getpatter.stt import deepgram, whisper, openai_transcribe, cartesia, soniox, speechmatics, assemblyai
from getpatter.tts import elevenlabs, openai as openai_tts, cartesia as cartesia_tts, rime, lmnt
stt = deepgram.STT() # reads DEEPGRAM_API_KEY
stt = openai_transcribe.STT() # gpt-4o-transcribe — ~10× faster than Whisper
tts = elevenlabs.TTS(voice_id="sarah") # reads ELEVENLABS_API_KEY
tts = elevenlabs.TTS.for_twilio(voice_id="sarah") # μ-law @ 8 kHz native — no resample on Twilio
Examples
Inbound calls — default engine
import asyncio
from getpatter import Patter, Twilio, OpenAIRealtime
async def main() -> None:
phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
engine=OpenAIRealtime(),
system_prompt="You are a helpful customer service agent.",
first_message="Hello! How can I help?",
)
await phone.serve(
agent,
tunnel=True,
on_call_start=lambda data: print(f"Call from {data['caller']}"),
on_call_end=lambda data: print("Call ended"),
)
asyncio.run(main())
Custom voice — Deepgram STT + ElevenLabs TTS
from getpatter import Patter, Twilio
from getpatter.stt import deepgram
from getpatter.tts import elevenlabs
phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
stt=deepgram.STT(), # reads DEEPGRAM_API_KEY
tts=elevenlabs.TTS(voice="sarah"), # reads ELEVENLABS_API_KEY
system_prompt="You are a helpful voice assistant.",
)
await phone.serve(agent, tunnel=True)
Pipeline mode — pick STT, LLM, TTS independently
from getpatter import Patter, Twilio, DeepgramSTT, AnthropicLLM, ElevenLabsTTS
phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
stt=DeepgramSTT(), # reads DEEPGRAM_API_KEY
llm=AnthropicLLM(), # reads ANTHROPIC_API_KEY
tts=ElevenLabsTTS(voice_id="sarah"), # reads ELEVENLABS_API_KEY
system_prompt="You are a helpful voice assistant.",
)
await phone.serve(agent, tunnel=True)
Available LLM providers: OpenAILLM, AnthropicLLM, GroqLLM, CerebrasLLM, GoogleLLM. Tool calling works across all five. For fully custom logic, drop llm= and pass an on_message callback to serve() instead.
Tool calling
from getpatter import Patter, Twilio, OpenAIRealtime, Tool, tool
@tool
async def check_availability(date: str) -> dict:
"""Check appointment availability for a given ISO date."""
return {"available": True}
phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
engine=OpenAIRealtime(),
system_prompt="You are a booking assistant.",
tools=[check_availability],
)
await phone.serve(agent, tunnel=True)
Outbound calls
from getpatter import Patter, Twilio, OpenAIRealtime
phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
engine=OpenAIRealtime(),
system_prompt="You are making reminder calls.",
first_message="Hi, this is a reminder from Acme Corp.",
)
await phone.serve(agent, tunnel=True)
await phone.call(to="+14155551234", agent=agent)
Dynamic variables
agent = phone.agent(
engine=OpenAIRealtime(),
system_prompt="You are helping {customer_name}, account #{account_id}.",
first_message="Hi {customer_name}! How can I help you today?",
variables={"customer_name": "Jane", "account_id": "A-789"},
)
Contributing
Pull requests are welcome.
cd libraries/python && pip install -e ".[dev]" && pytest tests/ -v
Please open an issue before submitting large changes so we can discuss the approach first.
License
MIT — see LICENSE.
Metadata
Release files for getpatter 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| getpatter-0.7.0.tar.gz | 4.9 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| getpatter-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 9.8 MB
Release files / getpatter-0.7.0.tar.gz
| Download URL | getpatter-0.7.0.tar.gz |
|---|---|
| Size | 4.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4cf6630f701cbc776762074503a8534768b898bdbe86d64d789b4b801e992d65
|
|
BLAKE2b-256 checksum How to use checksums |
fc2a7272f5b6ccb5ca51105626aa5f41451f5269259e40acf4982bc89be4d816
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 30, 2026.
Transparency logRelease files / getpatter-0.7.0-py3-none-any.whl
| Download URL | getpatter-0.7.0-py3-none-any.whl |
|---|---|
| Size | 4.9 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
520bc523b7fb8000f555a01d7ce6bb48b5c175362d627db6bc4b5c8b6d21bfea
|
|
BLAKE2b-256 checksum How to use checksums |
c0940e3029fe895325a83dcac8e646dc86ef4cf5eaa9c8d270cba9231601a59e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 30, 2026.
Transparency logSecurity for getpatter 0.7.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for getpatter-0.7.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.