Docs
BlogHomeStart building

Totalum API for AI Agents

Everything an AI agent needs to build and ship apps with Totalum, in one file.


Download totalum-api.mdThis page as one Markdown file

#What Totalum is

Totalum turns a natural-language prompt into a working, hosted, full-stack web app, and keeps changing it with more prompts. You call the API; Totalum's own AI coding agent writes the app; Totalum builds it, gives it a preview, publishes it, and runs it. There is no infrastructure to set up and no key to configure for the built-in features.

  • Base URL: https://builder-api.totalum.app/api/v1/vcaas (https://api-accounts.totalum.app answers the same paths).
  • Auth: header api-key: tlm_sk_… on every request. Call the API from a server only; never put the key in browser or mobile code.
  • Envelope: { "errors": null, "data": … }, or { "errors": { "errorCode", "errorMessage", "errorDetails?" }, "data": null }. Branch on errorCode.
  • Async: a prompt and a publish answer at once; poll their status every 10–15 seconds.
  • Ids: always use the projectId the API returns (a taken id gets a random suffix).
  • Legacy projects: projects created before 8 October 2026, or without the v2 engine, are legacy projects and do not have every endpoint below — see https://www.totalum.app/docs/api/migration-v2.

#What a generated app is

A standard Next.js app (App Router, React, TypeScript, Tailwind CSS) running on Cloudflare Workers, with:

  • Its own SQL database (SQLite on Cloudflare D1, used through Drizzle ORM), in two environments: dev (agent + preview) and live (published site). Schema changes are migrations; a publish applies them to live. A database can be moved to Turso past 10 GB. Nightly backups on paid plans, 30-day point-in-time restore.
  • Sign-in (e-mail + password, password reset, Google), with the owner as admin.
  • Built-in features through totalum-sdk — no keys: e-mail, AI (chat, structured output, vision, images, video, speech, embeddings), file storage, document scanning (OCR), web scraping and search, a cloud browser, PDFs, payments (the owner's Stripe account), real-time channels, scheduled jobs, vector search, logs, site analytics, SEO.
  • Mobile apps (Android and iOS, paid plans) from the same web app: a shell that opens the published site, built by Totalum and sent to the stores with the owner's own Google Play and Apple developer accounts.
  • The code is the owner's: downloadable, syncable to GitHub, runnable locally.

To use a feature, say it in the prompt ("send me an e-mail on every booking", "take card payments", "let users upload photos"). For another service, add its key as a secret and name it in the prompt.

#The development cycle

text
launch ─▶ status "done" ─▶ preview ─▶ prompt ─▶ … ─▶ publish ─▶ live site
  1. POST /projects/launch — create the project and run the first prompt.
  2. GET /projects/:projectId/agent/status every 10–15 s until status is "done" (show GET …/agent/progress meanwhile).
  3. GET /projects/:projectId → temporalDevelopmentProjectUrl is the preview. Read it again after every prompt.
  4. More prompts: POST /projects/:projectId/agent/start, then poll again.
  5. POST /projects/:projectId/deployments/deploy → poll GET …/deployments/status until "success" → the live site is productionProjectUrl (<projectId>.totalum-project.com, <projectId>.webapp-project.com when chosen as the main address with PATCH /projects/:projectId {"liveHostSuffix":"webapp-project.com"} on Professional/Enterprise, where both addresses serve the site, or the custom domain).
    • No "totalum" in the address: on Professional/Enterprise set PATCH /projects/:projectId {"liveHostSuffix":"webapp-project.com"}, or attach a custom domain (PUT /projects/:projectId/domain) or buy one (Custom Domains). Domains are per project; an account-wide domain for all projects is not supported.

Prompts, file edits and restores change dev only (each code change is a version); only a publish changes live. One prompt, publish or restore runs at a time per project (409 AGENT_RUNNING, DEPLOYMENT_RUNNING, OPERATION_IN_PROGRESS).

#Limits and credits

  • Every plan can use the API. Everything is paid in credits; spending actions need a balance of at least 1 credit (403 VCAAS_INSUFFICIENT_CREDITS, 402 INSUFFICIENT_CREDITS).
  • A prompt costs at least 3 credits, usually 3–50, by the work done; a failed run that built nothing is refunded. Fixed prices (create 2, publish 0.5, …) and monthly fees: GET /credit-costs.
  • A prompt's agent works up to about 40 minutes, then wraps up and saves; most prompts take a few minutes. Prompt ≤ 40,000 characters, ≤ 10 attachments.
  • Projects per plan: Free 2, Starter 10, Builder 50, Professional 300, Enterprise unlimited (403 MAX_PROJECTS_REACHED); creation rate per plan (429 RATE_LIMIT_EXCEEDED).
  • JSON body ≤ 1 MB; upload ≤ 20 MB. Project caps: PATCH /projects/:projectId/credit-limits (defaults for API projects: development 5,000 and infrastructure 300 credits a month; set creditLimits at create).
  • The running app: 100 built-in calls a second per project, 20 paid AI/web calls a minute per visitor.

Full list: https://www.totalum.app/docs/api/errors

#Essential endpoints

#Launch Project

POST/api/v1/vcaas/projects/launchapi-key2 credits + the first prompt

Create a project and start building it with its first prompt, in one call. Secrets, attachments, credit limits and a Figma token sent here are in place before the first build starts, which is the order the build needs.

Control your costs

Every project has two monthly caps: development (what the AI agent and project actions spend) and infrastructure (what the running app spends on hosting, database, AI, e-mail…). Projects created through the API start at 5,000 and 300 credits a month. Set your own in creditLimits here or in Create Project, and change them any time with Update Credit Limits.

Body parameters

FieldTypeRequiredDescription
projectIdstringYesYour preferred id: 4–35 characters, lowercase letters, digits and single hyphens, starting with a letter, never containing -dev-. It becomes the address <projectId>.totalum-project.com. If it is taken, a short random suffix is added and requestedProjectId is returned
promptstringYesWhat to build (the first prompt, up to 40,000 characters)
tierstringNo"auto" (default), "cheap", "normal" or "expensive" — see agent tiers. Also accepted as model or modelTier
engineVersionstringNo"v2" to require the current engine. A request that cannot get it is refused (ENGINE_V2_DISABLED, ENGINE_REQUEST_NOT_ELIGIBLE) and nothing is created. New accounts create every project on the current engine, with or without it ("legacy" is refused: ENGINE_LEGACY_UNAVAILABLE); on an older account a create without it can be a legacy project
labelstringNoDisplay name, up to 80 characters. Can be changed later; the id cannot
descriptionstringNoUp to 500 characters
groupIdstringNoAn existing project group
dataResidencystringNo"global" or "eu" (data kept in the EU only). Default: your account's setting (Set Data Residency), else "global". Fixed for the project's life
audienceCountrystringNoTwo-letter country code of the app's users ("US"); the database is placed nearest them. Default: your account's country. Never makes a project EU-only
filesobject[]NoUp to 10 attachments for the first prompt. Each has exactly one of url or content
files[].namestringYesFile name, e.g. logo.png
files[].descriptionstringNoWhat the file is for ("use as the logo"). imageDescription is accepted too
files[].urlstringNoPublic http(s) URL of a hosted file
files[].contentstringNoThe file as base64 (a data:…;base64, prefix is accepted). Uploaded for you, free
secretsobject[]NoUp to 50 environment variables, stored before the build starts
secrets[].secretNamestringYesUppercase letters, digits and _, starting with a letter (STRIPE_KEY)
secrets[].secretValuestringYes1–10,000 characters. Never returned again
secrets[].environmentstringNo"development", "production" or "both" (default)
creditLimitsobjectNoMonthly caps applied from the first prompt. Set them to control your costs — see Spending & Rate Limits
creditLimits.maxDevelopmentCreditsPerMonthnumber | nullNoCap on what the AI agent and project actions spend (prompts, builds, publishing). Default 5000 for projects created through the API; null = no cap
creditLimits.maxInfrastructureCreditsPerMonthnumber | nullNoCap on what the running app spends (hosting, database, AI, e-mail…). Default 300 for projects created through the API; null = no cap
figmaobjectNoConnect Figma so the agent can read designs linked in the prompt
figma.tokenstringYesA Figma personal access token, checked before anything is created

Response fields

FieldTypeDescription
data.projectIdstringThe id that was created. Use this one from now on
data.requestedProjectIdstring | undefinedThe id you asked for, only when the created one differs
data.descriptionstringProject description
data.planstringAlways "api"
data.createdAtstringISO 8601 creation time
data.labelstring | undefinedThe label, when one was given
data.groupIdstring | undefinedThe group, when one was given
data.dataResidencystring"global" or "eu"
data.engineVersionstring"v2"
data.engineStatestring | undefinedPresent only when the project was still being set up when the call answered ("provisioning"); the build starts by itself
data.creditLimitsobjectThe caps stored on the project, defaults included
data.filesobject[]The attachments the agent received: { name, imageDescription, url }
data.secretsobject[]The secrets stored: { _id, secretName, environment } (never the value)
data.figmaobject | undefined{ connected: true, account } when Figma was connected
data.agent.startedbooleanfalse = the project exists but the first prompt did not start (see warnings)
data.agent.statusstring | undefined"init" when the run started
data.agent.runIdstring | undefinedThe run's id
data.agent.messagestringWhat to do next
data.agent.expectedMinutesnumber | undefinedEstimated minutes for the first prompt — an estimate, never a deadline
data.agent.expectedFinishAtstring | undefinedStart time + expectedMinutes (ISO 8601)
data.agent.warningsstring[] | undefinedRun options that were ignored
data.warningsobject[]Steps that did not happen after the project was created: { step, errorCode, errorMessage }; step is "secrets", "figma", "files" or "agent", and the message names the endpoint that retries it
A 200 means the project exists — read `agent.started` and `warnings`

Everything checked before creation (body, balance, Figma token, project limit, name) answers 4xx and creates nothing. After creation a failed step is a warning, never an error: if agent.started is false, call Run AI Agent with the same prompt.

Example request

curl -X POST \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"barber-bookings","prompt":"Build a booking site for a barbershop with online payments","label":"Barber bookings","creditLimits":{"maxDevelopmentCreditsPerMonth":500}}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/launch
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "projectId": "barber-bookings",
    "description": "",
    "plan": "api",
    "createdAt": "2026-10-08T10:30:00.000Z",
    "label": "Barber bookings",
    "dataResidency": "global",
    "creditLimits": { "maxDevelopmentCreditsPerMonth": 500, "maxInfrastructureCreditsPerMonth": 300 },
    "files": [],
    "secrets": [],
    "agent": {
      "started": true,
      "status": "init",
      "runId": "run_01k6x8m2c9",
      "message": "Process started. Estimated time: about 9 minutes. This is only an approximation…",
      "expectedMinutes": 9,
      "expectedFinishAt": "2026-10-08T10:39:00.000Z"
    },
    "warnings": [],
    "engineVersion": "v2"
  }
}

