openapi: 3.0.3
info:
  title: KargoJet Partner API
  version: 1.0.0
  description: KargoJet gönderi, taşıyıcı seçimi, etiket ve takip işlemleri için partner API.
servers:
  - url: https://api.kargojet.com/partner-api/v1
    description: Production
  - url: https://sandbox-api.kargojet.com/partner-api/v1
    description: Sandbox
tags:
  - name: Shipments
    description: Gönderi ve taşıyıcı işlemleri
security:
  - bearerAuth: []
paths:
  /shipments:
    post:
      tags: [Shipments]
      summary: Gönderi oluştur
      operationId: createShipment
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShipmentCreate'
      responses:
        '200':
          description: Gönderi oluşturuldu
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
    get:
      tags: [Shipments]
      summary: Gönderileri listele
      operationId: listShipments
      parameters:
        - name: q
          in: query
          schema: { type: string }
        - name: status
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, minimum: 1, maximum: 100, default: 100 }
        - name: offset
          in: query
          schema: { type: integer, minimum: 0, default: 0 }
      responses:
        '200':
          description: Gönderi listesi
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Shipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /shipments/{shipment_id}:
    get:
      tags: [Shipments]
      summary: Gönderi detayı getir
      operationId: getShipment
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Gönderi detayı
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /shipments/quote:
    post:
      tags: [Shipments]
      summary: Taşıyıcı tekliflerini getir
      operationId: quoteShipment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [shipment_id]
              properties:
                shipment_id: { type: string }
      responses:
        '200':
          description: Taşıyıcı teklifleri
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/QuoteOption'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /shipments/choose-carrier:
    post:
      tags: [Shipments]
      summary: Taşıyıcı seç
      operationId: chooseCarrier
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [shipment_id, carrier_slug]
              properties:
                shipment_id: { type: string }
                carrier_slug: { type: string }
                rate_source:
                  type: string
                  enum: [kargojet, own_account]
                  default: kargojet
                carrier_account_id:
                  type: string
                  nullable: true
      responses:
        '200':
          description: Taşıyıcı seçildi
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /shipments/{shipment_id}/submit:
    post:
      tags: [Shipments]
      summary: Gönderiyi taşıyıcıya gönder
      operationId: submitShipment
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Gönderi aktarıldı
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Conflict'
  /shipments/{shipment_id}/labels:
    get:
      tags: [Shipments]
      summary: Gönderi etiketlerini listele
      operationId: listShipmentLabels
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
        - name: package_id
          in: query
          required: false
          schema: { type: string }
          description: Yalnızca belirtilen pakete ait etiketleri döndürür.
      responses:
        '200':
          description: Gönderi veya paket seviyesindeki etiketler
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Label'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags: [Shipments]
      summary: Etiket oluştur
      operationId: createShipmentLabel
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Etiket oluşturuldu
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Label'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /shipments/{shipment_id}/packages:
    get:
      tags: [Shipments]
      summary: Gönderi paketlerini getir
      operationId: listShipmentPackages
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Paketler ve paket seviyesindeki takip durumu
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ShipmentPackage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Shipments]
      summary: Paket bilgilerini güncelle
      operationId: updateShipmentPackages
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [packages]
              properties:
                packages:
                  type: array
                  minItems: 1
                  maxItems: 10
                  items:
                    $ref: '#/components/schemas/ShipmentPackageUpdate'
      responses:
        '200':
          description: Paket bilgileri güncellendi
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /shipments/{shipment_id}/packages/{package_id}/status-history:
    get:
      tags: [Shipments]
      summary: Paket durum geçmişini getir
      operationId: getShipmentPackageStatusHistory
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
        - name: package_id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Immutable canonical paket durum geçmişi
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ShipmentPackageStatusHistory'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /shipments/{shipment_id}/tracking:
    get:
      tags: [Shipments]
      summary: Gönderi ve paket takibini getir
      operationId: getShipmentTracking
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Gönderi aggregation ve paket takip olayları
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShipmentTracking'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: KargoJet API token
  parameters:
    ShipmentId:
      name: shipment_id
      in: path
      required: true
      schema: { type: string }
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      schema: { type: string, maxLength: 120 }
      description: Tekrar denemelerde çift gönderi oluşmasını önlemek için kullanın.
  schemas:
    ShipmentCreate:
      type: object
      required: [warehouse_id, buyer_name, buyer_phone, buyer_state, buyer_city, buyer_address]
      properties:
        warehouse_id: { type: string }
        buyer_name: { type: string }
        buyer_phone: { type: string }
        buyer_email: { type: string, format: email, nullable: true }
        buyer_state: { type: string }
        buyer_city: { type: string }
        buyer_address: { type: string }
        is_cod:
          type: boolean
          default: false
          description: Gönderide Kapıda Ödeme kullanılıp kullanılmayacağını belirtir.
        cod_amount: { type: number, nullable: true }
        cod_currency: { type: string, default: TRY }
        cod_payment_method:
          type: string
          enum: [cash, card, mixed]
          nullable: true
        freight_payer:
          type: string
          enum: [sender, recipient]
          default: sender
        packages:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/Package'
    Package:
      type: object
      additionalProperties: true
      description: Paket boyutu, ağırlığı ve içerik alanları.
    ShipmentPackageUpdate:
      allOf:
        - $ref: '#/components/schemas/Package'
        - type: object
          properties:
            id: { type: string, description: Mevcut paket kimliği }
    Shipment:
      type: object
      required: [id, status, buyer_name, buyer_phone, buyer_state, buyer_city, buyer_address, package_count, currency, created_at, updated_at]
      properties:
        id: { type: string }
        status: { type: string }
        status_label: { type: string }
        buyer_name: { type: string }
        buyer_phone: { type: string }
        buyer_state: { type: string }
        buyer_city: { type: string }
        buyer_address: { type: string }
        package_count: { type: integer }
        canonical_status: { type: string }
        aggregation_policy_version: { type: integer, example: 1 }
        carrier_slug: { type: string, nullable: true }
        tracking_code: { type: string, nullable: true }
        estimated_price: { type: number, nullable: true }
        real_price: { type: number, nullable: true }
        currency: { type: string }
        is_cod: { type: boolean }
        cod_amount: { type: number, nullable: true }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    ShipmentPackage:
      type: object
      required: [id, sequence, status, tracking_state]
      properties:
        id: { type: string }
        sequence: { type: integer, minimum: 1 }
        desi: { type: integer }
        weight_kg: { type: number, nullable: true }
        width_cm: { type: integer, nullable: true }
        height_cm: { type: integer, nullable: true }
        length_cm: { type: integer, nullable: true }
        barcode: { type: string }
        provider_parcel_id: { type: string, nullable: true }
        tracking_number: { type: string, nullable: true }
        status: { type: string }
        tracking_state:
          type: string
          enum: [available, pending, unsupported, unavailable]
        tracking:
          $ref: '#/components/schemas/ParcelTracking'
        capabilities:
          $ref: '#/components/schemas/PackageCapabilities'
        delivered_at: { type: string, format: date-time, nullable: true }
        label_url: { type: string, nullable: true }
        status_history:
          type: array
          items:
            $ref: '#/components/schemas/ShipmentPackageStatusHistory'
    ShipmentPackageStatusHistory:
      type: object
      required: [id, to_status, source, created_at]
      properties:
        id: { type: string }
        from_status: { type: string, nullable: true }
        to_status: { type: string }
        source: { type: string }
        reason: { type: string, nullable: true }
        created_at: { type: string, format: date-time }
    CarrierCapabilities:
      type: object
      properties:
        version: { type: integer, example: 1 }
        multi_parcel: { type: boolean }
        parcel_tracking: { type: boolean }
        parcel_labels: { type: boolean }
        max_packages: { type: integer }
        tracking_scope: { type: string }
        label_scope: { type: string }
    PackageCapabilities:
      type: object
      properties:
        version: { type: integer, example: 1 }
        tracking: { type: boolean }
        label: { type: boolean }
    ParcelTracking:
      type: object
      required: [state]
      properties:
        number: { type: string, nullable: true }
        state:
          type: string
          enum: [available, pending, unsupported, unavailable]
    ShipmentAggregation:
      type: object
      required: [policy_version, canonical_status]
      properties:
        policy_version: { type: integer }
        canonical_status: { type: string }
        package_statuses:
          type: array
          items: { type: string }
    ShipmentTracking:
      type: object
      properties:
        shipment_id: { type: string }
        status: { type: string }
        status_label: { type: string }
        tracking_code: { type: string, nullable: true }
        packages:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              sequence: { type: integer }
              tracking:
                type: object
                properties:
                  number: { type: string, nullable: true }
                  state:
                    type: string
                    enum: [available, pending, unsupported, unavailable]
              status: { type: string }
        events:
          type: array
          items: { type: object }
        capabilities:
          $ref: '#/components/schemas/CarrierCapabilities'
        aggregation:
          $ref: '#/components/schemas/ShipmentAggregation'
    QuoteOption:
      type: object
      properties:
        carrier_slug: { type: string }
        service_level: { type: string }
        currency: { type: string }
        estimated_price: { type: number }
        eta_days_min: { type: integer, nullable: true }
        eta_days_max: { type: integer, nullable: true }
    Label:
      type: object
      properties:
        id: { type: string }
        shipment_id: { type: string }
        package_id: { type: string, nullable: true }
        format: { type: string }
        content_type: { type: string }
        bytes_base64: { type: string }
        created_at: { type: string, format: date-time }
    Error:
      type: object
      properties:
        detail: { type: string }
  responses:
    Unauthorized:
      description: API token eksik veya geçersiz.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Token bu işlem için yetkili değil.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Kayıt bulunamadı.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: İstek mevcut durumla çakışıyor.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationError:
      description: İstek doğrulanamadı.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
