openapi: 3.1.0
info:
  title: White-label AI Gateway API
  version: 1.0.0
  description: |
    Provider isimlerini ve iç altyapı ayrıntılarını istemciden gizleyen,
    OpenAI uyumlu yapay zekâ gateway sözleşmesi. Kanonik üretim adresi
    `https://api.yapayzekaniz.com/v1`; uyumluluk alias'ı
    `https://yapayzekaniz.com/v1` aynı gateway'e gider.
    API anahtarı aktif bir paketle ilişkilidir. Paket süresi dolduğunda anahtar
    silinmez ancak yeni bir paket etkinleşene kadar istekler `402` ile reddedilir.
    Günlük ve toplam istek limitleri atomik olarak uygulanır; limit aşımı veya
    PAYG kapalı/bakiye yetersiz durumlarında upstream'e istek gönderilmez.
  contact:
    name: Teknik destek
    email: uygarturksutcu@gmail.com
servers:
  - url: https://api.yapayzekaniz.com/v1
    description: Önerilen kanonik üretim API adresi
  - url: https://yapayzekaniz.com/v1
    description: Root-domain uyumluluk alias'ı
tags:
  - name: Catalog
  - name: Completions
  - name: Images
  - name: Billing
  - name: Panel AI
security:
  - bearerAuth: []
