Viewport daemon, relay, and hosted runtime are available in alpha. Surfaces may change.
VIEWPORT
ReferenceAPI reference

API

The generated REST reference, the WebSocket protocol, and the auth model that ties them together.

Viewport exposes two protocol surfaces:

  • REST at https://api.getviewport.com for control-plane reads and writes (workspaces, machines, pairing, runs, receipts, plans, inbox, audit, vault, workflows).
  • WebSocket for both the daemon's local control surface and the relay-mediated transport between daemons and browser clients.

The REST reference

The pages in this section are generated from the platform's OpenAPI 3.1 document (viewportai/platform: apps/api/openapi.json) — the same spec the platform's contract tests run against. One page per resource group; use the sidebar or jump to the busiest groups:

Honesty note on coverage. The spec is contract-tested but unevenly hand-tightened: operations marked generated contract stub are real, served routes whose request/response schemas are still placeholders (GenericApiResponse). The per-page banner tells you how much of each group is stubbed. Treat non-stub schemas as reliable; treat stubs as "the route exists, verify the shape against a live response".

The snapshot this reference was generated from is committed at openapi/openapi.json, with its digests in openapi/openapi.meta.json. CI fails when the published reference lags the platform's spec — see "Keeping this current" below.

Auth model

Two principals talk to the platform: human users via the web app, and daemons via a per-install issue token.

Browser sessions

The server API uses WorkOS AuthKit for SSO/login. In the current hosted app, the callback returns a bearer token to the browser app, and authenticated REST routes accept that token. This is an alpha implementation detail and may move to a cookie-backed session before GA.

Daemon → platform

Daemons pair via the pairing flow and receive a per-install issue token (bcrypt-hashed in installs.daemon_issue_token_hash). The daemon presents this token to runtime endpoints (/api/runtime/*, throttled). The token grants a relay JWT via POST /api/runtime/relay-token, which the daemon then presents on its WebSocket upgrade to the relay.

Personal access tokens

Users can mint personal API tokens at /settings/api-tokens:

MethodPathPurpose
GET/api/me/api-tokensList
POST/api/me/api-tokensCreate (returns plaintext once)
DELETE/api/me/api-tokens/{tokenId}Revoke

These tokens authorize the same API surface as a browser session.

Conventions

  • {workspace} is a ULID; /api/resources/{workspace}/... is an alias for /api/workspaces/{workspace}/... — the Resources group mirrors workspace-scoped routes.
  • Authentication is the browser/user token (sanctum) unless a route is under /api/runtime/* (daemon issue token / JWT) or explicitly public (/api/health, /api/.well-known/*, webhook ingestion).

WebSocket protocol

The daemon ↔ client wire shapes live in the open-source runtime code as Zod schemas: viewport/packages/daemon/src/server/ws-protocol.ts (IncomingMessageSchema, a discriminated union on type: launch, kill, prompt, respond-permission, subscribe, workflow-run, workflow-approve, and the rest). Outgoing frames are ack, session-update, session-started/session-ended, discovered-session events, workflow-run-updated, and the Claude Code hook bridge (hook-*). A CI job catches drift between the daemon and web client copies (npm run check:protocol-matrix in the daemon package, reference viewport/packages/daemon/docs/protocol-matrix.json).

Relay WS upgrade

GET wss://relay.getviewport.com/ws
    ?role=workspace-daemon|client
    &workspaceId=ULID
    &runtimeTargetId=ULID           # optional, scopes to one machine

Auth: a JWT in Authorization: Bearer …, the Sec-WebSocket-Protocol subprotocol header, or ?token=. The relay calls POST /api/runtime/internal/relay/validate to admit the upgrade.

The relay does not decrypt frames. It identifies envelopes (workspace, runtime target) and routes daemon ↔ client. Payload encryption is handled by the daemon/browser relay session inside the envelope.

Pairing flow (end-to-end)

  ┌──────────┐    1. POST /api/workspaces/{ws}/pairing-codes      ┌────────────┐
  │ Browser  ├────────────────────────────────────────────────────►            │
  │          │◄───────────────────────── code ─────────────────────│ Platform  │
  └────┬─────┘                                                     │  API      │
       │  2. shows code                                            └─────┬──────┘
       │                                                                 │
  ┌────▼────┐    3. POST /api/pairing-codes/{code}/claim                 │
  │ Daemon  ├─────────────────────────────────────────────────────────────►
  │ vpd pair│◄──────────────────── status_token ──────────────────────────
  │  <code> │
  │         │    4. GET /api/pairing-codes/{code}/status (poll, with token)
  │         ├─────────────────────────────────────────────────────────────►
  └─────────┘
       ▲                                                                 │
       │              5. User clicks Approve in browser                  │
       │             ┌───────────────────────────────────────────────────┘
       │             │  POST /api/pairing-codes/{code}/approve
       │             ▼
       │       The platform binds machine → workspace, retires any
       │       prior install for that pair, and issues a fresh relay
       │       credential for the daemon (token hash stored, plaintext
       │       returned once in the approve response).

       │  6. status returns approved + relay_credentials
       └─── 7. daemon writes config, restartDaemon() ────► connects to relay

Same flow for daemon-initiated pairing, just swap steps 1 and 3.

Keeping this current

The platform repo owns the spec. This repo commits a snapshot and generates the reference pages from it:

# in the docs repo, with the platform checkout as a sibling directory:
npm run api:sync        # copy platform/apps/api/openapi.json + regenerate pages
npm run api:generate    # regenerate pages from the committed snapshot only
npm run api:check       # what CI runs — fails if pages lag the snapshot

CI enforces two layers: the committed pages must match the committed snapshot (always), and the snapshot's git blob SHA must match viewportai/platform:apps/api/openapi.json on main (when the PLATFORM_READ_TOKEN secret is available to the workflow).

What's NOT here today

PlannedThere is no published SDK or client package for this API — no `@viewportai/protocol` on npm for external use, no generated clients. The reference above is documentation, not an SDK; integrate with plain HTTP. The internal Bruno/Postman collections are engineering handoff artifacts, not a supported product.
  • Sessions for what session-update frames mean
  • CLI for the surface that consumes the local-daemon WS
  • Self-host for running your own relay

On this page