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 defaults to https://cueno.dev; set http://localhost:3000 for local dev
});
// 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 | https://cueno.dev | API origin, without the /api/v1 suffix. Use http://localhost:3000 for local dev. |
| 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.