Python SDK

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

PyPI

Installation

1pip install agentphone

For async support:

1pip install agentphone[async]

Quick start

1from agentphone import AgentPhone
2
3client = AgentPhone(api_key="YOUR_API_KEY")
4
5# Create an agent
6agent = client.agents.create(name="Support Bot")
7
8# Buy a number and attach it
9number = client.numbers.buy(country="US", agent_id=agent.id)
10
11# Make an AI-powered call
12call = client.calls.make(
13 agent_id=agent.id,
14 to_number="+15559876543",
15 system_prompt="You are a helpful support assistant.",
16 initial_greeting="Hi, this is Support Bot!",
17)

Async usage

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

Resources

Agents

1# List all agents
2agents = client.agents.list(limit=20, offset=0)
3
4# Create an agent with voice configuration
5agent = client.agents.create(
6 name="Support Bot",
7 description="Handles customer inquiries",
8 voice_mode="hosted", # "hosted" for built-in AI, "webhook" for custom
9 system_prompt="You are a friendly support agent.",
10 begin_message="Hi! How can I help?",
11 voice="11labs-Brian", # see list_voices()
12 transfer_number="+15551234567", # optional: transfer calls here
13 voicemail_message="Leave a message after the beep.",
14)
15
16# Get agent details
17agent = client.agents.get(agent_id="agt_abc123")
18
19# Update an agent
20agent = client.agents.update(
21 agent_id="agt_abc123",
22 name="Updated Bot",
23 system_prompt="New prompt here.",
24)
25
26# Delete an agent
27client.agents.delete(agent_id="agt_abc123")
28
29# Attach / detach a number
30client.agents.attach_number(agent_id="agt_abc123", number_id="num_xyz789")
31client.agents.detach_number(agent_id="agt_abc123", number_id="num_xyz789")
32
33# List calls and conversations for an agent
34calls = client.agents.list_calls(agent_id="agt_abc123", limit=20)
35convos = client.agents.list_conversations(agent_id="agt_abc123", limit=20)
36
37# List available voices
38voices = client.agents.list_voices()

Per-agent webhooks

1# Set a webhook for a specific agent (overrides project default)
2webhook = client.agents.set_webhook(
3 agent_id="agt_abc123",
4 url="https://your-server.com/agent-hook",
5 context_limit=10,
6 timeout=30,
7)
8
9# Get / delete an agent's webhook
10webhook = client.agents.get_webhook(agent_id="agt_abc123")
11client.agents.delete_webhook(agent_id="agt_abc123")
12
13# View deliveries and send a test event
14deliveries = client.agents.list_webhook_deliveries(agent_id="agt_abc123", limit=50)
15client.agents.test_webhook(agent_id="agt_abc123")

Numbers

1# List numbers
2numbers = client.numbers.list(limit=20, offset=0)
3
4# Buy a new number
5number = client.numbers.buy(
6 country="US",
7 area_code="415", # optional, US/CA only
8 agent_id="agt_abc123", # optional: attach immediately
9)
10
11# Get messages for a number
12messages = client.numbers.get_messages(number_id="num_xyz789", limit=50)
13
14# List calls for a number
15calls = client.numbers.list_calls(number_id="num_xyz789", limit=20)
16
17# Release a number (irreversible)
18client.numbers.release(number_id="num_xyz789")

Messages

1# Send an SMS or iMessage
2client.messages.send(
3 agent_id="agt_abc123",
4 to_number="+15559876543",
5 body="Hello from my agent!",
6 media_url="https://example.com/image.png", # optional (MMS/iMessage)
7 number_id="num_xyz789", # optional: send from specific number
8)
9
10# Send a tapback reaction (iMessage only)
11client.messages.react(
12 message_id="msg_abc123",
13 reaction="love", # love, like, dislike, laugh, emphasize, question
14)

Contacts

1# List contacts
2contacts = client.contacts.list(limit=50, search="Alice")
3
4# Create a contact
5contact = client.contacts.create(
6 phone_number="+15559876543",
7 name="Alice Smith",
8 email="[email protected]",
9 notes="VIP customer",
10)
11
12# Get / update / delete
13contact = client.contacts.get(contact_id="ct_abc123")
14contact = client.contacts.update(contact_id="ct_abc123", name="Alice Johnson")
15client.contacts.delete(contact_id="ct_abc123")

Conversations

