Paraşüt API v4, istek ve yanıt biçiminde büyük ölçüde JSON:API standardını izler. Bu, tüm uçlarda aynı iskeleti görmeniz demektir: kaynaklar data altında, alanlar attributes içinde, ilişkiler relationships içinde durur; listeler meta ile sayfalama bilgisi taşır. Bu yapıyı bir kez doğru kurduğunuzda cari, ürün ya da fatura uçları arasında geçiş yapmak kolaylaşır.

Bu yazıda istek gövdesinin kurallarını, include ile ilişkili kayıtları tek istekte almayı, filter[...] ve sort parametrelerini, sayfa boyutu limitlerini ve istisnalarını ele alıyor, sonunda tüm carileri sayfa sayfa çeken bir Node.js örneği veriyoruz. Token almayı OAuth2 yazımızda, genel mimariyi Paraşüt entegrasyonu rehberinde anlattık.

İstek temelleri: adres ve başlıklar

İstek temelleri: adres ve başlıklar: Authorization, Content-Type, Accept

Tüm yollar https://api.parasut.com/v4/{company_id}/... biçimindedir; tek istisna GET /me ucudur. company_id, erişilmek istenen firmanın Paraşüt kayıt numarasıdır; örneğin 115 numaralı firmanın cari listesi https://api.parasut.com/v4/115/contacts adresindedir.

BaşlıkDeğer
AuthorizationBearer <ACCESS_TOKEN>
Content-Typeapplication/vnd.api+json veya application/json
Acceptapplication/json

Kimlik tiplerine dikkat edin: JSON:API gövdesindeki id değerleri her zaman string, yoldaki company_id ve {id} parametreleri ise integer'dır. Şemada number olan alanlar JSON sayısı olarak, tarihler YYYY-MM-DD olarak gönderilir; para birimi değerleri TRL, USD, EUR, GBP'dir (TRY değil).

Gövde yapısı: data, type, attributes, relationships

Kayıt oluşturma ve güncellemede gövde tek bir data nesnesidir:

AlanKural
data.typeKaynağın çoğul adı: contacts, products, sales_invoices. Tek istisna webhook: "webhook"
data.idOluştururken boş bırakılır ya da hiç gönderilmez
data.attributesKaydın alanları; salt okunur alanlar gönderilmez
data.relationshipsİlişkili kayıtlar; mevcut kayıtların id değeri string olarak dolu, yeni kalemlerin id değeri boş

Örneğin bir cari oluştururken name ve account_type zorunludur; account_type yalnızca customer veya supplier olabilir. balance gibi bakiye alanları salt okunurdur ve gönderilmez. Mevcut bir kategoriye bağlamak için ilişki kullanılır:

bash
curl -X POST "https://api.parasut.com/v4/{company_id}/contacts" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/vnd.api+json" \
  -H "Accept: application/json" \
  -d '{
    "data": {
      "type": "contacts",
      "attributes": {
        "name": "Örnek Yazılım A.Ş.",
        "account_type": "customer",
        "contact_type": "company",
        "tax_number": "1234567890",
        "tax_office": "Kadıköy"
      },
      "relationships": {
        "category": { "data": { "type": "item_categories", "id": "<CATEGORY_ID>" } }
      }
    }
  }'

Satış faturasında kalemler relationships.details.data dizisinde kendi attributes alanlarıyla gönderilir ve yeni oldukları için id taşımaz; carinin id değeri ise doludur. Fatura gövdesinin tamamı fatura kesme yazımızda, cari alanlarının tamamı cari hesap yazımızda yer alıyor.

include: ilişkili kayıtları tek istekte almak

include: ilişkili kayıtları tek istekte almak: contacts, products, sales_invoices, purchase_bills

include parametresi, ana kayıtla birlikte ilişkili kayıtları yanıtın included dizisinde döndürür. Değerler virgülle ayrılır ve yalnızca o uçta listelenen değerler kullanılabilir. Nokta ile iç içe ilişki de istenebilir, örneğin details.product.

KaynakGeçerli include değerleri
contactscategory, contact_portal, contact_people
productsinventory_levels, category
sales_invoicescategory, contact, details, details.product, details.warehouse, payments, payments.transaction, tags, sharings, recurrence_plan, active_e_document
purchase_billscategory, spender, details, details.product, details.warehouse, payments, payments.transaction, tags, recurrence_plan, active_e_document, pay_to
shipment_documentscontact, stock_movements, stock_movements.product, tags, invoices
meuser_roles, companies, profile
bash
curl "https://api.parasut.com/v4/{company_id}/sales_invoices/{id}?include=contact,details.product" \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Accept: application/json" 

