Metadata-Version: 2.4
Name: inbounter
Version: 1.1.0
Summary: Official Python server SDK for Inbounter
Project-URL: Homepage, https://inbounter.com
Project-URL: Repository, https://github.com/inbounter/inbounter-python
Project-URL: Issues, https://github.com/inbounter/inbounter-python/issues
Author: Inbounter
License: MIT
Keywords: agents,ai,contacts,conversations,email,inbounter,inboxes,multi-channel,sdk,sms
Requires-Python: >=3.10
Requires-Dist: requests>=2.31.0
Description-Content-Type: text/markdown

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://inbounter.com/logos/inbounter_light.svg" />
    <source media="(prefers-color-scheme: light)" srcset="https://inbounter.com/logos/inbounter_dark.svg" />
    <img src="https://inbounter.com/logos/inbounter_dark.svg" alt="Inbounter" width="300" />
  </picture>
</p>

<p align="center">
  <b>Official Python SDK for <a href="https://inbounter.com">Inbounter</a></b><br/>
  Multi-channel agent platform: contacts, conversations, email, SMS, phones, and smart routing.
</p>

<p align="center">
  <a href="https://pypi.org/project/inbounter/">
    <img src="https://img.shields.io/pypi/v/inbounter.svg?style=flat-square" alt="PyPI Version" />
  </a>
  <a href="https://github.com/inbounter/inbounter/blob/main/LICENSE">
    <img src="https://img.shields.io/github/license/inbounter/inbounter.svg?style=flat-square" alt="License" />
  </a>
</p>

# Inbounter Python SDK

## What is Inbounter?

**Inbounter** is a multi-channel agent platform. It gives AI agents their own contacts, conversations, inboxes, email addresses, phone numbers, and SMS, with built-in security, smart routing, and webhooks.

- **Contacts**, Create, search, and manage contacts with multi-channel identities
- **Conversations**, Track and manage conversations across all channels
- **Messages**, Unified multi-channel send and reply
- **Emails**, Create email addresses, send and receive email, manage threads
- **Phones**, Provision phone numbers for your agents
- **SMS**, Send and receive text messages
- **Wait**, Pause and wait for incoming emails (OTPs, magic links, replies)
- **Routes**, Filter, transform, and deliver inbound messages to any destination
- **Webhooks**, Real-time event notifications
- **Credits**, Usage-based billing with auto-replenish

