# Totalum App Builder API — GitHub

This file is the complete reference for this area. The short guide for agents is https://www.totalum.app/totalum-api.md.

- Base URL: `https://builder-api.totalum.app/api/v1/vcaas`
- Authentication: header `api-key: tlm_sk_…` on every request, from a server only.
- Response envelope: `{ "errors": null | { "errorCode", "errorMessage", "errorDetails?" }, "data": … }`.


---

# GitHub

> Two-way GitHub sync for your project via a fine-grained personal access token.

:::note
For a step-by-step walkthrough, see the [GitHub guide](https://www.totalum.app/docs/developers/github). GitHub sync is included on paid plans.
:::

Connect a GitHub repository to your project for two-way sync. After connecting, every new version (a prompt, a file save, a restore, an import) is pushed to `develop`, the published code is pushed to `main` on every publish, and commits you push to `develop` are pulled in before the next prompt. All endpoints require the `api-key` header and are **free**.

## Connect GitHub Repository

```endpoint
method: POST
path: /api/v1/vcaas/projects/:projectId/github/connect
cost: Free
```

Connect a GitHub repository to your project using a Fine-grained Personal Access Token (PAT). This validates permissions, stores credentials, sets up branches (`develop` and `main`), and performs the initial sync. After connecting, every new version is pushed to `develop`, and the published code to `main` on every publish.

:::tip Create a GitHub token
1. Go to `https://github.com/settings/personal-access-tokens`.
2. Click "Generate new token".
3. Select "Only select repositories" and choose your repo.
4. Under "Repository permissions", set **Contents: Read and write** (Metadata: Read is added automatically).
5. Click "Generate token" and copy it (it starts with `github_pat_`).
:::

:::note Branch setup
For empty repositories, Totalum creates the `develop` and `main` branches automatically. For non-empty repositories, both branches must already exist.
:::

**`syncDirection`** (controls behavior when the repo has existing content with no common git history):

- `"totalum_to_github"` (default) — pushes the Totalum project code to GitHub, replacing the repository content on both `develop` and `main` branches.
- `"github_to_totalum"` — pulls the GitHub repository code into Totalum, replacing the current project code. If the `develop` branch doesn't exist on GitHub, it is created from `main`.

**Path parameters**

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

**Body parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `token` | string | Yes | GitHub Fine-grained Personal Access Token |
| `repositoryFullName` | string | Yes | Full repository name (e.g. `"owner/repo"`) |
| `syncDirection` | string | No | `"totalum_to_github"` (default) or `"github_to_totalum"`. Controls behavior when repo has existing content with no common history |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.connected` | boolean | true if connection was successful |
| `data.repositoryFullName` | string | The connected repository |
| `data.syncAction` | string | `"push_new"`, `"push"`, `"pull"`, `"merge_and_push"`, or `"already_synced"` |
| `data.repoHasContent` | boolean | Whether the repo had existing content |
| `data.requiresRebuild` | boolean | Whether a rebuild was triggered. If true, poll `GET /github/pull-status` until status is `"success"` or `"error"` |

**Example request**

```bash
curl -X POST \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"token":"github_pat_xxx","repositoryFullName":"myuser/my-repo","syncDirection":"totalum_to_github"}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/my-app/github/connect
```

:::success Success · 200 OK
```json
{
  "errors": null,
  "data": {
    "connected": true,
    "repositoryFullName": "myuser/my-repo",
    "syncAction": "push_new",
    "repoHasContent": false,
    "requiresRebuild": false
  }
}
```
:::

:::danger Error · 400
```json
{
  "errors": {
    "errorCode": "MISSING_GITHUB_FIELDS",
    "errorMessage": "token and repositoryFullName are required"
  },
  "data": null
}
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `MISSING_GITHUB_FIELDS` | 400 | token and repositoryFullName are required |
| `INVALID_SYNC_DIRECTION` | 400 | syncDirection must be "totalum_to_github" or "github_to_totalum" |
| `GITHUB_VALIDATION_FAILED` | 400 | Token invalid, missing permissions, or repo not found |
| `GITHUB_SYNC_FAILED` | 400 | Initial sync with GitHub failed |
| `AGENT_RUNNING` | 409 | Cannot connect while agent is running |
| `PLATFORM_CAPABILITY_QUOTA_REACHED` | 403 | GitHub sync is not included on your plan |

## Disconnect GitHub

```endpoint
method: DELETE
path: /api/v1/vcaas/projects/:projectId/github/connect
cost: Free
```

