# Totalum App Builder API — Logs & Preview Server

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


---

# Logs & Preview Server

> See what your app prints, in development and in production.

A project writes logs in two places: the **development preview** (`dev`) and the **published site** (`live`). Each log line belongs to one request, so you can follow a failing request from its status to the error it threw. [Get Project Logs](#get-project-logs) reads both with filters; the other two endpoints keep simpler shapes.

## Get Project Logs

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

Log lines of the preview or the published site, newest first, with filters.

**Path parameters**

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

**Query parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `env` | string | No | `"dev"` (preview) or `"live"` (published site) |
| `from` | string | No | Start, ISO 8601 |
| `to` | string | No | End, ISO 8601 |
| `level` | string | No | `"error"`, `"warn"`, `"info"`, `"log"` or `"debug"` |
| `q` | string | No | Text to find in the message (up to 200 characters) |
| `status` | string | No | HTTP status of the request: `500`, or a class like `5xx` |
| `path` | string | No | Request path |
| `requestId` | string | No | One request's lines |
| `limit` | number | No | 1–500 |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.items` | object[] | Log lines |
| `data.items[].ts` | string | ISO 8601 time |
| `data.items[].level` | string | `error`, `warn`, `info`, `log` or `debug` |
| `data.items[].message` | string | The text (secrets redacted) |
| `data.items[].requestId` | string \| null | The request it belongs to |
| `data.items[].method` | string \| null | HTTP method |
| `data.items[].path` | string \| null | Request path |
| `data.items[].status` | number \| null | Response status |
| `data.items[].cpuMs` | number \| null | CPU time of the request |
| `data.items[].wallMs` | number \| null | Duration of the request |
| `data.items[].outcome` | string | How the request ended (`ok`, `exception`, `canceled`, …) |
| `data.items[].exception` | object \| undefined | `{ name, message, stack? }` of an uncaught error |
| `data.window` | object | `{ from, to, source }`: the range searched |
| `data.nextCursor` | string \| null | Cursor for older lines |
| `data.sampled` | boolean | `true` when a very busy window was sampled |
| `data.hint` | string \| undefined | A suggestion when nothing matched |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  "https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/logs?env=live&level=error&limit=50"
```

:::success Success · 200 OK
```json
{
  "errors": null,
  "data": {
    "items": [
      {
        "ts": "2026-10-08T12:03:11.000Z",
        "level": "error",
        "message": "TotalumError [INSUFFICIENT_CREDITS]: The account has no credits",
        "requestId": "9a1f2c",
        "method": "POST",
        "path": "/api/summary",
        "status": 502,
        "cpuMs": 4,
        "wallMs": 210,
        "outcome": "ok"
      }
    ],
    "window": { "from": "2026-10-08T06:03:00.000Z", "to": "2026-10-08T12:03:30.000Z", "source": "buffer" },
    "nextCursor": null,
    "sampled": false
  }
}
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `VALIDATION_ERROR` | 400 | A filter has an invalid value |

## Get Production Logs

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId/backend/prod/logs
cost: Free
```

The published site's requests, one record per request with the console output and exceptions of that request nested in it. Without `from`/`to` it covers the last 6 hours (the last 3 days when `to` or `regexSearch` is given). At most 500 log lines are read.

**Path parameters**

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

**Query parameters**

| Field | Type | Required | Description |
|---|---|---|---|
| `from` | string | No | Start, ISO 8601 |
| `to` | string | No | End, ISO 8601 |
| `regexSearch` | string | No | Text to find — the fastest way to a specific request |

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.ok` | boolean | `true` |
| `data.organizationId` | string | The project id |
| `data.fromMs` | number | Start of the range (epoch ms) |
| `data.toMs` | number | End of the range (epoch ms) |
| `data.regexSearch` | string \| null | The search |
| `data.count` | number | Records returned |
| `data.records` | object[] | One per request |
| `data.records[].EventTimestampMs` | number | When it was served |
| `data.records[].Outcome` | string | How it ended |
| `data.records[].WallTimeMs` | number \| undefined | Duration |
| `data.records[].CPUTimeMs` | number \| undefined | CPU time |
| `data.records[].Event.Request` | object | `{ URL, Method }` |
| `data.records[].Event.Response` | object \| undefined | `{ Status }` |
| `data.records[].Logs` | object[] | `{ Level, Message: [text], TimestampMs }` |
| `data.records[].Exceptions` | object[] | `{ Name, Message, TimestampMs }` |
| `data.totalChars` | number | Size of the records |
| `data.truncated` | boolean | `true` when the 500-line read limit was hit — narrow the range |
| `data.window` | object | `{ from, to, source }` actually searched |

**Example request**

```bash
curl -H "api-key: tlm_sk_your_key" \
  "https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/backend/prod/logs?regexSearch=checkout"
```

:::success Success · 200 OK
```json
{
  "errors": null,
  "data": {
    "ok": true,
    "organizationId": "barber-bookings",
    "fromMs": 1791547200000,
    "toMs": 1791806400000,
    "regexSearch": "checkout",
    "count": 1,
    "records": [
      {
        "EventTimestampMs": 1791806380000,
        "Outcome": "ok",
        "WallTimeMs": 128,
        "CPUTimeMs": 14,
        "Event": { "Request": { "URL": "/api/checkout", "Method": "POST" }, "Response": { "Status": 500 } },
        "Logs": [{ "Level": "error", "Message": ["checkout failed: missing price"], "TimestampMs": 1791806380000 }],
        "Exceptions": []
      }
    ],
    "totalChars": 312,
    "truncated": false
  }
}
```
:::

## Get Dev Server Logs

```endpoint
method: GET
path: /api/v1/vcaas/projects/:projectId/backend/dev/logs
cost: Free
```

The development preview's log of the last hour, as plain text (`[time] message` per line, oldest first).

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.logs` | string | The lines, or a note when there are none yet |

**Example request**

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

:::success Success · 200 OK
```json
{ "errors": null, "data": { "logs": "[2026-10-08T11:58:02.000Z] booking saved bk_1001\n[2026-10-08T11:58:03.000Z] e-mail sent to owner" } }
```
:::

## Start or Restart Server

```endpoint
method: POST
path: /api/v1/vcaas/projects/:projectId/agent/server/start-or-restart
cost: 2 credits (free to wake a sleeping project)
```

Restart the project's development environment from its saved code, for example when the preview is stuck. On a project asleep after a long inactivity it wakes it instead (free): the preview answers again within seconds. You rarely need this — a prompt wakes a project by itself.

**Path parameters**

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

**Response fields**

| Field | Type | Description |
|---|---|---|
| `data.message` | string | `"Server is restarting"` or `"Project is reactivating"` |
| `data.status` | string | `"restarting"` or `"reactivating"` |
| `data.operationId` | string | The operation, visible in [Get Project State](https://www.totalum.app/docs/api/projects#get-project-state) |
| `data.creditsCharged` | number | 2 for a restart, 0 to wake |

**Example request**

```bash
curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/agent/server/start-or-restart
```

:::success Success · 200 OK
```json
{ "errors": null, "data": { "message": "Server is restarting", "status": "restarting", "operationId": "op_01k6xt", "creditsCharged": 2 } }
```
:::

**Error codes**

| Code | HTTP | Meaning |
|---|---|---|
| `AGENT_RUNNING` | 409 | A prompt is running |
| `OPERATION_IN_PROGRESS` | 409 | Another operation (publish, restore…) is running |
