@sente-labs/cli installs the sente binary. It targets the same REST API as the SDKs
(https://api.sente.run/v1) and is built for terminals, shell scripts, and agent shells. Requires
Node.js 18+.
sente login opens a browser (Auth0), mints an API key, and stores it in ~/.sente/credentials
(mode 0600). The first sign-in creates your organization and free tier.
Two conventions used throughout:
--jsonis a global flag. Put it before the subcommand (sente --json connect ...) and every command emits machine-readable JSON on stdout instead of human text.<ref>is an identity id (idt_…), its full email, or just the local-part. Registration and run ids (reg_…,run_…) are always passed literally.
Sign in
Create an account
Neither command needs--identity. Omit it and Sente provisions the backing email address from the
app’s hostname; sente identity list shows what you got.
Connect one you already own
--username and --password are required. The command blocks until the run settles, printing
status lines to stderr, then prints the outcome:
--totp-seed vaults the authenticator secret (the base32 string behind a QR code’s “can’t scan?”
link, or a full otpauth:// URI) so future re-logins clear TOTP 2FA without a human — the server
computes the 30-second code, and the seed never reaches any model.
SSO-only accounts cannot be connected — Google/Okta sign-in and passkeys have no username and
password to vault. On a connected account,
sente credentials returns
403 CREDENTIALS_WRITE_ONLY: Sente will not read your password back to you. Email or SMS code MFA
blocks the run with MFA_REQUIRED — the code went to your inbox, so a person enters it in the
live view and then runs sente run resume <runId>.Register a new one
--identity <ref>, --username <u>, --password <p> (both generated server-side if
omitted), --confirm-before-submit, --autonomous, --no-wait.
Wait for a verification code
For signups you drive by hand. Accounts created throughconnect / register need none of
this — the run completes verification itself.
--otp or --magic-link, the only thing on stdout is the extracted code or link, so $(…)
captures cleanly. They are mutually exclusive. On timeout it writes Timed out — no message. to
stderr and nothing to stdout, so test for an empty variable rather than the exit code — a timeout
still exits 0.
--forward POSTs each inbound message in the exact webhook envelope, with x-sente-secret
taken from SENTE_WEBHOOK_SECRET if it’s set — the way to develop a webhook handler with no public
URL. Like the real webhook, the envelope carries no mail body; fetch it by id.
Get a logged-in session
$(…) stays clean. If the login went stale the CLI re-logs in first (vaulted credentials plus a
fresh code from the account’s inbox), printing Login expired — logging back in…, then hands back
a session it just confirmed. --no-verify skips the freshness check.
To use your own browser stack instead:
Handle a blocked run
BLOCKED_TIMEOUT. To hear about blocks without watching a terminal:
watch desktop-notifies on macOS (Notification Center) and rings the terminal bell elsewhere, with
the live-view URL and the exact resume command. It notifies once per blocked run and re-arms if the
run blocks again. For a server, use a webhook instead — see below.
Terminal failures you’ll see instead of a block:
BLOCKED_TIMEOUT (nobody cleared it in time),
VERIFICATION_TIMEOUT (no usable verification email arrived), APP_UNREACHABLE, AUTOMATION_STUCK,
ABORTED. Full lifecycle in Runs.
Webhooks
--events is a comma-separated list, defaulting to message.received; omit --identity for
org-wide. The register output includes your organization’s signing secret — webhook list never
shows it. Verify the x-sente-secret header on every delivery. Payloads in
Webhooks.
Identities
Optional — an account creates its address for you. Create one explicitly when several accounts should share a mailbox, or when the address itself matters.--on-conflict defaults to error (fail if the address is taken); suffix appends a random one.
Role addresses (support, admin, billing, security, postmaster, …) are reserved —
support-bot is fine, support is not.
Send from an identity:
--text and/or --html is required. --reply-to takes an inbound message id and threads the
reply.
Command reference
Next steps
- Runs and Human takeover — what blocks a run and how a person clears it.
- Sessions — CDP versus
storageState, freshness, and re-login healing. - TypeScript SDK · Python SDK · API reference.
