Ana içeriğe geç
YouSoftGeliştirici Portalı
Dokümanlar

Sorgu sözleşmesi

Bütün uçların ortak kuralları: sayfalama, süzme, sıralama, genişletme, veri biçimleri, Idempotency-Key, oran sınırı ve yeniden deneme. Bir ucu ilk kez çağırmadan önce bir kez okuyun; ucun kendi alanları OpenAPI referansındadır.

Bu sayfadaki kurallar bütün /v1 uçlarında aynıdır. Hangi ucun hangi alanı süzdüğü, sıraladığı ya da genişlettiği aşağıdaki tabloda ve OpenAPI referansında (x-filter, x-sort, x-include) yazar. Bu kuralların bir fatura kesme akışında nasıl birleştiği için Fatura kesme akışı sayfasına bakın.

Sayfalama

Liste uçları imleç tabanlı (keyset) sayfalama kullanır. Sayfa numarası (page) ve toplam kayıt sayısı (total) yoktur: doğru bir toplam ikinci bir sayım sorgusu gerektirir, o da sayfalamayı pahalı yapar.

ParametreKural
page_sizeVarsayılan 25, en çok 100. 100’den büyük değer hata vermez, 100’e indirilir. 1’den küçük ya da sayı olmayan değer 400 validation_failed alır.
cursorÖnceki yanıttaki meta.next_cursor değeri. Opak bir dizedir: içeriğini çözmeyin, yalnız geri verin. Çözülemeyen değer 400 validation_failed alır.
HTTP
# İlk sayfa
GET /v1/units?page_size=100

# Yanıt
{
  "data": [ … ],
  "meta": { "next_cursor": "MjAyNi0wMy0xNVQxMTowMDowMC40ODI5MTNafDAxOTJlOGExLTdjM2YtN2QyZS1iNWE0LTNmNmM5ZDhlMWEyMA" }
}

# Sonraki sayfa: next_cursor değeri aynen geri verilir
GET /v1/units?page_size=100&cursor=MjAyNi0wMy0xNVQxMTowMDowMC40ODI5MTNafDAxOTJlOGExLTdjM2YtN2QyZS1iNWE0LTNmNmM5ZDhlMWEyMA

# Son sayfa
{ "data": [ … ], "meta": { "next_cursor": null } }

meta.next_cursor null ise son sayfadasınız. Kayıtlar tam dolu bir sayfada bitse bile son sayfa null taşır; boş bir sonraki sayfayı istemeniz gerekmez. Tüm kayıtları okumak için döngü şöyle kurulur:

Sözde kod
cursor = null
do:
    yol = "/v1/units?page_size=100"
    if cursor != null:
        yol += "&cursor=" + cursor
    yanit = istek_at("GET", yol)
    isle(yanit.data)
    cursor = yanit.meta.next_cursor
while cursor != null

Süzme

Süzgeç filter[alan][operatör]=değer biçiminde yazılır. Operatör açıkça belirtilir; operatörsüz filter[alan] tanınmaz.

OperatörAnlamı
gtebüyük veya eşit
lteküçük veya eşit
gtbüyük
ltküçük
eqeşit

Hangi alanın hangi operatörü kabul ettiği uca göre değişir (aşağıdaki tablo). Değerin biçimi alanın türüne bağlıdır:

  • Zaman alanları (created_at): RFC 3339 zaman damgası, örneğin 2026-01-01T00:00:00Z. Saniyenin altı kabul edilir. Yalnız tarih (2026-01-01) geçerli değildir, 400 alır. Saat farkındaki + karakteri %2B olarak yazılır: 2026-01-01T00:00:00%2B03:00. Yanıttaki bir created_at değerini süzgeç olarak geri veriyorsanız saniye altı hassasiyetini kırpmayın: kırpılmış değer aynı saniyedeki kayıtları atlatır.
  • Kur tarihi (date: kurlar): değer yine RFC 3339 olarak verilir ama yalnız gün kısmı kullanılır.
  • Belge tarihi (issue_date: faturalar): yalnız gün, YYYY-MM-DD biçiminde (örneğin 2026-10-01). Saat taşıyan değer 400 alır. gte ve lte o günü de kapsar.
  • Sözlük alanları (status, document_type): yalnız eq, değer sözleşmedeki kümeden biri olmalıdır.
  • Metin alanları (invoice_no): yalnız eq, tam eşleşme (parça arama yoktur), en çok 32 karakter.
  • Kimlik alanları (uuid yani ETTN, envelope_id yani zarf kimliği): yalnız eq, UUID biçiminde.

