> ## Documentation Index
> Fetch the complete documentation index at: https://predev.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Agents: start with https://docs.pre.dev/agents.md, which has complete recipes, plan access, polling rules, errors and limits.
> Authenticate with the workspace API key (pdk_…) from Integrations → Built-in, sent as Authorization: Bearer <key>.
> REST API: https://api.pre.dev (OpenAPI: https://docs.pre.dev/api-reference/openapi.json). AI Gateway: https://api.pre.dev/v1, OpenAI-compatible (OpenAPI: https://docs.pre.dev/api-reference/ai-gateway.openapi.json).
> pre.dev MCP server: npx -y @predotdev/mcp setup adds it to every local coding agent with every pre.dev tool plus the user's own Chrome; the hosted server is https://api.pre.dev/mcp. Search these docs over MCP at https://docs.pre.dev/mcp.

# Node.js SDK

> Generate specifications and run browser tasks with the published predev-api TypeScript client.

```bash Node.js theme={null}
npm install predev-api
```

```bash Python theme={null}
pip install predev-api
```

The package is ESM and supports Node.js 18+. This guide targets published **1.1.0**.

## Generate and wait for a specification

```typescript theme={null}
import { PredevAPI } from 'predev-api';

const client = new PredevAPI({ apiKey: process.env.PREDEV_API_KEY! });
const job = await client.fastSpecAsync({
  input: 'Build a team task manager with projects and assignees.',
  currentContext: 'Use TypeScript and preserve organization-scoped access.',
});
console.log('Save this ID:', job.specId);
const deadline = Date.now() + 600_000;

async function waitForSpec() {
  while (Date.now() < deadline) {
    const spec = await client.getSpecStatus(job.specId);
    if (spec.status === 'completed') return spec;
    if (spec.status === 'failed') {
      throw new Error(spec.errorMessage ?? 'Specification failed');
    }
    await new Promise(resolve => setTimeout(resolve, 5_000));
  }
  throw new Error('Polling deadline reached; retrieve the saved spec ID later.');
}

const spec = await waitForSpec();
console.log(spec.codingAgentSpecMarkdown);
```

## Method reference

| Method | Parameters | Return |
| - | - | - |
| `fastSpec(options)` | `input`, optional `currentContext`, `docURLs`, `file` | `Promise<SpecResponse>` |
| `deepSpec(options)` | Same | `Promise<SpecResponse>` |
| `fastSpecAsync(options)` | Same | `Promise<AsyncResponse>` |
| `deepSpecAsync(options)` | Same | `Promise<AsyncResponse>` |
| `getSpecStatus(specId)` | Specification ID | `Promise<SpecResponse>` |
| `listSpecs(params)` | Optional `limit`, `skip`, `endpoint`, `status` | `Promise<ListSpecsResponse>` |
| `findSpecs(params)` | Required `query`; same filters | `Promise<ListSpecsResponse>` |
| `getCreditsBalance()` | None | `Promise<CreditsBalanceResponse>` |
| `browserAgent(tasks, options)` | Task array; optional `concurrency`, `stream`, `async` | A Promise, or an async generator when `stream: true` |
| `getBrowserAgent(id, options)` | Run ID; optional `includeEvents` | A Promise of the run |
| `listBrowserAgents(params)` | Optional `limit`, `skip`, `status` | A Promise of `batches`, `total`, `hasMore` |
| `browserAgentStatus()` | None | A Promise of your queue and `cap` |

All methods use `https://api.pre.dev` by default. The constructor accepts `apiKey` and optional `baseUrl`; it has no timeout, retry, or custom-header option. Use direct HTTP when you need those controls. A polling deadline does not abort an in-flight SDK request.

## Specification history and credits

```typescript theme={null}
const page = await client.findSpecs({ query: 'report|export', status: 'completed' });
for (const summary of page.specs) {
  console.log(summary._id, summary.input);
}
const balance = await client.getCreditsBalance();
console.log(balance.creditsRemaining);
```

