---
title: "Local API Reference: Agent Automation & Endpoints · Metrix.Bar"
description: "Integrate AI agents with Metrix.Bar's local HTTP API on 127.0.0.1:6387. Learn endpoint specs, bearer authentication, validation, source editing, and event dispatching."
canonical: "https://metrix.bar/docs/local-api"
language: "en"
---

# Local API Reference

> HTTP REST API specification for AI agents and local automation.

## Overview

Metrix.Bar runs a local HTTP REST service bound strictly to loopback (`127.0.0.1:6387`). It is designed for AI coding agents, command-line scripts, and developer tools to inspect, validate, update, and manage `.bar` dashboards.

- **Host & Port**: `http://127.0.0.1:6387`
- **Authentication**: Bearer token via `Authorization: Bearer <token>` (`GET /v0/health` is exempt)
- **Full Schema**: The complete OpenAPI specification is accessible at [`/openapi.json`](/openapi.json) (v0 specification)

Port `6387` is an example throughout this guide. Use the address and port shown in the app’s Agent settings.

## Security Model & Status Codes

The local API is hardened against unauthorized local network access and browser-based attacks:

1. **Strict Loopback & Host Header Verification**: The HTTP server binds exclusively to `127.0.0.1` and verifies the `Host` request header, accepting only `127.0.0.1`, `localhost`, or loopback IP variants (with optional port). Non-loopback hosts return `403 Forbidden`.
2. **Browser Cross-Origin Protection**: Requests containing an `Origin` header (including `Origin: null`) are rejected immediately with `403 Forbidden`. CORS is disabled, preventing DNS rebinding and cross-site execution.
3. **Bearer Token Verification**: Every endpoint (except `/v0/health`) requires the Bearer token configured in **Settings** → **Agent**; missing or invalid tokens return `401 Unauthorized`.
4. **Request Body Limit**: Payloads are capped at 2 MiB per request.
5. **Secret Isolation**: Endpoint responses never reveal raw secret values stored in the system credential store (keychain). Secret inputs only report `is_set: true | false`, with `value` fixed to `null`.

### Status Codes Reference

| Status Code                  | Meaning             | Description                                                                                                                                  |
| ---------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `200 OK`                     | Success             | Returns the requested resource or operation result.                                                                                          |
| `201 Created`                | Created             | Dashboard created successfully; response headers include the new source `ETag` and resource `Location`.                                      |
| `202 Accepted`               | Accepted            | Asynchronous task accepted and dispatched (refresh, event dispatch); does not wait for execution.                                            |
| `204 No Content`             | No Content          | Request processed with no response body (inputs update, secret write, delete dashboard).                                                     |
| `400 Bad Request`            | Bad Request         | Malformed JSON, missing required fields, or undeclared/invalid input parameters.                                                             |
| `401 Unauthorized`           | Unauthorized        | Missing or invalid Bearer token; includes `WWW-Authenticate: Bearer realm="Metrix.Bar"`. The scheme is case-insensitive; the token is exact. |
| `413 Payload Too Large`      | Too large           | Request body exceeds 2 MiB.                                                                                                                  |
| `415 Unsupported Media Type` | Unsupported type    | Missing, malformed, or unsupported Content-Type on source endpoints.                                                                         |
| `403 Forbidden`              | Forbidden           | Request carries an `Origin` header, or `Host` header is not a loopback address.                                                              |
| `404 Not Found`              | Not Found           | Dashboard not found, or secret input not declared in `.bar`.                                                                                 |
| `412 Precondition Failed`    | Precondition Failed | `If-Match` optimistic concurrency check failed; ETag mismatch; GET source again before retrying.                                             |
| `422 Unprocessable Entity`   | Validation Failed   | `.bar` source parsing failed; returns structured `diagnostics`.                                                                              |
| `501 Not Implemented`        | Not Implemented     | The host integration callback is not implemented in the current runtime.                                                                     |

## Endpoint Summary

All routes are mounted under `/v0`:

