Sessions & HITL
Hand a code back to a run that is waiting
Resumes a run parked on a 2FA or approval wall — an SMS or email code, a TOTP, an out-of-band approval, or a credential your own app already holds — on the same parked browser, without restarting.
submit_verification
What it is
The job: Finish a sign-in that got as far as the second factor.
A parked run is a browser that is still open, one field short of being signed in. submit_verification fills that field. Two shapes, matching how the run paused: a PARKED run from run_goal or submit_run gives you a sessionId, and a LIVE streamed run emits an await-user SSE event carrying a token. Pass whichever you were given, plus the code.
An approval you completed out of band — a push notification you tapped on your phone — needs no code at all: resume with just the identifier and the run picks up from the now-cleared wall.
The `secrets` object is the third path and the most easily misused. It exists for the case where a run paused with code: "credential_missing" and YOUR application already holds the credential — a first-party login form, say. The values are merged into the parked session’s vault so the same browser continues without restarting, and they are redacted from every frame, step and log. What it is not for is asking a user to type a password into a conversation with a model; that is what connect_account is for.
Send cancel: true to abandon the wait instead.
The call
Call it exactly like this.
Copied from the tool's registration and the route handler — not paraphrased.
// MCP tool call — server "twin-browser"
{
"tool": "submit_verification",
"arguments": {
"sessionId": "…",
"code": "482913"
}
}curl -X POST https://twin-browser.com/api/v1/runs/RUN_ID/resume \
-H "Authorization: Bearer $TWIN_API_KEY" \
-H "content-type: application/json" \
-d '{"code":"482913"}'The two paths are addressed differently. Over REST you resume by the WEB run id — POST /api/v1/runs/{runId}/resume — and the platform looks up the parked engine session from the run record; a texted code on a STREAMED run goes to POST /api/v1/live/resume with its { token, code } instead. The MCP tool takes the engine `sessionId` for the parked path and the SSE `token` for the streamed one.
| Parameter | Type | What it does |
|---|---|---|
| sessionId | string | The sessionId from a paused run_goal / submit_run. Resumes the parked engine session directly. |
| token | string | The token from an await-user SSE event on a streamed run. Routed through the web live-resume channel. |
| code | string | The verification code the user provided. Omit for an approval you completed out of band. |
| secrets | object | Credentials for a run that paused with credential_missing, e.g. { "email": "…", "password": "…" }. Merged into the parked browser so it continues without restarting; redacted from every frame and log. Only send values your app already holds. |
| cancel | boolean | Abandon the wait or pause instead of submitting a code. |
Returns
{ "status": "complete", "success": true, "steps": 9, "path": [ … ] }
// …or, if it is still blocked:
{ "status": "paused", "sessionId": "…", "reason": "…" }What it costs
10 credits on completion
The pause refunded the original run’s reservation in full, so the resume reserves and settles the flat run floor when it completes. A resume that is still paused afterwards is refunded again — you are charged once, for a run that finished, however many times it had to wait.
See the full rate cardWhich one
When a different tool is the right call.
The honest answer is often the neighbouring tool. These are the trades.
control_runIf the wall needs a code, this is one call. If it needs a click, control_run is the tool — this one cannot press a button.
control_runconnect_accountWhen the credential is not yours to hold, do not collect it at all. Send a connect link and let the person complete the whole sign-in themselves.
connect_accountsolve_captchaA CAPTCHA is not a verification code. solve_captcha hands that specific wall to the solver.
solve_captchaQuestions
submit_verification, answered.
- Can the platform fetch the code itself?
- Often, yes — and then no pause happens at all. Connect an IMAP inbox (POST /api/v1/email-inbox, validated against a real IMAP connection before it is stored) or a phone number, and the engine’s email-first path reads and fills the emailed or texted code itself. submit_verification is the fallback for the codes it cannot reach.
- What is the difference between sessionId and token?
- They come from different pause mechanics. A synchronous or async run parks a browser session in the engine and returns its sessionId. A streamed run holds open an SSE and emits an await-user event with a one-time token. Pass back whichever you were handed; sending neither is an error.
Keep going
The rest of the tool set.
run_goalDo one thing on one site, right now, and block until you know whether it worked.
control_runClear a blocker you can see on the page, when no code and no credential would have helped.
connect_accountGet logged into a site that an agent is not going to be allowed to log into.
solve_captchaClear the one wall that is not a code, not a login, and not a click.
get_jobFind out whether the thing you started has finished, and get what it produced.