> For the complete documentation index, see [llms.txt](https://docs.avastudio.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.avastudio.com/api-and-mcp/sdk.md).

# JavaScript and TypeScript SDK

Use Ava Studio from your Node.js application with [`@hologramlabs/ava-sdk`](https://www.npmjs.com/package/@hologramlabs/ava-sdk). Every API tool is available as a typed method: manage brands, research, draft, run campaigns, generate media, edit projects and export.

The SDK calls the same hosted API as REST and MCP. Your work appears in Ava Studio, and the same [permissions](/api-and-mcp/api-keys.md) and [credit prices](/api-and-mcp/credits-and-pricing.md) apply.

### Install

Requires **Node.js 22.14 or newer**. You do not need Bun or access to Ava's source repository.

```bash
npm install @hologramlabs/ava-sdk
```

This guide targets SDK **0.1.3**, supporting API contract **0.8.0** and its 76 tools, including [the project planner](/api-and-mcp/project-agent.md). The original 0.1.0 release supports contract 0.5.0. Package versions and API contract versions are independent. The SDK includes the shared schemas and types; there is no separate spec package to install.

### Create an API key

In Ava Studio, open **Settings > Developers**, create a key and copy it once. A paid plan is required. New keys default to **Read only**, which is enough for the first example below. Enable **Research** for the research example and **Write drafts** for drafting. Use the [permissions reference](/api-and-mcp/api-keys.md#permissions) for other tools, and set a monthly budget for unattended scripts.

Set `AVA_API_KEY` in your environment or secret manager. In Bash:

```bash
read -rs AVA_API_KEY && export AVA_API_KEY
```

Paste the key, then press Enter. Use the SDK from your server or local script so the key stays out of browser bundles and source control. The SDK uses API keys; connecting an MCP app with OAuth is covered in [Connect your AI app](/api-and-mcp/connect.md).

### Make your first call

Save this as `ava-example.mjs`:

```javascript
import { AvaClient } from "@hologramlabs/ava-sdk";

const apiKey = process.env.AVA_API_KEY;
if (!apiKey) throw new Error("Set AVA_API_KEY before running this script.");

const ava = new AvaClient({ apiKey });
const account = await ava.tools.whoami({});

console.log("Credits:", account.credits_balance);
console.log("Brands:", account.brands);
```

Run it with:

```bash
node ava-example.mjs
```

Every method under `ava.tools` uses the tool name and input fields from the [tool reference](/api-and-mcp/tools.md). In TypeScript, inputs and outputs are inferred. You can also call a tool by name with `ava.call(name, input)`.

For example, after enabling **Research**, add this to the script. This call uses credits:

```javascript
const trends = await ava.tools.research_trends({
  niche: "protein snacks",
  limit: 5,
});

console.log(trends);
```

### Run a longer task

Tools such as `draft_briefs`, `plan_run`, `creative_produce` and media generation return a job. `ava.runJob()` starts the tool, polls until it finishes, and returns its result. It throws an `AvaApiError` if the job fails.

With **Write drafts** enabled, add this to the script, replacing `YOUR_BRAND_ID` with an id from `account.brands`. Drafting uses credits:

```javascript
const result = await ava.runJob(
  "draft_briefs",
  { brand_id: "YOUR_BRAND_ID", count: 1 },
  {
    idempotencyKey: "launch-brief-001",
    timeoutMs: 20 * 60 * 1000,
    onProgress(job) {
      console.error(job.status, job.progress?.label ?? "");
    },
  },
);

console.log(result);
```

Use a different idempotency key for each new action. Reuse `launch-brief-001` with the same input only when retrying this action.

If you need to save the job id before waiting, start it with `ava.tools.draft_briefs()` instead:

```javascript
const accepted = await ava.tools.draft_briefs(
  { brand_id: "YOUR_BRAND_ID", count: 1 },
  { idempotencyKey: "launch-brief-002" },
);

console.log("Save this job id:", accepted.job_id);
const job = await ava.waitForJob(accepted.job_id, {
  timeoutMs: 20 * 60 * 1000,
});

if (job.status === "failed") {
  console.error(job.error);
} else {
  console.log(job.result);
}
```

`waitForJob()` returns the final job, including failed jobs; check its `status`. It polls every 2 seconds and waits up to 10 minutes by default. Set `intervalMs`, `timeoutMs` or `onProgress` to change that behavior. You can pass an `AbortSignal` as `signal` to stop waiting. A timeout or stopped wait does not cancel the server's work: save the job id and check it again rather than starting a new action.

### Retries and idempotency

The SDK automatically retries transient errors with backoff, up to three retries by default. Paid and write calls receive an idempotency key, and their automatic retries reuse it.

A separate method call gets a fresh key unless you supply `idempotencyKey`, as above. Persist that key with your own operation id when an action may be retried after a process restart. Keep the input unchanged. See [Jobs, retries and errors](/api-and-mcp/jobs-retries-and-errors.md#retries) for the server's replay window and conflict behavior.

### Handle errors

```javascript
import { AvaApiError } from "@hologramlabs/ava-sdk";

try {
  console.log(await ava.tools.brand_get({ brand_id: "YOUR_BRAND_ID" }));
} catch (error) {
  if (!(error instanceof AvaApiError)) throw error;
  console.error(error.code, error.message);
  if (error.hint) console.error(error.hint);
  if (error.requestId) console.error("Request id:", error.requestId);
}
```

`forbidden` means the key lacks a permission or the account needs a paid plan. `insufficient_credits` means the account balance cannot cover the work; `budget_exceeded` means the key's monthly budget cannot admit it. Fix those conditions before retrying. A failed media job can still have charges for completed media; see [Credits and pricing](/api-and-mcp/credits-and-pricing.md).

### Upload local files

The upload helpers get a signed upload URL, send the file, register it and wait for the import job. Brand media import needs **Edit brands** and costs credits for analysis:

```javascript
import { openAsBlob } from "node:fs";

const imported = await ava.uploadBrandMedia(
  "YOUR_BRAND_ID",
  [{ filename: "product.mp4", body: await openAsBlob("./product.mp4"), label: "broll" }],
);

console.log(imported);
```

Use `ava.uploadLibraryMedia()` for reusable Studio library files; it needs **Produce media**. Both helpers accept 1–10 files. Their lower-level counterparts, `uploadMediaFile()` and `uploadLibraryFile()`, upload one file and return its `media_url` without registering it. If registration must survive a process restart, save that URL and register it with `brand_media_add` or `library_add`, retaining the same input and idempotency key for retries.

### Configure the client

```javascript
const ava = new AvaClient({
  apiKey: process.env.AVA_API_KEY ?? "",
  apiUrl: "https://mcp.avastudio.com",
  clientName: "my-ava-integration",
  requestTimeoutMs: 120_000,
  retry: { maxRetries: 3 },
});
```

`apiUrl` is the server's base URL, without `/v1` or `/mcp`. `requestTimeoutMs` controls each HTTP request; job polling has its own `timeoutMs`. Set `retry: false` to disable automatic request retries.

Common types such as `ToolInput`, `ToolOutput`, `ToolJobResult` and `Job` are exported from `@hologramlabs/ava-sdk`. Full tool schemas and metadata are available from `@hologramlabs/ava-sdk/spec`.

### Next

* Browse the [tool reference](/api-and-mcp/tools.md) for inputs, outputs, permissions and prices.
* Use the [CLI](/api-and-mcp/cli.md) for the same tools from your terminal.
* Read [API keys and permissions](/api-and-mcp/api-keys.md) and [Jobs, retries and errors](/api-and-mcp/jobs-retries-and-errors.md) before running unattended work.