Yanıtta ilişki relationships.contact.data içinde yalnızca type ve id olarak durur; kaydın kendisi included dizisindedir. İkisini type + id anahtarıyla eşlersiniz:

javascript
const index = new Map((body.included ?? []).map((r) => [`${r.type}:${r.id}`, r]));
const rel = body.data.relationships?.contact?.data;
const contact = rel ? index.get(`${rel.type}:${rel.id}`) : undefined;
console.log(contact?.attributes.name);

filter[...]: uca özel filtreler

Filtreler filter[alan]=değer biçimindedir ve yalnızca uçta listelenen alanlarla çalışır. contacts ucunda belgelenen filtreler name, email, tax_number, tax_office, city ve account_type'tır (GET /contacts belgesi). sales_invoices ucunda issue_date, due_date, contact_id, invoice_id, invoice_series, item_type, print_status ve payment_status filtreleri vardır.

text
GET /{company_id}/contacts?filter[tax_number]=1234567890
GET /{company_id}/contacts?filter[account_type]=supplier&filter[city]=İstanbul
GET /{company_id}/sales_invoices?filter[payment_status]=overdue

Bazı tarih filtreleri operatör alır: filter[issue_date][gt]=2026-01-01 gibi. Kullanılabilen operatörler eq, lt, gt, gteq, lteq, not_eq olup bu kullanım checks, inbound_e_despatches ve warehouse_transfers uçlarında belgelenmiştir. Başka bir uçta operatörlü filtre kullanmadan önce o ucun belgesine bakın.

filter[...] kullanmayan istisnalar

UçParametre biçimi
commentscommentable_type ve commentable_id zorunlu düz parametre
transaction_history_itemstransaction_type_filter, transaction_from_date_filter, transaction_to_date_filter, tag_ids_filter
e_despatch_inboxestax_number zorunlu düz parametre
products/by_idsids=1,2,3 (en fazla 50 ID)

sort: sıralama

Sıralama sort=alan ile yapılır; azalan sıralama için alanın başına - eklenir, örneğin sort=-issue_date. Sıralanabilir alanlar ve varsayılan değer uca göre değişir. contacts için sıralanabilir alanlar id, balance, name, email olup varsayılan -balance'tır. sales_invoices için id, issue_date, due_date, remaining, remaining_in_trl, description, net_total kullanılabilir; varsayılan id'dir. transaction_history_items uçlarında sıralama sabittir (en yeniden en eskiye).

Sayfalama: page[number], page[size] ve limitler

Sayfalama: page[number], page[size] ve limitler: Diğer tüm listeler (contacts, products, sales_invoices..., transaction_history_items, {belge}/{id}/payments listeleri, checks...

Listeler page[number] ve page[size] parametreleriyle sayfalanır. Çoğu uçta varsayılan sayfa boyutu 15, üst sınır 25'tir. Ancak her uç aynı değildir:

UçlarVarsayılanEn fazla
Diğer tüm listeler (contacts, products, sales_invoices, purchase_bills, accounts, shipment_documents ...)1525
transaction_history_items1025
{belge}/{id}/payments listeleri, checks, price_lists, price_lists/{id}/details1510000
webhooks, exchange_rates, products/by_ids, sales_invoices/{id}/details, purchase_bills/{id}/details, price_lists/{id}/product_discounts, e_despatch_inboxesSayfalama yokYok

Ek notlar: opening_balances ödeme listesi sayfalanmaz. sales_offers listesinde spec yalnızca page[number], sales_offers/{id}/details ucunda yalnızca page[size] belgeler. En az comments ve inbound_e_despatches uçlarında page[size] 25'ten büyükse 422 döner; limiti aşmayın.

meta alanı

Liste yanıtının meta nesnesi dört alan taşır:

json
{
  "data": [ { "id": "101", "type": "contacts", "attributes": { "name": "..." } } ],
  "meta": {
    "current_page": 1,
    "total_pages": 7,
    "total_count": 160,
    "per_page": 25
  }
}

Kural: current_page < total_pages olduğu sürece page[number] değerini artırın. Yukarıdaki değerler yalnızca biçimi göstermek içindir.

Örnek: tüm carileri sayfa sayfa çekmek

Aşağıdaki fonksiyon müşteri tipindeki tüm carileri 25'erli sayfalarla çeker, kategori bilgisini include ile aynı istekte alır ve sayfa sayısını meta.total_pages değerinden okur. Token ve tekrar deneme mantığı için OAuth2 ve hata kodları yazılarındaki yardımcıları kullanabilirsiniz.

javascript
const API = 'https://api.parasut.com/v4';

export async function tumMusteriler(companyId, token) {
  const cariler = [];
  const kategoriler = new Map();
  let sayfa = 1;
  let toplamSayfa = 1;

  do {
    const url = new URL(`${API}/${companyId}/contacts`);
    url.searchParams.set('filter[account_type]', 'customer');
    url.searchParams.set('sort', 'id');            // sabit sıralama
    url.searchParams.set('include', 'category');
    url.searchParams.set('page[size]', '25');      // bu uçta üst sınır 25
    url.searchParams.set('page[number]', String(sayfa));

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
    });
    if (!res.ok) throw new Error(`HTTP ${res.status}`); // 401/429 için yeniden deneme katmanını kullanın
    const body = await res.json();

    for (const r of body.included ?? []) {
      if (r.type === 'item_categories') kategoriler.set(r.id, r.attributes.name);
    }
    for (const c of body.data) {
      const kat = c.relationships?.category?.data;
      cariler.push({ id: c.id, ad: c.attributes.name, vkn: c.attributes.tax_number,
                     kategori: kat ? kategoriler.get(kat.id) : null });
    }

    toplamSayfa = body.meta.total_pages;
    sayfa += 1;
  } while (sayfa <= toplamSayfa);

  return cariler;
}

