Puras JS/TS SDK Reference
Auto-generated reference for the puras npm package — call your deployed skills from Node, React, and React Native / Expo.
The puras npm package is the JavaScript counterpart of the
Python client: it submits jobs to your
deployed skills and hands back the result. Zero dependencies, one
fetch-based client that runs unchanged in Node (≥18),
browsers/React, and React Native / Expo.
npm install puras
It is the client half only — the in-skill runtime (puras.media,
puras.drive, …) and the deploy CLI ship with the Python package
(pip install puras).
Address a skill the same way you see it on its page — a
workspace/skillpack/skill path you can copy straight from the playground:
import { Puras } from "puras";
const puras = new Puras(); // PURAS_API_KEY from env (Node)
const ad = await puras.run("acme/ugc-ads/ugc-ad", {
product: "https://example.com/product",
duration: 15,
});
console.log(ad.video); // signed media URL
For your own skills, set a default skillpack once and call skills bare:
const puras = new Puras({ apiKey, skillpack: "ugc-ads" });
const ad = await puras.run("ugc-ad", { product: url });
Auth is a workspace API key. In a backend, use a secret key
(puras_live_…). In a mobile/web app, use a publishable key
(puras_pub_…) — it can only submit jobs and read the jobs it submitted,
and can be fenced to specific skills and a daily spend cap when minted in
the dashboard (see API keys). Never ship a puras_live_
key inside an app bundle.
Puras
Call deployed skills. Three ways to consume a job, by increasing involvement:
- one await —
run()submits and polls to completion, returning the skill'sresult(orsubmit(…, { wait: true })for short jobs, which holds the HTTP request open server-side instead); - async —
submit()returns the job id immediately; check back later withget()/wait(); - live events (SSE) —
events()yields the job's event stream (step,tool_call,log, …) as it happens, for progress UIs.
Pass a fully-qualified workspace/skillpack/skill path to run/submit
(copyable from a skill's page), or set a default skillpack once — a slug
or UUID — and call skills by bare name.
Client is exported as an alias of Puras, matching the Python SDK's puras.Client.
new Puras(options?)
export interface PurasOptions {
/** Workspace API key (`puras_live_…` or `puras_pub_…`). Falls back to the
* `PURAS_API_KEY` env var when running under Node. */
apiKey?: string;
/** Default skillpack (slug or UUID) for bare skill names. */
skillpack?: string;
/** API origin; defaults to https://api.puras.co (or `PURAS_API_BASE`). */
apiBase?: string;
/** Per-request timeout in ms for non-streaming calls. Default 120 000. */
timeoutMs?: number;
/** Custom fetch implementation (tests, polyfills). Defaults to global fetch. */
fetch?: typeof fetch;
}
puras.submit(skill, inputs?, options?)
async submit(
skill: string,
inputs: Record<string, unknown> = {},
options: SubmitOptions = {},
): Promise<Job>
Submit a job and return the job object ({id, status, …}) immediately —
or, with wait: true, after it finishes (server-side wait, bounded by
timeoutSeconds).
skill may be a fully-qualified path — "workspace/skillpack/skill"
(copyable from the skill's page) or "skillpack/skill" for one of your
own — in which case the skillpack is taken from the path. A bare skill
name uses the default skillpack passed here or to new Puras(...).
puras.run(skill, inputs?, options?)
async run(
skill: string,
inputs: Record<string, unknown> = {},
options: RunOptions = {},
): Promise<Record<string, unknown>>
Submit + wait, returning the skill's result object. Throws JobError
(carrying the full job) if the job failed, was cancelled, or is still
running when timeoutMs (default 10 minutes) elapses.
Waiting is client-side polling (pollIntervalMs, default 2 s) rather
than a held connection — media skills run for minutes, longer than any
proxy keeps a request open, and React Native's fetch can't stream.
puras.get(jobId)
async get(jobId: string): Promise<Job>
Fetch a job by id (status, result, error, outputs with signed URLs).
puras.wait(jobId, options?)
async wait(jobId: string, options: WaitOptions = {}): Promise<Job>
Poll a job until it reaches a terminal status (succeeded / failed /
cancelled) or timeoutMs elapses; returns the last-seen job either way.
puras.events(jobId, options?)
async *events(jobId: string, options: EventsOptions = {}): AsyncGenerator<JobEvent>
Stream a job's events live — an async iterator that yields each
JobEvent as the worker emits it and ends when the job reaches a
terminal status:
const job = await puras.submit("acme/ugc-ads/ugc-ad", { product: url });
for await (const ev of puras.events(job.id)) {
console.log(ev.type, ev.payload); // step, tool_call, log, …
}
const done = await puras.get(job.id); // terminal by now
Transport is picked automatically: Server-Sent Events over a streaming
fetch (GET /v1/jobs/{id}/stream) where the runtime supports it
(Node ≥18, browsers), falling back to polling
GET /v1/jobs/{id}/events on runtimes that can't stream (React
Native / Expo). Force one with transport: "sse" | "poll". The stream
replays the job's full event backlog first, so a viewer attached late
still sees everything (resume with afterId).
Types
/** A job as returned by the API. Extra fields ride along untyped. */
export interface Job {
id: string;
status: string;
result?: Record<string, unknown> | null;
error?: string | null;
skill_name?: string;
created_at?: string;
[key: string]: unknown;
}
/** One job event (`step`, `tool_call`, `log`, …) as emitted by the worker. */
export interface JobEvent {
id: number;
type: string;
payload: Record<string, unknown> | null;
[key: string]: unknown;
}
export interface SubmitOptions {
/** Skillpack override (slug, `workspace/skillpack` path, or UUID). */
skillpack?: string;
/** Pin the run to a specific deployment version; omit for the active one. */
version?: number;
/** Block server-side until the job finishes or `timeoutSeconds` elapses
* (synchronous mode — the HTTP request itself waits). Short jobs only;
* for multi-minute media jobs prefer `run()` or `wait()`. */
wait?: boolean;
/** Server-side wait budget in seconds (only with `wait: true`). Default 60. */
timeoutSeconds?: number;
}
export interface WaitOptions {
/** Give up after this long. Default 600 000 (10 min — media jobs take minutes). */
timeoutMs?: number;
/** Poll interval. Default 2000. */
pollIntervalMs?: number;
}
export interface EventsOptions {
/** Yield only events with id greater than this (resume a dropped stream). */
afterId?: number;
/** `"sse"` = live Server-Sent Events over a streaming fetch; `"poll"` =
* repeated `GET /v1/jobs/{id}/events` (works everywhere, incl. React
* Native, whose fetch can't stream). Default `"auto"`: SSE when the
* runtime supports response streaming, else poll. */
transport?: "auto" | "sse" | "poll";
/** Poll interval for the poll transport. Default 1500. */
pollIntervalMs?: number;
/** Abort the stream early (e.g. when a screen unmounts). */
signal?: AbortSignal;
}
export type RunOptions = Pick<SubmitOptions, "skillpack" | "version"> & WaitOptions;
Errors
PurasAPIError
Non-2xx response from the Puras API. Notable cases: 402 insufficient
credits, 403 skill not in the key's allowlist, 429 the key's daily
spend cap is reached.
JobError
A job finished in a non-succeeded state (failed / cancelled / still
running at timeout). .job holds the full job object.