{
  "openapi": "3.1.0",
  "info": {
    "title": "EPC API",
    "version": "1.0.0",
    "summary": "VIN'den araca uyan parça numaraları; stoktaki numaraların araca uyup uymadığı.",
    "description": "GM / Opel / Chevrolet elektronik parça kataloğu (epc.vin) için sunucudan sunucuya API. Alan adları İngilizce, değerler Türkçe. Ayrıntılı belge: /v1/docs"
  },
  "servers": [{ "url": "https://api.epc.vin/v1", "description": "Canlı" }],
  "security": [{ "apiKey": [] }],
  "tags": [
    { "name": "Araçlar", "description": "VIN çözme ve araca uyan parçaların tamamı" },
    { "name": "Parçalar", "description": "Parça numaralarını araca göre kontrol etme, parça geçmişi" },
    { "name": "Hesap", "description": "Sınırlar ve bugünkü kullanım" }
  ],
  "paths": {
    "/vehicles/{vin}": {
      "get": {
        "tags": ["Araçlar"],
        "operationId": "getVehicle",
        "summary": "VIN'in aracı",
        "description": "VIN'i çözer: marka, model, katalog, donanım. Bir VIN birden çok donanıma denk gelebilir; diğer uçlarda `equipment_id` ile seçilir, verilmezse ilki kullanılır. Günlük VIN kotasına sayılır.",
        "parameters": [{ "$ref": "#/components/parameters/Vin" }],
        "responses": {
          "200": {
            "description": "Araç bulundu",
            "headers": { "$ref": "#/components/headers/Common" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VehicleAnswer" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/vehicles/{vin}/parts": {
      "get": {
        "tags": ["Araçlar"],
        "operationId": "listVehicleParts",
        "summary": "Araca uyan tüm parça numaraları",
        "description": "Aracın kataloğunda araca uyan her parça numarası bir kez, katalog sırasıyla; her biri bulunduğu yerler (grup, sayfa, çizim numarası, adet) ve yerine geçen yeni numaralarıyla. Stoğunu kendi tarafında karşılaştırmak isteyenler için. Bir aracın ilk listesi 2-3 saniye sürebilir, sonraki istekler (30 dakika) hızlıdır. Günlük VIN kotasına sayılır.",
        "parameters": [{ "$ref": "#/components/parameters/Vin" }, { "$ref": "#/components/parameters/EquipmentId" }],
        "responses": {
          "200": {
            "description": "Liste",
            "headers": { "$ref": "#/components/headers/Common" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PartsListAnswer" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/parts/check": {
      "post": {
        "tags": ["Parçalar"],
        "operationId": "checkParts",
        "summary": "Bu numaralar bu araca uyar mı?",
        "description": "En çok 500 parça numarası gönderilir; her biri gönderildiği sırayla ve gönderildiği yazımla yanıtlanır. Numaralar iki tarafta da boşluk, tire, nokta ve eğik çizgi atılıp büyük harfe çevrilerek karşılaştırılır (`13 300 900` = `13300900`, `0836115063` = `08361-15063`). Opel KAT numaraları da tanınır. Bütün pazarların parça geçmişi (alternatifler) hesaba katılır. Katalog eski bir numarayı listeliyor ve gönderilen onun yerine geçen yenisiyse `yeni_numara`; tersi `eski_numara` olarak uyar. Günlük VIN kotasına sayılır.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CheckRequest" },
              "example": { "vin": "KL1SF48W9BB261734", "part_numbers": ["96829637", "95955933", "13300900", "abc;"] }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Her numara için sonuç",
            "headers": { "$ref": "#/components/headers/Common" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckAnswer" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "413": { "$ref": "#/components/responses/TooLarge" },
          "415": { "$ref": "#/components/responses/NotJson" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/parts/{part_number}": {
      "get": {
        "tags": ["Parçalar"],
        "operationId": "getPartHistory",
        "summary": "Parça geçmişi",
        "description": "Parçanın yerine geçen numaralar (zincir sırasıyla, en yenisi sonda) ve yerini aldığı eski numaralar. GM Avrupa ve GM Daewoo fiyat kitaplarından. VIN kotasına sayılmaz.",
        "parameters": [
          { "name": "part_number", "in": "path", "required": true, "schema": { "type": "string", "maxLength": 40 }, "example": "13300900" }
        ],
        "responses": {
          "200": {
            "description": "Geçmiş",
            "headers": { "$ref": "#/components/headers/Common" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PartAnswer" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/usage": {
      "get": {
        "tags": ["Hesap"],
        "operationId": "getUsage",
        "summary": "Sınırlar ve bugünkü kullanım",
        "responses": {
          "200": {
            "description": "Kullanım",
            "headers": { "$ref": "#/components/headers/Common" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UsageAnswer" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "epc_ ile başlayan 53 karakterlik anahtar",
        "description": "`Authorization: Bearer epc_…`. Anahtar yönetimden verilir ve yalnız bir kez gösterilir. Tarayıcıda, mobil uygulamada ya da kaynak kodda tutulmamalı."
      }
    },
    "parameters": {
      "Vin": { "name": "vin", "in": "path", "required": true, "description": "17 karakter; harf ve rakam, I, O ve Q yok. Küçük harf kabul edilir.", "schema": { "type": "string", "pattern": "^[A-HJ-NPR-Za-hj-npr-z0-9]{17}$" }, "example": "KL1SF48W9BB261734" },
      "EquipmentId": { "name": "equipment_id", "in": "query", "required": false, "description": "VIN birden çok donanıma denk geliyorsa hangisi (`/vehicles/{vin}` yanıtından).", "schema": { "type": "integer" } }
    },
    "headers": {
      "Common": {
        "X-Request-Id": { "description": "İsteğin kimliği; destek isterken bildirin.", "schema": { "type": "string" } },
        "RateLimit-Policy": { "description": "Dakika sınırı, IETF taslağına göre: `\"dakika\";q=60;w=60`", "schema": { "type": "string" } },
        "RateLimit": { "description": "Bu dakikadan kalan ve sıfırlanmaya kalan saniye: `\"dakika\";r=57;t=41`", "schema": { "type": "string" } }
      }
    },
    "responses": {
      "BadRequest": { "description": "İstek hatalı: `gecersiz_vin`, `gecersiz_numara`, `gecersiz_istek`, `numara_yok`, `cok_fazla_numara`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "gecersiz_vin", "message": "VIN 17 karakter olmalı; harf ve rakam, I, O ve Q yok." } } } } },
      "Unauthorized": { "description": "Anahtar yok, geçersiz ya da iptal edilmiş: `anahtar_yok`, `gecersiz_anahtar`, `anahtar_iptal`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Forbidden": { "description": "Hesap kapalı ya da süresi doldu: `hesap_kapali`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "`arac_bulunamadi`, `donanim_bulunamadi`, `parca_bulunamadi`, `bulunamadi`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "TooLarge": { "description": "Gövde 64 KB'tan büyük: `govde_cok_buyuk`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotJson": { "description": "Gövde JSON değil: `json_bekleniyor`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "MethodNotAllowed": { "description": "Adres bu yöntemle çağrılmaz: `yontem_desteklenmiyor`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "ServerError": { "description": "Bizim tarafta bir hata: `sunucu_hatasi`. `X-Request-Id` ile bildirin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "TooMany": {
        "description": "Sınır doldu: `istek_siniri` (dakika), `gunluk_vin_kotasi` (gün), `cok_fazla_hatali_anahtar` (adres). `Retry-After` kaç saniye bekleneceğini söyler.",
        "headers": { "Retry-After": { "schema": { "type": "integer" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "description": "Programlar için sabit kod (Türkçe, ASCII)." },
              "message": { "type": "string", "description": "İnsanlar için açıklama." }
            }
          }
        }
      },
      "Vehicle": {
        "type": "object",
        "required": ["equipment_id", "make", "model", "catalog_code", "catalog_name", "equipment_name", "production_date", "source"],
        "properties": {
          "source": { "type": "string", "enum": ["arac_kaydi", "vin_cozumleme"], "description": "`arac_kaydi`: VIN katalogun araç kayıtlarında, donanımı biliniyor. `vin_cozumleme`: kayıtlarda yok, VIN'in kendisinden çözüldü; donanımı daha az bilindiği için araca uyan parça listesi daha geniştir." },
          "equipment_id": { "type": "integer", "example": 2986 },
          "make": { "type": ["string", "null"], "example": "Chevrolet" },
          "model": { "type": ["string", "null"], "example": "AVEO (T250 / T255) [EUR] (2006 - 2011)" },
          "catalog_code": { "type": ["string", "null"], "example": "T06" },
          "catalog_name": { "type": ["string", "null"], "example": "T06" },
          "equipment_name": { "type": ["string", "null"], "example": "AVEO (T250/T255) [EUR]" },
          "production_date": { "type": ["string", "null"], "description": "Katalogda kayıtlıysa, katalogun yazdığı biçimde." }
        }
      },
      "VehicleAnswer": {
        "type": "object",
        "required": ["vin", "vehicles"],
        "properties": {
          "vin": { "type": "string", "example": "KL1SF48W9BB261734" },
          "vehicles": { "type": "array", "items": { "$ref": "#/components/schemas/Vehicle" } }
        }
      },
      "Place": {
        "type": "object",
        "description": "Parçanın katalogdaki bir yeri.",
        "required": ["group", "page_code", "page_title", "callout", "description", "usage", "quantity", "notes", "not_serviced"],
        "properties": {
          "group": { "type": "array", "items": { "type": "string" }, "description": "Gruplar, üstten alta.", "example": ["YAKIT & MOTOR KONTROL", "YAKIT"] },
          "page_code": { "type": ["string", "null"], "example": "2111" },
          "page_title": { "type": ["string", "null"], "example": "YAKIT DEPOSU II  (2111)" },
          "callout": { "type": ["string", "null"], "description": "Çizimdeki numara.", "example": "1" },
          "description": { "type": ["string", "null"], "example": "DEPO A-YAKIT 45L" },
          "usage": { "type": ["string", "null"], "description": "Kullanım bilgisi (donanım, motor kodları).", "example": "(BASE,LS,LT,E) (69,08,48) (&LDT/LMU)" },
          "quantity": { "type": ["string", "null"], "example": "1" },
          "notes": { "type": "array", "items": { "type": "string" } },
          "not_serviced": { "type": "boolean", "description": "Katalog bu parçayı servis parçası olarak satmıyor." },
          "catalog_part_number": { "type": "string", "description": "Yalnız `/parts/check`: bu yerde katalogun yazdığı numara." }
        }
      },
      "Part": {
        "type": "object",
        "required": ["part_number", "description", "replaced_by", "alternatives", "places"],
        "properties": {
          "part_number": { "type": "string", "example": "96829637" },
          "description": { "type": ["string", "null"], "example": "DEPO A-YAKIT 45L" },
          "replaced_by": { "type": "array", "items": { "type": "string" }, "description": "Bizim pazarımızın kitaplarında (GM Avrupa, GM Daewoo) yerine geçen numaralar, zincir sırasıyla (en yenisi sonda).", "example": ["95955933"] },
          "alternatives": { "type": "array", "items": { "$ref": "#/components/schemas/Alternative" }, "description": "Bütün pazarların kitaplarında bağlı numaralar (arayüzdeki G penceresi), önce bizim pazarımızınkiler." },
          "places": { "type": "array", "items": { "$ref": "#/components/schemas/Place" } }
        }
      },
      "Alternative": {
        "type": "object",
        "required": ["part_number", "relation", "interchangeable", "markets"],
        "properties": {
          "part_number": { "type": "string", "example": "95955933" },
          "relation": { "type": "string", "enum": ["yeni_numara", "eski_numara"], "description": "`yeni_numara`: bu numaranın yerine geçer. `eski_numara`: bu numaranın yerini aldığı." },
          "interchangeable": { "type": "boolean", "description": "Kitap ikisini birbirinin yerine kullanılabilir diye işaretliyor (INTERCHANGE); yoksa yalnız biri ötekinin yerine geçer (REPLACE)." },
          "markets": { "type": "array", "items": { "type": "string" }, "description": "Bağı kuran kitapların pazarı: GM Avrupa, GM Daewoo, ABD, Kanada, Meksika, Brezilya, Avustralya, Tayland, Çin, Hindistan, Ludvig, diğer.", "example": ["GM Daewoo", "Tayland"] }
        }
      },
      "PartsListAnswer": {
        "type": "object",
        "required": ["vin", "vehicle", "count", "parts"],
        "properties": {
          "vin": { "type": "string" },
          "vehicle": { "$ref": "#/components/schemas/Vehicle" },
          "count": { "type": "integer", "example": 1776 },
          "parts": { "type": "array", "items": { "$ref": "#/components/schemas/Part" } }
        }
      },
      "CheckRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["vin", "part_numbers"],
        "properties": {
          "vin": { "type": "string" },
          "part_numbers": { "type": "array", "minItems": 1, "maxItems": 500, "items": { "type": "string" } },
          "equipment_id": { "type": "integer" }
        }
      },
      "CheckResult": {
        "type": "object",
        "required": ["part_number", "fits"],
        "properties": {
          "part_number": { "type": "string", "description": "Gönderildiği gibi." },
          "fits": { "type": "boolean" },
          "match": { "type": "string", "enum": ["birebir", "yeni_numara", "eski_numara"], "description": "Uyuyorsa nasıl uyduğu." },
          "catalog_part_numbers": { "type": "array", "items": { "type": "string" }, "description": "Uyuyorsa katalogdaki numara(lar)." },
          "places": { "type": "array", "maxItems": 20, "items": { "$ref": "#/components/schemas/Place" } },
          "markets": { "type": "array", "items": { "type": "string" }, "description": "Yalnız `yeni_numara` / `eski_numara`: iki numarayı bağlayan kitapların pazarı." },
          "reason": { "type": "string", "enum": ["araca_uymuyor", "katalogda_yok", "gecersiz_numara"], "description": "Uymuyorsa neden." }
        }
      },
      "CheckAnswer": {
        "type": "object",
        "required": ["vin", "vehicle", "summary", "results"],
        "properties": {
          "vin": { "type": "string" },
          "vehicle": { "$ref": "#/components/schemas/Vehicle" },
          "summary": { "type": "object", "properties": { "asked": { "type": "integer" }, "fits": { "type": "integer" } } },
          "results": { "type": "array", "items": { "$ref": "#/components/schemas/CheckResult" } }
        }
      },
      "PartAnswer": {
        "type": "object",
        "required": ["part_number", "replaced_by", "replaces", "alternatives"],
        "properties": {
          "part_number": { "type": "string", "description": "Katalogun yazdığı biçimde (tireli olabilir).", "example": "13300900" },
          "replaced_by": { "type": "array", "items": { "type": "string" }, "example": ["13363092", "13403607", "13473421"] },
          "replaces": { "type": "array", "items": { "type": "string" } },
          "alternatives": { "type": "array", "items": { "$ref": "#/components/schemas/Alternative" } }
        }
      },
      "UsageAnswer": {
        "type": "object",
        "properties": {
          "account": { "type": "string" },
          "limits": { "type": "object", "properties": { "requests_per_minute": { "type": "integer" }, "vins_per_day": { "type": "integer" }, "part_numbers_per_check": { "type": "integer" } } },
          "today": { "type": "object", "properties": { "vins": { "type": "integer" }, "requests": { "type": "integer" } } }
        }
      }
    }
  }
}
