Skip to main content
When a run reaches something only a person should do — a CAPTCHA, an SMS code, a payment form, the final submit — it does not fail and it does not try to get around it. It blocks: it keeps its browser session open on the exact page it stopped at, pages a human, and waits to be resumed. That is deliberate. The gate is the site’s decision to require a person, and Sente honors it. Design your integration so a human can be reached in about ten minutes, and blocking becomes a normal step in the loop rather than an outage.
The two MFA cases that stay autonomous: a connected account with a vaulted TOTP seed (the server derives the 6-digit code — the seed itself never leaves the vault), and a connected account whose notification email the owner repointed to the Sente identity (verifyToIdentity — the emailed code lands in the account’s own inbox and is applied like any other verification email). Everything else — SMS codes, codes sent to your inbox, CAPTCHAs — needs a person.

What blocks a run

Full meanings and the failure codes: Runs.

The clock

A blocked run holds for about 10 minutes. If nobody resumes it, the run fails with BLOCKED_TIMEOUT, the browser session is released, and the live-view URL stops working — reopening a stale link later gets you nothing. Wire up at least one notification path before you run anything that can block.
The run.blocked webhook payload carries holdExpiresAt, the exact instant the hold ends. Put it in whatever alert you send so the person knows how long they have. A run that times out is not destructive — start the operation again and it queues a fresh run.

How a person gets paged

Four independent channels; use whichever matches where your humans are.
The webhook is the only channel that reaches a headless deployment reliably — it fires the instant the run blocks. It is a single attempt, so if your endpoint was down, GET /v1/runs?status=blocked is the catch-up query.

Clearing a block

1

Get the run

From the webhook payload, sente run <runId>, or runs.get(id). You need error.code (what the gate is) and liveViewUrl (where to clear it).
2

Open the live view

liveViewUrl is an interactive browser view, not a screenshot: the person sees the page the agent stopped on and can click and type in it. The dashboard’s run page embeds the same view with the resume button next to it — that link is actionUrl in the webhook payload.
3

Do the one thing that was needed

Complete the CAPTCHA, enter the code, fill the field, click submit. Nothing else — the agent still owns the rest of the task.
4

Resume

Resume returns the run with status: "running" and error: null. If the run is no longer blocked (someone else resumed it, or the hold expired) you get 409 NOT_BLOCKED with the current status.
5

The agent continues

Same browser, same page. It is told the block was cleared and re-reads the page before acting. After a confirm-before-submit pause it is told explicitly that a human already submitted, so it does not submit twice. The run carries on toward completed.

Taking over a run you didn’t have to

You can also pause a healthy run and drive it yourself — useful when you can see it heading the wrong way in the live view.
The run flips to blocked with error.code: "MANUAL_INTERVENTION" and the browser session stays alive. Only running and awaiting_verification runs can be taken over (409 NOT_INTERVENABLE otherwise). The same ~10-minute hold applies — an unresumed takeover fails with BLOCKED_TIMEOUT exactly like any other block. Hand back with POST /v1/runs/:id/resume; the agent re-observes the page a human changed and continues. To stop instead of hand back, POST /v1/runs/:id/abort (sente run abort) ends the run with ABORTED and kills the browser session.

Confirm-before-submit

The one block you ask for on purpose. Pass confirmBeforeSubmit: true (Python confirm_before_submit=True, CLI --confirm-before-submit) on a registration and the agent fills every field and checks the terms boxes, then stops without pressing the final button. The run blocks with SUBMIT_CONFIRMATION_REQUIRED; a person reviews the filled form in the live view and clicks submit themselves — so a human, not an agent, forms the agreement with the site — and then resumes. Sente completes email verification afterwards as usual. Use it whenever you want a person accountable at the moment of account creation. Register only where the target’s terms permit it — see Acceptable use.

Things that bite

Emailed and SMS second-factor codes usually live 5–15 minutes, which overlaps the hold. If the person arrives late, the code they type may already be dead: the run resumes, the app rejects it, and the run blocks or fails again. Trigger a fresh code in the live view before typing, and treat the operator email as a fallback rather than the primary path for MFA-heavy accounts.
The URL belongs to a browser session. Once the run reaches a terminal state — resumed and finished, aborted, or BLOCKED_TIMEOUT — the session is gone and the link is dead. Always route people via actionUrl (the dashboard run page), which stays valid and shows the run’s current state.
Two adjustments: register the run.blocked webhook so alerts land where your team already is, and prefer connect with a vaulted TOTP seed over accounts whose 2FA goes to email or SMS — those re-login without a human at all.
A blocked run holds a live browser session open and counts toward your run quota while it lives. If it ends failed — including BLOCKED_TIMEOUT — it stops counting: failed runs never consume quota. See Limits.

Next steps

Runs

Statuses, the full code tables, and how to follow a run.

Webhooks

The run.blocked payload and how to verify a delivery.

Register a new account

Confirm-before-submit in the context of signup.

Connect an account you own

TOTP seeds and verifyToIdentity — the paths that avoid a human.