# Toplu fiyat ve stok güncellemek

> **Ne yaparsınız:** Binlerce ürünün fiyatını veya bir depodaki sayım sonucunu tek istekte gönderir, işi izler ve hatalı satırları okursunuz.
> **Hangi uçlar:** [Toplu fiyat güncelle](https://developer.siparis.app/uc/toplu-fiyat-guncelle), [Toplu stok say](https://developer.siparis.app/uc/toplu-stok-say), [Toplu işin durumunu getir](https://developer.siparis.app/uc/toplu-isin-durumunu-getir), [Toplu işin hatalarını listele](https://developer.siparis.app/uc/toplu-isin-hatalarini-listele)
> **Gereken izin:** Fiyat için `products:write`, stok için `inventory:write`; `cost` alanı için ayrıca `costs:write`.

API adresi `https://api.siparis.app/v1`. Bir istekte en çok **10.000 satır** gönderin. Sunucu `202 Accepted` ve `job_…` kimliği döndürür; sonucu işi izleyerek veya `job.completed` olayıyla öğrenirsiniz. `cost` alanını göndermek (null dahil) `costs:write` ister. Yalnız işi açan API bağlantısı (paneldeki erişim kaydı; anahtarı döndürmek onu değiştirmez) işi ve hatalarını okuyabilir.

## 1. Fiyatları gönderin

Örnekler Bash, curl ve jq kullanır. `SIPARIS_API_KEY` ortam değişkenini kendi anahtarınızla ayarlayın, örnek stok kodlarını kataloğunuzdakilerle değiştirin. Anahtarı kaynak koduna yazmayın.

İlk örnek için zorunlu alanlar (POST /v1/bulk/prices):

| Alan | Tür | Açıklama | Örnek |
| --- | --- | --- | --- |
| `lines` | liste: nesne (en çok 10000 öğe) | En çok 10.000 fiyat satırı; satır numaraları 1 ile başlar. |  |
| `lines[].price` | sayı (en az 0; en çok 999999999) veya metin | Excel Fiyat sütunu: indirimsiz TRY fiyatı. compare_at_price verilirse price satış fiyatı, compare_at_price indirimsiz fiyat olarak eşlenir. | `"120.00"` |

Bütün alanlar: [Toplu fiyat güncelle](https://developer.siparis.app/uc/toplu-fiyat-guncelle).

```bash
export API='https://api.siparis.app/v1'
: "${SIPARIS_API_KEY:?SIPARIS_API_KEY ortam değişkenini ayarlayın}"
KEY="fiyat-$(date +%s)-$RANDOM"
cat > fiyatlar.json <<'JSON'
{"lines":[
  {"sku":"KUPA-MAVI","price":"120.00"},
  {"sku":"KUPA-BEYAZ","price":"150.00","sale_price":"125.00"}
]}
JSON
JOB=$(curl --fail-with-body -sS "$API/bulk/prices" \
  -H "Authorization: Bearer $SIPARIS_API_KEY" \
  -H "Idempotency-Key: $KEY" -H 'Content-Type: application/json' \
  --data-binary @fiyatlar.json | jq -er .id)
printf 'İş: %s\n' "$JOB"
```

`Idempotency-Key` tekrar anahtarıdır: aynı isteği güvenle yeniden göndermek için sizin ürettiğiniz benzersiz metin. Yanıt kaybolursa **aynı dosya ve aynı KEY** ile yeniden gönderin; aynı iş döner. Hataları düzeltip yeni iş açarken yeni anahtar kullanın.

Fiyatlar paneldeki Excel sütunlarıyla işlenir: `price` **Fiyat** (KDV dahil, indirimsiz), `sale_price` **İndirimli fiyat**, `cost` **Maliyet** sütunudur. Üstü çizili fiyatı alternatif olarak `price` satış fiyatı ve `compare_at_price` indirimsiz fiyat diye de gönderebilirsiniz. `store` ile markaya özel fiyat yazılır. Her alanın anlamı [Toplu fiyat güncelle](https://developer.siparis.app/uc/toplu-fiyat-guncelle) sayfasındadır.

API tutarlarında nokta ondalık ayracıdır: `"1.500"` ve `"1.5"` **1,50 TL**, `"1500"` **1.500,00 TL** anlamına gelir. Tutarlar iki ondalıkla saklanır; binlik ayraç kullanmayın.

Fiyat pozitif olmalı; indirimli fiyat indirimsiz fiyattan küçük olmalı. Seçenek indirimi panelde olduğu gibi ürünün indirim kuralına uyar; bağımsız seçenek indirimi satır hatasıdır. Açık seçenek fiyatı mutlak fiyat olur. Bir satırdaki ticari doğrulama hatası o satırın tamamını atlatır. Aynı seçenek aynı işte tekrar gönderilirse ilk satır kullanılır, sonraki satır `duplicate_row` olur. Bulunmayan veya markanızın dışında kalan seçenek `not_found` verir. Fiyat geçmişi, site önbelleği ve bağlı pazaryerine gönderim panelle aynı yoldan güncellenir.

## 2. Depo stoğunu sayın

Önce `inventory:read` ile `/inventory/levels` üzerinden depo miktarını okuyun; depolar `/locations` listesindedir. Sayım miktarı bir artış değil, **depoda kaç tane var** bilgisidir. Aşağıdaki örnekte son okunan miktar 10, sayılan miktar 12'dir. `loc_…` değerini kendi deponuzla değiştirin.

İlk örnek için zorunlu alanlar (POST /v1/bulk/inventory):

| Alan | Tür | Açıklama | Örnek |
| --- | --- | --- | --- |
| `lines` | liste: nesne (en çok 10000 öğe) | En çok 10.000 sayım satırı; her satırda expected zorunludur. |  |
| `lines[].expected` | sayı (en az -100000000; en çok 100000000) veya metin · null olabilir | Zorunlu: son okunan depo miktarı; stok sayılmıyorsa null. Değişmişse satır stock_changed ile atlanır. | `"10"` |
| `lines[].quantity` | sayı (en az 0; en çok 100000000) veya metin | Depoda sayılan miktar; sıfır olabilir, en çok üç ondalık hane. | `"12"` |
| `location` | metin (en çok 36 karakter) | Sayımın loc_ depo kimliği. | `"loc_1234567890abcdef1234567890abcdef"` |

Bütün alanlar: [Toplu stok say](https://developer.siparis.app/uc/toplu-stok-say).

```bash
LOCATION='loc_1234567890abcdef1234567890abcdef'
STOCK_KEY="sayim-$(date +%s)-$RANDOM"
jq -n --arg location "$LOCATION" \
  '{location:$location,lines:[{sku:"KUPA-MAVI",quantity:"12",expected:"10"}]}' > sayim.json
STOCK_JOB=$(curl --fail-with-body -sS "$API/bulk/inventory" \
  -H "Authorization: Bearer $SIPARIS_API_KEY" \
  -H "Idempotency-Key: $STOCK_KEY" -H 'Content-Type: application/json' \
  --data-binary @sayim.json | jq -er .id)
printf 'Sayım işi: %s\n' "$STOCK_JOB"
```

`expected` zorunludur: sayımdan önce okuduğunuz depo miktarıdır, çünkü siz dosyayı hazırlarken satış gerçekleşmiş olabilir. Kilit altındaki güncel depo miktarı farklıysa o satır `stock_changed` ile atlanır; satışın düşürdüğü stok ezilmez. Sayılan miktar güncel miktara eşit olsa bile eski `expected` reddedilir. Stok “sayılmıyor” olarak okunmuşsa açıkça `expected:null` gönderin; bu arada sayılmaya başladıysa yine atlanır. Negatif mevcut miktar `expected` olarak gönderilebilir; sayılan miktar negatif olamaz. Adet biriminde tam sayı, diğer birimlerde en çok üç ondalık hane kullanılır. Ortak stoktan düşen seçenek yerine ortak stok havuzunun `var_` kimliğini kullanın.

## 3. İşi izleyin

```bash
curl --fail-with-body -sS "$API/jobs/$JOB" \
  -H "Authorization: Bearer $SIPARIS_API_KEY" | jq .
```

Örnek HTTP 202 yanıtı (uydurma veri):

```json
{
  "created_at": "2026-10-16T09:00:00Z",
  "error_code": null,
  "failed": 0,
  "finished_at": null,
  "id": "job_1234567890abcdef1234567890abcdef",
  "kind": "prices",
  "processed": 0,
  "status": "queued",
  "total": 10000
}
```



`queued` sırada, `running` çalışıyor; `succeeded`, `failed`, `partially_failed` bitmiş durumlarıdır. `total` gönderilen satır, `processed` hatalar dahil sonuçlanan satır, `failed` hatalı satır sayısıdır; başarılı satır sayısı `processed - failed` olur. `finished_at` bitiş zamanıdır. İşin tamamını durduran `error_code` varsa işlenmemiş satırlar da kalabilir (`total - processed`).

Parçalar ayrı kısa işlemlerde kesinleşir. Yarıda kesilen işçi son kesinleşen parçadan sürer; o parçanın fiyatı ve sayımı iki kez uygulanmaz. Daha önce tamamlanan parçalar başka bir satır hatası yüzünden geri alınmaz.

Tek satır süre sınırında tamamlanamazsa `row_timeout` hatasıyla atlanır, sonraki satırlar devam eder. İş başladıktan sonra 1 saat sürerse veya art arda 20 parça zaman aşımına uğrarsa `error_code: "timeout"` ile biter; işletme yeni iş açabilir ve `job.completed` olayı yayımlanır. Önceki başarılı satırlar korunur. Yeniden gönderirken yalnız hatalı ve işlenmemiş satırları seçin; stok için güncel miktarı yeniden okuyun.

## 4. Satır hatalarını okuyun

```bash
CURSOR=''
while :; do
  PAGE=$(curl --fail-with-body -sS -G "$API/jobs/$JOB/errors" \
    -H "Authorization: Bearer $SIPARIS_API_KEY" \
    --data-urlencode 'limit=100' ${CURSOR:+--data-urlencode} ${CURSOR:+"cursor=$CURSOR"}) || break
  printf '%s' "$PAGE" | jq '.data[]'
  [ "$(printf '%s' "$PAGE" | jq -r .has_more)" = true ] || break
  CURSOR=$(printf '%s' "$PAGE" | jq -r .next_cursor)
done
```

Döngü her yanıttaki `next_cursor` değerini (sonraki sayfanın yer imi) `cursor` olarak geri gönderir. Hatalar 1 ile başlayan `line`, gönderdiğiniz `identifier`, sabit `code` ve Türkçe `detail` taşır. İş sürerken liste büyür; eksiksiz liste için iş bitince ilk sayfadan başlayın. `stock_changed` satırını yeniden saymadan önce güncel stoğu okuyun; eski `expected` değerini gelişigüzel değiştirmeyin.

## 5. Bitişi olayla öğrenin

`events:read` ve işin yazma kapsamıyla (izniyle) `/events?types=job.completed` okuyun ya da aynı bağlantının webhook adresinde `job.completed` seçin. Olayın `data.object.id` alanı iş kimliğidir; `changes.current.status` bitmiş durumu söyler. Olay yalnız işi açan bağlantıya, işin marka ve kaynak kapsamlarıyla gider; başka bağlantı aynı markayı görse de alamaz. Maliyetli işin olayı ayrıca `costs:write` ister. Webhook imzasını doğrulayın: [Webhook rehberi](https://developer.siparis.app/webhook).

## Büyük katalog ve satış önceliği

10.000'den büyük kataloğu en çok 10.000 satırlık dosyalara bölün. Aynı işletmede fiyat ve stok işleri birlikte **tek etkin iş** paylaşır; ikinci istek `409 bulk_in_progress` ve `Retry-After` verir. Açık iş bitince sıradaki dosyayı yeni tekrar anahtarıyla gönderin. Her oluşturma isteği 60 hız puanıdır.

İşçi satış/ödeme kuyruğundan ayrı, dışa aktarmayla ortak tek çalışma yuvasını kullanır. Kilitli ürün/seçenek beklenmez; parça ertelenir. Uzayan parça küçülür. Yoğunlukta toplu işin beklemesi satış yoluna öncelik verir. İşletme sahibi panelde **Entegrasyonlar → Geliştirici → İşler** listesinden durum ve satır sayılarını görebilir; panel AI'ı da aynı listeyi okur.