Error codes

Nothing is created or charged for any of these.

CodeHTTPMeaning
MISSING_PROMPT400prompt is missing or empty
MISSING_PROJECT_ID400projectId is missing
INVALID_PROJECT_NAME400Not lowercase letters, digits and single hyphens starting with a letter
INVALID_PROJECT_NAME_LENGTH400Not 4–35 characters
RESERVED_PROJECT_NAME400The id contains -dev-
INVALID_FILES / TOO_MANY_FILES400files is not an array of objects, or has more than 10 items
INVALID_FILE_NAME / INVALID_FILE_SOURCE / INVALID_FILE_URL / INVALID_FILE_CONTENT400An attachment has no name, has both or neither of url/content, a non-http(s) URL, or invalid base64
INVALID_SECRETS / TOO_MANY_SECRETS / MISSING_SECRET_FIELDS / DUPLICATE_SECRET_NAME / INVALID_SECRET_ENVIRONMENT400A malformed secrets list
INVALID_CREDIT_LIMITS / INVALID_LIMIT400creditLimits is not an object, or a cap is not a positive number or null
INVALID_FIGMA / MISSING_FIGMA_TOKEN / FIGMA_TOKEN_MALFORMED / FIGMA_TOKEN_INVALID / FIGMA_TOKEN_FORBIDDEN400The Figma token is missing or was rejected
INVALID_PROJECT_GROUP400The group does not exist, is not yours, or is full
INVALID_DATA_RESIDENCY400dataResidency is not "global" or "eu"
INVALID_AUDIENCE_COUNTRY400audienceCountry is not a two-letter code
INVALID_ENGINE_VERSION400engineVersion has an unknown value
INSUFFICIENT_CREDITS402Your balance cannot pay the project (or is below 1 credit, needed to start the agent)
MAX_PROJECTS_REACHED403Your plan's project limit is in use. errorDetails: maxProjects, projectsUsed, plan, upgradePlan
ENGINE_REQUEST_NOT_ELIGIBLE403engineVersion: "v2" was asked but this account cannot create projects on it yet
VCAAS_INSUFFICIENT_CREDITS403Your balance is below 1 credit
PROJECT_ALREADY_EXISTS409The name and every suffixed candidate were taken
ENGINE_V2_DISABLED409New projects cannot be created on the current engine right now
ENGINE_LEGACY_UNAVAILABLE409engineVersion: "legacy", or a legacy (.zip) export code, on a new account: it creates only on the current engine
RATE_LIMIT_EXCEEDED429You are creating projects faster than your plan allows (new projects and prompts share one per-minute allowance, see Limits) — retry after Retry-After seconds
RATE_LIMITED429Your account has the most prompts running at once its plan allows, or too many Figma token checks (10 a minute) — retry after Retry-After seconds
LAUNCH_PROJECT_ERROR503A temporary failure; nothing was created. Retry after Retry-After seconds

