# Totalum App Builder API — Secrets, Keys & .env

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": … }`.


---

# Secrets, Keys & .env

> Environment variables, project keys and the .env file.

**Secrets** are environment variables the app reads on the server (`process.env.STRIPE_KEY`): values are write-only and never returned. **Project API keys** (`tlm_pk_…`) let your own servers call the project's database, files and built-in features. They are not your account key (`tlm_sk_…`).

## Create Secret

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

Add an environment variable. It reaches the preview and the agent at once and the published site at the next publish. The agent never asks for a key in its reply text: when the app needs one, its message carries `secretKeysNeeded` ([Get Agent Status](https://www.totalum.app/docs/api/agent#get-agent-status)). Let your user choose the environment, add each name here, then prompt again ("the keys are saved, carry on").

**Path parameters**

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

**Body parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `secretName` | string | Yes | Uppercase letters, digits and `_`, starting with a letter. Names starting with `TOTALUM_` (except `TOTALUM_VCAAS_API_KEY`) and `NODE_ENV`, `NEXT_PUBLIC_APP_URL`, `BETTER_AUTH_SECRET`, `NEXT_SERVER_ACTIONS_ENCRYPTION_KEY`, `TURSO_AUTH_TOKEN`, `TURSO_DATABASE_URL` are reserved |
| `secretValue` | string | Yes | 1–10,000 characters |
| `environment` | string | No | `"development"` (preview), `"production"` (published site) or `"both"` (default) |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data._id` | string | Secret id (to delete it) |
| `data.secretName` | string | Name |
| `data.environment` | string | Environment |
| `data.createdAt` | string | ISO 8601 |

**Example request**

```bash
curl -X POST \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"secretName":"OPENWEATHER_KEY","secretValue":"abc123","environment":"both"}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/secrets
```

:::success Success · 200 OK
```json
{ "errors": null, "data": { "_id": "sec_01k6xy", "secretName": "OPENWEATHER_KEY", "environment": "both", "createdAt": "2026-10-08T12:00:00.000Z" } }
```
:::

:::danger Validation error · 400
```json
{ "errors": { "errorCode": "VALIDATION_ERROR", "errorMessage": "secretName: Secret name must be uppercase, separated by underscores, cannot start with a number, and can only contain letters, numbers, and underscores", "errorDetails": { "errors": [{ "value": "openweather", "msg": "Secret name must be uppercase, separated by underscores, cannot start with a number, and can only contain letters, numbers, and underscores", "param": "secretName", "location": "body" }] } }, "data": null }
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `MISSING_SECRET_FIELDS` | 400 | `secretName` or `secretValue` is missing |
| `VALIDATION_ERROR` | 400 | The name, value or environment is invalid; `errorDetails.errors` lists each field |
| `SECRET_CREATE_FAILED` | 400 | A secret with this name already exists in that environment |
| `RESERVED_SECRET_NAME` | 400 | The name is reserved |
| `SECRET_LIMIT_EXCEEDED` | 400 | The project already has 500 secrets |

## Delete Secret

```endpoint
method: DELETE
path: /api/v1/vcaas/projects/:projectId/secrets/:secretId
cost: Free
```

Remove an environment variable. The names and ids of a project's secrets are in [Get Project](https://www.totalum.app/docs/api/projects#get-project) (`secrets[]`).

**Path parameters**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |
| `secretId` | string | The secret's `_id` |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.success` | boolean | `true` |

**Example request**

```bash
curl -X DELETE -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/secrets/sec_01k6xy
```

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

## List Project Keys

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId/api-keys
cost: Free
```

The project's own API keys, revoked ones included (secrets are never listed). Two are managed by Totalum: `managed: "app"` runs the app itself, `managed: "env"` is the key in the downloaded `.env`; rotate them, never revoke them.

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.keys[].id` | string | Key id |
| `data.keys[].name` | string | Name |
| `data.keys[].prefix` | string | The visible start of the key |
| `data.keys[].scopes` | string[] | Scopes |
| `data.keys[].env` | string | `"dev"`, `"live"` or `"both"` |
| `data.keys[].createdAt` | string \| null | ISO 8601 |
| `data.keys[].expiresAt` | string \| null | Expiry |
| `data.keys[].revokedAt` | string \| null | When it was revoked |
| `data.keys[].managed` | string \| undefined | `"app"` or `"env"` for Totalum's own keys |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/api-keys
```

:::success Success · 200 OK
```json
{ "errors": null, "data": { "keys": [{ "id": "key_01k6xz", "name": "backend", "prefix": "tlm_pk_a1b2", "scopes": ["db:read", "db:write"], "env": "both", "createdAt": "2026-10-08T12:00:00.000Z", "expiresAt": null, "revokedAt": null }] } }
```
:::

## Create Project API Key

```endpoint
method: POST
path: /api/v1/vcaas/projects/:projectId/api-keys
cost: Free
```

Create a `tlm_pk_` key for calling this project's database, files and built-in features from your servers (with [`totalum-sdk`](https://www.totalum.app/docs/sdk) or HTTP). It works only on this project and only for its scopes. The secret is returned **once**. Owner (or a member allowed to manage API keys) only.

| Scope | Allows |
|---|---|
| `db:read`, `db:write`, `db:schema` | Read rows, write rows, change the schema |
| `files:read`, `files:write` | Read and upload files (`files:write` also makes PDFs) |
| `ai`, `email`, `web`, `scan`, `speech`, `vectors`, `cron`, `seo` | One built-in feature each |
| `integrations:*` | Every built-in feature above |
| `browser`, `payments`, `realtime` | Cloud browser, payments, real-time |
| `logs:read`, `analytics:read`, `meter:write` | Logs, site analytics, usage reporting |
| `*` | Everything |

**Path parameters**

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

**Body parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | A name |
| `scopes` | string[] | Yes | One or more scopes from the table |
| `env` | string | No | `"dev"`, `"live"` or both (default): the database environment the key may use |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.key` | object | The key, with the fields of [List Project Keys](#list-project-keys) |
| `data.key.plaintext` | string | The secret — shown only now |

**Example request**

```bash
curl -X POST \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"name":"backend","scopes":["db:read","db:write","integrations:*"]}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/api-keys
```

:::success Success · 201 Created
```json
{ "errors": null, "data": { "key": { "id": "key_01k6xz", "name": "backend", "prefix": "tlm_pk_a1b2", "scopes": ["db:read", "db:write", "integrations:*"], "env": "both", "createdAt": "2026-10-08T12:00:00.000Z", "expiresAt": null, "revokedAt": null, "plaintext": "tlm_pk_a1b2…" } } }
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `INVALID_SCOPES` | 400 | Empty or unknown scopes; the message lists the valid ones |
| `OWNER_ONLY` | 403 | Only the owner or a member allowed to manage API keys |
| `PROJECT_NOT_READY` | 409 | The project is still being set up; retry after `Retry-After` seconds |

## Rotate Project Key

```endpoint
method: POST
path: /api/v1/vcaas/projects/:projectId/api-keys/:keyId/rotate
cost: Free
```

Replace a key with a new secret (same name, scopes and environment); the old one stops at once. For the app's own key, the new key reaches the app before the old one stops.

**Path parameters**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |
| `keyId` | string | The key id |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.key` | object | The new key, with `plaintext` (shown once) |

**Example request**

```bash
curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/api-keys/key_01k6xz/rotate
```

:::success Success · 201 Created
```json
{ "errors": null, "data": { "key": { "id": "key_01k6y0", "name": "backend", "prefix": "tlm_pk_c3d4", "scopes": ["db:read", "db:write", "integrations:*"], "env": "both", "createdAt": "2026-10-08T12:10:00.000Z", "expiresAt": null, "revokedAt": null, "plaintext": "tlm_pk_c3d4…" } } }
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `KEY_NOT_FOUND` | 404 | Unknown or already revoked |
| `OWNER_ONLY` | 403 | Only the owner or a member allowed to manage API keys |

## Revoke Project Key

```endpoint
method: DELETE
path: /api/v1/vcaas/projects/:projectId/api-keys/:keyId
cost: Free
```

Revoke a key at once.

**Path parameters**

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | The project id |
| `keyId` | string | The key id |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.revoked` | boolean | `true` |

**Example request**

```bash
curl -X DELETE -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/api-keys/key_01k6y0
```

:::success Success · 200 OK
```json
{ "errors": null, "data": { "revoked": true } }
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `KEY_NOT_FOUND` | 404 | Unknown or already revoked |
| `KEY_MANAGED` | 409 | The app's own key or the `.env` key: rotate it instead |
| `OWNER_ONLY` | 403 | Only the owner or a member allowed to manage API keys |

## Get Project Env

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

The project's `.env` file for one environment, as **plain text** (not JSON): the project key, the auth secret, the URLs and that environment's secrets. Save it as `.env` (dev) or `.env.prod` (live) next to the [downloaded source](https://www.totalum.app/docs/api/source-files#download-source-code) to run the app locally; never commit it. Owner (or a member allowed to manage API keys) only.

**Path parameters**

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

**Query parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `env` | string | Yes | `"dev"` or `"live"` |

**Response fields**

| Field | Type | Description |
|---|---|---|
| (body) | text | The `.env` file (`Content-Disposition: attachment`) |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" -o .env \
  "https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/env?env=dev"
```

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `INVALID_ENV` | 400 | `env` is not `dev` or `live` |
| `OWNER_ONLY` | 403 | Only the owner or a member allowed to manage API keys |