Farklı süzgeçler birlikte uygulanır, yani kayıt hepsini sağlamalıdır. Aynı alana iki farklı operatör verebilirsiniz (aralık için gte ve lt). Aynı süzgeci iki kez göndermek 400 alır.

HTTP
GET /v1/exchange-rates?filter[date][gte]=2026-01-01T00:00:00Z&filter[date][lt]=2026-02-01T00:00:00Z
GET /v1/invoices?filter[issue_date][gte]=2026-10-01&filter[issue_date][lte]=2026-10-31&filter[status][eq]=accepted
GET /v1/invoices?filter[invoice_no][eq]=YSF2026000000012

Sözleşmede olmayan bir süzgeç, operatör ya da değer biçimi 400 alır; süzülmemiş bir listeyi süzülmüş sanmanız mümkün değildir.

Uca göre süzme, sıralama ve genişletme

Tüm alanlar gte, lte, gt ve lt operatörlerini kabul eder; istisnalar “Süzgeç alanları” sütununda belirtilmiştir. Varsayılan sıralama /v1/tax-rates dışında her yerde azalandır: -created_at, -date ya da -issue_date. /v1/tax-rates NACE koduna göre artan sıralanır, sırası sabittir ve sort kabul etmez.

UçSüzgeç alanlarıSıralamaGenişletme
/v1/invoices
issue_date (gte, lte); status, document_type, invoice_no, uuid, envelope_id (yalnız eq)issue_datecontact
/v1/inbox-invoices
issue_date (gte, lte); status, document_type, invoice_no, uuid (yalnız eq)issue_dateyok
/v1/exchange-rates
datedateyok
/v1/units
created_atcreated_atyok
/v1/tax-rates
yoknace_code (sabit, artan)yok

Fatura listelerinde (GET /v1/invoices, GET /v1/inbox-invoices) sıralama ve aralık süzgeci belge tarihi (issue_date) üzerindedir. Aralık süzgeci sıralama alanıyla aynıdır, çünkü farklı bir alana göre süzen liste sayfasını doldurmak için firmanın bütün kayıtlarını taramak zorunda kalırdı.

Tek kayıt okuyan uçlardan yalnız ikisi include kabul eder: /v1/invoices/{id} için contact, /v1/me için firma.

Sıralama

sort=alan artan, sort=-alan azalan sıralar. Tek alan verilebilir. Sıralanabilen alanlar uca göre değişir (yukarıdaki tablo) ve bilerek dardır: cursor sıralama alanı ile kayıt kimliğinden oluşan bir konum olduğu için sayfalama yalnız o alan üzerinde tutarlıdır. sort verilmezse tablodaki varsayılan uygulanır. Tabloda olmayan bir alan 400 alır.

Genişletme

include=ad ya da include=ad1,ad2 ilişkili kaydı yanıtın içine koyar. Örneğin GET /v1/invoices/{id}?include=contact faturanın içine contact alanını ekler; cari kartını ayrıca istemeniz gerekmez.

  • Derinlik 1’dir: include=contact.address gibi noktalı bir ad 400 alır. Tabloda olmayan bir ad da 400 alır.
  • İlişki ayrı bir included dizisinde değil, kaydın içinde gelir: invoice.contact = {…}. İstemci tarafında birleştirme yapmazsınız.
  • Yazma uçları include kabul etmez (bkz. “Yazma uçlarında doğrulama”).

Fatura detayında include=contact ayrıca cari kartı okuma kapsamını ister; anahtar bu kapsamı taşımıyorsa istek 403 forbidden alır, genişletmesiz istek etkilenmez. /v1/me?include=firma ise ek kapsam istemez.