#Run AI Agent

POST/api/v1/vcaas/projects/:projectId/agent/startapi-keyMetered, at least 3 credits

Send one prompt to the agent (or a multi-prompt batch). One run at a time per project. Once the running prompt's change is in the preview and the run is only finishing, the next prompt is accepted and waits: it starts as soon as that run ends. One prompt can wait; Stop Agent cancels the waiting one first.

A prompt is priced by the work the agent does: at least 3 credits, usually 3 to 50. A question that changes nothing is answered in seconds for 3 credits and creates no version. A run that fails without producing anything is refunded. The agent works up to about 40 minutes on one prompt, then wraps up and saves what it built; most prompts take a few minutes.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
promptstringYes¹The instruction, up to 40,000 characters
tierstringNo"auto" (default), "cheap", "normal" or "expensive" — see Agent tiers. model and modelTier are accepted as aliases
inputFilesobject[]NoUp to 10 files for this prompt. Say in the prompt what each one is for ("use this as the logo")
inputFiles[].namestringYesFile name
inputFiles[].imageDescriptionstringNoWhat the file shows
inputFiles[].urlstringYesPublic URL, or the url returned by Upload File
multiPromptobjectNoRun a batch of prompts one after another, unattended. Omit for normal use
multiPrompt.promptsstring[]No²1–50 prompts, run in order
multiPrompt.letTotalumDecidebooleanNo²true = Totalum plans the prompts from prompt (the goal)