Birkaç pratik not: URLSearchParams köşeli parantezleri %5B ve %5D olarak kodlar, bu normaldir. Rate limit 10 saniyelik pencerelerle uygulandığından çok sayfalı listelerde istekleri bu hızın altında tutun; ayrıntılar rate limit yazımızda. Büyük listeleri her seferinde baştan çekmek yerine düzenli bir zamanlanmış görevle eşitlemek ve değişiklikleri webhook ile yakalamak daha verimlidir; eşitleme işini crontab ile zamanlayabilirsiniz. Sonucu dışa aktarmak isterseniz CSV ve JSON dönüşümü yazımız işinize yarar.

Sparse fieldsets ve diğer JSON:API özellikleri

JSON:API standardı yanıtta yalnızca istenen alanları döndürmek için fields[...] parametresini tanımlar. Paraşüt'ün OpenAPI belgesinde bu parametre hiçbir uçta belgelenmemiştir, bu yüzden ona güvenmeyin. Paraşüt de belgesinde JSON:API standartlarına "çoğunlukla" uymaya çalıştığını söyler; standartta olup belgede olmayan bir özelliği kullanmak yerine her zaman ilgili uç belgesine bakın. Yanıtlarda tanımadığınız alanları ve enum değerlerini yok sayın; Paraşüt'ün kuralı budur.

Sık Sorulan Sorular

Paraşüt API'de bir sayfada en fazla kaç kayıt alabilirim?

Çoğu listede en fazla 25 kayıt alırsınız, varsayılan 15'tir. Ödeme listeleri, checks ve price_lists gibi uçlarda üst sınır 10000'dir; webhooks ve exchange_rates gibi bazı uçlar hiç sayfalanmaz.

Carinin vergi numarasıyla arama nasıl yapılır?

GET /{company_id}/contacts?filter[tax_number]=1234567890 isteğini kullanın. Kayıt varsa data dizisinde döner; yoksa cariyi oluşturursunuz. Bu, Paraşüt'ün kanonik cari hazırlama akışıdır.

include ile istediğim her ilişkiyi alabilir miyim?

Hayır. Yalnızca uçta listelenen değerler geçerlidir. Örneğin purchase_bills ucunda supplier diye bir include yoktur; tedarikçi bilgisini belgelenmiş ilişkilerden almanız gerekir (alış faturası yazımız).

Sayfalama döngüsü ne zaman durmalı?

meta.current_page değeri meta.total_pages değerine ulaştığında durun. Döngüyü boş sayfa gelene kadar sürdürmek yerine total_pages değerine güvenmek gereksiz istekleri önler.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Paraşüt entegrasyonu genel rehberi · Paraşüt OAuth2 token alma · Paraşüt API hata kodları ve rate limit · Paraşüt API ile cari oluşturma · Paraşüt API ürün ve stok · Node.js REST API geliştirme