Skip to navigation

Python SDK

The official AgentPhone Python library provides convenient access to the AgentPhone API with both synchronous and asynchronous clients.

PyPI

Installation

pip install agentphone

For async support:

pip install agentphone[async]

Quick start

from agentphone import AgentPhone
client = AgentPhone(api_key="YOUR_API_KEY")
# Create an agent
agent = client.agents.create(name="Support Bot")
# Buy a number and attach it
number = client.numbers.buy(country="US", agent_id=agent.id)
# Make an AI-powered call
call = client.calls.make(
agent_id=agent.id,
to_number="+15559876543",
system_prompt="You are a helpful support assistant.",
initial_greeting="Hi, this is Support Bot!",
)

Async usage

from agentphone import AsyncAgentPhone
async with AsyncAgentPhone(api_key="YOUR_API_KEY") as client:
agents = await client.agents.list()
call = await client.calls.make(
agent_id=agents.data[0].id,
to_number="+15559876543",
system_prompt="You are a helpful assistant.",
)

Resources

Agents

# List all agents
agents = client.agents.list(limit=20, offset=0)
# Create an agent with voice configuration
agent = client.agents.create(
name="Support Bot",
description="Handles customer inquiries",
voice_mode="hosted", # "hosted" for built-in AI, "webhook" for custom
system_prompt="You are a friendly support agent.",
begin_message="Hi! How can I help?",
voice="11labs-Brian", # see list_voices()
transfer_number="+15551234567", # optional: transfer calls here
voicemail_message="Leave a message after the beep.",
)
# Get agent details
agent = client.agents.get(agent_id="agt_abc123")
# Update an agent
agent = client.agents.update(
agent_id="agt_abc123",
name="Updated Bot",
system_prompt="New prompt here.",
)
# Delete an agent
client.agents.delete(agent_id="agt_abc123")
# Attach / detach a number
client.agents.attach_number(agent_id="agt_abc123", number_id="num_xyz789")
client.agents.detach_number(agent_id="agt_abc123", number_id="num_xyz789")
# List calls and conversations for an agent
calls = client.agents.list_calls(agent_id="agt_abc123", limit=20)
convos = client.agents.list_conversations(agent_id="agt_abc123", limit=20)
# List available voices
voices = client.agents.list_voices()

Per-agent webhooks

# Set a webhook for a specific agent (overrides project default)
webhook = client.agents.set_webhook(
agent_id="agt_abc123",
url="https://your-server.com/agent-hook",
context_limit=10,
timeout=30,
)
# Get / delete an agent's webhook
webhook = client.agents.get_webhook(agent_id="agt_abc123")
client.agents.delete_webhook(agent_id="agt_abc123")
# View deliveries and send a test event
deliveries = client.agents.list_webhook_deliveries(agent_id="agt_abc123", limit=50)
client.agents.test_webhook(agent_id="agt_abc123")

Numbers

# List numbers
numbers = client.numbers.list(limit=20, offset=0)
# Buy a new number
number = client.numbers.buy(
country="US",
area_code="415", # optional, US/CA only
agent_id="agt_abc123", # optional: attach immediately
)
# Get messages for a number
messages = client.numbers.get_messages(number_id="num_xyz789", limit=50)
# List calls for a number
calls = client.numbers.list_calls(number_id="num_xyz789", limit=20)
# Release a number (irreversible)
client.numbers.release(number_id="num_xyz789")

Messages

# Send an SMS or iMessage
client.messages.send(
agent_id="agt_abc123",
to_number="+15559876543",
body="Hello from my agent!",
media_url="https://example.com/image.png", # optional (MMS/iMessage)
number_id="num_xyz789", # optional: send from specific number
)
# Send a tapback reaction (iMessage only)
client.messages.react(
message_id="msg_abc123",
reaction="love", # love, like, dislike, laugh, emphasize, question
)

Contacts

# List contacts
contacts = client.contacts.list(limit=50, search="Alice")
# Create a contact
contact = client.contacts.create(
phone_number="+15559876543",
name="Alice Smith",
notes="VIP customer",
)
# Get / update / delete
contact = client.contacts.get(contact_id="ct_abc123")
contact = client.contacts.update(contact_id="ct_abc123", name="Alice Johnson")
client.contacts.delete(contact_id="ct_abc123")

Conversations

# List all conversations
convos = client.conversations.list(limit=20, offset=0)
# Get a conversation with messages
convo = client.conversations.get(
conversation_id="conv_abc123",
message_limit=50,
)
# Get messages with cursor pagination
messages = client.conversations.get_messages(
conversation_id="conv_abc123",
limit=50,
before="msg_older", # cursor pagination
after="msg_newer",
)
# Update conversation metadata
convo = client.conversations.update(
conversation_id="conv_abc123",
metadata={"priority": "high", "assigned_to": "team-a"},
)

Calls

