Project Files
Read and edit your project's code file by file, then rebuild.
These endpoints let you work with your project's code directly instead of downloading the whole archive: list the tree, read one file, write one file, and rebuild so the running app picks the change up. All of them require the api-key header.
The typical loop is:
GET /files/tree— find the file you want.GET /files/content?path=…— read it.PUT /files/content— write it back.POST /rebuild→ pollGET /rebuild/status— make it live.
Writing a file puts it on the sandbox and commits a new version, but the server keeps serving the previous build until you rebuild. That is why PUT /files/content answers with rebuildRequired: true. Batch your edits, then rebuild once.
PUT /files/content is refused on the Free plan with FREE_PLAN_NO_SOURCE_EDITING. Reading the tree and reading files work on every plan.
#Get Project Tree
/api/v1/vcaas/projects/:projectId/files/treeFreeThe project's files and folders, as a flat, sorted list. node_modules, .next, .git, dist and other build output are never included.
Path parameters
| Parameter | Type | Description |
|---|---|---|
projectId | string | The project ID |
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
path | string | No | Limit the tree to one subfolder, e.g. src/app. Default: the whole project |
limit | number | No | Page size. Default 2000, max 5000 |
offset | number | No | Entries to skip. Default 0 |
Response fields
| Field | Type | Description |
|---|---|---|
data.entries[].path | string | Path relative to the project root, e.g. src/app/page.tsx |
data.entries[].name | string | File or folder name |
data.entries[].type | string | "file" or "folder" |
data.entries[].size | number | undefined | Size in bytes. Files only |
data.entries[].depth | number | 0 at the project root |
data.totalEntries | number | Entries matching the request before paging |
data.offset | number | The offset used |
data.limit | number | The limit used |
data.hasMore | boolean | true when more entries follow |
data.commitSha | string | null | Git commit the tree was read from |
data.filesCount | number | Files tracked in the project, before exclusions |
Entries are sorted the way a file explorer shows them: folders before files, alphabetically, within each parent.
Example request
curl -H "api-key: tlm_sk_your_key" \
"https://api-accounts.totalum.app/api/v1/vcaas/projects/my-app/files/tree?path=src/app&limit=100"{
"errors": null,
"data": {
"entries": [
{ "path": "src/app", "name": "app", "type": "folder", "depth": 1 },
{ "path": "src/app/layout.tsx", "name": "layout.tsx", "type": "file", "size": 1284, "depth": 2 },
{ "path": "src/app/page.tsx", "name": "page.tsx", "type": "file", "size": 3902, "depth": 2 }
],
"totalEntries": 3,
"offset": 0,
"limit": 100,
"hasMore": false,
"commitSha": "a1b2c3d",
"filesCount": 47
}
}Error codes
| Code | HTTP | Meaning |
|---|---|---|
MISSING_PROJECT_ID | 400 | projectId is required |
INVALID_PATH | 400 | The path is absolute, contains .., or points inside an excluded folder |
PROJECT_NOT_FOUND | 404 | Project does not exist or you don't own it |
PROJECT_TOO_LARGE | 413 | The project archive is too large to browse through the API — download it instead |
#Get File Content
/api/v1/vcaas/projects/:projectId/files/contentFreeThe complete content of one file. Files up to 1 MB.
Path parameters
| Parameter | Type | Description |
|---|---|---|
projectId | string | The project ID |
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Path relative to the project root, e.g. src/app/page.tsx |
Response fields
| Field | Type | Description |
|---|---|---|
data.path | string | The normalised path |
data.name | string | File name |
data.size | number | Size of the stored file in bytes |
data.encoding | string | "utf8" for text, "base64" for binary |
data.content | string | The file content, in encoding |
data.commitSha | string | null | Git commit the file was read from |
Binary files (anything with a NUL byte in the first 8 KB) come back base64-encoded, so they survive a round trip through PUT /files/content.
Example request
curl -H "api-key: tlm_sk_your_key" \
"https://api-accounts.totalum.app/api/v1/vcaas/projects/my-app/files/content?path=src/app/page.tsx"{
"errors": null,
"data": {
"path": "src/app/page.tsx",
"name": "page.tsx",
"size": 62,
"encoding": "utf8",
"content": "export default function Page() {\n return <h1>Hello</h1>;\n}\n",
"commitSha": "a1b2c3d"
}
}{
"errors": {
"errorCode": "FILE_NOT_FOUND",
"errorMessage": "No file at \"src/app/missing.tsx\" in this project."
},
"data": null
}Error codes
| Code | HTTP | Meaning |
|---|---|---|
INVALID_PATH | 400 | The path is absolute, contains .., or points inside an excluded folder |
PROJECT_NOT_FOUND | 404 | Project does not exist or you don't own it |
FILE_NOT_FOUND | 404 | No file at that path |
FILE_TOO_LARGE | 413 | The file is larger than 1 MB — download the source archive instead |
#Write File Content
/api/v1/vcaas/projects/:projectId/files/content1 creditReplace a file completely, or create it if it does not exist. Missing parent folders are created for you. Content up to 512 KB.
Every write is committed and snapshotted as a new version, and pushed to GitHub if the project is connected — so you can always roll back from Versions.
Path parameters
| Parameter | Type | Description |
|---|---|---|
projectId | string | The project ID |
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Path relative to the project root |
content | string | Yes | The complete new content. Send "" to empty the file |
encoding | string | No | "utf8" (default) or "base64" for binary |
Response fields
| Field | Type | Description |
|---|---|---|
data.path | string | The normalised path |
data.bytesWritten | number | Bytes written |
data.created | boolean | true when the file did not exist before |
data.commitSha | string | undefined | The commit this write produced |
data.filesCount | number | undefined | Files tracked after the write |
data.rebuildRequired | boolean | Always true — call POST /rebuild to make it live |
Example request
curl -X PUT \
-H "api-key: tlm_sk_your_key" \
-H "Content-Type: application/json" \
-d '{"path":"src/app/page.tsx","content":"export default function Page() {\n return <h1>Hi</h1>;\n}\n"}' \
https://api-accounts.totalum.app/api/v1/vcaas/projects/my-app/files/content{
"errors": null,
"data": {
"path": "src/app/page.tsx",
"bytesWritten": 58,
"created": false,
"commitSha": "d4e5f6a",
"filesCount": 47,
"rebuildRequired": true
}
}{
"errors": {
"errorCode": "INVALID_PATH",
"errorMessage": "path must not contain \"..\""
},
"data": null
}Error codes
| Code | HTTP | Meaning |
|---|---|---|
INVALID_PATH | 400 | The path is absolute, contains .., or points inside an excluded folder |
MISSING_CONTENT | 400 | content is required and must be a string |
INVALID_ENCODING | 400 | encoding must be "utf8" or "base64" |
PROJECT_NOT_FOUND | 404 | Project does not exist or you don't own it |
FILE_TOO_LARGE | 413 | Content is larger than 512 KB |
INSUFFICIENT_CREDITS | 402 | Not enough credits |
FREE_PLAN_NO_SOURCE_EDITING | 400 | Editing source code requires a paid plan |
AGENT_RUNNING | 409 | The AI agent is running — wait for it to finish |
DEPLOYMENT_RUNNING | 409 | A deployment is in progress — wait for it to finish |
NO_SANDBOX | 400 | The project has no sandbox yet — run the agent once first |
SANDBOX_NOT_ACTIVE | 400 | The sandbox is starting or archived — wait and retry |
#Rebuild Project
/api/v1/vcaas/projects/:projectId/rebuild1 creditRebuild the project and reload the server, so files you wrote become live. Asynchronous: it returns immediately and the rebuild continues in the background.
A rebuild is a cold build — the build cache is cleared first — so it usually takes 1 to 4 minutes. Poll GET /rebuild/status until status is "success" or "error".
Path parameters
| Parameter | Type | Description |
|---|---|---|
projectId | string | The project ID |
Response fields
| Field | Type | Description |
|---|---|---|
data.projectId | string | The project ID |
data.status | string | Always "rebuilding" |
data.startedAt | string | ISO 8601 start time |
data.message | string | Human-readable next step |
Example request
curl -X POST -H "api-key: tlm_sk_your_key" \
https://api-accounts.totalum.app/api/v1/vcaas/projects/my-app/rebuild{
"errors": null,
"data": {
"projectId": "my-app",
"status": "rebuilding",
"startedAt": "2026-08-02T09:14:03.120Z",
"message": "Rebuild started. It usually takes 1-4 minutes. Poll GET /projects/:projectId/rebuild/status until status is \"success\" or \"error\"."
}
}Error codes
| Code | HTTP | Meaning |
|---|---|---|
PROJECT_NOT_FOUND | 404 | Project does not exist or you don't own it |
REBUILD_RUNNING | 409 | A rebuild is already in progress for this project |
AGENT_RUNNING | 409 | The AI agent is running — wait for it to finish |
DEPLOYMENT_RUNNING | 409 | A deployment is in progress — wait for it to finish |
INSUFFICIENT_CREDITS | 402 | Not enough credits |
REBUILD_LIMIT_REACHED | 400 | Your plan's rebuild limit was reached — wait or upgrade |
NO_SANDBOX | 400 | The project has no sandbox yet — run the agent once first |
SANDBOX_NOT_ACTIVE | 400 | The sandbox is starting or archived — wait and retry |
#Get Rebuild Status
/api/v1/vcaas/projects/:projectId/rebuild/statusFreePoll this after POST /rebuild. Every 10–15 seconds is plenty.
Path parameters
| Parameter | Type | Description |
|---|---|---|
projectId | string | The project ID |
Response fields
| Field | Type | Description |
|---|---|---|
data.projectId | string | The project ID |
data.status | string | "idle", "rebuilding", "success" or "error" |
data.startedAt | string | null | ISO 8601 start time |
data.finishedAt | string | null | ISO 8601 finish time |
data.elapsedSeconds | number | null | How long it has been running, or took |
data.livePreviewUrl | string | undefined | Preview URL after a successful rebuild |
data.errorCode | string | undefined | Present when status is "error" |
data.errorMessage | string | undefined | Present when status is "error" |
"idle" means this project has never been rebuilt through the API.
Example request
curl -H "api-key: tlm_sk_your_key" \
https://api-accounts.totalum.app/api/v1/vcaas/projects/my-app/rebuild/status{
"errors": null,
"data": {
"projectId": "my-app",
"status": "success",
"startedAt": "2026-08-02T09:14:03.120Z",
"finishedAt": "2026-08-02T09:16:41.902Z",
"elapsedSeconds": 159,
"livePreviewUrl": "https://my-app.totalum-project.com"
}
}{
"errors": null,
"data": {
"projectId": "my-app",
"status": "error",
"startedAt": "2026-08-02T09:14:03.120Z",
"finishedAt": "2026-08-02T09:15:22.418Z",
"elapsedSeconds": 79,
"errorCode": "REBUILD_FAILED",
"errorMessage": "Failed to rebuild and reload: Type error in src/app/page.tsx"
}
}Error codes
| Code | HTTP | Meaning |
|---|---|---|
MISSING_PROJECT_ID | 400 | projectId is required |
PROJECT_NOT_FOUND | 404 | Project does not exist or you don't own it |
A failed rebuild is reported as status: "error" in a 200 response, not as an HTTP error — the request to read the status succeeded, it is the rebuild that did not.
