One live run per identity. Starting a second operation on an identity that already has a run in flight returns that in-flight run rather than queuing a competing one — so
register and connect are safe to call again after a network blip.What a run will not do
Stated up front, because it shapes how you design around runs:- A CAPTCHA, an SMS/phone step, or a payment form stops the run. Sente does not work around any of them — the run goes
blockedand pages a human with an interactive live view. See Human takeover. - A second-factor code sent to your own email or phone (connected accounts) stops the run the same way. A vaulted TOTP seed is the one MFA case that stays autonomous.
- Email verification for the account’s own address is the part that is fully automatic: the code or link arrives in the identity’s inbox, gets classified on arrival, and is applied inside the run. You never handle it.
Lifecycle
blocked is a stop state, not a failure — the run keeps its browser session and waits for a person.
Follow a run end to end
The complete pattern: start the operation, wait, handle the one case that needs a human, then use the account.Push instead of poll
run.completed, run.failed, and run.blocked webhooks fire on the transition, so a server-side agent reacts without a polling loop. They are single-attempt: if your endpoint is down at that moment the notification is gone. Keep GET /v1/runs/:id as the backstop for anything you must not miss.
Blocked codes
Whilestatus is blocked, error.code says which gate stopped the run and who is expected to clear it. Full flow: Human takeover.
Failure codes
Terminal.error.detail usually carries the agent’s own description of where it stopped.
A blocked-style code can also land on a failed run. If the browser session had already died when the gate was detected, takeover was impossible, so the truthful code (
CAPTCHA_REQUIRED, PHONE_REQUIRED, …) is recorded on the failure instead of a generic one. Judge by status, not by the code alone.Errors when starting or controlling a run
Edge cases
Retrying a register or connect call
Retrying a register or connect call
Both are idempotent per (identity, app origin). If a run is already in flight for that identity you get that run back. If the account is already
active, register returns it without driving anything — re-authentication is login. Calling connect again on an existing connection rotates the stored credentials and clears a prior revoke.A run whose worker dies
A run whose worker dies
Each run heartbeats while it is being driven. If the heartbeat goes stale (about 5 minutes), a sweep fails the run with
AUTOMATION_STUCK and stops its browser session, so a dead worker can never leave a run stuck running forever.Watching a run while it happens
Watching a run while it happens
A run carries
liveViewUrl once its browser session exists — an interactive view of the exact page the agent is on. The dashboard embeds it on the run page with take-over and resume buttons. After the run ends, GET /v1/runs/:id/recording returns a video of the session once processing finishes.Listing runs
Listing runs
GET /v1/runs returns the org’s runs newest-first, filterable by identityId, status, type, since, and limit — the activity feed behind the dashboard. The list projection joins the identity email and app URL but drops result and liveViewUrl; fetch GET /v1/runs/:id for those. See the runs API reference.Next steps
Human takeover
What blocks a run, how a person clears it, and how they get paged.
Webhooks
Get run outcomes pushed instead of polling for them.
Runs API reference
Endpoints, exact payloads, and query parameters.
