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

# API reference

> Request and response fields for the public worker endpoints: boot, chat, browser capture context, consent, and voice.

The public worker surface is small on purpose. Your install script and the SDK call it for you, but the request and response shapes are documented here so you can debug traffic, build server-to-server tooling, or reason about what the guide sends.

<Info>
  All public endpoints are served from `https://worker.puntego.com`. Paths below are relative to that base URL.
</Info>

## Authentication

`POST /boot` is the only unauthenticated endpoint. It mints a short-lived visitor JWT (roughly a 15-minute TTL) that every other public endpoint requires as a bearer token:

```http theme={null}
Authorization: Bearer <visitor-jwt>
```

For the full token flow, expiry, and refresh behavior, see [Authentication](/authentication).

<Note>
  This page documents the public subset of the worker. There is a larger internal surface (ingestion, retrieval, events, billing, and dashboard APIs) that is not exhaustively listed here.
</Note>

## Boot

`POST /boot`

Resolves the workspace configuration, the consent directive for the visitor's region, and a fresh visitor JWT. No `Authorization` header is required — this is where the token is issued. Returns `503` when the global killswitch is on, and never returns `401`.

### Request

<ParamField path="app_id" type="string" required>
  Your app ID (the `gp_` token from your install script).
</ParamField>

<ParamField path="origin" type="string" required>
  The page origin requesting boot. It must match the request `Origin` header and an allowed domain for the app, or boot is refused with a `403`.
</ParamField>

<ParamField path="path" type="string" required>
  The current page path, used to record the boot and resolve mounting.
</ParamField>

<ParamField path="visitor_id" type="string">
  A previously issued visitor ID to continue an existing visitor session. Omit it to mint a new one.
</ParamField>

<ParamField path="gpc" type="boolean">
  Set when the browser signals Global Privacy Control. GPC opt-out is always honored.
</ParamField>

<ParamField path="user_agent_hint" type="string">
  A user-agent hint used for bot filtering when the request lacks a `User-Agent` header.
</ParamField>

### Response

<ResponseField name="jwt" type="string | null">
  The visitor JWT to send as a bearer token on every other endpoint.
</ResponseField>

<ResponseField name="jwt_expires_at" type="number">
  Token expiry as a Unix timestamp in seconds.
</ResponseField>

<ResponseField name="visitor_id" type="string">
  The visitor ID for this session. Persist it and replay it on the next boot.
</ResponseField>

<ResponseField name="config" type="object">
  The resolved workspace configuration, including the active consent settings.
</ResponseField>

<ResponseField name="consent_directive" type="object">
  The directive computed for this visitor: `mode`, `policy_version`, `reason`, `region_code`, and `ai_disclosure`.
</ResponseField>

<ResponseField name="region" type="object">
  The resolved location: `country_code`, `region_code`, and `accept_language`.
</ResponseField>

<ResponseField name="features" type="object">
  Runtime feature flags for the session.
</ResponseField>

<ResponseField name="show_branding" type="boolean">
  Whether the guide shows the Puntego mark for this workspace.
</ResponseField>

## Chat

`POST /chat`

The conversation turn. Requires `Authorization: Bearer <visitor-jwt>`. It enforces visitor and workspace rate limits (returning `429` with a `Retry-After` header when exceeded), and when a production rollout list is configured, only enabled workspaces are served.

The response is a Server-Sent Events stream (`Content-Type: text/event-stream`). It streams the assistant's answer in chunks, interleaved with structured metadata events (such as proposed guide actions, citations, and suggested next steps).

### Request

<ParamField path="message" type="string" required>
  The visitor's message. Must be non-empty.
</ParamField>

<ParamField path="dom_digest" type="string" required>
  A compact, sanitized digest of the current page the guide can act on. Must be non-empty.
</ParamField>

<ParamField path="conversation_id" type="string" required>
  The conversation this turn belongs to. The worker may return an effective ID on the stream if it rotates yours.
</ParamField>

<ParamField path="turn_idx" type="number" required>
  The zero-based index of this turn within the conversation.
</ParamField>

<ParamField path="url" type="string" required>
  The full URL of the page. Its origin must match the visitor session.
</ParamField>

<ParamField path="viewport" type="object">
  Viewport size as `{ width, height, dpr }`. Defaults are used when omitted.
</ParamField>

<ParamField path="document" type="object">
  Document size as `{ width, height }`. Defaults are used when omitted.
</ParamField>

<ParamField path="scroll_pos" type="number">
  The current vertical scroll position.
</ParamField>

<ParamField path="capture_context" type="object">
  Structured browser context (see below). Sanitized by the worker before prompt assembly.
</ParamField>

<ParamField path="viewport_screenshot" type="object">
  An optional JPEG capture of the viewport as `{ data, mime, width, height }`, where `data` is raw base64 and `mime` is `image/jpeg`. Oversized or malformed captures are dropped.
</ParamField>

### Browser capture context

When present, `capture_context` gives the guide structured web context without granting any arbitrary browser control — the worker sanitizes it before it reaches the model. It carries:

<ParamField path="page" type="object">
  `{ title, url }` for the page.
</ParamField>

<ParamField path="viewport" type="object">
  `{ width, height, dpr }`.
</ParamField>

<ParamField path="document" type="object">
  `{ width, height }`.
