Başlarken
Sayfalama

Sayfalama

Platform standardı cursor tabanlı (keyset) sayfalamadır: limit + cursor.

⚠️

Endpoint'ler bu standarda kademeli geçiyor. Bugün hepsi aynı biçimi kabul etmiyor — hangi endpoint'in ne kabul ettiği aşağıdaki tabloda. Yeni entegrasyonda limit + cursor kullanın; eski parametreler çalışmaya devam ediyor ama artık uyarı başlığı dönüyorlar.

İstek

ParametreTipVarsayılanAçıklama
limitnumber25Sayfa başına kayıt (1–100). Tavanın üstü 400 döner.
cursorstring—Önceki yanıtın pageInfo.nextCursor değeri. İlk sayfada gönderilmez.
includeTotalbooleanfalsetrue ise pageInfo.totalCount doldurulur. Maliyetlidir.
curl "https://api-gateway.lunixi.com/api/v1/wallet/operations?limit=25" \
  -H "Authorization: Bearer $LUNIXI_TOKEN"

Yanıt

pageInfo her zaman data'nın yanındadır — içinde değil.

{
  "status": "success",
  "code": "SUCCESS",
  "data": [ { "id": "wop_01J8ZU..." } ],
  "pageInfo": {
    "hasMore": true,
    "nextCursor": "eyJ2IjoxLCJ0IjoiMjAyNi0wOC0xN1QxMDowMDowMC4xMjM0NTZaIiwiaWQiOiJ3b3BfMDFKOFpVIn0",
    "totalCount": null
  },
  "requestId": "req_01J8ZV..."
}
⚠️

Sayfalama bilgisini pageInfo'dan okuyun, data'nın içinden değil.

data endpoint'e göre bir dizi ya da bir nesne olabilir — ikisi de olur ve bu değişmeyecek. Bu yüzden sayfalama bilgisi data'nın içine konmadı: diziyi nesneye çevirmek mevcut entegrasyonları kırardı. pageInfo her iki durumda da aynı yerde, data'nın kardeşi olarak durur.

Bazı endpoint'ler geçiş süresince data.pageInfo içinde bir kopya da döndürüyor. Ona güvenmeyin — kanonik konum üst seviyedir ve kopya ileride kaldırılabilir.

data nesne dönen bir endpoint'te aynı yanıt şöyle görünür — pageInfo yine aynı yerde:

{
  "status": "success",
  "code": "SUCCESS",
  "data": { "items": [ { "id": "sub_01J8ZU..." } ] },
  "pageInfo": { "hasMore": false, "nextCursor": null, "totalCount": null },
  "requestId": "req_01J8ZV..."
}

Bilmeniz gerekenler

  • totalCount varsayılan olarak null'dır. Toplam kayıt sayısı cursor sayfalamada pahalıdır (filtreli COUNT(*) = tam tarama). İlerlemek için hasMore kullanın. Gerçekten gerekiyorsa includeTotal=true gönderin — bu istek daha yavaştır ve rate-limit'e daha ağır sayılır.
  • Sayfalama yalnızca ileri yöndedir. prevCursor yoktur. Geri navigasyon için gördüğünüz cursor'ları kendi tarafınızda saklayın.
  • cursor opaktır. İçeriğini çözmeyin, üretmeyin, elle değiştirmeyin — biçimi haber verilmeden değişebilir. Yalnızca bir önceki yanıttan aldığınız değeri geri gönderin.
  • Sıralama her zaman en yeniden en eskiye ve deterministiktir. Siz sayfalarken liste yazılmaya devam etse bile kayıt tekrarlanmaz veya atlanmaz.
  • Filtreleri değiştirirseniz cursor'ı sıfırlayın (baştan başlayın). Cursor, üretildiği sorguya bağlıdır.

