> ## Documentation Index
> Fetch the complete documentation index at: https://docs.robase.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> robase — sync + async clients built on httpx.

## Install

```bash theme={null}
pip install robase
```

Python ≥ 3.9. Dependencies: `httpx`.

## Initialize

```python theme={null}
import os
from robase import Robase

pm = Robase(os.environ["ROBASE_API_KEY"])
```

Use as a context manager to close the connection pool on exit:

```python theme={null}
with Robase(os.environ["ROBASE_API_KEY"]) as pm:
    pm.sms.send(to="+2348012345678", from_="SHUTTLERS", body="hi")
```

## Surface

```python theme={null}
pm.emails.send(from_=..., to=[...], subject=..., html=..., text=...)
pm.emails.batch(emails=[...])
pm.emails.get(email_id)
pm.emails.cancel(email_id)

pm.sms.send(to=..., from_=..., body=...)
pm.sms.batch(from_=..., messages=[...])
pm.sms.get(sms_id)
pm.sms.list(limit=..., cursor=...)
pm.sms.analytics(days=...)

pm.sms_templates.create(name=..., body=...)
pm.sms_templates.list()
pm.sms_templates.get(template_id)
pm.sms_templates.update(template_id, body=...)
pm.sms_templates.remove(template_id)
pm.sms_templates.render(template_id, variables={...})

pm.sender_ids.create(sender_id=..., countries=[...], ...)
pm.sender_ids.list()
pm.sender_ids.update(sid_id, notes=...)

pm.domains.create(name=...)
pm.domains.list()
pm.domains.verify(domain_id)

pm.webhooks.create(url=..., events=[...])
pm.webhooks.list()

pm.suppressions.add(email=..., reason=...)
pm.suppressions.remove(email)
```

## Note on `from_`

Python reserves `from` as a keyword, so the SDK uses `from_` everywhere an email sender or SMS sender is needed:

```python theme={null}
pm.sms.send(to="+2348012345678", from_="SHUTTLERS", body="hi")
pm.emails.send(from_="a@mail.shuttlers.ng", to=["b@example.com"], subject="hi")
```

## Errors

```python theme={null}
from robase import Robase, RobaseError

try:
    pm.sms.send(to="+2348012345678", from_="SHUTTLERS", body="hi")
except RobaseError as e:
    print(e.status, e.type, e.message)
    if e.type == "phone_invalid":
        ...
```

## Async

```python theme={null}
import asyncio
from robase import AsyncRobase

async def main():
    pm = AsyncRobase(os.environ["ROBASE_API_KEY"])
    res = await pm.request("POST", "/sms", body={"to": "+234...", "from": "X", "body": "hi"})
    print(res)
    await pm.aclose()

asyncio.run(main())
```

The async client exposes the same surface; service classes are on the sync `Robase` today with async parity on the roadmap.

## Webhook verification

```python theme={null}
from robase import verify_webhook

ok = verify_webhook(raw_body, request.headers["Robase-Signature"], webhook_secret)
```
