@sente-labs/sdk is a thin typed client for the Sente REST API. Zero runtime dependencies
(it uses global fetch). Types ship with the package — no @types/* install.
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.
new Sente(opts) takes { apiKey, baseUrl?, fetch? }. baseUrl defaults to
https://api.sente.run; the fetch override is handy for tests.
Create an identity
An identity is a real, reusable email address onsente.run that your agent owns.
See Identities and registrations.
localPart is taken, create throws a 409 (onConflict: "error" is the default);
pass onConflict: "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:
null on timeout. timeout is in seconds (1–60, 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, messages.stream(identityId)
is an async 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 appUrl under the identity’s
email and completes email verification from the identity’s own inbox. It returns the durable
account (registration) and its first run.
waitForRun(id, { timeout?, pollMs? }) polls until the run is completed, failed, or
blocked (timeout in milliseconds, default 180000). blocked means human takeover:
CAPTCHAs, SMS verification, and confirm-before-submit all stop the run — a person finishes
the step in the interactive liveViewUrl, then runs.resume(id) continues. On timeout it
returns the last-polled run (status still running / awaiting_verification). See
Runs and human takeover.
Re-authenticate later with registrations.login(registrationId) — same
{ registration, run } shape. Vaulted credentials: registrations.getCredentials(id) /
setCredentials(id, { username?, password? }) (setCredentials 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.getCredentialson a connected account returns 403CREDENTIALS_WRITE_ONLY. - With a vaulted
totpSeed, 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 inliveViewUrl, thenruns.resume(run.id)— unless the account’s notification email has been repointed to the Sente identity.
connections.revoke(id) disables the connection but keeps the vault (a later connect
re-enables it); connections.delete(id) revokes and purges the vaulted credentials.
Connected accounts are also reachable through the registrations methods (getSession,
login, exportSession) by the returned id.
Open or export a logged-in session
getSession 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: true tells you that happened, and a cold
call can take a minute.
closeSession when done. getSession accepts { verify?, maxStaleSec?, timeoutMs? }; verify: false
skips the freshness check (raw). openSession(id) is the raw primitive with no freshness
check. If re-login can’t recover, getSession throws a SenteError whose body is the
failed/blocked run.
To use your own browser stack instead, export the logged-in state (cookies +
localStorage) as a Playwright storageState:
Webhooks
Register your endpoint and Sente pushes events to it with retries. Thesecret is returned
once — store it.
message.received payloads are thin notifications with no body — fetch content with
messages.get(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 a timing-safe compare. Full payloads and verification code in the
Webhooks guide.
Errors
Non-2xx responses throwSenteError with status (HTTP status) and body (response body).
Method reference
Endpoint-level details are in the API reference. Python sibling:
sente-sdk. CLI: sente.
