Open-source voice AI SDK — connect any AI agent to real phone calls in 4 lines of code
Project description
Patter Python SDK
Connect AI agents to phone numbers with 4 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
import asyncio
from patter import Patter
async def main():
phone = Patter(
twilio_sid="AC...", twilio_token="...",
openai_key="sk-...",
phone_number="+1...",
)
agent = phone.agent(
system_prompt="You are a friendly customer service agent for Acme Corp.",
voice="alloy",
first_message="Hello! Thanks for calling. How can I help?",
)
await phone.serve(agent=agent, port=8000)
asyncio.run(main())
Features
| Feature | Method | Example |
|---|---|---|
| Inbound calls | phone.serve(agent) |
Answer calls as an AI |
| Outbound calls + AMD | phone.call(to, machineDetection) |
Place calls with voicemail detection |
| Tool calling (webhooks) | agent(tools=[...]) |
Agent calls external APIs mid-conversation |
| Custom STT + TTS | agent(provider="pipeline") |
Bring your own voice providers |
| Dynamic variables | agent(variables={...}) |
Personalize prompts per caller |
| Custom LLM (any model) | serve(onMessage=handler) |
Claude, Mistral, LLaMA, etc. |
| Call recording | serve(recording=True) |
Record all calls |
| Call transfer | transfer_call (auto-injected) |
Transfer to a human |
| Voicemail drop | call(voicemailMessage="...") |
Play message on voicemail |
Configuration
Environment Variables
| Variable | Required | Description |
|---|---|---|
OPENAI_API_KEY |
Yes (Realtime mode) | OpenAI API key with Realtime access |
TWILIO_ACCOUNT_SID |
Yes | Twilio account SID |
TWILIO_AUTH_TOKEN |
Yes | Twilio auth token |
TWILIO_PHONE_NUMBER |
Yes | Your Twilio phone number (E.164) |
DEEPGRAM_API_KEY |
Pipeline mode | Deepgram STT key |
ELEVENLABS_API_KEY |
Pipeline mode | ElevenLabs TTS key |
ANTHROPIC_API_KEY |
Custom LLM | For bringing your own model |
WEBHOOK_URL |
No | Public URL (auto-tunneled via Cloudflare if omitted) |
cp .env.example .env
# Edit .env with your API keys
Telnyx: Telnyx is a fully supported telephony provider alternative to Twilio. Both carriers receive equal support for DTMF, transfer, recording, and metrics.
Voice Modes
| Mode | Latency | Quality | Best For |
|---|---|---|---|
| OpenAI Realtime | Lowest | High | Fluid, low-latency conversations |
| Deepgram + ElevenLabs | Low | High | Independent control over STT and TTS |
| ElevenLabs ConvAI | Low | High | ElevenLabs-managed conversation flow |
API Reference
Patter Constructor
Patter(
twilio_sid: str,
twilio_token: str,
openai_key: str,
phone_number: str,
webhook_url: str = None, # Optional; auto-tunneled via Cloudflare if omitted
)
| Parameter | Type | Description |
|---|---|---|
twilio_sid |
str |
Twilio account SID |
twilio_token |
str |
Twilio auth token |
openai_key |
str |
OpenAI API key |
phone_number |
str |
Your Twilio phone number (E.164 format) |
webhook_url |
str |
Public URL for Twilio webhooks (optional) |
phone.agent() Method
agent = phone.agent(
system_prompt: str,
voice: str = "alloy",
first_message: str = None,
variables: dict = None,
tools: list = None,
)
| Parameter | Type | Description |
|---|---|---|
system_prompt |
str |
Prompt with optional {variable} placeholders |
voice |
str |
TTS voice name (e.g., "alloy", "echo", "fable") |
first_message |
str |
Opening message (supports {variable} placeholders) |
variables |
dict |
Values substituted into prompts |
tools |
list |
Tool definitions: {name, description, parameters, webhook_url} |
phone.serve() Method
await phone.serve(
agent: Agent,
port: int = 8000,
dashboard: bool = False,
recording: bool = False,
onCallStart: callable = None,
onCallEnd: callable = None,
onTranscript: callable = None,
)
| Parameter | Type | Description |
|---|---|---|
agent |
Agent |
Agent configuration to use for calls |
port |
int |
Port to listen on (default: 8000) |
dashboard |
bool |
Enable the built-in monitoring dashboard |
recording |
bool |
Enable call recording via the telephony provider |
onCallStart |
callable |
Called when a call connects; receives data.caller, data.call_id |
onCallEnd |
callable |
Called when a call ends; receives data.history, data.transcript, data.duration |
onTranscript |
callable |
Called on each transcript turn; receives data.role, data.text, data.history |
phone.call() Method
await phone.call(
to: str,
first_message: str = None,
machine_detection: bool = False,
voicemail_message: str = None,
)
| Parameter | Type | Description |
|---|---|---|
to |
str |
Destination phone number (E.164 format) |
first_message |
str |
Opening message for the outbound call |
machine_detection |
bool |
Enable answering machine detection |
voicemail_message |
str |
Message to play when voicemail is detected |
Static Provider Helpers
Patter.deepgram(api_key: str, language: str = "en") -> STT
Patter.elevenlabs(api_key: str, voice: str = "aria") -> TTS
Patter.openai_tts(api_key: str, voice: str = "alloy") -> TTS
Patter.whisper(api_key: str, language: str = "en") -> STT
Examples
Inbound Calls (AI answers the phone)
import asyncio
from patter import Patter, IncomingMessage
async def agent(msg: IncomingMessage) -> str:
if "hours" in msg.text.lower():
return "We're open Monday through Friday, 9 to 5."
return "How can I help you today?"
async def main():
phone = Patter(
twilio_sid="AC...", twilio_token="...",
openai_key="sk-...",
phone_number="+1...",
)
await phone.serve(
agent=phone.agent(
system_prompt="You are a helpful customer service agent.",
first_message="Hello! How can I help?",
),
port=8000,
onCallStart=lambda data: print(f"Call from {data['caller']}"),
onCallEnd=lambda data: print("Call ended"),
)
asyncio.run(main())
Outbound Calls (AI calls someone)
import asyncio
from patter import Patter, IncomingMessage
async def agent(msg: IncomingMessage) -> str:
return "Thanks for picking up. This is a reminder about your appointment tomorrow."
async def main():
phone = Patter(
twilio_sid="AC...", twilio_token="...",
openai_key="sk-...",
phone_number="+1...",
)
agent_config = phone.agent(
system_prompt="You are making reminder calls.",
first_message="Hi, this is an automated reminder from Acme Corp.",
)
await phone.serve(agent=agent_config, port=8000)
await phone.call(to="+14155551234", first_message="Hi, just checking in.")
asyncio.run(main())
Tool Calling (Agent calls external APIs)
agent = phone.agent(
system_prompt="You are a booking assistant. Check availability before confirming.",
tools=[{
"name": "check_availability",
"description": "Check appointment availability for a given date",
"parameters": {
"type": "object",
"properties": {
"date": {"type": "string", "description": "ISO date, e.g. 2025-06-15"},
},
"required": ["date"],
},
"webhook_url": "https://api.example.com/availability",
}],
)
Custom Voice (Deepgram STT + ElevenLabs TTS)
phone = Patter(
twilio_sid="AC...", twilio_token="...",
openai_key="sk-...",
phone_number="+1...",
)
agent = phone.agent(
system_prompt="You are a helpful voice assistant.",
voice="aria",
)
# Use custom STT and TTS in pipeline mode
await phone.serve(
agent=agent,
port=8000,
stt=Patter.deepgram(api_key="dg_...", language="en"),
tts=Patter.elevenlabs(api_key="el_...", voice="aria"),
)
Call Recording
await phone.serve(
agent=agent,
port=8000,
recording=True, # Records all inbound and outbound calls
)
Dynamic Variables in Prompts
agent = phone.agent(
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 sdk-py && 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.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file getpatter-0.4.4.tar.gz.
File metadata
- Download URL: getpatter-0.4.4.tar.gz
- Upload date:
- Size: 4.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bc9a3a1a949a687f6f689f72e73e233c88a5fdab8b73b4ff06ceb6e114d2753
|
|
| MD5 |
6c293bf0c52cb300a2e0fb8a34fb907f
|
|
| BLAKE2b-256 |
f0d0d5ce9378e4d910923e9a496c826aeab5e346498a59eb4fb4b6fa04c3b678
|
Provenance
The following attestation bundles were made for getpatter-0.4.4.tar.gz:
Publisher:
release.yml on PatterAI/Patter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
getpatter-0.4.4.tar.gz -
Subject digest:
5bc9a3a1a949a687f6f689f72e73e233c88a5fdab8b73b4ff06ceb6e114d2753 - Sigstore transparency entry: 1353459487
- Sigstore integration time:
-
Permalink:
PatterAI/Patter@e9fe209082ca2fb94446e95014fd9755e23abbb1 -
Branch / Tag:
refs/tags/v0.4.4 - Owner: https://github.com/PatterAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9fe209082ca2fb94446e95014fd9755e23abbb1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file getpatter-0.4.4-py3-none-any.whl.
File metadata
- Download URL: getpatter-0.4.4-py3-none-any.whl
- Upload date:
- Size: 4.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6dfaa595baab98ba07e75e063ba949ffb7c64c06c422132b69673f55892dd48
|
|
| MD5 |
faa8dc8dfaca7f360d61ddbd10239481
|
|
| BLAKE2b-256 |
dd18640210b94aa140edecc694d000180421f88361c0894ef4974fbb62eca6c8
|
Provenance
The following attestation bundles were made for getpatter-0.4.4-py3-none-any.whl:
Publisher:
release.yml on PatterAI/Patter
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
getpatter-0.4.4-py3-none-any.whl -
Subject digest:
c6dfaa595baab98ba07e75e063ba949ffb7c64c06c422132b69673f55892dd48 - Sigstore transparency entry: 1353459636
- Sigstore integration time:
-
Permalink:
PatterAI/Patter@e9fe209082ca2fb94446e95014fd9755e23abbb1 -
Branch / Tag:
refs/tags/v0.4.4 - Owner: https://github.com/PatterAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9fe209082ca2fb94446e95014fd9755e23abbb1 -
Trigger Event:
push
-
Statement type: