Docs
BlogHomeStart building

Projects

Create, inspect and manage your apps.


A project is one app: its source code, its preview, its published site, its own databases and files. Start with Launch Project — it creates the project and starts building it in one call. Every endpoint needs the api-key header and answers the { "errors", "data" } envelope.

#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, 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
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
ENGINE_V2_REQUIRES_PLATFORM_ACCOUNT403engineVersion: "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_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

#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
ENGINE_V2_REQUIRES_PLATFORM_ACCOUNT403engineVersion: "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_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

#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"
    }
  ]
}
End of List ProjectsNext endpointGETGet Project

#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

#Get Project State

GET/api/v1/vcaas/projects/:projectId/stateapi-keyFree

The project's live state in one read: what is running, the preview, the lifecycle and any long operation with its progress (for example the copy of a database move to Turso).

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.projectIdstringProject id
data.engineVersionstring"v2"
data.statestring"queued", "provisioning", "ready", "error", "suspended" or "deleting"
data.lifecycleobject{ state, nextStep: { kind, at } | null, lastActivityAt, pausedAt, resumable }
data.sandboxobject{ state, lastPromptAt }
data.previewobject{ temporalLinkUrl, token8, versionId?, status, deploy: { state, fixAttempt?, error? } }
data.lastDeployErrorobject | nullThe last failed preview or publish build
data.runobject | nullThe running prompt: { id, state, startedAt, batchId?, promptPreview }
data.batchobject | nullThe newest multi-prompt batch
data.contextClearPendingbooleanThe next prompt starts fresh
data.operationsobject[]{ kind, id, status, startedAt, errorMessage?, versionId?, progress? }
data.liveHostSuffixstringDomain of the published host
data.liveUrlstringPublished URL
data.dataResidencystring"global" or "eu"
data.updatedAtstringWhen this state last changed

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/state
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "projectId": "barber-bookings",
    "engineVersion": "v2",
    "state": "ready",
    "lifecycle": { "state": "active", "nextStep": null, "lastActivityAt": "2026-10-08T11:02:00.000Z", "pausedAt": null, "resumable": false },
    "sandbox": { "state": "active", "lastPromptAt": "2026-10-08T10:30:05.000Z" },
    "preview": { "temporalLinkUrl": "https://barber-bookings-dev-temporal-link-preview-k3y9x2ab.totalum-project.com", "token8": "k3y9x2ab", "status": "live", "deploy": { "state": "idle" } },
    "lastDeployError": null,
    "run": null,
    "batch": null,
    "contextClearPending": false,
    "operations": [],
    "liveHostSuffix": "totalum-project.com",
    "liveUrl": "https://barber-bookings.totalum-project.com",
    "dataResidency": "global",
    "updatedAt": "2026-10-08T11:02:00.000Z"
  }
}
End of Get Project StateNext endpointPATCHUpdate Project

#Update Project

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

Change the label, the description, the group or the live address's domain. The project id never changes. A field you leave out is untouched; null clears it.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
labelstring | nullNoDisplay name, up to 80 characters; null clears it
descriptionstring | nullNoUp to 500 characters; null clears it
groupIdstring | nullNoAn existing group; null takes the project out of its group
liveHostSuffixstringNo"totalum-project.com" (default) or "webapp-project.com" — the main domain of the published site and its preview, e.g. <projectId>.webapp-project.com. webapp-project.com needs the Professional or Enterprise plan. On those plans both domains serve the site and its preview; this setting only picks the main one (productionProjectUrl, canonical and sitemap links). Links inside your app follow at your next publish. If the account moves to a lower plan, the project goes back to totalum-project.com and its webapp-project.com address redirects for 90 days.

Response fields

FieldTypeDescription
data.projectIdstringProject id
data.labelstring | undefinedThe label after the change
data.descriptionstringThe description after the change
data.groupIdstring | undefinedThe group after the change
data.liveHostSuffixstring | undefinedThe domain chosen, when liveHostSuffix was sent

Example request

curl -X PATCH \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"label":"Barber shop","groupId":null}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings
Success · 200 OK
json
{ "errors": null, "data": { "projectId": "barber-bookings", "label": "Barber shop", "description": "" } }

Error codes

CodeHTTPMeaning
NOTHING_TO_UPDATE400None of label, description, groupId, liveHostSuffix was sent
INVALID_LIVE_HOST_SUFFIX400liveHostSuffix is not totalum-project.com or webapp-project.com
PLAN_REQUIRED403webapp-project.com needs the Professional or Enterprise plan (errorDetails.upgradePlan)
AGENT_RUNNING / OPERATION_IN_PROGRESS409A prompt, an operation or a preview deploy is in progress; try again when it ends
INVALID_PROJECT_GROUP400The group does not exist or is not yours
PROJECT_NOT_FOUND404No such project in your account

#Delete Project

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

Permanently delete a project: its published site, preview, custom domain, databases, files and versions. It cannot be undone, and its id can never be used again. Deleting is free and always allowed, whatever your balance.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.successbooleantrue

Example request

curl -X DELETE -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": { "success": true } }

