# TypeScript SDK

> The @browlix/sdk package — typed calls for every operation, safe retries, session helpers and webhook verification.

`@browlix/sdk` has no runtime dependencies and works in Node 20 or later, Bun, Deno, Cloudflare Workers and browsers that may hold a key.

## Install

The package is published by Browlix, with its SHA-256 on the [download page](https://browlix.com/en/download):

```bash
npm i https://browlix.com/api/public/sdk/v1/download/latest
```

## Create a client

```ts
import { Browlix, BrowlixError } from "@browlix/sdk";

const bx = new Browlix({ apiKey: process.env.BROWLIX_API_KEY! }); // the SDK never reads env itself
```

Options: `baseUrl` (default `https://browlix.com`), `attempts` for safe retries (default 3), `timeoutMs` per request (default 60 s), and a custom `fetch`.

## Call any operation

```ts
const { data, page } = await bx.call("profiles.list", { query: { limit: 20, os: "windows" } });

for await (const profile of bx.paginate("profiles.list", { state: "active" })) {
  console.log(profile);
}
```

`call(id, { path, query, body })` is typed from the OpenAPI document for each operation id: the input always, and from SDK `0.9.0` also `data` for every operation whose reply fields are published in the [reference](https://browlix.com/en/docs/reference) (profiles, sessions, bindings, devices, feedback, workspace). `OPERATIONS` lists them. New reply fields can appear without notice — see [Versions and changelog](https://browlix.com/en/docs/api-changelog).

## Writes

Writes get a `command_id` when you omit one and report it as `reply.commandId`. Automatic retries resend the same id, so nothing is applied twice. After `OUTCOME_UNKNOWN`, read the result with `bx.receipt(commandId)`.

## Sessions

```ts
const s = await bx.startSession({ profileId, deviceId, wait: { timeoutMs: 120_000 } });
const a = await bx.attach(s.data.id);
// … connect Playwright on this machine with a.endpoint and a.headers …
await bx.stopSession(s.data.id);
```

`waitForSession` and `startSession({ wait })` only wait. Aborting with `signal` (`INTERRUPTED`) or timing out (`WAIT_TIMEOUT`) leaves the session running. `stopSession` is the only call that ends one.

## Errors

Every failure is a `BrowlixError` with the API's `code`, `retryable`, and `info` (`status`, `details`, `correlationId`, `commandId`, `retryAfterSeconds`). Key-shaped text is redacted from messages.

```ts
try {
  await bx.call("sessions.start", { body: { profile_id: profileId, device_id: deviceId } });
} catch (e) {
  if (e instanceof BrowlixError && e.code === "BUSY") console.log("BUSY", e.info.details);
  else throw e;
}
```

## Webhooks

`verifyWebhook(secret, headers, rawBody)` checks a [Standard Webhooks](https://www.standardwebhooks.com) signature and timestamp (5-minute tolerance) and returns the parsed event — see [Webhooks](https://browlix.com/en/docs/webhooks).
