# TypeScript SDK (@bria-ai/client)

[`@bria-ai/client`](https://www.npmjs.com/package/@bria-ai/client) is Bria's official TypeScript and JavaScript SDK. It wraps the v2 REST API with a single async client, handles the async job lifecycle (submit, poll, webhooks), retries transient failures, and converts local images to API inputs for you. It mirrors the [Python SDK](/integration-methods/python-sdk): same method names, same behavior.

- Source and issues: [github.com/Bria-AI/bria-client](https://github.com/Bria-AI/bria-client) (MIT)
- Node.js 20 or newer. No runtime dependencies: it uses the built-in `fetch`, `FormData` and `Blob`.
- Ships ESM and CommonJS builds with type declarations.
- Covers every v2 endpoint. Legacy `/v1` endpoints are not reachable through the SDK; call them with `fetch`.


## Install and authenticate

```bash
npm install @bria-ai/client
export BRIA_API_TOKEN="<your-bria-api-token>"
```

The client reads `BRIA_API_TOKEN` from the environment. You can also pass `apiToken` to the constructor, or override it per call with `apiToken` in the options of any method. Get a token from the [Bria Console](https://platform.bria.ai/organization-management/api-keys).

## Your first call

```typescript
import { BriaClient, Image } from "@bria-ai/client";

const client = new BriaClient();

const response = await client.run("image/edit/remove_background", {
  image: new Image("https://labs-assets.bria.ai/sandbox-example-inputs/remove_background_example.jpg").asBriaApiInput,
});

console.log(response.status); // COMPLETED
console.log(response.result?.image_url); // URL of the cutout
```

The first argument is the v2 path without the `/v2/` prefix, exactly as it appears in the API reference: `image/generate`, `image/edit/remove_background`, `video/edit/remove_background`, and so on.

## One client

JavaScript has no synchronous HTTP, so there is a single `BriaClient` and every method returns a `Promise`. Where the Python SDK offers `BriaSyncClient` and `BriaAsyncClient`, here you `await` (or run many calls with `Promise.all`).

## Three request methods

Bria's v2 endpoints are asynchronous by default. The SDK gives you three ways to work with that:

| Method | What it does | Returns |
|  --- | --- | --- |
| `client.run(endpoint, payload, options?)` | Sends the request with `sync: true` and resolves when the result is ready. Simplest for interactive use. | `BriaResponse` with `result` |
| `client.submit(endpoint, payload, options?)` | Sends the request with `sync: false` and resolves immediately. Pass `webhookUrl` in the options to be called back. | `BriaResponse` with `requestId` and `statusUrl` |
| `client.poll(responseOrRequestId, options?)` | Polls the [status endpoint](/status) until the job finishes. `interval` and `timeout` are in seconds (defaults 1 and 60). Throws when the timeout elapses. | `BriaResponse` with `result` |


Do not put `sync` in your payload; `run` and `submit` set it for you and throw if you pass it.

```typescript
// Submit now, collect later
const job = await client.submit("video/segment/mask_by_prompt", { video: videoUrl, prompt: "the red car" });
console.log("submitted:", job.requestId);

const final = await client.poll(job, { interval: 2, timeout: 600 });
console.log(final.result?.video_url);
```

`client.status(requestId)` returns just the current status (`IN_PROGRESS`, `COMPLETED`, `ERROR`, `UNKNOWN`) if you manage polling yourself. Every method also accepts an `AbortSignal` as `signal` to cancel a call.

## Responses and errors

Every method returns a `BriaResponse`:

| Field | Description |
|  --- | --- |
| `requestId` | Job identifier; also the deduplication key for webhooks |
| `status` | `IN_PROGRESS`, `COMPLETED`, `ERROR` or `UNKNOWN` (the `Status` enum) |
| `result` | Endpoint result as an open object, `null` until the job completes: `result?.image_url`, `result?.video_url`, `result?.seed`, `result?.structured_prompt` |
| `error` | `code`, `message`, `details` when the request failed, otherwise `null` |
| `statusUrl` | Polling URL for async jobs |


Pass `raiseForStatus: true` to turn API errors into a thrown `BriaException` instead of inspecting `response.error`. `poll` does this by default.

```typescript
import { BriaClient, BriaException } from "@bria-ai/client";

const client = new BriaClient();

try {
  const response = await client.run("image/generate", { prompt: "a matte black travel mug" }, { raiseForStatus: true });
  console.log(response.result?.image_url);
} catch (err) {
  if (err instanceof BriaException) console.error("Bria API error:", err.code, err.message, err.details);
  else throw err;
}
```

Requests time out after 30 seconds by default (`requestTimeout`, in seconds, on the constructor) and throw a `BriaException` with code `408`. `GET` requests, including polling, are retried on `429`, `502`, `503`, `504` and on network errors (3 attempts with exponential backoff, honoring `Retry-After`). `run` and `submit` are never retried, so a job is never started twice; retry those yourself only when you know the submission did not go through.

## Images: URLs, files, bytes

`Image` turns any of these into the string the API expects, either the URL as-is or a raw base64 payload without the `data:` prefix:

```typescript
import { readFile } from "node:fs/promises";
import { Image } from "@bria-ai/client";

new Image("https://example.com/photo.jpg").asBriaApiInput; // public URL, passed through
new Image("./photo.png").asBriaApiInput; // local path, encoded to base64
new Image(await readFile("./photo.png")).asBriaApiInput; // Buffer or Uint8Array
(await Image.fromBlob(new Blob([await readFile("./photo.png")]))).asBriaApiInput; // Blob, e.g. a browser File
```

The client also drops `null` and `undefined` values from payloads, so optional parameters can be left unset without being sent.

## Webhooks

Instead of polling, ask Bria to POST the result to your server when the job completes. `webhookUrl` is an option of `submit`, not part of the payload:

```typescript
const job = await client.submit(
  "image/generate",
  { prompt: "a serene mountain landscape at dawn" },
  { webhookUrl: "https://your-app.example.com/api/bria/webhook" },
);
```

Verify every delivery with the bundled HMAC-SHA256 helper before acting on it:

```typescript
import { verifyWebhookSignature } from "@bria-ai/client";

// Any framework: pass the raw request body and the three Bria-Webhook-* headers.
function isValid(rawBody: string, headers: Record<string, string>): boolean {
  return verifyWebhookSignature({
    payload: rawBody,
    webhookId: headers["bria-webhook-id"] ?? "",
    timestamp: headers["bria-webhook-timestamp"] ?? "",
    signatureHeader: headers["bria-webhook-signature"] ?? "",
    apiToken: process.env.BRIA_API_TOKEN ?? "",
  });
}
```

See [webhooks](/getting-started/async-requests#webhooks) for the delivery contract, retries and a complete Express receiver.

## Video upload

Video endpoints take a hosted URL. `client.upload()` wraps the [Video Upload Service](/local-video-upload-service): it requests a presigned upload, uploads the file, and returns a URL that stays valid for about 24 hours. Pass the file's MIME type; only `video/*` types are accepted.

```typescript
const fileUrl = await client.upload("path/to/video.mp4", "video/mp4");

const job = await client.submit("video/edit/remove_background", { video: fileUrl });
const result = await client.poll(job, { interval: 5, timeout: 600 });
console.log(result.result?.video_url);
```

`upload` also takes a `Buffer`, `Uint8Array` or `Blob` instead of a path. Treat the returned URL as a secret: anyone holding it can download the file.

## Batch processing

Submit everything first, then poll. `Promise.all` runs the calls concurrently on one event loop:

```typescript
import { BriaClient, Image } from "@bria-ai/client";

const client = new BriaClient();
const paths = ["sku-1.jpg", "sku-2.jpg", "sku-3.jpg"];

const jobs = await Promise.all(
  paths.map((p) => client.submit("image/edit/remove_background", { image: new Image(p).asBriaApiInput })),
);
const results = await Promise.all(jobs.map((job) => client.poll(job, { timeout: 120 })));
for (const [i, r] of results.entries()) console.log(paths[i], r.result?.image_url);
```

Stay within your plan's [rate limits](/getting-started/rate-limits-and-errors): cap how many jobs you submit at once.

## Configuration reference

| Setting | Constructor option | Environment variable | Default |
|  --- | --- | --- | --- |
| API token | `apiToken` | `BRIA_API_TOKEN` | required |
| Base URL | `baseUrl` | `BRIA_BASE_URL` | `https://engine.prod.bria-api.com` |
| Request timeout | `requestTimeout` (seconds) |  | `30` |
| Retries | `retry` (`{ total, backoffFactor }`) |  | `{ total: 3, backoffFactor: 2 }` |
| Extra headers | `defaultHeaders` |  | none |


Contributions and bug reports are welcome on [GitHub](https://github.com/Bria-AI/bria-client/issues); the repository README covers the development setup.