Install
The package is published as @cueno/sdkwith no runtime dependencies — it's a thin, typed wrapper over global fetch.
npm install @cueno/sdk
Quick start
Construct the client once with your API key and reuse it. The most common path is render— it resolves the version deployed to your key's environment, validates your variables server-side, and returns the interpolated string.
import { Cueno } from "@cueno/sdk";
const cueno = new Cueno({
apiKey: process.env.CUENO_API_KEY!, // cueno_live_… or cueno_test_…
baseUrl: "https://cueno.dev", // omit for http://localhost:3000
});
// the key's env picks the live version — the server renders it
const { prompt } = await cueno.prompts.render("welcome-support", {
company: "Acme",
customer: "Lee",
});
const reply = await model.run(prompt);Authentication
Every request authenticates with a Cueno API key, sent automatically as a bearer token. Keys are minted in the web app under Settings → API keys; the full secret is shown once at creation.
- The key's environment is authoritative. There is no
envparameter — a key only ever resolves what's deployed to its environment. To read a different environment, use a key minted for it. - Keys are workspace-scoped. A slug in another workspace returns
404, not403.
| Key prefix | Environment |
|---|---|
| cueno_live_… | production |
| cueno_test_… | staging |
API reference
All prompt operations hang off cueno.prompts. Every method returns a typed response (the exported types mirror the server contract) and sets Authorization and Content-Type for you.
cueno.prompts.list()→ ListPromptsResponseEvery prompt with a version deployed to the key's environment, each carrying its live version label and declared variables. Bodies are omitted.
cueno.prompts.get(slug)→ ResolvedPromptResolve a slug to its deployed version and return the raw, un-rendered body plus declared variables — render it yourself.
cueno.prompts.render(slug, vars?)→ RenderResponseResolve + interpolate. The server validates vars against the declared variables and returns the rendered prompt string. Throws on invalid input — see Errors.
cueno.prompts.versions(slug)→ VersionsResponseThe full version history for a prompt, newest first, so you can discover labels to pin to. Bodies omitted.
cueno.prompts.version(slug, label)→ VersionResponseFetch one specific version by label — regardless of what's deployed — including its body. The reproducibility path: pin to v3 and always get the same prompt back.
render accepts a Record<string, string | number | boolean>. Unknown keys are ignored; a declared-but-optional variable left out is rendered verbatim as its {{token}} rather than blanked.
Errors
Any non-2xx response throws a CuenoApiError. Branch on code (stable), not message (human text that may change). For a failed render, the issues getter exposes the per-variable reasons.
import { CuenoApiError } from "@cueno/sdk";
try {
await cueno.prompts.render("welcome-support", { company: 123 });
} catch (e) {
if (e instanceof CuenoApiError) {
e.code; // "validation_failed" — branch on this
e.status; // 422
e.issues; // [{ variable: "company", message: "Expected a string value." }]
}
}| code | status | Meaning |
|---|---|---|
| unauthorized | 401 | Missing, malformed, or unknown API key |
| not_found | 404 | Unknown slug, nothing deployed to the env, or unknown version label |
| invalid_request | 400 | Malformed JSON or a wrong-shaped variables payload |
| validation_failed | 422 | Variable values failed validation — see .issues |
| method_not_allowed | 405 | Wrong HTTP method for the route |
Configuration
The constructor takes a single options object: new Cueno(options).
| Option | Default | Notes |
|---|---|---|
| apiKey | — | Required. Your cueno_live_… / cueno_test_… secret. |
| baseUrl | http://localhost:3000 | API origin, without the /api/v1 suffix. |
| fetch | globalThis.fetch | Inject a custom fetch (Node <18, or for tests). |
That's the whole SDK surface. Prefer to skip the client and call the endpoints yourself? See the REST API below — every method maps one-to-one onto /api/v1.
REST API
The SDK is a thin wrapper — every method maps one-to-one onto an HTTP endpoint under /api/v1. If you're not on JavaScript, or just prefer raw fetch/curl, call the API directly. Same bearer auth, same environment semantics, and the same error envelope.
It's JSON in, JSON out. Authenticate with your key on every request; the v1 path segment is the contract version.
Authorization: Bearer cueno_live_8Kd2_xxxxxxxxxxxx
Endpoints
/prompts/prompts/{slug}/prompts/{slug}/render/prompts/{slug}/versions/prompts/{slug}/versions/{label}Resolve a prompt
The main read path — resolve a slug to the body deployed in your key's environment:
curl https://cueno.dev/api/v1/prompts/welcome-support \ -H "Authorization: Bearer $CUENO_API_KEY"
{
"slug": "welcome-support",
"title": "Welcome Support",
"description": "Opening line for support chats.",
"environment": "production",
"version": { "label": "v3", "author": "kit", "createdAt": "2026-05-28T00:00:00.000Z" },
"body": "You are {{agent_name}} for {{company}}. Greet {{customer}}.",
"variables": [
{ "name": "company", "type": "string", "required": true, "note": null }
]
}Render with variables
POST the values and let the server validate and interpolate. An empty body (or no variables) is treated as {}; unknown keys are ignored, and an optional token left unprovided stays verbatim.
curl https://cueno.dev/api/v1/prompts/welcome-support/render \
-H "Authorization: Bearer $CUENO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "variables": { "company": "Acme", "customer": "Lee", "agent_name": "Ada" } }'{
"slug": "welcome-support",
"environment": "production",
"version": "v3",
"prompt": "You are Ada for Acme. Greet Lee."
}?env= override. Errors use the shared envelope { "error": { "code", "message", "details"? } } — branch on error.code.