1# List all conversations
2convos = client.conversations.list(limit=20, offset=0)
3
4# Get a conversation with messages
5convo = client.conversations.get(
6 conversation_id="conv_abc123",
7 message_limit=50,
8)
9
10# Get messages with cursor pagination
11messages = client.conversations.get_messages(
12 conversation_id="conv_abc123",
13 limit=50,
14 before="msg_older", # cursor pagination
15 after="msg_newer",
16)
17
18# Update conversation metadata
19convo = client.conversations.update(
20 conversation_id="conv_abc123",
21 metadata={"priority": "high", "assigned_to": "team-a"},
22)

Calls

1# List calls with optional filters
2calls = client.calls.list(
3 limit=20,
4 status="completed", # optional filter
5 direction="outbound", # optional filter
6 type="conversation", # optional filter
7 search="+1415", # optional search
8)
9
10# Get a call with transcript
11call = client.calls.get(call_id="call_abc123")
12
13# Make an outbound call with built-in AI (no webhook needed)
14call = client.calls.make(
15 agent_id="agt_abc123",
16 to_number="+15559876543",
17 system_prompt="You are a support agent helping with order inquiries.",
18 initial_greeting="Hello! How can I help you today?",
19 from_number_id="num_xyz789", # optional: call from specific number
20 voice="11labs-Brian", # optional: override agent voice
21)
22
23# Make a webhook-based call (omit system_prompt, requires webhook configured)
24call = client.calls.make(
25 agent_id="agt_abc123",
26 to_number="+15559876543",
27 initial_greeting="Hello!",
28)
29
30# Create a web-based call (browser)
31web_call = client.calls.create_web_call(
32 agent_id="agt_abc123",
33 metadata={"source": "dashboard"},
34)
35
36# Get transcript separately
37transcript = client.calls.get_transcript(call_id="call_abc123")
38
39# Stream transcript via Server-Sent Events
40for event in client.calls.stream_transcript(call_id="call_abc123"):
41 if event["event"] == "turn":
42 print(f"[{event['data']['role']}] {event['data']['content']}")
43 elif event["event"] == "ended":
44 print(f"Call ended ({event['data']['durationSeconds']}s)")

Webhooks (project-level)

1# Get webhook config
2webhook = client.webhooks.get()
3
4# Set or update webhook
5webhook = client.webhooks.set(
6 url="https://your-server.com/webhook",
7 context_limit=10, # 0-50 recent messages in payloads
8 timeout=30, # response timeout in seconds (5-120)
9)
10print(webhook.secret) # save this!
11
12# View delivery history
13deliveries = client.webhooks.list_deliveries(limit=50)
14
15# Get delivery statistics
16stats = client.webhooks.get_delivery_stats(hours=24)
17print(f"Success rate: {stats.success_rate}%")
18
19all_time = client.webhooks.get_all_time_stats()
20
21# Test webhook
22client.webhooks.test(agent_id="agt_abc123") # optional: test for specific agent
23
24# Delete webhook
25client.webhooks.delete()

Usage

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

Error handling

1from agentphone import (
2 AgentPhoneError,
3 AuthenticationError,
4 NotFoundError,
5 RateLimitError,
6)
7
8try:
9 agent = client.agents.get(agent_id="bad-id")
10except NotFoundError:
11 print("Agent not found")
12except AuthenticationError:
13 print("Invalid API key")
14except RateLimitError:
15 print("Too many requests")
16except AgentPhoneError as e:
17 print(f"API error {e.status}: {e.message}")

Webhook verification

Verify incoming webhook signatures and parse events:

1from agentphone import construct_event, verify_webhook, WebhookVerificationError
2
3# Option 1: Verify signature only
4try:
5 verify_webhook(payload, signature, secret)
6except WebhookVerificationError:
7 print("Invalid signature")
8
9# Option 2: Verify + parse into a typed event
10event = construct_event(payload, signature, secret)
11
12if event.event == "agent.message":
13 print(event.data.message)
14 print(event.data.from_number)
15 print(event.channel) # "sms", "imessage", or "voice"
16 for item in event.recent_history: # recent conversation context
17 print(f" [{item.direction}] {item.content}")
18 print(event.conversation_state) # custom metadata

Flask example

1from flask import Flask, request, jsonify
2from agentphone import construct_event
3
4app = Flask(__name__)
5
6@app.route("/webhook", methods=["POST"])
7def webhook():
8 event = construct_event(
9 payload=request.data,
10 signature=request.headers.get("X-Webhook-Signature", ""),
11 secret="your_webhook_secret",
12 )
13
14 # Respond to the caller
15 return jsonify({"response": f"Got your message: {event.data.message}"})

Advanced

Context manager

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

Custom base URL and timeout

1client = AgentPhone(
2 api_key="YOUR_API_KEY",
3 base_url="https://api.agentphone.to",
4 timeout=30.0,
5)