¹ Not needed when multiPrompt.prompts is given. ² Exactly one of prompts or letTotalumDecide: true.

fastMode and effort are accepted for compatibility and ignored (an ignored option is listed in data.warnings; the run still starts).

#Agent tiers

TierUse it for
"auto"The default. Totalum picks the tier for each prompt from the prompt, the recent conversation and the project: cheap for small edits and questions, normal for builds and features, expensive only when a fix keeps failing or you ask for the best
"cheap"The lowest price for real work: landing pages, redesigns, small apps, edits
"normal"Full apps and features, with more thinking
"expensive"The most capable agent, for complex apps and hard bugs. Uses the most credits

With "auto", the run's final message carries autoMode: true and modelUsed: the tier that ran ("cheap", "normal" or "expensive"), charged as that tier.

Response fields

FieldTypeDescription
data.projectIdstringProject id
data.statusstringAlways "init"
data.messagestringHow to follow the run
data.expectedMinutesnumber | undefinedEstimated minutes — an estimate, never a deadline
data.expectedFinishAtstring | undefinedStart + expectedMinutes (ISO 8601)
data.warningsstring[] | undefinedOptions that were ignored
data.runIdstring | undefinedThe run's id (single prompt)
data.multiPrompt.totalPromptsnumber | undefinedBatches only: number of prompts (absent while Totalum plans them)

Example request

curl -X POST \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Add a contact form that emails me every message"}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/agent/start
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "projectId": "barber-bookings",
    "status": "init",
    "message": "Process started. Estimated time: about 6 minutes. This is only an approximation… Fetch GET /api/v1/vcaas/projects/barber-bookings/agent/status every 10-15 seconds to track progress; the status there is the only reliable signal that the run has finished.",
    "expectedMinutes": 6,
    "expectedFinishAt": "2026-10-08T11:16:00.000Z",
    "runId": "run_01k6xc4n7d"
  }
}

Error codes

CodeHTTPMeaning
MISSING_PROMPT400prompt is missing
INVALID_MULTI_PROMPT400multiPrompt needs exactly one of prompts or letTotalumDecide: true
TOO_MANY_PROMPTS400More than 50 prompts
INVALID_PROMPT_ITEM400A prompt in prompts is empty
VALIDATION_ERROR400The prompt is over 40,000 characters, or a field has the wrong type
INSUFFICIENT_CREDITS402Your balance is below 1 credit
PROJECT_CREDIT_LIMIT_REACHED403The project's monthly development cap is reached — raise it with Update Credit Limits
AGENT_RUNNING409A prompt is already running and its change is not in the preview yet, or a prompt is already waiting (a batch answers 400 AGENT_ALREADY_RUNNING)
PROJECT_PAUSED_BY_USER409The project is paused — resume it first
OPERATION_IN_PROGRESS409A restore, import or other long operation holds the project
PROJECT_NOT_FOUND404No such project in your account
RATE_LIMITED429Your account sent more prompts this minute than its plan allows, or has the most prompts running at once (all projects together, see Limits) — retry after Retry-After seconds
A run that runs out of credits

If your balance reaches 0 during a run, the run stops: what it built is saved as a version, it ends with INSUFFICIENT_CREDITS, and you are charged only what it used. A run that crosses the project's development cap stops the same way with PROJECT_CREDIT_LIMIT_REACHED.

#Get Agent Status

GET/api/v1/vcaas/projects/:projectId/agent/statusapi-keyFree

Whether a prompt is running, and the messages of the current (or last) run. Poll every 10–15 seconds. When status becomes "done", read the preview URL again from Get Project.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.projectIdstringProject id
data.statusstring"init" (running), "done" (the last run finished) or "idle" (never prompted)
data.startedAtstring | nullStart of the current or last run
data.runIdstring | undefinedThe current or last run
data.realtimeConversationobject[]The run's messages, oldest first (the user's prompt is in Get Full Conversation)
data.realtimeConversation[].authorstring"user" or "agent"
data.realtimeConversation[].messagestringText
data.realtimeConversation[].messageTypestring"regular", "starting", "building" (a status note), "finished" (the run's summary), "error", "limit-reached" or "individual" (a one-off notice)
data.realtimeConversation[].createdAtstringISO 8601
data.realtimeConversation[].versionIdstring | undefinedThe version this message created
data.realtimeConversation[].secretKeysNeededobject | undefinedSecrets the app needs: { NAME: { isProvided, description } }. Add them with Create Secret
data.realtimeConversation[].filesobject[] | undefinedFiles attached to the message
data.realtimeConversation[].autoModeboolean | undefinedtrue on the final message of an auto run
data.realtimeConversation[].modelUsedstring | undefinedThe tier an auto run used
data.creditsSpentnumber | undefinedWhat the last run cost, once "done"
data.expectedMinutesnumber | nullEstimate of the running prompt
data.expectedFinishAtstring | nullStart + estimate
data.multiPromptobject | nullThe batch, when one exists: { status, totalPrompts, currentPromptIndex, prompts[{ order, prompt, status }], startedAt, updatedAt }. status: "executing", "paused", "done", "cancelled"

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/agent/status
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "projectId": "barber-bookings",
    "status": "done",
    "startedAt": "2026-10-08T11:10:00.000Z",
    "realtimeConversation": [
      {
        "author": "agent",
        "message": "Added a contact form on /contact. Every message is saved and e-mailed to you. I sent a test message: saved, 200.",
        "messageType": "finished",
        "versionId": "ver_01k6xd2m8q",
        "createdAt": "2026-10-08T11:15:40.000Z",
        "autoMode": true,
        "modelUsed": "normal"
      }
    ],
    "creditsSpent": 7.4,
    "expectedMinutes": null,
    "expectedFinishAt": null,
    "multiPrompt": null,
    "runId": "run_01k6xc4n7d"
  }
}
Right after "done"

