openapi: 3.0.3
info:
  title: Invozon API
  description: |
    Invozon REST API — Türkiye pazaryeri satıcıları için tek API'den
    Trendyol, Hepsiburada, N11, Amazon TR ve diğer 10 pazaryeri yönetimi.
    Ürün, stok, sipariş, kargo, e-fatura ve AI üretkenlik tek arabirimde.

    **Tam dokümantasyon:** https://docs.invozon.com
    **Geliştirici desteği:** hello@ozy2.com
  version: "1.0.0"
  contact:
    name: Invozon API Desteği
    email: hello@ozy2.com
    url: https://docs.invozon.com
  license:
    name: Proprietary
    url: https://ozy2.com/sozlesmelerimiz/invozon-hizmet-eki

servers:
  - url: https://api.invozon.com/v1
    description: Production
  - url: https://api-demo.invozon.com/v1
    description: Demo (izole veritabanı, test için)

security:
  - bearerAuth: []

tags:
  - name: Authentication
    description: Token alma, yenileme, çıkış, şifre yönetimi
  - name: Products
    description: Ürün CRUD ve bulk import
  - name: Orders
    description: Sipariş listesi, detayı, statü güncelleme
  - name: Marketplaces
    description: Pazaryeri bağlama ve pazaryeri-spesifik endpoint'ler
  - name: Cargo
    description: Kargo etiketi üretimi ve takibi
  - name: Invoices
    description: e-Fatura / e-Arşiv (Nilvera, Foriba, Mikro, QNB)
  - name: Webhooks
    description: Olay tabanlı bildirimler
  - name: Jobs
    description: Asenkron iş takibi (bulk import, ilk senkron)