Remove the GitHub integration from your project. Deletes the stored token and repository credentials. Code already in your project is not affected.

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.success` | boolean | true on success |
| `data.message` | string | Confirmation message |

**Example request**

```bash
curl -X DELETE -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/my-app/github/connect
```

:::success Success · 200 OK
```json
{ "errors": null, "data": { "success": true, "message": "GitHub disconnected successfully" } }
```
:::

:::danger Error · 400
```json
{
  "errors": {
    "errorCode": "GITHUB_NOT_CONNECTED",
    "errorMessage": "GitHub is not connected to this project"
  },
  "data": null
}
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `GITHUB_NOT_CONNECTED` | 400 | GitHub is not connected to this project |

## Get GitHub Status

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

Check whether GitHub is connected and the token is still valid.

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.connected` | boolean | Whether GitHub is connected |
| `data.tokenValid` | boolean | Whether the PAT token is still valid |
| `data.tokenExpired` | boolean | True when the last linked PAT was detected as expired/revoked. Sticky until the user reconnects. Use this to prompt the user to reconnect |
| `data.repositoryFullName` | string | Connected repository, or last-known repo when tokenExpired is true |
| `data.developBranch` | string | `"develop"` (only if connected) |
| `data.productionBranch` | string | `"main"` (only if connected) |
| `data.lastPushAt` | string \| undefined | When Totalum last pushed to GitHub |
| `data.lastPushSha` | string \| null \| undefined | The commit it pushed |
| `data.needsReconnect` | boolean \| undefined | `true` when the token must be replaced (connect again) |

**Example request**

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

:::success Success · 200 OK
```json
{
  "errors": null,
  "data": {
    "connected": true,
    "tokenValid": true,
    "tokenExpired": false,
    "repositoryFullName": "myuser/my-repo",
    "developBranch": "develop",
    "productionBranch": "main"
  }
}
```
:::

## Pull from GitHub

```endpoint
method: POST
path: /api/v1/vcaas/projects/:projectId/github/pull
cost: Free
```

Pull the latest changes from the GitHub `develop` branch into your project. If files changed, the server rebuilds automatically in the background. Poll the pull status endpoint until complete. This is an async operation.

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.status` | string | `"pulling"` (async rebuild started) or `"no_changes"` |
| `data.message` | string | Status message |
| `data.filesUpdated` | number | Number of files updated |

**Example request**

```bash
curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/my-app/github/pull
```

:::success Success · 200 OK
```json
{
  "errors": null,
  "data": {
    "status": "pulling",
    "message": "Pull started successfully",
    "filesUpdated": 5
  }
}
```
:::

:::danger Error · 400
```json
{
  "errors": {
    "errorCode": "GITHUB_NOT_CONNECTED",
    "errorMessage": "GitHub is not connected (connect first)"
  },
  "data": null
}
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `GITHUB_NOT_CONNECTED` | 400 | GitHub is not connected (connect first) |
| `GITHUB_PULL_ERROR` | 400 | Failed to pull changes |
| `AGENT_RUNNING` | 409 | Cannot pull while agent is running |
| `PLATFORM_CAPABILITY_QUOTA_REACHED` | 403 | GitHub sync is not included on your plan |

## Get Pull Status

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId/github/pull-status
cost: Free
```

Poll this endpoint after calling Pull from GitHub, OR after a Connect GitHub call that returned `requiresRebuild: true` (e.g. `github_to_totalum` direction). Returns the current rebuild status. Auto-completes after 10 minutes.

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.status` | string | `"pulling"`, `"success"`, `"error"`, or null (no pull) |
| `data.createdAt` | string | ISO 8601 date when the pull started |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/my-app/github/pull-status
```

:::success Success · 200 OK
```json
{ "errors": null, "data": { "status": "success", "createdAt": "2026-03-17T10:00:00.000Z" } }
```
:::

## Download Environment Variables

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

Get the project's environment variables as `.env` file content for both development and production environments. Use this to set up a local development environment or configure production deployments.

Same content as [Get Project Env](https://www.totalum.app/docs/api/secrets#get-project-env), both environments at once, as JSON. Included on paid plans (source download).

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.envDev` | string | .env file content for development (KEY=VALUE format, one per line) |
| `data.envProd` | string | .env file content for production (KEY=VALUE format, one per line) |

**Example request**

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

:::success Success · 200 OK
```json
{
  "errors": null,
  "data": {
    "envDev": "TOTALUM_ENV=dev\nTOTALUM_PROJECT_KEY=tlm_pk_xxx\nBETTER_AUTH_SECRET=xxxx\nNEXT_PUBLIC_APP_URL=http://localhost:3100\n...",
    "envProd": "TOTALUM_ENV=live\nTOTALUM_PROJECT_KEY=tlm_pk_xxx\nBETTER_AUTH_SECRET=xxxx\nNEXT_PUBLIC_APP_URL=https://my-app.totalum-project.com\n..."
  }
}
```
:::


**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `PLATFORM_CAPABILITY_QUOTA_REACHED` | 403 | Source download is not included on your plan |
| `PROJECT_NOT_READY` | 409 | The project is still being set up; retry after `Retry-After` seconds |
