Metadata-Version: 2.4
Name: tone-sdk
Version: 1.2.0
Summary: Official Python SDK for the Tone API
License: MIT
Keywords: ai-agents,sms,telephony,tone,voice
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.0.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: respx>=0.21.0; extra == 'dev'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2.0; extra == 'langchain'
Description-Content-Type: text/markdown

# tone-sdk (Python)

Official Python SDK for the [Tone API](https://usetone.ai).

Give your AI agent a real phone number. One API call provisions a number, one webhook handles calls and SMS.

## Installation

```bash
pip install tone-sdk
```

## Async Quickstart

```python
import asyncio
from tone import ToneClient

async def main():
    tone = ToneClient(api_key='tone_live_...')

    # Give your agent a phone number
    number = await tone.numbers.provision(country_code='IN')
    print(f'Your number: {number.phone_number}')  # +918035001234

    # Make a call
    await tone.calls.initiate(
        to='+919876543210',
        from_number=number.phone_number,
        instructions='You are a helpful assistant. Greet the caller in Hindi.',
    )

    # Send an SMS
    await tone.sms.send(
        to='+919876543210',
        from_number=number.phone_number,
        message='Your OTP is 4821',
    )

asyncio.run(main())
```

## Sync Quickstart

```python
from tone import SyncToneClient

tone = SyncToneClient(api_key='tone_live_...')

number = tone.numbers.provision(country_code='IN')
tone.sms.send(
    to='+919876543210',
    from_number=number.phone_number,
    message='Hello from Tone!',
)
```

## API Reference

### `ToneClient(api_key, ...)`

| Parameter | Type | Required | Default |
|-----------|------|----------|---------|
| `api_key` | `str` | ✓ | — |
| `base_url` | `str` | | `https://api.usetone.ai` |
| `timeout` | `float` (seconds) | | `30.0` |
| `max_retries` | `int` | | `3` |

---

### `tone.numbers`

#### `provision(country_code='IN', friendly_name=None)` → `PhoneNumber`

```python
number = await tone.numbers.provision(
    country_code='IN',       # 'IN' | 'US' | 'GB' | 'AU'
    friendly_name='Support', # optional
)
```

#### `list()` → `NumberList`

```python
result = await tone.numbers.list()
for n in result.numbers:
    print(n.phone_number)
```

#### `release(phone_number)` → `None`

```python
await tone.numbers.release('+918035001234')
```

---

### `tone.calls`

#### `initiate(to, from_number, instructions, max_duration=300)` → `Call`

Note: `from_number` is used instead of `from` (Python reserved word).

```python
call = await tone.calls.initiate(
    to='+919876543210',
    from_number='+918035001234',
    instructions='You are a support agent.',
    max_duration=300,
)
```

#### `list(number=None, limit=20, cursor=None)` → `CallLogList`

```python
result = await tone.calls.list(limit=50)
for log in result.logs:
    print(log.call_id, log.state)
if result.next_cursor:
    more = await tone.calls.list(cursor=result.next_cursor)
```

---

### `tone.sms`

#### `send(to, from_number, message)` → `SmsResult`

```python
result = await tone.sms.send(
    to='+919876543210',
    from_number='+918035001234',
    message='Your OTP is 4821',  # max 1600 chars
)
```

#### `list(number=None, limit=20, cursor=None)` → `SmsLogList`

```python
result = await tone.sms.list(limit=50)
```

---

## Error Handling

```python
from tone import ToneClient, ToneApiError, ToneValidationError, ToneNetworkError

tone = ToneClient(api_key='tone_live_...')

try:
    await tone.sms.send(to='invalid', from_number='+91...', message='hi')
except ToneValidationError as e:
    print(f"Validation error on '{e.field}': {e}")
except ToneApiError as e:
    print(f"API error {e.status_code} [{e.code}]: {e}")
    print(f"Request ID: {e.request_id}")
except ToneNetworkError as e:
    print(f"Network failure: {e.cause}")
```

---

## LangChain Integration

```python
from tone import ToneClient
from tone.integrations.langchain import ToneSendSmsTool, ToneInitiateCallTool

tone = ToneClient(api_key='tone_live_...')

tools = [
    ToneSendSmsTool(client=tone),
    ToneInitiateCallTool(client=tone),
]

# Use with any LangChain agent
from langchain.agents import initialize_agent
agent = initialize_agent(tools, llm, agent='zero-shot-react-description')
```

Install with LangChain extras:

```bash
pip install "tone-sdk[langchain]"
```

---

## HTTP Behaviour

- **Retries**: 429 and 5xx are retried with exponential backoff (1s → 2s → 4s). 4xx not retried.
- **Retry-After**: Honoured on 429 responses.
- **User-Agent**: `tone-sdk-python/1.0.0` on every request.
- **X-Request-Id**: UUID per request for server-side tracing.
- **Timeout**: Full lifecycle (default 30s).

## Requirements

- Python ≥ 3.10
- httpx ≥ 0.27
- pydantic ≥ 2.0

## License

MIT © Bellatech Marketing Pvt. Ltd.