</ParamField>

<ParamField path="scroll" type="object">
  `{ x, y }` scroll offset.
</ParamField>

<ParamField path="selected_text" type="string">
  Text the visitor has selected, when any.
</ParamField>

<ParamField path="focused_target" type="object">
  The currently focused candidate, in the same shape as a `targets` entry.
</ParamField>

<ParamField path="targets" type="array">
  Ranked DOM-backed candidates the guide can point at, each with a selector, a stable `gp_id`, viewport and document bounding boxes, a confidence score, and accessibility hints.
</ParamField>

## Consent

`POST /consent`

Records the visitor's consent decision together with the region, policy version, and override source. Requires `Authorization: Bearer <visitor-jwt>`.

### Request

<ParamField path="visitor_id" type="string" required>
  The visitor the decision applies to.
</ParamField>

<ParamField path="url" type="string" required>
  The page where the decision was made; its origin must match the visitor session.
</ParamField>

<ParamField path="copy_version" type="string" required>
  The version of the consent copy the visitor saw.
</ParamField>

<ParamField path="at" type="number" required>
  When the decision was made, as a Unix timestamp in milliseconds.
</ParamField>

<ParamField path="decision" type="string" required>
  One of `accepted`, `denied`, `rejected`, `dismissed`, `browser-denied`, or `gpc_auto_optout`.
</ParamField>

<ParamField path="mode" type="string">
  The consent mode in effect at decision time.
</ParamField>

<ParamField path="gpc" type="boolean">
  Whether Global Privacy Control was active.
</ParamField>

<ParamField path="policy_version" type="string">
  The policy version recorded with the decision.
</ParamField>

<ParamField path="region" type="object">
  An explicit region override as `{ country_code, region_code }`.
</ParamField>

<ParamField path="override_source" type="string">
  Where the decision came from: `runtime`, `gpc`, or `region_override`.
</ParamField>

### Response

<ResponseField name="ok" type="boolean">
  Always `true` for a well-formed request.
</ResponseField>

<ResponseField name="recorded" type="boolean">
  `true` when a valid visitor session let the decision be persisted.
</ResponseField>

## Voice

Voice is optional per workspace. All three endpoints require `Authorization: Bearer <visitor-jwt>` and respect the workspace voice cap. When voice provider credentials are absent, the worker falls back to an internal mock voice.

### Mint a speech token

`GET /transcribe-token`

Mints a visitor-scoped realtime speech token (or a mock token when voice is not configured).

<ParamField path="provider" type="string" query>
  Optional provider preference: `elevenlabs`, `openai`, `openai-batch`, `mock`, or `auto` (the default).
</ParamField>

<ResponseField name="token" type="string">
  The provider token the browser uses to open the realtime session.
</ResponseField>

<ResponseField name="provider" type="string">
  The provider that issued the token: `elevenlabs`, `openai`, or `mock`.
</ResponseField>

<ResponseField name="expires_at" type="number">
  Token expiry as a Unix timestamp.
</ResponseField>

<ResponseField name="mode" type="string">
  `realtime`, `batch`, or `mock`.
</ResponseField>

<ResponseField name="transport" type="string">
  How to connect: `webrtc`, `websocket`, `upload`, or `mock`.
</ResponseField>

<ResponseField name="reserved_seconds" type="number">
  Seconds reserved against the voice cap at mint time. Realtime sessions reconcile actual elapsed time against this baseline.
</ResponseField>

### Transcribe audio

`POST /transcribe`

Accepts browser-recorded audio as `multipart/form-data` and returns a transcript. Returns `503` when transcription is not configured and `413` when the audio exceeds the size limit.

<ParamField path="audio" type="file" required>
  The recorded audio blob, sent as a multipart form field.
</ParamField>

<ResponseField name="text" type="string">
  The transcribed visitor speech.
</ResponseField>

<ResponseField name="model" type="string">
  The transcription model that produced the text.
</ResponseField>

### Synthesize speech

`POST /tts`

Synthesizes speech from text and streams the audio back. The response body is the audio stream; the served provider is reported in the `x-puntego-tts-provider` response header.

<ParamField path="text" type="string" required>
  The text to synthesize. Must be non-empty and within the length limit.
</ParamField>

<ParamField path="voice_id" type="string">
  A specific voice to use. Falls back to the workspace voice when omitted.
</ParamField>

<ParamField path="provider" type="string">
  Provider preference: `elevenlabs`, `openai`, `cartesia`, `mock`, or `auto` (the default).
</ParamField>

## Dashboard APIs

The dashboard's workspace APIs (under `/api/tenants/:tenantId/`) are not part of this public bearer-token surface. They are protected by session cookies and are organization-scoped, so middleware blocks cross-workspace access. Use the dashboard UI for these; they are not intended for direct integration.

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/authentication">
    How the visitor JWT is minted, scoped, and refreshed.
  </Card>

  <Card title="Errors" icon="triangle-alert" href="/errors">
    The error envelope and what each status code means.
  </Card>

  <Card title="Rate limits" icon="gauge" href="/rate-limits">
    Per-visitor and per-workspace limits and the Retry-After header.
  </Card>

  <Card title="Verified Actions" icon="shield-check" href="/verified-actions">
    The customer-approved tools the guide can run.
  </Card>
</CardGroup>
