Skip to content

Guides

TypeScript SDK

@lumorig/sdk: a typed, zero-dependency client for Node 18+, Bun, Deno and browsers.

@lumorig/sdk is a thin, typed client for the REST API. It has no dependencies and uses the platform fetch, so it runs in Node 18+, Bun, Deno and browsers. The MCP server and the lumorig CLI are built on it.

Install#

terminal
$ npm install @lumorig/sdk

Create a client#

lumorig.ts
import { Lumorig } from "@lumorig/sdk";

const lumorig = new Lumorig({
  apiKey: process.env.LUMORIG_API_KEY, // the default
  baseUrl: "https://lumorig.com",      // default: LUMORIG_URL, then https://lumorig.com
});
LumorigOptions
NameTypeDescription
apiKeystringYour lr_sk_… key. Defaults to LUMORIG_API_KEY.
baseUrlstringServer origin, without /api/v1. Defaults to LUMORIG_URL, then https://lumorig.com.
fetchtypeof fetchA custom fetch (testing, proxies, edge runtimes).

Resources#

Methods mirror the REST endpoints one to one. Anything that returns a Job is asynchronous server-side; see the next section for waiting.

lumorig.uploads#

MethodWhat it does
file(data, filename = "mascot.png")Upload bytes (Blob, ArrayBuffer or Uint8Array) as multipart. PNG, JPG, WebP, SVG, or an MP4/MOV/WebM reference video. → FileObject
url(url)Have the server fetch an image or video from a URL. → FileObject

lumorig.mascots#

MethodWhat it does
list()Mascots in the workspace (newest first). → Mascot[]
get(id, { includeSpec? })One mascot; includeSpec adds the full MascotSpec as `spec`. → Mascot
create(input)Start generation (prompt) or a rebuild (upload_id / image_url). Returns at once. → { mascot, job }
createAndWait(input, waitOptions?)create, wait for the job, then get. Throws job_failed if the job doesn't succeed. → Mascot
import(spec, name?)Create a mascot directly from a MascotSpec. Synchronous. → Mascot
update(id, { name?, public? })Rename, or make public for permanent embeds. → Mascot
delete(id)Delete the mascot.
spec(id, versionId?)The current (or a past) MascotSpec.
putSpec(id, spec)Save a hand-edited spec as a new version. → { mascot, warnings }
recolor(id, palette)Instant palette change by token, e.g. { fur: "#3b82f6" }. → Mascot
edit(id, instruction, max_cost_usd?)Change the design in words. → Job
variant(id, name, instruction)Rig-compatible look variant (outfits, seasonal). → Job
versions(id)Version history with preview URLs.
restore(id, versionId)Make a past version current. → Mascot
share(id, ttl_seconds?)Signed, expiring bundle URL for a private mascot. → { token, expires_at, bundle_url }
bundle(id)The <lumorig-player> bundle: { spec, clips, canvases }.
preview(id, { format?, animation?, t?, size? })A rendered still as an ArrayBuffer (png by default; svg, sticker, logo).
previewUrl(id, opts?)The absolute URL of that still, for an <img>.

lumorig.animations#

MethodWhat it does
list(mascotId)Saved animations. → Animation[]
get(mascotId, id)One animation, including its clip.
preset(mascotId, preset, { intensity?, duration?, name? })Instant, free preset fitted to the rig. → Animation
fromPrompt(mascotId, prompt, { duration?, name?, max_cost_usd? })Custom motion from a description. → Job (result.animation)
fromPromptAndWait(mascotId, prompt, opts?)fromPrompt plus wait. Throws job_failed on failure. → Animation
fromVideo(mascotId, reference_upload_id, { prompt?, duration?, name?, max_cost_usd? })Perform the motion in an uploaded reference video. → Job
batch(mascotId, prompts, { duration? })Up to 8 described motions, one job each. → Job[]
suggestions(mascotId, use_case?)Signature-motion ideas; waits and returns the finished job (result.suggestions).
delete(mascotId, id)Delete an animation.

lumorig.exports · lumorig.scenes · lumorig.files#

MethodWhat it does
exports.create(mascotId, input)Start an export. → Job (result.file)
exports.createAndDownload(mascotId, input, waitOptions?)Export, wait and download. → { file, data: ArrayBuffer }
scenes.create(mascotId, { prompt, size?, animation?, t?, max_cost_usd? })Illustrated vector scene. → Job (result: { svg, png, summary })
files.download(id)File bytes. → ArrayBuffer

lumorig.canvases#

MethodWhat it does
templates()chatbot, ai_agent and onboarding, with their graphs.
list(mascotId)Canvases on a mascot.
create(mascotId, { name, template?, graph? })From a template or a custom graph.
update(mascotId, id, { name?, graph? })Rename or replace the graph.
delete(mascotId, id)Delete a canvas.

