openapi: 3.0.3
info:
  title: Qufas AI API
  version: 1.0.0
  description: |
    The public Qufas AI developer API. Chat completions use an OpenAI
    SDK-compatible documented subset. Image generation and model discovery use
    related OpenAI wire formats; usage, balance, and API key listing are Qufas
    management endpoints.

    This specification follows the frozen production Runtime contract. Browser
    session routes, dashboard routes, compatibility aliases, billing webhooks,
    OAuth callbacks, internal administration routes, and API-key write operations
    that currently return `501` are intentionally excluded from the supported
    public SDK surface. Unknown `/v1/*` paths return an OpenAI-shaped JSON `404`.
  contact:
    name: Qufas AI
servers:
  - url: https://qufas.ai
    description: Production
  - url: http://localhost:3000
    description: Local development
tags:
  - name: OpenAI Compatible
    description: OpenAI-compatible endpoints authenticated with Qufas API keys.
  - name: Qufas Management
    description: Qufas-specific account and usage read endpoints.
security:
  - bearerAuth: []
paths:
  /v1/chat/completions:
    post:
      tags:
        - OpenAI Compatible
      operationId: createChatCompletion
      summary: Create a chat completion
      description: |
        Creates a non-streaming JSON completion or an OpenAI-compatible SSE stream.
        The gateway rejects unknown top-level request fields rather than silently
        ignoring them. Tool calling and structured outputs depend on the selected
        model/provider capability.

        Streaming responses contain `data: <json>\n\n` events. Normal completion ends
        with exactly one `data: [DONE]`. Content and tool-call deltas retain the OpenAI chunk shape.
        A terminal usage chunk, when exposed, contains
        `metadata.request_id`. Stream failures after headers are sent are emitted as
        an in-band error event followed by `[DONE]` while the connection remains writable.
        `[DONE]` terminates SSE; it is not by itself proof of success or committed billing.
        Client Stop/abort stops receipt/display, not guaranteed upstream generation cancellation.
        The provider may continue generating; final billable usage may include tokens generated
        after Stop. When final usage reaches accounting, tenant-scoped idempotency settles the
        logical operation once. Disconnects need not deliver final usage or `[DONE]` and do not
        imply zero cost. Missing usage or interrupted accounting can leave settlement unverified;
        check usage and billing by request ID before retrying.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/ClientRequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateChatCompletionRequest"
            examples:
              basic:
                summary: Basic chat completion
                value:
                  model: kimi-k2.7-code
                  messages:
                    - role: user
                      content: Write a haiku about artificial intelligence.
              streaming:
                summary: Stream with a visible usage chunk
                value:
                  model: kimi-k2.7-code
                  messages:
                    - role: user
                      content: Say hello in one sentence.
                  stream: true
                  stream_options:
                    include_usage: true
              toolCalling:
                summary: Function tool call
                value:
                  model: kimi-k2.7-code
                  messages:
                    - role: user
                      content: What is the weather in San Francisco?
                  tools:
                    - type: function
                      function:
                        name: get_weather
                        description: Get the current weather for a city.
                        parameters:
                          type: object
                          properties:
                            location:
                              type: string
                          required:
                            - location
                  tool_choice: auto
                  parallel_tool_calls: true
              structuredOutput:
                summary: JSON Schema template (requires an available model with verified structured output)
                value:
                  model: <structured-output-capable-model-id>
                  messages:
                    - role: user
                      content: Extract the name and age from John is 25 years old.
                  response_format:
                    type: json_schema
                    json_schema:
                      name: user_info
                      schema:
                        type: object
                        properties:
                          name:
                            type: string
                          age:
                            type: integer
                        required:
                          - name
                          - age
                        additionalProperties: false
                      strict: true
      responses:
        "200":
          description: Chat completion response or OpenAI-compatible SSE stream.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChatCompletion"
              examples:
                text:
                  value:
                    id: chatcmpl_xxxxxxxxx
                    object: chat.completion
                    created: 1750000000
                    model: kimi-k2.7-code
                    choices:
                      - index: 0
                        message:
                          role: assistant
                          content: Artificial minds bloom quietly in circuits.
                        finish_reason: stop
                    usage:
                      prompt_tokens: 18
                      completion_tokens: 26
                      total_tokens: 44
                toolCall:
                  value:
                    id: chatcmpl_xxxxxxxxx
                    object: chat.completion
                    created: 1750000000
                    model: kimi-k2.7-code
                    choices:
                      - index: 0
                        message:
                          role: assistant
                          content: null
                          tool_calls:
                            - id: call_xxxxxxxxx
                              type: function
                              function:
                                name: get_weather
                                arguments: '{"location":"San Francisco, CA"}'
                        finish_reason: tool_calls
                    usage:
                      prompt_tokens: 92
                      completion_tokens: 18
                      total_tokens: 110
            text/event-stream:
              schema:
                type: string
                description: |
                  OpenAI-compatible SSE. Successful streams end with `[DONE]`.
                  Terminal usage metadata includes the canonical Qufas request ID.
              examples:
                textStream:
                  value: |
                    data: {"id":"chatcmpl_xxxxxxxxx","object":"chat.completion.chunk","created":1750000000,"model":"kimi-k2.7-code","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

                    data: {"id":"chatcmpl_xxxxxxxxx","object":"chat.completion.chunk","created":1750000000,"model":"kimi-k2.7-code","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

                    data: {"id":"chatcmpl_xxxxxxxxx","object":"chat.completion.chunk","created":1750000000,"model":"kimi-k2.7-code","choices":[],"usage":{"prompt_tokens":10,"completion_tokens":4,"total_tokens":14},"metadata":{"request_id":"req_0a1b2c3d4e5f"}}

                    data: [DONE]
                toolCallStream:
                  value: |
                    data: {"id":"chatcmpl_xxxxxxxxx","object":"chat.completion.chunk","created":1750000000,"model":"kimi-k2.7-code","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_xxxxxxxxx","type":"function","function":{"name":"get_weather","arguments":"{\\\"location\\\":"}}]},"finish_reason":null}]}

                    data: [DONE]
                inBandError:
                  value: |
                    data: {"error":{"message":"The upstream provider timed out during streaming.","type":"provider_error","param":null,"code":"provider_timeout","request_id":"req_0a1b2c3d4e5f"}}

                    data: [DONE]
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
  /v1/images/generations:
    post:
      tags:
        - OpenAI Compatible
      operationId: createImageGeneration
      summary: Create an image
      description: |
        Generates one to four images with an enabled image model. The current public
        Runtime returns URL results only. `response_format: b64_json` is rejected and
        therefore is not part of this supported contract.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/ClientRequestId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateImageGenerationRequest"
            examples:
              image:
                value:
                  model: <enabled-image-model-id>
                  prompt: A cute panda drinking coffee
                  size: 1024x1024
                  n: 1
                  response_format: url
      responses:
        "200":
          description: OpenAI-compatible URL image response.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImageGenerationResponse"
              examples:
                image:
                  value:
                    created: 1750000000
                    data:
                      - url: https://example.com/generated.png
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "502":
          $ref: "#/components/responses/BadGateway"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
        "504":
          $ref: "#/components/responses/GatewayTimeout"
  /v1/models:
    get:
      tags:
        - OpenAI Compatible
      operationId: listModels
      summary: List models
      description: |
        Returns enabled public model IDs across supported workloads that are available to
        the API key. Use chat models with `POST /v1/chat/completions` and image models
        with `POST /v1/images/generations`. The public model object is intentionally
        limited to `id`, `object`, `created`, and `owned_by`.
      responses:
        "200":
          description: OpenAI-compatible model list.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListModelsResponse"
              examples:
                models:
                  value:
                    object: list
                    data:
                      - id: kimi-k2.7-code
                        object: model
                        created: 1764547200
                        owned_by: aliyun
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalServerError"
  /v1/usage:
    get:
      tags:
        - Qufas Management
      operationId: listUsage
      summary: List usage
      description: |
        Returns the most recent usage rows for the authenticated account. `totals`
        aggregate only the rows returned in `data`; they are not all-time account totals.
        Runtime input is normalized to an integer and clamped to 1..500. Missing or
        non-numeric input uses 100.
      parameters:
        - name: limit
          in: query
          required: false
          description: Requested row count. Runtime clamps the effective value to 1..500.
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
      responses:
        "200":
          description: Usage rows and totals for those returned rows.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListUsageResponse"
              examples:
                usage:
                  value:
                    object: list
                    data:
                      - id: 4d1fb884-e99b-4cd7-bb91-706d5f7a1011
                        date: "2026-07-08T12:34:56.000Z"
                        model: kimi-k2.7-code
                        requests: 1
                        input_tokens: 18
                        output_tokens: 26
                        total_tokens: 44
                        cost: 0.000123
                    totals:
                      requests: 1
                      input_tokens: 18
                      output_tokens: 26
                      total_tokens: 44
                      cost: 0.000123
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalServerError"
  /v1/generations:
    get:
      tags:
        - Qufas Management
      operationId: listGenerations
      summary: List generations for the calling API key
      description: |
        Returns metadata-only inference generations owned by the exact API key used
        for this request. Results are ordered by `created_at DESC, id DESC`. The
        opaque cursor is signed and bound to the calling key and active filters.
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: cursor
          in: query
          required: false
          description: Opaque cursor returned by the preceding page.
          schema:
            type: string
            minLength: 1
        - name: status
          in: query
          required: false
          schema:
            $ref: "#/components/schemas/GenerationStatus"
        - name: model
          in: query
          required: false
          description: Exact public model ID.
          schema:
            type: string
            minLength: 1
      responses:
        "200":
          description: API-key-scoped generation page.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenerationList"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
  /v1/generations/{request_id}:
    get:
      tags:
        - Qufas Management
      operationId: retrieveGeneration
      summary: Retrieve a generation for the calling API key
      description: |
        Returns a metadata-only generation and its public provider-attempt history.
        Unknown IDs and IDs owned by another API key produce the same `404` response.
        The response header request ID identifies this lookup; the body `id` identifies
        the original inference generation.
      parameters:
        - name: request_id
          in: path
          required: true
          schema:
            type: string
            pattern: '^req_[a-z0-9]+$'
      responses:
        "200":
          description: API-key-owned generation detail.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenerationDetail"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/GenerationNotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
  /v1/billing/balance:
    get:
      tags:
        - Qufas Management
      operationId: getBillingBalance
      summary: Get billing balance
      description: Returns the current prepaid USD balance for the authenticated account.
      responses:
        "200":
          description: Current prepaid balance.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BillingBalanceResponse"
              examples:
                balance:
                  value:
                    object: balance
                    balance: 82.8193
                    currency: USD
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalServerError"
  /v1/keys:
    get:
      tags:
        - Qufas Management
      operationId: listApiKeys
      summary: List API keys
      description: |
        Returns safe API-key metadata. Full secret values are never returned. Public
        create/delete operations currently return `501` at Runtime and are deliberately
        absent from this supported API surface.
      responses:
        "200":
          description: API-key metadata list.
          headers:
            x-qufas-request-id:
              $ref: "#/components/headers/QufasRequestId"
            X-Request-ID:
              $ref: "#/components/headers/LegacyRequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListApiKeysResponse"
              examples:
                keys:
                  value:
                    object: list
                    data:
                      - id: fdc863e0-0693-459d-a977-e12de6186534
                        name: Production server
                        description: Main backend key
                        prefix: qf_sk_ab12...
                        status: active
                        allowed_models: []
                        environment: production
                        expires_at: null
                        last_used_at: "2026-07-08T12:34:56.000Z"
                        monthly_spend_limit: null
                        rate_limit: 100
                        created_at: "2026-07-01T12:00:00.000Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalServerError"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Qufas API Key
      description: "Send `Authorization: Bearer qf_sk_...`."
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: |
        Optional billing idempotency key for chat and image generation. Reuse it when
        retrying the same logical operation to prevent duplicate usage charging. It is
        separate from the canonical Qufas request ID and does not imply response caching.
      schema:
        type: string
        minLength: 1
        example: idem_01J00000000000000000000000
    ClientRequestId:
      name: X-Request-ID
      in: header
      required: false
      description: |
        Optional client correlation value. It never replaces the gateway-generated
        canonical `req_*` identifier and is not used as an idempotency key. Responses
        return the canonical ID in both request-ID response headers.
      schema:
        type: string
        minLength: 1
        example: client-order-42
  headers:
    QufasRequestId:
      description: Canonical gateway request ID generated on entry, in `req_*` form.
      required: true
      schema:
        type: string
        pattern: '^req_[a-z0-9]+$'
        example: req_0a1b2c3d4e5f
    LegacyRequestId:
      description: Legacy response alias containing the same canonical value as `x-qufas-request-id`.
      required: true
      schema:
        type: string
        pattern: '^req_[a-z0-9]+$'
        example: req_0a1b2c3d4e5f
  responses:
    BadRequest:
      description: Invalid input, unsupported parameter, or incompatible model capability.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            unsupportedParameter:
              value:
                error:
                  message: The parameter 'n' is not supported by this gateway.
                  type: invalid_request_error
                  param: n
                  code: unsupported_parameter
    Unauthorized:
      description: Missing, invalid, expired, or inactive Qufas API key.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            invalidApiKey:
              value:
                error:
                  message: Invalid API key.
                  type: authentication_error
                  param: null
                  code: invalid_api_key
    InsufficientBalance:
      description: The prepaid account balance cannot cover the operation.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            insufficientBalance:
              value:
                error:
                  message: Insufficient balance. Please recharge before making requests.
                  type: insufficient_balance
                  param: null
                  code: insufficient_balance
    Forbidden:
      description: The API key cannot use the requested model or has exceeded a configured spend limit.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            modelNotAllowed:
              value:
                error:
                  message: This API key does not have access to this model.
                  type: permission_error
                  param: model
                  code: model_not_allowed
    NotFound:
      description: The requested model is not available.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            modelNotFound:
              value:
                error:
                  message: Model qwen-unknown is not available.
                  type: invalid_request_error
                  param: model
                  code: model_not_found
    GenerationNotFound:
      description: The generation does not exist or is not owned by the calling API key.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            generationNotFound:
              value:
                error:
                  message: Generation not found.
                  type: invalid_request_error
                  param: null
                  code: generation_not_found
    RateLimited:
      description: The API key exceeded its request rate limit.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            rateLimit:
              value:
                error:
                  message: Rate limit exceeded
                  type: rate_limit_error
                  param: null
                  code: rate_limit_exceeded
    InternalServerError:
      description: Qufas could not complete internal processing or billing finalization.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            internal:
              value:
                error:
                  message: Internal server error.
                  type: internal_error
                  param: null
                  code: internal_server_error
    BadGateway:
      description: The upstream provider failed or returned an invalid response.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            providerFailure:
              value:
                error:
                  message: The upstream provider request failed.
                  type: provider_error
                  param: null
                  code: provider_unknown_error
    ServiceUnavailable:
      description: No configured provider is currently available for the request.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            unavailable:
              value:
                error:
                  message: All providers are currently unavailable
                  type: provider_error
                  param: null
                  code: all_providers_unavailable
    GatewayTimeout:
      description: The upstream provider did not complete before the gateway timeout.
      headers:
        x-qufas-request-id:
          $ref: "#/components/headers/QufasRequestId"
        X-Request-ID:
          $ref: "#/components/headers/LegacyRequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            timeout:
              value:
                error:
                  message: Upstream provider timed out.
                  type: provider_error
                  param: null
                  code: provider_timeout
  schemas:
    CreateChatCompletionRequest:
      type: object
      required:
        - model
        - messages
      properties:
        model:
          type: string
          minLength: 1
          pattern: '.*\S.*'
          description: Enabled chat model identifier.
          example: kimi-k2.7-code
        messages:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/ChatMessage"
        stream:
          type: boolean
          default: false
          description: Return OpenAI-compatible SSE when true.
        temperature:
          type: number
          format: float
          nullable: true
          minimum: 0
          maximum: 2
          exclusiveMaximum: true
          description: Sampling temperature in the half-open range [0, 2). Null is treated as omitted.
        max_tokens:
          type: integer
          nullable: true
          minimum: 1
          description: Positive maximum completion token count. Null is treated as omitted.
        top_p:
          type: number
          format: float
          nullable: true
          minimum: 0
          exclusiveMinimum: true
          maximum: 1
          description: Nucleus sampling value in the range (0, 1]. Null is treated as omitted.
        stop:
          nullable: true
          description: Null, one non-empty string, or one to four non-empty strings.
          oneOf:
            - type: string
              minLength: 1
              pattern: '.*\S.*'
            - type: array
              minItems: 1
              maxItems: 4
              items:
                type: string
                minLength: 1
                pattern: '.*\S.*'
        presence_penalty:
          type: number
          format: float
          nullable: true
          minimum: -2
          maximum: 2
          description: Presence penalty in the inclusive range [-2, 2]. Null is treated as omitted.
        tools:
          type: array
          description: Function tools available to the model.
          items:
            $ref: "#/components/schemas/ChatCompletionTool"
        tool_choice:
          $ref: "#/components/schemas/ChatCompletionToolChoice"
        parallel_tool_calls:
          type: boolean
          description: |
            Whether the provider may emit multiple tool calls. `true` requires at
            least one item in `tools`; `false` is accepted without tools.
        response_format:
          $ref: "#/components/schemas/ResponseFormat"
        seed:
          type: integer
          format: int32
          nullable: true
          minimum: 0
          maximum: 2147483647
          description: Deterministic sampling seed when supported. Null is treated as omitted.
        stream_options:
          $ref: "#/components/schemas/StreamOptions"
      additionalProperties: false
    ChatMessage:
      description: Valid system, user, assistant, or tool message.
      oneOf:
        - $ref: "#/components/schemas/SystemMessage"
        - $ref: "#/components/schemas/UserMessage"
        - $ref: "#/components/schemas/AssistantMessage"
        - $ref: "#/components/schemas/ToolMessage"
    SystemMessage:
      type: object
      required:
        - role
        - content
      properties:
        role:
          type: string
          enum: [system]
        content:
          type: string
      additionalProperties: true
    UserMessage:
      type: object
      required:
        - role
        - content
      properties:
        role:
          type: string
          enum: [user]
        content:
          oneOf:
            - type: string
            - type: array
              minItems: 1
              items:
                $ref: "#/components/schemas/UserContentPart"
      additionalProperties: true
    UserContentPart:
      oneOf:
        - $ref: "#/components/schemas/TextContentPart"
        - $ref: "#/components/schemas/ImageUrlContentPart"
    TextContentPart:
      type: object
      required:
        - type
        - text
      properties:
        type:
          type: string
          enum: [text]
        text:
          type: string
      additionalProperties: true
    ImageUrlContentPart:
      type: object
      required:
        - type
        - image_url
      properties:
        type:
          type: string
          enum: [image_url]
        image_url:
          type: object
          required:
            - url
          properties:
            url:
              type: string
              minLength: 1
            detail:
              type: string
              enum: [auto, low, high]
          additionalProperties: true
      additionalProperties: true
    AssistantMessage:
      type: object
      required:
        - role
        - content
      properties:
        role:
          type: string
          enum: [assistant]
        content:
          type: string
          nullable: true
          description: Must be a string or null. When null, a non-empty valid `tool_calls` array is required.
        tool_calls:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/ToolCall"
      additionalProperties: true
    ToolMessage:
      type: object
      required:
        - role
        - content
        - tool_call_id
      properties:
        role:
          type: string
          enum: [tool]
        content:
          type: string
        tool_call_id:
          type: string
          minLength: 1
          pattern: '.*\S.*'
      additionalProperties: true
    ChatCompletionTool:
      type: object
      required:
        - type
        - function
      properties:
        type:
          type: string
          enum: [function]
        function:
          $ref: "#/components/schemas/FunctionDefinition"
      additionalProperties: true
    FunctionDefinition:
      type: object
      required:
        - name
        - parameters
      properties:
        name:
          type: string
          minLength: 1
          pattern: '.*\S.*'
        description:
          type: string
        parameters:
          type: object
          additionalProperties: true
          description: JSON Schema describing function arguments.
      additionalProperties: true
    ChatCompletionToolChoice:
      description: |
        `auto`, `none`, or an explicit named function declared in `tools`. The Runtime
        does not accept `required`.
      oneOf:
        - type: string
          enum: [auto, none]
        - $ref: "#/components/schemas/NamedToolChoice"
    NamedToolChoice:
      type: object
      required:
        - type
        - function
      properties:
        type:
          type: string
          enum: [function]
        function:
          type: object
          required:
            - name
          properties:
            name:
              type: string
              minLength: 1
              pattern: '.*\S.*'
          additionalProperties: true
      additionalProperties: true
    ResponseFormat:
      description: OpenAI-compatible text, JSON object, or JSON Schema output mode.
      oneOf:
        - $ref: "#/components/schemas/TextResponseFormat"
        - $ref: "#/components/schemas/JsonObjectResponseFormat"
        - $ref: "#/components/schemas/JsonSchemaResponseFormat"
    TextResponseFormat:
      type: object
      required: [type]
      properties:
        type:
          type: string
          enum: [text]
      additionalProperties: true
    JsonObjectResponseFormat:
      type: object
      required: [type]
      properties:
        type:
          type: string
          enum: [json_object]
      additionalProperties: true
    JsonSchemaResponseFormat:
      type: object
      required:
        - type
        - json_schema
      properties:
        type:
          type: string
          enum: [json_schema]
        json_schema:
          type: object
          required:
            - name
            - schema
          properties:
            name:
              type: string
              minLength: 1
              pattern: '.*\S.*'
            schema:
              type: object
              additionalProperties: true
            strict:
              type: boolean
          additionalProperties: true
      additionalProperties: true
    StreamOptions:
      type: object
      nullable: true
      description: |
        Only valid when `stream` is true. If `stream_options` is omitted or null,
        Qufas preserves its legacy behavior and exposes the terminal usage chunk.
        If the object is present, omitted `include_usage` defaults to false. Explicit
        `true` exposes the usage chunk; explicit `false` suppresses it from the client.
        Provider usage is still collected internally for billing in either case.
        A disconnected client may not receive final usage. Receiving a usage chunk does not
        itself confirm a committed charge; hiding it does not disable billing.
      properties:
        include_usage:
          type: boolean
          default: false
      additionalProperties: false
    CreateImageGenerationRequest:
      type: object
      required:
        - model
        - prompt
      properties:
        model:
          type: string
          minLength: 1
          pattern: '.*\S.*'
          description: Exact enabled upstream image model ID. Placeholder is not a callable model.
          example: <enabled-image-model-id>
        prompt:
          type: string
          minLength: 1
          pattern: '.*\S.*'
          example: A cute panda drinking coffee
        size:
          type: string
          default: 1024x1024
          description: Model-supported image size. `*` is normalized to `x` by Runtime.
          example: 1024x1024
        n:
          type: integer
          minimum: 1
          maximum: 4
          default: 1
        response_format:
          type: string
          enum: [url]
          default: url
      additionalProperties: true
    ImageGenerationResponse:
      type: object
      required:
        - created
        - data
      properties:
        created:
          type: integer
          format: int64
        data:
          type: array
          items:
            $ref: "#/components/schemas/ImageGenerationData"
      additionalProperties: false
    ImageGenerationData:
      type: object
      required:
        - url
      properties:
        url:
          type: string
          format: uri
      additionalProperties: false
    ChatCompletion:
      type: object
      required:
        - id
        - object
        - created
        - model
        - choices
      properties:
        id:
          type: string
        object:
          type: string
          enum: [chat.completion]
        created:
          type: integer
          format: int64
        model:
          type: string
        choices:
          type: array
          items:
            $ref: "#/components/schemas/ChatCompletionChoice"
        usage:
          $ref: "#/components/schemas/Usage"
      additionalProperties: true
    ChatCompletionChoice:
      type: object
      required:
        - index
        - message
      properties:
        index:
          type: integer
        message:
          $ref: "#/components/schemas/ChatCompletionMessage"
        finish_reason:
          type: string
          nullable: true
      additionalProperties: true
    ChatCompletionMessage:
      type: object
      required:
        - role
        - content
      properties:
        role:
          type: string
          enum: [assistant]
        content:
          type: string
          nullable: true
        tool_calls:
          type: array
          items:
            $ref: "#/components/schemas/ToolCall"
      additionalProperties: true
    ToolCall:
      type: object
      required:
        - id
        - type
        - function
      properties:
        id:
          type: string
          minLength: 1
        type:
          type: string
          enum: [function]
        function:
          $ref: "#/components/schemas/FunctionCall"
      additionalProperties: true
    FunctionCall:
      type: object
      required:
        - name
        - arguments
      properties:
        name:
          type: string
          minLength: 1
        arguments:
          type: string
          description: JSON-encoded function arguments.
      additionalProperties: true
    Usage:
      type: object
      required:
        - prompt_tokens
        - completion_tokens
        - total_tokens
      properties:
        prompt_tokens:
          type: integer
          minimum: 0
        completion_tokens:
          type: integer
          minimum: 0
        total_tokens:
          type: integer
          minimum: 0
      additionalProperties: true
    ListModelsResponse:
      type: object
      required:
        - object
        - data
      properties:
        object:
          type: string
          enum: [list]
        data:
          type: array
          items:
            $ref: "#/components/schemas/Model"
      additionalProperties: false
    Model:
      type: object
      required:
        - id
        - object
        - created
        - owned_by
      properties:
        id:
          type: string
        object:
          type: string
          enum: [model]
        created:
          type: integer
          format: int64
        owned_by:
          type: string
        name:
          type: string
        workload:
          type: string
        availability:
          type: string
        pricing:
          type: object
          description: Customer rates read from the registry. Token rates are USD per one million tokens; billing reads rates at final settlement. No provider costs are included.
          properties:
            currency:
              type: string
              enum: [USD]
            input:
              type: number
              nullable: true
            output:
              type: number
              nullable: true
            unit_price:
              type: number
              nullable: true
            input_exact:
              type: string
              nullable: true
              pattern: '^-?\d+(?:\.\d+)?$'
              description: Original registry NUMERIC text when supplied by the exact RDS projection. Use this for lossless display or comparison; billing remains authoritative SQL.
            output_exact:
              type: string
              nullable: true
              pattern: '^-?\d+(?:\.\d+)?$'
            unit_price_exact:
              type: string
              nullable: true
              pattern: '^-?\d+(?:\.\d+)?$'
            price_version:
              type: string
              description: Public catalog price policy version when known.
            unit:
              type: string
              nullable: true
            billing_type:
              type: string
          additionalProperties: false
        capabilities:
          type: object
          description: Verified capabilities supported by the gateway, not all upstream provider features.
          additionalProperties:
            type: boolean
        context_window:
          type: integer
          nullable: true
        max_output_tokens:
          type: integer
          nullable: true
      additionalProperties: false
    ListUsageResponse:
      type: object
      required:
        - object
        - data
        - totals
      properties:
        object:
          type: string
          enum: [list]
        data:
          type: array
          items:
            $ref: "#/components/schemas/UsageRecord"
        totals:
          $ref: "#/components/schemas/UsageTotals"
      additionalProperties: false
    UsageRecord:
      type: object
      required:
        - id
        - date
        - model
        - requests
        - input_tokens
        - output_tokens
        - total_tokens
        - cost
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date-time
        model:
          type: string
        requests:
          type: integer
          minimum: 0
        input_tokens:
          type: integer
          minimum: 0
        output_tokens:
          type: integer
          minimum: 0
        total_tokens:
          type: integer
          minimum: 0
        cost:
          type: number
          format: double
          minimum: 0
      additionalProperties: false
    UsageTotals:
      type: object
      required:
        - requests
        - input_tokens
        - output_tokens
        - total_tokens
        - cost
      properties:
        requests:
          type: integer
          minimum: 0
        input_tokens:
          type: integer
          minimum: 0
        output_tokens:
          type: integer
          minimum: 0
        total_tokens:
          type: integer
          minimum: 0
        cost:
          type: number
          format: double
          minimum: 0
      additionalProperties: false
    BillingBalanceResponse:
      type: object
      required:
        - object
        - balance
        - currency
      properties:
        object:
          type: string
          enum: [balance]
        balance:
          type: number
          format: double
        currency:
          type: string
          enum: [USD]
      additionalProperties: false
    ListApiKeysResponse:
      type: object
      required:
        - object
        - data
      properties:
        object:
          type: string
          enum: [list]
        data:
          type: array
          items:
            $ref: "#/components/schemas/ApiKey"
      additionalProperties: false
    ApiKey:
      type: object
      required:
        - id
        - name
        - description
        - prefix
        - status
        - allowed_models
        - environment
        - expires_at
        - last_used_at
        - monthly_spend_limit
        - rate_limit
        - created_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
          nullable: true
        prefix:
          type: string
          description: Safe prefix only; the secret key is never returned.
        status:
          type: string
          enum: [active, revoked]
        allowed_models:
          type: array
          items:
            type: string
        environment:
          type: string
        expires_at:
          type: string
          format: date-time
          nullable: true
        last_used_at:
          type: string
          format: date-time
          nullable: true
        monthly_spend_limit:
          type: number
          format: double
          nullable: true
          minimum: 0
        rate_limit:
          type: integer
          nullable: true
          minimum: 1
        created_at:
          type: string
          format: date-time
      additionalProperties: false
    GenerationStatus:
      type: string
      enum: [in_progress, completed, failed, cancelled]
    GenerationUsage:
      type: object
      nullable: true
      required:
        - input_tokens
        - output_tokens
        - total_tokens
        - units
        - unit
      properties:
        input_tokens:
          type: integer
          minimum: 0
          nullable: true
        output_tokens:
          type: integer
          minimum: 0
          nullable: true
        total_tokens:
          type: integer
          minimum: 0
          nullable: true
        units:
          type: string
          nullable: true
        unit:
          type: string
          enum: [tokens, images, seconds, requests]
          nullable: true
      additionalProperties: false
    GenerationCost:
      type: object
      nullable: true
      required:
        - input
        - output
        - total
        - currency
      properties:
        input:
          type: string
          pattern: '^\d+(?:\.\d+)?$'
          nullable: true
        output:
          type: string
          pattern: '^\d+(?:\.\d+)?$'
          nullable: true
        total:
          type: string
          pattern: '^\d+(?:\.\d+)?$'
        currency:
          type: string
          nullable: true
      additionalProperties: false
    GenerationError:
      type: object
      nullable: true
      required:
        - type
        - code
        - message
      properties:
        type:
          type: string
        code:
          type: string
        message:
          type: string
      additionalProperties: false
    GenerationProviderAttempt:
      type: object
      required:
        - index
        - provider
        - status
        - started_at
        - completed_at
        - latency_ms
        - upstream_status
        - error
      properties:
        index:
          type: integer
          minimum: 1
        provider:
          type: string
          nullable: true
        status:
          type: string
          enum: [in_progress, succeeded, failed]
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        latency_ms:
          type: integer
          minimum: 0
          nullable: true
        upstream_status:
          type: integer
          minimum: 100
          maximum: 599
          nullable: true
        error:
          type: object
          nullable: true
          required:
            - type
            - code
          properties:
            type:
              type: string
              enum: [provider_error]
            code:
              type: string
          additionalProperties: false
      additionalProperties: false
    GenerationRouting:
      type: object
      required:
        - selected_provider
        - fallback
        - attempt_count
      properties:
        selected_provider:
          type: string
          nullable: true
        fallback:
          type: boolean
        attempt_count:
          type: integer
          minimum: 0
      additionalProperties: false
    GenerationSummary:
      type: object
      required:
        - id
        - object
        - created_at
        - status
        - workload
        - model
        - provider
        - latency_ms
        - usage
        - cost
        - fallback
      properties:
        id:
          type: string
          pattern: '^req_[a-z0-9]+$'
        object:
          type: string
          enum: [generation]
        created_at:
          type: string
          format: date-time
        status:
          $ref: "#/components/schemas/GenerationStatus"
        workload:
          type: string
          enum: [chat, image]
          nullable: true
        model:
          type: string
        provider:
          type: string
          nullable: true
        latency_ms:
          type: integer
          minimum: 0
          nullable: true
        usage:
          $ref: "#/components/schemas/GenerationUsage"
        cost:
          $ref: "#/components/schemas/GenerationCost"
        fallback:
          type: boolean
      additionalProperties: false
    GenerationDetail:
      type: object
      required:
        - id
        - object
        - created_at
        - status
        - workload
        - model
        - provider
        - latency_ms
        - usage
        - cost
        - fallback
        - completed_at
        - streaming
        - time_to_first_token_ms
        - tokens_per_second
        - routing
        - attempts
        - error
      properties:
        id:
          type: string
          pattern: '^req_[a-z0-9]+$'
        object:
          type: string
          enum: [generation]
        created_at:
          type: string
          format: date-time
        status:
          $ref: "#/components/schemas/GenerationStatus"
        workload:
          type: string
          enum: [chat, image]
          nullable: true
        model:
          type: string
        provider:
          type: string
          nullable: true
        latency_ms:
          type: integer
          minimum: 0
          nullable: true
        usage:
          $ref: "#/components/schemas/GenerationUsage"
        cost:
          $ref: "#/components/schemas/GenerationCost"
        fallback:
          type: boolean
        completed_at:
          type: string
          format: date-time
          nullable: true
        streaming:
          type: boolean
        time_to_first_token_ms:
          type: integer
          minimum: 0
          nullable: true
        tokens_per_second:
          type: number
          minimum: 0
          nullable: true
        routing:
          $ref: "#/components/schemas/GenerationRouting"
        attempts:
          type: array
          items:
            $ref: "#/components/schemas/GenerationProviderAttempt"
        error:
          $ref: "#/components/schemas/GenerationError"
      additionalProperties: false
    GenerationList:
      type: object
      required:
        - object
        - data
        - has_more
        - next_cursor
      properties:
        object:
          type: string
          enum: [list]
        data:
          type: array
          items:
            $ref: "#/components/schemas/GenerationSummary"
        has_more:
          type: boolean
        next_cursor:
          type: string
          nullable: true
          description: Opaque signed cursor; its internal representation is not part of the contract.
      additionalProperties: false
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          $ref: "#/components/schemas/OpenAIError"
      additionalProperties: false
    OpenAIError:
      type: object
      required:
        - message
        - type
        - param
        - code
      properties:
        message:
          type: string
        type:
          type: string
        param:
          type: string
          nullable: true
        code:
          type: string
      additionalProperties: false
    StreamingErrorEvent:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - message
            - type
            - param
            - code
            - request_id
          properties:
            message:
              type: string
            type:
              type: string
            param:
              type: string
              nullable: true
            code:
              type: string
            request_id:
              type: string
              pattern: '^req_[a-z0-9]+$'
          additionalProperties: false
      additionalProperties: false
