Quickstart: Build and Deploy Your First App
One script, start to finish. Get an API key, create a project, send a prompt, watch it build, and deploy it to a public URL.
These docs are for using the AI app builder via the API. If you'd rather build visually, click and prompt in your browser, use the app builder directly — no code required:
This is the shortest complete path through the Totalum App Builder API: one script that takes a natural-language prompt and returns a live, deployed application — with database, auth and hosting already wired up. Copy it, run it, and you have a production URL.
Using an AI agent? If you work with Claude Code, Codex, Cursor or any other coding agent, give it the docs and it can drive the whole API for you — copy them to your clipboard or hand it the file:
#The flow
| # | Step | Endpoint | Time |
|---|---|---|---|
| 1 | Create a project | POST /projects | seconds |
| 2 | Run the AI agent | POST /projects/:id/agent/start | returns instantly |
| 3 | Poll until it's done | GET /projects/:id/agent/status | 10 to 30 min |
| 4 | Read the preview URL | GET /projects/:id | seconds |
| 5 | Deploy to production | POST /projects/:id/deployments/deploy | returns instantly |
| 6 | Poll the deploy | GET /projects/:id/deployments/status | 2 to 5 min |
| 7 | Read the production URL | GET /projects/:id | seconds |
Steps 2 and 5 are asynchronous: they return immediately and you poll for completion. Everything else is a plain, instant request.
#The complete script
Save as quickstart.ts, then run it:
npm install -g tsx # or: npx tsx quickstart.ts
export TOTALUM_API_KEY=tlm_sk_your_key
npx tsx quickstart.tsThis is the whole program — nothing is elided, and there are no other files:
/**
* Totalum App Builder API — end-to-end quickstart.
* Creates a project, builds it with the AI agent, and deploys it to production.
*
* Run: TOTALUM_API_KEY=tlm_sk_your_key npx tsx quickstart.ts
*/
const API_KEY = process.env.TOTALUM_API_KEY;
// 4-35 chars, lowercase letters/numbers/hyphens, must start with a letter.
const PROJECT_ID = "my-first-app";
const PROMPT =
"Build a task tracker: a landing page explaining the product, and a dashboard " +
"where a logged-in user can create, complete and delete tasks.";
if (!API_KEY) {
console.error("Missing TOTALUM_API_KEY. Get one at https://accounts.totalum.app/vcaas");
process.exit(1);
}
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
/** Every call goes through here. Unwraps `{ errors, data }`; throws on failure. */
async function api(method: string, path: string, body?: unknown) {
const res = await fetch(`https://api-accounts.totalum.app/api/v1/vcaas${path}`, {
method,
// The key is a server-side secret. Never ship it to a browser.
headers: { "api-key": API_KEY!, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
});
const r = await res.json();
// Check the status too, not just `errors`: a rejected API key answers 401 with
// a bare { error } instead of the envelope, which would otherwise slip through
// as a success with `data: undefined`.
if (!res.ok || r.errors) {
throw Object.assign(new Error(r.errors?.errorMessage ?? r.error ?? res.statusText), {
code: r.errors?.errorCode ?? res.status,
});
}
return r.data;
}
async function main() {
// 1. Create the project. Re-running the script reuses the existing one.
try {
await api("POST", "/projects", { projectId: PROJECT_ID, description: "My first app" });
console.log(`Created project "${PROJECT_ID}"`);
} catch (e: any) {
if (e.code !== "PROJECT_ALREADY_EXISTS") throw e;
console.log(`Project "${PROJECT_ID}" already exists — reusing it`);
}
// 2. Start the agent. Returns instantly — the build runs in the background.
await api("POST", `/projects/${PROJECT_ID}/agent/start`, { prompt: PROMPT });
console.log("Agent started. This takes 10 to 30 minutes.\n");
// 3. Poll until it finishes, printing each new message. Status reads are free.
for (let printed = 0; ; ) {
await sleep(12_000);
const s = await api("GET", `/projects/${PROJECT_ID}/agent/status`);
for (const m of s.realtimeConversation.slice(printed)) {
console.log(` [${m.messageType}] ${m.message}`);
// The agent tells you here if it needs a secret from you (a Stripe key, etc).
for (const [key, info] of Object.entries(m.secretKeysNeeded ?? {}) as any) {
if (!info.isProvided) console.log(` ! Needs secret: ${key} — ${info.description}`);
}
}
printed = s.realtimeConversation.length;
if (s.status === "done") {
console.log(`\nAgent finished. Credits spent: ${s.creditsSpent ?? "n/a"}`);
break;
}
}
// 4. Read the preview URL. Re-read it after every run — it can change, and the
// field that holds it is named by developmentUrlFieldToUse.
const p = await api("GET", `/projects/${PROJECT_ID}`);
const preview = p[p.developmentUrlFieldToUse ?? "temporalDevelopmentProjectUrl"];
console.log(`Preview: ${preview ?? "(no dev server running yet)"}`);
// 5. Deploy. If the dev server was archived it auto-starts, so retry while it wakes.
for (;;) {
try {
await api("POST", `/projects/${PROJECT_ID}/deployments/deploy`);
break;
} catch (e: any) {
if (e.code !== "SERVER_NOT_READY") throw e;
console.log("Server waking up, retrying in 20s...");
await sleep(20_000);
}
}
console.log("Deploying. This takes 2 to 5 minutes.");
// 6. Poll the deploy until it goes live.
for (;;) {
await sleep(12_000);
const d = await api("GET", `/projects/${PROJECT_ID}/deployments/status`);
if (d.status === "success") break;
if (d.status === "error") throw new Error("Deployment failed — check the build logs");
}
// 7. The production URL lives on the project, not on the deployment.
const done = await api("GET", `/projects/${PROJECT_ID}`);
console.log(`\nLive: ${done.productionProjectUrl}`);
console.log(`Credits: ${done.totalCreditsSpent} spent on this project`);
}
main().catch((e) => {
console.error(e.code ? `API error [${e.code}]: ${e.message}` : e);
process.exit(1);
});#What you should see
Created project "my-first-app"
Agent started. This takes 10 to 30 minutes.
[starting] Analyzing your request...
[building] Creating the tasks table...
[building] Building the dashboard page...
[finished] Done! Your task tracker is ready.
Agent finished. Credits spent: 23
Preview: https://dev-my-first-app.totalum-project.com
Deploying. This takes 2 to 5 minutes.
Live: https://my-first-app.totalum-project.com
Credits: 31 spent on this projectThat URL is a real, deployed Next.js app with a database and auth behind it. Open it, then keep prompting: every follow-up change is another call to runAgent().
#The five things that trip people up
tlm_sk_ keys carry full account access. The correct topology is User → your frontend → your backend → Totalum API. Never put the key in browser code, a mobile app, or a public repo. If a key leaks, delete it from the API section of your account.
- Everything slow is asynchronous.
agent/startanddeployments/deployreturn in milliseconds; the work happens in the background. Poll every 10 to 15 seconds — status reads are free. - Re-read the preview URL after every run. It can change between runs. Always resolve it through
developmentUrlFieldToUse, and fall back totemporalDevelopmentProjectUrlwhen that field isnull. - The production URL lives on the project, not on the deployment:
GET /projects/:id→productionProjectUrl. - One heavy operation at a time per project. Starting a deploy while the agent runs returns
409 AGENT_RUNNING. Let each finish. - Credits gate the call, not the plan. A zero balance returns
403 VCAAS_INSUFFICIENT_CREDITS, and an agent run needs at least 50 credits to start.
#Try it in Postman
Import the OpenAPI 3.1 spec and every endpoint appears as a ready-to-send request:
- In Postman, click Import → Link.
- Paste
https://www.totalum.app/openapi.jsonand confirm. - Open the new Totalum App Builder API collection, set the
apiKeyvariable to yourtlm_sk_key, and send any request.
#Where to go next
- AI Agent — input files, multi-prompt batches, stopping a run.
- Database — query and mutate your project's data over the API.
- Custom Domains — swap
*.totalum-project.comfor your own domain. - Webhooks — get called when a run finishes, instead of polling.
- Totalum MCP — hand all of this to Claude Code, Cursor or any MCP agent.
- Create Your Own AI App Builder — an open-source Next.js app that wraps these calls, ready to clone.
- Self-host on your own server — take the source code and run it yourself.