| Method   | Endpoint                           | Description                                                                                                 |
| -------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `GET`    | `/v0/health`                       | Unauthenticated health check returning status, uptime in ms, and protocol version.                          |
| `POST`   | `/v0/validate`                     | Validates `.bar` source text without persisting it.                                                         |
| `POST`   | `/v0/check`                        | Preflights an isolated Worker and validates its view tree without installing it.                            |
| `GET`    | `/v0/types`                        | Returns the Worker TypeScript declarations for editors and agents.                                          |
| `GET`    | `/v0/dashboards`                   | Lists all saved dashboards with status and ETag.                                                            |
| `POST`   | `/v0/dashboards`                   | Creates a new dashboard from raw `.bar` source text.                                                        |
| `GET`    | `/v0/dashboards/:id`               | Returns complete dashboard state snapshot and view tree.                                                    |
| `DELETE` | `/v0/dashboards/:id`               | Uninstalls a dashboard and its associated keychain secrets.                                                 |
| `GET`    | `/v0/dashboards/:id/source`        | Retrieves raw `.bar` source text with an `ETag` header.                                                     |
| `PUT`    | `/v0/dashboards/:id/source`        | Replaces `.bar` source text (supports `If-Match` concurrency check).                                        |
| `PATCH`  | `/v0/dashboards/:id/settings`      | Updates entrance, default, and per-source opening-mode overrides (`placements`, `open_mode`, `open_modes`). |
| `GET`    | `/v0/dashboards/:id/inputs`        | Reads input schema declarations, current values, and secret statuses.                                       |
| `POST`   | `/v0/dashboards/:id/inputs`        | Updates public input values.                                                                                |
| `PUT`    | `/v0/dashboards/:id/secrets/:name` | Stores a secret value in the system credential store.                                                       |
| `POST`   | `/v0/dashboards/:id/refresh`       | Triggers an immediate refresh cycle.                                                                        |
| `POST`   | `/v0/dashboards/:id/reload`        | Reloads saved source after Worker preflight; preserves the existing runtime if preflight fails.             |
| `POST`   | `/v0/dashboards/:id/events/:name`  | Dispatches a custom event to the dashboard Worker.                                                          |

---

## Detailed Endpoint Guide

### Health Check

#### `GET /v0/health`

Verifies that the local server is running. Requires no authentication. Returns process uptime in milliseconds and the protocol version.

```bash
curl -fsS http://127.0.0.1:6387/v0/health
```

**Response (`200 OK`)**:

```json
{
  "status": "ok",
  "uptime_ms": 9900000,
  "protocol_version": "0",
  "app_version": "0.1.0"
}
```

> `app_version` is optional and only present when configured by the application.

---

### Worker Preflight and Types

`POST /v0/check` accepts only `application/json` (optional charset) with `{ "source": "...", "inputs": {}, "scheduled": false, "tree": false }`. Only `source` is required. It runs `render` in an isolated Worker; `scheduled` runs first only when `scheduled: true`. Both `scheduled` and `tree` default to false. Set `tree: true` to include the view tree. Diagnostics are returned without installation; network or storage side effects can still occur. `GET /v0/types` returns `metrixbar.d.ts` as text for editor completion.

To apply an external edit already saved on disk, call `POST /v0/dashboards/:id/reload` with the Bearer token and no body. A failed preflight preserves the previous runtime.

### Isolated fixtures and PNG screenshots

`POST /v0/check` also accepts optional `fixture` and `screenshot` objects. The tree and screenshot come from one Worker `render`. A fixture supplies test data, is limited to 2 MiB of JSON, and rejects unknown fields. Its required `now` field is an RFC 3339 timestamp that freezes the UTC clock. Inputs merge in this order: declared defaults → `fixture.inputs` → request `inputs`, with normal type validation and secret input restrictions.

```json
{
  "source": "<bar ...>...</bar>",
  "scheduled": true,
  "tree": true,
  "fixture": {
    "now": "2026-06-01T12:00:00Z",
    "inputs": {},
    "secrets": { "token": "fixture-token" },
    "files": { "/fixture/value": "42" },
    "icons": {
      "lucide:zap": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"m13 2-3 8H4l7 12 3-8h6Z\"/></svg>"
    },
    "http": [{ "url": "https://metrics.example.com/value", "body": "42" }]
  },
  "screenshot": { "theme": "dark", "width": 320, "height": 240, "scale": 2 }
}
```

