# AUTO-GENERATED tier-A mirror spec — do not edit.
# Source: openapi/runtime/ai-providers.yaml (runtime 0.71.2)
#  + policy: openapi/mirror-policies/ai-providers.json
# Regenerate: node scripts/generate-mirror-specs.mjs
openapi: 3.0.3
info:
  title: naturali.ai — AI Providers API
  version: 1.0.0
  description: >-
    AI providers: the credentialed model vendors a project generates through, the models each one
    offers, and their per-token prices. A fully runtime-backed module — this spec is generated
    verbatim from the runtime's own, re-rooted under /v1/projects/{project_id}. The project in the
    path is authorized by naturali and enforced upstream by the project's scoped credential.


    One value is naturali's own: `provider: "naturali"` creates a provider that runs on naturali's
    model access, needs no credentials of yours, and serves every model `GET /v1/models` lists. It
    takes a `default_model` and nothing else — no `secret_id`, no `config`, no `base_url`.


    This module mirrors the upstream runtime verbatim (tier A, #304): paths are the runtime's own
    re-rooted under /v1/projects/{project_id}, and every field, method, status code and error shape
    passes through unchanged. Errors raised by the runtime arrive in its envelope; errors raised by
    naturali itself (authentication, project resolution, an unreachable runtime) use naturali's.
  contact:
    name: naturali.ai
    url: https://naturali.ai
servers:
  - url: "{baseUrl}"
    description: Host of your naturali.ai deployment; every path carries the /v1 prefix.
    variables:
      baseUrl:
        description: Base host URL.
        default: https://api.naturali.ai
tags:
  - name: AI Providers
    description: Manage AI providers
security:
  - bearerAuth: []
  - oauth2:
      - mcp:access
paths:
  /v1/projects/{project_id}/ai-providers:
    get:
      tags:
        - AI Providers
      summary: List AI providers
      description: Returns a list of AI provider configurations for a project
      operationId: listAiProviders
      parameters:
        - name: limit
          in: query
          description: Number of results per page
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: offset
          in: query
          description: Number of results to skip
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of AI providers
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        provider:
                          type: string
                          enum:
                            - openai
                            - anthropic
                            - google
                            - xai
                            - groq
                            - ollama
                            - azure
                            - bedrock
                            - vertex
                            - gateway
                            - custom
                            - naturali
                        default_model:
                          type: string
                        secret_id:
                          x-naturali-ref: secrets
                          type: string
                          nullable: true
                          description: Secret ID containing API credentials, or null when the record links none.
                        base_url:
                          type: string
                          description: Custom base URL for the provider. Absent when the record sets none.
                        config:
                          type: object
                          description: Additional provider-specific configuration. Absent when the record sets none.
                        project_id:
                          x-naturali-ref: projects
                          type: string
                        created_at:
                          type: string
                          format: date-time
                        updated_at:
                          type: string
                          format: date-time
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "500":
          description: Internal server error
    post:
      tags:
        - AI Providers
      summary: Create an AI provider
      description: |
        Creates a new LLM provider configuration.

        A `bedrock` or `vertex` record must carry a credential of its own — a
        linked `secret_id`, or an `apiKey` in `config`. Without one the provider
        SDK signs with the server's own credentials (the AWS default credential
        chain, Google Application Default Credentials), which is refused with
        `400 VALIDATION_FAILED` unless the deployment allows it.
      operationId: createAiProvider
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - provider
                - default_model
              additionalProperties: false
              properties:
                name:
                  type: string
                  description: Provider configuration name
                  example: OpenAI Production
                provider:
                  type: string
                  enum:
                    - openai
                    - anthropic
                    - google
                    - xai
                    - groq
                    - ollama
                    - azure
                    - bedrock
                    - vertex
                    - gateway
                    - custom
                    - naturali
                  description: LLM provider
                  example: openai
                default_model:
                  type: string
                  description: Default model to use
                  example: gpt-4
                secret_id:
                  x-naturali-ref: secrets
                  type: string
                  description: Secret ID containing API credentials. Required for `bedrock` and `vertex` unless
                    `config.apiKey` carries one.
                  example: sec_V1StGXR8Z5jdHi6B
                base_url:
                  type: string
                  description: Custom base URL for the provider
                config:
                  type: object
                  description: Additional provider-specific configuration
      responses:
        "201":
          description: AI provider created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  provider:
                    type: string
                  default_model:
                    type: string
                  project_id:
                    x-naturali-ref: projects
                    type: string
                  created_at:
                    type: string
                    format: date-time
                  updated_at:
                    type: string
                    format: date-time
        "400":
          description: Bad request (invalid provider or missing fields)
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "500":
          description: Internal server error
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/ai-providers/{ai_provider_id}:
    get:
      tags:
        - AI Providers
      summary: Get an AI provider
      description: Returns a specific AI provider configuration
      operationId: getAiProvider
      parameters:
        - name: ai_provider_id
          in: path
          required: true
          description: AI Provider ID
          schema:
            type: string
            example: aip_V1StGXR8Z5jdHi6B
      responses:
        "200":
          description: AI provider details
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  provider:
                    type: string
                  default_model:
                    type: string
                  project_id:
                    x-naturali-ref: projects
                    type: string
                  secret_id:
                    x-naturali-ref: secrets
                    type: string
                    nullable: true
                  base_url:
                    type: string
                  config:
                    type: object
                  created_at:
                    type: string
                    format: date-time
                  updated_at:
                    type: string
                    format: date-time
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: AI provider not found
    patch:
      tags:
        - AI Providers
      summary: Update an AI provider
      description: Updates an AI provider configuration
      operationId: updateAiProvider
      parameters:
        - name: ai_provider_id
          in: path
          required: true
          description: AI Provider ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                name:
                  type: string
                provider:
                  type: string
                  enum:
                    - openai
                    - anthropic
                    - google
                    - xai
                    - groq
                    - ollama
                    - azure
                    - bedrock
                    - vertex
                    - gateway
                    - custom
                    - naturali
                  description: LLM provider
                  example: openai
                default_model:
                  type: string
                secret_id:
                  x-naturali-ref: secrets
                  type: string
                  nullable: true
                base_url:
                  type: string
                config:
                  type: object
      responses:
        "200":
          description: AI provider updated successfully
        "400":
          description: Bad request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: AI provider not found
    delete:
      tags:
        - AI Providers
      summary: Delete an AI provider
      description: >
        Deletes an AI provider configuration.


        Live references — chats, agents, and model routes whose targets name this provider — always
        block deletion with `409 AI_PROVIDER_HAS_DEPENDENTS`; `force` does not override them, so
        delete or repoint those resources first. Soft dependents — price overrides and
        usage/generation records — also block with `409` unless `force=true`, which deletes the
        provider's price overrides and unlinks (nulls) its usage history, preserving those rows. The
        `409` body's `error.meta` reports the counts, a sample of offending IDs, and a `forcible`
        flag that is `true` when a `force=true` retry would succeed.
      operationId: deleteAiProvider
      parameters:
        - name: ai_provider_id
          in: path
          required: true
          description: AI Provider ID
          schema:
            type: string
        - name: force
          in: query
          required: false
          description: >
            When `true`, delete the provider's price overrides and unlink its usage history so a
            provider with only soft dependents can be removed. Has no effect on live references
            (chats, agents, model routes), which always block deletion.
          schema:
            type: boolean
            default: false
      responses:
        "204":
          description: AI provider deleted successfully
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: AI provider not found
        "409":
          description: >
            The AI provider still has dependents. Live references always block; soft dependents
            block unless force=true. See error.meta for counts, offending IDs, and the forcible
            flag.
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/ai-providers/{ai_provider_id}/models:
    get:
      tags:
        - AI Providers
      summary: List the models this provider can run
      description: >
        Asks the provider which models it can run, using this provider record's own credentials and
        configuration, and returns provider-native model ids — the same strings `default_model` and
        an agent's `model` carry.

        Which models are reachable is a property of the credential, not of the provider type: a
        Vertex provider sees only the publisher models its Google Cloud project and location serve,
        and a Bedrock provider only the foundation models enabled in its region. Reading the list is
        how a caller avoids pinning a model that fails at generation time.

        Not every provider type can answer. `azure` lists deployments an operator named rather than
        models, and `ollama` lists whatever was pulled onto that host, so both return `400
        MODEL_LISTING_UNSUPPORTED`.

        Listing resolves credentials the same way generation does, so a record that can generate can
        list. The API-key types (`openai`, `groq`, `xai`, `gateway`, `custom`, `anthropic`,
        `google`) use the record's linked secret and cannot list without one. `bedrock` and `vertex`
        use the linked secret — IAM keys or a Bedrock API key, a Google service-account key. A
        record with no `secret_id` would fall back to the server's own credentials (the AWS default
        credential chain, Google Application Default Credentials); it can list only on a deployment
        that allows a record to use them, and returns `400 AI_PROVIDER_MISCONFIGURED` otherwise.

        A Vertex record needs no `config.project` when its secret is a service-account key, since
        the key file names its own project. A Vertex record in express mode (API key) cannot list at
        all: the publisher-model listing rejects API keys and needs a credential that asserts a
        principal, so it returns `400 MODEL_LISTING_UNSUPPORTED`.

        The Vertex answer is the publisher catalogue the record's `config.location` region serves.
        The project behind the credential is billed and quota'd for the call but does not filter the
        result, so a listed model may still be unavailable to that project at generation time.
      operationId: listAiProviderModels
      parameters:
        - name: ai_provider_id
          in: path
          required: true
          description: AI Provider ID
          schema:
            type: string
            example: aip_V1StGXR8Z5jdHi6B
      responses:
        "200":
          description: The models this provider can run
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderModelsResponse"
        "400":
          description: >
            The provider type or authentication mode cannot enumerate models (including Vertex
            express mode), or the record is missing configuration the listing needs (a Vertex
            project from either `config.project` or the service-account key file, a Bedrock region,
            or — for the API-key provider types — a linked secret).
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: AI provider not found
        "502":
          description: The provider rejected the listing request
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/ai-providers/{ai_provider_id}/prices:
    get:
      tags:
        - AI Providers
      summary: List per-provider price overrides
      description: >
        Returns the per-provider price overrides for this AI provider instance. An override prices
        this specific provider (e.g. an enterprise-negotiated rate or a gateway with markup) and
        wins over the global default at cost time. Authorized by the caller's access to the
        provider's project — so, unlike the global price book, a project's own overrides are visible
        here.
      operationId: getAiProviderPrices
      parameters:
        - name: ai_provider_id
          in: path
          required: true
          description: AI Provider ID
          schema:
            type: string
            example: aip_V1StGXR8Z5jdHi6B
      responses:
        "200":
          description: The provider's price overrides
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderPricesResponse"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: AI provider not found
    put:
      tags:
        - AI Providers
      summary: Upsert per-provider price overrides
      description: >
        Upserts price overrides for this AI provider instance, keyed on (model, effective_from). The
        provider slug is taken from the AI provider itself, so only the model, rates, and
        effective_from are supplied. Authorized by the caller's access to the provider's project.
        `effective_from` must be in the future once the (model, component) has a price row — past
        prices are immutable, so corrections ship as new future-dated rows. A first price for a
        (model, component) nothing prices yet may be dated now or earlier, so a new provider is
        never live and unpriced.
      operationId: updateAiProviderPrices
      x-naturali-agent-exclude: true
      parameters:
        - name: ai_provider_id
          in: path
          required: true
          description: AI Provider ID
          schema:
            type: string
            example: aip_V1StGXR8Z5jdHi6B
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpsertProviderPricesRequest"
      responses:
        "200":
          description: The upserted price overrides
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderPricesResponse"
        "400":
          description: Bad Request (e.g. non-future effective_from)
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: AI provider not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
components:
  schemas:
    ProviderPrice:
      type: object
      properties:
        id:
          type: string
          description: Public ID of the price row
          example: price_V1StGXR8Z5jdHi6B
        ai_provider_id:
          x-naturali-ref: ai-providers
          type: string
          description: The AI provider instance this override prices
          example: aip_V1StGXR8Z5jdHi6B
        meter_type:
          type: string
          description: Always `llm_tokens` for provider price overrides
        provider:
          type: string
          description: Provider slug (taken from the AI provider instance)
          example: openai
        model:
          type: string
          example: gpt-4o
        component:
          type: string
          description: The token component this row prices (`input_tokens`, `output_tokens`, `cached_tokens`,
            `cache_write_tokens`)
        unit:
          type: string
          description: Always `token` for provider price overrides
        unit_price:
          type: number
          description: USD per token for this component
        effective_from:
          type: string
          format: date-time
          description: The row with the latest effective_from <= now() prices a call
        created_at:
          type: string
          format: date-time
    ProviderPricesResponse:
      type: object
      properties:
        prices:
          type: array
          items:
            $ref: "#/components/schemas/ProviderPrice"
    UpsertProviderPricesRequest:
      type: object
      required:
        - prices
      additionalProperties: false
      properties:
        prices:
          type: array
          items:
            type: object
            required:
              - model
              - component
              - unit
              - unit_price
              - effective_from
            additionalProperties: false
            properties:
              model:
                type: string
                example: gpt-4o
              component:
                type: string
                description: The token component this row prices (`input_tokens`, `output_tokens`, `cached_tokens`,
                  `cache_write_tokens`)
              unit:
                type: string
                description: Always `token` for token pricing
              unit_price:
                type: number
                description: USD per token for this component
              effective_from:
                type: string
                format: date-time
                description: Must be in the future; past prices are immutable
    ProviderModelsResponse:
      type: object
      required:
        - provider
        - models
      properties:
        provider:
          type: string
          description: The provider slug the listing came from
          example: vertex
        models:
          type: array
          items:
            type: object
            required:
              - id
            properties:
              id:
                type: string
                description: >
                  The provider-native model id, ready to use as `default_model` or an agent's `model`
                example: gemini-2.5-flash
              display_name:
                type: string
                description: The provider's own human-readable name, when it reports one
                example: Gemini 2.5 Flash
              vendor:
                type: string
                description: Who makes the model, when the provider reports it
                example: google
              input_modalities:
                type: array
                description: Lowercased input modalities, when the provider reports them
                items:
                  type: string
                example:
                  - text
                  - image
              output_modalities:
                type: array
                description: Lowercased output modalities, when the provider reports them
                items:
                  type: string
                example:
                  - text
              streaming:
                type: boolean
                description: Whether the model supports streaming responses
              lifecycle:
                type: string
                description: >
                  `active`, `legacy` or `deprecated`, as the provider reports it. A model that is
                  not `active` still serves today but should not be pinned by anything new.
                enum:
                  - active
                  - legacy
                  - deprecated
              inference_types:
                type: array
                description: >
                  Lowercased inference types the model supports, when reported. A Bedrock model
                  offering only `inference_profile` must be invoked through a cross-region profile
                  id rather than the bare model id.
                items:
                  type: string
                example:
                  - on_demand
                  - inference_profile
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Project public ID (proj_ prefix).
      schema:
        type: string
        example: proj_V1StGXR8Z5jdHi6B
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A naturali API key (nat_sk_…) or a session JWT.
    oauth2:
      type: oauth2
      description: "A connected app's OAuth access token, issued by this API's authorization server
        (discovery: /.well-known/oauth-authorization-server). Its one scope carries every operation,
        confined to the projects the user chose when approving the app."
      flows:
        authorizationCode:
          authorizationUrl: https://api.naturali.ai/authorize
          tokenUrl: https://api.naturali.ai/token
          refreshUrl: https://api.naturali.ai/token
          scopes:
            mcp:access: Every operation this API serves, on the projects the grant covers.
