> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surfacearea.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Dashboards from the CLI

> Create a design dashboard, check its TypeScript render code out into a directory, edit it with any editor or coding agent, push it back and render it on real data with gateway dashboards.

`gateway dashboards` puts a design dashboard's code in a local directory. Edit the TypeScript render files with your own editor or coding agent, push them, and the dashboard shows the new code in the app. The same dashboards are the ones the Design Agent builds, so you can switch between the two at any time.

## Build one in five commands

```bash theme={null}
gateway dashboards create "Support review"     # working starter code in ./support-review
cd support-review
$EDITOR render/index.ts                          # render(ctx) returns HTML with Tailwind classes
gateway dashboards push                          # refused, with every problem listed, if it does not compile
gateway dashboards render                        # renders it on the newest session and prints the app URL
```

`gateway dashboards open` prints the same URL and opens it when run from a terminal.

## What a dashboard directory holds

```text theme={null}
support-review/
  gateway-dashboard.json   # the dashboard id and the stamp your next push sends
  render/
    index.ts               # the entrypoint: exports render(ctx) returning an HTML string
    lib/html.ts            # any file the entrypoint imports by relative path
  transform.js             # optional: transform(ctx) reshapes the data before render
```

* `render/` holds the `.ts` or `.js` render files. Files import each other with relative paths.
* `transform.js` is plain JavaScript. Without it, `render` receives the data as loaded. Deleting it and pushing removes the dashboard's transform.
* Hidden files are skipped. Symbolic links are refused.

## What render receives

A session dashboard's `ctx` is one session: `ctx.session` (id, title, trace count, users, tags), `ctx.traces` (each with its observations and scores), `ctx.matchingTraces`, `ctx.sessionScores` and `ctx.computerUse.steps`. A task dashboard receives `ctx.taskSet`, `ctx.task` and `ctx.tasks`; an environment dashboard receives `ctx.environment`, `ctx.versions`, `ctx.rollouts` and `ctx.summary`. Run `gateway dashboards test-transform` to see the real shape.

```typescript theme={null}
import { escapeHtml } from "./lib/html";

export function render(ctx) {
  const rows = ctx.traces.map(({ trace }) => `<li>${escapeHtml(trace.name ?? "Untitled")}</li>`).join("");
  return `<main class="p-6"><h1 class="text-xl">${escapeHtml(ctx.session.title ?? "Session")}</h1><ul>${rows}</ul></main>`;
}
```

## Commands

| Command | What it does |
| - | - |
| `list [--limit N] [--json]` | The project's dashboards, newest first: id, kind (`session`, `task`, `environment`), name. |
| `create <name> [--task-set <id> \| --environment <id>] [--template starter\|empty] [--description] [--target <dir>]` | Creates the dashboard and checks it out into `./<name>` (or `--target`). Without a binding it renders sessions. |
| `pull <id> [--target <dir>] [--force]` | Checks an existing dashboard out. An explicit `--target` with local edits, or holding another dashboard, is refused unless `--force`. |
| `push [dir] [--dry-run] [--force]` | Replaces the dashboard's code with the directory's files in one write, then records the new stamp. `--dry-run` checks without writing. |
| `test-transform [dir] [--session <id>]` | Runs the directory's `transform.js` on a real session and prints its output and shape. Nothing is saved. Session dashboards only. |
| `render [dir\|id] [--session <id>] [--task-set <id>] [--task <id>] [--out <file>]` | Renders the pushed dashboard on real data and prints whether it rendered and the app URL. `--out` also writes the HTML. A dashboard with no code yet is not rendered (exit `1`): push render files first. |
| `open [dir\|id] [--no-launch]` | Prints the app URL and opens it from a terminal. `GATEWAY_NO_BROWSER=1` or a non-terminal output only prints it. |

## When a push is refused

A refused push writes nothing, and the directory keeps its stamp.

| Refusal | Why | What to do |
| - | - | - |
| `400 dashboard_code_rejected` | A file path, import or TypeScript error; every problem is listed | Fix the files and push again. |
| `409 dashboard_moved` | Someone (the Design Agent, a teammate, another push) changed the dashboard since your pull | `gateway dashboards pull <id> --target . --force` after saving your edits elsewhere, reapply them, push. `push --force` overwrites instead. |

Exit codes: `0` done, `1` the platform refused or the dashboard did not render, `2` bad arguments or not a dashboard directory (nothing was sent).

## The same from coding agents and HTTP

| CLI | MCP tool | REST |
| - | - | - |
| `list` | `list_dashboards` | `GET /api/public/dashboards` |
| `create` | `create_dashboard` | `POST /api/public/dashboards` |
| `pull` | `pull_dashboard` | `GET /api/public/dashboards/{id}` |
| `push` | `push_dashboard` | `PUT /api/public/dashboards/{id}/code` |
| `test-transform` | `test_dashboard_transform` | `POST /api/public/dashboards/{id}/test-transform` |
| `render` | `preview_dashboard` | `POST /api/public/dashboards/{id}/render` |

Reads accept the public key. Creating, pushing, testing a transform and rendering need the secret key, because they write or run code.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.