`http` matches the exact method (default GET), full URL, and optional `request_body`. Status defaults to 200 and body is the raw response string. Remote modules and Image/Avatar resources also use fixture HTTP; resources load before the original Worker closes, with the same permissions and request identity headers. `files` matches absolute virtual paths and still requires declared permissions. `secrets` reads only fixture values, never system credentials. Named Icon resources (and Image.name without src/svg) require an explicit `icons` entry containing SVG or an image data URI, 1..256 KiB each. Missing names fail without consulting Assets, disk caches, or live Iconify services. Inline SVG and image data URIs remain supported. Sockets are disabled in fixture mode. Unmatched HTTP, files, secrets, or icons always produce diagnostics, even if the script catches the error. Never include real credentials in fixtures. Without a fixture, checks can still perform real I/O allowed by the dashboard permissions.

Omitting `screenshot` skips PNG generation. When supplied, `theme` must be `light` or `dark`; width/height use DIP and default to the panel size (320×240 when undeclared), each within 1..2048. Scale defaults to 1 and allows 0.5..4; the final image must not exceed 16 Mi pixels. Success adds `screenshot: {mime_type:"image/png", width:640, height:480, data_base64:"..."}` with actual PNG pixel dimensions. Rendering uses memory and waits for resources. Resource collection failures produce `resources` diagnostics (plus `fixture` for unmatched fixture I/O), and drawing failures produce `screenshot` diagnostics: HTTP 200, `valid:false`, no screenshot. Invalid parameters return 400. Loopback, Bearer, Origin, and secret isolation rules still apply.

### Validating `.bar` Source

#### `POST /v0/validate`

Validates `.bar` source syntax and schema compliance without saving it to disk. The parser operates in a fail-fast model, returning at most one diagnostic.

```bash
curl -fsS -X POST http://127.0.0.1:6387/v0/validate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary @dashboard.bar
```

**Valid Source Response (`200 OK`)**:

```json
{
  "valid": true,
  "diagnostics": []
}
```

**Invalid Source Response (`200 OK`)**:

```json
{
  "valid": false,
  "diagnostics": [
    {
      "severity": "error",
      "code": "inputs/input@type",
      "message": "unknown input type \"color\"",
      "path": "inputs/input@type"
    }
  ]
}
```

---

### Dashboard Management

#### `GET /v0/dashboards`

Lists all installed dashboards. The `status` field enum values are `idle`, `loading`, `ok`, or `error`. When present in the dashboard definition, optional metadata fields (`version`, `icon`, `description`, `license`, `homepage`, `docs`) are included in each summary.

```bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards
```

**Response (`200 OK`)**:

```json
{
  "dashboards": [
    {
      "id": "github-repo",
      "title": "GitHub Repo",
      "status": "ok",
      "protocol_version": "0",
      "revision": 3,
      "etag": "0123456789abcdef",
      "version": "1.0.0",
      "description": "Track GitHub repository stars, forks, and activity."
    }
  ]
}
```

#### `POST /v0/dashboards`

Creates and installs a new dashboard from `.bar` source text. The request body accepts `text/plain`, `application/octet-stream`, or JSON `{"source": "..."}`. Returns the created dashboard summary (including `protocol_version` and any optional metadata fields declared in `<bar>`).

```bash
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary @dashboard.bar
```

**Response (`201 Created`)**:
The response header includes `ETag: "0123456789abcdef"`.

```json
{
  "id": "github-repo",
  "status": "loading",
  "title": "GitHub Repo",
  "protocol_version": "0",
  "etag": "0123456789abcdef"
}
```

If source parsing fails, returns `422 Unprocessable Entity` with `diagnostics`.

#### `GET /v0/dashboards/:id`

Retrieves a dashboard's current state and rendered UI view tree snapshot. In view trees returned over the API, function callbacks (such as `onClick`) are serialized as `null`.

```bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo
```

**Response (`200 OK`)**:

```json
{
  "id": "github-repo",
  "status": "ok",
  "updated_at": "2025-01-01T00:00:00Z",
  "revision": 3,
  "inputs": {
    "repository": "golang/go"
  },
  "docs": "Configure your GitHub repository path to start monitoring.",
  "view": {
    "tray": {
      "title": "★ 1.2k"
    },
    "panel": {
      "type": "Column",
      "props": { "padding": 16, "gap": 12 },
      "children": [{ "type": "Text", "props": { "text": "egoist/mygo", "bold": true } }]
    }
  }
}
```