Veri biçimleri

  • Zaman damgası (created_at, updated_at): RFC 3339, saniye altı hassasiyetle.
  • Tarih (issue_date): YYYY-MM-DD. “Bugün” Europe/Istanbul saat dilimine göre hesaplanır (örneğin ileri tarihli fatura denetimi).
  • Tutar ve oran, okurken (total, vat_rate, unit_price): ondalık metin, örneğin "1250.00". Para hesabında kayan noktalı (float) tür yerine ondalık (decimal) tür kullanın.
  • Boş alanlar: isteğe bağlı bir alan boşsa yanıtta hiç yer almaz, null gelmez. Her zaman gelen alanlar OpenAPI’deki required listesindedir. Tek istisna meta.next_cursor: son sayfada null olur.
  • Tutar ve oran, yazarken: türü uca göre değişir. Fatura kaleminde quantity, unit_price, vat_rate ve stok kartında unit_price, vat_rate JSON sayısıdır; stok hareketindeki quantity, unit_price ondalık metindir ("1250.50"). Her alanın türü OpenAPI referansında yazar.

Yazma uçlarında doğrulama

Yazma uçları (POST, PATCH) include, filter ve sort parametrelerinden birini alırsa 400 verir. Gövde en fazla 2 MiB olabilir; aşan istek 400 alır. Sözleşmede olmayan gövde alanı yok sayılır. Tanımlı bir alanın kuralına uymayan değer 400 validation_failed alır ve details.field o alanın istekteki adını taşır (tax_number, items[1].product_id gibi). Sık karşılaşılan kurallar:

  • Sözlük alanları (cari type ve person_type, ürün type, fatura invoice_type ve scenario) yalnız sözleşmedeki değerleri alır.
  • Cari tax_number 10 haneli VKN, national_id 11 haneli TCKN olmalı ve kontrol basamağı tutmalıdır. İstisnalar: nihai tüketici kimliği 11111111111 ve yurt dışındaki alıcının VKN’si 2222222222. Kişinin email alanının biçimi denetlenir.
  • Ürün unit değeri sözleşmedeki birim kodlarından biri ya da firmanın aktif bir birimi olmalıdır. currency büyük harfli ISO 4217 kodudur (TRY, USD). vat_rate 0, 1, 8, 10, 18 ya da 20 olabilir (fatura kaleminde de). Ürünün unit_price alanı negatif olamaz ve en fazla iki ondalık hane taşır.

Benzersiz olması gereken bir değer zaten kullanılıyorsa yanıt 409 conflict olur: aynı tax_number ya da code ile cari kartı, aynı code ile stok kartı, aynı adla stok kategorisi. Etiket adları benzersiz değildir.

Idempotency-Key

Idempotency, aynı isteği birden çok kez göndermenin tek kez göndermekle aynı sonucu vermesidir. API bunu yalnız POST /v1/invoices/{id}/send ucunda Idempotency-Key başlığıyla sağlar: aynı anahtarla gelen ikinci istek saklanan yanıtı döndürür ve GİB’e ikinci bir gönderim olmaz. Aynı anahtarı başka bir istek için kullanmak 422 idempotency_key_reuse verir; her mantıksal işlem için yeni bir UUID üretin. Anahtar en çok 255 karakter olabilir; fazlası 400 validation_failed alır ve hiçbir şey yapılmaz. Anahtarlar API anahtarı başına ayrı tutulur: iki farklı API anahtarı aynı değeri seçse bile birbirinin yanıtını almaz. Gönderimi yeniden denemenin ayrıntısı Fatura kesme akışında.

Anahtar 24 saat saklanır. Süre dolduktan sonra aynı anahtarla gelen istek yeni bir istek sayılır ve yeniden çalışır. Saklanan kayıtlar saatlik bir işle silindiği için 24. saatin hemen ardından gelen bir istek hâlâ saklanan yanıtı alabilir; buna güvenmeyin. Çift gönderim koruması için, gönderimi yeniden denemeden önce gönderim sonucunu faturanın durumundan da (GET /v1/invoices/{id}) doğrulayın.