For a few seconds after a run ends, the preview may answer 503 with the header x-totalum-preview: updating while the new version goes live. Retry after the retry-after header; the preview never serves the previous version in that window.

#Get Agent Progress

GET/api/v1/vcaas/projects/:projectId/agent/progressapi-keyFree

A step-by-step view of the current run (or the last one): its stage, the files it changed, the builds and checks it ran. Use it to show a live "building" timeline. data is null before the first run.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.runIdstringThe run
data.statestring"queued", "provisioning", "running", "finishing", "done", "failed", "cancelled" or "incomplete"
data.stagestringWhere the run is: "queued", "sandbox", "starting", "thinking", "working", "build", "deploy", "fixing", "wrapping-up", "entry-page", "done", "failed", "cancelled", "incomplete"
data.startedAtstringStart time
data.finishedAtstring | undefinedEnd time
data.turnsnumberAgent turns so far
data.stepsobject[]Up to the 40 latest steps: { at, kind, file?, task?, ok?, added?, removed? }. kind: read, search, create, edit, delete, command, web, browser, integration, helper, plan, waiting; task (commands): build, test, install, database, check, save, other
data.totalStepsnumberAll steps of the run
data.filesobject[]Files changed: { path, change: "create" | "edit" | "delete", added, removed }
data.countsobject{ reads, searches, edits, commands, failedCommands }
data.buildobject | undefined{ ok } of the last build
data.testsobject | undefined{ runs, failed, failedFlows? }: the checks the agent ran on the pages it changed
data.versionIdstring | undefinedThe version the run saved
data.failurestring | undefinedWhy it failed: INSUFFICIENT_CREDITS, PROJECT_CREDIT_LIMIT_REACHED, DEPLOY_FAILED, RUN_TIMED_OUT, RUN_INTERRUPTED, CONTENT_BLOCKED, … or OTHER
data.creditsSpentnumber | undefinedWhat the run cost, once it ended
data.previewAtstring | undefinedWhen the preview last started serving this run's new build (reload your preview frame when it changes)

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/agent/progress
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "runId": "run_01k6xc4n7d",
    "state": "running",
    "stage": "working",
    "startedAt": "2026-10-08T11:10:00.000Z",
    "turns": 12,
    "steps": [
      { "at": "2026-10-08T11:12:01.000Z", "kind": "edit", "file": "src/app/contact/page.tsx", "added": 64, "removed": 0 },
      { "at": "2026-10-08T11:12:30.000Z", "kind": "command", "task": "database", "ok": true }
    ],
    "totalSteps": 18,
    "files": [{ "path": "src/app/contact/page.tsx", "change": "create", "added": 64, "removed": 0 }],
    "counts": { "reads": 6, "searches": 2, "edits": 4, "commands": 3, "failedCommands": 0 }
  }
}

#Get Project

GET/api/v1/vcaas/projects/:projectIdapi-keyFree