Error codes

CodeHTTPMeaning
PROJECT_NOT_FOUND404No such project in your account
PLAN_NOT_API400The project was not created through the API or the Totalum app's API plan and cannot be deleted here

#Pause Project

POST/api/v1/vcaas/projects/:projectId/pauseapi-key2 credits

Take the whole project offline so it consumes nothing: the published site and the preview show a paused page and the databases are saved first. Answers 202 while the pause runs; follow it on Get Project State (lifecycle.state becomes paused-by-user).

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.acceptedbooleantrue
data.statestringThe lifecycle state now
data.operationobject | undefinedThe operation started: { kind, id, startedAt, estimateSec }. Absent (and 200) when the project was already paused
data.creditsChargednumberCredits charged (0 when nothing started)

Example request

curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/pause
Success · 202 Accepted
json
{ "errors": null, "data": { "accepted": true, "state": "active", "operation": { "kind": "pause", "id": "op_01k6xa", "startedAt": "2026-10-08T12:00:00.000Z", "estimateSec": 60 }, "creditsCharged": 2 } }

Error codes

CodeHTTPMeaning
INSUFFICIENT_CREDITS402Not enough credits
AGENT_RUNNING409A prompt is running; wait for it
DEPLOYMENT_RUNNING409A publish is running
OPERATION_IN_PROGRESS409Another long operation is running
LIFECYCLE_NOT_APPLICABLE409The project is asleep after a long inactivity: prompt it (or restart its server) to wake it first

#Resume Project

POST/api/v1/vcaas/projects/:projectId/resumeapi-key2 credits

Bring a paused project back: databases restored, preview and published site online again. Answers 202 while it runs.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.acceptedbooleantrue
data.statestringThe lifecycle state now
data.operationobject{ kind, id, startedAt, estimateSec }
data.creditsChargednumberCredits charged

Example request

curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/resume
Success · 202 Accepted
json
{ "errors": null, "data": { "accepted": true, "state": "paused-by-user", "operation": { "kind": "resume", "id": "op_01k6xb", "startedAt": "2026-10-09T08:00:00.000Z", "estimateSec": 90 }, "creditsCharged": 2 } }

Error codes

CodeHTTPMeaning
INSUFFICIENT_CREDITS402Not enough credits
PROJECT_NOT_PAUSED409The project is not paused
OPERATION_IN_PROGRESS409Another long operation is running
PAUSE_DUMP_EXPIRED410The saved databases of this pause are gone; the project cannot be resumed

#Release Runaway Pause

POST/api/v1/vcaas/projects/:projectId/runaway/releaseapi-keyFree

Bring a site back online after the emergency stop paused it. The emergency stop pauses a project when its app uses far more than normal (for example a loop that sends thousands of requests or spends credits on built-in features every minute): the site shows a paused page, the app's calls to Totalum answer 429 PROJECT_PAUSED_RUNAWAY, and you get an e-mail and the project.kill_switch.fired webhook. The pause lifts by itself after 24 hours (runawayPause.resumesAt on Get Project); this call lifts it now. Fix the cause first, or the same usage pauses it again. Free at any balance; the site answers again within about a minute. A project that is not paused by the emergency stop answers 200 with released: false and nothing changes. This is not Resume Project, which brings back a project you paused yourself.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.releasedbooleantrue when the pause was lifted, false when the project was not paused by the emergency stop
data.messagestringWhat happened, in plain words

runawayPause.metric (on Get Project) says which limit was crossed: credits_hour (credits spent on built-in features in one hour), usd_hour (unusually heavy use of built-in features in one hour; no value or threshold), requests_5min, rows_written_5min or rows_read_5min (in 5 minutes). value is what was measured and threshold the limit.

Example request

curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/runaway/release
Success · 200 OK
json
{ "errors": null, "data": { "released": true, "message": "The site is back online; it can take up to a minute to show everywhere." } }
End of Release Runaway PauseNext endpointPOSTRotate Preview URL

#Rotate Preview URL

POST/api/v1/vcaas/projects/:projectId/preview/rotate-urlapi-keyFree

Give the development preview a new address; the old one stops working at once. The preview address also changes by itself every month, so never store it: read temporalDevelopmentProjectUrl from Get Project. The published site and custom domains never change. Owner (or a member allowed to manage API keys) only; at most once a minute.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.temporalLinkUrlstringThe new preview address
data.rotatedAtstringISO 8601 time of the change

Example request

curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/preview/rotate-url
Success · 200 OK
json
{ "errors": null, "data": { "temporalLinkUrl": "https://barber-bookings-dev-temporal-link-preview-p7m2q9rt.totalum-project.com", "rotatedAt": "2026-10-08T12:00:00.000Z" } }

Error codes

CodeHTTPMeaning
OWNER_ONLY403Only the owner or a member allowed to manage API keys
AGENT_RUNNING409A prompt is running
OPERATION_IN_PROGRESS409A publish or other operation is running
RATE_LIMIT_EXCEEDED429Rotated less than a minute ago
End of Rotate Preview URL Back to top