For operations and platform teams

Ship the integration the vendor never built.

When a system has no API, no webhook and no export, the fallback is a person clicking through it every day. Twin compiles that click-path once into a named skill, then replays it per record with the values bound at call time — no model in the loop, no coordinate recording to maintain.

The problem

What this costs you today.

The vendor’s answer is always the same: the API is on the roadmap, or it is an enterprise add-on, or it exists but does not cover the one screen you need. So the workflow stays manual. Someone signs in every morning, keys in the same twenty records, exports a CSV that another person re-keys somewhere else. It is not hard work, it is just work that never ends — and it is invisible to every system you actually measure.

  • “There is no API. IT quoted eighteen months for a middleware project.”
  • “We pay a contractor to re-key the same records between two systems.”
  • “We tried an RPA bot. The vendor redesigned the page and it broke that week.”
  • “The screen we need is behind a login, so nothing off-the-shelf can reach it.”
portal.example.com
  1. Open the vendor screendone
  2. Resolve login from the vaultrunning
  3. Bind params for this recordqueued
  4. Replay the stored pathqueued
  5. Verify the success conditionqueued
One Twin run for systems with no api — the work happens in a real browser, under your guardrails.

How Twin solves it

The mechanism, not a promise.

A compiled skill is the integration. Twin plans the flow once against the live UI, minimizes it into a stored action path, and replays that path deterministically — with the parameters, credentials and session bound per call rather than frozen at compile time.

  1. 1Describe the flow once, in wordsPOST /api/v1/skills with the target and the goal. Twin discovers the path with the planner, then minimizes it into a stored, named skill. This is the one expensive call.
  2. 2Write the variable parts as tokensA goal can carry `{{param:NAME}}` for call arguments and `{{secret:NAME}}` for vault credentials. Neither is baked into the skill: both are resolved per replay, so one skill drives every record and every login on that host.
  3. 3Replay it deterministicallyPOST /api/v1/skills/{name}/run executes the stored path with no model in the loop. Pass `params` for the values and `account` to pick which stored login and session to use.
  4. 4Fail loudly, before it costs anythingA replay whose parameters are unresolved returns 400 with `code:"params_missing"` and the list of missing names — before a run row exists and before a credit is reserved. It never types a literal `{{param:qty}}` onto a live page.
  5. 5Plans survive a redesignThe planner acts on a numerically-indexed map of the page’s interactive elements rather than CSS selectors or coordinates, so a cosmetic redesign shifts the map rather than snapping a hard-coded path.

In practice

The actual call, and what it returns.

Compile the click-path once, then call it like a function. The values arrive per call, which is what makes one compiled skill an integration rather than a recording of one afternoon.

Compile + replayvendor-erp.shbash
# 1 — compile the flow ONCE from the live UI.
curl https://twin-browser.com/api/v1/skills \
  -H "Authorization: Bearer $TWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": "https://erp.vendor.example.com",
    "goal": "raise a purchase order for {{param:sku}} quantity {{param:qty}}",
    "as": "create-po"
  }'

# 2 — replay it per record. No LLM in the loop.
curl https://twin-browser.com/api/v1/skills/create-po/run \
  -H "Authorization: Bearer $TWIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": "https://erp.vendor.example.com",
    "account": "erp-service-user",
    "params": { "sku": "AX-7741", "qty": "12" }
  }'

# → 200 { "success": true, "steps": 8, "runId": "…", "credits_charged": 1 }

# An under-specified call is refused BEFORE anything is charged:
# → 400 { "error": "missing required parameter(s): qty",
#         "code": "params_missing", "missing": ["qty"] }

What this call does

  • `params` binds `{{param:NAME}}` at call time, so one skill covers every record instead of one example.
  • `account` selects which stored login and cookie jar to use, so the same skill drives several logins on the same host.
  • Vault secrets referenced as `{{secret:NAME}}` are resolved server-side for this tenant, host and account — the value never appears in a request or a response.
  • The stored success condition is re-bound with the same params, so a skill that verifies by reading back what it typed still verifies correctly.
Every endpoint, every field

What it costs

Priced per action, not per seat.

One expensive call, then a flat cheap one per record. That shape is the whole economic argument for compiling instead of re-reasoning.

Credit cost of the actions this solution uses
ActionCreditsWhat you get
POST /skills — compile the flow50 / skillPaid once per flow. A planning pass plus minimization into a stored path.
POST /skills/{name}/run — replay1 / recordDeterministic execution of the stored path. No model call.
POST /run — an ad-hoc run10 / runFor the one-off variant that does not justify a skill.

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.
  • Compiling is worth it as soon as you will run the flow more times than the compile costs in saved runs — the exact number is computed on the pricing page from these same figures.
The full rate card

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 not a supported integration, and cannot pretend to be

You are driving a UI the vendor may change without telling you. Skills plan over an indexed element map rather than selectors, which absorbs cosmetic churn — but a flow that is genuinely restructured needs a recompile, and you should expect that a few times a year on an actively developed product.

It does not give you an API’s throughput

Every replay drives a real browser: think seconds per record, not milliseconds, and bounded concurrency. For tens of thousands of records a night, a real API — even a paid one — is the right answer and we will tell you so.

It does not create authorization you do not have

Twin runs against accounts you are entitled to use, on targets you supply. It does not bypass a licence, a rate limit you agreed to, or a vendor’s terms of service.

Under the hood

The primitives this runs on.

Nothing here is specific to this problem — the same mechanisms carry every solution on the site.

FAQ

Systems with no APIcommon questions.

What happens when the vendor redesigns the screen?
Small changes are absorbed: the planner works from a numerically-indexed map of interactive elements, not from CSS selectors or pixel coordinates, so a moved button is still the same button. A genuine restructure needs a recompile — one call, not a rebuild.
Where do the credentials live?
In the per-tenant vault. You store a secret once with POST /api/v1/secrets, reference it as {{secret:NAME}}, and it is resolved inside the browser session at fill time. GET /api/v1/secrets returns names, never values.
How is this different from RPA?
Classic RPA records exact coordinates and selectors, so a redesign breaks it and a developer rebuilds the recording. Twin compiles intent into a path over indexed elements and binds every account-shaped input per call, so one skill covers every record, every login and small amounts of page drift.
Can a person approve a step before it commits?
Yes. Set hitl:true on a run and it parks on an approval or verification wall, returning { status:"paused", sessionId } for a human to act on, then resumes from that exact step via POST /api/v1/runs/{id}/resume.

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.