Metadata-Version: 2.4
Name: ravimail
Version: 1.0.0
Summary: Official Python SDK for the RaviMail REST API v1 (api.ravimail.com.br).
Project-URL: Homepage, https://ravimail.com.br
Project-URL: Documentation, https://ravimail.com.br/docs
Project-URL: Repository, https://github.com/ravisystems/ravimail-python
Project-URL: Issues, https://github.com/ravisystems/ravimail-python/issues
Project-URL: Changelog, https://ravimail.com.br/docs/changelog
Author-email: Ravi Systems LTDA <dev@ravimail.com.br>
License: MIT
License-File: LICENSE
Keywords: email,marketing,ravimail,smtp,transactional,webhooks
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.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Email
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: requests>=2.31
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: requests-mock>=1.11; extra == 'dev'
Requires-Dist: ruff>=0.2; extra == 'dev'
Description-Content-Type: text/markdown

# RaviMail Python SDK

[![PyPI version](https://img.shields.io/pypi/v/ravimail?color=4f46e5)](https://pypi.org/project/ravimail/)
[![PyPI downloads](https://img.shields.io/pypi/dm/ravimail?color=10b981)](https://pypi.org/project/ravimail/)
[![Python versions](https://img.shields.io/pypi/pyversions/ravimail?color=3776ab)](https://pypi.org/project/ravimail/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Official Python client for the [RaviMail](https://ravimail.com.br) REST API v1.
Type-hinted, minimal dependencies (`requests` only).

> **Why this exists.** RaviMail is a Brazilian email infrastructure platform with
> 155 documented REST endpoints. This SDK gives you Pythonic access to all of them,
> with proper exceptions and a familiar `Session`-based HTTP layer.

## Installation

```bash
pip install ravimail
```

**Requirements:** Python 3.9+.

## Quickstart — Hello World

```python
from ravimail import Client

rm = Client("rvm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")

resp = rm.transactional.send(
    to="recipient@example.com",
    from_="you@yourdomain.com",  # `from` is reserved in Python — use from_
    subject="Welcome to the show",
    html="<h1>Glad you're here</h1>",
)

print(resp["data"]["message_id"])  # msg_xxxxxxxxxxxx
```

## Test mode (no billing, no real send)

Any token prefixed with `rvm_test_*` enables sandbox mode automatically.

```python
test = Client("rvm_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
test.transactional.send(to="bounce@simulator.ravimail.com.br", from_="...", subject="...", html="...")
```

See the [Mailbox Simulator](https://ravimail.com.br/docs#api-sandbox) for
deterministic test scenarios.

## Available resources

All 23 resources mirror the REST API exactly.

| Resource | Coverage |
|---|---|
| `rm.me` | Token info + quotas |
| `rm.transactional` | `send` / `batch` / `list` / `get` |
| `rm.contacts` | CRUD + `add_tags` / `remove_tags` / `suppress` / `import_` |
| `rm.lists` | CRUD |
| `rm.templates` | CRUD + `render` + versioning |
| `rm.campaigns` | CRUD + `start` / `pause` / `resume` / `cancel` / `duplicate` + A/B |
| `rm.domains` | Add / verify / dns_check / upgrade_to_panel |
| `rm.files` | Upload (path or file handle) / list / get / delete |
| `rm.webhooks` | Endpoints + deliveries + `rotate_secret` |
| `rm.suppression` | `list` / `suppress` / `unsuppress` |
| `rm.analytics` | summary / timeseries / by_domain/ip/node/isp/device/country / heatmap |
| `rm.events` | `list` + `stream_url()` |
| `rm.verification` | `email` + `batch` |
| `rm.segments` | CRUD + `compute` |
| `rm.exports` | Async CSV jobs |
| `rm.inbound_routes` | Inbound parsing → forward webhook |
| `rm.drips` | CRUD + steps + `subscribe` / `unsubscribe` |
| `rm.subaccounts` | Multi-tenant CRUD + tokens |
| `rm.reputation` | summary / domains / ips / nodes |
| `rm.custom_fields` | List / create / delete |
| `rm.reports` | Scheduled reports CRUD |
| `rm.account` | Balance / usage / packages / top_up / payments |
| `rm.smtp_relay` | SMTP credentials + log |

## Idempotency keys

For any write you want to retry safely:

```python
rm.transactional.send(
    to="user@example.com",
    from_="you@yourdomain.com",
    subject="Order confirmation",
    html="<p>...</p>",
    idempotency_key="order-1024-confirmation",
)
```

The API guarantees the second call with the same key returns the original
response, byte for byte.

## Webhook signature verification

Standalone function — no Client instance needed:

```python
from ravimail import verify_signature

def handle_webhook(request):
    signature = request.headers.get("X-Ravimail-Signature", "")
    raw_body  = request.get_data()  # Flask: raw bytes; Django: request.body
    secret    = os.environ["RAVIMAIL_WEBHOOK_SECRET"]

    if not verify_signature(signature, raw_body, secret):
        return "", 401

    # ... process payload
    return "", 200
```

Uses `hmac.compare_digest` — constant-time comparison.

## Error handling

```python
from ravimail import (
    Client,
    ApiError,
    AuthenticationError,
    ValidationError,
    RateLimitError,
)

try:
    rm.transactional.send(to="...", from_="...", subject="...", html="...")
except ValidationError as e:
    # 422 — bad payload; e.detail has the field-level info
    print(e.detail)
except RateLimitError as e:
    # 429 — back off
    time.sleep(e.retry_after_seconds or 5)
except AuthenticationError:
    # 401 — token invalid/revoked
    ...
except ApiError as e:
    # any other 4xx/5xx; e.status has the HTTP code
    ...
```

## Configuration

```python
import requests

rm = Client(
    "rvm_live_...",
    base_url="https://api.ravimail.com.br",  # override for staging
    timeout=30.0,                             # seconds
    session=requests.Session(),               # reuse for connection pooling
)
```

## SSE event stream

Install `sseclient-py`:

```bash
pip install sseclient-py
```

```python
import requests
from sseclient import SSEClient

res = requests.get(
    rm.events.stream_url(),
    headers={"Authorization": f"Bearer {token}", "Accept": "text/event-stream"},
    stream=True,
)
for event in SSEClient(res).events():
    print(event.data)
```

## Links

- 📚 [Full docs](https://ravimail.com.br/docs)
- 🔌 [API reference](https://ravimail.com.br/api)
- 📝 [Changelog](https://ravimail.com.br/docs/changelog)
- 💡 [Conceitos](https://ravimail.com.br/docs/conceitos)
- 🐛 [Issues](https://github.com/ravisystems/ravimail-python/issues)
- 🐘 [PHP SDK](https://github.com/ravisystems/ravimail-php)
- 🟢 [Node SDK](https://github.com/ravisystems/ravimail-node)

## License

[MIT](LICENSE) © Ravi Systems LTDA
