REST API · v1
One Bearer key, from goal to action.
Hand your agent the live web from your own stack: it drives any site you point it at and signs into the accounts you connect, on exactly the URL you pass. A small, versioned surface under /api/v1 — run a goal, queue it, or replay a compiled skill.
The first call
Three lines from your stack.
No SDK required: one POST against https://twin-browser.com/api/v1 is the whole integration. A solved goal settles at 10 credits, and the run id in the response is the handle for the recording and the audit record.
curl https://twin-browser.com/api/v1/run \
-H "Authorization: Bearer ab_live_…" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.acme.com/invoices",
"prompt": "export last month as CSV",
"success": { "kind": "urlIncludes", "value": "/exports" }
}'
# HTTP/2 200
# {
# "success": true,
# "path": [ ... ], <- the action path that solved it
# "runId": "run_...", <- fetch the recording or the audit record
# "credits_charged": 10
# }// One key, one POST. Base URL: https://twin-browser.com/api/v1
const res = await fetch('https://twin-browser.com/api/v1/run', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TWIN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://app.acme.com/invoices',
prompt: 'export last month as CSV',
success: { kind: 'urlIncludes', value: '/exports' },
}),
});
const run = await res.json();
run.success; // true
run.runId; // stable id — recording + audit record
run.credits_charged; // what this call actually costHow a call flows
Authenticated, observable, settled.
Every call runs the same four stages — and the third one is where the cost falls away.
Authenticate
Every call carries Authorization: Bearer with a per-tenant key. Default-deny RLS scopes every read and write to your tenant; an invalid key fails closed with 401.
Observe the target
Twin loads the URL you passed and serializes it into a compact, numerically-indexed map of interactive elements — token-efficient state, not raw HTML.
Replay or plan
The semantic cache fuzzy-matches the request to a compiled skill. On a hit it replays deterministically with zero LLM calls; on a miss the planner discovers a path and compiles it.
Settle & audit
Credits settle on the first terminal read — failed, cancelled, or unsolved actions refund in full. The goal, path, target, and key are written to the audit log.
Endpoints
54 endpoints, printed in full.
Not a feature list — the surface itself, rendered from the same OpenAPI document served at GET /api/v1/openapi. If an endpoint ships, it appears here on the next build.
| Method | Endpoint | What it does |
|---|---|---|
| Runs & live control | ||
| POST | /api/v1/run | Run a goal synchronously on an authorized target (~10 credits). |
| POST | /api/v1/live | Run a goal and stream the LIVE browser view back as Server-Sent Events (~10 credits). |
| POST | /api/v1/live/resume | Hand a 2FA code (or cancel) to a live run holding on an await-user step. |
| GET | /api/v1/runs | List this tenant's runs (newest first, paginated). |
| GET | /api/v1/runs/{id} | Read a single run: status, success, masked action path, credits, video, timestamps. |
| POST | /api/v1/runs/{id}/resume | Continue a paused (HITL) sync run after an out-of-band approval. |
| POST | /api/v1/runs/{id}/input | Drive a paused run's browser by hand (click / type / key / scroll / goto). |
| GET | /api/v1/runs/{id}/stream | Watch a paused run's browser live (SSE screencast) so you can drive it. |
| GET | /api/v1/runs/{id}/video | Stream a run's recorded video (only if the run was created with record:true). |
| POST | /api/v1/agentdeprecated | DEPRECATED alias for POST /run with {"stealth":true} — prefer that. Forces the full anti-detection stack. |
| Semantic cache & skills | ||
| POST | /api/v1/dispatch | Semantic cache: match to a compiled skill (hit) or compile + cache (miss). |
| GET | /api/v1/skills | List this tenant's compiled skills. |
| POST | /api/v1/skills | Compile a skill — discover + minimize (~50 credits). |
| POST | /api/v1/skills/{name}/run | Deterministically replay a compiled skill — no LLM (~1 credit). |
| GET | /api/v1/library | Search the cross-tenant shared skill corpus — metadata only (free). |
| GET | /api/v1/cache/stats | Semantic-cache analytics for this tenant — hit rate + credits saved (free, no charge). |
| GET | /api/v1/templates | List the extraction-template catalog used by /extract and /etl (public, no key). |
| Async jobs | ||
| GET | /api/v1/jobs | List this tenant's recent async jobs. |
| POST | /api/v1/jobs | Submit a goal as an async job (~10 credits). Returns 202 { jobId }. |
| GET | /api/v1/jobs/{id} | Poll an async job; settles credits on first terminal status. |
| GET | /api/v1/jobs/{id}/stream | Stream an async job's status as Server-Sent Events (an alternative to polling). |
| POST | /api/v1/jobs/{id}/cancel | Cancel a running async job — full refund of the reserved credits. |
| Data tools | ||
| POST | /api/v1/search | Web search across blended sources (Brave web/discussions/news/faq + Hacker News, deduped, each result tagged with source). Shallow (default): sync ranked results (~3 cr) + optional fetch (+1 cr/page). Deep (depth:"deep"): async job, scrapes top N (3 cr + 5 cr/page), returns 202 { jobId }. |
| POST | /api/v1/extract | Extract structured JSON from a page with an LLM (metered, ~5-credit floor). |
| POST | /api/v1/screenshot | Capture a single page as a PNG (flat ~1 credit, no LLM cost). Waits for the page to be visually finished — fonts, images, entrance animations, pixel stability — before capturing. |
| POST | /api/v1/map | Discover all URLs of a site fast — sitemap + robots + shallow link scan (flat ~2 credits, no LLM). |
| POST | /api/v1/crawl | Crawl an entire site as an async job — billed per page (~3 credits/page). Returns 202 { jobId }. |
| POST | /api/v1/etl | General ETL: extract → transform (clean + optional schema) → chunk → embed → load into the queryable content store. |
| POST | /api/v1/etl/query | Semantic search over content ingested via /etl — top-k similar chunks with source url/title (flat 1 credit, no LLM). |
| Monitors | ||
| GET | /api/v1/monitors | List this tenant's monitors (secrets excluded). |
| POST | /api/v1/monitors | Create a monitor that watches a page on a schedule and pushes a signed webhook on change. |
| GET | /api/v1/monitors/{id} | Read a single monitor (secrets excluded). |
| PATCH | /api/v1/monitors/{id} | Update / pause / resume a monitor (active, interval, callback, selector, watch spec). |
| DELETE | /api/v1/monitors/{id} | Delete a monitor and its check history. |
| GET | /api/v1/monitors/{id}/history | A monitor's recent check history (changed/unchanged/error + value excerpts). |
| Sessions & credentials | ||
| GET | /api/v1/connect/sessions | List connect links and whether each sign-in was completed. |
| POST | /api/v1/connect/sessions | Mint a link that lets a HUMAN sign into a site, so later runs are already logged in. |
| POST | /api/v1/sessions/import | Import a browser session captured on the user’s OWN machine (the capture extension). |
| GET | /api/v1/accounts | Which logins this tenant already has — metadata only, never a credential. |
| GET | /api/v1/secrets | List this tenant's stored secret NAMES (write-only vault — values are never returned). Free. |
| POST | /api/v1/secrets | Store (encrypt) a named secret for this tenant, referenceable in prompts as {{secret:NAME}}. Free. |
| DELETE | /api/v1/secrets/{name} | Delete a stored secret by name for this tenant. Free. |
| GET | /api/v1/email-inbox | List the connected IMAP inboxes used to auto-resolve emailed 2FA codes (host/user only). |
| POST | /api/v1/email-inbox | Connect an IMAP inbox so sign-in runs auto-fill the emailed 2FA code (no HITL pause). |
| DELETE | /api/v1/email-inbox | Disconnect a stored IMAP inbox by name. |
| Page intelligence & anti-bot | ||
| POST | /api/v1/observe | Serialize a page into indexed DOM state without acting (~1 credit). |
| POST | /api/v1/solve-captcha | Solve a captcha blocking an in-flight session (~5 credits, charged only on success). |
| Platform & billing | ||
| GET | /api/v1/tenants | Reseller: list your subtenants with balances and what they cost you. |
| POST | /api/v1/tenants | Reseller: create a subtenant under your account and mint its API key. |
| GET | /api/v1/pricing | Public credit rate card (no API key required). |
| Everything else | ||
| GET | /api/v1/site | What we already know about a host — its anti-bot wall, your auth state there, and the run configuration both imply. Free. |
| GET | /api/v1/warnings | List this tenant's open warnings — the general notifications surface. Free. |
| POST | /api/v1/warnings/{id}/ack | Acknowledge (dismiss) one warning by id. Free. |
| POST | /api/v1/download | Fetch an authorized asset's raw bytes — a logo, image, PDF, or other document (flat ~1 credit, no LLM cost). |
Request fields, success-condition specs, response codes and error shapes are in the full endpoint reference.
Reading the cache
The outcome is a field, not a guess.
Every /dispatch response names how it was served and what it cost, in the body you already parsed. A drifted skill and an unsolved goal both come back as a 502 with the reservation refunded in full.
| mode | Credits | Meaning |
|---|---|---|
| cache-hit | 2 | A skill compiled for this host matched. Blind replay, no LLM in the loop. |
| cache-adapt | 5 | A near-match from the shared corpus seeded the run; only the delta was explored. |
| cache-miss-compiled | 10 | Nothing matched. The planner discovered a path and cached it for next time. |
| cache-hit-failed | refunded | The cached path no longer reaches the goal — drift. Returned as 502, fully refunded. |
| cache-miss-failed | refunded | Discovery could not solve the goal. Returned as 502, fully refunded. |
# The cheap path. /dispatch matches the goal against skills already
# compiled for this host, and compiles one when nothing matches.
curl -X POST https://twin-browser.com/api/v1/dispatch \
-H "Authorization: Bearer ab_live_…" -H "Content-Type: application/json" \
-d '{ "url": "https://app.acme.com/invoices",
"prompt": "export last month as CSV",
"success": { "kind": "urlIncludes", "value": "/exports" } }'
# HIT { "mode": "cache-hit", "skill": "acme-export", "version": 3,
# "success": true, "credits_charged": 2 }
# ADAPT { "mode": "cache-adapt", "source": "acme-export",
# "success": true, "credits_charged": 5 }
# MISS { "mode": "cache-miss-compiled", "skill": "acme-export", "version": 1,
# "success": true, "credits_charged": 10 }# Compile a goal into a reusable, deterministic skill (discover + minimize).
# "as" names it; the body is { url, prompt } or { target, goal }.
curl -X POST https://twin-browser.com/api/v1/skills \
-H "Authorization: Bearer ab_live_…" -H "Content-Type: application/json" \
-d '{ "url": "https://app.acme.com/invoices",
"prompt": "export last month as CSV", "as": "acme-export" }'
# -> { "name": "acme-export", "version": 1, "steps": 6,
# "runId": "run_...", "credits_charged": 50 }
# List your compiled skills.
curl https://twin-browser.com/api/v1/skills -H "Authorization: Bearer ab_live_…"
# -> { "skills": [ ... ] }
# Replay one by name — no LLM in the loop. Takes "target".
curl -X POST https://twin-browser.com/api/v1/skills/acme-export/run \
-H "Authorization: Bearer ab_live_…" -H "Content-Type: application/json" \
-d '{ "target": "https://app.acme.com/invoices" }'
# -> { "success": true, "credits_charged": 1 }Operating the API
Limits, discovery, and the rate card.
Rate limits
Applied per tenant. Over the limit returns 429 with a Retry-After header — back off and retry. Higher tiers raise limits and replay throughput.
OpenAPI + rate card
The surface is an OpenAPI 3.1 document at /api/v1/openapi for client generation and agent discovery, and the live rate card is JSON at /api/v1/pricing.
Auth & audit
Per-tenant keys, default-deny RLS, and an audit log on every call. The target URL is the authorization signal — see the security page.
FAQ
API, answered
How do I authenticate?
How do I tell whether a call hit the cache?
Is there an OpenAPI spec?
How are rate limits enforced?
What does a call cost?
Prefer tools over endpoints?
The same engine is an MCP server for Claude and Cursor, and a one-line adapter for LangChain and AutoGen.