Anahtarsız gönderimde ikinci isteğin sonucu belgenin durumuna bağlıdır. Gönderilmiş, kabul edilmiş, reddedilmiş ya da iptal edilmiş belge 409 conflict alır; hata durumundaki (failed) ya da kuyrukta takılı kalmış belge ise yeniden kuyruğa alınır ve 202 döner. Ayrıntı OpenAPI referansında sendInvoice açıklamasındadır. Bu yüzden her gönderim niyeti için sabit bir Idempotency-Key kullanın. Belirsiz kalan bir gönderimde status alanını okuyarak durumu doğrulayın.

Oran sınırı

Oran sınırı jetonlu kovadır: kova dolu başlar, yani kapasite kadar isteklik bir yığın tek seferde geçer; kova boşaldıktan sonra hak dolum hızıyla yeniden doğar. İki sınıf vardır. Her sınıfta iki kova birlikte sayılır: anahtar başına ve firma toplamı (firmanın bütün anahtarları). Firma toplamı, aynı firma için birden çok anahtar açarak kotayı katlamayı önler.

SınıfAnahtar başınaFirma toplamı
Genel: her imzalı /v1 isteğiKapasite 60, dakikada 120Kapasite 120, dakikada 300
İndirme: yalnız fatura XML ve PDF uçlarıKapasite 10, dakikada 60Kapasite 20, dakikada 120; aynı anda en çok 4 indirme

İndirme isteği hem genel hem indirme kovasından düşer. İndirme adımında reddedilen istek genel jetonu geri alır: Retry-After kadar bekleyip yeniden deneyen istemci kendi genel kotasını tüketmez. Liste uçları ve yazma uçları genel sınıftadır. Hangi ucun indirme sınıfında olduğu OpenAPI referansında x-oran-sinifi: download ile yazar; sayılar sözleşmenin x-oran-siniri bloğundadır.

İmzası doğrulanmış her yanıt şu başlıkları taşır:

BaşlıkAnlamı
X-RateLimit-ResourceAşağıdaki üç başlığın anlattığı kova: key:general, firm:general, key:download, firm:download; eşzamanlılık reddinde firm:download-concurrency. İsteğin sayıldığı kovalar içinde en dar olanı (kalan hakkı kapasitesine oranla en az olan) gösterilir.
X-RateLimit-LimitO kovanın kapasitesi, yani anlık yığılma tavanı (anahtarın genel kovası için 60). Eşzamanlılık reddinde firmanın aynı anda açabileceği indirme sayısı (4).
X-RateLimit-RemainingKovada kalan istek hakkı; 429 yanıtında 0.
X-RateLimit-ResetKovanın tamamen dolacağı an, Unix saniyesi olarak (süre farkı değil). Eşzamanlılık reddinde yeniden denenebilecek an.
Retry-AfterYalnız 429 yanıtında gelir: bir sonraki istek hakkının doğmasına kalan saniye (en az 1). Eşzamanlılık reddinde 1.

Bu başlıklar 401 yanıtında yoktur. Kotayı istemci tarafında sabit bir sayıyla taklit etmeyin; kalan hakkı başlıklardan okuyun. Aynı firmanın birden çok anahtarı varsa firma toplamı hepsinin ortak kotasıdır: bir anahtarın yoğun kullanımı diğerine firm:general ile 429 döndürebilir.

Yeniden deneme

  • 429: istek işlenmemiştir. Retry-After saniyesi kadar bekleyip aynı isteği yeniden gönderin.
  • 500, 502, 503, 504 ve 408: üstel bekleme ve rastgele sapmayla (jitter) yeniden deneyin. API’nin kendisi yalnız 500 üretir ve gövdesi hata zarfıdır; 502, 503 ve 504 ağ katmanından gelir, gövdesi JSON olmayabilir. Gövdeyi çözmeden önce Content-Type başlığına bakın.
  • Diğer 4xx: yeniden göndermek sonucu değiştirmez. İsteği düzeltin; 409 için önce kaydın güncel durumunu okuyun.
Sözde kod
deneme = 0
while deneme < 5:
    yanit = istek_at()
    if yanit.status == 429:
        bekle(yanit.headers["Retry-After"])          # sunucunun verdiği saniye
    elif yanit.status in (408, 500, 502, 503, 504):
        bekle(min(2 ** deneme, 60) + rastgele(0, 1)) # üstel bekleme + jitter
    else:
        break                                        # başarı ya da yeniden denenmeyecek 4xx
    deneme += 1