List and search return summaries. Retrieve a specific ID for full bodies and graphs. Optional artifacts can be absent or null; see [inputs and outputs](https://predev.mintlify.site/architect-agent/inputs-and-outputs.md).

## File uploads

Pass a **Blob with an accepted MIME type**. SDK 1.1.0 encodes its `{ data: Buffer, name }` alternative as `application/octet-stream`, which the API's file filter does not accept.

```typescript theme={null}
import { readFile } from 'node:fs/promises';

const bytes = await readFile('requirements.pdf');
const upload = await client.fastSpecAsync({
  input: 'Create an implementation plan from these requirements.',
  file: new Blob([new Uint8Array(bytes)], { type: 'application/pdf' }),
  docURLs: ['https://www.postgresql.org/docs/current/'],
});
console.log(upload.specId);
```

The SDK serializes `docURLs` correctly for multipart requests. It generates a filename for Blob uploads; use direct multipart HTTP if preserving the original filename matters. See [accepted formats and limits](https://predev.mintlify.site/architect-agent/inputs-and-outputs.md#upload-a-file).

## Run browser tasks and poll

```typescript theme={null}
const tasks = [{
  url: 'https://example.com',
  instruction: 'Extract the main page heading.',
  output: {
    type: 'object',
    properties: { heading: { type: 'string' } },
    required: ['heading'],
  },
}];
const browserJob = await client.browserAgent(tasks, { async: true });
console.log('Save this run ID:', browserJob.id);
const runDeadline = Date.now() + 600_000;

async function waitForRun() {
  while (Date.now() < runDeadline) {
    const run = await client.getBrowserAgent(browserJob.id);
    if (run.status === 'completed' || run.status === 'failed') return run;
    await new Promise(resolve => setTimeout(resolve, 5_000));
  }
  throw new Error('Polling deadline reached; retrieve the saved run later.');
}

const run = await waitForRun();
for (const task of run.results) {
  if (task?.status === 'SUCCESS') console.log(task.data);
  else console.error(task?.error ?? 'No successful result');
}
```

Use batch `status` to detect completion and inspect every task result. The current TypeScript declarations lag some REST fields, including running stubs and nullable result slots; guard missing values at runtime. Published task types do not declare `mode`, `maxSteps`, or `maxDurationSeconds`; use [direct REST](https://predev.mintlify.site/browser-agents/api/run-task.md) when your integration needs those controls without extending the package's types.

## Stream a new browser submission

```typescript theme={null}
let sawDone = false;
for await (const message of client.browserAgent(tasks, { stream: true })) {
  if (message.event === 'task_event') {
    console.log(message.data.taskIndex, message.data.type);
  } else if (message.event === 'done') {
    sawDone = true;
    console.log(message.data.status, message.data.totalCreditsUsed);
  }
}
if (!sawDone) throw new Error('The stream ended before a final result arrived.');
```

`stream: true` returns an async generator; synchronous and async-submission modes return a Promise. The stream example creates a **new run**. Streamed `error` frames raise exceptions. To attach to an existing run, use [direct HTTP SSE](https://predev.mintlify.site/browser-agents/api/stream-task.md).

## Browser history and evidence

```typescript theme={null}
const queue = await client.browserAgentStatus();
console.log(queue.total, queue.cap);

const runs = await client.listBrowserAgents({ limit: 20, status: 'completed' });
for (const summary of runs.batches) console.log(summary.id, summary.totalCreditsUsed);

const evidence = await client.getBrowserAgent(browserJob.id, { includeEvents: true });
console.log(evidence.status);
```

Timelines can be large; omit `includeEvents` for routine polling. See [status and results](https://predev.mintlify.site/browser-agents/api/task-status.md) for event shapes and per-task fields.

## Errors

```typescript theme={null}
import {
  AuthenticationError,
  InsufficientCreditsError,
  PredevAPIError,
  QueueFullError,
  RateLimitError,
} from 'predev-api';

try {
  await client.listSpecs({ limit: 20 });
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Check the API key and account access.');
  } else if (error instanceof RateLimitError) {
    console.error('Back off before retrying.');
  } else if (error instanceof PredevAPIError) {
    console.error(error.message);
  } else {
    throw error;
  }
}

try {
  await client.browserAgent(tasks, { async: true });
} catch (error) {
  if (error instanceof InsufficientCreditsError) {
    console.error(error.message, error.actionUrl);
  } else if (error instanceof QueueFullError) {
    console.error('Wait for tasks to finish before submitting more work.');
  } else if (error instanceof PredevAPIError) {
    console.error(error.message);
  } else {
    throw error;
  }
}
```

The base exception has no `statusCode` property. Browser gate errors have typed subclasses; see [errors and retries](https://predev.mintlify.site/api-reference/errors.md). An Architect `402` without a machine-readable browser gate code raises `PredevAPIError`. Use [direct REST](https://predev.mintlify.site/browser-agents/api/run-task.md#idempotency) for an idempotency key; the SDK does not forward arbitrary options as headers or request fields.

See the [npm package](https://www.npmjs.com/package/predev-api) or the [SDK source](https://github.com/predotdev/predev-api).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.