TypeScript SDK
The official SDK is a thin, dependency-free wrapper over the v1 REST API with typed methods, automatic retries on transient errors, a polling helper, and webhook verification. It runs on Node 18+ and in any runtime with a global fetch.
Install#
npm install @loopllama/sdkInitialize#
Construct a client with your API key. Read it from the environment rather than hard-coding it.
import { LoopLlama } from "@loopllama/sdk";
const ll = new LoopLlama({
apiKey: process.env.LOOPLLAMA_API_KEY!,
// baseUrl defaults to https://loopllama.ai/api/v1
// Self-hosting? baseUrl: "https://your-host/api/v1"
});Workflows#
Create, list, and retrieve workflows. Pass a crew to override the default planner→writer pipeline.
// Create
const workflow = await ll.workflows.create({
name: "Spec reviewer",
crew: [
{ role: "researcher", systemPrompt: "Extract key claims and open questions." },
{ role: "writer", systemPrompt: "Draft a one-page summary with a Risks section." },
],
});
// List
const workflows = await ll.workflows.list();
// Retrieve
const wf = await ll.workflows.get(workflow.id);Runs#
Trigger and poll
poll() handles the retrieve loop for you, resolving once the run reaches completed, failed, or pauses in waiting_input so you can respond.
const run = await ll.runs.create(workflow.id, {
input: "Summarize the attached spec and propose 3 risks.",
});
const result = await ll.runs.poll(run.id, {
intervalMs: 2000, // default
timeoutMs: 10 * 60_000, // default
onUpdate: (r) => console.log(r.status, r.total_steps),
});
if (result.status === "completed") {
console.log(result.output);
} else if (result.status === "failed") {
console.error(result.error);
}Respond to a paused run
When a workflow has human-in-the-loop enabled, a run can pause with status: "waiting_input" and a pending object describing the question or the action awaiting approval.
if (result.status === "waiting_input") {
if (result.pending?.kind === "input") {
await ll.runs.respond(run.id, { action: "input", input: "Use the Q3 numbers." });
} else {
await ll.runs.respond(run.id, { action: "approve" });
// or: { action: "reject", reason: "Wrong recipient" }
}
const finished = await ll.runs.poll(run.id);
}Retrieve steps
const full = await ll.runs.get(run.id);
for (const step of full.steps ?? []) {
console.log(`[${step.order}] ${step.role}: ${step.tokens_in}/${step.tokens_out} tokens`);
}Webhooks#
In production, prefer webhooks over polling. Register an endpoint, then verify each delivery's signature with the helper. See the webhooks guide for the payload shape and retry behaviour.
// Register (the secret is returned exactly once — store it)
const endpoint = await ll.webhooks.create({
url: "https://example.com/loopllama/webhook",
events: ["run.completed", "run.failed", "run.waiting_input"],
});
console.log(endpoint.secret); // whsec_...
// Receive (Express) — verify against the RAW body
import express from "express";
import { constructWebhookEvent, LoopLlamaError } from "@loopllama/sdk";
app.post("/loopllama/webhook", express.raw({ type: "application/json" }), (req, res) => {
try {
const event = constructWebhookEvent(
req.body.toString("utf8"),
req.header("LoopLlama-Signature"),
process.env.LOOPLLAMA_WEBHOOK_SECRET!,
);
if (event.type === "run.completed") {
console.log(event.data.run.output);
}
res.sendStatus(200);
} catch (err) {
if (err instanceof LoopLlamaError) return res.sendStatus(400);
throw err;
}
});Error handling#
SDK methods throw a typed LoopLlamaError carrying the HTTP status and the API error message. Timeouts throw LoopLlamaTimeoutError. See Errors for the full list of codes.
import { LoopLlama, LoopLlamaError } from "@loopllama/sdk";
try {
await ll.runs.create(workflowId, { input: "" });
} catch (err) {
if (err instanceof LoopLlamaError) {
console.error(err.status, err.message); // 400 "String must contain at least 1 character(s)"
}
}Options#
baseUrl— override for self-hosted deployments.maxRetries— retries for GET requests on 429, 5xx, and network errors (default 2, exponential backoff). Writes are never retried automatically.timeoutMs— per-request timeout (default 30 s).fetch— supply a custom fetch for tests or older runtimes.