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.

MCPsubmit_run.mcp.jsonjson
// 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_…"
  }
}
POST /api/v1/jobsrequest.shbash
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_…"}'
Parameters accepted by submit_run
ParameterTypeWhat it does
target*stringBase URL of the target you are authorized to act on.
goal*stringA registry goal name: login, search, extract, reply or like.
callbackUrlstringAbsolute http(s) URL that receives the completed result (success, failure or cancel). Best-effort and retried.
callbackSecretstringSecret used to HMAC-SHA256 sign the callback body — header X-Twin-Signature: sha256=…
hitlbooleanPark on a 2FA/approval wall instead of failing. Sign-in jobs park by default; pass false to opt out.
accountstringCredential/session label for this host.
persistSessionbooleanPersist and resume the browser session for this account.
proxystringYour own egress proxy URL.
proxyRotatebooleanForce per-request proxy rotation.
sessionKeystringExplicit override for the account stickiness key.
recordbooleanRecord the run to video. record_job is the dedicated tool that forces this plus annotation.
annotatebooleanDraw a visible cursor and element highlights during the run.
blockAssetsbooleanAbort image/media/font requests to save proxy bandwidth. Defaults ON when a proxy is set.

Returns

response.jsonjson
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 card

Which one

When a different tool is the right call.

The honest answer is often the neighbouring tool. These are the trades.

run_goal

run_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_goal
record_job

Same 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_job
submit_crawl

Both 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_crawl

Questions

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.