Sıralama neden hep bir zaman damgası? Cursor sayfalaması, sıralama anahtarının değişmez olmasını şart koşar — imleç o anahtarın bir değerine çapa atar. Anahtar sonradan değişirse (risk skoru, "son görülme", "son güncelleme" gibi) bir kayıt ya iki kez görünür ya da hiç görünmez. Bu yüzden sayfalanan listelerde sıralama, kaydın oluşturulma anına bağlıdır; "en son güncellenen" gibi sıralamalar cursor almaz.

INVALID_CURSOR

Bozuk, tanınmayan ya da desteklenmeyen sürümdeki bir cursor 400 INVALID_CURSOR döndürür.

{
  "status": "failure",
  "code": "INVALID_CURSOR",
  "statusCode": 400,
  "message": "Sayfalama imleci geçersiz — …"
}

Ne yapmalı: cursor'ı elle üretmeyin; bir önceki yanıttaki pageInfo.nextCursor değerini aynen gönderin. Filtreleri değiştirdiyseniz cursor'ı sıfırlayın.

Neden hata dönüyor da sessizce ilk sayfaya dönmüyor? Sessiz sıfırlama, mutabakat döngülerinde aynı sayfanın iki kez işlenmesine yol açıyordu. Açık hata, sessiz veri tekrarından iyidir.

Tüm sayfaları dolaşma

let cursor: string | undefined;
 
do {
  const qs = new URLSearchParams({ limit: '100', ...(cursor ? { cursor } : {}) });
  const res = await fetch(
    `https://api-gateway.lunixi.com/api/v1/wallet/operations?${qs}`,
    { headers: { Authorization: `Bearer ${process.env.LUNIXI_SECRET_KEY}` } },
  );
  const body = await res.json();
 
  // `data` bu endpoint'te bir nesne; başka endpoint'lerde dizi olabilir.
  // `pageInfo` ise HER İKİ durumda da aynı yerde — `data`'nın yanında.
  process(body.data.operations ?? body.data.items ?? body.data);
 
  cursor = body.pageInfo.hasMore ? body.pageInfo.nextCursor : undefined;
} while (cursor);

Eski parametreler

Bazı endpoint'ler geçmişten gelen page, offset, pageSize, pageToken, beforeSeq, cursorCreatedAt+cursorId parametrelerini kabul eder. Bunlar çalışmaya devam eder ama kullanıldıklarında yanıt şu başlıkları taşır:

Deprecation: true
Sunset: Sun, 31 Jan 2027 00:00:00 GMT
X-Deprecated-Params: offset
⚠️

Geçişi planlamanız için tek sinyal bu başlıklardır. İstemcinizde Deprecation: true gördüğünüzde loglayacak bir yer bırakın — Sunset tarihine kadar sessizce çalışmaya devam eder ve o tarihte fark etmeniz gerekir.

Endpoint durumu

Geçiş sürüyor. Bugünkü fotoğraf:

Cursor kabul eden endpoint'ler

