Metadata-Version: 2.4
Name: mailsocket
Version: 0.1.0
Summary: Official Python client for the mailsocket REST API — one line to wait for an OTP.
Author: mailsocket
License-Expression: MIT
Project-URL: Homepage, https://mailsocket.app
Project-URL: Documentation, https://dash.mailsocket.app/docs
Keywords: mailsocket,email,otp,inbox,magic-link
Classifier: Intended Audience :: Developers
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
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Dynamic: license-file

# mailsocket — Python SDK

The official Python client for the [mailsocket](https://mailsocket.app) v1 REST API.
Ephemeral email inboxes, message retrieval, and — the whole point — **wait for the OTP in one line**.

```python
from mailsocket import Client

client = Client("ms_live_...")            # your API key from the dashboard
inbox = client.create_inbox(label="signup")  # -> {"id": "inbox_...", "address": "..."}

# hand inbox["address"] to whatever form sends the code, then:
result = client.wait_for_otp(inbox["id"])     # blocks up to 60s
print(result.otp)         # "123456"
print(result.confidence)  # 0.95
```

No polling loop. No regex over the email body. `wait_for_otp` long-polls the
server (which already knows how to extract the code) and hands back a
deterministic result with a confidence score.

## Install

```bash
pip install mailsocket
```

Zero runtime dependencies — pure standard library (`urllib`). Python 3.9+.

## API

```python
client = Client(api_key, base_url="https://dash.mailsocket.app/api/v1")
```

| Method | Returns |
| --- | --- |
| `create_inbox(label=None)` | `dict` — the inbox (`id`, `address`, ...) |
| `list_inboxes(limit=None, cursor=None)` | `Page` — `.data` list + `.next_cursor` / `.has_more` |
| `get_inbox(inbox_id)` | `dict` — inbox plus `message_count_month`, `webhook_configured` |
| `delete_inbox(inbox_id)` | `None` |
| `list_messages(inbox_id, has_otp=None, subject_contains=None, sender=None, ...)` | `Page` — `sender` maps to the API's `from` filter |
| `get_latest(inbox_id)` | `dict` — newest message |
| `get_message(message_id)` | `dict` — full message |

### The moat

```python
result = client.wait_for_otp(inbox_id, *, timeout=60, min_confidence=0.0, since=0) -> WaitResult
result = client.wait_for_link(inbox_id, *, timeout=60, since=0)                       -> WaitResult
result = client.wait(inbox_id, *, timeout=60, min_confidence=0.0, since=0)            -> WaitResult  # otp OR link
```

`WaitResult` exposes `.otp`, `.confidence`, `.magic_link` (and `.link` as an
alias), plus the full `.message` dict. `str(result)` is the OTP (or the link).

Semantics:

- Each HTTP call blocks server-side for up to 25s (`timeout` is clamped to
  `[1, 25]`); the SDK re-calls until the **overall** `timeout` (seconds,
  default 60) elapses.
- `200` → the matching message. `204` → nothing yet, re-call immediately.
- `429` → honours `Retry-After` and retries within the deadline. `404` → raises.
- On overall deadline → `WaitTimeout`.

## Errors

`MailsocketError` (base) with subclasses:

- `AuthError` — HTTP 401 (bad/missing key).
- `NotFound` — HTTP 404.
- `RateLimited` — HTTP 429, carries `.subcode` (`rate_limited`,
  `too_many_wait_requests`, `wait_capacity`) and `.retry_after`.
- `WaitTimeout` — no matching message within the overall deadline.

## Develop

```bash
cd sdks/python
python -m pytest -q
```

Tests are fully offline — they monkeypatch `urllib.request.urlopen` with a
scripted responder, so nothing touches the live API.