# List calls with optional filters
calls = client.calls.list(
limit=20,
status="completed", # optional filter
direction="outbound", # optional filter
type="conversation", # optional filter
search="+1415", # optional search
)
# Get a call with transcript
call = client.calls.get(call_id="call_abc123")
# Make an outbound call with built-in AI (no webhook needed)
call = client.calls.make(
agent_id="agt_abc123",
to_number="+15559876543",
system_prompt="You are a support agent helping with order inquiries.",
initial_greeting="Hello! How can I help you today?",
from_number_id="num_xyz789", # optional: call from specific number
voice="11labs-Brian", # optional: override agent voice
)
# Make a webhook-based call (omit system_prompt, requires webhook configured)
call = client.calls.make(
agent_id="agt_abc123",
to_number="+15559876543",
initial_greeting="Hello!",
)
# Create a web-based call (browser)
web_call = client.calls.create_web_call(
agent_id="agt_abc123",
metadata={"source": "dashboard"},
)
# Get transcript separately
transcript = client.calls.get_transcript(call_id="call_abc123")
# Stream transcript via Server-Sent Events
for event in client.calls.stream_transcript(call_id="call_abc123"):
if event["event"] == "turn":
print(f"[{event['data']['role']}] {event['data']['content']}")
elif event["event"] == "ended":
print(f"Call ended ({event['data']['durationSeconds']}s)")

Webhooks (project-level)

# Get webhook config
webhook = client.webhooks.get()
# Set or update webhook
webhook = client.webhooks.set(
url="https://your-server.com/webhook",
context_limit=10, # 0-50 recent messages in payloads
timeout=30, # response timeout in seconds (5-120)
)
print(webhook.secret) # save this!
# View delivery history
deliveries = client.webhooks.list_deliveries(limit=50)
# Get delivery statistics
stats = client.webhooks.get_delivery_stats(hours=24)
print(f"Success rate: {stats.success_rate}%")
all_time = client.webhooks.get_all_time_stats()
# Test webhook
client.webhooks.test(agent_id="agt_abc123") # optional: test for specific agent
# Delete webhook
client.webhooks.delete()

Usage

# Get current usage summary (plan, limits, stats)
usage = client.usage.get()
print(f"Plan: {usage.plan.name}")
print(f"Numbers: {usage.numbers.used}/{usage.numbers.limit}")
print(f"Messages (30d): {usage.stats.messages_last_30d}")
print(f"Calls (30d): {usage.stats.calls_last_30d}")
# Daily breakdown
daily = client.usage.get_daily(days=30)
for day in daily.data:
print(f"{day.date}: {day.messages} msgs, {day.calls} calls")
# Monthly breakdown
monthly = client.usage.get_monthly(months=6)

Error handling

from agentphone import (
AgentPhoneError,
AuthenticationError,
NotFoundError,
RateLimitError,
)
try:
agent = client.agents.get(agent_id="bad-id")
except NotFoundError:
print("Agent not found")
except AuthenticationError:
print("Invalid API key")
except RateLimitError:
print("Too many requests")
except AgentPhoneError as e:
print(f"API error {e.status}: {e.message}")

Webhook verification

Each delivery is signed over "{timestamp}.{raw_body}". The signature arrives in the X-Webhook-Signature header and the timestamp in X-Webhook-Timestamp, so pass both along with the raw body and your secret. Both helpers also enforce a 5-minute replay window by default (tolerance=300; pass tolerance=None to skip it). Requires agentphone >= 0.17.0.

from agentphone import construct_event, verify_webhook, WebhookVerificationError
# payload = raw request body (bytes)
# signature = X-Webhook-Signature header
# secret = your webhook secret
# timestamp = X-Webhook-Timestamp header
# Option 1: Verify signature only
try:
verify_webhook(payload, signature, secret, timestamp)
except WebhookVerificationError:
print("Invalid or stale signature")
# Option 2: Verify + parse into a typed event
event = construct_event(payload, signature, secret, timestamp)
if event.event == "agent.message":
print(event.data.message)
print(event.data.from_number)
print(event.channel) # "sms", "imessage", or "voice"
for item in event.recent_history: # recent conversation context
print(f" [{item.direction}] {item.content}")
print(event.conversation_state) # custom metadata

Flask example

from flask import Flask, request, jsonify
from agentphone import construct_event
app = Flask(__name__)
@app.route("/webhook", methods=["POST"])
def webhook():
event = construct_event(
payload=request.data,
signature=request.headers.get("X-Webhook-Signature", ""),
secret="your_webhook_secret",
timestamp=request.headers.get("X-Webhook-Timestamp", ""),
)
# Respond to the caller
return jsonify({"response": f"Got your message: {event.data.message}"})

Advanced

Context manager

with AgentPhone(api_key="YOUR_API_KEY") as client:
agents = client.agents.list()
# session is automatically closed

Custom base URL and timeout

client = AgentPhone(
api_key="YOUR_API_KEY",
base_url="https://api.agentphone.ai",
timeout=30.0,
)