Metadata-Version: 2.4
Name: postbox-sdk
Version: 0.1.2
Summary: Official Postbox SDK for Python: mailbox infrastructure for developers.
Project-URL: Homepage, https://postboxapp.cloud
Project-URL: Repository, https://github.com/postboxapp/postbox
Author: Postbox
License: MIT
License-File: LICENSE
Keywords: api,email,mailbox,postbox,sdk,smtp,transactional-email,webhooks
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Postbox Python SDK

Official Python SDK for [Postbox](https://postboxapp.cloud), mailbox
infrastructure for developers. Provision real mailboxes over REST, send and
receive mail, stream events, and verify webhooks.

Retries, idempotency, event streaming, and webhook verification are built in and
work the same across every Postbox SDK.

## Install

```bash
pip install postbox-sdk
```

Requires Python 3.11+. Zero runtime dependencies (standard library only).

## Quickstart

```python
from postbox import Postbox

pb = Postbox("pb_live_…")

# Send a message (POST → auto idempotency key, retried safely on network blips)
pb.messages.send({
    "from": "hello@yourdomain.dev",
    "to": [{"address": "user@example.com"}],
    "subject": "Welcome",
    "bodyText": "Thanks for signing up.",
})

# Fetch a mailbox
mailbox = pb.mailboxes.get("mbx_123")

# List (query params are forwarded as-is)
pb.mailboxes.list(query={"domainId": "dom_1"})
```

### Configuration

```python
pb = Postbox(
    "pb_live_…",
    base_url="https://api.postboxapp.cloud/v1",  # override for self-host / staging
    project_id="proj_…",                          # sets X-Project-Id on every request
    timeout=30.0,                                  # per-attempt timeout (seconds)
    max_retries=2,                                 # extra attempts after the first (≤3 total)
    default_headers={"X-Trace": "…"},
    transport=my_transport,                        # inject a custom HttpTransport / proxy
)
```

A missing/blank `api_key` raises `ValueError` at construction, never on the first request.

## Streaming events (SSE)

```python
for event in pb.events.stream():
    print(event.event, event.data)
```

The stream auto-reconnects on drop and resumes from the last seen event id
(`Last-Event-ID`). Break out of the loop (or pass a `threading.Event` as
`cancel=`) to stop.

## Pagination

The API is page-based (`{items, totalPages}`). `Paginator` walks pages lazily:

```python
from postbox import Page, Paginator

page = Page.from_dict(raw_response)
for item in Paginator(page, fetch_next):  # yields across every page
    ...
```

## Webhook verification

```python
from postbox import verify_webhook, SignatureError

try:
    event = verify_webhook(
        raw_body,                      # the RAW request body (str or bytes)
        request.headers["X-Postbox-Signature"],
        request.headers["X-Postbox-Timestamp"],
        webhook_secret,
    )
except SignatureError:
    return 400  # bad signature, stale timestamp, or malformed body
```

Verification is timing-safe (HMAC-SHA256 over `f"{timestamp}.{raw_body}"`),
replay-protected (±300s tolerance), and returns the parsed event, never a
boolean you might forget to check.

## Errors

Every failure is a typed subclass of `PostboxError`, carrying `status`, `code`,
`request_id`, and (for validation) `issues`:

| Type | Status |
|---|---|
| `AuthenticationError` | 401 |
| `PermissionError` | 403 |
| `NotFoundError` | 404 |
| `ConflictError` | 409 |
| `ValidationError` | 400 / 422 |
| `RateLimitError` | 429 (carries `retry_after`) |
| `ServerError` | 5xx (except 501) |
| `NotImplementedError` | 501 |
| `TimeoutError` | per-attempt timeout |
| `NetworkError` | DNS / connection / TLS / reset |
| `SignatureError` | webhook verification |

These error types are the same across every Postbox SDK.

## License

MIT