> [Get your API key](https://inbounter.com)

## Installation

```bash
pip install inbounter
```

Requires Python 3.10+.

## Quick Start

```python
from inbounter import Inbounter

client = Inbounter("your-api-key")

# Create a contact
client.contacts.set_workspace_id("wks_123")
contact = client.contacts.create({
    "name": "Alice",
    "email": "alice@example.com",
    "tags": ["lead"],
})

# Send a multi-channel message
client.messages.set_workspace_id("wks_123")
msg = client.messages.send(
    channel="email",
    to="alice@example.com",
    from_="hello@myworkspace.inbounter.to",
    body="Welcome to Inbounter!",
    subject="Hello",
)

# Reply to a message
client.messages.reply_to(msg["id"], body="Thanks for reaching out!")

# List conversations in a workspace
client.conversations.set_workspace_id("wks_123")
convos = client.conversations.list_by_workspace(limit=20)

# Create an email address
email = client.emails.create(username="hello", domain="myworkspace.inbounter.to")
print(email["address"])  # "hello@myworkspace.inbounter.to"

# Send an email
client.emails.send(
    from_="hello@myworkspace.inbounter.to",
    to="user@example.com",
    subject="Hello",
    markdown="# Welcome!",
)

# Send SMS
client.sms.send(
    from_="+14155551234",
    to="+14155555678",
    body="Hello from Inbounter",
)

# Wait for incoming email
result = client.wait.create(
    email="hello@myworkspace.inbounter.to",
    type="OTP",
)
```

## Feature Support

| Feature                                              | Supported |
| ---------------------------------------------------- | --------- |
| `contacts.create` / `list` / `get` / `update` / `delete` | Yes  |
| `contacts.search` / `add_identity`                   | Yes       |
| `conversations.list_by_workspace` / `list_by_contact`| Yes       |
| `conversations.get` / `update_status` / `update_metadata` / `update_starred` | Yes |
| `messages.send` / `reply_to` / `list` / `get`        | Yes       |
| `emails.create` / `list` / `get` / `delete`          | Yes       |
| `emails.send` / `reply` / `send_batch`               | Yes       |
| `emails.messages.list` / `get` / `search`            | Yes       |
| `emails.threads.list` / `get`                        | Yes       |
| `phones.create` / `list` / `get` / `delete`          | Yes       |
| `sms.send`                                           | Yes       |
| `sms.messages.list` / `get`                          | Yes       |
| `webhooks`                                           | Yes       |
| `wait` (email polling)                               | Yes       |
| `credits`                                            | Yes       |
| `routes`                                             | Yes       |

## Contacts

Create and manage contacts with multi-channel identities.

### Create a contact

```python
client.contacts.set_workspace_id("wks_123")

contact = client.contacts.create(
    name="Alice Smith",
    channel="EMAIL",
    value="alice@example.com",
)
```

### Get, list, search, and delete

```python
contact = client.contacts.get("ct_abc123")

page = client.contacts.list(limit=20)
contacts = page["items"]

results = client.contacts.search("alice", limit=20)

client.contacts.delete("ct_abc123")
```

### Add an identity

```python
identity = client.contacts.add_identity(
    "ct_abc123",
    channel="email",
    value="alice.work@example.com",
    primary=True,
)
```

## Conversations

Track and manage conversations across all channels.

```python
client.conversations.set_workspace_id("wks_123")

# List conversations in the workspace
convos = client.conversations.list_by_workspace(status="OPEN", limit=20)

# List conversations for a contact
convos = client.conversations.list_by_contact("ct_abc123")

# Get a conversation
convo = client.conversations.get("conv_789")

# Update status
client.conversations.update_status("conv_789", "CLOSED")

# Update metadata
client.conversations.update_metadata("conv_789", {"priority": "high"})

# Star/unstar a conversation
client.conversations.update_starred("conv_789", True)
```

## Messages (Multi-Channel)

Send and receive messages on any channel through a unified API.

### Send a message

```python
client.messages.set_workspace_id("wks_123")

# Send via email
msg = client.messages.send(
    channel="EMAIL",
    to="alice@example.com",
    from_="support@myworkspace.inbounter.net",
    body="<p>Your order has shipped.</p>",
    subject="Order Update",
    content_type="HTML",
)

# Send via SMS
msg = client.messages.send(
    channel="SMS",
    to="+14155555678",
    from_="+14155551234",
    body="Your code is 123456",
)
```

### Reply to a message

```python
client.messages.reply_to("msg_456", body="Thanks for reaching out!")
```

### List and get messages

```python
# List messages in a conversation
messages = client.messages.list("conv_789", limit=20)

# Get a single message
message = client.messages.get("msg_456")
```

## Emails

Create and manage email addresses for your AI agents.

### Create an email address

```python
email = client.emails.create(
    username="support",
    domain="myworkspace.inbounter.to",
)
```

### Get, list, and delete

```python
email = client.emails.get("support@myworkspace.inbounter.to")

emails = client.emails.list()

client.emails.delete("support@myworkspace.inbounter.to")
```

### Send email

```python
# Note: use from_ (with trailing underscore) because "from" is a Python keyword

message = client.emails.send(
    from_="support@myworkspace.inbounter.to",
    to="customer@example.com",
    subject="Your order has shipped",
    markdown="Hi! Your order #1234 is on the way.",
)
```

### Reply to a thread

```python
client.emails.reply(
    "thread_123",
    from_="support@myworkspace.inbounter.to",
    markdown="Thanks for reaching out!",
)
```

### Send batch emails

```python
messages = client.emails.send_batch(
    from_="updates@myworkspace.inbounter.to",
    messages=[
        {
            "to": "alice@example.com",
            "subject": "Welcome!",
            "text": "Thanks for signing up.",
        },
        {
            "to": "bob@example.com",
            "subject": "Welcome!",
            "text": "Thanks for signing up.",
        },
    ],
)
```

### Messages, search, and threads

```python
# List messages for an email address (returns { items, hasMore })
page = client.emails.messages.list(
    "support@myworkspace.inbounter.to",
    limit=20,
)
messages = page["items"]

# Get a single message (returns { message, routeRuns, deliveries })
details = client.emails.messages.get("msg_456")

# Search messages
results = client.emails.messages.search(q="invoice", email="support@myworkspace.inbounter.to")

# List threads
threads = client.emails.threads.list("support@myworkspace.inbounter.to", limit=20)

# Get a full thread
thread = client.emails.threads.get("support@myworkspace.inbounter.to", "thread_789")
```

## Phones

Provision and manage phone numbers for your AI agents.

```python
# Provision a phone number
phone = client.phones.create(country="US")

# List all phone numbers
phones = client.phones.list()

# Get a specific phone number
phone = client.phones.get("+14155551234")

# Delete a phone number
client.phones.delete("+14155551234")
```

## SMS

Send and receive text messages from your agent's phone numbers.

```python
# Send an SMS (use from_ because "from" is a Python keyword)
sent = client.sms.send(
    from_="+14155551234",
    to="+14155555678",
    body="Your verification code is 123456",
)

# List SMS messages for a phone number (returns { items, hasMore })
page = client.sms.messages.list("+14155551234", limit=20)
messages = page["items"]

# Get a single SMS message (returns { message, routeRuns, deliveries })
details = client.sms.messages.get("msg_456")
```

## Wait (Email Polling)

Wait for a specific inbound email, perfect for OTP codes, magic links, or reply emails.

```python
# Create a wait request for an OTP code
waiter = client.wait.create(
    email="agent@myworkspace.inbounter.to",
    type="OTP",
    timeout=120,
)

# Poll for the result (long-polling)
result = client.wait.poll(waiter["id"], timeout=30)
```

## Routes (Smart Routing)

Routes let you filter inbound messages, apply AI transformations, and deliver to destinations.

### Scoping

Routes are scoped to an inbox or a source:

```python
inbox_scope = client.routes.scope_for_inbox("inbox_xyz789")
source_scope = client.routes.scope_for_source("src_abc123")
```

### Create a route

```python
scope = client.routes.scope_for_inbox("inbox_xyz789")

route = client.routes.create(scope, {
    "name": "Lead route",
    "enabled": True,
})
```

### Filters, transformations, and destinations

```python
# Add a filter
client.routes.create_filter(route["id"], {
    "field": "subject",
    "operator": "contains",
    "value": "pricing",
})

# Add AI extraction
client.routes.create_transformation(route["id"], {
    "type": "AI_EXTRACT",
    "outputKey": "lead",
    "config": {
        "fields": [
            {"name": "company", "description": "Company name", "type": "string"},
            {"name": "budget", "description": "Budget amount", "type": "number"},
        ]
    },
})

# Deliver to a webhook
client.routes.create_destination(scope, route["id"], {
    "type": "WEBHOOK",
    "name": "Agent webhook",
    "config": {"url": "https://example.com/webhook"},
})
```

### List, update, delete, and reorder

```python
routes = client.routes.list(scope)

client.routes.update(scope, "rte_abc123", {"name": "Updated route"})

client.routes.delete(scope, "rte_abc123")

client.routes.reorder(scope, ["rte_first", "rte_second", "rte_third"])
```

## Webhooks

Receive real-time event notifications.

```python
from inbounter import WebhookEvent

# Create a webhook
webhook = client.webhooks.create({
    "url": "https://example.com/webhooks/inbounter",
    "events": [WebhookEvent.MESSAGE_RECEIVED, WebhookEvent.MESSAGE_SENT],
})

# List webhooks
webhooks = client.webhooks.list()

# Get, update, delete
webhook = client.webhooks.get("whk_abc123")
client.webhooks.update("whk_abc123", {"events": [WebhookEvent.MESSAGE_RECEIVED]})
client.webhooks.delete("whk_abc123")

# Pause and resume
client.webhooks.pause("whk_abc123", reason="Maintenance window")
client.webhooks.resume("whk_abc123")

# Test a webhook
client.webhooks.test("whk_abc123")
```

## Credits

View and configure usage-based credits for your workspace.

```python
# Get current credit balance and recent ledger entries
credits = client.credits.get(ledger_limit=10)
print(credits)

# Update auto-replenish settings
client.credits.update({
    "autoReplenishEnabled": True,
    "autoReplenishThreshold": 100.0,
    "autoReplenishAmount": 500.0,
})
```

## Configuration

```python
from inbounter import Inbounter

client = Inbounter("your-api-key", options={
    "base_url": "https://api.inbounter.com",  # Custom API base URL
    "timeout": 30,                             # Request timeout in seconds (default: 30)
    "max_retries": 3,                          # Max retries on 429/5xx (default: 3)
    "api_version": "v1",                       # API version (default: "v1")
})

# Set workspace context (required for contacts, conversations, messages, routes, and credits)
client.set_workspace_id("wks_123")

# Enable debug mode to log requests and responses
client.set_debug_mode(True)

# Set custom headers
client.set_header("X-Custom-Header", "value")
client.set_headers({"X-Foo": "bar", "X-Baz": "qux"})
```

## Error Handling

All errors extend `InbounterError` and include structured metadata:

```python
from inbounter import (
    InbounterError,
    APIError,
    AuthenticationError,
    RateLimitError,
    NetworkError,
    TimeoutError,
    ValidationError,
)

try:
    client.emails.send(from_="", to="", subject="", text="")
except AuthenticationError as e:
    print(f"Auth failed: {e}, code={e.code}")
except RateLimitError as e:
    print(f"Rate limited: {e}, status={e.status_code}")
except TimeoutError as e:
    print(f"Request timed out: {e}")
except NetworkError as e:
    print(f"Network error: {e}")
except ValidationError as e:
    print(f"Validation error: {e}, data={e.data}")
except APIError as e:
    print(f"API error: {e}, status={e.status_code}, request_id={e.request_id}")
except InbounterError as e:
    print(f"General error: {e}")
```

| Exception             | Trigger                                |
| --------------------- | -------------------------------------- |
| `InbounterError`      | Base class for all SDK errors          |
| `APIError`            | Non-2xx response from the API          |
| `AuthenticationError` | Invalid or missing API key (401)       |
| `RateLimitError`      | Too many requests (429), after retries |
| `NetworkError`        | Connection or DNS failure              |
| `TimeoutError`        | Request exceeded timeout               |
| `ValidationError`     | Invalid input parameters               |

## Documentation

Full API reference and guides: **[inbounter.com/docs](https://inbounter.com/docs)**

---

## Other SDKs

| Language             | Package                                                            | Install                                    |
| -------------------- | ------------------------------------------------------------------ | ------------------------------------------ |
| Node.js / TypeScript | [`@inbounter/node`](https://www.npmjs.com/package/@inbounter/node) | `npm install @inbounter/node`              |
| Go                   | [`inbounter-go`](https://github.com/inbounter/inbounter-go)        | `go get github.com/inbounter/inbounter-go` |
| Rust                 | [`inbounter`](https://crates.io/crates/inbounter)                  | `cargo add inbounter`                      |
| CLI                  | [`@inbounter/cli`](https://www.npmjs.com/package/@inbounter/cli)   | `npm install -g @inbounter/cli`            |

## Contributing

We welcome contributions!

1. **Fork** the repo and create your branch: `git checkout -b feat/awesome`
2. Add tests and update documentation as needed
3. Open a **PR** against `main`

## License

[MIT](LICENSE)
