Skip to main content

SDKs

Node.js SDK

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

npm install predev-api
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

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

MethodParametersReturn
fastSpec(options)input, optional currentContext, docURLs, filePromise<SpecResponse>
deepSpec(options)SamePromise<SpecResponse>
fastSpecAsync(options)SamePromise<AsyncResponse>
deepSpecAsync(options)SamePromise<AsyncResponse>
getSpecStatus(specId)Specification IDPromise<SpecResponse>
listSpecs(params)Optional limit, skip, endpoint, statusPromise<ListSpecsResponse>
findSpecs(params)Required query; same filtersPromise<ListSpecsResponse>
getCreditsBalance()NonePromise<CreditsBalanceResponse>
browserAgent(tasks, options)Task array; optional concurrency, stream, asyncA Promise, or an async generator when stream: true
getBrowserAgent(id, options)Run ID; optional includeEventsA Promise of the run
listBrowserAgents(params)Optional limit, skip, statusA Promise of batches, total, hasMore
browserAgentStatus()NoneA 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

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.

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.

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.

Run browser tasks and poll

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 when your integration needs those controls without extending the package’s types.

Stream a new browser submission

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.

Browser history and evidence

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 for event shapes and per-task fields.

Errors

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. An Architect 402 without a machine-readable browser gate code raises PredevAPIError. Use direct REST for an idempotency key; the SDK does not forward arbitrary options as headers or request fields.

See the npm package or the SDK source.

Was this page helpful?

On this page