sente-sdk is a thin Python client for the Sente REST API. Stdlib only (urllib) — no
dependencies. The PyPI distribution is sente-sdk; the import is sente.
Setup
Get an API key from the dashboard (API keys page), or via the CLI withsente login + sente token. The key authenticates your whole
organization — keep it in an environment variable, never in code.
Sente(api_key, base_url=...) — base_url defaults to https://api.sente.run.
Create an identity
An identity is a real, reusable email address onsente.run that your agent owns.
See Identities and registrations.
local_part is taken, create raises a 409 SenteError (on_conflict="error" is
the default); pass on_conflict="suffix" to append a random suffix instead.
Wait for a verification code
Every inbound email is classified server-side (message.annotation:
kind / code / link / confidence), so an agent that signed up somewhere with the
identity’s address can block until the code or link arrives. Stamp since before
triggering the action so you never match an older email:
None on timeout. timeout is in seconds (1–60 per call, default 25). If you
omit since, the server falls back to a 60-second lookback. More patterns in
Receive verification codes.
Send and reply
text and/or html is required. To consume mail continuously,
sente.messages.stream(identity_id) is a generator that long-polls; for production prefer
webhooks (push, not poll).
Register at a third-party app
registrations.register drives a browser through signup at app_url under the identity’s
email and completes email verification from the identity’s own inbox. It returns
RegisterResult(registration, run).
wait_for_run(run_id, timeout=180, poll_ms=2000) polls until the run is completed,
failed, or blocked (timeout in seconds, default 180 — note the TypeScript SDK
takes milliseconds). blocked means human takeover: CAPTCHAs, SMS verification, and
confirm-before-submit all stop the run — a person finishes the step in the interactive
live_view_url, then runs.resume(run_id) continues. On timeout it returns the
last-polled Run. See Runs and human takeover.
Re-authenticate later with registrations.login(registration_id) — same RegisterResult
shape. Vaulted credentials: registrations.get_credentials(registration_id) returns
{ "username", "password", "origin" }; set_credentials(registration_id, username=..., password=...) overwrites the vault only — it does not change the password at the app.
Connect an account you already own
connections.connect is delegated access to an existing account: you supply the
credentials, Sente logs in, vaults them write-only, and keeps the account
re-loginable. See the Connect guide.
Limits, stated plainly:
- SSO-only accounts (Google/Okta sign-in, passkeys) cannot be connected — connect needs a username + password login.
- Credentials are write-only:
registrations.get_credentialson a connected account raises 403CREDENTIALS_WRITE_ONLY. - With a vaulted
totp_seed, re-login clears TOTP 2FA without a human; the server computes the 30-second code — the seed never reaches any model. - Email or SMS code MFA needs a human: the run goes
blockedwithMFA_REQUIRED— enter the code inlive_view_url, thenruns.resume(run.id)— unless the account’s notification email has been repointed to the Sente identity.
connections.revoke(connection_id) disables the connection but keeps the vault (a later
connect re-enables it); connections.delete(connection_id) revokes and purges the
vaulted credentials. Connected accounts are also reachable through the registrations
methods (get_session, login, export_session) by the returned id.
Open or export a logged-in session
get_session returns a remote browser already logged into the account, as a CDP URL you
drive yourself. If the login has gone stale, Sente re-logs-in first (vaulted credentials +
a fresh code from the identity’s inbox) — healed is True when that happened, and a cold
call can take a minute.
close_session when done. get_session accepts verify= (default True; False skips
the freshness check), max_stale_sec=, and timeout_ms= (default 240000). If re-login
can’t recover, it raises SenteError (inspect the body’s run).
open_session(registration_id) is the raw primitive with no freshness check.
To use your own browser stack instead, export the logged-in state (cookies +
localStorage) as a Playwright storage_state dict:
Webhooks
Register your endpoint and Sente pushes events to it with retries. Thesecret is only
present on the Webhook returned at register time — store it.
message.received payloads are thin notifications with no body — fetch content with
sente.messages.get(message_id). Run lifecycle events (run.blocked, run.completed,
run.failed) are also available. Every delivery carries the secret in the
x-sente-secret header — verify it with hmac.compare_digest. Full payloads and
verification code in the Webhooks guide.
Errors
Non-2xx responses raiseSenteError with status (HTTP status) and body (the parsed
error payload, if any).
Method reference
Endpoint-level details are in the API reference. TypeScript
sibling: @sente-labs/sdk. CLI: sente.
