Jobs · 02

Jobs SDK.

@olbrain/js-sdk wraps the workflow endpoints in one typed client — start a run, read and control it, and resolve its approvals. It uses the same Access Key as every other request.

Install

npmshell
npm install @olbrain/js-sdk

This page describes version 1.4.0; the Olbrain client needs 1.2.0 or later. The same client is in the Python SDK from 0.5.0. It runs anywhere fetch does — Node.js 18+, Deno, Bun, and the browser.

The package ships a complete offline API reference at node_modules/@olbrain/js-sdk/AGENTS.md, written for coding assistants.

Create a client

ClientTypeScript
import { Olbrain } from '@olbrain/js-sdk';

const olbrain = new Olbrain({ apiKey: process.env.OLBRAIN_ACCESS_KEY }); // ak_…
OptionTypeDescription
apiKeystringYour Access Key. Must start with ak_.
baseUrlstringDefaults to https://webhook.olbrain.com.
timeoutMsnumberPer-request timeout. Defaults to 30000.

Keep the key on your server — never ship it to a browser. Olbrain's own front ends pass getIdToken (a signed-in user's token) instead of apiKey; give one or the other, not both.

The client also has an olbrain.research namespace for research agents. It works only with getIdToken; an Access Key gets NotFoundError (agent_not_found). To chat with a conversational agent, use AgentClient instead.

Start a run

workflows.runTypeScript
const { runId, status } = await olbrain.workflows.run('agt_01HAKZ...', {
  payload: { order_id: 'ord_8842', invoice: 'fi_01HZQ...' },
  variables: { region: 'IN' },
});
// status: "triggered"

payload is the run's input — reference uploaded files by their fi_* id. variables override the workflow's default variables for this run. Both are optional. The run always uses the agent's published version.

This calls POST /api/workflows/{agent_id}/trigger, which returns:

Response shapeJSON
{ "run_id": "abfcbeef-6d3a-4ab3-aca7-516cbff72d01", "status": "triggered" }

Methods

Each method maps to one endpoint in Run management and returns its JSON body.

MethodEndpoint
workflows.run(agentId, { payload?, variables? })POST /api/workflows/{agent_id}/trigger
runs.list(agentId, { status?, limit? })GET /api/agents/{agent_id}/runs
runs.get(agentId, runId)GET /api/agents/{agent_id}/runs/{run_id}
runs.cancel(agentId, runId)POST …/runs/{run_id}/cancel
runs.pause(agentId, runId)POST …/runs/{run_id}/pause
runs.retry(agentId, runId)POST …/runs/{run_id}/retry
runs.resume(agentId, runId)POST …/runs/{run_id}/resume
approvals.list(agentId, runId)GET …/runs/{run_id}/approvals
approvals.resolve(agentId, runId, input) — input is { exceptionId, resolution, modifiedData? }POST …/runs/{run_id}/approvals/resolve

Resolve approvals

ApprovalsTypeScript
const run = await olbrain.runs.get(agentId, runId);

if (run.status === 'needs_review') {
  for (const a of await olbrain.approvals.list(agentId, runId)) {
    await olbrain.approvals.resolve(agentId, runId, {
      exceptionId: a.exception_id,
      resolution: 'approved',           // or 'rejected', or 'modified' with modifiedData
    });
  }
}

The run resumes by itself once its last blocking approval is resolved. See Approvals for the response fields.

Errors

Every failed call throws. API errors carry the HTTP status and the error code.

ClassWhen
BillingErrorYour organization can't pay for the run — insufficient_funds, credit_limit_exceeded, subscription_inactive, subscription_suspended, billing_unavailable.
AuthenticationError401 — the key is missing, revoked, or expired.
NotFoundError404 — unknown agent or run, or it belongs to another organization.
RateLimitError429 — retryAfter holds the seconds to wait, when the API sends it.
ApiErrorAny other error response (403, 409, 422, 5xx…). detail holds the response's raw detail field, when there is one. BillingError and NotFoundError extend it.
NetworkErrorThe request never got an answer, or ran past timeoutMs.
ValidationErrorThrown by the constructor for a missing or non-ak_ key.
Handling errorsTypeScript
import { ApiError, BillingError } from '@olbrain/js-sdk';

try {
  await olbrain.runs.cancel(agentId, runId);
} catch (err) {
  if (err instanceof BillingError) notifyBilling(err.code);
  else if (err instanceof ApiError && err.code === 'invalid_state_transition') refresh();
  else throw err;
}