paths:
  /models:
    get:
      tags: [Catalog]
      operationId: listModels
      summary: Anahtarın erişebildiği modelleri listele
      responses:
        '200':
          description: OpenAI uyumlu model listesi
          headers:
            X-Request-Id: { $ref: '#/components/headers/RequestId' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelList' }
              example:
                object: list
                data:
                  - id: gateway-default
                    object: model
                    owned_by: gateway
                    status: active
        '401': { $ref: '#/components/responses/InvalidApiKey' }
        '403': { $ref: '#/components/responses/HostNotAllowed' }
  /chat/completions:
    post:
      tags: [Completions]
      operationId: createChatCompletion
      summary: Chat completion oluştur
      description: >-
        `stream=true` gönderildiğinde yanıt `text/event-stream` olarak akar ve
        `data: [DONE]` ile tamamlanır.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ChatCompletionRequest' }
            example:
              model: gateway-default
              messages:
                - role: user
                  content: Merhaba!
              stream: false
      responses:
        '200':
          description: Completion veya SSE akışı
          headers:
            X-Request-Id: { $ref: '#/components/headers/RequestId' }
            X-Accel-Buffering:
              description: Streaming yanıtlarında proxy buffering durumunu belirtir.
              schema: { type: string, example: 'no' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ChatCompletion' }
            text/event-stream:
              schema: { type: string }
        '400':
          description: model veya messages alanı geçersiz
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
        '401': { $ref: '#/components/responses/InvalidApiKey' }
        '402':
          description: Aktif paket kotası, günlük limiti veya PAYG bakiyesi yetersiz. Hiçbir kullanım kaydı yazılmaz.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
        '404':
          description: Model bulunamadı
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
        '503':
          description: Model sağlayıcısı geçici olarak erişilemiyor
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
  /images/generations:
    post:
      tags: [Images]
      operationId: createImageGeneration
      summary: Görsel üret
      description: >-
        Görsel modeliyle tek bir görsel üretir. İstek, anahtarın aktif paket
        kapsamı ve günlük/toplam limitleri kontrol edildikten sonra işlenir.
        Paket veya PAYG hakkı yoksa `402` döner ve kullanım kaydı oluşturulmaz.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ImageGenerationRequest' }
            example:
              model: image-generator
              prompt: Minimal bir ürün illüstrasyonu
              size: 1024x1024
      responses:
        '200':
          description: Üretilen görsel URL'si veya veri URL'si
          headers:
            X-Request-Id: { $ref: '#/components/headers/RequestId' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ImageGenerationResponse' }
        '400':
          description: prompt, boyut veya model türü geçersiz
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
        '401': { $ref: '#/components/responses/InvalidApiKey' }
        '402': { $ref: '#/components/responses/PackageRequired' }
        '404':
          description: Görsel modeli bulunamadı
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
        '503':
          description: Görsel sağlayıcısı geçici olarak erişilemiyor
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
  /balance:
    get:
      tags: [Billing]
      operationId: getBalance
      summary: Paket veya PAYG durumunu getir
      responses:
        '200':
          description: Anahtar moduna göre bakiye/kota özeti
          headers:
            X-Request-Id: { $ref: '#/components/headers/RequestId' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Balance' }
        '401': { $ref: '#/components/responses/InvalidApiKey' }
  /usage:
    get:
      tags: [Billing]
      operationId: listUsage
      summary: Anahtarın bağlı olduğu host için kullanım kayıtlarını getir
      responses:
        '200':
          description: Token ve model kullanım listesi
          headers:
            X-Request-Id: { $ref: '#/components/headers/RequestId' }
          content:
            application/json:
              schema:
                type: object
                required: [object, mode, data]
                properties:
                  object: { type: string, const: list }
                  mode: { type: string, enum: [package, payg] }
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/UsageRecord' }
        '401': { $ref: '#/components/responses/InvalidApiKey' }
  /api/panel/ai/chat:
    post:
      servers:
        - url: https://yapayzekaniz.com
          description: Üretim paneli
      tags: [Panel AI]
      operationId: panelChat
      summary: Müşteri panelinden sohbet yanıtı üret
      description: >-
        Panel oturumu (web cookie) ile çalışan kolaylaştırılmış uçtur. İstek,
        kullanıcının aktif paketine bağlı bir anahtarla gateway'e iletilir;
        aktif paket yoksa `422`, kota veya bakiye yetersizse gateway'den `402`
        döner. `api_key_id` verilmezse en son etkin anahtar seçilir.
      security:
        - sessionCookie: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PanelChatRequest' }
      responses:
        '200':
          description: OpenAI uyumlu chat completion
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ChatCompletion' }
        '401': { $ref: '#/components/responses/SessionRequired' }
        '402': { $ref: '#/components/responses/PackageRequired' }
        '422':
          description: Aktif API anahtarı veya paket bulunamadı
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
  /api/panel/ai/image:
    post:
      servers:
        - url: https://yapayzekaniz.com
          description: Üretim paneli
      tags: [Panel AI]
      operationId: panelImage
      summary: Müşteri panelinden görsel üret
      description: >-
        Panel oturumu ile görsel üretim isteği gönderir. Paket kapsamı ve
        limitleri gateway tarafından uygulanır; hak yoksa `402` döner.
      security:
        - sessionCookie: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PanelImageRequest' }
      responses:
        '200':
          description: Görsel üretim yanıtı
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ImageGenerationResponse' }
        '401': { $ref: '#/components/responses/SessionRequired' }
        '402': { $ref: '#/components/responses/PackageRequired' }
        '422':
          description: Prompt/model doğrulaması veya etkin anahtar eksik
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: wl_live_…
      description: Panelde üretilen white-label API anahtarı.
    sessionCookie:
      type: apiKey
      in: cookie
      name: laravel_session
      description: Müşteri panelinde oturum açıldığında tarayıcı tarafından gönderilen oturum çerezi.
  headers:
    RequestId:
      description: Log ve destek takibi için benzersiz istek kimliği.
      schema: { type: string, example: req_0198a75d }
  responses:
    InvalidApiKey:
      description: API anahtarı eksik, geçersiz, iptal edilmiş veya süresi dolmuş
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorEnvelope' }
          example:
            error:
              message: Geçersiz API anahtarı.
              type: authentication_error
              param: null
              code: invalid_api_key
            request_id: req_0198a75d
    HostNotAllowed:
      description: Anahtar bu alan adı/host için yetkili değil
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorEnvelope' }
    PackageRequired:
      description: Aktif paket kotası veya PAYG bakiyesi yok; istek ve kullanım kaydı oluşturulmadı.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorEnvelope' }
          example:
            error:
              message: Aktif paket veya kullandıkça öde bakiyesi gerekli.
              type: billing_error
              param: null
              code: package_required
            request_id: req_0198a75d
    SessionRequired:
      description: Panel oturumu gerekli
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorEnvelope' }
  schemas:
    Model:
      type: object
      required: [id, object]
      properties:
        id: { type: string, example: gateway-default }
        object: { type: string, const: model }
        owned_by: { type: string, example: gateway }
        name: { type: string, example: Gateway Default }
        context_length: { type: [integer, 'null'], example: 128000 }
        status: { type: string, example: active }
        modality: { type: string, example: text }
        architecture: { type: object, additionalProperties: true }
    ModelList:
      type: object
      required: [object, data]
      properties:
        object: { type: string, const: list }
        data:
          type: array
          items: { $ref: '#/components/schemas/Model' }
    ChatMessage:
      type: object
      required: [role, content]
      properties:
        role: { type: string, enum: [system, user, assistant, tool] }
        content:
          oneOf:
            - { type: string }
            - { type: array, items: { type: object, additionalProperties: true } }
    ChatCompletionRequest:
      type: object
      required: [model, messages]
      properties:
        model: { type: string, example: gateway-default }
        messages:
          type: array
          minItems: 1
          items: { $ref: '#/components/schemas/ChatMessage' }
        stream: { type: boolean, default: false }
        temperature: { type: number, minimum: 0, maximum: 2 }
        max_tokens: { type: integer, minimum: 1 }
      additionalProperties: true
    ImageGenerationRequest:
      type: object
      required: [model, prompt]
      properties:
        model: { type: string, example: image-generator }
        prompt: { type: string, minLength: 3, maxLength: 4000 }
        size: { type: string, enum: [512x512, 1024x1024, 1024x1536, 1536x1024], default: 1024x1024 }
        n: { type: integer, minimum: 1, maximum: 1, default: 1 }
        response_format: { type: string, enum: [url, b64_json], default: url }
      additionalProperties: true
    ImageGenerationResponse:
      type: object
      required: [created, data]
      properties:
        created: { type: integer }
        model: { type: string }
        data:
          type: array
          minItems: 1
          items:
            type: object
            properties:
              url: { type: string, format: uri }
              b64_json: { type: string }
              revised_prompt: { type: string }
    PanelChatRequest:
      allOf:
        - $ref: '#/components/schemas/ChatCompletionRequest'
        - type: object
          properties:
            api_key_id: { type: string, maxLength: 40 }
    PanelImageRequest:
      allOf:
        - $ref: '#/components/schemas/ImageGenerationRequest'
        - type: object
          properties:
            api_key_id: { type: string, maxLength: 40 }
    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 }
    ChatCompletion:
      type: object
      required: [id, object, created, model, choices]
      properties:
        id: { type: string, example: chatcmpl-0198a75d }
        object: { type: string, example: chat.completion }
        created: { type: integer }
        model: { type: string }
        choices:
          type: array
          items: { type: object, additionalProperties: true }
        usage: { $ref: '#/components/schemas/Usage' }
    Balance:
      type: object
      required: [object, mode, used_tokens]
      properties:
        object: { type: string, const: balance }
        mode: { type: string, enum: [package, payg] }
        quota: { type: [integer, 'null'] }
        used_tokens: { type: integer }
        remaining_tokens: { type: [integer, 'null'] }
        package:
          allOf:
            - $ref: '#/components/schemas/PackageEntitlement'
            - type: object
              nullable: true
        payg:
          type: object
          nullable: true
          properties:
            enabled: { type: boolean }
            balance_usd: { type: number, format: double, minimum: 0 }
          additionalProperties: true
    PackageEntitlement:
      type: object
      description: Anahtarın aktif paketinden hesaplanan anlık haklar.
      properties:
        id: { type: [integer, 'null'] }
        name: { type: [string, 'null'] }
        starts_at: { type: [string, 'null'], format: date-time }
        ends_at: { type: [string, 'null'], format: date-time }
        daily_limit: { type: [integer, 'null'], minimum: 0 }
        daily_remaining: { type: [integer, 'null'], minimum: 0 }
        total_limit: { type: [integer, 'null'], minimum: 0 }
        total_remaining: { type: [integer, 'null'], minimum: 0 }
        carryover: { type: boolean }
      additionalProperties: true
    UsageRecord:
      type: object
      properties:
        requestId: { type: string }
        model: { type: string }
        promptTokens: { type: integer }
        completionTokens: { type: integer }
        totalTokens: { type: integer }
        mode: { type: string, enum: [package, payg] }
        createdAt: { type: string, format: date-time }
    ErrorEnvelope:
      type: object
      required: [error, request_id]
      properties:
        error:
          type: object
          required: [message, type, code]
          properties:
            message: { type: string }
            type: { type: string }
            param: { type: [string, 'null'] }
            code: { type: string }
        request_id: { type: string }
