Async jobs
Start a run and get the id back immediately
The asynchronous twin of run_goal: the same execution, accepted with a 202 and a job id, with an optional HMAC-signed webhook when it finishes.
submit_run
What it is
The job: Run something that will outlive your request timeout, or run twenty things at once.
A synchronous run holds an HTTP connection for as long as the browser is working. That is fine for a ten-second login and wrong for a flow that takes minutes, or for a caller that wants to start ten runs and collect the results later. submit_run accepts the same body, reserves the credits, hands the work to the engine and answers with { jobId, status: "running" }.
From there you have three ways to find out how it went: poll get_job, hold open the SSE status stream at GET /api/v1/jobs/{id}/stream, or give the job a callbackUrl and let the platform POST you the result — signed with HMAC-SHA256 in an X-Twin-Signature header when you supply a callbackSecret. The webhook is best-effort and retried; it is a convenience, not a replacement for being able to poll.
Parking works the same way as it does on a synchronous run. A sign-in job that meets a 2FA wall parks by default, and get_job then reports status "paused" with the parked session id rather than a failure.
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_run",
"arguments": {
"target": "https://app.example.com",
"goal": "extract",
"callbackUrl": "https://your.app/hooks/twin",
"callbackSecret": "whsec_…"
}
}curl -X POST https://twin-browser.com/api/v1/jobs \
-H "Authorization: Bearer $TWIN_API_KEY" \
-H "content-type: application/json" \
-d '{"target":"https://app.example.com","goal":"extract","callbackUrl":"https://your.app/hooks/twin","callbackSecret":"whsec_…"}'| Parameter | Type | What it does |
|---|---|---|
| target* | string | Base URL of the target you are authorized to act on. |
| goal* | string | A registry goal name: login, search, extract, reply or like. |
| callbackUrl | string | Absolute http(s) URL that receives the completed result (success, failure or cancel). Best-effort and retried. |
| callbackSecret | string | Secret used to HMAC-SHA256 sign the callback body — header X-Twin-Signature: sha256=… |
| hitl | boolean | Park on a 2FA/approval wall instead of failing. Sign-in jobs park by default; pass false to opt out. |
| account | string | Credential/session label for this host. |
| persistSession | boolean | Persist and resume the browser session for this account. |
| proxy | string | Your own egress proxy URL. |
| proxyRotate | boolean | Force per-request proxy rotation. |
| sessionKey | string | Explicit override for the account stickiness key. |
| record | boolean | Record the run to video. record_job is the dedicated tool that forces this plus annotation. |
| annotate | boolean | Draw a visible cursor and element highlights during the run. |
| blockAssets | boolean | Abort image/media/font requests to save proxy bandwidth. Defaults ON when a proxy is set. |
Returns
HTTP 202
{ "jobId": "…", "status": "running" }What it costs
10 credits
The reservation is taken at submit and settled on the first terminal poll: a completed job charges higher-of(the 10-credit floor, metered model + compute + egress); a failed or cancelled one is refunded in full. A job that parks on a 2FA wall is also refunded — you pay when it finishes, not when it waits.
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.
run_goalrun_goal is one call and no bookkeeping, and it blocks. If your run finishes inside your own timeout budget, it is the simpler tool and you should use it.
run_goalrecord_jobSame async job with recording and annotation forced on and asset-blocking off. If you want the video, use record_job rather than remembering three flags.
record_jobsubmit_crawlBoth return a job id polled by get_job, but a crawl is a read across many pages, not a goal executed on one. Different work, same job envelope.
submit_crawlQuestions
submit_run, answered.
- How do I verify the completion webhook is really from you?
- Supply a callbackSecret. The body is signed with HMAC-SHA256 and delivered in the X-Twin-Signature header as sha256=…; recompute it over the raw body with the same secret and compare. Without a secret the callback is still delivered, but you have no way to authenticate it — so set one.
- Do I have to poll, or can I stream?
- Either. GET /api/v1/jobs/{id}/stream emits an SSE status frame per engine transition and then a final authoritative frame before closing. Billing settles once on close; a client that disconnects early settles on its next poll instead, so you cannot lose or duplicate a charge by dropping the stream.
Keep going
The rest of the tool set.
get_jobFind out whether the thing you started has finished, and get what it produced.
cancel_jobChange your mind about a job that is still running, without paying for it.
run_goalDo one thing on one site, right now, and block until you know whether it worked.
record_jobCapture a flow that is too long to sit and wait for.