Pradra StudioDocs

API surface

Every export of @pradra/sdk in one page. For the underlying HTTP endpoints, see the API reference.

Client

import { Pradra } from "@pradra/sdk";

const pradra = new Pradra({
  baseUrl: "https://your-host",   // host only; /api/v1 is appended
  apiKey: "aif_….…",              // an API token
  token: "eyJ…",                  // …or a JWT access token
  orgId: "…",                     // default for org-scoped calls
});
MethodReturns
login(email, password)TokenPair — also stores it on the client
refresh(refreshToken)TokenPair
predict(capability, inputs, { sessionId?, signal? })PredictResult
predictStream(capability, inputs, { sessionId?, signal? })AsyncGenerator<StreamEvent>

PredictResult is { capability_id, outputs, usage?, cost_usd? }.

Agents

MethodNotes
agents.run(capabilityId, { goal, principal?, sessionId? })Returns as soon as the run is recorded
agents.stream(capabilityId, { goal, … , signal? })Progress events
agents.get(runId)The run with its full trace
agents.list(capabilityId)Run history
agents.wait(runId, { timeoutMs?, intervalMs? })Polls to a terminal state
agents.approve(stepId)Re-checks the parked call from scratch
agents.reject(stepId, reason?)Fed back to the agent as an observation

wait() treats awaiting_approval as terminal for the caller: nothing will change until a human acts, so continuing to poll would be a lie.

Sessions

MethodNotes
sessions.create({ capabilityId, externalRef?, title?, principal? })principal is stamped here and is immutable
sessions.send(sessionId, inputs)Generative answers directly; an agent returns run_id to poll
sessions.messages(sessionId)Transcript in replay order
sessions.list({ capabilityId?, workspaceId?, externalRef?, status? })
sessions.get(sessionId)
sessions.update(sessionId, { title?, status? })Only these two are mutable
sessions.delete(sessionId)Agent traces survive

Knowledge

MethodNotes
knowledge.create(input) / knowledge.upsert(input)Use upsert in deploy scripts
knowledge.ingest(sourceId, textOrFile)Async; poll get() to a terminal status
knowledge.search(sourceId, query, { hybrid?, contextWindow?, topK?, minScore?, sourceVersion?, mode? })
knowledge.get(sourceId) / knowledge.list(workspaceId)

A hit carries citation, score, match (vector / lexical / neighbour), page? and content.

Model adapters, pools, tool registry

MethodNotes
modelAdapters.create({ name, provider, modelIdentifier, apiKey? | secretId?, … })The key is sealed and never returned
modelAdapters.list(orgId?)
modelAdapters.pin(capabilityId, adapterId, orgId?)Pins the exact model for lineage
pools.create(name, description?, orgId?) / pools.list(orgId?)
tools.createMl(body, orgId?)ML-backed tools only
tools.grant(capabilityId, toolId)Always a separate, human-authored step

Worker tools do not go through tools.create* — they auto-register from the worker's manifest.

Serving

import { tool, mlTool, runWorker, mountTools, createWebhookHandler, createToolHandler } from "@pradra/sdk";

tool(def){ name, description?, sideEffect, permission?, costWeight?, routing?, input?, output?, handler }. input is a field map: { order_id: "integer", note: "string?" }. Types: string, text, category, number, float, double, decimal, integer, int, boolean, bool, date, datetime, timestamp. A ? suffix means optional.

runWorker(opts){ baseUrl, apiKey, orgId, pool, tools, contexts?, instanceId?, handlerTimeoutMs?, heartbeatMs?, onError?, fetch? }. Returns a Worker with stop() (drains in-flight handlers) and done.

Handler context — the second argument:

Field
principalThe caller's end-user identity. Scope with this, always.
jobIdStable across retries — use as your idempotency key
runId?The agent run, when it came from one
attempt1 on the first try; only read-only tools are ever retried
signalAborted when the lease is about to lapse or the worker is stopping

WebhooksmountTools(app, opts) for Express-like apps, createWebhookHandler(opts) for standard Request/Response runtimes, createToolHandler(opts) for raw body + headers. Options: { tools, secret, toleranceSeconds?, replayCacheSize?, path? }.

Streaming events

state · delta · tool_call · tool_result · approval_required · done · error, each with a type discriminant. An unrecognized event arrives as { type, …fields } rather than being dropped — new events are additive within a stream version, so ignore what you do not know instead of failing on it.

parseSSE(body) is exported if you need to consume a stream you opened yourself.

Errors

Every non-2xx throws ApiError:

class ApiError extends Error {
  status: number;      // HTTP status
  code: string;        // VALIDATION_FAILED, RATE_LIMITED, …
  requestId?: string;  // quote this when investigating
  details?: Array<{ field?: string; issue?: string }>;
}

A terminal error event inside a stream is yielded, not thrown: by then you may already have received text, and losing it in an exception would be worse than reporting the failure in order. A stream that never opened throws.

Versioning

The package version tracks the API contract version. Both are 0.1.0 today. Pin it: the platform's REST surface is versioned at /api/v1, and new fields are additive within that version.

SDK_VERSION and API_PREFIX are exported if you need to log them.

Next