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

# Session tokens

> The short-lived, agent-scoped credential your backend mints so an end user's browser can talk to a Karta agent - without ever holding an API key.

A **session token** is how an end user's browser reaches a Karta agent safely.
A `kt_live_...` key is an org-wide management credential and must never ship to a
client. Instead, your backend mints a short-lived, agent-scoped signed token per
authenticated end user, and the widget presents *that*.

This is the standard "ephemeral client secret" pattern: the long-lived secret
stays on your server; a narrow, expiring token reaches the browser.

## The flow

```
end user
  | login
  v
your backend
  | kt_live_ key with agents:write scope
  v
POST /api/agents/:slug/session_tokens
  | returns short-lived session token
  v
browser widget
  | session token
  v
/{org}/{agent}/v1/managed-agents/... or OpenAI adapters
```

<Steps>
  <Step title="Your backend mints a token">
    After your own auth confirms the user, call the mint endpoint with your
    `kt_live_...` key that has the `agents:write` scope. You get back a signed
    token scoped to one org + agent, with an `expires_in` value. If the org or
    verified end user is over budget, the mint endpoint returns
    [`402`](/api-reference/errors) instead of a token.
  </Step>

  <Step title="The widget uses it">
    The browser presents the session token to the agent's **consumer adapter**
    routes - the [Managed Agents API](/api-reference/adapters/managed-agents) the
    chat widget speaks, and the OpenAI-compatible
    [Responses and Chat Completions](/api-reference/adapters/overview) adapters.
    It does **not** work on the native session routes (`/v1/sessions`,
    `/{org}/{agent}/v1/sessions`) - those are your backend's surface and require a
    `kt_live_` API key.
  </Step>

  <Step title="Karta verifies it offline">
    Karta verifies the token's signature, scope, and expiry - without a network
    round-trip.
  </Step>
</Steps>

## Mint response

| Field        | Meaning                                                                                                  |
| ------------ | -------------------------------------------------------------------------------------------------------- |
| `token`      | Bearer token for the browser runtime paths.                                                              |
| `token_type` | Always `Bearer`.                                                                                         |
| `expires_in` | Token lifetime in seconds after Karta clamps the requested TTL.                                          |
| `agent`      | The agent id and slug the token can reach.                                                               |
| `identity`   | `{ verified: true, sub }` when your backend asserted `sub` / `user_id`; otherwise `{ verified: false }`. |

## TTL bounds

| Setting     | Value          |
| ----------- | -------------- |
| Default TTL | `900` seconds  |
| Minimum TTL | `60` seconds   |
| Maximum TTL | `3600` seconds |

If you pass `ttl_seconds`, Karta clamps it into that range.

## What the token pins

The minted token pins `org_id`, `agent_id`, `agent_slug`, `scope`, and `exp`.
Because org and agent are pinned, **cross-tenant** access is blocked: a token
for one agent cannot touch another.

When the widget mints from an embed key with [verified identity](/sdks/widget/identity),
the token also carries `sub`, the server-verified subject for your signed-in
user. If your server provides a signed step-up identity token, the session token
can also carry recent `stepped_up_at` / `aal` claims for sensitive approvals.

<Info>
  **Keep the TTL short.** A session token is scoped to one org + agent and
  expires after a 900-second default. Without verified identity it is not bound
  to an individual end user; with verified identity it is bound to the signed `sub`.
  Mint a fresh token per user session so a leak is contained to that token's
  lifetime.
</Info>

## Use session tokens in browsers

<Warning>
  A `kt_live_...` key is a server credential. Depending on scopes, it can manage
  keys, BYOK, budgets, and deploys. Putting one in a browser or mobile app
  exposes server-side authority. The session token exists precisely so the
  browser never needs the key: it is minted server-side, scoped to a single agent,
  and expires in minutes.
</Warning>

See [Trust](/security/trust) for where this sits in the full trust model, and
[API keys](/platform/api-keys) for the credential it replaces at the edge.
