Metadata-Version: 2.4
Name: postscale
Version: 1.0.0
Summary: Official Python SDK for the Postscale email API
Project-URL: Homepage, https://postscale.io
Project-URL: Documentation, https://docs.postscale.io
Project-URL: Repository, https://github.com/postscale/postscale-python
Project-URL: Issues, https://github.com/postscale/postscale-python/issues
Author-email: Postscale <support@postscale.io>
License-Expression: MIT
License-File: LICENSE
Keywords: api,email,postscale,sdk,smtp,transactional
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Email
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# Postscale Python SDK

Official Python SDK for the Postscale email API.

## Install

```bash
pip install postscale
```

## Send Email

```python
import os

from postscale import Postscale

postscale = Postscale(os.environ["POSTSCALE_API_KEY"])

result = postscale.emails.send({
    "from": "hello@example.com",
    "to": ["user@example.com"],
    "subject": "Welcome to Postscale",
    "html_body": "<strong>It works.</strong>",
})

if result.error:
    raise RuntimeError(f"{result.error.code}: {result.error.message}")

print(result.data["message_id"])
```

The SDK keeps Postscale API-native field names. Use `html_body`, `text_body`,
`template_id`, and `unsubscribe_scope`; the MVP SDK does not add `html` or
`text` aliases.

## Attachments

```python
from postscale import Postscale, attachment_from_file

postscale = Postscale()

result = postscale.emails.send({
    "from": "hello@example.com",
    "to": ["user@example.com"],
    "subject": "Invoice",
    "text_body": "Attached.",
    "attachments": [
        attachment_from_file("invoice.pdf", "application/pdf"),
    ],
})
```

Attachment helpers enforce the documented limits before sending: 10
attachments, 25 MB per attachment, and 50 MB total decoded attachment payload.

## Webhook Verification

```python
from postscale import verify_webhook_signature

verified = verify_webhook_signature(
    raw_body,
    request.headers.get("X-Postscale-Signature"),
    [current_secret, previous_secret],
)

if not verified.valid:
    return "invalid", 401
```

The verifier requires the Postscale signature format:
`t=<unix_seconds>,v1=<hex_hmac_sha256>`, signed over `<t>.<raw_body>`. It
supports multiple `v1=` signatures and multiple candidate secrets for rotation
windows.
