For implementation and onboarding teams
Enter a thousand records into a system with no importer.
Onboarding stalls on data entry: the new system has no bulk import, the old one has no export, and the implementation team is typing. Compile the entry flow once, then replay it per record with the values bound at call time — and reconcile what did not land from the run list.
The problem
What this costs you today.
Every new customer arrives with a spreadsheet and a deadline. The target system’s importer covers three of the eleven fields, or it exists on a plan the customer is not on, or the records have to be created through a wizard because that is the only place the validation runs. So the implementation team types — for a week, per customer — and the go-live date is set by how fast people can key in records.
- “Onboarding takes three weeks and two of them are data entry.”
- “The importer does not cover the fields that matter, so we use the UI anyway.”
- “We hired temps for the migration and then we had to check their work.”
- “Every customer’s data is slightly different, so a script never survives to the next one.”
- Read the next recorddone
- Bind params for this rowrunning
- Replay the entry flowqueued
- Verify the created recordqueued
- Log the failures for reviewqueued
How Twin solves it
The mechanism, not a promise.
The entry flow is compiled once and then called like a function, once per record. Because parameters, secrets and the session are bound at call time rather than frozen at compile time, the same skill runs against the next customer’s tenant with different values and a different login.
- 1Compile the wizard oncePOST /api/v1/skills discovers the entry flow with the planner and minimizes it into a stored, named path. Write the variable parts of the goal as `{{param:NAME}}`.
- 2Replay it per recordPOST /api/v1/skills/{name}/run with `params` for the record and `account` for the login. Each replay is deterministic and runs without a model.
- 3Refuse under-specified records earlyA replay missing a parameter returns 400 with `code:"params_missing"` and the missing names, before a run row exists and before credits are reserved — a bad row in the CSV costs nothing.
- 4Reconcile from the run listGET /api/v1/runs filters by `mode` and `status`, so `?mode=skill&status=failed` is your exception report. Each run carries a runId, and GET /api/v1/runs/{id}/video is the recording of exactly what happened.
- 5Use the customer’s own login, safelyWhen the target tenant belongs to the customer, POST /api/v1/connect/sessions gives them a single-use link to sign in themselves. You never hold their password, and the migration still runs.
In practice
The actual call, and what it returns.
One compiled skill, one call per record, and an exception report you can query. The loop is deliberately boring — that is what makes a migration reviewable.
# Compile once (POST /api/v1/skills), then replay per record.
while IFS=, read -r email plan; do
curl -s https://twin-browser.com/api/v1/skills/create-account/run \
-H "Authorization: Bearer $TWIN_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"target\": \"https://app.newvendor.example.com\",
\"account\": \"impl-team\",
\"params\": { \"email\": \"$email\", \"plan\": \"$plan\" }}"
done < records.csv
# → 200 { "success": true, "steps": 7, "runId": "…", "credits_charged": 1 }
# Reconcile: everything that did not land, newest first.
curl "https://twin-browser.com/api/v1/runs?mode=skill&status=failed&limit=100" \
-H "Authorization: Bearer $TWIN_API_KEY"
# Each failure carries a runId — GET /api/v1/runs/{id}/video is the recording.What this call does
- `params` are bound per call, so one compiled skill covers every record and every customer tenant.
- A missing parameter is a 400 with `code:"params_missing"` before anything is charged — a malformed CSV row fails free.
- GET /api/v1/runs takes `mode` (run | job | skill | compile | adapt) and `status` (running | complete | failed | paused | cancelled) so the exception report is one query.
- For volume, submit the same work through POST /api/v1/jobs and let the completion webhook tell you when a batch finished.
What it costs
Priced per action, not per seat.
A migration is one compile and N replays. That is the point: the expensive part is paid once per flow, not once per record.
| Action | Credits | What you get |
|---|---|---|
| POST /skills — compile the entry flow | 50 / flow | One planning pass, minimized into a stored path. |
| POST /skills/{name}/run — one record | 1 / record | Deterministic replay, no model call. |
| POST /jobs — a background batch | 10 / run | Async submission with an HMAC-signed completion webhook. |
| POST /run — the awkward exception | 10 / run | For the record that does not fit the compiled flow. |
How the unit works
- $1 buys 1,000 credits; the smallest pack is $5.
- A paid action bills the higher of its flat floor and what it actually spent on model, compute and egress — so a cheap run stays cheap.
- “Metered” means the action has no published flat floor on this page: GET /api/v1/pricing serves the live card.
- A cancelled job is refunded in full, and a replay refused for missing parameters is never charged at all.
Be sure this fits
What this does not do.
Every one of these will come up in your evaluation. Here they are first, from us.
It is browser-speed, not bulk-load speed
Each record is a real browser doing real clicks: seconds per record, with bounded concurrency. Ten thousand records is an overnight job, not a coffee break — and if the target system does have a working bulk importer, use it.
It does not validate your data for you
Twin verifies the flow’s success condition — that the record was created — not that the record is correct. Field-level validation, deduplication and mapping belong upstream, in the file you feed it.
A mid-flow change means a recompile
If the vendor changes the wizard halfway through a migration, replays that depended on the old structure will fail rather than guessing. That is deliberate, but it means somebody has to notice, recompile, and re-run the failures.
Under the hood
The primitives this runs on.
Nothing here is specific to this problem — the same mechanisms carry every solution on the site.
Deterministic replay
A successful run is minimized into a named, versioned skill — an ordered action path with its variable parts lifted out — and replaying it is a program, not a prompt.
Read the mechanism — Deterministic replaySkill library & catalog
Every compiled skill is stored per tenant with a version and a run contract — the params it takes, the secrets it fills, and whether your stored auth is ready for its host.
Read the mechanism — Skill library & catalogHuman-in-the-loop handoff
A 2FA prompt, an approval push or a CAPTCHA the agent cannot clear returns status "paused" with a live session — resolve it automatically from a connected inbox, hand back a code, or drive the page yourself.
Read the mechanism — Human-in-the-loop handoffLive view & session video
POST /api/v1/live streams the browser back as Server-Sent Events while the agent works, and an opted-in run keeps a durable video you can fetch long afterwards.
Read the mechanism — Live view & session videoOver MCP, the same work is these tools
compile_skillDiscover a goal once with the planner, then minimize it into a reusable, deterministic skill.
run_skillBlind-replay a compiled skill with no LLM in the loop — the cheap, deterministic path.
submit_runSubmit a goal as an async background job; returns a job id immediately. Same two shapes and the same run controls as run_goal. Like run_goal, a sign-in job parks by default on a 2FA/approval wall — get_job then reports status:"paused" with a sessionId.
connect_accountGet a one-time link that lets a HUMAN sign into a site by hand, so Twin Browser captures the session and later runs are already logged in. Use it when a run returns code:"credential_missing", when it returns code:"credential_rejected" and you have no better credential to supply (the one stored is wrong — a retry re-types it), or when a site hard-challenges automated logins from datacenter IPs and simply cannot be signed into by an agent. Never ask the user to paste a password — send this link. Re-run the task afterwards and it restores the session (sessionRestored:true).
FAQ
Onboarding and migration data entry — common questions.
How do we prove what the automation actually did?
The target system is the customer’s tenant. Do we need their password?
What happens to the records that fail?
Can a person approve a batch before it commits?
Related
Other problems this layer solves.
Systems with no API
Ops, finance and platform teams blocked by a vendor system that offers no programmatic access.
Supplier and customer portals
Procurement, accounts-payable and supply-chain teams whose data is spread across dozens of external portals.
Browser infrastructure for AI products
Founders and engineering leads whose AI product has to act on the live web for every customer.
Try it on your hardest screen.
Start free, point a run at the system that is blocking you, and watch it happen live. If it does not work, the run tells you why — and what to do instead.