EndpointKabul ettiğiYanıt
GET /paymentslimit + cursor · page deprecated{data[], total, page, limit, totalPages, nextCursor}
POST /payments/transactions:searchgövdede page.limit + page.afterRelay zarfı + pageInfo
GET /payments/splitlimit + afterRelay zarfı + pageInfo
GET /payments/links, /payments/links/{id}/attempts, /payments/links/{id}/paymentslimit + cursor (listede includeTotal de){items} + pageInfo
GET /kyc/sessionslimit + cursor{items, nextCursor} + pageInfo
GET /wallet/operations, /end-users, /corporate-accounts, /qrlimit + cursor{operations|items, nextCursor} + pageInfo
GET /subscriptions/* (13 uç)limit + cursor · pageSize/pageToken deprecated{items, nextPageToken, pageSize} + pageInfo
GET /marketplace/dealerslimit + cursor · offset deprecated{items, total, nextCursor}
GET /automation/audit-events, /connector-deliveries, /trigger-eventslimit + cursor · offset deprecated{items, total, limit, offset, nextCursor}
GET /identity/live-sessionslimit + cursordata dizi + pageInfo
GET /identity/mobile-threat/decisions, /pos-threat/decisionslimit + cursordata dizi + pageInfo
GET /fraud/decision-logslimit + cursordata dizi + pageInfo
GET /automation/flows/{id}/runslimit + cursor · offset deprecated{items, total, nextCursor} + pageInfo

Son iki satırda data bir dizidir ve öyle kalacaktır — sayfalama bilgisi yanındaki pageInfo'dan okunur. Diziyi nesneye çevirmek mevcut entegrasyonları kıracağı için bilinçli olarak yapılmadı.

Henüz cursor kabul etmeyenler — burada limit bir sayfa boyutu değil tavandır: ötesindeki kayıtlara bugün erişilemez ve yanıt bunu belirtmez. "En yeni N kaydı döner" diye düşünün.

EndpointBugün
GET /automation/shadow-runs, /approvalslimit + offset (deprecated)
GET /identity/mobile-apps, /live-sessions/top-sources, /mobile-threat/package-ioc*yalnız limit
GET /marketplace/audit-log, /payout-ledger, /transactionsyalnız limit
GET /ledger/payoutslimit + beforeSeq

Tasarım gereği sayfalanmayanlar — bunlarda cursor beklemeyin, gelmeyecek:

EndpointNeden
GET /identity/live-sessions/{traceId}/timeline, /related-sessions, /account-linksTek bir trace'in detayı — doğası gereği sınırlı
GET /sanction/intelligence/entity-searchAlaka sıralı arama; "en iyi N eşleşme" doğru davranıştır
GET /identity/admin/intelligence/{device,ip,identity}-profilesRisk skoruna göre sıralı triyaj listesi — sıralama anahtarı DEĞİŞKEN
GET /identity/admin/bot-trust/actorsKayan pencere (varsayılan 30 gün) üzerinde toplulaştırma

Risk sıralı listeler neden cursor almıyor? Keyset sayfalaması, sıralama anahtarının değişmez olmasını şart koşar — imleç, o anahtarın bir değerine çapa atar. Risk skoru her yeni oturumda yeniden hesaplanır: 2. sayfayı isterken 1. sayfadaki bir profilin skoru düşerse o profil 2. sayfaya kayar ve iki kez görünür; yükselen bir profil ise hiç görünmez. Bu yüzeylerde doğru davranış zaten "en riskli N" — sıralamanın tamamını dolaşmak değil. Daraltmak için scopeType, fingerprintId, deviceId, ipAddress, identityKey filtrelerini kullanın.

Panel / operatör yüzeyi — bu endpoint'ler RBAC izni ister ve Swagger'da listelenmez; entegrasyon akışında değil, panelde kullanılır.

EndpointKabul ettiği
GET /sanction/screening/historylimit + cursor ✅
GET /sanction/monitoring/{alerts,entities}limit + cursor + includeTotal ✅
GET /sanction/analyst/{case-queue,hit-queue}limit + cursor ✅
GET /sanction/monitoring/matcheslimit + cursor ✅
GET /sanction/blacklistlimit + cursor + sayaçlar ✅
GET /fraud/admin/{shadow-runs,outbox-events,blacklists}limit + cursor ✅
GET /fraud/admin/script-assets/{id}/execution-logslimit + cursor ✅
GET /identity/admin/bot-trust/{issuers,keys}limit — kayıtlı sağlayıcı kümesi küçüktür
GET /identity/admin/bot-trust/review-eventslimit (tavan 100)

bot-trust/review-events bir denetim izidir ve yalnız yönetici işlemiyle (sağlayıcı/anahtar durum değişikliği) yazılır — insan hızında büyür, akış değildir. Bugün 100'lük tavan yeterli; issuerId ile daraltıldığında bir sağlayıcının tüm geçmişi tek yanıta sığar. Tavana dayanan bir kurulum görürsek cursor eklenecek; "tasarım gereği sayfalanmaz" DEĞİL.

İlgili