stealthDOCS
SDKs

JavaScript SDK

@stealth-browser/cloud-sdk: a dependency-free client for the platform API, for Node ≥ 20 and browsers.

Install

The package is a workspace package in the repository until the platform launch; publishing to npm follows it. Until then, install it from a checkout:

cURL
npm install /path/to/stealth-browser/packages/cloud-sdk

No runtime dependency beyond the global fetch. It is not the local [stealth-cli SDK](/sdks/cli/), which launches Camoufox in-process; the two share the observation and action vocabulary, not code.

Client

JavaScript
import { StealthBrowser, StealthApiError, verifyWebhook } from '@stealth-browser/cloud-sdk'; const client = new StealthBrowser({ apiKey: process.env.STEALTH_API_KEY, baseUrl: process.env.STEALTH_API_URL });

new StealthBrowser({ apiKey, baseUrl?, fetch?, timeoutMs?, retries? }). The key's project is the project. timeoutMs (60 s) bounds each request; event streams are bounded by an idle watchdog instead. retries (2) applies to idempotent requests only — GETs, and creates, which carry an Idempotency-Key (a UUID unless you pass one).

Surfaces

SurfaceMethods
client.run(task, options)Create and wait; options are run fields (model_id, session_id, browser_id, workspace_id, profile_id, judge, credential_ids, allowed_domains, …) plus pollMs, timeoutMs, signal, idempotencyKey, onEvent
client.runscreate, get, list, cancel, wait(id), events(id) (async iterator), message(id, text), withdrawMessage(id, messageId)
client.agentSessionslist, get, rename(id, title), delete(id)
client.modelslist
client.browserscreate → CloudBrowser, attach(id), get, list, stop
client.assetslist(browserId), get, text, content (bytes), retry
client.profileslist, create, get, update, import, save(id, { browser_id })
client.workspaceslist, create, get, update, sessions; files.list, files.upload(id, path, bytes), files.download, files.text, files.move, files.folder, files.delete, files.archive(id, { ids })
client.scheduleslist, get, history, preview, runNow
client.integrations, client.proxieslist, get; policy, credentials
client.billingget, setBudget(usd | null), statement({ month }), ledger({ limit }), rates
client.history, client.usage, client.projectlist(query), get({ days }), current
client.request(method, path, { body, bytes, query, responseType })Any other route, with the same errors and retries
verifyWebhook({ body, headers, secret })Standard Webhooks v1 check with Web Crypto; returns the event or throws webhook_signature

A revision a write needs (update, move, folder, delete, archive, import, save, retry) is fetched from the resource when you do not pass one. What the API reserves for console sessions — credentials, webhook endpoints, schedule and integration management, deletions — is not here; the API answers permission_denied for a key.

CloudBrowser

JavaScript
const browser = await client.browsers.create({ settings: { locale: 'en-US' } });await browser.ready();                                          // waits for `running`await browser.navigate('https://example.com');                  // { url, title }const state = await browser.observe({ boxes: true });           // the observation object, snake_case as the API sends itconst link = state.targets.find((t) => t.role === 'link');await browser.click(link.ref);const { text } = await browser.text({ format: 'markdown' });    // read-only: keeps the frameconst { matches } = await browser.find({ role: 'button', name: 'sign in' });await browser.stop();

act(name, params) runs any action with the registry's parameter names; the convenience methods are click(ref), type(ref, text, { slowly, submit }), select(ref, values), press(key, ref?), scroll(delta_y, ref?), hover(ref), wait({ ms, state }), clickAt(x, y), back(), forward(), reload(), switchTab(tab), closeTab(tab?), newTab(url?), dialogPolicy(policy, prompt_text?), and the read-only find(query) and text({ format, max_chars }). preview() is the observation plus a JPEG image; snapshot() is the tree alone; events() iterates the browser's events to its terminal state; assets() lists its downloads and recordings; stop() resolves with the final resource even when the API reports cleanup_failed.

Frames: the client holds the last observation's frame_id, observes when it has none, re-observes once on stale_frame, drops the frame after a page-changing action and keeps it after a read-only one.

Events

runs.events(id) and browser.events() open GET …/events?stream=1 and read server-sent events until the end event, reconnecting from the last sequence up to three times when the connection drops or goes quiet for 45 s; when the API answers JSON instead, or refuses the stream with rate_limited (its bound on open streams per key and project), they poll the cursor every pollMs. client.diagnostics counts streams opened and polls made. run(task, { onEvent }) streams on the side while waiting; a timeout or abort stops the stream too.

Errors

Every failure is a StealthApiError: status, code (the API's — see Errors), request_id, details and local. A request the contracts reject is thrown with local: true before anything is sent. Without a response the code is network_error, bad_response, sdk_timeout or aborted.

JavaScript
try {  await browser.click('e99');} catch (error) {  if (error instanceof StealthApiError && error.code === 'stale_frame') { /* the client already retried once */ }  throw error;}

In a browser page

The contracts use globalThis.crypto and a portable IP check, so the package loads in a browser as well as in Node. The API must allowlist the page's origin for that to be useful, and a key in a page is visible to everyone who loads it — see Authentication.