Skip to main content

Patter Python SDK

Connect AI agents to phone numbers in four lines of code

PyPI MIT License Python 3.11+

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)

Source distribution for getpatter 0.7.0
File Size Uploaded Provenance
getpatter-0.7.0.tar.gz 4.9 MB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for getpatter 0.7.0
File Interpreter ABI Platform
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 log

Release 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 log

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

Select from 2 files

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 log

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

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page