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

# LiveKit token

> Generates a LiveKit JWT token for a participant to join a specific room.

When authentication is enabled (`auth.id` is present), this handler:
1. Creates the room if it doesn't exist
2. Sets `room.metadata.auth_id` to the authenticated tenant's ID
3. Issues the token only after metadata is verified/set

# Arguments
* `state` - Shared application state containing LiveKit configuration
* `request` - Token request with room name and participant details

# Returns
* `Response` - JSON response with token or error status

# Errors
* 400 Bad Request - Invalid request data (empty fields)
* 403 Forbidden - Room exists with a different tenant's `auth_id`
* 500 Internal Server Error - LiveKit service not configured, room creation failed, or token generation failed

Generates a LiveKit JWT token for a participant to join a specific room. When authentication is enabled, this endpoint also handles room creation and ownership.

## Room creation behavior

When `auth.id` is present (authenticated mode):

1. If the room doesn't exist, Sayna creates it automatically.
2. The room's `metadata.auth_id` is set to your tenant ID.
3. The token is issued only after ownership is verified or established.

<Note>
  Room names are used exactly as provided. No prefixing or modification is applied.
</Note>

## Authorization

| Scenario                     | Behavior                                                       |
| ---------------------------- | -------------------------------------------------------------- |
| Room doesn't exist           | Room is created with `metadata.auth_id` set to your tenant ID. |
| Room exists, you own it      | Token is issued normally.                                      |
| Room exists, different owner | `403 Forbidden` is returned.                                   |
| Unauthenticated mode         | Token is issued without ownership checks.                      |

## Error responses

| Status                      | Condition                                          |
| --------------------------- | -------------------------------------------------- |
| `400 Bad Request`           | Missing or empty required fields.                  |
| `403 Forbidden`             | Room exists with a different tenant's `auth_id`.   |
| `500 Internal Server Error` | LiveKit not configured or token generation failed. |

<Warning>
  If you receive a `403`, the room already exists and is owned by another tenant. Choose a different room name rather than retrying with the same one.
</Warning>


## OpenAPI

````yaml POST /livekit/token
openapi: 3.1.0
info:
  title: Sayna API
  description: >-
    Real-time voice processing server with Speech-to-Text (STT) and
    Text-to-Speech (TTS) services
  contact:
    name: Sayna
    url: https://api.sayna.ai
  license:
    name: Apache-2.0
    identifier: Apache-2.0
  version: 0.1.0
servers:
  - url: https://api.sayna.ai
    description: Production API
  - url: http://localhost:3001
    description: Local development
security: []
tags:
  - name: health
    description: Health check endpoints
  - name: voices
    description: TTS voice management
  - name: tts
    description: Text-to-speech synthesis
  - name: livekit
    description: LiveKit room and token management
  - name: recordings
    description: Recording download operations
  - name: sip
    description: SIP webhook configuration management
  - name: websocket
    description: WebSocket API for real-time communication
paths:
  /livekit/token:
    post:
      tags:
        - livekit
      summary: Handler for POST /livekit/token endpoint
      description: >-
        Generates a LiveKit JWT token for a participant to join a specific room.


        When authentication is enabled (`auth.id` is present), this handler:

        1. Creates the room if it doesn't exist

        2. Sets `room.metadata.auth_id` to the authenticated tenant's ID

        3. Issues the token only after metadata is verified/set


        # Arguments

        * `state` - Shared application state containing LiveKit configuration

        * `request` - Token request with room name and participant details


        # Returns

        * `Response` - JSON response with token or error status


        # Errors

        * 400 Bad Request - Invalid request data (empty fields)

        * 403 Forbidden - Room exists with a different tenant's `auth_id`

        * 500 Internal Server Error - LiveKit service not configured, room
        creation failed, or token generation failed
      operationId: generate_token
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
        required: true
      responses:
        '200':
          description: >-
            Token generated successfully. Room is created if it doesn't exist
            and metadata.auth_id is set.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          description: Invalid request (missing or empty fields)
        '403':
          description: 'Access denied: room exists with a different tenant''s auth_id'
        '500':
          description: >-
            LiveKit service not configured, room creation failed, or token
            generation failed
      security:
        - auth: []
components:
  schemas:
    TokenRequest:
      type: object
      description: |-
        Request body for generating a LiveKit token

        # Example
        ```json
        {
          "room_name": "conversation-room-123",
          "participant_name": "Alice Smith",
          "participant_identity": "user-alice-456"
        }
        ```
      required:
        - room_name
        - participant_name
        - participant_identity
      properties:
        participant_identity:
          type: string
          description: Unique identifier for the participant (e.g., "user-123")
          example: user-alice-456
        participant_name:
          type: string
          description: Display name for the participant (e.g., "John Doe")
          example: Alice Smith
        room_name:
          type: string
          description: The LiveKit room name to generate a token for
          example: conversation-room-123
    TokenResponse:
      type: object
      description: |-
        Response containing the generated LiveKit token

        # Example
        ```json
        {
          "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
          "room_name": "conversation-room-123",
          "participant_identity": "user-alice-456",
          "livekit_url": "ws://localhost:7880"
        }
        ```
      required:
        - token
        - room_name
        - participant_identity
        - livekit_url
      properties:
        livekit_url:
          type: string
          description: The LiveKit server URL to connect to
          example: ws://localhost:7880
        participant_identity:
          type: string
          description: Echo back the participant identity for client confirmation
          example: user-alice-456
        room_name:
          type: string
          description: Echo back the room name for client confirmation
          example: conversation-room-123
        token:
          type: string
          description: The generated JWT token for LiveKit
          example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
  securitySchemes:
    auth:
      type: http
      scheme: bearer
      bearerFormat: Token
      description: >-
        Authentication token for protected endpoints. Can be provided as
        `Authorization: Bearer <token>` or `?api_key=<token>`. Required when
        AUTH_REQUIRED is enabled.

````