AI (totalum-sdk/ai)
import … from 'totalum-sdk/ai' — or the same namespace on totalum from 'totalum-sdk'. Install, configuration and errors: Totalum SDK.
AI from your server without any provider account: chat completions (streamed or not), structured JSON, a label
out of a list, embeddings, image generation and editing, video generation and image description. model is a preset
— fast, smart (the default), reasoning, vision, image, video, … each following the newest model of its
family — or a pinned model id from models.list(). Every call is billed in Totalum credits and returns them in
usage.credits. For a library that wants an OpenAI-compatible client (the Vercel AI SDK, openai), use
gatewayConfig().
const { text } = await totalum.ai.chat({ model: 'fast', messages: [{ role: 'user', content: 'Say hi' }] });| Method or type | What it does |
|---|---|
totalumAi | Creates the ai namespace (the same object as totalum.ai, scope ai): a model is a preset (fast, smart, reasoning, vision, image, image-edit, video, tts, stt, embed — each follows the newest model of its family, resolved by Totalum) or a pinned model id; every call returns the Totalum credits it was charged (usage.credits, never tokens). |
ai.chat | Asks a model and waits for the whole answer: summaries, replies, rewriting, extraction, tool calling. |
ai.generateObject | Asks a model for a JSON object that matches your JSON Schema — extract fields from text, classify with details, produce data for your UI. |
ai.classify | Picks one label out of your list for an input — route a support ticket, tag a review, detect a language. |
ai.embed | Turns texts into embedding vectors (lists of numbers whose distance measures meaning). |
ai.image.generate | Generates images from a prompt — illustrations, product shots, avatars, hero images — and stores each one as a public project file. |
ai.image.edit | Edits images with a prompt — remove a background, change a colour, put a product in a new scene — and stores the result as a public project file. |
ai.video.generate | Starts generating a video clip (up to 30 s) from a prompt, optionally from a first/last frame or reference images. |
ai.video.get | Reads a video job: its status and, once completed, the stored clip (video.key, video.url) and its final cost. |
ai.video.wait | ⚠ NODE-ONLY: polls get(jobId) until the job leaves queued/running (default every 5 s, at most 45 min — the longest clip, 30 s at 1080p, renders in about 22 min). |
ai.vision.describe | Describes an image in words, or answers a question about it — alt text, moderation, reading a chart, checking a photo. |
ai.speech.speak | Deprecated: Since 1.0.0: use totalum.speech.synthesize (the same call). |
ai.speech.transcribe | Deprecated: Since 1.0.0: use totalum.speech.transcribe (the same call). |
ai.models.list | Each preset's current model and the catalogue of models this project may call (their ids, names, inputs and context sizes) — to let an admin pick a model, or to pin one. |
ai.gatewayConfig | The URL and key of Totalum's OpenAI-compatible AI gateway, for any library that takes an OpenAI client: the Vercel AI SDK, openai, LangChain. |
AiChatInput | A chat request: the model, the conversation and the generation options, in the OpenAI format. |
AiEmbedInput | The input of ai.embed(). |
AiEmbedOutput | The answer of ai.embed(). |
AiImageEditInput | The input of ai.image.edit(). |
AiImageInput | The input of ai.image.generate(). |
AiImageOutput | Generated images, stored as public project files. |
AiModelsOutput | The models this project can use, from ai.models.list(). |
AiPresetName | A model preset: a name that always points to Totalum's current choice for a job, so apps get newer models without code changes. |
AiTextOutput | A text answer, from ai.vision.describe(). |
AiVideoCreated | A started video job, from ai.video.generate(). |
AiVideoInput | The input of ai.video.generate(). |
AiVideoJob | A video job, from ai.video.get() and ai.video.wait(). |
AiVisionDescribeInput | The input of ai.vision.describe(). |
ChatResult | ai.chat(): the first choice read out, the raw completion kept. |
ChatStream | ai.chat.stream(): the OpenAI-format SSE (raw), its text deltas, and the result once it ends. |
ChatUsage | What a call was charged, in Totalum credits — the only usage Totalum shows (no tokens). |
TotalumAiOptions | The options of totalumAi(): the client options plus the AI gateway URL gatewayConfig() returns. |
#totalumAi
totalumAi(options?: TotalumAiOptions): TotalumAiCreates the ai namespace (the same object as totalum.ai, scope ai): a model is a preset (fast,
smart, reasoning, vision, image, image-edit, video, tts, stt, embed — each follows the newest model
of its family, resolved by Totalum) or a pinned model id; every call returns the Totalum credits it was charged
(usage.credits, never tokens). There is no ai.agent(): an agent loop runs on an OpenAI-compatible library (the
template's src/agents/ uses the Vercel AI SDK) pointed at gatewayConfig(), which reaches the same models with the
same key, billed in credits. Vector search is totalum.vectors.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options | TotalumAiOptions | no | The project key, API URL, environment and AI gateway URL; each falls back to its environment variable. |
options.aiBaseUrl | string | no | The OpenAI-compatible AI gateway gatewayConfig() returns; default TOTALUM_AI_BASE_URL, else https://ai.totalum.app/api/v1. |
options.key | string | no | Project key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only. |
options.apiUrl | string | no | The Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app. |
options.env | 'dev' | 'live' | no | Sent as x-totalum-env on every call (the database it targets, and dev vs live usage); default TOTALUM_ENV, else live. |
Returns TotalumAi — The ai namespace: the methods below.
Example
import { totalumAi } from 'totalum-sdk/ai';
const ai = totalumAi();
const { text } = await ai.chat({ messages: [{ role: 'user', content: 'Hello' }] });The namespace's type is exported as TotalumAi (import type { TotalumAi } from 'totalum-sdk/ai').
#ai.chat
ai.chat(req: { model?, messages, tools?, toolChoice?, parallelToolCalls?, responseFormat?, temperature?, maxTokens?, reasoning?, serverTools?, maxToolCalls?, cache?, zdr?, fastMode?, user? }): Promise<ChatResult>Asks a model and waits for the whole answer: summaries, replies, rewriting, extraction, tool calling. model is a
preset (fast, smart — the default — or reasoning) or a pinned model id from models.list(); messages is
the conversation in the OpenAI format. Billed in credits by the model's use. To show the answer while it is
written, use chat.stream.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | { model?, messages, tools?, toolChoice?, parallelToolCalls?, responseFormat?, temperature?, maxTokens?, reasoning?, serverTools?, maxToolCalls?, cache?, zdr?, fastMode?, user? } | yes | The model, the conversation and the generation options. |
req.model | string | no | A preset (fast, smart, reasoning) or a model id from models.list(). Default smart (with automatic fallbacks). Allowed: at least 1 character. |
req.messages | ({ })[] | yes | The conversation, oldest first: { role, content } each. role is system, user, assistant, tool or developer; content is a string, or a list of parts ({ type: "text", text }, { type: "image_url", image_url: { url } }) to send images. Allowed: at least 1 item. |
req.tools | (Record<string, unknown>)[] | no | Functions the model may ask you to call, in the OpenAI format: { type: "function", function: { name, description, parameters } }. |
req.toolChoice | unknown | no | auto, none, required, or { type: "function", function: { name } } to force one tool. |
req.parallelToolCalls | boolean | no | Whether the model may ask for several tool calls at once. |
req.responseFormat | Record<string, unknown> | no | Force JSON: { type: "json_object" } or { type: "json_schema", json_schema: { name, schema, strict } } (or use generateObject). |
req.temperature | number | no | Randomness, 0–2: lower is more predictable. |
req.maxTokens | number | no | The longest answer, in tokens. Default 4096. Allowed: > 0, ≤ 32768. |
req.reasoning | { effort } | no | How much the model thinks before answering, for models that support it. |
req.reasoning.effort | string | yes | low, medium or high. |
req.serverTools | ('web_search' | 'image_generation' | 'web_fetch' | 'datetime')[] | no | Accepted but not used today. |
req.maxToolCalls | number | no | Accepted but not used today. Allowed: > 0, ≤ 30. |
req.cache | { ttlSec } | no | Accepted but not used today. |
req.cache.ttlSec | number | yes | Accepted but not used today. Allowed: ≥ 1, ≤ 86400. |
req.zdr | boolean | no | true only uses providers that keep no copy of the prompt (zero data retention). |
req.fastMode | boolean | no | Accepted but not used today. |
req.user | string | no | An id of your end user, passed to the provider for abuse monitoring. |
Returns Promise<ChatResult> — The answer's text, any tool calls, why it stopped, and what it cost.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The completion's id. |
model | string | yes | The model that answered (a preset resolves to the model it currently points to). |
text | string | yes | The answer's text (empty when the model only called tools). |
toolCalls | ({ id, type, function })[] | yes | The tools the model asks you to call (with tools); empty otherwise. |
toolCalls.id | string | yes | The call's id: send the tool's answer back as a tool message with this tool_call_id. |
toolCalls.type | string | yes | Always function. |
toolCalls.function | { name, arguments } | yes | The function the model wants to call. |
toolCalls.function.name | string | yes | The tool's name, as you declared it in tools. |
toolCalls.function.arguments | string | yes | Its arguments as a JSON string: JSON.parse it. |
finishReason | string | null | yes | Why it stopped: stop (finished), length (hit maxTokens), tool_calls, content_filter, or null. |
usage | ChatUsage | yes | What the call cost. |
raw | unknown | yes | The whole OpenAI-format completion, as received. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_MODEL_UNKNOWN | 400 | No such preset or model id. |
AI_MODEL_NOT_ALLOWED | 403 | The model is not available on this plan; details says why. |
AI_PROVIDER_UNAVAILABLE | 502 | The model did not answer; retry, or use another model. |
Examples
const reply = await totalum.ai.chat({
model: 'fast',
messages: [
{ role: 'system', content: 'Answer in one word.' },
{ role: 'user', content: 'What is the capital of France?' },
],
});Result:
{
"id": "gen-1791740364-UbQ0IKv75XZajyP5We6y",
"model": "openai/gpt-6-luna",
"text": "Paris",
"toolCalls": [],
"finishReason": "stop",
"usage": { "credits": 0.0007, "inputTokens": 0, "outputTokens": 0 },
"raw": { "id": "gen-1791740364-UbQ0IKv75XZajyP5We6y", "object": "chat.completion", "choices": [ … ], … }
}// tool calling: the model asks for getWeather, you run it and send the answer back
const first = await totalum.ai.chat({
messages: [{ role: 'user', content: 'Weather in Madrid?' }],
tools: [{ type: 'function', function: { name: 'getWeather', parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] } } }],
});Result:
{ "text": "", "toolCalls": [{ "id": "call_8f2k", "type": "function", "function": { "name": "getWeather", "arguments": "{\"city\":\"Madrid\"}" } }], "finishReason": "tool_calls", … }#ai.generateObject
ai.generateObject(req: { schema, prompt?, messages?, model?, healing? }): Promise<{ object, usage }>Asks a model for a JSON object that matches your JSON Schema — extract fields from text, classify with details,
produce data for your UI. The output is checked against the schema (and repaired when slightly off). Pass
prompt or messages.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | { schema, prompt?, messages?, model?, healing? } | yes | The schema, the prompt (or messages) and the model. |
req.schema | Record<string, unknown> | yes | The JSON Schema of the object (at most 32 KB); use additionalProperties: false and required. |
req.prompt | string | no | The instruction and the input, as one text (or pass messages). |
req.messages | ({ })[] | no | The conversation, instead of prompt. |
req.model | string | no | A preset or model id. Default smart. |
req.healing | boolean | no | false turns off the automatic repair of almost-valid JSON. Default true. |
Returns Promise<{ object, usage }> — The object and what it cost.
| Field | Type | Always present | Description |
|---|---|---|---|
object | unknown | yes | The JSON object, matching schema. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_INVALID_SCHEMA | 502 | The model did not answer JSON for the schema; details.usage says what was charged. |
AI_MODEL_UNKNOWN | 400 | No such preset or model id. |
AI_PROVIDER_UNAVAILABLE | 502 | The model did not answer; retry. |
Example
const { object } = await totalum.ai.generateObject({
model: 'fast',
prompt: 'Ana is 31 and lives in Madrid.',
schema: {
type: 'object',
properties: { name: { type: 'string' }, age: { type: 'number' }, city: { type: 'string' } },
required: ['name', 'age', 'city'],
additionalProperties: false,
},
});Result:
{ "object": { "name": "Ana", "age": 31, "city": "Madrid" }, "usage": { "credits": 0.0014 } }#ai.classify
ai.classify(req: { input, options, model?, instructions? }): Promise<{ label }>Picks one label out of your list for an input — route a support ticket, tag a review, detect a language. Sugar
over generateObject.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | { input, options, model?, instructions? } | yes | The input, the labels and optional instructions. |
req.input | string | yes | The text to classify. |
req.options | string[] | yes | The labels to choose from. |
req.model | string | no | A preset or model id. Default smart. |
req.instructions | string | no | How to choose, in words. Default: "Classify the input into exactly one label." |
Returns Promise<{ label }> — { label }: one of options (or '' if the model gave none).
| Field | Type | Always present | Description |
|---|---|---|---|
label | string | yes | The chosen label: one of options, or '' when the model gave none. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_INVALID_SCHEMA | 502 | The model did not answer a label. |
AI_MODEL_UNKNOWN | 400 | No such preset or model id. |
Example
const { label } = await totalum.ai.classify({
input: 'My card was charged twice',
options: ['billing', 'bug', 'other'],
model: 'fast',
});Result:
{ "label": "billing" }#ai.embed
ai.embed(req: AiEmbedInput): Promise<AiEmbedOutput>Turns texts into embedding vectors (lists of numbers whose distance measures meaning). To store and search them,
totalum.vectors does the embedding for you; use this for your own similarity maths.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | AiEmbedInput | yes | The texts and the model. |
req.input | string | string[] | yes | One text, or a list of texts. Allowed: at least 1 character, at least 1 item, at most 256 items. |
req.model | string | no | A preset or model id. Default embed. Allowed: at least 1 character. |
Returns Promise<AiEmbedOutput> — One vector per input, in order, and what it cost.
| Field | Type | Always present | Description |
|---|---|---|---|
embeddings | (number[])[] | yes | One vector (list of numbers) per input text, in order. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_MODEL_UNKNOWN | 400 | No such preset or model id. |
AI_PROVIDER_UNAVAILABLE | 502 | The model did not answer; retry. |
Example
const { embeddings } = await totalum.ai.embed({ input: ['red shoes', 'blue sandals'] });Result:
{ "embeddings": [[0.0138, -0.0121, 0.0125, …], [0.0042, -0.0337, 0.0198, …]], "usage": { "credits": 0.0001 } }#ai.image.generate
ai.image.generate(req: AiImageInput): Promise<AiImageOutput>Generates images from a prompt — illustrations, product shots, avatars, hero images — and stores each one as a
public project file. Generate once and save the key: never generate on every page view.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | AiImageInput | yes | The prompt, the model, the size and how many. |
req.prompt | string | yes | What to draw, in words: subject, style, light, composition. Allowed: at least 1 character. |
req.model | string | no | A preset or model id. Default image. Allowed: at least 1 character. |
req.aspectRatio | string | no | The shape: 1:1, 16:9, 9:16, 4:3, 3:4, … (what the model supports). |
req.resolution | '1K' | '2K' | '4K' | '512' | no | The size class: 512, 1K, 2K or 4K (for models that support it). |
req.quality | string | no | A quality level the model supports (low, medium, high). |
req.outputFormat | 'png' | 'jpeg' | 'webp' | no | png, jpeg or webp. |
req.background | string | no | transparent for a transparent background (models that support it), or opaque. |
req.n | number | no | How many images. Default 1. Allowed: ≥ 1, ≤ 4. |
req.references | string[] | no | Reference images for style or content: project file keys, files URLs or https:// URLs. Allowed: at most 14 items. |
req.fileName | string | no | The base name of the stored files. |
req.store | boolean | no | Accepted for compatibility: images are always stored as project files. |
Returns Promise<AiImageOutput> — The stored images and what they cost.
| Field | Type | Always present | Description |
|---|---|---|---|
images | ({ key, url, mediaType, width?, height? })[] | yes | The stored images. |
images.key | string | yes | The stored file's key (see totalum.files). |
images.url | string | yes | The image's public URL. |
images.mediaType | string | yes | The image's MIME type. |
images.width | number | no | Width in pixels, when known. |
images.height | number | no | Height in pixels, when known. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
fileName | string | no | The first image's file name (kept for apps written against the first AI API). |
imageUrl | string | no | The first image's URL (kept for apps written against the first AI API). |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_MODEL_UNKNOWN | 400 | No such preset or model id. |
AI_MODEL_NOT_ALLOWED | 403 | The model is not available on this plan. |
AI_PROVIDER_UNAVAILABLE | 502 | The model returned no image; retry or change the prompt. |
Example
const { images } = await totalum.ai.image.generate({
prompt: 'A watercolor fox reading a book, warm light',
aspectRatio: '16:9',
});Result:
{
"images": [
{ "key": "p/acme-crm/01M4P1J2K3L4M5N6P7Q8R9S0TV.png", "url": "https://files.totalum-project.com/p/acme-crm/01M4P1J2K3L4M5N6P7Q8R9S0TV.png", "mediaType": "image/png", "width": 1344, "height": 768 }
],
"usage": { "credits": 0.2741 },
"fileName": "01M4P1J2K3L4M5N6P7Q8R9S0TV.png",
"imageUrl": "https://files.totalum-project.com/p/acme-crm/01M4P1J2K3L4M5N6P7Q8R9S0TV.png"
}#ai.image.edit
ai.image.edit(req: AiImageEditInput): Promise<AiImageOutput>Edits images with a prompt — remove a background, change a colour, put a product in a new scene — and stores the result as a public project file.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | AiImageEditInput | yes | The instruction and the images to edit. |
req.prompt | string | yes | The edit, in words. Allowed: at least 1 character. |
req.images | string[] | yes | The images to edit (project file keys, files URLs or https:// URLs). Allowed: at least 1 item, at most 16 items. |
req.mask | string | no | Accepted but not used today. |
req.fileName | string | no | The base name of the stored files. |
req.size | string | no | Accepted but not used today. |
req.quality | string | no | A quality level the model supports. |
req.outputFormat | 'png' | 'jpeg' | 'webp' | no | png, jpeg or webp. |
req.background | string | no | transparent or opaque, for models that support it. |
req.inputFidelity | string | no | Accepted but not used today. |
Returns Promise<AiImageOutput> — The stored result image(s) and what they cost.
| Field | Type | Always present | Description |
|---|---|---|---|
images | ({ key, url, mediaType, width?, height? })[] | yes | The stored images. |
images.key | string | yes | The stored file's key (see totalum.files). |
images.url | string | yes | The image's public URL. |
images.mediaType | string | yes | The image's MIME type. |
images.width | number | no | Width in pixels, when known. |
images.height | number | no | Height in pixels, when known. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
fileName | string | no | The first image's file name (kept for apps written against the first AI API). |
imageUrl | string | no | The first image's URL (kept for apps written against the first AI API). |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_MODEL_UNKNOWN | 400 | No such preset or model id. |
FILE_NOT_FOUND | 404 | An input image key does not exist. |
AI_PROVIDER_UNAVAILABLE | 502 | The model returned no image; retry. |
Example
const { images } = await totalum.ai.image.edit({
prompt: 'Put this sneaker on a white marble pedestal, studio light',
images: [product.photo.url],
});Result:
{
"images": [{ "key": "p/acme-crm/01M4P1K3L4M5N6P7Q8R9S0T1UW.png", "url": "https://files.totalum-project.com/p/acme-crm/01M4P1K3L4M5N6P7Q8R9S0T1UW.png", "mediaType": "image/png" }],
"usage": { "credits": 0.3012 },
"fileName": "01M4P1K3L4M5N6P7Q8R9S0T1UW.png",
"imageUrl": "https://files.totalum-project.com/p/acme-crm/01M4P1K3L4M5N6P7Q8R9S0T1UW.png"
}#ai.video.generate
ai.video.generate(req: AiVideoInput): Promise<AiVideoCreated>Starts generating a video clip (up to 30 s) from a prompt, optionally from a first/last frame or reference
images. It takes minutes: store the jobId and read get(jobId) later (a page refresh, a cron job); the clip
is stored as a project file when it completes.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | AiVideoInput | yes | The prompt, the duration and the look. |
req.prompt | string | yes | The scene, in words: subject, action, camera, style. Allowed: at least 1 character. |
req.durationSec | number | yes | The clip's length in seconds (the video preset renders 2–30 s). Allowed: > 0, ≤ 30. |
req.model | string | no | A preset or model id. Default video. Allowed: at least 1 character. |
req.resolution | string | no | The size the model supports (720p, 1080p). |
req.aspectRatio | string | no | 16:9, 9:16, 1:1, … (what the model supports). |
req.firstFrame | string | no | An image the clip starts from (a project file key or a URL). |
req.lastFrame | string | no | An image the clip ends on. |
req.references | string[] | no | Reference images for style or subject. |
req.generateAudio | boolean | no | true adds a soundtrack (models that support it). |
req.seed | number | no | A number that makes the result reproducible. |
Returns Promise<AiVideoCreated> — The job id and the credits reserved for it (settled when it ends).
| Field | Type | Always present | Description |
|---|---|---|---|
jobId | string | yes | The job id: store it and read ai.video.get(jobId) later. |
status | 'queued' | yes | Always queued. |
estimatedCredits | number | yes | The credits reserved for the job; the real charge is settled when it ends. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_MODEL_UNKNOWN | 400 | No such preset or model id. |
AI_VIDEO_FAILED | 502 | The video could not be started. |
AI_MEDIA_PAUSED_THIS_HOUR | 429 | Image, video and speech AI are paused until the hour ends. |
Example
const job = await totalum.ai.video.generate({
prompt: 'A paper boat sailing down a rainy street, cinematic',
durationSec: 5,
aspectRatio: '16:9',
});
// save job.jobId on the row, then read ai.video.get(jobId) laterResult:
{ "jobId": "vid_8m2k4n6p8q0r", "status": "queued", "estimatedCredits": 21.5 }#ai.video.get
ai.video.get(jobId: string): Promise<AiVideoJob>Reads a video job: its status and, once completed, the stored clip (video.key, video.url) and its final cost.
Call it when the user opens the page or from a cron job until the status is no longer queued/running.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
jobId | string | yes | The jobId from video.generate(). |
Returns Promise<AiVideoJob> — The job's status, and the clip when done.
| Field | Type | Always present | Description |
|---|---|---|---|
jobId | string | yes | The job id. |
status | 'expired' | 'completed' | 'queued' | 'running' | 'failed' | 'cancelled' | yes | queued and running while it renders; then completed, failed, cancelled or expired. |
progress | number | no | How far it is, 0–1, when the model reports it. |
video | { key, url } | no | The stored clip, once completed. |
video.key | string | yes | The stored file's key (see totalum.files). |
video.url | string | yes | The clip's public URL (MP4). |
usage | { credits } | no | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
error | { code, message } | no | Why it failed, when failed or expired. |
error.code | string | yes | The error code (AI_VIDEO_FAILED). |
error.message | string | yes | What happened. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_VIDEO_JOB_NOT_FOUND | 404 | No such job in this project. |
AI_PROVIDER_UNAVAILABLE | 503 | The video service could not be reached; retry. |
Example
const job = await totalum.ai.video.get('vid_8m2k4n6p8q0r');Result:
{
"jobId": "vid_8m2k4n6p8q0r",
"status": "completed",
"video": { "key": "p/acme-crm/01M4P1M5N6P7Q8R9S0T1U2V3WX.mp4", "url": "https://files.totalum-project.com/p/acme-crm/01M4P1M5N6P7Q8R9S0T1U2V3WX.mp4" },
"usage": { "credits": 19.8 }
}#ai.video.wait
ai.video.wait(jobId: string, opts?: { intervalMs?, timeoutMs? }): Promise<AiVideoJob>⚠ NODE-ONLY: polls get(jobId) until the job leaves queued/running (default every 5 s, at most 45 min —
the longest clip, 30 s at 1080p, renders in about 22 min). For scripts (npx tsx scripts/…); in a Worker (the
preview and the published app) it throws NODE_ONLY — store the jobId and read get(jobId) later instead.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
jobId | string | yes | The jobId from video.generate(). |
opts | { intervalMs?, timeoutMs? } | no | How often and how long to poll. |
opts.intervalMs | number | no | Milliseconds between polls. Default 5000. |
opts.timeoutMs | number | no | Stop polling after this long and return the job as it is. Default 2700000 (45 min). |
Returns Promise<AiVideoJob> — The job once it finished (or when the timeout passed).
| Field | Type | Always present | Description |
|---|---|---|---|
jobId | string | yes | The job id. |
status | 'expired' | 'completed' | 'queued' | 'running' | 'failed' | 'cancelled' | yes | queued and running while it renders; then completed, failed, cancelled or expired. |
progress | number | no | How far it is, 0–1, when the model reports it. |
video | { key, url } | no | The stored clip, once completed. |
video.key | string | yes | The stored file's key (see totalum.files). |
video.url | string | yes | The clip's public URL (MP4). |
usage | { credits } | no | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
error | { code, message } | no | Why it failed, when failed or expired. |
error.code | string | yes | The error code (AI_VIDEO_FAILED). |
error.message | string | yes | What happened. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
NODE_ONLY | — | Called inside a Worker; details.replacement says what to do instead. |
AI_VIDEO_JOB_NOT_FOUND | 404 | No such job in this project. |
Example
// scripts/make-intro-video.ts, run with: npx tsx scripts/make-intro-video.ts
const { jobId } = await totalum.ai.video.generate({ prompt: 'Logo reveal on black', durationSec: 5 });
const job = await totalum.ai.video.wait(jobId);
console.log(job.status, job.video?.url);Result:
completed https://files.totalum-project.com/p/acme-crm/01M4P1M5N6P7Q8R9S0T1U2V3WX.mp4#ai.vision.describe
ai.vision.describe(req: AiVisionDescribeInput): Promise<AiTextOutput>Describes an image in words, or answers a question about it — alt text, moderation, reading a chart, checking a
photo. The image can be a project file key, a files URL, an https:// URL or a data URL.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
req | AiVisionDescribeInput | yes | The image, the question and the model. |
req.image | string | yes | The image: a project file key, a files URL, an https:// URL, a data: URL or bare base64. Allowed: at least 1 character. |
req.prompt | string | no | The question or instruction. Default: "Describe this image." |
req.model | string | no | A preset or model id. Default vision. Allowed: at least 1 character. |
Returns Promise<AiTextOutput> — The answer's text and what it cost.
| Field | Type | Always present | Description |
|---|---|---|---|
text | string | yes | The answer. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_NOT_FOUND | 404 | The image key does not exist. |
AI_PROVIDER_UNAVAILABLE | 502 | The model could not read the image (for example the URL refused the download); retry with a project file. |
Example
const { text } = await totalum.ai.vision.describe({
image: 'p/acme-crm/01M4P1J2K3L4M5N6P7Q8R9S0TV.png',
prompt: 'Write alt text for this image in one sentence.',
});Result:
{ "text": "A watercolor fox sits under a lamp reading an open book.", "usage": { "credits": 0.0019 } }#ai.speech.speak
Deprecated. Since 1.0.0: use totalum.speech.synthesize (the same call).
ai.speech.speak(input: { text, voice?, language?, model?, format?, speed?, store }): Promise<StoredMedia>
ai.speech.speak(input: SpeakInput): Promise<StoredMedia | SpeechAudio>#ai.speech.transcribe
Deprecated. Since 1.0.0: use totalum.speech.transcribe (the same call).
ai.speech.transcribe(input: TranscribeInput): Promise<TranscribeOutput>#ai.models.list
ai.models.list(): Promise<AiModelsOutput>Each preset's current model and the catalogue of models this project may call (their ids, names, inputs and context sizes) — to let an admin pick a model, or to pin one. Free.
Returns Promise<AiModelsOutput> — The presets and the model catalogue.
| Field | Type | Always present | Description |
|---|---|---|---|
presets | { image?, fast?, reasoning?, video?, smart?, vision?, image-edit?, tts?, stt?, embed?, embed-image? } | yes | Each preset and the model id it points to today. |
presets.image | string | no | The image preset's model. |
presets.fast | string | no | The fast preset's model. |
presets.reasoning | string | no | The reasoning preset's model. |
presets.video | string | no | The video preset's model. |
presets.smart | string | no | The smart preset's model. |
presets.vision | string | no | The vision preset's model. |
presets.image-edit | string | no | The image-edit preset's model. |
presets.tts | string | no | The tts preset's model. |
presets.stt | string | no | The stt preset's model. |
presets.embed | string | no | The embed preset's model. |
presets.embed-image | string | no | The embed-image preset's model. |
models | ({ slug, name, inputModalities, pricing, contextLength })[] | yes | Every model the plan may call. |
models.slug | string | yes | The model id to pass as model. |
models.name | string | yes | Its display name. |
models.inputModalities | string[] | yes | What it accepts (text, image, file, audio, video). |
models.pricing | Record<string, unknown> | yes | Always {}: prices are in credits, per call (usage.credits). |
models.contextLength | number | yes | The most tokens it reads at once. |
denylist | string[] | no | Model ids this project may not use. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_PROVIDER_UNAVAILABLE | 503 | The model catalogue is unavailable; retry. |
Example
const { presets, models } = await totalum.ai.models.list();Result:
{
"presets": {
"fast": "openai/gpt-6-luna",
"smart": "openai/gpt-6.1-sol",
"reasoning": "anthropic/claude-opus-5.5",
"vision": "openai/gpt-6-luna",
"image": "microsoft/mai-image-2.6-flash",
"image-edit": "microsoft/mai-image-2.6-flash",
"video": "alibaba/wan-3.0",
"tts": "microsoft/mai-voice-2.1-flash",
"stt": "openai/whisper-large-v3-turbo",
"embed": "openai/text-embedding-3-small",
"embed-image": "google/gemini-embedding-2"
},
"models": [
{ "slug": "anthropic/claude-haiku-5.5", "name": "Anthropic: Claude Haiku 5.5", "inputModalities": ["text", "image", "file"], "pricing": {}, "contextLength": 1000000 },
…
]
}#ai.gatewayConfig
ai.gatewayConfig(): { baseURL, apiKey }The URL and key of Totalum's OpenAI-compatible AI gateway, for any library that takes an OpenAI client: the
Vercel AI SDK, openai, LangChain. Same models, same key, billed in credits. Server-side only: the key is the
project key. No request is made.
Returns { baseURL, apiKey } — { baseURL, apiKey } to pass to the client.
| Field | Type | Always present | Description |
|---|---|---|---|
baseURL | string | yes | The gateway's OpenAI-compatible base URL: TOTALUM_AI_BASE_URL, else https://ai.totalum.app/api/v1. |
apiKey | string | yes | The project key, used as the API key. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
SDK_NOT_CONFIGURED | — | No project key is set. |
Examples
import OpenAI from 'openai';
const openai = new OpenAI(totalum.ai.gatewayConfig());
const r = await openai.chat.completions.create({ model: 'fast', messages: [{ role: 'user', content: 'Hi' }] });import { createOpenAI } from '@ai-sdk/openai';
import { generateText } from 'ai';
const gateway = createOpenAI(totalum.ai.gatewayConfig());
const { text } = await generateText({ model: gateway('smart'), prompt: 'Write a haiku about Mondays' });Result:
{ "baseURL": "https://ai.totalum.app/api/v1", "apiKey": "tlm_pk_…" }#AiChatInput
A chat request: the model, the conversation and the generation options, in the OpenAI format.
| Name | Type | Required | Description |
|---|---|---|---|
messages | ({ })[] | yes | The conversation, oldest first: { role, content } each. role is system, user, assistant, tool or developer; content is a string, or a list of parts ({ type: "text", text }, { type: "image_url", image_url: { url } }) to send images. Allowed: at least 1 item. |
model | string | no | A preset (fast, smart, reasoning) or a model id from models.list(). Default smart (with automatic fallbacks). Allowed: at least 1 character. |
tools | (Record<string, unknown>)[] | no | Functions the model may ask you to call, in the OpenAI format: { type: "function", function: { name, description, parameters } }. |
toolChoice | unknown | no | auto, none, required, or { type: "function", function: { name } } to force one tool. |
parallelToolCalls | boolean | no | Whether the model may ask for several tool calls at once. |
responseFormat | Record<string, unknown> | no | Force JSON: { type: "json_object" } or { type: "json_schema", json_schema: { name, schema, strict } } (or use generateObject). |
temperature | number | no | Randomness, 0–2: lower is more predictable. |
maxTokens | number | no | The longest answer, in tokens. Default 4096. Allowed: > 0, ≤ 32768. |
reasoning | { effort } | no | How much the model thinks before answering, for models that support it. |
reasoning.effort | string | yes | low, medium or high. |
serverTools | ('web_search' | 'image_generation' | 'web_fetch' | 'datetime')[] | no | Accepted but not used today. |
maxToolCalls | number | no | Accepted but not used today. Allowed: > 0, ≤ 30. |
stream | boolean | no | Set by the SDK (chat vs chat.stream); do not pass it. |
cache | { ttlSec } | no | Accepted but not used today. |
cache.ttlSec | number | yes | Accepted but not used today. Allowed: ≥ 1, ≤ 86400. |
zdr | boolean | no | true only uses providers that keep no copy of the prompt (zero data retention). |
fastMode | boolean | no | Accepted but not used today. |
user | string | no | An id of your end user, passed to the provider for abuse monitoring. |
#AiEmbedInput
The input of ai.embed().
| Name | Type | Required | Description |
|---|---|---|---|
input | string | string[] | yes | One text, or a list of texts. Allowed: at least 1 character, at least 1 item, at most 256 items. |
model | string | no | A preset or model id. Default embed. Allowed: at least 1 character. |
#AiEmbedOutput
The answer of ai.embed().
| Field | Type | Always present | Description |
|---|---|---|---|
embeddings | (number[])[] | yes | One vector (list of numbers) per input text, in order. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
#AiImageEditInput
The input of ai.image.edit().
| Name | Type | Required | Description |
|---|---|---|---|
prompt | string | yes | The edit, in words. Allowed: at least 1 character. |
images | string[] | yes | The images to edit (project file keys, files URLs or https:// URLs). Allowed: at least 1 item, at most 16 items. |
mask | string | no | Accepted but not used today. |
fileName | string | no | The base name of the stored files. |
size | string | no | Accepted but not used today. |
quality | string | no | A quality level the model supports. |
outputFormat | 'png' | 'jpeg' | 'webp' | no | png, jpeg or webp. |
background | string | no | transparent or opaque, for models that support it. |
inputFidelity | string | no | Accepted but not used today. |
#AiImageInput
The input of ai.image.generate().
| Name | Type | Required | Description |
|---|---|---|---|
prompt | string | yes | What to draw, in words: subject, style, light, composition. Allowed: at least 1 character. |
model | string | no | A preset or model id. Default image. Allowed: at least 1 character. |
aspectRatio | string | no | The shape: 1:1, 16:9, 9:16, 4:3, 3:4, … (what the model supports). |
resolution | '1K' | '2K' | '4K' | '512' | no | The size class: 512, 1K, 2K or 4K (for models that support it). |
quality | string | no | A quality level the model supports (low, medium, high). |
outputFormat | 'png' | 'jpeg' | 'webp' | no | png, jpeg or webp. |
background | string | no | transparent for a transparent background (models that support it), or opaque. |
n | number | no | How many images. Default 1. Allowed: ≥ 1, ≤ 4. |
references | string[] | no | Reference images for style or content: project file keys, files URLs or https:// URLs. Allowed: at most 14 items. |
fileName | string | no | The base name of the stored files. |
store | boolean | no | Accepted for compatibility: images are always stored as project files. |
#AiImageOutput
Generated images, stored as public project files.
| Field | Type | Always present | Description |
|---|---|---|---|
images | ({ key, url, mediaType, width?, height? })[] | yes | The stored images. |
images.key | string | yes | The stored file's key (see totalum.files). |
images.url | string | yes | The image's public URL. |
images.mediaType | string | yes | The image's MIME type. |
images.width | number | no | Width in pixels, when known. |
images.height | number | no | Height in pixels, when known. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
fileName | string | no | The first image's file name (kept for apps written against the first AI API). |
imageUrl | string | no | The first image's URL (kept for apps written against the first AI API). |
#AiModelsOutput
The models this project can use, from ai.models.list().
| Field | Type | Always present | Description |
|---|---|---|---|
presets | { image?, fast?, reasoning?, video?, smart?, vision?, image-edit?, tts?, stt?, embed?, embed-image? } | yes | Each preset and the model id it points to today. |
presets.image | string | no | The image preset's model. |
presets.fast | string | no | The fast preset's model. |
presets.reasoning | string | no | The reasoning preset's model. |
presets.video | string | no | The video preset's model. |
presets.smart | string | no | The smart preset's model. |
presets.vision | string | no | The vision preset's model. |
presets.image-edit | string | no | The image-edit preset's model. |
presets.tts | string | no | The tts preset's model. |
presets.stt | string | no | The stt preset's model. |
presets.embed | string | no | The embed preset's model. |
presets.embed-image | string | no | The embed-image preset's model. |
models | ({ slug, name, inputModalities, pricing, contextLength })[] | yes | Every model the plan may call. |
models.slug | string | yes | The model id to pass as model. |
models.name | string | yes | Its display name. |
models.inputModalities | string[] | yes | What it accepts (text, image, file, audio, video). |
models.pricing | Record<string, unknown> | yes | Always {}: prices are in credits, per call (usage.credits). |
models.contextLength | number | yes | The most tokens it reads at once. |
denylist | string[] | no | Model ids this project may not use. |
#AiPresetName
A model preset: a name that always points to Totalum's current choice for a job, so apps get newer models without code changes. models.list() shows where each points today.
| Value | Meaning |
|---|---|
'image' | Image generation. |
'fast' | Quick, low-cost text model: classification, short answers, extraction. |
'reasoning' | The strongest text model, for hard problems (slower, costs more). |
'video' | Video generation. |
'smart' | The default text model: good quality for most tasks. |
'vision' | A model that reads images (used by vision.describe and scan). |
'image-edit' | Image editing. |
'tts' | Text to speech. |
'stt' | Speech to text. |
'embed' | Text embeddings (used by embed and vectors for text). |
'embed-image' | Text and image embeddings in one space (used by vectors for images). |
#AiTextOutput
A text answer, from ai.vision.describe().
| Field | Type | Always present | Description |
|---|---|---|---|
text | string | yes | The answer. |
usage | { credits } | yes | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
#AiVideoCreated
A started video job, from ai.video.generate().
| Field | Type | Always present | Description |
|---|---|---|---|
jobId | string | yes | The job id: store it and read ai.video.get(jobId) later. |
status | 'queued' | yes | Always queued. |
estimatedCredits | number | yes | The credits reserved for the job; the real charge is settled when it ends. |
#AiVideoInput
The input of ai.video.generate().
| Name | Type | Required | Description |
|---|---|---|---|
prompt | string | yes | The scene, in words: subject, action, camera, style. Allowed: at least 1 character. |
durationSec | number | yes | The clip's length in seconds (the video preset renders 2–30 s). Allowed: > 0, ≤ 30. |
model | string | no | A preset or model id. Default video. Allowed: at least 1 character. |
resolution | string | no | The size the model supports (720p, 1080p). |
aspectRatio | string | no | 16:9, 9:16, 1:1, … (what the model supports). |
firstFrame | string | no | An image the clip starts from (a project file key or a URL). |
lastFrame | string | no | An image the clip ends on. |
references | string[] | no | Reference images for style or subject. |
generateAudio | boolean | no | true adds a soundtrack (models that support it). |
seed | number | no | A number that makes the result reproducible. |
#AiVideoJob
A video job, from ai.video.get() and ai.video.wait().
| Field | Type | Always present | Description |
|---|---|---|---|
jobId | string | yes | The job id. |
status | 'expired' | 'completed' | 'queued' | 'running' | 'failed' | 'cancelled' | yes | queued and running while it renders; then completed, failed, cancelled or expired. |
progress | number | no | How far it is, 0–1, when the model reports it. |
video | { key, url } | no | The stored clip, once completed. |
video.key | string | yes | The stored file's key (see totalum.files). |
video.url | string | yes | The clip's public URL (MP4). |
usage | { credits } | no | What the call cost. |
usage.credits | number | yes | Credits charged for this call. |
error | { code, message } | no | Why it failed, when failed or expired. |
error.code | string | yes | The error code (AI_VIDEO_FAILED). |
error.message | string | yes | What happened. |
#AiVisionDescribeInput
The input of ai.vision.describe().
| Name | Type | Required | Description |
|---|---|---|---|
image | string | yes | The image: a project file key, a files URL, an https:// URL, a data: URL or bare base64. Allowed: at least 1 character. |
prompt | string | no | The question or instruction. Default: "Describe this image." |
model | string | no | A preset or model id. Default vision. Allowed: at least 1 character. |
#ChatResult
ai.chat(): the first choice read out, the raw completion kept.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The completion's id. |
model | string | yes | The model that answered (a preset resolves to the model it currently points to). |
text | string | yes | The answer's text (empty when the model only called tools). |
toolCalls | ({ id, type, function })[] | yes | The tools the model asks you to call (with tools); empty otherwise. |
toolCalls.id | string | yes | The call's id: send the tool's answer back as a tool message with this tool_call_id. |
toolCalls.type | string | yes | Always function. |
toolCalls.function | { name, arguments } | yes | The function the model wants to call. |
toolCalls.function.name | string | yes | The tool's name, as you declared it in tools. |
toolCalls.function.arguments | string | yes | Its arguments as a JSON string: JSON.parse it. |
finishReason | string | null | yes | Why it stopped: stop (finished), length (hit maxTokens), tool_calls, content_filter, or null. |
usage | ChatUsage | yes | What the call cost. |
raw | unknown | yes | The whole OpenAI-format completion, as received. |
#ChatStream
ai.chat.stream(): the OpenAI-format SSE (raw), its text deltas, and the result once it ends.
| Field | Type | Always present | Description |
|---|---|---|---|
raw | ReadableStream<Uint8Array> | yes | The OpenAI-format server-sent events, as received: return them from a route (new Response(stream.raw)) to stream to the browser. Read either raw or text(), not both. |
#stream.text
stream.text(): AsyncIterable<string>The answer's text pieces as they arrive (one pass): for await (const piece of stream.text()).
Returns AsyncIterable<string> — An async iterable of text pieces.
Example
const stream = await totalum.ai.chat.stream({ model: 'fast', messages: [{ role: 'user', content: 'Count 1 to 3.' }] });
for await (const piece of stream.text()) process.stdout.write(piece);Result:
"1" ", " "2" ", " "3" "."#stream.result
stream.result(): Promise<ChatResult>The final result once the stream ends, with its credits (reads the rest of the stream if needed).
Returns Promise<ChatResult> — The same result chat() returns; raw holds the accumulated text.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The completion's id. |
model | string | yes | The model that answered (a preset resolves to the model it currently points to). |
text | string | yes | The answer's text (empty when the model only called tools). |
toolCalls | ({ id, type, function })[] | yes | The tools the model asks you to call (with tools); empty otherwise. |
toolCalls.id | string | yes | The call's id: send the tool's answer back as a tool message with this tool_call_id. |
toolCalls.type | string | yes | Always function. |
toolCalls.function | { name, arguments } | yes | The function the model wants to call. |
toolCalls.function.name | string | yes | The tool's name, as you declared it in tools. |
toolCalls.function.arguments | string | yes | Its arguments as a JSON string: JSON.parse it. |
finishReason | string | null | yes | Why it stopped: stop (finished), length (hit maxTokens), tool_calls, content_filter, or null. |
usage | ChatUsage | yes | What the call cost. |
raw | unknown | yes | The whole OpenAI-format completion, as received. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
AI_STREAM_ERROR | 502 | The stream broke before the end. |
Example
const stream = await totalum.ai.chat.stream({ model: 'fast', messages: [{ role: 'user', content: 'Count 1 to 3.' }] });
const result = await stream.result();Result:
{
"id": "gen-1791740371-wfMT8WhpKYrAEa1s9XKT",
"model": "openai/gpt-6-luna",
"text": "1, 2, 3.",
"toolCalls": [],
"finishReason": "stop",
"usage": { "credits": 0.0005, "inputTokens": 0, "outputTokens": 0 },
"raw": { "id": "gen-1791740371-wfMT8WhpKYrAEa1s9XKT", "model": "openai/gpt-6-luna", "text": "1, 2, 3.", "choices": [{ "message": { "content": "1, 2, 3." }, "finish_reason": "stop" }] }
}#ChatUsage
What a call was charged, in Totalum credits — the only usage Totalum shows (no tokens).
| Field | Type | Always present | Description |
|---|---|---|---|
credits | number | yes | Credits charged for this call. |
inputTokens | number | yes | Deprecated. Since 1.1.0: always 0 — Totalum shows only credits. |
outputTokens | number | yes | Deprecated. Since 1.1.0: always 0 — Totalum shows only credits. |
#TotalumAiOptions
The options of totalumAi(): the client options plus the AI gateway URL gatewayConfig() returns.
| Name | Type | Required | Description |
|---|---|---|---|
aiBaseUrl | string | no | The OpenAI-compatible AI gateway gatewayConfig() returns; default TOTALUM_AI_BASE_URL, else https://ai.totalum.app/api/v1. |
key | string | no | Project key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only. |
apiUrl | string | no | The Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app. |
env | 'dev' | 'live' | no | Sent as x-totalum-env on every call (the database it targets, and dev vs live usage); default TOTALUM_ENV, else live. |
