Metadata-Version: 2.4
Name: primitif
Version: 0.0.5
Summary: Primitif Python SDK — Mail and Approval APIs for AI agents
Project-URL: Homepage, https://primitif.ai
Project-URL: Repository, https://github.com/PrimitifAI/primitif-python
Project-URL: Documentation, https://primitif.ai/docs
Author-email: Primitif <hello@primitif.ai>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
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: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.0.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: respx>=0.22.0; extra == 'dev'
Description-Content-Type: text/markdown

# primitif

Python SDK for [Primitif](https://primitif.ai) — give your AI agents email and human approval.

```
pip install primitif
```

## Setup

Set your API key as an env var and you're good to go:

```bash
export PRIMITIF_API_KEY="pk_live_..."
```

## Mail

Create a temporary mailbox, send and receive emails, then throw it away.

```python
from primitif import Primitif

with Primitif() as p:
    mb = p.mail.create_mailbox(name="support-agent", ttl=3600)
    print(mb.address)  # support-agent-x7k2@mail.primitif.ai

    # send an email
    mb.send(to="user@example.com", subject="Hey", body_text="Following up on your ticket.")

    # check inbox
    for msg in mb.inbox():
        email = mb.read(msg.id)
        print(email.from_address, email.subject, email.body_text)
        # reply to it
        mb.reply(msg.id, body_text="Thanks, we're on it.")

    # done — delete the mailbox
    mb.delete()
```

If you already have a mailbox token, use it directly:

```python
from primitif import Mailbox

with Mailbox(token="mb_tok_...") as mb:
    mb.send(to="someone@example.com", subject="Hi", body_text="Hello")
```

Tokens can be persisted for crash recovery:

```python
mb = p.mail.create_mailbox(name="agent")
save_to_db(mb.token)  # persist the token

# later, reconnect:
mb = Mailbox(token=load_from_db())
```

### Send with human approval

Emails can be held for human approval before sending:

```python
result = mb.send(
    to="ceo@bigclient.com",
    subject="Contract proposal",
    body_text="Please find attached...",
    require_approval=True,
)
print(result.status)        # "held"
print(result.approval_url)  # link for a human to approve/reject
```

### Pagination

All list methods return a `PaginatedList` that you can iterate directly,
or use `auto_paging_iter()` to fetch all pages automatically:

```python
for msg in mb.inbox().auto_paging_iter():
    print(msg.subject)
```

## Approval

Pause your agent and wait for a human to say "go ahead."

```python
from primitif import Primitif

with Primitif() as p:
    request = p.approval.create_request(
        action="Delete user account #1234",
        context={"user_id": 1234, "reason": "requested by user"},
    )

    print(request.status)        # "pending"
    print(request.approval_url)  # send this to a human

    # later — check if they approved
    updated = p.approval.get_request(request.id)
    print(updated.status)  # "approved" or "rejected"
```

### With a webhook callback

Instead of polling, get notified when a decision is made:

```python
p.approval.create_request(
    action="Send contract to Acme Corp",
    context={"amount": 45000},
    callback_url="https://myapp.com/webhooks/approval",
)
```

## Webhooks

Verify incoming webhook signatures:

```python
from primitif import verify_webhook, InvalidSignature

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

## Error handling

All errors inherit from `PrimitifError`, so you can catch broadly or narrowly:

```python
from primitif import PrimitifError, AuthError, NetworkError
from primitif.mail import MailRateLimitError

try:
    mb.send(to="user@test.com", subject="Hi", body_text="Hello")
except MailRateLimitError as e:
    print(f"Slow down — retry after {e.retry_after}s")
except NetworkError:
    print("Network issue — timeout or DNS failure")
except AuthError:
    print("Bad credentials")
except PrimitifError:
    print("Something went wrong")
```

## License

MIT