Everything about one project: its URLs, the agent's state, the last publish, its domain, secrets and spending. Read it again after every prompt: the preview address can change.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.projectIdstringProject id
data.labelstring | undefinedDisplay name, when set
data.groupIdstring | undefinedGroup, when filed in one
data.descriptionstringDescription
data.planstring"api"
data.createdAtstringISO 8601 creation time
data.agentProcessStatusstring"init" (a prompt is running), "done" (the last one finished) or "idle" (never prompted)
data.agentServerStatusstring"Creating" while the project is set up, "Archived" while it sleeps after a long inactivity (the next prompt wakes it), else "Active"
data.temporalDevelopmentProjectUrlstringThe development preview URL
data.developmentUrlFieldToUsestringAlways "temporalDevelopmentProjectUrl"
data.productionProjectUrlstringThe published host: the custom domain once it is active, else <projectId>.totalum-project.com (it answers only after the first publish)
data.deploymentobject | nullThe last publish: { createdAt, status, versionId }, status = "deploying", "success" or "error", versionId = the version it put online (or tried to); null if never published
data.customDomainobject | null{ _id, projectId, deploymentId, hostname, status, sslStatus, dnsRecordsToAdd, createdAt, updatedAt, source? } — see Custom Domains
data.secretsobject[]{ _id, secretName, environment } (values are never returned)
data.versionRecoveryobject | nullWhile a version restore runs or after it failed: { status: "recovering" | "error", versionId, startedAt, errorMessage? }
data.importInProgressobject | null{ startedAt } while an import runs
data.previewImageUrlstring | undefinedScreenshot of the home page
data.totalCreditsSpentnumberCredits this project has spent in total
data.creditLimitsobject{ maxDevelopmentCreditsPerMonth, maxInfrastructureCreditsPerMonth }, null = no cap
data.multiPromptobject | nullThe multi-prompt batch, as on Get Agent Status
data.engineVersionstring"v2"
data.engineStatestring"queued", "provisioning", "ready", "error", "suspended" or "deleting"
data.liveHostSuffixstring"totalum-project.com" or "webapp-project.com": the domain of the main published address and preview (Update Project)
data.dataResidencystring"global" or "eu"
data.sandboxobjectThe builder: { state, lastPromptAt }
data.lifecycleobject{ state, nextStep, lastActivityAt, pausedAt, resumable } — see Pause Project
data.previewobject{ url, token8, status, deployState }: the preview address and whether its latest build is live (status: "live", "deploying", "failed", "parked", "none")
data.lastDeployErrorobject | nullWhy the last preview or publish build failed, when it did
data.currentRunobject | nullThe running prompt: { id, state, startedAt, batchId?, promptPreview }
data.statestring"active" or "paused-by-user"
data.pausedAtstring | nullWhen it was paused
data.resumablebooleanWhether a paused project can be resumed
data.runawayPauseobject | nullSet while the emergency stop has paused the site: { pausedAt, resumesAt, metric, value?, threshold? } — see Release Runaway Pause
data.contextClearPendingbooleanThe next prompt starts with a fresh context
data.operationsobject[]Long operations running now (publish, restore, import…): { kind, id, status, startedAt, errorMessage?, versionId?, progress? }

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "projectId": "barber-bookings",
    "label": "Barber bookings",
    "description": "",
    "plan": "api",
    "agentProcessStatus": "done",
    "createdAt": "2026-10-08T10:30:00.000Z",
    "agentServerStatus": "Active",
    "deployment": { "createdAt": "2026-10-08T11:02:00.000Z", "status": "success", "versionId": "ver_01k6xd2m4n6p8q0r2s4t6v8w0y" },
    "versionRecovery": null,
    "importInProgress": null,
    "secrets": [{ "_id": "sec_01k6x9", "secretName": "STRIPE_KEY", "environment": "production" }],
    "customDomain": null,
    "developmentUrlFieldToUse": "temporalDevelopmentProjectUrl",
    "temporalDevelopmentProjectUrl": "https://barber-bookings-dev-temporal-link-preview-k3y9x2ab.totalum-project.com",
    "productionProjectUrl": "barber-bookings.totalum-project.com",
    "totalCreditsSpent": 24.6,
    "creditLimits": { "maxDevelopmentCreditsPerMonth": 500, "maxInfrastructureCreditsPerMonth": 300 },
    "multiPrompt": null,
    "engineVersion": "v2",
    "engineState": "ready",
    "dataResidency": "global",
    "sandbox": { "state": "active", "lastPromptAt": "2026-10-08T10:30:05.000Z" },
    "preview": { "url": "https://barber-bookings-dev-temporal-link-preview-k3y9x2ab.totalum-project.com", "token8": "k3y9x2ab", "status": "live", "deployState": "idle" },
    "lastDeployError": null,
    "currentRun": null,
    "state": "active",
    "pausedAt": null,
    "resumable": false,
    "contextClearPending": false,
    "operations": []
  }
}

Error codes

CodeHTTPMeaning
PROJECT_NOT_FOUND404No project with this id in your account
API_ACCESS_DISABLED403API access is switched off for this project in your account settings
PROJECT_NOT_ALLOWED403Your API key is restricted to other projects

#List Projects

GET/api/v1/vcaas/projectsapi-keyFree

Your projects, newest first, up to 100 per page. The total is in the X-Total-Count header and X-Has-More says whether another page exists.

Query parameters

FieldTypeRequiredDescription
limitnumberNo1–100 (default 100)
skipnumberNoProjects to skip, for paging (default 0)
searchstringNoCase-insensitive match on id, label and description
sortFieldstringNodate (default, creation time) or lastModified
sortDirectionstringNodesc (default) or asc
groupIdstringNoOnly the projects of one group; none = only ungrouped projects
createdFromstringNoCreated at or after this date (ISO 8601; a bare day is UTC)
createdTostringNoCreated at or before this date (a bare day includes the whole day)

Response fields

