openapi: 3.0.3
info:
  title: Liya AI — External API
  description: |
    Complete REST API reference for the Liya AI platform.

    This document covers two API families served from the same backend:

    ---

    ## 1 · OpenAI-Compatible API (`/v1/…`)
    Drop-in replacement for the OpenAI Chat Completions API.  
    Connect any tool that natively supports OpenAI (AnythingLLM, OpenClaw,
    LangChain, LlamaIndex, etc.) without code changes.

    ### Authentication
    ```
    Authorization: Bearer <your-liya-api-key>
    ```

    ### Base URL
    ```
    https://app-{X}-ai.liyalabs.com/api/v1/openai/v1
    ```
    Replace `{X}` with your project instance ID.

    > **Note:** The `/v1` suffix is required — clients such as AnythingLLM,
    > the OpenAI SDK, LangChain and LlamaIndex append `/chat/completions`
    > and `/models` to the base URL. This mirrors the official OpenAI
    > convention used by LM Studio, vLLM and llama.cpp.
    >
    > The legacy paths without `/v1`
    > (`/api/v1/openai/chat/completions`, `/api/v1/openai/models`) remain
    > available for backwards compatibility but are deprecated.

    ### Model field
    The `model` field maps to your **Liya AI assistant UUID**.
    Retrieve available assistant IDs with `GET /api/v1/openai/v1/models`.
    If omitted, the first active assistant of the project is used automatically.

    ### Supported endpoints
    | Method | Path | Description |
    |--------|------|-------------|
    | POST | `/api/v1/openai/v1/chat/completions` | Chat completion (OpenAI format) |
    | GET  | `/api/v1/openai/v1/models`           | List assistants as OpenAI models |
    | GET  | `/api/v1/openai`                     | Connectivity probe (no auth) |

    ---

    ## 2 · External API (`/api/v1/…`)
    Full-featured Liya AI API for custom integrations, widgets, mobile apps,
    the VS Code plugin, and third-party services.

    ### Authentication
    ```
    X-API-Key: <your-liya-api-key>
    ```
    Public endpoints (`/api/v1/public/…` and `/api/v1/external/mobile/config/`)
    do **not** require authentication.

    ### Base URL
    ```
    https://app-{X}-ai.liyalabs.com
    ```

    ---

    ## Getting your API Key
    Log in to [ai.liyalabs.com](https://ai.liyalabs.com), open your project
    settings, and copy the API key from the **Developer** tab.

  version: 0.2.0
  contact:
    name: Liya Labs Support
    url: https://liyalabs.com
    email: support@liyalabs.com

servers:
  - url: https://app-{X}-ai.liyalabs.com
    description: Replace {X} with your project instance ID.
    variables:
      X:
        default: "1"
        description: Project instance number

security:
  - ApiKeyHeader: []

components:
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
      description: Used for all `/api/v1/external/…` endpoints.
    BearerAuth:
      type: http
      scheme: bearer
      description: Used for OpenAI-compatible `/v1/…` endpoints.

  schemas:
    # ── Shared ────────────────────────────────────────────────────────────────

    ErrorResponse:
      type: object
      properties:
        status:
          type: string
          example: error
        message:
          type: string
          example: API Key required

    SuccessEnvelope:
      type: object
      properties:
        status:
          type: string
          example: success
        data:
          type: object

    # ── OpenAI compat ─────────────────────────────────────────────────────────

    OAIMessage:
      type: object
      required: [role, content]
      properties:
        role:
          type: string
          enum: [system, user, assistant]
        content:
          type: string
        name:
          type: string
          description: Optional sender name.

    OAIChatCompletionRequest:
      type: object
      required: [messages]
      properties:
        model:
          type: string
          description: |
            Liya AI assistant UUID (returned by `GET /api/v1/openai/models`).  
            If omitted, the project's first active assistant is used.
          example: 3fa85f64-5717-4562-b3fc-2c963f66afa6
        messages:
          type: array
          items:
            $ref: "#/components/schemas/OAIMessage"
        temperature:
          type: number
          minimum: 0
          maximum: 2
          default: 1.0
        top_p:
          type: number
          minimum: 0
          maximum: 1
        max_tokens:
          type: integer
          minimum: 1
          description: |
            Maximum tokens to generate. Accepted but not yet enforced.
            Liya AI, OpenAI uyumlu yapısı ile OpenLaw ve AnythingLLM gibi araçlarla kullanılabilir; yerel LLM entegrasyonu desteklenmektedir.
        stream:
          type: boolean
          default: false
          description: |
            Accepted but response is always returned as a complete object
            (SSE streaming is planned for a future release).
        n:
          type: integer
          default: 1
          description: Only `n=1` is supported.
        stop:
          type: array
          items:
            type: string
          maxItems: 4
        presence_penalty:
          type: number
          minimum: -2
          maximum: 2
        frequency_penalty:
          type: number
          minimum: -2
          maximum: 2
        user:
          type: string
          description: End-user identifier for abuse detection.

    OAIChatCompletionResponse:
      type: object
      properties:
        id:
          type: string
          example: chatcmpl-a1b2c3d4e5f6
        object:
          type: string
          example: chat.completion
        created:
          type: integer
          example: 1714000000
        model:
          type: string
          description: The assistant UUID that processed the request.
        choices:
          type: array
          items:
            type: object
            properties:
              index:
                type: integer
              message:
                $ref: "#/components/schemas/OAIMessage"
              finish_reason:
                type: string
                example: stop
              logprobs:
                type: object
                nullable: true
        usage:
          type: object
          properties:
            prompt_tokens:
              type: integer
            completion_tokens:
              type: integer
            total_tokens:
              type: integer
        system_fingerprint:
          type: string
          nullable: true

    OAIModel:
      type: object
      properties:
        id:
          type: string
          description: Assistant UUID (use this as the `model` field in chat completions).
        object:
          type: string
          example: model
        created:
          type: integer
        owned_by:
          type: string
          example: liya-ai
        display_name:
          type: string
        description:
          type: string

    OAIModelList:
      type: object
      properties:
        object:
          type: string
          example: list
        data:
          type: array
          items:
            $ref: "#/components/schemas/OAIModel"

    OAIError:
      type: object
      properties:
        error:
          type: object
          properties:
            message:
              type: string
            type:
              type: string
              example: invalid_request_error
            code:
              type: string
              example: invalid_api_key

    # ── Chat / Message ─────────────────────────────────────────────────────────

    ChatResponseData:
      type: object
      properties:
        session_id:
          type: string
          format: uuid
        message_id:
          type: string
          format: uuid
        response:
          type: string
          description: AI-generated reply text.
        response_time:
          type: number
          description: Processing time in seconds.
        suggestions:
          type: array
          items:
            type: string
        billing:
          type: object
          properties:
            charged:
              type: boolean
            cost_usd:
              type: number

    # ── Session ────────────────────────────────────────────────────────────────

    SessionObject:
      type: object
      properties:
        id:
          type: string
          format: uuid
        assistant_id:
          type: string
          format: uuid
        assistant_name:
          type: string
        session_name:
          type: string
        message_count:
          type: integer
        created_at:
          type: string
          format: date-time
        last_message_at:
          type: string
          format: date-time
          nullable: true

    MessageObject:
      type: object
      properties:
        id:
          type: string
          format: uuid
        content:
          type: string
        role:
          type: string
          enum: [user, assistant]
        created_at:
          type: string
          format: date-time

    # ── Assistant ──────────────────────────────────────────────────────────────

    AssistantObject:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        assistant_type:
          type: string
          example: coding_agent
        model:
          type: string
          example: claude-3-5-sonnet
        model_display:
          type: string
        is_reasoning_model:
          type: boolean
        is_claude_model:
          type: boolean
        total_messages:
          type: integer

    # ── Translation ────────────────────────────────────────────────────────────

    TranslateRequest:
      type: object
      required: [text, source_lang, target_lang, assistant_id]
      properties:
        text:
          type: string
          description: Text to translate (max 2 000 characters).
          example: Merhaba, nasılsın?
        source_lang:
          type: string
          description: BCP-47 language code of the source text.
          example: tr
        target_lang:
          type: string
          description: BCP-47 language code of the desired output.
          example: zh
        context:
          type: string
          default: conversation
          description: |
            Translation context hint — helps the model choose the right register.
            Examples: `conversation`, `formal`, `technical`, `marketing`.
        assistant_id:
          type: string
          format: uuid
          description: ID of the active Liya AI assistant to use for translation.

    TranslateResponseData:
      type: object
      properties:
        translated_text:
          type: string
        source_lang:
          type: string
        target_lang:
          type: string
        confidence:
          type: number
          example: 0.95
        billing:
          type: object
          properties:
            charged:
              type: boolean
            cost_usd:
              type: number

tags:
  - name: OpenAI Compatible
    description: |
      Drop-in OpenAI Chat Completions endpoints.  
      Authentication: `Authorization: Bearer <api-key>`
  - name: Chat
    description: Send messages and receive AI responses.
  - name: Sessions
    description: Create and manage conversation sessions.
  - name: Assistants
    description: List available assistants for a project.
  - name: Files
    description: Upload files and attach them to sessions.
  - name: Translation
    description: Real-time text translation via LLM backend.
  - name: TTS / STT
    description: Text-to-Speech and Speech-to-Text services.
  - name: Avatar
    description: 3D avatar speech with viseme timing data (Premium+).
  - name: Widget
    description: Widget configuration and feature flags.
  - name: Plugin (VS Code)
    description: Endpoints used by the Liya AI VS Code extension.
  - name: Public
    description: No authentication required.

paths:
  # ════════════════════════════════════════════════════════════════════════════
  # OpenAI-Compatible  (/v1/…)
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/openai/v1/chat/completions:
    post:
      tags: [OpenAI Compatible]
      summary: Create a chat completion
      operationId: openaiChatCompletion
      description: |
        Generates an AI response in the OpenAI `ChatCompletion` object format.

        **`model` field:** Pass your assistant's UUID here.
        Use `GET /api/v1/openai/v1/models` to list available assistant IDs.
        If `model` is omitted, the project's first active assistant is used.

        **Streaming:** `stream: true` is accepted but responses are always
        returned as a complete object. SSE support is planned.

        **Legacy path:** `POST /api/v1/openai/chat/completions` (without `/v1`)
        is still served for already-deployed integrations but is deprecated.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OAIChatCompletionRequest"
            examples:
              basic:
                summary: Simple user message
                value:
                  model: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                  messages:
                    - role: user
                      content: Hello! What can you help me with?
              with_system:
                summary: System + user message
                value:
                  model: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                  messages:
                    - role: system
                      content: You are a helpful assistant.
                    - role: user
                      content: Summarise the benefits of TypeScript.
                  temperature: 0.7
      responses:
        "200":
          description: Chat completion returned successfully.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAIChatCompletionResponse"
              example:
                id: chatcmpl-a1b2c3d4e5f6
                object: chat.completion
                created: 1714000000
                model: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                choices:
                  - index: 0
                    message:
                      role: assistant
                      content: I can help you with coding, writing, analysis and more!
                    finish_reason: stop
                    logprobs: null
                usage:
                  prompt_tokens: 0
                  completion_tokens: 0
                  total_tokens: 0
                system_fingerprint: null
        "400":
          description: Invalid request body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAIError"
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAIError"
        "402":
          description: Insufficient account balance.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAIError"
        "404":
          description: No active assistant found for this project.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAIError"

  /api/v1/openai/v1/models:
    get:
      tags: [OpenAI Compatible]
      summary: List available models (assistants)
      operationId: openaiListModels
      description: |
        Returns the project's active assistants formatted as an OpenAI model
        list. Use the `id` field from any entry as the `model` parameter in
        `POST /api/v1/openai/v1/chat/completions`.

        **Legacy path:** `GET /api/v1/openai/models` (without `/v1`) is still
        served for already-deployed integrations but is deprecated.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Model list returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAIModelList"
              example:
                object: list
                data:
                  - id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
                    object: model
                    created: 1714000000
                    owned_by: liya-ai
                    display_name: My Support Bot
                    description: Customer support assistant for website widget.
        "401":
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OAIError"

  # ════════════════════════════════════════════════════════════════════════════
  # Public  (/api/v1/public/…)   — no auth required
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/public/status/:
    get:
      tags: [Public]
      summary: System status
      operationId: publicStatus
      description: |
        Returns platform availability flags.  
        No authentication required.
      security: []
      responses:
        "200":
          description: Status retrieved.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: object
                    properties:
                      public_ready:
                        type: boolean
                      shop_enabled:
                        type: boolean
                      registration_message:
                        type: string
                        description: Present only when `public_ready` is false.

  /api/v1/public/pricing/:
    get:
      tags: [Public]
      summary: Pricing information
      operationId: publicPricing
      description: |
        Returns subscription plan prices, token costs, and exchange rates.  
        No authentication required.
      security: []
      parameters:
        - name: currency
          in: query
          description: Target currency for price display. Defaults to `USD`.
          schema:
            type: string
            enum: [USD, TRY, EUR, GBP]
            default: USD
      responses:
        "200":
          description: Pricing data retrieved.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: object
                    properties:
                      plans:
                        type: object
                        description: Plan name → pricing detail map.
                      balance:
                        type: object
                        description: Minimum balance and welcome bonus info.
                      exchange_rates:
                        type: object
                        description: Latest exchange rates relative to USD.
                      selected_currency:
                        type: string
                      supported_currencies:
                        type: array
                        items:
                          type: string
                      model_token_prices:
                        type: object
                      tax_rates:
                        type: object

  # ════════════════════════════════════════════════════════════════════════════
  # Chat  (/api/v1/external/chat/…)
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/chat/:
    post:
      tags: [Chat]
      summary: Send a message
      operationId: externalChat
      description: |
        Sends a user message to a Liya AI assistant and returns the AI reply.

        - If `session_id` is omitted a new session is created automatically.
        - The project owner's balance is charged on each successful call.
        - For long-running responses (>45 s) the endpoint returns HTTP 202
          `processing` — poll again with the same payload to retrieve the result.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [assistant_id, message]
              properties:
                assistant_id:
                  type: string
                  format: uuid
                  description: ID of the target assistant.
                message:
                  type: string
                  description: User message text.
                session_id:
                  type: string
                  format: uuid
                  description: Existing session ID. Omit to start a new conversation.
                external_session_id:
                  type: string
                  description: Your own session identifier (for correlation).
                file_ids:
                  type: array
                  items:
                    type: string
                    format: uuid
                  description: IDs of previously uploaded files to include.
            example:
              assistant_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
              message: What are the main features of Liya AI?
              session_id: null
      responses:
        "200":
          description: AI response returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/ChatResponseData"
        "202":
          description: |
            Request is being processed asynchronously.  
            Retry the same request after `retry_after` seconds.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: processing
                  retry_after:
                    type: integer
                    example: 3
                  data:
                    type: object
                    properties:
                      session_id:
                        type: string
                        format: uuid
        "400":
          description: Missing required fields.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: API key missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "402":
          description: Insufficient balance.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Assistant not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /api/v1/external/chat/with-files/:
    post:
      tags: [Chat]
      summary: Send a message with file attachments
      operationId: externalChatWithFiles
      description: |
        Equivalent to `POST /api/v1/external/chat/` with mandatory file
        attachment support. Upload files first with
        `POST /api/v1/external/files/`, then pass their IDs here.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [assistant_id, message]
              properties:
                assistant_id:
                  type: string
                  format: uuid
                session_id:
                  type: string
                  format: uuid
                message:
                  type: string
                file_ids:
                  type: array
                  items:
                    type: string
                    format: uuid
      responses:
        "200":
          description: AI response returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.
        "402":
          description: Insufficient balance.
        "404":
          description: Assistant not found.

  # ════════════════════════════════════════════════════════════════════════════
  # Sessions  (/api/v1/external/sessions/…)
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/sessions/:
    get:
      tags: [Sessions]
      summary: List sessions
      operationId: listSessions
      description: Lists active sessions for a given assistant (excludes auto-sessions).
      parameters:
        - name: assistant_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Session list returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      sessions:
                        type: array
                        items:
                          $ref: "#/components/schemas/SessionObject"
                      total:
                        type: integer
                      limit:
                        type: integer
                      offset:
                        type: integer
        "401":
          description: API key missing or invalid.

    post:
      tags: [Sessions]
      summary: Create a session
      operationId: createSession
      description: Creates a new conversation session for the given assistant.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [assistant_id]
              properties:
                assistant_id:
                  type: string
                  format: uuid
                session_name:
                  type: string
                  default: Yeni Sohbet
                external_session_id:
                  type: string
                  description: Your own identifier for correlation.
      responses:
        "201":
          description: Session created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.

  /api/v1/external/sessions/{session_id}/:
    get:
      tags: [Sessions]
      summary: Get session details
      operationId: getSession
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Session details returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    $ref: "#/components/schemas/SessionObject"
        "401":
          description: API key missing or invalid.
        "404":
          description: Session not found.

    delete:
      tags: [Sessions]
      summary: Close a session
      operationId: closeSession
      description: Marks the session as inactive. History is preserved.
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Session closed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "404":
          description: Session not found.

  /api/v1/external/sessions/{session_id}/history/:
    get:
      tags: [Sessions]
      summary: Get session message history
      operationId: getSessionHistory
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Message history returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      session_id:
                        type: string
                        format: uuid
                      messages:
                        type: array
                        items:
                          $ref: "#/components/schemas/MessageObject"
                      total:
                        type: integer
        "401":
          description: API key missing or invalid.
        "404":
          description: Session not found.

  # ════════════════════════════════════════════════════════════════════════════
  # Assistants
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/assistants/:
    get:
      tags: [Assistants]
      summary: List project assistants
      operationId: listAssistants
      description: Returns all active assistants belonging to the authenticated project.
      parameters:
        - name: type
          in: query
          description: |
            Filter by assistant type.  
            Examples: `coding_agent`, `linux_os`, `translator`
          schema:
            type: string
      responses:
        "200":
          description: Assistant list returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/AssistantObject"
        "401":
          description: API key missing or invalid.

  # ════════════════════════════════════════════════════════════════════════════
  # Files
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/files/:
    post:
      tags: [Files]
      summary: Upload a file
      operationId: uploadFile
      description: |
        Uploads a file and associates it with a session.  
        Maximum file size: **50 MB**.  
        Returns a file ID that can be used in `file_ids` when sending a message.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [file, session_id]
              properties:
                file:
                  type: string
                  format: binary
                session_id:
                  type: string
                  format: uuid
      responses:
        "201":
          description: File uploaded successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      file_name:
                        type: string
                      file_size:
                        type: integer
                      file_type:
                        type: string
                      openai_file_id:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                        format: date-time
        "400":
          description: Missing file or session_id, or file too large.
        "401":
          description: API key missing or invalid.
        "404":
          description: Session not found.

  # ════════════════════════════════════════════════════════════════════════════
  # Translation
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/translate/:
    post:
      tags: [Translation]
      summary: Translate text
      operationId: translateText
      description: |
        Translates text between two languages using the LLM backend.

        - Source and target language codes follow **BCP-47** (e.g. `tr`, `en`,
          `zh`, `de`, `fr`, `ja`, `ar`).
        - If `source_lang == target_lang` the original text is returned as-is
          with no charge.
        - Text is truncated to **2 000 characters** if longer.
        - The project owner's balance is charged per translation request.

        ### Context hint
        Pass `context` to guide register and tone:

        | Value | Effect |
        |-------|--------|
        | `conversation` (default) | Natural, everyday language |
        | `formal` | Formal / business register |
        | `technical` | Technical terminology preserved |
        | `marketing` | Brand-friendly, engaging copy |
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TranslateRequest"
            examples:
              tr_to_zh:
                summary: Turkish → Chinese
                value:
                  text: Merhaba, nasılsın?
                  source_lang: tr
                  target_lang: zh
                  context: conversation
                  assistant_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
              en_to_de_formal:
                summary: English → German (formal)
                value:
                  text: We are pleased to announce our new product line.
                  source_lang: en
                  target_lang: de
                  context: formal
                  assistant_id: 3fa85f64-5717-4562-b3fc-2c963f66afa6
      responses:
        "200":
          description: Translation successful.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/TranslateResponseData"
              example:
                status: success
                data:
                  translated_text: 你好，你好吗？
                  source_lang: tr
                  target_lang: zh
                  confidence: 0.95
                  billing:
                    charged: true
                    cost_usd: 0.0003
        "400":
          description: |
            Missing required field (`text`, `source_lang`, `target_lang`, or `assistant_id`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: API key missing or invalid.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "402":
          description: Insufficient balance.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  code:
                    type: string
                    example: INSUFFICIENT_BALANCE
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      current_balance:
                        type: number
                      estimated_cost:
                        type: number
                      required_balance:
                        type: number
        "500":
          description: Internal translation error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  # ════════════════════════════════════════════════════════════════════════════
  # TTS / STT
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/tts/:
    post:
      tags: [TTS / STT]
      summary: Text to Speech
      operationId: textToSpeech
      description: |
        Converts text to audio.  
        Returns the raw audio file (not JSON).  
        Maximum text length: **4 096 characters**.

        ### Supported voices
        `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`

        ### Supported formats
        `mp3` (default), `opus`, `aac`, `flac`, `wav`, `pcm`
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [text]
              properties:
                text:
                  type: string
                voice:
                  type: string
                  default: nova
                  description: One of `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`.
                speed:
                  type: number
                  default: 1.0
                  minimum: 0.25
                  maximum: 4.0
                response_format:
                  type: string
                  default: mp3
                  description: Audio encoding format.
                model:
                  type: string
                  default: gpt-4o-mini-tts
                  description: TTS model override (e.g. `tts-1`, `tts-1-hd`).
      responses:
        "200":
          description: Audio file returned.
          content:
            audio/mpeg:
              schema:
                type: string
                format: binary
            audio/ogg:
              schema:
                type: string
                format: binary
        "400":
          description: Missing `text` field.
        "401":
          description: API key missing or invalid.

  /api/v1/external/transcribe/:
    post:
      tags: [TTS / STT]
      summary: Speech to Text (transcribe audio)
      operationId: transcribeAudio
      description: |
        Transcribes an audio recording to text using OpenAI Whisper.  
        Maximum file size: **25 MB**.  
        Supported formats: `webm`, `mp3`, `wav`, `m4a`, `ogg`, `flac`.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [audio]
              properties:
                audio:
                  type: string
                  format: binary
                  description: Audio file to transcribe.
                model:
                  type: string
                  default: gpt-4o-mini-transcribe
      responses:
        "200":
          description: Transcription successful.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      text:
                        type: string
        "400":
          description: Missing audio file or file too large.
        "401":
          description: API key missing or invalid.

  # ════════════════════════════════════════════════════════════════════════════
  # Avatar  (Premium+)
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/avatar/speech/:
    post:
      tags: [Avatar]
      summary: Generate avatar speech with viseme data
      operationId: avatarSpeech
      description: |
        Generates audio **and** per-phoneme viseme timing data for 3D avatar
        lip-sync animation.

        **Requires:** Premium, Premium Plus, or Enterprise subscription.  
        Returns HTTP 403 for Standard-tier projects.

        Maximum text length: **4 096 characters**.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [text]
              properties:
                text:
                  type: string
                voice:
                  type: string
                  default: nova
                speed:
                  type: number
                  default: 1.0
                include_audio:
                  type: boolean
                  default: true
                return_audio_directly:
                  type: boolean
                  default: false
                  description: |
                    If `true`, returns raw audio bytes with viseme JSON in the
                    `X-Viseme-Data` response header (Base64-encoded).
      responses:
        "200":
          description: Avatar speech data returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      visemes:
                        type: array
                        items:
                          type: object
                          properties:
                            time:
                              type: number
                            viseme:
                              type: integer
                            duration:
                              type: number
                      duration:
                        type: number
                      audio_url:
                        type: string
                        nullable: true
        "401":
          description: API key missing or invalid.
        "403":
          description: Feature requires Premium or higher subscription.

  /api/v1/external/avatar/model/:
    get:
      tags: [Avatar]
      summary: Get avatar model configuration
      operationId: getAvatarModel
      description: Returns the 3D avatar model and animation configuration for the project.
      responses:
        "200":
          description: Avatar model config returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.

  # ════════════════════════════════════════════════════════════════════════════
  # Widget
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/widget/config/:
    get:
      tags: [Widget]
      summary: Get widget configuration
      operationId: getWidgetConfig
      description: |
        Returns the list of allowed widgets and feature flags based on the
        project owner's subscription tier.

        | Tier | Widgets | Notable features |
        |------|---------|-----------------|
        | Standard | `liya-ai-chat-vuejs` | STT only |
        | Premium | + `liya-3d-avatar-widget-vuejs` | TTS, 3D avatar, file upload |
        | Premium Plus / Enterprise | Same + kiosk mode | Custom avatar |

        Optional query parameter `assistant_id` (Linux OS type) also returns
        `os_daemon.connected` status in the response.
      parameters:
        - name: assistant_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: If provided and assistant is Linux OS type, includes daemon connection status.
      responses:
        "200":
          description: Widget config returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      account_type:
                        type: string
                        example: PREMIUM
                      allowed_widgets:
                        type: array
                        items:
                          type: string
                      features:
                        type: object
                        properties:
                          stt_enabled:
                            type: boolean
                          tts_enabled:
                            type: boolean
                          avatar_3d_enabled:
                            type: boolean
                          custom_avatar_enabled:
                            type: boolean
                          file_upload_enabled:
                            type: boolean
                          kiosk_mode_enabled:
                            type: boolean
                      os_daemon:
                        type: object
                        description: Present only when `assistant_id` is a Linux OS assistant.
                        properties:
                          connected:
                            type: boolean
                          status:
                            type: string
                            enum: [online, offline]
        "401":
          description: API key missing or invalid.

  /api/v1/external/user/access/:
    get:
      tags: [Widget]
      summary: Check user access level
      operationId: getUserAccess
      description: Returns the subscription tier and access flags for the project owner.
      responses:
        "200":
          description: Access info returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.

  /api/v1/external/scene/background/:
    get:
      tags: [Widget]
      summary: Get scene background
      operationId: getSceneBackground
      description: Returns the configured background image or colour for the 3D avatar scene.
      responses:
        "200":
          description: Scene background config returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.

  # ════════════════════════════════════════════════════════════════════════════
  # Presentations
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/presentations/:
    get:
      tags: [Widget]
      summary: List presentations
      operationId: listPresentations
      description: Returns the presentations attached to the project for widget display.
      responses:
        "200":
          description: Presentation list returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.

  /api/v1/external/presentations/page/{page_id}/image/:
    get:
      tags: [Widget]
      summary: Get presentation page image
      operationId: getPresentationPageImage
      parameters:
        - name: page_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Page image returned (binary).
          content:
            image/png:
              schema:
                type: string
                format: binary
            image/jpeg:
              schema:
                type: string
                format: binary
        "401":
          description: API key missing or invalid.
        "404":
          description: Page not found.

  # ════════════════════════════════════════════════════════════════════════════
  # Async Tasks
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/tasks/{task_id}/status/:
    get:
      tags: [Plugin (VS Code)]
      summary: Get async task status
      operationId: getTaskStatus
      description: |
        Polls the status of an async media generation task (Kling AI image / video).

        ### Task status values
        | Value | Meaning |
        |-------|---------|
        | `pending` | Queued, not yet started |
        | `submitted` | Sent to provider |
        | `processing` | Provider is generating |
        | `completed` | Result ready — `result_urls` populated |
        | `failed` | Generation failed — `error_message` set |
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Task status returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      task_id:
                        type: string
                      status:
                        type: string
                        enum:
                          [pending, submitted, processing, completed, failed]
                      task_type:
                        type: string
                      result_urls:
                        type: array
                        items:
                          type: string
                      error_code:
                        type: string
                        nullable: true
                      error_message:
                        type: string
                        nullable: true
                      created_at:
                        type: string
                      completed_at:
                        type: string
                        nullable: true
        "401":
          description: API key missing or invalid.
        "404":
          description: Task not found.

  # ════════════════════════════════════════════════════════════════════════════
  # Plugin (VS Code)
  # ════════════════════════════════════════════════════════════════════════════

  /api/v1/external/plugin/config/:
    get:
      tags: [Plugin (VS Code)]
      summary: Get VS Code plugin configuration
      operationId: getPluginConfig
      description: |
        Returns project metadata, balance, feature flags, backend URLs, and
        plugin version compatibility information.  
        Called by the VS Code extension on startup.
      responses:
        "200":
          description: Plugin config returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      project_name:
                        type: string
                      owner_email:
                        type: string
                      balance:
                        type: number
                      currency:
                        type: string
                        example: USD
                      coding_assistants_count:
                        type: integer
                      backend:
                        type: object
                        properties:
                          api_url:
                            type: string
                          websocket_url:
                            type: string
                          api_version:
                            type: string
                      features:
                        type: object
                        properties:
                          streaming_enabled:
                            type: boolean
                          thinking_enabled:
                            type: boolean
                          max_iterations:
                            type: integer
                          max_tool_calls_per_iteration:
                            type: integer
                      plugin_version:
                        type: object
                        properties:
                          min_supported:
                            type: string
                          latest:
                            type: string
                          python_tools_version:
                            type: string
                      announcements:
                        type: array
                        items:
                          type: object
        "401":
          description: API key missing or invalid.

  /api/v1/external/balance/:
    get:
      tags: [Plugin (VS Code)]
      summary: Get account balance
      operationId: getBalance
      description: Returns the current USD balance and a low-balance warning flag.
      responses:
        "200":
          description: Balance returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      balance:
                        type: number
                        example: 12.50
                      currency:
                        type: string
                        example: USD
                      low_balance_warning:
                        type: boolean
                        description: True when balance < $5.00.
        "401":
          description: API key missing or invalid.

  /api/v1/external/coding-agent/chat/:
    post:
      tags: [Plugin (VS Code)]
      summary: Coding Agent chat (subagent HTTP endpoint)
      operationId: codingAgentChat
      description: |
        Synchronous HTTP endpoint used by the VS Code plugin's SubagentManager
        (`task` tool). The main interactive coding agent session runs over
        WebSocket (`wss://.../ws/coding-agent/`).

        **`assistant_id`** must belong to an assistant with
        `assistant_type = coding_agent`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [assistant_id]
              properties:
                assistant_id:
                  type: string
                  format: uuid
                session_id:
                  type: string
                  format: uuid
                  description: Omit to create a new session automatically.
                message:
                  type: string
                  description: User/agent message text.
                tool_results:
                  type: array
                  description: Tool call results from the previous turn.
                  items:
                    type: object
                context:
                  type: object
                  description: Workspace context (open files, cursor position, etc.).
                previous_context:
                  type: string
                  nullable: true
                enable_thinking:
                  type: boolean
                  nullable: true
                project_memory:
                  type: string
                  nullable: true
                permission_mode:
                  type: string
                  default: default
                  description: |
                    Tool permission mode. Valid values: `default`, `auto`, `manual`.
                additional_tools:
                  type: array
                  items:
                    type: object
                  nullable: true
                available_skills:
                  type: array
                  items:
                    type: object
                  nullable: true
                statusline:
                  type: string
                  nullable: true
                output_style:
                  type: string
                  nullable: true
                images:
                  type: array
                  items:
                    type: string
                  nullable: true
                  description: Base64-encoded image strings.
      responses:
        "200":
          description: Coding agent response returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "400":
          description: Missing `assistant_id`.
        "401":
          description: API key missing or invalid.
        "404":
          description: Coding agent assistant not found.

  /api/v1/external/coding-agent/session-summary/:
    post:
      tags: [Plugin (VS Code)]
      summary: Get coding agent session summary
      operationId: codingAgentSessionSummary
      description: |
        Returns a compressed summary of a coding agent session.  
        Used when the user switches AI models — the new model receives the
        summary as context instead of the full message history.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [session_id]
              properties:
                session_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Session summary returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      summary:
                        type: string
                      message_count:
                        type: integer
                      session_id:
                        type: string
                        format: uuid
        "400":
          description: Missing `session_id`.
        "401":
          description: API key missing or invalid.
        "404":
          description: Session not found.

  /api/v1/external/analytics/events/:
    post:
      tags: [Plugin (VS Code)]
      summary: Track plugin analytics event
      operationId: trackAnalyticsEvent
      description: |
        Records a VS Code plugin analytics event (feature usage, errors, etc.).  
        Used for internal product analytics only — no PII is stored.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                event_type:
                  type: string
                  description: Event category/name.
                properties:
                  type: object
                  description: Arbitrary key-value metadata for the event.
      responses:
        "200":
          description: Event recorded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.

  /api/v1/external/feature-flags/:
    get:
      tags: [Plugin (VS Code)]
      summary: List feature flags
      operationId: listFeatureFlags
      description: |
        Returns active A/B test and feature flag assignments for the
        authenticated project. Used by clients to enable/disable experimental
        features without a release.
      responses:
        "200":
          description: Feature flags returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        flag:
                          type: string
                        enabled:
                          type: boolean
                        variant:
                          type: string
                          nullable: true
        "401":
          description: API key missing or invalid.

  /api/v1/external/owned-skills/:
    get:
      tags: [Plugin (VS Code)]
      summary: List owned coding skills
      operationId: listOwnedSkills
      description: |
        Returns the marketplace coding skills purchased by the project owner.  
        Skills extend the coding agent with specialised capabilities.
      responses:
        "200":
          description: Owned skills list returned.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "401":
          description: API key missing or invalid.

  /api/v1/external/mobile/config/:
    get:
      tags: [Public]
      summary: Mobile app public configuration
      operationId: mobileConfig
      description: |
        Returns the public assistant ID, API key, base URL, and app version
        metadata for the LiyaAI mobile application.  
        **No authentication required.**
      security: []
      responses:
        "200":
          description: Mobile config returned.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  data:
                    type: object
                    properties:
                      public_assistant_id:
                        type: string
                        format: uuid
                      public_api_key:
                        type: string
                      base_url:
                        type: string
                      assistant_name:
                        type: string
                      welcome_message_tr:
                        type: string
                      welcome_message_en:
                        type: string
                      min_app_version:
                        type: string
                      maintenance_mode:
                        type: boolean
                      public_ready:
                        type: boolean
                      shop_enabled:
                        type: boolean
        "503":
          description: Public mobile assistant not configured.
