Metadata-Version: 2.4
Name: sente-sdk
Version: 0.7.0
Summary: Managed email identities for AI agents — the Python client for Sente (import sente).
Author: Sente Labs
License: UNLICENSED
Project-URL: Homepage, https://sente.run
Project-URL: Documentation, https://sente.run/skill.md
Keywords: sente,ai-agents,email,identity,agentaccount
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# sente-sdk

Working accounts for AI agents — the Python client for [Sente](https://sente.run).
Create accounts on third-party apps (or connect ones you already own), let Sente keep the login
alive, and get a **browser that's already signed in** — or a Playwright `storageState` for your own
stack — in one call. Underneath, every agent gets a real email identity it owns, with verification
emails parsed on arrival.

Dependency-free (stdlib only). The PyPI distribution is `sente-sdk`; the import is `sente`.

```bash
pip install sente-sdk
```

## Quickstart: accounts and sessions

```python
from sente import Sente

sente = Sente(api_key="sk_sente_...")              # get a key with the `sente` CLI: `sente login`

idt = sente.identities.create(name="support-bot")  # -> support-bot@sente.run, the agent's identity

# Create an account at an app under that identity. Email verification completes
# from the identity's own inbox — the run controller applies the code for you:
reg = sente.registrations.register(identity_id=idt.id, app_url="https://app.example.com/signup")
sente.runs.wait_for_run(reg.run.id)

# ...or connect an account you already own (credentials vaulted write-only;
# with a totp_seed, TOTP re-login needs no human):
# conn = sente.connections.connect(identity_id=idt.id, app_url="https://app.example.com",
#                                  username="me@co.example.com", password="...", totp_seed="...")

# When the agent needs to act: a remote browser, already signed in. If the login
# went stale, Sente re-logs-in first (vaulted creds + a fresh code from the inbox):
s = sente.registrations.get_session(reg.registration.id)
browser = playwright.chromium.connect_over_cdp(s["cdpUrl"])  # already logged in
# ...do the work...
sente.registrations.close_session(reg.registration.id)

# Prefer your own browser stack? Export the logged-in state instead:
state = sente.registrations.export_session(reg.registration.id)  # Playwright storageState dict
```

Honest edges: some sites fraud-block automated signups, and CAPTCHAs are a hard stop — the run goes
`blocked` with a `live_view_url` for a human takeover (`runs.resume` after). Pass
`confirm_before_submit=True` to keep a human clicking the final signup submit.

## The identity underneath: send & receive email

```python
sente.messages.send(idt.id, to="user@example.com", subject="hi", text="from your agent")

# React to inbound mail (a long-running consumer; for production prefer a webhook):
for msg in sente.messages.stream(idt.id):
    if msg.direction != "outbound":
        body = (msg.parsed or {}).get("text", "")
        # ... handle msg.from_addr / msg.subject / body
```

Waiting for a verification email yourself (e.g. an app you drive with your own automation sent a
code to `idt.email`)? Stamp `since` **before** triggering the action, then block for the extracted
code or link:

```python
from datetime import datetime, timezone

since = datetime.now(timezone.utc).isoformat()
# ... trigger the app's "send code" action ...
r = sente.messages.wait_for_otp(idt.id, since=since, timeout=60)  # -> OtpResult(code, message) | None
if r:
    print(r.code)  # magic links: wait_for_magic_link(...) -> MagicLinkResult(link, message) | None
```

Webhook handlers receive a thin event `{ "type": "message.received", "message": { "id": ... } }` with
an `x-sente-secret` header; fetch the full message with `sente.messages.get(message_id)`.

Full onboarding playbook (CLI + framework templates): <https://sente.run/skill.md>.