FieldTypeDescription
data[].projectIdstringProject id
data[].descriptionstringDescription
data[].planstring"api"
data[].createdAtstringISO 8601 creation time
data[].labelstring | undefinedDisplay name, when set
data[].groupIdstring | undefinedGroup, when filed in one
data[].previewImageUrlstring | undefinedScreenshot of the home page, once a prompt has finished
data[].lastModifiedAtstringLast change (prompt, publish, edit); never null
data[].productionProjectUrlstringThe published host, as on Get Project: the custom domain once it is active, else <projectId>.totalum-project.com (or <projectId>.webapp-project.com when chosen)
data[].engineVersionstring"v2"
data[].liveHostSuffixstring | undefined"webapp-project.com" when chosen (Update Project); absent otherwise

Example request

curl -H "api-key: tlm_sk_your_key" \
  "https://builder-api.totalum.app/api/v1/vcaas/projects?limit=20&sortField=lastModified"
Success · 200 OK
json
{
  "errors": null,
  "data": [
    {
      "projectId": "barber-bookings",
      "description": "",
      "plan": "api",
      "createdAt": "2026-10-08T10:30:00.000Z",
      "label": "Barber bookings",
      "previewImageUrl": "https://files.totalum.app/previews/barber-bookings.png",
      "lastModifiedAt": "2026-10-08T10:41:00.000Z",
      "productionProjectUrl": "barber-bookings.totalum-project.com",
      "engineVersion": "v2"
    }
  ]
}

#Deploy to Production

POST/api/v1/vcaas/projects/:projectId/deployments/deployapi-key0.5 credits

Publish the project's current version. It takes about 2 to 5 minutes. If the build or a migration fails, Totalum's agent tries to repair it once or twice before the publish ends in error; the live site keeps serving the previous version until a publish succeeds.

Migrations that delete data. A migration that drops a table or column, or deletes or overwrites existing live rows, runs only when the prompt that made it asked for it, or when you publish with confirmDestructive: true. Otherwise the publish ends in error with DESTRUCTIVE_MIGRATION_NEEDS_CONFIRMATION and nothing changes on live. Rows already in the live database that break a new rule end the publish with the list of those rows; Totalum never changes production data on its own.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
confirmDestructivebooleanNotrue = run the pending migrations that drop columns or tables or delete/overwrite live rows

Response fields

FieldTypeDescription
data.projectIdstringProject id
data.statusstring"deploying"
data.deploymentIdstringThis publish
data.messagestringHow to follow it

Example request

curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/deployments/deploy
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "projectId": "barber-bookings",
    "status": "deploying",
    "message": "Deployment started. It will take from 2 to 5 minutes depending on project size. Fetch GET /api/v1/vcaas/projects/barber-bookings/deployments/status every 10-15 seconds to track progress.",
    "deploymentId": "dep_01k6xf3a9b"
  }
}

Error codes

CodeHTTPMeaning
AGENT_RUNNING409A prompt is running; publish when it is done
DEPLOYMENT_RUNNING409A publish is already running
OPERATION_IN_PROGRESS409A restore, import or other long operation holds the project, or it is still being set up
INSUFFICIENT_CREDITS402Not enough credits
PROJECT_CREDIT_LIMIT_REACHED403The project's monthly development cap is reached

#Get Deployment Status

GET/api/v1/vcaas/projects/:projectId/deployments/statusapi-keyFree

The newest publish and where it is. Poll every 10–15 seconds until status is "success" or "error".

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.statusstring | null"deploying", "success", "error", or null if never published
data.createdAtstring | nullWhen the publish started
data.deploymentIdstring | undefinedThe publish
data.stepstring | undefined"build", "migration", "upload", "probe", "fixing" (the agent is repairing a failed step) or "done"
data.progressobject | undefinedWhile step is "fixing": { stage: "fixing", failedStage, fixAttempt, fixAttempts }
data.errorDetailsobject | undefinedOn "error": { stage, title, message, logExcerpt, versionId, deploymentId?, migrationName?, fixSummary?, at } — fixSummary says what the repair tried and what you need to do
data.liveUrlstringThe live address

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/deployments/status
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "status": "success",
    "createdAt": "2026-10-08T11:20:00.000Z",
    "deploymentId": "dep_01k6xf3a9b",
    "step": "done",
    "liveUrl": "https://barber-bookings.totalum-project.com"
  }
}

When it is "success", the address to show is productionProjectUrl on Get Project (the custom domain once active).

#Create Project

POST/api/v1/vcaas/projectsapi-key2 credits

Create an empty project: no prompt, nothing built. Use it only when you will import a project into it, connect GitHub first, or write the files yourself; otherwise use Launch Project.

The project is set up in the background (about 15 seconds); a write sent before it is ready waits for it.

Body parameters

