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
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
import { Olbrain } from '@olbrain/js-sdk';
const olbrain = new Olbrain({ apiKey: process.env.OLBRAIN_ACCESS_KEY }); // ak_…
| Option | Type | Description |
|---|---|---|
apiKey | string | Your Access Key. Must start with ak_. |
baseUrl | string | Defaults to https://webhook.olbrain.com. |
timeoutMs | number | Per-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
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:
{ "run_id": "abfcbeef-6d3a-4ab3-aca7-516cbff72d01", "status": "triggered" }
Methods
Each method maps to one endpoint in Run management and returns its JSON body.
| Method | Endpoint |
|---|---|
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
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.
| Class | When |
|---|---|
BillingError | Your organization can't pay for the run — insufficient_funds, credit_limit_exceeded, subscription_inactive, subscription_suspended, billing_unavailable. |
AuthenticationError | 401 — the key is missing, revoked, or expired. |
NotFoundError | 404 — unknown agent or run, or it belongs to another organization. |
RateLimitError | 429 — retryAfter holds the seconds to wait, when the API sends it. |
ApiError | Any other error response (403, 409, 422, 5xx…). detail holds the response's raw detail field, when there is one. BillingError and NotFoundError extend it. |
NetworkError | The request never got an answer, or ran past timeoutMs. |
ValidationError | Thrown by the constructor for a missing or non-ak_ key. |
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;
}