Metadata-Version: 2.4
Name: robasedev
Version: 0.1.0
Summary: Official Robase.dev Python SDK — transactional email for Nigerian developers.
Project-URL: Homepage, https://robase.dev
Project-URL: Repository, https://github.com/robase-dev/robase
Author: Robase.dev
License: MIT
Keywords: email,nigeria,robase,ses,transactional
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Email
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# robasedev (Python)

Official Python SDK for [Robase.dev](https://robase.dev) — transactional email for Nigerian developers, API-compatible with Resend.

## Install

```bash
pip install robasedev
```

Python 3.9+.

## Quickstart

```python
import os
from robasedev import Robase

client = Robase(api_key=os.environ["ROBASE_API_KEY"])

email = client.emails.send(
    from_="hello@acme.com",
    to="customer@example.com",
    subject="Welcome",
    html="<p>Thanks!</p>",
)
print(email["id"])
```

## Configuration

```python
client = Robase(
    api_key="sk_live_...",
    base_url="https://api.robase.dev",  # override for self-hosted
    timeout=30.0,
    max_retries=2,                     # 5xx / 429 / transport errors
)
```

Use as a context manager to close the underlying httpx client automatically:

```python
with Robase(api_key=...) as client:
    client.emails.send(...)
```

## Sending

```python
client.emails.send(
    from_="hello@acme.com",
    to=["a@ex.com", "b@ex.com"],
    cc="c@ex.com",
    subject="Hi",
    html="<p>…</p>",
    text="fallback",
    reply_to="support@acme.com",
    headers={"X-Campaign": "spring"},
    tags={"campaign": "spring"},
    tracking={"opens": True, "clicks": True},
    attachments=[
        {"filename": "invoice.pdf", "content": b64_bytes, "content_type": "application/pdf"}
    ],
    scheduled_at="2026-05-01T09:00:00Z",
    idempotency_key="welcome-user-42",
)
```

### Batch

```python
out = client.emails.batch([
    {"from_": "a@ex.com", "to": "x@ex.com", "subject": "one", "text": "1"},
    {"from_": "a@ex.com", "to": "y@ex.com", "subject": "two", "text": "2"},
])
for item in out:
    if "error" in item:
        print("failed:", item["error"]["type"])
    else:
        print("queued:", item["id"])
```

### Retrieve + cancel

```python
client.emails.get("em_xyz")
client.emails.cancel("em_xyz")   # raises ValidationError if already sent
```

## Errors

| Class | HTTP |
|---|---|
| `AuthenticationError` | 401 |
| `NotFoundError` | 404 |
| `ValidationError` | 422 — inspect `err.type` to branch |
| `RateLimitError` | 429 (`err.retry_after` in seconds) |
| `ServerError` | 5xx |

The client retries `ServerError`, `RateLimitError`, and transport failures with exponential backoff + jitter.

## Webhook verification

```python
from robasedev import verify_webhook_signature

@app.post("/webhooks/robase")
async def hook(request):
    body = await request.body()  # raw bytes
    verify_webhook_signature(
        payload=body,
        header=request.headers["Robase-Signature"],
        secret=os.environ["ROBASE_WEBHOOK_SECRET"],
    )
    event = json.loads(body)
    ...
```

## License

MIT.