lumorig.jobs · lumorig.webhooks · account#

MethodWhat it does
jobs.get(id)Current state of a job.
jobs.list({ status?, type?, mascot_id?, limit? })Recent jobs, filtered.
jobs.cancel(id)Cancel a queued or running job (credits are refunded).
jobs.wait(id, waitOptions?)Long-poll until the job finishes. → Job
jobs.events(id, after = 0, signal?)Async iterator over the job's events: history first, then live.
webhooks.list() · create(url, events = ["*"]) · delete(id)Manage webhook endpoints. create returns the signing secret once.
presets() · styles()The preset and style catalogs (no key needed).
credits()Plan, balance and the credit price of each action.
usage()Model spend, total and by job type.
analyze(url)Website → brand brief and three mascot concepts. → Job
request(method, path, body?, init?)Low-level call to any /api/v1 path. Throws LumorigError on non-2xx.
url(path)Absolute URL for a path the API returned (file.url, preview_url, bundle_url).

End to end#

launch.ts
import fs from "node:fs";
import { Lumorig } from "@lumorig/sdk";

const lumorig = new Lumorig();

const mascot = await lumorig.mascots.createAndWait({ prompt: "a grumpy cactus who loves hugs", style: "kawaii" });
await lumorig.animations.preset(mascot.id, "celebrate", { intensity: 1.5 });
const { data } = await lumorig.exports.createAndDownload(mascot.id, { format: "lottie", animation: "celebrate" });
fs.writeFileSync("cactus.lottie.json", Buffer.from(data));

Waiting on jobs#

jobs.wait(id, options) long-polls GET /jobs/{id}?wait=60 until the job reaches succeeded, failed or canceled, and returns the job. It does not throw on a failed job: check job.status. The *AndWait and createAndDownload helpers do throw, with code job_failed.

WaitOptions
NameTypeDescription
timeoutMsnumberGive up after this long (default 15 minutes). Throws LumorigError with status 408 and code timeout; the job keeps running.
signalAbortSignalStop waiting early. Aborting the wait does not cancel the job; call jobs.cancel for that.
onEvent(e: JobEvent) => voidStream live progress (SSE) while waiting.
wait.ts
const { job } = await lumorig.mascots.create({ image_url: "https://example.com/logo.png" });
const done = await lumorig.jobs.wait(job.id, { timeoutMs: 10 * 60_000 });

if (done.status === "succeeded") console.log("fidelity", done.result.fidelity);
else console.error(done.status, done.error);

Streaming events#

Every job has an event log: queued, started, progress (human-readable notes), tool_call, tool_input_delta, tool_result, draft (an intermediate spec while it draws), draft_clip (an intermediate motion), usage (running cost) and finished (with the final status and error). Each event has seq, at and type. jobs.events replays the history after after, then follows live until finished.

events.ts
for await (const e of lumorig.jobs.events(job.id)) {
  if (e.type === "progress") process.stdout.write(String(e.text));
  if (e.type === "usage") console.log("spent so far $", e.cost_usd);
}

// or, while waiting:
await lumorig.jobs.wait(job.id, { onEvent: (e) => console.log(e.type) });

Errors#

Every non-2xx response throws a LumorigError carrying the API's error body. status is the HTTP status, code the stable machine-readable code, details extra data (validation messages, or the credit shortfall).

errors.ts
import { LumorigError } from "@lumorig/sdk";

try {
  await lumorig.mascots.create({ prompt: "a cheerful lighthouse keeper" });
} catch (err) {
  if (err instanceof LumorigError && err.code === "insufficient_credits") {
    const { needed, balance } = err.details as { needed: number; balance: number };
    console.log(`Need ${needed} credits, have ${balance}`);
  } else throw err;
}

Codes the SDK adds itself: http_error (a non-JSON error body, or a failed event stream), job_failed (a helper's job didn't succeed) and timeout (status 408 from jobs.wait). The full list is in Errors.

Webhook verification#

verifyWebhook(body, header, secret, toleranceSec = 300) checks a delivery's Lumorig-Signature header: an HMAC-SHA256 of `${t}.${body}` with your endpoint secret, and a timestamp within the tolerance. Pass the raw request body, not re-serialised JSON. It uses Web Crypto, so it works in Node, Deno, Bun and edge runtimes.

app/hooks/lumorig/route.ts
import { verifyWebhook } from "@lumorig/sdk";

export async function POST(req: Request) {
  const body = await req.text();
  const ok = await verifyWebhook(body, req.headers.get("lumorig-signature") ?? "", process.env.LUMORIG_WEBHOOK_SECRET!);
  if (!ok) return new Response("bad signature", { status: 400 });
  const event = JSON.parse(body); // { type: "job.succeeded", created_at, data: Job }
  return new Response("ok");
}