> ## 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.

# Authentication

> How Puntego authenticates the embedded guide, dashboard APIs, and programmatic exports — visitor JWTs, Better Auth sessions, and scoped API keys.

Puntego uses three credentials, each scoped to exactly what it needs. The embedded guide on your site runs on a short-lived visitor JWT, your dashboard and tenant APIs run on a signed-in session, and read-only data exports run on a scoped API key. Nothing in the browser ever holds a credential that can change your account or read another workspace.

| Surface                                                | Credential                                   | Scope                                              |
| ------------------------------------------------------ | -------------------------------------------- | -------------------------------------------------- |
| The guide on your site (`/chat` and friends)           | Visitor JWT, sent as `Authorization: Bearer` | One visitor, one workspace, one origin             |
| Dashboard and tenant APIs (`/api/tenants/:tenantId/*`) | Better Auth session cookie                   | The signed-in member, scoped to their organization |
| Programmatic data export (`/api/export/*`)             | Scoped API key (`pk_live_`)                  | Read-only analytics and events                     |

## The visitor JWT

The guide never ships with a long-lived secret. When the boot script loads, the browser calls `POST /boot` with your app ID (the `gp_` token) from a page on a verified domain. The worker checks the origin against your allowlisted domains and, only then, mints a visitor JWT and returns it in the boot response.

The token is an HS256-signed JWT carrying the visitor ID, the workspace it was minted for, and the exact origin it was issued to. It has roughly a 15-minute TTL, so even if a token were captured it expires on its own and is bound to a single origin. The runtime refreshes it transparently by re-booting before it lapses — you never manage this yourself.

<Info>
  `POST /boot` authenticates by **app ID plus verified origin**, so it never returns `401`. An unrecognized origin or a disabled workspace is a `403`, and the global killswitch is a `503`. See the full mapping in [Error reference](/errors).
</Info>

Once the browser has a token, every guide request that needs identity sends it as a bearer token:

```http theme={null}
POST /chat HTTP/1.1
Host: worker.puntego.com
Authorization: Bearer <visitor-jwt>
Content-Type: application/json
```

`POST /chat` validates the signature and expiry on every turn. A missing or invalid token is `chat/unauthorized` (`401`), and an expired token returns `jwt_expired` (`401`) — the runtime treats that as its cue to re-boot. On top of authentication, `/chat` enforces per-visitor and per-workspace rate limits; see [Rate limits](/rate-limits).

### The boot-then-chat handshake

```mermaid theme={null}
sequenceDiagram
    participant B as Browser (boot script)
    participant W as Worker (worker.puntego.com)
    B->>W: POST /boot (app ID + Origin)
    Note over W: Verify origin against allowlisted domains
    W-->>B: 200 + visitor JWT (~15 min TTL)
    B->>W: POST /chat (Authorization: Bearer <jwt>)
    Note over W: Verify signature + expiry, apply rate limits
    W-->>B: 200 streamed reply
    Note over B,W: Before the JWT lapses, the runtime re-boots for a fresh token
```

<Note>
  The worker base URL is `https://worker.puntego.com`. The boot script must pass `data-gp-api` pointing at it — without that attribute the worker is unreachable and no token is ever minted. See the [install reference](/install).
</Note>

## Dashboard and tenant APIs

Your dashboard, and the tenant APIs it calls under `/api/tenants/:tenantId/*`, authenticate with a Better Auth session cookie established when you sign in at the [Puntego dashboard](https://puntego.com/sign-in). These APIs are organization-scoped: middleware ties the session to the member's organization and blocks any attempt to read or write another workspace's tenant ID. This is the only credential class that can change configuration — it lives in your dashboard session, never in the browser embed.

## Programmatic API keys

For pulling your own data into a warehouse or BI tool, create a scoped API key in your workspace dashboard. Keys are prefixed `pk_live_`, generated once, and stored only as a hash — the full secret is shown a single time at creation and never again.

API keys are deliberately read-only. They carry one or both of two scopes:

<ParamField path="analytics:read" type="scope">
  Read aggregated analytics for your workspace.
</ParamField>

<ParamField path="events:read" type="scope">
  Read raw events, conversations, and transcripts for your workspace.
</ParamField>

They authenticate the export endpoints under `/api/export/*` and nothing else — there is no chat, write, or configuration scope, so a leaked key cannot drive the guide or alter your account. Send the key in the `x-api-key` header (or as `Authorization: Bearer <pk_live_...>`):

```http theme={null}
GET /api/export/events HTTP/1.1
Host: worker.puntego.com
x-api-key: pk_live_your_key
```

A missing or invalid key returns `export/unauthorized` (`401`); a key without the required scope returns `export/insufficient_scope` (`403`). Export endpoints are rate limited per key — see [Rate limits](/rate-limits).

<Tip>
  Treat keys as least-privilege by default: grant only the scope a job needs, use a distinct key per integration, and revoke from the dashboard to rotate. Because keys are hashed at rest, rotation is the recovery path if one is ever exposed.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="API reference" icon="braces" href="/api-reference">
    Every public worker endpoint — boot, chat, consent, export, and voice.
  </Card>

  <Card title="Error reference" icon="circle-alert" href="/errors">
    The full status-code and error-namespace mapping, including the `/boot` 403 vs `/chat` 401 distinction.
  </Card>

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

  <Card title="Verified actions" icon="shield-check" href="/verified-actions">
    How customer-approved tools layer policy checks on top of the visitor session.
  </Card>
</CardGroup>
