# AgentPhone Python SDK

Official Python SDK for [AgentPhone](https://agentphone.to) — give your AI agents real phone numbers, SMS, and voice calls.

## Installation

```bash
pip install agentphone
```

For async support:

```bash
pip install agentphone[async]
```

## Quickstart

```python
import os
from agentphone import AgentPhone

client = AgentPhone(api_key=os.environ["AGENTPHONE_API_KEY"])

# Create an agent with hosted voice
agent = client.agents.create(
    name="My Agent",
    voice_mode="hosted",
    system_prompt="Schedule appointments for a dentist office.",
    voice="alloy",
)

# Buy a number and attach it
number = client.numbers.buy(country="US", agent_id=agent.id)

# Make an AI conversation call — no webhook needed
call = client.calls.make_conversation(
    agent_id=agent.id,
    to_number="+14155551234",
    topic="Schedule a dentist appointment for next Tuesday at 2pm",
    initial_greeting="Hi, I'm calling to schedule an appointment.",
)
print(call.status)
```

## Async

```python
import os
from agentphone import AsyncAgentPhone

async with AsyncAgentPhone(api_key=os.environ["AGENTPHONE_API_KEY"]) as client:
    numbers = await client.numbers.list()
    call = await client.calls.make_conversation(
        agent_id="agent_123",
        to_number="+14155551234",
        topic="Ask about their return policy",
    )
```

## Resources

| Resource | Methods |
|---|---|
| `client.numbers` | `list()`, `buy()`, `release()`, `get_messages()`, `list_calls()` |
| `client.contacts` | `list()`, `create()`, `get()`, `update()`, `delete()` |
| `client.agents` | `list()`, `create()`, `get()`, `update()`, `delete()`, `attach_number()`, `detach_number()`, `list_calls()`, `list_conversations()`, `list_voices()`, `set_webhook()`, `get_webhook()`, `delete_webhook()`, `list_webhook_deliveries()`, `test_webhook()` |
| `client.calls` | `list()`, `get()`, `make()`, `make_conversation()`, `create_web_call()`, `get_transcript()` |
| `client.messages` | `send()`, `react()` |
| `client.conversations` | `list()`, `get()`, `update()`, `get_messages()` |
| `client.webhooks` | `get()`, `set()`, `delete()`, `list_deliveries()`, `test()`, `get_delivery_stats()`, `get_all_time_stats()` |
| `client.usage` | `get()`, `get_daily()`, `get_monthly()` |

## Contacts

```python
# Create a contact
contact = client.contacts.create(
    phone_number="+14155551234",
    name="Jane Doe",
    email="jane@example.com",
    notes="VIP customer",
)

# List contacts with search
contacts = client.contacts.list(search="Jane")

# Get, update, delete
contact = client.contacts.get("contact_123")
client.contacts.update("contact_123", name="Jane Smith")
client.contacts.delete("contact_123")
```

## Agents

```python
# Create with full voice config
agent = client.agents.create(
    name="Support Bot",
    description="Handles customer support",
    voice_mode="hosted",
    system_prompt="Handle customer support inquiries.",
    begin_message="Hello, how can I help you today?",
    voice="alloy",
)

# Update
client.agents.update(agent.id, name="Updated Bot", voice="nova")

# Delete
client.agents.delete(agent.id)

# Attach / detach numbers
client.agents.attach_number(agent.id, number_id="num_123")
client.agents.detach_number(agent.id, number_id="num_123")

# List available voices
voices = client.agents.list_voices()
for v in voices:
    print(v["name"])

# Agent-specific webhooks
client.agents.set_webhook(agent.id, url="https://my-server.com/hook")
client.agents.get_webhook(agent.id)
client.agents.delete_webhook(agent.id)
client.agents.list_webhook_deliveries(agent.id)
client.agents.test_webhook(agent.id)

# List agent's calls and conversations
calls = client.agents.list_calls(agent.id)
convos = client.agents.list_conversations(agent.id)
```

## Calls

```python
# Webhook-based call (your server handles the conversation)
call = client.calls.make(
    agent_id=agent.id,
    to_number="+14155551234",
    from_number_id="num_123",  # optional: pick which number to call from
    voice="alloy",             # optional: override voice
)

# AI conversation call (no webhook needed)
call = client.calls.make_conversation(
    agent_id=agent.id,
    to_number="+14155551234",
    topic="Ask about their return policy",
    initial_greeting="Hi, I'm calling about a recent order.",
    from_number_id="num_123",
    voice="alloy",
)

# List calls with filters
calls = client.calls.list(status="completed", direction="outbound", limit=10)

# Get call details and transcript
call = client.calls.get("call_123")
for t in call.transcripts:
    print(f"[{t.created_at}] {t.transcript}")

# Dedicated transcript endpoint
transcript = client.calls.get_transcript("call_123")
```

## Sending Messages

```python
# Send an outbound SMS
client.messages.send(
    agent_id="agent_123",
    to_number="+14155551234",
    body="Your appointment is confirmed for Tuesday at 2pm.",
)

# Send with media
client.messages.send(
    agent_id="agent_123",
    to_number="+14155551234",
    body="Here's your receipt",
    media_url="https://example.com/receipt.pdf",
)

# React to a message (iMessage only)
client.messages.react("msg_123", reaction="love")
```

## Conversations & SMS

```python
# List conversations
convos = client.conversations.list()

# Get conversation with messages
convo = client.conversations.get("conv_123", message_limit=100)
for msg in convo.messages:
    print(f"{msg.from_number}: {msg.body}")

# Paginated messages
messages = client.conversations.get_messages("conv_123", limit=50, before="msg_456")

# Update conversation metadata
client.conversations.update("conv_123", metadata={"tag": "vip"})
```

## Usage

```python
# Current usage summary
usage = client.usage.get()
print(f"Plan: {usage.plan.name}")
print(f"Numbers: {usage.numbers.used}/{usage.numbers.limit}")
print(f"Calls (30d): {usage.stats.calls_last_30d}")

# Daily and monthly breakdowns
daily = client.usage.get_daily(days=7)
monthly = client.usage.get_monthly(months=3)
```

## Webhooks

```python
# Project-level webhook
webhook = client.webhooks.set(url="https://my-server.com/hook", context_limit=10)
client.webhooks.get()
client.webhooks.delete()

# Delivery monitoring
deliveries = client.webhooks.list_deliveries(limit=50)
stats = client.webhooks.get_delivery_stats(hours=24)
print(f"Success rate: {stats.success_rate}%")
```

## Webhook Verification

```python
from agentphone import construct_event, WebhookVerificationError

@app.post("/webhook")
async def handle(request: Request):
    body = await request.body()
    sig = request.headers["X-Webhook-Signature"]
    try:
        event = construct_event(body, sig, secret=os.environ["WEBHOOK_SECRET"])
    except WebhookVerificationError:
        return Response(status_code=403)

    if event.event == "agent.message":
        print(f"SMS from {event.data.from_number}: {event.data.message}")
```

## Error Handling

```python
from agentphone import AgentPhoneError, AuthenticationError, NotFoundError, RateLimitError

try:
    call = client.calls.get("bad-id")
except NotFoundError:
    print("Call not found")
except RateLimitError:
    print("Rate limited — back off and retry")
except AuthenticationError:
    print("Invalid API key")
except AgentPhoneError as e:
    print(f"API error {e.status}: {e.message}")
```

## Requirements

- Python 3.9+
- `requests` (sync client)
- `httpx` (async client, optional)

## Links

- [AgentPhone Dashboard](https://agentphone.to)
- [Documentation](https://docs.agentphone.to)
- [API Reference](https://docs.agentphone.to/api-reference)
- [MCP Server](https://github.com/AgentPhone-AI/agentphone-mcp)
- [Node.js SDK](https://github.com/AgentPhone-AI/agentphone-node)
