# Totalum API for AI Agents

> Everything an AI agent needs to build and ship apps with Totalum, in one 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](https://www.totalum.app/docs/api/database-backups#move-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](https://www.totalum.app/docs/api/secrets#create-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"}` (the app's emails then also come from `p-<projectId>@mail.webapp-project.com`), or attach a custom domain (`PUT /projects/:projectId/domain`) or buy one ([Custom Domains](https://www.totalum.app/docs/api/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](https://www.totalum.app/docs/api/versions)); 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

```endpoint
method: POST
path: /api/v1/vcaas/projects/launch
cost: 2 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.

:::tip 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](https://www.totalum.app/docs/api/projects#create-project), and change them any time with [Update Credit Limits](https://www.totalum.app/docs/api/limits#update-credit-limits).
:::

**Body parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `projectId` | string | Yes | Your 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 |
| `prompt` | string | Yes | What to build (the first prompt, up to 40,000 characters) |
| `tier` | string | No | `"auto"` (default), `"cheap"`, `"normal"` or `"expensive"` — see [agent tiers](https://www.totalum.app/docs/api/agent#agent-tiers). Also accepted as `model` or `modelTier` |
| `engineVersion` | string | No | `"v2"` to require the current engine. A request that cannot get it is refused (`ENGINE_V2_DISABLED`, `ENGINE_REQUEST_NOT_ELIGIBLE`, `ENGINE_V2_REQUIRES_PLATFORM_ACCOUNT`) 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](https://www.totalum.app/docs/api/migration-v2) |
| `label` | string | No | Display name, up to 80 characters. Can be changed later; the id cannot |
| `description` | string | No | Up to 500 characters |
| `groupId` | string | No | An existing [project group](https://www.totalum.app/docs/api/project-groups) |
| `ownerEmail` | string | No | The e-mail of the app's owner: the person who may use the app's admin and staff pages once they sign up with it and confirm it in the app. Default: none — then the first person who signs up in the app becomes its admin after entering the 8-digit code e-mailed to them (the preview and the published app each have their own first sign-up). **Builders on Totalum** (your users build apps through your product): send your user's e-mail. Change it later with [Set App Owner](https://www.totalum.app/docs/api/projects#set-app-owner) |
| `dataResidency` | string | No | `"global"` or `"eu"` (data kept in the EU only). Default: your account's setting ([Set Data Residency](https://www.totalum.app/docs/api/account#set-data-residency)), else `"global"`. Fixed for the project's life |
| `audienceCountry` | string | No | Two-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 |
| `files` | object[] | No | Up to 10 attachments for the first prompt. Each has exactly one of `url` or `content` |
| `files[].name` | string | Yes | File name, e.g. `logo.png` |
| `files[].description` | string | No | What the file is for ("use as the logo"). `imageDescription` is accepted too |
| `files[].url` | string | No | Public `http(s)` URL of a hosted file |
| `files[].content` | string | No | The file as base64 (a `data:…;base64,` prefix is accepted). Uploaded for you, free |
| `secrets` | object[] | No | Up to 50 environment variables, stored before the build starts |
| `secrets[].secretName` | string | Yes | Uppercase letters, digits and `_`, starting with a letter (`STRIPE_KEY`) |
| `secrets[].secretValue` | string | Yes | 1–10,000 characters. Never returned again |
| `secrets[].environment` | string | No | `"development"`, `"production"` or `"both"` (default) |
| `creditLimits` | object | No | Monthly caps applied from the first prompt. Set them to control your costs — see [Spending & Rate Limits](https://www.totalum.app/docs/api/limits) |
| `creditLimits.maxDevelopmentCreditsPerMonth` | number \| null | No | Cap on what the AI agent and project actions spend (prompts, builds, publishing). Default `5000` for projects created through the API; `null` = no cap |
| `creditLimits.maxInfrastructureCreditsPerMonth` | number \| null | No | Cap on what the running app spends (hosting, database, AI, e-mail…). Default `300` for projects created through the API; `null` = no cap |
| `figma` | object | No | Connect Figma so the agent can read designs linked in the prompt |
| `figma.token` | string | Yes | A Figma personal access token, checked before anything is created |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.projectId` | string | The id that was created. **Use this one from now on** |
| `data.requestedProjectId` | string \| undefined | The id you asked for, only when the created one differs |
| `data.description` | string | Project description |
| `data.plan` | string | Always `"api"` |
| `data.createdAt` | string | ISO 8601 creation time |
| `data.label` | string \| undefined | The label, when one was given |
| `data.groupId` | string \| undefined | The group, when one was given |
| `data.dataResidency` | string | `"global"` or `"eu"` |
| `data.engineVersion` | string | `"v2"` |
| `data.engineState` | string \| undefined | Present only when the project was still being set up when the call answered (`"provisioning"`); the build starts by itself |
| `data.creditLimits` | object | The caps stored on the project, defaults included |
| `data.files` | object[] | The attachments the agent received: `{ name, imageDescription, url }` |
| `data.secrets` | object[] | The secrets stored: `{ _id, secretName, environment }` (never the value) |
| `data.figma` | object \| undefined | `{ connected: true, account }` when Figma was connected |
| `data.agent.started` | boolean | `false` = the project exists but the first prompt did not start (see `warnings`) |
| `data.agent.status` | string \| undefined | `"init"` when the run started |
| `data.agent.runId` | string \| undefined | The run's id |
| `data.agent.message` | string | What to do next |
| `data.agent.expectedMinutes` | number \| undefined | Estimated minutes for the first prompt — an estimate, never a deadline |
| `data.agent.expectedFinishAt` | string \| undefined | Start time + `expectedMinutes` (ISO 8601) |
| `data.agent.warnings` | string[] \| undefined | Run options that were ignored |
| `data.warnings` | object[] | 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 |

:::warning 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](https://www.totalum.app/docs/api/agent#run-ai-agent) with the same prompt.
:::

**Example request**

```bash
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 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.

| Code | HTTP | Meaning |
|---|---|---|
| `MISSING_PROMPT` | 400 | `prompt` is missing or empty |
| `MISSING_PROJECT_ID` | 400 | `projectId` is missing |
| `INVALID_PROJECT_NAME` | 400 | Not lowercase letters, digits and single hyphens starting with a letter |
| `INVALID_PROJECT_NAME_LENGTH` | 400 | Not 4–35 characters |
| `RESERVED_PROJECT_NAME` | 400 | The id contains `-dev-` |
| `INVALID_FILES` / `TOO_MANY_FILES` | 400 | `files` is not an array of objects, or has more than 10 items |
| `INVALID_FILE_NAME` / `INVALID_FILE_SOURCE` / `INVALID_FILE_URL` / `INVALID_FILE_CONTENT` | 400 | An 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_ENVIRONMENT` | 400 | A malformed `secrets` list |
| `INVALID_CREDIT_LIMITS` / `INVALID_LIMIT` | 400 | `creditLimits` 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_FORBIDDEN` | 400 | The Figma token is missing or was rejected |
| `INVALID_PROJECT_GROUP` | 400 | The group does not exist, is not yours, or is full |
| `INVALID_DATA_RESIDENCY` | 400 | `dataResidency` is not `"global"` or `"eu"` |
| `INVALID_AUDIENCE_COUNTRY` | 400 | `audienceCountry` is not a two-letter code |
| `INVALID_ENGINE_VERSION` | 400 | `engineVersion` has an unknown value |
| `INSUFFICIENT_CREDITS` | 402 | Your balance cannot pay the project (or is below 1 credit, needed to start the agent) |
| `MAX_PROJECTS_REACHED` | 403 | Your plan's project limit is in use. `errorDetails`: `maxProjects`, `projectsUsed`, `plan`, `upgradePlan` |
| `ENGINE_REQUEST_NOT_ELIGIBLE` | 403 | `engineVersion: "v2"` was asked but this account cannot create projects on it yet |
| `ENGINE_V2_REQUIRES_PLATFORM_ACCOUNT` | 403 | `engineVersion: "v2"` (or a current-engine export code) from an account with no Totalum platform account: sign in once at platform.totalum.app, then retry |
| `VCAAS_INSUFFICIENT_CREDITS` | 403 | Your balance is below 1 credit |
| `PROJECT_ALREADY_EXISTS` | 409 | The name and every suffixed candidate were taken |
| `ENGINE_V2_DISABLED` | 409 | New projects cannot be created on the current engine right now |
| `ENGINE_LEGACY_UNAVAILABLE` | 409 | `engineVersion: "legacy"`, or a legacy (`.zip`) export code, on a new account: it creates only on the current engine |
| `RATE_LIMIT_EXCEEDED` | 429 | You are creating projects faster than your plan allows (new projects and prompts share one per-minute allowance, see [Limits](https://www.totalum.app/docs/limits)) — retry after `Retry-After` seconds |
| `RATE_LIMITED` | 429 | Your 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_ERROR` | 503 | A temporary failure; nothing was created. Retry after `Retry-After` seconds |

## Run AI Agent

```endpoint
method: POST
path: /api/v1/vcaas/projects/:projectId/agent/start
cost: Metered, 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](https://www.totalum.app/docs/api/agent#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**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |

**Body parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `prompt` | string | Yes¹ | The instruction, up to 40,000 characters |
| `tier` | string | No | `"auto"` (default), `"cheap"`, `"normal"` or `"expensive"` — see [Agent tiers](https://www.totalum.app/docs/api/agent#agent-tiers). `model` and `modelTier` are accepted as aliases |
| `inputFiles` | object[] | No | Up to 10 files for this prompt. Say in the prompt what each one is for ("use this as the logo") |
| `inputFiles[].name` | string | Yes | File name |
| `inputFiles[].imageDescription` | string | No | What the file shows |
| `inputFiles[].url` | string | Yes | Public URL, or the `url` returned by [Upload File](https://www.totalum.app/docs/api/source-files#upload-file) |
| `multiPrompt` | object | No | Run a batch of prompts one after another, unattended. Omit for normal use |
| `multiPrompt.prompts` | string[] | No² | 1–50 prompts, run in order |
| `multiPrompt.letTotalumDecide` | boolean | No² | `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

| Tier | Use 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**

| Field | Type | Description |
|---|---|---|
| `data.projectId` | string | Project id |
| `data.status` | string | Always `"init"` |
| `data.message` | string | How to follow the run |
| `data.expectedMinutes` | number \| undefined | Estimated minutes — an estimate, never a deadline |
| `data.expectedFinishAt` | string \| undefined | Start + `expectedMinutes` (ISO 8601) |
| `data.warnings` | string[] \| undefined | Options that were ignored |
| `data.runId` | string \| undefined | The run's id (single prompt) |
| `data.multiPrompt.totalPrompts` | number \| undefined | Batches only: number of prompts (absent while Totalum plans them) |

**Example request**

```bash
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 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**

| Code | HTTP | Meaning |
|---|---|---|
| `MISSING_PROMPT` | 400 | `prompt` is missing |
| `INVALID_MULTI_PROMPT` | 400 | `multiPrompt` needs exactly one of `prompts` or `letTotalumDecide: true` |
| `TOO_MANY_PROMPTS` | 400 | More than 50 prompts |
| `INVALID_PROMPT_ITEM` | 400 | A prompt in `prompts` is empty |
| `VALIDATION_ERROR` | 400 | The prompt is over 40,000 characters, or a field has the wrong type |
| `INSUFFICIENT_CREDITS` | 402 | Your balance is below 1 credit |
| `PROJECT_CREDIT_LIMIT_REACHED` | 403 | The project's monthly development cap is reached — raise it with [Update Credit Limits](https://www.totalum.app/docs/api/limits#update-credit-limits) |
| `AGENT_RUNNING` | 409 | A 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_USER` | 409 | The project is paused — [resume](https://www.totalum.app/docs/api/projects#resume-project) it first |
| `OPERATION_IN_PROGRESS` | 409 | A restore, import or other long operation holds the project |
| `PROJECT_NOT_FOUND` | 404 | No such project in your account |
| `RATE_LIMITED` | 429 | Your account sent more prompts this minute than its plan allows, or has the most prompts running at once (all projects together, see [Limits](https://www.totalum.app/docs/limits)) — retry after `Retry-After` seconds |

:::info 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

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId/agent/status
cost: Free
```

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](https://www.totalum.app/docs/api/projects#get-project).

**Path parameters**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.projectId` | string | Project id |
| `data.status` | string | `"init"` (running), `"done"` (the last run finished) or `"idle"` (never prompted) |
| `data.startedAt` | string \| null | Start of the current or last run |
| `data.runId` | string \| undefined | The current or last run |
| `data.realtimeConversation` | object[] | The run's messages, oldest first (the user's prompt is in [Get Full Conversation](https://www.totalum.app/docs/api/agent#get-full-conversation)) |
| `data.realtimeConversation[].author` | string | `"user"` or `"agent"` |
| `data.realtimeConversation[].message` | string | Text |
| `data.realtimeConversation[].messageType` | string | `"regular"`, `"starting"`, `"building"` (a status note), `"finished"` (the run's summary), `"error"`, `"limit-reached"` or `"individual"` (a one-off notice) |
| `data.realtimeConversation[].createdAt` | string | ISO 8601 |
| `data.realtimeConversation[].versionId` | string \| undefined | The version this message created |
| `data.realtimeConversation[].secretKeysNeeded` | object \| undefined | Secrets the app needs: `{ NAME: { isProvided, description } }`. Add them with [Create Secret](https://www.totalum.app/docs/api/secrets#create-secret) |
| `data.realtimeConversation[].files` | object[] \| undefined | Files attached to the message |
| `data.realtimeConversation[].autoMode` | boolean \| undefined | `true` on the final message of an `auto` run |
| `data.realtimeConversation[].modelUsed` | string \| undefined | The tier an `auto` run used |
| `data.creditsSpent` | number \| undefined | What the last run cost, once `"done"` |
| `data.expectedMinutes` | number \| null | Estimate of the running prompt |
| `data.expectedFinishAt` | string \| null | Start + estimate |
| `data.multiPrompt` | object \| null | The batch, when one exists: `{ status, totalPrompts, currentPromptIndex, prompts[{ order, prompt, status }], startedAt, updatedAt }`. `status`: `"executing"`, `"paused"`, `"done"`, `"cancelled"` |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/agent/status
```

:::success 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"
  }
}
```
:::

:::info 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

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId/agent/progress
cost: Free
```

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**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.runId` | string | The run |
| `data.state` | string | `"queued"`, `"provisioning"`, `"running"`, `"finishing"`, `"done"`, `"failed"`, `"cancelled"` or `"incomplete"` |
| `data.stage` | string | Where the run is: `"queued"`, `"sandbox"`, `"starting"`, `"thinking"`, `"working"`, `"build"`, `"deploy"`, `"fixing"`, `"wrapping-up"`, `"entry-page"`, `"done"`, `"failed"`, `"cancelled"`, `"incomplete"` |
| `data.startedAt` | string | Start time |
| `data.finishedAt` | string \| undefined | End time |
| `data.turns` | number | Agent turns so far |
| `data.steps` | object[] | 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.totalSteps` | number | All steps of the run |
| `data.files` | object[] | Files changed: `{ path, change: "create" \| "edit" \| "delete", added, removed }` |
| `data.counts` | object | `{ reads, searches, edits, commands, failedCommands }` |
| `data.build` | object \| undefined | `{ ok }` of the last build |
| `data.tests` | object \| undefined | `{ runs, failed, failedFlows? }`: the checks the agent ran on the pages it changed |
| `data.versionId` | string \| undefined | The version the run saved |
| `data.failure` | string \| undefined | Why it failed: `INSUFFICIENT_CREDITS`, `PROJECT_CREDIT_LIMIT_REACHED`, `DEPLOY_FAILED`, `RUN_TIMED_OUT`, `RUN_INTERRUPTED`, `CONTENT_BLOCKED`, … or `OTHER` |
| `data.creditsSpent` | number \| undefined | What the run cost, once it ended |
| `data.previewAt` | string \| undefined | When the preview last started serving this run's new build (reload your preview frame when it changes) |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/agent/progress
```

:::success 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

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId
cost: Free
```

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**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.projectId` | string | Project id |
| `data.label` | string \| undefined | Display name, when set |
| `data.groupId` | string \| undefined | Group, when filed in one |
| `data.description` | string | Description |
| `data.plan` | string | `"api"` |
| `data.createdAt` | string | ISO 8601 creation time |
| `data.agentProcessStatus` | string | `"init"` (a prompt is running), `"done"` (the last one finished) or `"idle"` (never prompted) |
| `data.agentServerStatus` | string | `"Creating"` while the project is set up, `"Archived"` while it sleeps after a long inactivity (the next prompt wakes it), else `"Active"` |
| `data.temporalDevelopmentProjectUrl` | string | **The development preview URL** |
| `data.developmentUrlFieldToUse` | string | Always `"temporalDevelopmentProjectUrl"` |
| `data.productionProjectUrl` | string | The published host: the custom domain once it is active, else `<projectId>.totalum-project.com` (it answers only after the first publish) |
| `data.deployment` | object \| null | The last publish: `{ createdAt, status, versionId }`, `status` = `"deploying"`, `"success"` or `"error"`, `versionId` = the version it put online (or tried to); `null` if never published |
| `data.customDomain` | object \| null | `{ _id, projectId, deploymentId, hostname, status, sslStatus, dnsRecordsToAdd, createdAt, updatedAt, source? }` — see [Custom Domains](https://www.totalum.app/docs/api/domains) |
| `data.secrets` | object[] | `{ _id, secretName, environment }` (values are never returned) |
| `data.versionRecovery` | object \| null | While a version restore runs or after it failed: `{ status: "recovering" \| "error", versionId, startedAt, errorMessage? }` |
| `data.importInProgress` | object \| null | `{ startedAt }` while an import runs |
| `data.previewImageUrl` | string \| undefined | Screenshot of the home page |
| `data.totalCreditsSpent` | number | Credits this project has spent in total |
| `data.creditLimits` | object | `{ maxDevelopmentCreditsPerMonth, maxInfrastructureCreditsPerMonth }`, `null` = no cap |
| `data.multiPrompt` | object \| null | The multi-prompt batch, as on [Get Agent Status](https://www.totalum.app/docs/api/agent#get-agent-status) |
| `data.engineVersion` | string | `"v2"` |
| `data.engineState` | string | `"queued"`, `"provisioning"`, `"ready"`, `"error"`, `"suspended"` or `"deleting"` |
| `data.liveHostSuffix` | string | `"totalum-project.com"` or `"webapp-project.com"`: the domain of the main published address and preview ([Update Project](https://www.totalum.app/docs/api/projects#update-project)) |
| `data.dataResidency` | string | `"global"` or `"eu"` |
| `data.sandbox` | object | The builder: `{ state, lastPromptAt }` |
| `data.lifecycle` | object | `{ state, nextStep, lastActivityAt, pausedAt, resumable }` — see [Pause Project](https://www.totalum.app/docs/api/projects#pause-project) |
| `data.preview` | object | `{ url, token8, status, deployState }`: the preview address and whether its latest build is live (`status`: `"live"`, `"deploying"`, `"failed"`, `"parked"`, `"none"`) |
| `data.lastDeployError` | object \| null | Why the last preview or publish build failed, when it did |
| `data.currentRun` | object \| null | The running prompt: `{ id, state, startedAt, batchId?, promptPreview }` |
| `data.state` | string | `"active"` or `"paused-by-user"` |
| `data.pausedAt` | string \| null | When it was paused |
| `data.resumable` | boolean | Whether a paused project can be resumed |
| `data.runawayPause` | object \| null | Set while the emergency stop has paused the site: `{ pausedAt, resumesAt, metric, value?, threshold? }` — see [Release Runaway Pause](https://www.totalum.app/docs/api/projects#release-runaway-pause) |
| `data.contextClearPending` | boolean | The next prompt starts with a fresh context |
| `data.operations` | object[] | Long operations running now (publish, restore, import…): `{ kind, id, status, startedAt, errorMessage?, versionId?, progress? }` |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings
```

:::success 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**

| Code | HTTP | Meaning |
|---|---|---|
| `PROJECT_NOT_FOUND` | 404 | No project with this id in your account |
| `API_ACCESS_DISABLED` | 403 | API access is switched off for this project in your account settings |
| `PROJECT_NOT_ALLOWED` | 403 | Your API key is restricted to other projects |

## List Projects

```endpoint
method: GET
path: /api/v1/vcaas/projects
cost: Free
```

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**

| Field | Type | Required | Description |
|---|---|---|---|
| `limit` | number | No | 1–100 (default 100) |
| `skip` | number | No | Projects to skip, for paging (default 0) |
| `search` | string | No | Case-insensitive match on id, label and description |
| `sortField` | string | No | `date` (default, creation time) or `lastModified` |
| `sortDirection` | string | No | `desc` (default) or `asc` |
| `groupId` | string | No | Only the projects of one group; `none` = only ungrouped projects |
| `createdFrom` | string | No | Created at or after this date (ISO 8601; a bare day is UTC) |
| `createdTo` | string | No | Created at or before this date (a bare day includes the whole day) |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data[].projectId` | string | Project id |
| `data[].description` | string | Description |
| `data[].plan` | string | `"api"` |
| `data[].createdAt` | string | ISO 8601 creation time |
| `data[].label` | string \| undefined | Display name, when set |
| `data[].groupId` | string \| undefined | Group, when filed in one |
| `data[].previewImageUrl` | string \| undefined | Screenshot of the home page, once a prompt has finished |
| `data[].lastModifiedAt` | string | Last change (prompt, publish, edit); never null |
| `data[].productionProjectUrl` | string | The published host, as on [Get Project](https://www.totalum.app/docs/api/projects#get-project): the custom domain once it is active, else `<projectId>.totalum-project.com` (or `<projectId>.webapp-project.com` when chosen) |
| `data[].engineVersion` | string | `"v2"` |
| `data[].liveHostSuffix` | string \| undefined | `"webapp-project.com"` when chosen ([Update Project](https://www.totalum.app/docs/api/projects#update-project)); absent otherwise |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  "https://builder-api.totalum.app/api/v1/vcaas/projects?limit=20&sortField=lastModified"
```

:::success 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

```endpoint
method: POST
path: /api/v1/vcaas/projects/:projectId/deployments/deploy
cost: 0.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**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |

**Body parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `confirmDestructive` | boolean | No | `true` = run the pending migrations that drop columns or tables or delete/overwrite live rows |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.projectId` | string | Project id |
| `data.status` | string | `"deploying"` |
| `data.deploymentId` | string | This publish |
| `data.message` | string | How to follow it |

**Example request**

```bash
curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/deployments/deploy
```

:::success 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**

| Code | HTTP | Meaning |
|---|---|---|
| `AGENT_RUNNING` | 409 | A prompt is running; publish when it is done |
| `DEPLOYMENT_RUNNING` | 409 | A publish is already running |
| `OPERATION_IN_PROGRESS` | 409 | A restore, import or other long operation holds the project, or it is still being set up |
| `INSUFFICIENT_CREDITS` | 402 | Not enough credits |
| `PROJECT_CREDIT_LIMIT_REACHED` | 403 | The project's monthly development cap is reached |

## Get Deployment Status

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId/deployments/status
cost: Free
```

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

**Path parameters**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.status` | string \| null | `"deploying"`, `"success"`, `"error"`, or `null` if never published |
| `data.createdAt` | string \| null | When the publish started |
| `data.deploymentId` | string \| undefined | The publish |
| `data.step` | string \| undefined | `"build"`, `"migration"`, `"upload"`, `"probe"`, `"fixing"` (the agent is repairing a failed step) or `"done"` |
| `data.progress` | object \| undefined | While `step` is `"fixing"`: `{ stage: "fixing", failedStage, fixAttempt, fixAttempts }` |
| `data.errorDetails` | object \| undefined | On `"error"`: `{ stage, title, message, logExcerpt, versionId, deploymentId?, migrationName?, fixSummary?, at }` — `fixSummary` says what the repair tried and what you need to do |
| `data.liveUrl` | string | The live address |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/deployments/status
```

:::success 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](https://www.totalum.app/docs/api/projects#get-project) (the custom domain once active).

## Create Project

```endpoint
method: POST
path: /api/v1/vcaas/projects
cost: 2 credits
```

Create an **empty** project: no prompt, nothing built. Use it only when you will [import](https://www.totalum.app/docs/api/transfer#import-project) a project into it, [connect GitHub](https://www.totalum.app/docs/api/github#connect-github-repository) first, or [write the files](https://www.totalum.app/docs/api/project-files#write-file-content) yourself; otherwise use [Launch Project](https://www.totalum.app/docs/api/projects#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**

| Field | Type | Required | Description |
|---|---|---|---|
| `projectId` | string | Yes | Same rules as [Launch Project](https://www.totalum.app/docs/api/projects#launch-project); a taken id gets a suffix |
| `label` | string | No | Display name, up to 80 characters |
| `description` | string | No | Up to 500 characters |
| `groupId` | string | No | An existing project group |
| `ownerEmail` | string | No | The e-mail of the app's owner (its admin once confirmed in the app). Default: none — then the first person who signs up in the app becomes its admin after entering the 8-digit code e-mailed to them (the preview and the published app each have their own first sign-up); see [Set App Owner](https://www.totalum.app/docs/api/projects#set-app-owner) |
| `engineVersion` | string | No | `"v2"` to require the current engine (new accounts always get it), as in Launch Project |
| `dataResidency` | string | No | `"global"` or `"eu"`, as in Launch Project |
| `audienceCountry` | string | No | Two-letter country code of the app's users |
| `sourceImportCode` | string | No | An `importCode` from [Export Project](https://www.totalum.app/docs/api/transfer#export-project): the new project is created to receive it (checked before anything is created) |
| `creditLimits` | object | No | The project's monthly spending caps, as in [Launch Project](https://www.totalum.app/docs/api/projects#launch-project) |
| `creditLimits.maxDevelopmentCreditsPerMonth` | number \| null | No | Cap on what the AI agent and project actions spend. Default `5000`; `null` = no cap |
| `creditLimits.maxInfrastructureCreditsPerMonth` | number \| null | No | Cap on what the running app spends. Default `300`; `null` = no cap |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.projectId` | string | The id that was created. Use this one |
| `data.requestedProjectId` | string \| undefined | The id you asked for, when it differs |
| `data.description` | string | Project description |
| `data.plan` | string | Always `"api"` |
| `data.createdAt` | string | ISO 8601 creation time |
| `data.label` | string \| undefined | The label, when given |
| `data.groupId` | string \| undefined | The group, when given |
| `data.dataResidency` | string | `"global"` or `"eu"` |
| `data.engineVersion` | string | `"v2"` |
| `data.engineState` | string | `"provisioning"` — the project is being set up |

**Example request**

```bash
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 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**

| Code | HTTP | Meaning |
|---|---|---|
| `MISSING_PROJECT_ID` / `INVALID_PROJECT_NAME` / `INVALID_PROJECT_NAME_LENGTH` / `RESERVED_PROJECT_NAME` | 400 | The id is missing or malformed |
| `INVALID_PROJECT_GROUP` | 400 | Unknown or full group |
| `INVALID_DATA_RESIDENCY` / `INVALID_AUDIENCE_COUNTRY` / `INVALID_ENGINE_VERSION` | 400 | An invalid option |
| `INVALID_CREDIT_LIMITS` / `INVALID_LIMIT` | 400 | `creditLimits` is not an object, or a cap is not a positive number or `null` |
| `INSUFFICIENT_CREDITS` | 402 | Not enough credits for the project |
| `MAX_PROJECTS_REACHED` | 403 | Your plan's project limit is in use |
| `ENGINE_V2_REQUIRES_PLATFORM_ACCOUNT` | 403 | `engineVersion: "v2"` (or a current-engine export code) from an account with no Totalum platform account: sign in once at platform.totalum.app, then retry |
| `PROJECT_ALREADY_EXISTS` | 409 | The name and every suffixed candidate were taken |
| `ENGINE_V2_DISABLED` | 409 | New projects cannot be created on the current engine right now |
| `ENGINE_LEGACY_UNAVAILABLE` | 409 | `engineVersion: "legacy"`, or a legacy (`.zip`) export code, on a new account: it creates only on the current engine |
| `RATE_LIMIT_EXCEEDED` | 429 | Too many creates for your plan (new projects and prompts share one per-minute allowance, see [Limits](https://www.totalum.app/docs/limits)); retry after `Retry-After` seconds |
| `CREATE_PROJECT_ERROR` | 503 | A temporary failure; nothing was created. Retry after `Retry-After` seconds |

Project limits per plan:

| Plan | Projects at once | New projects |
|---|---|---|
| Free | 2 | 1 a minute |
| Starter | 10 | 5 a minute |
| Builder | 50 | 10 a minute |
| Professional | 300 | 30 a minute |
| Enterprise | Unlimited | 100 a minute |

## Everything else

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

| Need | Docs |
|---|---|
| Read/write the app's data (rows, SQL, relations), migrations | [Database](https://www.totalum.app/docs/api/database) · [database.md](https://www.totalum.app/totalum-api/database.md) |
| Backups, restore, time travel, move to Turso | [Database Backups & Restore](https://www.totalum.app/docs/api/database-backups) · [database-backups.md](https://www.totalum.app/totalum-api/database-backups.md) |
| Versions, diffs, restore a version | [Versions](https://www.totalum.app/docs/api/versions) · [versions.md](https://www.totalum.app/totalum-api/versions.md) |
| Read or edit code files, rebuild | [Project Files](https://www.totalum.app/docs/api/project-files) · [project-files.md](https://www.totalum.app/totalum-api/project-files.md) |
| Download source, upload files for prompts | [Source Code & Uploads](https://www.totalum.app/docs/api/source-files) · [source-files.md](https://www.totalum.app/totalum-api/source-files.md) |
| Stop a run, conversation, multi-prompt, agent instructions, MCP servers | [AI Agent](https://www.totalum.app/docs/api/agent) · [agent.md](https://www.totalum.app/totalum-api/agent.md) |
| Update, delete, pause, resume a project, its live state | [Projects](https://www.totalum.app/docs/api/projects) · [projects.md](https://www.totalum.app/totalum-api/projects.md) |
| Custom domain on a project | [Custom Domains](https://www.totalum.app/docs/api/domains) · [domains.md](https://www.totalum.app/totalum-api/domains.md) |
| Buy a domain | [Buy a Domain](https://www.totalum.app/docs/api/domain-registration) · [domain-registration.md](https://www.totalum.app/totalum-api/domain-registration.md) |
| Mobile apps, signing, store uploads | [Mobile Apps](https://www.totalum.app/docs/api/mobile) · [mobile.md](https://www.totalum.app/totalum-api/mobile.md) |
| Payments (the app's Stripe) | [Payments](https://www.totalum.app/docs/api/payments) · [payments.md](https://www.totalum.app/totalum-api/payments.md) |
| Webhooks (prompt finished, publish ended…) | [Webhooks](https://www.totalum.app/docs/api/webhooks) · [webhooks.md](https://www.totalum.app/totalum-api/webhooks.md) |
| Scheduled jobs | [Scheduled Jobs](https://www.totalum.app/docs/api/cron-jobs) · [cron-jobs.md](https://www.totalum.app/totalum-api/cron-jobs.md) |
| Secrets, project API keys, .env | [Secrets, Keys & .env](https://www.totalum.app/docs/api/secrets) · [secrets.md](https://www.totalum.app/totalum-api/secrets.md) |
| Logs of the preview and the live site | [Logs](https://www.totalum.app/docs/api/server) · [server.md](https://www.totalum.app/totalum-api/server.md) |
| Usage, spending, site analytics | [Usage & Analytics](https://www.totalum.app/docs/api/analytics) · [analytics.md](https://www.totalum.app/totalum-api/analytics.md) |
| Spending caps, feature switches, rate limit | [Spending & Rate Limits](https://www.totalum.app/docs/api/limits) · [limits.md](https://www.totalum.app/totalum-api/limits.md) |
| Copy a project / templates | [Export & Import](https://www.totalum.app/docs/api/transfer) · [transfer.md](https://www.totalum.app/totalum-api/transfer.md) |
| GitHub sync | [GitHub](https://www.totalum.app/docs/api/github) · [github.md](https://www.totalum.app/totalum-api/github.md) |
| Build from Figma designs | [Figma](https://www.totalum.app/docs/api/figma) · [figma.md](https://www.totalum.app/totalum-api/figma.md) |
| Visual edits without a prompt | [Visual Edits](https://www.totalum.app/docs/api/visual-edit) · [visual-edit.md](https://www.totalum.app/totalum-api/visual-edit.md) |
| Folders for projects | [Project Groups](https://www.totalum.app/docs/api/project-groups) · [project-groups.md](https://www.totalum.app/totalum-api/project-groups.md) |
| Balance, prices, EU-only data | [Account](https://www.totalum.app/docs/api/account) · [account.md](https://www.totalum.app/totalum-api/account.md) |
| An app that itself creates apps | [Building on Totalum's API](https://www.totalum.app/docs/api/build-on-totalum) · [build-on-totalum.md](https://www.totalum.app/totalum-api/build-on-totalum.md) |
| Error codes and limits | [Errors & Limits](https://www.totalum.app/docs/api/errors) · [errors.md](https://www.totalum.app/totalum-api/errors.md) |
| Every endpoint in one file | [full.md](https://www.totalum.app/totalum-api/full.md) · [OpenAPI](https://www.totalum.app/openapi.json) |
| Inside the generated app: auth, e-mail, AI, files, payments, real-time, cron, database driver | [Totalum SDK](https://www.totalum.app/docs/sdk) · [totalum-sdk.md](https://www.totalum.app/totalum-sdk.md) |
| Use Totalum from Claude Code, Codex, Cursor | [Totalum MCP](https://www.totalum.app/docs/mcp) |
| Stack, running locally, self-hosting | [For Developers](https://www.totalum.app/docs/developers) |
| Legacy projects | [Legacy projects: what differs](https://www.totalum.app/docs/api/migration-v2) |