paths:
  # =====================================================
  # AUTH
  # =====================================================
  /auth/token:
    post:
      tags: [Authentication]
      summary: E-posta + şifre ile token al
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginRequest"
      responses:
        "200":
          description: Token başarıyla üretildi
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TokenResponse" }
        "401":
          description: Geçersiz e-posta veya şifre
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }
        "429":
          description: Çok fazla deneme, geçici kilit
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Error" }

  /auth/refresh:
    post:
      tags: [Authentication]
      summary: Refresh token ile yeni access token üret
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [refresh_token]
              properties:
                refresh_token: { type: string }
      responses:
        "200":
          description: Yeni access token
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TokenResponse" }
        "401":
          description: Refresh token geçersiz veya süresi doldu

  /auth/logout:
    post:
      tags: [Authentication]
      summary: Mevcut token'ı geçersiz kıl
      responses:
        "204":
          description: Başarıyla çıkış yapıldı

  /auth/set-password:
    post:
      tags: [Authentication]
      summary: Kullanıcının şifresini ayarla / değiştir
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [new_password]
              properties:
                new_password:
                  type: string
                  minLength: 10
                  description: En az 10 karakter
                current_password:
                  type: string
                  description: Mevcut şifre değiştiriliyorsa zorunlu
      responses:
        "200":
          description: Şifre güncellendi
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  has_password: { type: boolean }

  /auth/has-password:
    get:
      tags: [Authentication]
      summary: Kullanıcının şifresi var mı?
      responses:
        "200":
          description: Şifre durumu
          content:
            application/json:
              schema:
                type: object
                properties:
                  has_password: { type: boolean }

  /auth/me:
    get:
      tags: [Authentication]
      summary: Aktif kullanıcı bilgilerini al
      responses:
        "200":
          description: Kullanıcı bilgisi
          content:
            application/json:
              schema: { $ref: "#/components/schemas/User" }

  # =====================================================
  # PRODUCTS
  # =====================================================
  /products:
    get:
      tags: [Products]
      summary: Ürünleri listele
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: per_page
          in: query
          schema: { type: integer, default: 25, maximum: 100 }
        - name: search
          in: query
          schema: { type: string }
          description: SKU, başlık veya barkod araması
        - name: marketplace
          in: query
          schema: { type: string }
          description: Yalnız belirli pazaryerinde aktif ürünleri filtrele
      responses:
        "200":
          description: Ürün listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Product" }
                  pagination: { $ref: "#/components/schemas/Pagination" }

    post:
      tags: [Products]
      summary: Yeni ürün ekle
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ProductInput" }
      responses:
        "201":
          description: Ürün oluşturuldu
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Product" }
        "409":
          description: Aynı SKU'lu ürün zaten var

  /products/{id}:
    get:
      tags: [Products]
      summary: Ürün detayı
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Ürün detayı
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Product" }
        "404":
          description: Ürün bulunamadı

    patch:
      tags: [Products]
      summary: Ürün güncelle
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ProductInput" }
      responses:
        "200":
          description: Güncellendi
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Product" }

    delete:
      tags: [Products]
      summary: Ürünü sil (soft delete)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "204":
          description: Silindi

  /products/bulk:
    post:
      tags: [Products]
      summary: Toplu ürün ekle / güncelle (max 500/istek)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [products]
              properties:
                products:
                  type: array
                  maxItems: 500
                  items: { $ref: "#/components/schemas/ProductInput" }
      responses:
        "202":
          description: İş kuyruğa alındı, job_id ile takip et
          content:
            application/json:
              schema:
                type: object
                properties:
                  accepted: { type: integer }
                  rejected: { type: integer }
                  rejections:
                    type: array
                    items:
                      type: object
                      properties:
                        index: { type: integer }
                        sku: { type: string }
                        code: { type: string }
                        field: { type: string }
                  job_id: { type: string }

  /products/{id}/stock:
    put:
      tags: [Products]
      summary: Stok güncelle (tüm pazaryerlerine senkron)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [stock]
              properties:
                stock: { type: integer, minimum: 0 }
      responses:
        "200":
          description: Stok güncellendi

  # =====================================================
  # ORDERS
  # =====================================================
  /orders:
    get:
      tags: [Orders]
      summary: Sipariş listesi
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: status
          in: query
          schema:
            type: string
            enum: [created, processing, shipped, delivered, cancelled, returned]
        - name: marketplace
          in: query
          schema: { type: string }
        - name: from_date
          in: query
          schema: { type: string, format: date }
        - name: to_date
          in: query
          schema: { type: string, format: date }
      responses:
        "200":
          description: Sipariş listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Order" }
                  pagination: { $ref: "#/components/schemas/Pagination" }

  /orders/{id}:
    get:
      tags: [Orders]
      summary: Sipariş detayı
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Sipariş detayı
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Order" }

  /orders/{id}/status:
    patch:
      tags: [Orders]
      summary: Sipariş statüsünü güncelle (pazaryerine de yansır)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [status]
              properties:
                status:
                  type: string
                  enum: [processing, shipped, delivered]
                tracking_number: { type: string }
                cargo_provider: { type: string }
      responses:
        "200":
          description: Statü güncellendi

  # =====================================================
  # MARKETPLACES
  # =====================================================
  /marketplaces:
    get:
      tags: [Marketplaces]
      summary: Bağlı pazaryerlerinin listesi
      responses:
        "200":
          description: Pazaryeri listesi
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/MarketplaceConnection" }

  /marketplaces/{slug}/connect:
    post:
      tags: [Marketplaces]
      summary: Pazaryeri bağla
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
            enum: [trendyol, hepsiburada, n11, amazon-tr, ciceksepeti, pttavm, pazarama, beymen, flo, boyner, koctas, lcw, teknosa, woocommerce]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              description: |
                Auth alanları pazaryerine göre değişir:
                - **trendyol**: supplier_id, api_key, api_secret
                - **hepsiburada**: merchant_id, username, password
                - **n11**: api_key, api_secret
                - **amazon-tr**: OAuth flow (LWA) tetiklenir
      responses:
        "200":
          description: Bağlantı başarılı, ilk senkron başladı
          content:
            application/json:
              schema:
                type: object
                properties:
                  connection_id: { type: string }
                  sync_job_id: { type: string }

  /marketplaces/{slug}/disconnect:
    post:
      tags: [Marketplaces]
      summary: Pazaryeri bağlantısını kopar
      parameters:
        - name: slug
          in: path
          required: true
          schema: { type: string }
      responses:
        "204":
          description: Bağlantı koparıldı

  /marketplaces/{slug}/products:
    get:
      tags: [Marketplaces]
      summary: Pazaryerine özgü ürün listesi
      parameters:
        - name: slug
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Pazaryerine listelenmiş ürünler

  /marketplaces/{slug}/orders:
    get:
      tags: [Marketplaces]
      summary: Pazaryerine özgü sipariş listesi
      parameters:
        - name: slug
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Pazaryerine ait siparişler

  # =====================================================
  # CARGO
  # =====================================================
  /cargo/labels:
    post:
      tags: [Cargo]
      summary: Kargo etiketi üret
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order_id, provider]
              properties:
                order_id: { type: string }
                provider:
                  type: string
                  enum: [aras, yurtici, mng, ptt, surat, hepsijet, trendyol-express]
                package_count:
                  type: integer
                  default: 1
                weight_kg: { type: number }
      responses:
        "200":
          description: Etiket PDF URL'i + tracking number
          content:
            application/json:
              schema:
                type: object
                properties:
                  label_url: { type: string, format: uri }
                  tracking_number: { type: string }
                  provider: { type: string }

  /cargo/tracking/{tracking_number}:
    get:
      tags: [Cargo]
      summary: Kargo durumu sorgula
      parameters:
        - name: tracking_number
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Kargo durumu
          content:
            application/json:
              schema:
                type: object
                properties:
                  tracking_number: { type: string }
                  status: { type: string }
                  events:
                    type: array
                    items:
                      type: object
                      properties:
                        timestamp: { type: string, format: date-time }
                        description: { type: string }
                        location: { type: string }

  # =====================================================
  # INVOICES
  # =====================================================
  /invoices:
    get:
      tags: [Invoices]
      summary: Fatura listesi
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1 }
        - name: provider
          in: query
          schema:
            type: string
            enum: [nilvera, foriba, mikro, qnb-efinans, uyumsoft]
      responses:
        "200":
          description: Fatura listesi
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Invoice" }
                  pagination: { $ref: "#/components/schemas/Pagination" }

    post:
      tags: [Invoices]
      summary: Manuel e-fatura kes (sipariş otomatik dışında)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [order_id, provider]
              properties:
                order_id: { type: string }
                provider:
                  type: string
                  enum: [nilvera, foriba, mikro, qnb-efinans]
      responses:
        "201":
          description: Fatura kesildi
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Invoice" }

  /invoices/{id}:
    get:
      tags: [Invoices]
      summary: Fatura detayı + PDF URL
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Fatura
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Invoice" }

  # =====================================================
  # WEBHOOKS
  # =====================================================
  /webhooks:
    get:
      tags: [Webhooks]
      summary: Kayıtlı webhook'ların listesi
      responses:
        "200":
          description: Webhook listesi
          content:
            application/json:
              schema:
                type: array
                items: { $ref: "#/components/schemas/Webhook" }

    post:
      tags: [Webhooks]
      summary: Yeni webhook kaydet
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url: { type: string, format: uri, description: HTTPS zorunlu }
                events:
                  type: array
                  items:
                    type: string
                    enum:
                      - order.created
                      - order.updated
                      - order.cancelled
                      - order.returned
                      - product.stock_changed
                      - product.sold_out
                      - invoice.created
                      - invoice.failed
                      - marketplace.connected
                      - marketplace.disconnected
                active: { type: boolean, default: true }
      responses:
        "201":
          description: Webhook oluşturuldu
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Webhook"
                  - type: object
                    properties:
                      secret:
                        type: string
                        description: Yalnız bu yanıtta görünür; imza doğrulama için.

  /webhooks/{id}:
    delete:
      tags: [Webhooks]
      summary: Webhook kaydını sil
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "204":
          description: Silindi

  # =====================================================
  # JOBS
  # =====================================================
  /jobs/{id}:
    get:
      tags: [Jobs]
      summary: Asenkron iş durumunu sorgula
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: İş durumu
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  status:
                    type: string
                    enum: [queued, running, completed, failed]
                  progress: { type: integer, description: "0-100" }
                  result:
                    type: object
                    description: İş bittiyse sonuç (örn. bulk import raporu)
                  error: { type: string }
                  created_at: { type: string, format: date-time }
                  updated_at: { type: string, format: date-time }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  schemas:
    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email: { type: string, format: email }
        password: { type: string, minLength: 10 }

    TokenResponse:
      type: object
      properties:
        access_token: { type: string }
        refresh_token: { type: string }
        token_type: { type: string, default: Bearer }
        expires_in: { type: integer, description: Saniye (24h = 86400) }
        user: { $ref: "#/components/schemas/User" }

    User:
      type: object
      properties:
        id: { type: integer }
        email: { type: string }
        name: { type: string }
        role:
          type: string
          enum: [owner, admin, staff, viewer]
        tenant_id: { type: string }

    Product:
      type: object
      properties:
        id: { type: string }
        sku: { type: string }
        barcode: { type: string }
        title: { type: string }
        description: { type: string }
        price: { type: number }
        sale_price: { type: number }
        currency: { type: string, default: TRY }
        stock: { type: integer }
        category: { type: string }
        brand: { type: string }
        images:
          type: array
          items: { type: string, format: uri }
        marketplaces:
          type: array
          items: { type: string }
          description: Aktif pazaryeri slug listesi
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    ProductInput:
      type: object
      required: [sku, title, price]
      properties:
        sku: { type: string }
        barcode: { type: string }
        title: { type: string, maxLength: 100 }
        description: { type: string }
        price: { type: number, minimum: 0 }
        sale_price: { type: number }
        currency: { type: string, default: TRY }
        stock: { type: integer, minimum: 0 }
        category: { type: string }
        brand: { type: string }
        images:
          type: array
          items: { type: string, format: uri }

    Order:
      type: object
      properties:
        id: { type: string }
        marketplace: { type: string }
        marketplace_order_id: { type: string }
        status:
          type: string
          enum: [created, processing, shipped, delivered, cancelled, returned]
        customer:
          type: object
          properties:
            name: { type: string }
            phone: { type: string }
            email: { type: string }
        items:
          type: array
          items:
            type: object
            properties:
              sku: { type: string }
              title: { type: string }
              quantity: { type: integer }
              price: { type: number }
        total: { type: number }
        currency: { type: string }
        shipping_address:
          type: object
          properties:
            line1: { type: string }
            line2: { type: string }
            city: { type: string }
            district: { type: string }
            postal_code: { type: string }
            country: { type: string, default: TR }
        billing_address:
          type: object
          properties:
            tax_office: { type: string }
            tax_number: { type: string }
            line1: { type: string }
            city: { type: string }
        tracking_number: { type: string }
        cargo_provider: { type: string }
        invoice_id: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }

    Invoice:
      type: object
      properties:
        id: { type: string }
        order_id: { type: string }
        provider:
          type: string
          enum: [nilvera, foriba, mikro, qnb-efinans, uyumsoft]
        type:
          type: string
          enum: [earsiv, efatura]
        number: { type: string, description: GİB onaylı numara (varsa) }
        status:
          type: string
          enum: [pending, sent, accepted, rejected, cancelled]
        pdf_url: { type: string, format: uri }
        xml_url: { type: string, format: uri }
        total: { type: number }
        currency: { type: string }
        issued_at: { type: string, format: date-time }

    MarketplaceConnection:
      type: object
      properties:
        slug:
          type: string
          enum: [trendyol, hepsiburada, n11, amazon-tr, ciceksepeti, pttavm, pazarama, beymen, flo, boyner, koctas, lcw, teknosa, woocommerce]
        connected: { type: boolean }
        connected_at: { type: string, format: date-time }
        last_sync_at: { type: string, format: date-time }
        product_count: { type: integer }
        active_listing_count: { type: integer }

    Webhook:
      type: object
      properties:
        id: { type: string }
        url: { type: string, format: uri }
        events:
          type: array
          items: { type: string }
        active: { type: boolean }
        created_at: { type: string, format: date-time }
        last_delivered_at: { type: string, format: date-time }
        last_status_code: { type: integer }

    Pagination:
      type: object
      properties:
        page: { type: integer }
        per_page: { type: integer }
        total: { type: integer }
        total_pages: { type: integer }

    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: domain.specific_code biçiminde sabit string
            message:
              type: string
              description: Türkçe kullanıcı mesajı
            details:
              type: object
              description: Doğrulama hatasında alan-bazlı detay
            request_id:
              type: string
              description: Destek ile iletişimde kullanılır