FieldTypeRequiredDescription
projectIdstringYesSame rules as Launch Project; a taken id gets a suffix
labelstringNoDisplay name, up to 80 characters
descriptionstringNoUp to 500 characters
groupIdstringNoAn existing project group
engineVersionstringNo"v2" to require the current engine (new accounts always get it), as in Launch Project
dataResidencystringNo"global" or "eu", as in Launch Project
audienceCountrystringNoTwo-letter country code of the app's users
sourceImportCodestringNoAn importCode from Export Project: the new project is created to receive it (checked before anything is created)
creditLimitsobjectNoThe project's monthly spending caps, as in Launch Project
creditLimits.maxDevelopmentCreditsPerMonthnumber | nullNoCap on what the AI agent and project actions spend. Default 5000; null = no cap
creditLimits.maxInfrastructureCreditsPerMonthnumber | nullNoCap on what the running app spends. Default 300; null = no cap

Response fields

FieldTypeDescription
data.projectIdstringThe id that was created. Use this one
data.requestedProjectIdstring | undefinedThe id you asked for, when it differs
data.descriptionstringProject description
data.planstringAlways "api"
data.createdAtstringISO 8601 creation time
data.labelstring | undefinedThe label, when given
data.groupIdstring | undefinedThe group, when given
data.dataResidencystring"global" or "eu"
data.engineVersionstring"v2"
data.engineStatestring"provisioning" — the project is being set up

Example request

curl -X POST \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"crm-import","label":"CRM (imported)","creditLimits":{"maxDevelopmentCreditsPerMonth":1000,"maxInfrastructureCreditsPerMonth":500}}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "projectId": "crm-import",
    "description": "",
    "plan": "api",
    "createdAt": "2026-10-08T10:30:00.000Z",
    "label": "CRM (imported)",
    "dataResidency": "global",
    "engineVersion": "v2",
    "engineState": "provisioning"
  }
}

Error codes

CodeHTTPMeaning
MISSING_PROJECT_ID / INVALID_PROJECT_NAME / INVALID_PROJECT_NAME_LENGTH / RESERVED_PROJECT_NAME400The id is missing or malformed
INVALID_PROJECT_GROUP400Unknown or full group
INVALID_DATA_RESIDENCY / INVALID_AUDIENCE_COUNTRY / INVALID_ENGINE_VERSION400An invalid option
INVALID_CREDIT_LIMITS / INVALID_LIMIT400creditLimits is not an object, or a cap is not a positive number or null
INSUFFICIENT_CREDITS402Not enough credits for the project
MAX_PROJECTS_REACHED403Your plan's project limit is in use
PROJECT_ALREADY_EXISTS409The name and every suffixed candidate were taken
ENGINE_V2_DISABLED409New projects cannot be created on the current engine right now
ENGINE_LEGACY_UNAVAILABLE409engineVersion: "legacy", or a legacy (.zip) export code, on a new account: it creates only on the current engine
RATE_LIMIT_EXCEEDED429Too many creates for your plan (new projects and prompts share one per-minute allowance, see Limits); retry after Retry-After seconds
CREATE_PROJECT_ERROR503A temporary failure; nothing was created. Retry after Retry-After seconds

Project limits per plan:

PlanProjects at onceNew projects
Free21 a minute
Starter105 a minute
Builder5010 a minute
Professional30030 a minute
EnterpriseUnlimited100 a minute

#Everything else

Each link is the complete reference for one area: the page, and the same page as Markdown for agents.

NeedDocs
Read/write the app's data (rows, SQL, relations), migrationsDatabase · database.md
Backups, restore, time travel, move to TursoDatabase Backups & Restore · database-backups.md
Versions, diffs, restore a versionVersions · versions.md
Read or edit code files, rebuildProject Files · project-files.md
Download source, upload files for promptsSource Code & Uploads · source-files.md
Stop a run, conversation, multi-prompt, agent instructions, MCP serversAI Agent · agent.md
Update, delete, pause, resume a project, its live stateProjects · projects.md
Custom domain on a projectCustom Domains · domains.md
Buy a domainBuy a Domain · domain-registration.md
Mobile apps, signing, store uploadsMobile Apps · mobile.md
Payments (the app's Stripe)Payments · payments.md
Webhooks (prompt finished, publish ended…)Webhooks · webhooks.md
Scheduled jobsScheduled Jobs · cron-jobs.md
Secrets, project API keys, .envSecrets, Keys & .env · secrets.md
Logs of the preview and the live siteLogs · server.md
Usage, spending, site analyticsUsage & Analytics · analytics.md
Spending caps, feature switches, rate limitSpending & Rate Limits · limits.md
Copy a project / templatesExport & Import · transfer.md
GitHub syncGitHub · github.md
Build from Figma designsFigma · figma.md
Visual edits without a promptVisual Edits · visual-edit.md
Folders for projectsProject Groups · project-groups.md
Balance, prices, EU-only dataAccount · account.md
An app that itself creates appsBuilding on Totalum's API · build-on-totalum.md
Error codes and limitsErrors & Limits · errors.md
Every endpoint in one filefull.md · OpenAPI
Inside the generated app: auth, e-mail, AI, files, payments, real-time, cron, database driverTotalum SDK · totalum-sdk.md
Use Totalum from Claude Code, Codex, CursorTotalum MCP
Stack, running locally, self-hostingFor Developers
Legacy projectsLegacy projects: what differs