primitif-mail 0.5.1
Python SDK for Mail — ephemeral email infrastructure for AI agents
Requires Python: >=3.11
Keywords: agents, ai, cloudflare, email, ephemeral

# primitif-mail

Python SDK for [Mail](https://primitif.ai/mail). Ephemeral email for AI agents.

An agent gets a real email address. It sends, receives, and replies. When the job's done, the mailbox self-destructs.

## Install

```bash
pip install primitif-mail
```

## Setup

Sign in at [dash.primitif.ai](https://dash.primitif.ai) to get your API key.

```bash
export MAIL_API_KEY=ck_live_...
```

The SDK picks this up automatically. No need to pass credentials in code.

## Quick start

### Create a mailbox

```python
from mail import Mail

mail = Mail()  # uses MAIL_API_KEY from env
mb = mail.create_mailbox(name="support-bot", ttl=3600)
print(mb.address)  # support-bot-a1b2@mail.primitif.ai
print(mb.token)    # mb_tok_... (one-time, save it)
```

### Send, receive, reply

```python
from mail import Mail

mailbox = Mail(token="mb_tok_...")

# Send
mailbox.send(to="alice@example.com", subject="Hi", body="Hello from my agent!")

# Check inbox
for msg in mailbox.inbox(unread_only=True):
    print(msg.from_address, msg.subject)

# Read and reply
full = mailbox.read(msg.id)
print(full.body_text)
mailbox.reply(msg.id, body="Got it, thanks!")

# Done, nuke the mailbox
mailbox.destroy()
```

Works as a context manager too:

```python
with Mail(token="mb_tok_...") as mailbox:
    mailbox.send(to="bob@example.com", subject="Hey", body="Yo.")
# client cleaned up automatically
```

## Reading email

The inbox returns message summaries. Call `read()` to get the full body (and mark it as read).

```python
inbox = mailbox.inbox(unread_only=True)

for msg in inbox:
    print(msg.from_address, msg.subject, msg.snippet)

    full = mailbox.read(msg.id)
    print(full.body_text)

    mailbox.reply(msg.id, body="Thanks!")
```

## Webhooks

Get notified when emails arrive instead of polling:

```python
wh = mailbox.set_webhook("https://example.com/hook")
print(wh.secret)  # whsec_... (save this for signature verification)
```

Verify incoming webhooks (HMAC-SHA256, same pattern as Stripe/GitHub):

```python
from mail import verify_webhook, InvalidSignature

try:
    payload = verify_webhook(
        secret="whsec_...",
        signature=request.headers["X-Mail-Signature"],
        timestamp=request.headers["X-Mail-Timestamp"],
        body=request.body,
    )
    print(payload["event"], payload["data"])
except InvalidSignature:
    return 401
```


## Threads and attachments

```python
# Threads
for t in mailbox.threads():
    print(t.subject, f"({t.message_count} messages)")

thread = mailbox.thread(t.id)
for msg in thread.messages:
    print(msg.from_address, msg.body_text)

# Attachments
for att in mailbox.list_attachments(msg.id):
    print(att.filename, att.size_bytes)
    data = mailbox.download_attachment(msg.id, att.id)
```

## Managing mailboxes

```python
mail = Mail()  # uses MAIL_API_KEY from env

mb = mail.create_mailbox(name="task-runner", ttl=7200)
print(mb.address, mb.token)

for mb in mail.list_mailboxes():
    print(mb.id, mb.address, mb.expires_at)

mail.delete_mailbox(mb.id)
```

## Pagination

All list endpoints return a `PaginatedList` that can be iterated directly:

```python
page = mailbox.inbox(limit=10)
for msg in page:
    print(msg.subject)

if page.has_more:
    next_page = mailbox.inbox(limit=10, cursor=page.cursor)
```

## Error handling

```python
from mail import (
    MailError,           # base
    MailAuthError,       # 401, bad or expired token
    MailNotFoundError,   # 404
    MailValidationError, # 422, bad request body
    MailRateLimitError,  # 429, retry_after available
    MailServerError,     # 5xx
)

try:
    mailbox.send(to="x", subject="", body="")
except MailRateLimitError as e:
    print(f"Slow down, retry in {e.retry_after}s")
except MailAuthError:
    print("Token expired or invalid")
```

Retries on 429 and 5xx are built in (3 attempts with backoff).