> When `status` is `error`, the response includes an `error` field describing the most recent run failure. `docs` contains plain-text setup instructions from `<docs>` (omitted when empty). `view` retains the last successfully rendered UI tree (`null` if no successful render has occurred yet).

#### `GET /v0/dashboards/:id/source`

Retrieves the raw `.bar` source text. The response header includes an RFC 9110 quoted `ETag` (the first 16 hex characters of the source's SHA-256 hash).

```bash
curl -fsS -H "Authorization: Bearer $TOKEN" -i http://127.0.0.1:6387/v0/dashboards/github-repo/source
```

#### `PUT /v0/dashboards/:id/source`

Replaces the `.bar` source text. The request body accepts `text/plain`, `application/octet-stream`, or JSON `{"source": "..."}`. The `id` in the new source must match the `:id` parameter in the URL.

Supports optimistic concurrency control via the `If-Match` header: provide the quoted ETag returned by GET source, a comma-separated list of quoted tags, or standalone `*` to match any existing dashboard. Strong comparison applies: weak tags never match, bare tags and malformed lists return 400. Only an omitted header skips the tag check; concurrent file edits remain protected.

```bash
curl -fsS -X PUT http://127.0.0.1:6387/v0/dashboards/github-repo/source \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  -H 'If-Match: "0123456789abcdef"' \
  --data-binary @updated.bar
```

**Success Response (`200 OK`)**:
The response header returns the new source `ETag`.

```json
{
  "id": "github-repo",
  "updated": true
}
```

**ETag Mismatch Response (`412 Precondition Failed`)**:

```json
{
  "error": "etag mismatch"
}
```

**Validation Failed Response (`422 Unprocessable Entity`)**:

```json
{
  "error": "validation failed",
  "diagnostics": [
    {
      "severity": "error",
      "code": "bar@id",
      "message": "source id \"other-repo\" does not match dashboard \"github-repo\"",
      "path": "bar@id"
    }
  ]
}
```

#### `DELETE /v0/dashboards/:id`

Uninstalls the specified dashboard, stops the running instance, and removes its stored secrets from the system credential store. The `.bar` source file is retained on disk.

```bash
curl -fsS -X DELETE -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo
```

**Response (`204 No Content`)**: Empty response body.

---

### Inputs & Secrets

#### `GET /v0/dashboards/:id/inputs`

Lists input parameter declarations and current values. Only secret inputs keep `value` set to `null` and report presence via `is_set: true | false`. File inputs return their local path string through this authenticated API.

```bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/inputs
```

**Response (`200 OK`)**:

```json
{
  "inputs": [
    {
      "name": "repository",
      "type": "string",
      "required": true,
      "label": "Repository",
      "help": "owner/repo",
      "value": "egoist/mygo",
      "is_set": true
    },
    {
      "name": "token",
      "type": "secret",
      "required": false,
      "label": "GitHub Token",
      "value": null,
      "is_set": true
    }
  ]
}
```

#### `POST /v0/dashboards/:id/inputs`

Updates public configuration input values. The request body must be wrapped in `{"inputs": {...}}`. Submitting new values triggers an asynchronous refresh cycle.

Values must match declared types (`string`/`file` as string, `bool` as boolean, `integer` as integer number, `number` as finite number). `secret` inputs cannot be updated here (use the secrets endpoint); undeclared input keys return `400 Bad Request`.

```bash
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards/github-repo/inputs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"inputs": {"repository": "golang/go"}}'
```

**Response (`204 No Content`)**: Empty response body.

#### `PUT /v0/dashboards/:id/secrets/:name`

Writes a sensitive credential into the system credential store (keychain) and triggers a dashboard refresh. The target input must be declared with `type="secret"` in `.bar`, or `404 Not Found` is returned. Secret values are excluded from ordinary dashboard records, snapshots, and API responses. Dashboard scripts can read them for authentication.

Request body is JSON `{"value": "..."}`:

```bash
curl -fsS -X PUT http://127.0.0.1:6387/v0/dashboards/github-repo/secrets/token \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": "ghp_xxxxxxxxxxxx"}'
```

**Response (`204 No Content`)**: Empty response body.

---

### Actions & Events

#### `POST /v0/dashboards/:id/refresh`

Dispatches a refresh command to the scheduler, triggering the dashboard Worker to fetch fresh data. Returns `202 Accepted` to indicate asynchronous acceptance without waiting for render completion.

```bash
curl -fsS -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/refresh
```

**Response (`202 Accepted`)**: Empty response body.

#### `POST /v0/dashboards/:id/events/:name`

Dispatches a custom event to the dashboard Worker's `event(name, payload)` handler. The request body accepts any valid JSON value (or empty, which defaults to `null`). A render is automatically scheduled when event processing finishes.

```bash
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards/github-repo/events/notify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action": "toggle-mode"}'
```

**Response (`202 Accepted`)**: Empty response body.

#### `PATCH /v0/dashboards/:id/settings`

The request body accepts `placements`, `open_mode`, and `open_modes`, with at least one field required:

- `placements`: an array containing any unique combination of `menubar`, `sidebar-left`, `sidebar-right`, and `sidebar-bottom`, or `null`.
- `open_mode`: default opening mode override (`flyout`, `dialog`, or `null`).
- `open_modes`: per-source opening mode overrides object, or `null`:
  - Omitted: preserves existing per-source overrides.
  - `null` or `{}`: clears all per-source overrides (all sources revert to following the default opening mode).
  - Object: replaces the entire per-source override map (not a partial merge). Valid keys: `menubar`, `sidebar-left`, `sidebar-right`, `sidebar-bottom`, `shortcut`. Valid values: `flyout`, `dialog`. Unknown keys, invalid values, or wrong types return 400.

Omitted fields stay unchanged; `null` clears only that field's user override and restores its author default. `placements: []` disables all visual entrances. Overrides are stored independently and apply equally to official and third-party dashboards.

Unknown fields, wrong types, unknown or duplicate placements, invalid modes, and an empty object return 400. The former `show_in_menu_bar` field is no longer accepted. A missing dashboard returns 404, an operation conflict returns 409, persistence failure returns 500, and an unavailable settings callback returns 501.

Configure entrances, default opening mode, and per-source overrides (e.g. Menu Bar and Right Sidebar entrances, default Flyout, with Keyboard Shortcut and Right Sidebar opening Dialog):

```bash
curl -fsS -X PATCH http://127.0.0.1:6387/v0/dashboards/github-repo/settings \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"placements":["menubar","sidebar-right"],"open_mode":"flyout","open_modes":{"shortcut":"dialog","sidebar-right":"dialog"}}'
```

**Response (`200 OK`)**: Returns the final effective settings. `placements` returns `[]` when disabled, never `null`. `open_modes` always returns the resolved effective modes across all five sources, never `null`.

```json
{
  "id": "github-repo",
  "placements": ["menubar", "sidebar-right"],
  "open_mode": "flyout",
  "open_modes": {
    "menubar": "flyout",
    "sidebar-left": "flyout",
    "sidebar-right": "dialog",
    "sidebar-bottom": "flyout",
    "shortcut": "dialog"
  }
}
```

Other request bodies:

Change only the default opening mode:

```json
{ "open_mode": "flyout" }
```

Disable all visual entrances:

```json
{ "placements": [] }
```

Restore default entrances (leaves opening modes untouched):

```json
{ "placements": null }
```

Restore default opening mode (leaves per-source overrides untouched):

```json
{ "open_mode": null }
```

Clear all per-source overrides (all sources revert to the default opening mode):

```json
{ "open_modes": null }
```

Restore default entrances, default opening mode, and clear all per-source overrides:

```json
{ "placements": null, "open_mode": null, "open_modes": null }
```

Programmatic panel actions (such as `$metrix.panel.open()` or `toggle()`) follow the effective default opening mode (`open_mode`), and keep the `{open}` response.

Source endpoints (`validate`, create, and source replacement) require an explicit `Content-Type`: `text/plain`, `application/octet-stream`, or `application/json` with `{ "source": "..." }`. Media type parameters such as charset are allowed. Missing or unsupported types return 415; malformed JSON returns 400; bodies over 2 MiB return 413.
