Docs
BlogHomeStart building

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:

  1. GET /files/tree — find the file you want.
  2. GET /files/content?path=… — read it.
  3. PUT /files/content — write it back.
  4. POST /rebuild → poll GET /rebuild/status — make it live.
A write does not go live on its own

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.

Editing source code needs a paid plan

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

GET/api/v1/vcaas/projects/:projectId/files/treeFree

The project's files and folders, as a flat, sorted list. node_modules, .next, .git, dist and other build output are never included.

Path parameters

ParameterTypeDescription
projectIdstringThe project ID

Query parameters

FieldTypeRequiredDescription
pathstringNoLimit the tree to one subfolder, e.g. src/app. Default: the whole project
limitnumberNoPage size. Default 2000, max 5000
offsetnumberNoEntries to skip. Default 0

Response fields

FieldTypeDescription
data.entries[].pathstringPath relative to the project root, e.g. src/app/page.tsx
data.entries[].namestringFile or folder name
data.entries[].typestring"file" or "folder"
data.entries[].sizenumber | undefinedSize in bytes. Files only
data.entries[].depthnumber0 at the project root
data.totalEntriesnumberEntries matching the request before paging
data.offsetnumberThe offset used
data.limitnumberThe limit used
data.hasMorebooleantrue when more entries follow
data.commitShastring | nullGit commit the tree was read from
data.filesCountnumberFiles 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

bash
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"
Success · 200 OK
json
{
  "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

CodeHTTPMeaning
MISSING_PROJECT_ID400projectId is required
INVALID_PATH400The path is absolute, contains .., or points inside an excluded folder
PROJECT_NOT_FOUND404Project does not exist or you don't own it
PROJECT_TOO_LARGE413The project archive is too large to browse through the API — download it instead

#Get File Content

GET/api/v1/vcaas/projects/:projectId/files/contentFree

The complete content of one file. Files up to 1 MB.

Path parameters

ParameterTypeDescription
projectIdstringThe project ID

Query parameters

FieldTypeRequiredDescription
pathstringYesPath relative to the project root, e.g. src/app/page.tsx

Response fields

FieldTypeDescription
data.pathstringThe normalised path
data.namestringFile name
data.sizenumberSize of the stored file in bytes
data.encodingstring"utf8" for text, "base64" for binary
data.contentstringThe file content, in encoding
data.commitShastring | nullGit 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

bash
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"
Success · 200 OK
json
{
  "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"
  }
}
Error · 404
json
{
  "errors": {
    "errorCode": "FILE_NOT_FOUND",
    "errorMessage": "No file at \"src/app/missing.tsx\" in this project."
  },
  "data": null
}

Error codes

CodeHTTPMeaning
INVALID_PATH400The path is absolute, contains .., or points inside an excluded folder
PROJECT_NOT_FOUND404Project does not exist or you don't own it
FILE_NOT_FOUND404No file at that path
FILE_TOO_LARGE413The file is larger than 1 MB — download the source archive instead

#Write File Content

PUT/api/v1/vcaas/projects/:projectId/files/content1 credit

Replace 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

ParameterTypeDescription
projectIdstringThe project ID

Body parameters

FieldTypeRequiredDescription
pathstringYesPath relative to the project root
contentstringYesThe complete new content. Send "" to empty the file
encodingstringNo"utf8" (default) or "base64" for binary

Response fields

FieldTypeDescription
data.pathstringThe normalised path
data.bytesWrittennumberBytes written
data.createdbooleantrue when the file did not exist before
data.commitShastring | undefinedThe commit this write produced
data.filesCountnumber | undefinedFiles tracked after the write
data.rebuildRequiredbooleanAlways true — call POST /rebuild to make it live

Example request

bash
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
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "path": "src/app/page.tsx",
    "bytesWritten": 58,
    "created": false,
    "commitSha": "d4e5f6a",
    "filesCount": 47,
    "rebuildRequired": true
  }
}
Error · 400
json
{
  "errors": {
    "errorCode": "INVALID_PATH",
    "errorMessage": "path must not contain \"..\""
  },
  "data": null
}

Error codes

CodeHTTPMeaning
INVALID_PATH400The path is absolute, contains .., or points inside an excluded folder
MISSING_CONTENT400content is required and must be a string
INVALID_ENCODING400encoding must be "utf8" or "base64"
PROJECT_NOT_FOUND404Project does not exist or you don't own it
FILE_TOO_LARGE413Content is larger than 512 KB
INSUFFICIENT_CREDITS402Not enough credits
FREE_PLAN_NO_SOURCE_EDITING400Editing source code requires a paid plan
AGENT_RUNNING409The AI agent is running — wait for it to finish
DEPLOYMENT_RUNNING409A deployment is in progress — wait for it to finish
NO_SANDBOX400The project has no sandbox yet — run the agent once first
SANDBOX_NOT_ACTIVE400The sandbox is starting or archived — wait and retry
End of Write File ContentNext endpointPOSTRebuild Project

#Rebuild Project

POST/api/v1/vcaas/projects/:projectId/rebuild1 credit

Rebuild 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

ParameterTypeDescription
projectIdstringThe project ID

Response fields

FieldTypeDescription
data.projectIdstringThe project ID
data.statusstringAlways "rebuilding"
data.startedAtstringISO 8601 start time
data.messagestringHuman-readable next step

Example request

bash
curl -X POST -H "api-key: tlm_sk_your_key" \
  https://api-accounts.totalum.app/api/v1/vcaas/projects/my-app/rebuild
Success · 200 OK
json
{
  "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

CodeHTTPMeaning
PROJECT_NOT_FOUND404Project does not exist or you don't own it
REBUILD_RUNNING409A rebuild is already in progress for this project
AGENT_RUNNING409The AI agent is running — wait for it to finish
DEPLOYMENT_RUNNING409A deployment is in progress — wait for it to finish
INSUFFICIENT_CREDITS402Not enough credits
REBUILD_LIMIT_REACHED400Your plan's rebuild limit was reached — wait or upgrade
NO_SANDBOX400The project has no sandbox yet — run the agent once first
SANDBOX_NOT_ACTIVE400The sandbox is starting or archived — wait and retry

#Get Rebuild Status

GET/api/v1/vcaas/projects/:projectId/rebuild/statusFree

Poll this after POST /rebuild. Every 10–15 seconds is plenty.

Path parameters

ParameterTypeDescription
projectIdstringThe project ID

Response fields

FieldTypeDescription
data.projectIdstringThe project ID
data.statusstring"idle", "rebuilding", "success" or "error"
data.startedAtstring | nullISO 8601 start time
data.finishedAtstring | nullISO 8601 finish time
data.elapsedSecondsnumber | nullHow long it has been running, or took
data.livePreviewUrlstring | undefinedPreview URL after a successful rebuild
data.errorCodestring | undefinedPresent when status is "error"
data.errorMessagestring | undefinedPresent when status is "error"

"idle" means this project has never been rebuilt through the API.

Example request

bash
curl -H "api-key: tlm_sk_your_key" \
  https://api-accounts.totalum.app/api/v1/vcaas/projects/my-app/rebuild/status
Success · 200 OK
json
{
  "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"
  }
}
Error · 200 OK with a failed rebuild
json
{
  "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

CodeHTTPMeaning
MISSING_PROJECT_ID400projectId is required
PROJECT_NOT_FOUND404Project 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.

End of Get Rebuild Status Back to top