Public API v1 sözleşmesi Gönderiler PA-03
Gönderi oluşturma
POST/api/v1/shipments
Amaç
Sevkora'nın taşıyıcı hesabında tenant adına doğrudan gönderi açmak (Hepsijet akışı; DEC-180 saf bayi). Gönderici bilgisi warehouse_idden kopyalanır, taşıyıcıya oluşturma ANINDA iletilir; ayrı "hazır hale getirme" adımı yoktur.
Göç İNDİ (TASK-243; DEC-185/4 köprüsü)
carrier_account_id girdisi BAYİ taşıyıcılarda bir TAŞIYICI İPUCUSUDUR, kimlik taşımaz. Sunucu ipucundan yalnız taşıyıcı kodunu + ortam niyetini okur, kimliği SİSTEM hesabından çözer ve gönderiyi sistem satırına bağlar; emekli (disabled) hesap geçerli ipucu kalır. Taşıyıcının sistem hesabı yapılandırılmamışsa istek fail-closed 422 errors.carrier_account_id döner (taşıyıcıya istek çıkmaz).
Satırın İKİNCİ okuması (TASK-412; DEC-361/363)
kendi-hesap modeline AÇIK bir taşıyıcıda aynı alan bir ipucu değil AÇIK SEÇİMDİR. Satırın KENDİSİ kimliktir: gönderi tenant'ın kendi taşıyıcı hesabıyla çıkar, Sevkora ücret ALMAZ ve gönderi billing_mode=carrier_direct olarak damgalanır. Ayrımı satırın durumu değil TAŞIYICI KODU yapar. Satır sevk edilebilir değilse (doğrulanmamış, doğrulaması düşmüş ya da devre dışı) istek 422 errors.carrier_account_id alır ve BAYİ YOLUNA DÜŞMEZ: sessiz düşüş, tenant kendi hesabını kastederken bakiyesinden para kesilmesi anlamına gelirdi.
Alan OPSİYONELLEŞTİ ve İNDİ (TASK-244; DEC-181/DEC-186/DEC-187)
alan gövdede HİÇ YOKKEN gönderi DRAFT doğar; taşıyıcıya SIFIR istek çıkar, ücret kesilmez; yanıt 201 status=DRAFT + carrier_account/barcode/tracking_url null, meta.carrier_capabilities boş (hesap bağı yok = fail-closed). delivery_no yine üretilir ve ilk olay DRAFT (source=system) yazılır. VARKEN bugünkü tek çağrılı davranış AYNEN korunur; mevcut tüketici kırılmaz (expand adımı). İki fazlı akış PA-18/PA-19 ile sürer. Sinyal ALANIN YOKLUĞUDUR (DEC-187): açık null (ya da metin olmayan bir değer) GEÇERSİZDİR ve 422 errors.carrier_account_id döner; alanın yokluğu ile geçersiz bir değer AYNI ŞEY DEĞİLDİR (PA-06 desi alanındaki DEC-175 ayrımıyla aynı). /api/v2'de alan kalkar (contract).
Yetki
Bearer anahtar; warehouse_id ve carrier_account_id YALNIZ anahtarın tenant'ında aranır; başka tenant'ın kimliği "yok" muamelesi görür (422). İpucu semantiği bu sınırı DEĞİŞTİRMEZ: tenant yalnız kendi görebildiği hesap kimliğiyle işaret edebilir.
Başlık
Idempotency-Key ZORUNLU (Temel model bölümündeki sözleşme).
İstek gövdesi
(Sevkora'nın fiili doğrulama kuralları):
| Alan | Tip | Zorunlu | Kural |
|---|---|---|---|
warehouse_id |
string (ULID) | evet | tenant'ın adres defterinde var olmalı |
carrier_account_id |
string (ULID) | hayır (TASK-244) | ALAN YOKSA gönderi DRAFT doğar (taşıyıcıya istek yok, ücret yok). Gönderildiğinde: tenant'ın taşıyıcı hesabı olmalı, taşıyıcı createShipment desteklemeli. Bayi taşıyıcıda İPUCUDUR (TASK-243: kimlik sistem satırından çözülür); kendi-hesap modeline açık taşıyıcıda SEÇİMDİR (TASK-412: satırın kendisi kimliktir, satır doğrulanmış ve aktif olmalı). Açık null/metin olmayan değer 422; sinyal YOKLUKTUR (DEC-187) |
buyer_name |
string | evet | max 255 |
buyer_phone |
string | evet | max 32 |
buyer_email |
string | hayır | geçerli e-posta, max 255 |
buyer_address |
string | evet | max 1000 |
buyer_city |
string | evet | max 100; sunucu serbest metin kabul eder, SPA kapalı kümeden seçtirir (TASK-364) |
buyer_district |
string | evet | max 100; sunucu serbest metin kabul eder (ilçe telde townName olarak aynen gider) |
buyer_tax_number |
string | hayır | max 32 |
order_reference |
string | hayır | max 255. SİPARİŞİN KENDİ numarasıdır; etiket gövdesinde order.reference olarak taşınır ve gönderi aramasında kullanılır (TASK-488; DEC-474) |
order_price |
numeric | hayır | min 0. TAŞIMA ÜCRETİ DEĞİLDİR. Temel ücret desiden ve tarifeden türer (Yurtiçi kademesi ise koli başına desi ile kg'nin büyüğünden seçilir; ağırlık girilmezse desiden); iki fiyatlama stratejisi de bunu KENDİ bildiriminde yazar. Siparişin KENDİ tutarıdır ve etiket gövdesinde order.price olarak MÜŞTERİYE GİDEN belgeye basılır; yanlış doldurulursa etikette yanlış tutar görünür. Panelde alanın altında bu ayrımı yazan görünür bir açıklama durur (TASK-488; DEC-474) |
desi |
numeric | hayır | min 0. Gönderi desisi HER ZAMAN koli desilerinin TOPLAMINDAN türetilir (DEC-195; yüzde-desi tam sayı aritmetiği); asgari örnek yalnız parcels[].desi ile fiyatlanabilir gönderi üretir. Alan artık türetmeyi EZMEZ (DEC-316): koliler zorunlu olduğu için daima vardır ve ücret onlardan türer (DEC-312), dolayısıyla o toplamla ÇELİŞEN bir değer 422 ile errors.desi olarak reddedilir; toplamla aynı değer kabul edilir ve etkisizdir |
parcels |
array | evet | en az 1 öğe |
parcels.*.desi |
numeric | evet | min 0 |
parcels.*.weight_kg |
numeric | hayır | min 0. Parçanın ağırlığı (kilogram). Zorunlu DEĞİLDİR ve gönderilmezse null kaydedilir; null "ağırlık BİLİNMİYOR" demektir, sıfır kilo DEMEZ. Ağır kargo eşiği sözleşmede iki bacaklıdır ("tek parçada 100 kg VEYA belirli desi üzeri") ve ağırlık yazılmadığı sürece o eşiğin kilo bacağı o parça için değerlendirilemez (DEC-317). Girilirse Yurtiçi kademesi desi ile kg'nin büyüğünden seçilir (DEC-525 (a)). Yurtiçi tek parçada en fazla 201 kg kabul eder: ağırlığı 201 kg'yi aşan parça Yurtiçi teklifinden düşer, Yurtiçi ile onay ve etiket ücreti 422 ile reddedilir; tam 201 kg kabul edilir (DEC-558) |
parcels.*.reference |
string | hayır | max 255 |
parcels.*.content |
string | hayır | max 255 |
Başarı 201
{data, meta.carrier_capabilities}; detay gövdesiyle aynı biçim (alan SETİ taslakta da BİREBİR aynıdır; yeni alan sızmaz). Taşıyıcılı yolda delivery_no/barcode/tracking_url sunucu/taşıyıcı üretimidir ve ilk olay CREATED (source=system) yazılmıştır; taslak yolda yalnız delivery_no üretilir, barcode/tracking_url null kalır ve ilk olay DRAFT (source=system) olur.
Ücret ön kontrolü (DEC-566)
carrier_account_id gönderildiğinde gönderi, taşıyıcıya iletilmeden ÖNCE etiket anındaki ücret hesabından geçer. Bu hesabın fiyatlayamadığı gönderi, örneğin Yurtiçi'nin tek parçada kabul ettiği 201 kg'yi aşan ağırlıkta ya da tarifenin kapanış kademesini aşan ölçüde kolisi olan gönderi, PA-06 etiket ucundaki ücret reddiyle aynı yanıtı alır: 422 {message}. Taşıyıcıya istek çıkmaz; gönderi, koli ve barkod oluşmaz, ücret kesilmez. Gönderi tenant'ın kendi taşıyıcı hesabıyla oluşturuluyorsa Sevkora ücret almadığı için bu kontrol yapılmaz.
Bakiye ön kontrolü
carrier_account_id gönderildiğinde ve gönderi Sevkora hesabıyla çıkacaksa, taşıyıcıya iletilmeden ÖNCE cüzdan bakiyesinin bu gönderinin etiket ücretini (KDV dahil) karşılayıp karşılamadığı da sorulur. Karşılamıyorsa istek PA-06 etiket ucundaki yetersiz bakiye reddiyle aynı yanıtı alır: 422 {message}. Taşıyıcıya istek çıkmaz; gönderi, koli ve barkod oluşmaz. Ön kontrol bakiyeyi değiştirmez ve tutarı ayırmaz; ücret yine etiket anında (PA-06) kesilir. Bu yüzden oluşturma ile etiket arasında aynı cüzdandan başka bir harcama yapılırsa etiket isteği yine yetersiz bakiye nedeniyle 422 alabilir; bakiye yüklendikten sonra etiket yeniden istenebilir ya da gönderi PA-05 ile iptal edilebilir. Gönderi tenant'ın kendi taşıyıcı hesabıyla oluşturuluyorsa bu kontrol de yapılmaz.
Hatalar
401, 409 (Idempotency-Key çakışması / işlem sürüyor), 422 alan hataları (errors{alan}; Idempotency-Key başlığı eksikse errors.idempotency_key; başka tenant'ın ya da olmayan kimlik errors.warehouse_id / errors.carrier_account_id alan hatası olarak döner, varlık sızdırılmaz), 422 errors.carrier_account_id (taşıyıcı createShipment desteklemiyor; örn. tex hesabı; adaptör hiç kurulmaz, DEC-014 guard'ı), 422 {message} (taşıyıcı reddi: validation/business_rule/conflict/not_found), 422 {message} (ücret ya da bakiye ön kontrolü reddi; taşıyıcıya istek çıkmaz), 429, 502 (taşıyıcıya ulaşılamıyor; kayıt oluşmadı, yanıt idempotency deposuna YAZILMAZ; yeniden denenebilir). Taşıyıcı conflict tekrarında çift kayıt oluşmaz; mevcut kayıt döner (idempotensi, 09-integrations).
curl -X POST "{{base_url}}/shipments" \
-H "Accept: application/json" -H "Content-Type: application/json" \
-H "Authorization: Bearer SEVKORA_API_ANAHTARINIZ" \
-H "Idempotency-Key: 6f1c1c9e-ornek-istemci-anahtari" \
-d '{
"warehouse_id": "01K1ADRS000000000000000001",
"carrier_account_id": "01K1TSYC000000000000000001",
"buyer_name": "Ornek Alici",
"buyer_phone": "5550000000",
"buyer_address": "Ornek Mah. Deneme Cad. No:1",
"buyer_city": "Istanbul",
"buyer_district": "Kadikoy",
"parcels": [{"desi": 2.5}]
}'