Lamina Labs

Documentation

Node SDK

Async-first TypeScript SDK for Node 22+.

install
npm install lamina-sdk

Node 22 or newer is required. LAMINA_API_KEY is read from the environment when apiKey is omitted.

Quick start

ts
import { Simi } from "lamina-sdk";

const client = new Simi({ apiKey: "lamina_your_key" });
const video = await client.generate("A narrated lesson about derivatives", {
  duration: 1,
});
await video.save("out.mp4");
await client.aclose();

duration is in whole minutes, 1 to 5 — the same unit as the HTTP API and the Python SDK.

Stream events

ts
const job = await client.submitAsync("Explain our refund policy", {
  duration: 1,
  document: "policy.pdf",
});

for await (const event of client.streamEvents(job)) {
  if (event.isProgress()) {
    console.log(event.payload.phase, event.payload.progress);
  } else {
    console.log(event.type);
  }
}

await client.aclose();

The is* guards narrow event.payload to a typed shape. If the stream drops, the SDK reconnects and resumes from the last sequence it delivered, so you will not see an event twice.

Callback style

ts
const job = await client.submitAsync("A narrated lesson about derivatives");
job.onstream((event) => console.log(event.type));
job.oncompletion((video) => console.log(`Ready: ${video.jobId}`));
await job.wait();

List, cancel, and play back

ts
const jobs = await client.listJobs({ limit: 10, status: "complete" });
for (const job of jobs) {
  console.log(job.jobId, job.status);
}

await client.cancel("job_abc123");

const playback = await client.playback(jobs[0].jobId);
console.log(playback.src, playback.srcAvailable, playback.expiresAt);

Retries and errors

Reads are retried automatically on 429 and 5xx with exponential backoff, honouring retry-after. A submit is only retried when you pass an idempotencyKey, so a retry can never create a second video.

ts
import { Simi, SimiRateLimitError } from "lamina-sdk";

const client = new Simi({ maxRetries: 3 });
try {
  await client.submitAsync("Explain our refund policy", { idempotencyKey: "req-1" });
} catch (error) {
  if (error instanceof SimiRateLimitError) {
    console.log(error.status, error.retryAfter, error.requestId);
  }
}

Every error derives from SimiError: SimiAPIError (carrying status, requestId, and retryAfter), SimiRateLimitError, SimiJobError, SimiStreamError, SimiDownloadError, and SimiCancelledError.

Cancellation and timeouts

ts
const controller = new AbortController();
const video = await client.generate("A narrated lesson about derivatives", {
  signal: controller.signal,
  timeout: 120,
});

Every call accepts an AbortSignal and a per-call timeout in seconds. Closing the client aborts everything still in flight, so a stuck job cannot keep your process alive.