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

# Token Exchange

> Exchange a long-lived service key for a short-lived signed token.

`POST /v1/auth/token`

Exchange a long-lived service key for a short-lived `GatelitSigned` token. The gateway verifies the service key, signs a token with its internal secret, and returns it. No raw signing secret is ever exposed to your backend.

Use this to issue per-user tokens from your backend — authenticate with a service key, provide the user's identity and optional constraints, and receive a token your frontend can use directly with any other gateway endpoint.

## Auth

Only service keys are accepted at this endpoint. Signed tokens and OIDC are rejected.

```http theme={null}
Authorization: GatelitKey glk_...
```

## Request body

| Field           | Type      | Required | Default | Description                                                |
| --------------- | --------- | -------- | ------- | ---------------------------------------------------------- |
| `sub`           | `string`  | **Yes**  | —       | Subject identifier — typically the end user's ID           |
| `ttl`           | `integer` | No       | `60`    | Token lifetime in seconds (max `3600` / 1 hour)            |
| `model`         | `string`  | No       | —       | Restrict the token to one model in `provider/model` format |
| `maxTokens`     | `integer` | No       | —       | Cap output tokens per request for this token               |
| `endUserId`     | `string`  | No       | —       | End-user identifier for log filtering                      |
| `requestSource` | `string`  | No       | `"api"` | `"api"` or `"eval"` — tags requests in logs                |

The issued token inherits the `orgId` from the service key used to authenticate the exchange. This enables org-scoped provider key resolution for the downstream requests.

## Response

```json theme={null}
{
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "scheme": "GatelitSigned"
}
```

Use the returned `token` with the `GatelitSigned` scheme on all other gateway endpoints:

```http theme={null}
Authorization: GatelitSigned eyJhbGciOiJIUzI1NiJ9...
```

## Examples

### curl — minimal

```bash theme={null}
curl https://gateway.gatelit.dev/v1/auth/token \
  -H "Content-Type: application/json" \
  -H "Authorization: GatelitKey glk_..." \
  -d '{
    "sub": "user_123",
    "ttl": 60
  }'
```

### curl — with model constraint and token cap

```bash theme={null}
curl https://gateway.gatelit.dev/v1/auth/token \
  -H "Content-Type: application/json" \
  -H "Authorization: GatelitKey glk_..." \
  -d '{
    "sub": "user_123",
    "model": "openai/gpt-4o",
    "maxTokens": 2000,
    "ttl": 300,
    "endUserId": "customer_789",
    "requestSource": "api"
  }'
```

### SDK — exchangeToken()

```ts theme={null}
import { exchangeToken } from "@gatelit/sdk"

const { token } = await exchangeToken({
  gatewayUrl: "https://gateway.gatelit.dev",
  serviceKey: process.env.GATELIT_SERVICE_KEY!,
  sub: session.userId,
  model: "openai/gpt-4o",
  maxTokens: 2000,
  ttl: 60,
})
// → { token: "eyJ...", scheme: "GatelitSigned" }
```

## Errors

| Status | Code            | Meaning                                                   |
| ------ | --------------- | --------------------------------------------------------- |
| 400    | `gateway_error` | Missing required `sub` field or invalid JSON              |
| 401    | `unauthorized`  | Invalid service key or wrong auth scheme (not GatelitKey) |


## OpenAPI

````yaml post /v1/auth/token
openapi: 3.1.0
info:
  title: Gatelit Gateway API
  version: '1.0'
  description: |
    The Gatelit gateway is a single HTTP endpoint that routes AI requests to any
    supported provider. It handles authentication, provider key resolution,
    request transformation, stream normalisation, spend tracking, and logging.

    All requests must include an `Authorization` header. Three auth schemes are
    supported — choose based on your deployment scenario.
servers:
  - url: https://gateway.gatelit.dev
    description: Your gateway URL
security: []
paths:
  /v1/auth/token:
    post:
      summary: Exchange a service key for a short-lived signed token
      description: >
        Exchange a long-lived service key for a short-lived `GatelitSigned`
        token.

        The gateway verifies the service key, signs a token with its internal

        secret, and returns it. No raw signing secret is ever exposed to your
        backend.


        Use this to issue per-user tokens from your backend — authenticate with
        a

        service key, provide the user's identity and optional constraints, and

        receive a token your frontend can use directly.
      operationId: exchangeToken
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - sub
              properties:
                sub:
                  type: string
                  description: Subject identifier — typically the end user's ID.
                ttl:
                  type: integer
                  default: 60
                  minimum: 1
                  maximum: 3600
                  description: Token lifetime in seconds (max 1 hour).
                model:
                  type: string
                  description: Restrict to one model in `provider/model` format.
                maxTokens:
                  type: integer
                  description: Cap output tokens per request.
                endUserId:
                  type: string
                  description: End-user identifier for log filtering.
                requestSource:
                  type: string
                  enum:
                    - api
                    - eval
                  default: api
      responses:
        '200':
          description: Signed token issued successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
                    description: The signed token for use with `GatelitSigned` auth.
                  scheme:
                    type: string
                    enum:
                      - GatelitSigned
              example:
                token: eyJhbGciOiJIUzI1NiJ9...
                scheme: GatelitSigned
        '401':
          description: Invalid or missing service key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GatelitError'
              example:
                error: true
                code: unauthorized
                message: Invalid service key
      security:
        - GatelitKey: []
      x-codeSamples:
        - lang: ts
          label: SDK — exchangeToken()
          source: |
            import { exchangeToken } from "@gatelit/sdk"

            const { token } = await exchangeToken({
              gatewayUrl: "https://gateway.gatelit.dev",
              serviceKey: process.env.GATELIT_SERVICE_KEY!,
              sub: "user_123",
              model: "openai/gpt-4o",
              ttl: 60,
            })
            // → { token: "eyJ...", scheme: "GatelitSigned" }
        - lang: curl
          label: curl
          source: |
            curl https://gateway.gatelit.dev/v1/auth/token \
              -H "Content-Type: application/json" \
              -H "Authorization: GatelitKey glk_..." \
              -d '{
                "sub": "user_123",
                "ttl": 60
              }'
components:
  schemas:
    GatelitError:
      type: object
      required:
        - error
        - code
        - message
      properties:
        error:
          type: boolean
          enum:
            - true
        code:
          type: string
          enum:
            - unauthorized
            - gateway_error
            - provider_error
            - forbidden
            - policy_violation
            - rate_limit_exceeded
            - model_not_allowed
            - token_limit_exceeded
        message:
          type: string
  securitySchemes:
    GatelitKey:
      type: apiKey
      in: header
      name: Authorization
      description: |
        Long-lived service key for backend-to-backend use.
        Format: `GatelitKey glk_...`

        Create service keys in the dashboard under Settings → Service Keys.

````