Paraşüt entegrasyonunun kalbi satış faturasıdır. Siparişleri, abonelikleri ya da hizmet bedellerini ön muhasebeye taşımak istediğinizde sonunda hep aynı uç noktaya gelirsiniz: POST /v4/{company_id}/sales_invoices. Bu uç nokta küçük görünür ama içinde cari ilişkisi, kalem dizisi, KDV ve indirim alanları, salt okunur toplamlar ve mükerrer faturayı engelleyen bir idempotans mekanizması barındırır.
Bu rehberde bir satış faturasını Paraşüt API v4 ile baştan sona nasıl oluşturacağınızı, hangi alanların zorunlu olduğunu, kalemlere ürün ve indirim bağlamayı, external_reference_key ile tekrar denemeleri güvenli hâle getirmeyi ve faturayı iptal etme, arşivleme ve silme uçlarını anlatıyoruz. Paraşüt API'nin genel yapısını henüz tanımıyorsanız önce Paraşüt entegrasyonu genel rehberine göz atın.
Başlamadan Önce Hazır Olması Gerekenler

Fatura isteği tek başına çalışmaz; üç kimliğe ihtiyaç duyar. Paraşüt'ün resmî fatura rehberi akışı müşteri, ürün ve fatura olarak üç zorunlu adıma böler; tahsilat, resmileştirme ve PDF isteğe bağlı adımlardır.
- Access token: OAuth2 Authorization Code + PKCE akışıyla alınan Bearer token. Ayrıntılar için Paraşüt OAuth2 token alma rehberine bakın.
- company_id: Faturayı kesecek firmanın kimliği. Rehber bunu
GET /v4/me?include=companiesile bulmayı gösterir. Tüm yollarhttps://api.parasut.com/v4/{company_id}/...biçimindedir. - Cari (contact) id: Faturanın kesileceği müşterinin Paraşüt'teki kimliği. Arama ve oluşturma için cari hesap oluşturma rehberini kullanın.
- Ürün (product) id: Her fatura kalemi bir ürüne ya da hizmete bağlanır. Ürün tarafı için ürün, stok ve depo yönetimi rehberine bakın.
Her istekte Authorization: Bearer başlığı ve Content-Type: application/json (ya da application/vnd.api+json) gönderilir. Gövde JSON:API biçimindedir: data.type çoğuldur, ilişkilerdeki id değerleri string olarak yazılır. Filtreleme ve sayfalama ayrıntıları için JSON:API filtreleme ve sayfalama rehberine bakabilirsiniz.
Satış Faturası İsteğinin Yapısı
Bir satış faturası üç parçadan oluşur: attributes içinde faturanın kendi alanları, relationships.contact içinde müşteri, relationships.details içinde kalemler. OpenAPI belgesine göre zorunlu fatura alanları yalnızca item_type ve issue_date'dir; resmî rehber ayrıca cari ilişkisini ve kalem dizisini zorunlu olarak işaretler.
Fatura düzeyindeki alanlar
| Alan | Tip | Açıklama |
|---|---|---|
| item_type | enum (zorunlu) | invoice, export, estimate, cancelled, recurring_invoice, recurring_estimate, recurring_export, refund |
| issue_date | tarih (zorunlu) | Düzenleme tarihi, YYYY-MM-DD |
| due_date | tarih | Son tahsilat tarihi |
| currency | enum | TRL, USD, EUR, GBP (TRY değil) |
| exchange_rate | sayı | TRL dışı para birimlerinde döviz kuru |
| invoice_series / invoice_id | string / integer | Fatura serisi ve sıra numarası |
| description | string | Fatura açıklaması |
| invoice_discount_type / invoice_discount | enum / sayı | Fatura geneli indirim: percentage veya amount |
| withholding_rate | sayı | Stopaj oranı |
| order_no / order_date | string / tarih | Sipariş no ve tarihi; resmileştirmede biri doluysa diğeri de zorunlu |
| billing_address, tax_number, tax_office, city, district | string | Faturaya basılacak alıcı bilgileri |
| invoice_note | string | Fatura notu |
| cash_sale | boolean | Peşin satış |
| shipment_included | boolean | İrsaliyeli fatura |
item_type değerleri arasında proforma için estimate, iade için refund, ihracat için export bulunur. Proforma kayıtlar daha sonra PATCH /sales_invoices/{id}/convert_to_invoice ile faturaya çevrilebilir; belgeye göre bu sırada ürünlerin stok hareketleri varsayılan depodan oluşur. Teklif ve proforma akışının tamamı için teklif ve proforma rehberine bakın.
Kalem (details) alanları
relationships.details.data dizisindeki her eleman type: "sales_invoice_details" taşır ve bir fatura satırıdır. Kalem düzeyinde üç alan zorunludur: miktar, birim fiyat ve KDV oranı.
| Alan | Tip | Açıklama |
|---|---|---|
| quantity | sayı (zorunlu) | Miktar |
| unit_price | sayı (zorunlu) | Birim fiyat |
| vat_rate | sayı (zorunlu) | KDV oranı, yüzde olarak (ör. 20) |
| discount_type / discount_value | enum / sayı | Satır indirimi: percentage veya amount |
| vat_withholding_rate | sayı | Tevkifat oranı |
| excise_duty_type / excise_duty_value | enum / sayı | ÖTV tipi ve değeri |
| communications_tax_rate | sayı | ÖİV oranı |
| description | string | Kalem açıklaması |
| relationships.product | ilişki | Ürünün id değeri, type: products |
| relationships.warehouse | ilişki | Depo id değeri, type: warehouses (depo takibi varsa) |
İndirim iki düzeyde tanımlanabilir: kalemdeki discount_type + discount_value yalnızca o satırı etkiler, faturadaki invoice_discount_type + invoice_discount ise faturanın genelini. Her ikisi de percentage ya da amount değerini alır. Hangi toplamın nasıl hesaplandığını tahmin etmek yerine, oluşturma yanıtındaki salt okunur toplamları (total_discount, total_invoice_discount, net_total) kendi sisteminizdeki tutarla karşılaştırın.
Tam Örnek: curl ile Satış Faturası Oluşturma
Aşağıdaki istek iki kalemli, satır ve fatura indirimli, idempotans anahtarlı bir satış faturası açar. Yer tutucuları kendi değerlerinizle değiştirin. Paraşüt'ün resmî kural dosyası, şemada number olan alanların JSON sayısı olarak gönderilmesini ister; bu yüzden fiyatlar tırnaksız yazıldı.
curl -X POST "https://api.parasut.com/v4/{company_id}/sales_invoices" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"data": {
"type": "sales_invoices",
"attributes": {
"item_type": "invoice",
"issue_date": "2026-11-03",
"due_date": "2026-12-03",
"currency": "TRL",
"invoice_series": "A",
"invoice_id": 1042,
"description": "Kasım 2026 hizmet bedeli",
"invoice_discount_type": "percentage",
"invoice_discount": 5
},
"relationships": {
"contact": { "data": { "type": "contacts", "id": "<CONTACT_ID>" } },
"details": {
"data": [
{
"type": "sales_invoice_details",
"attributes": {
"quantity": 2,
"unit_price": 750,
"vat_rate": 20,
"discount_type": "amount",
"discount_value": 50,
"description": "Yıllık lisans"
},
"relationships": {
"product": { "data": { "type": "products", "id": "<PRODUCT_ID_1>" } }
}
},
{
"type": "sales_invoice_details",
"attributes": { "quantity": 1, "unit_price": 300, "vat_rate": 20 },
"relationships": {
"product": { "data": { "type": "products", "id": "<PRODUCT_ID_2>" } }
}
}
]
},
"external_reference_key": {
"data": {
"type": "external_reference_keys",
"attributes": { "key": "6f1c2a4e-9b3d-4e8a-a1f0-2c5d7e9b3a14" }
}
}
}
}
}'
Başarılı istek 201 döner ve data içinde oluşan faturayı verir. data.id değerini saklayın; tahsilat, resmileştirme ve iptal gibi sonraki tüm işlemler bu kimlikle yapılır. Yanıttaki net_total (genel toplam), gross_total (ara toplam), total_vat, remaining (ödenmemiş tutar) ve payment_status alanları Paraşüt tarafından hesaplanır.
external_reference_key ile Mükerrer Faturayı Önleme

Fatura isteği zaman aşımına uğradığında faturanın oluşup oluşmadığını bilemezsiniz. İsteği körü körüne tekrar gönderirseniz aynı sipariş için ikinci bir fatura açılabilir. Paraşüt bunun için relationships.external_reference_key ilişkisini sunar. Bu standart bir JSON:API ilişkisi değildir: data içinde id yoktur, type: "external_reference_keys" ve attributes.key vardır. Anahtar UUID biçiminde olmalıdır.
- Anahtarı istekten önce saklayın. Her yeni fatura için bir UUID üretin ve isteği göndermeden önce kendi veritabanınıza, sipariş kaydının yanına yazın.
- Tekrarda aynı anahtarı kullanın. Zaman aşımı, 5xx ya da bağlantı hatasında isteği aynı anahtarla yeniden gönderin.
- 422 mükerrer yanıtını okuyun. Fatura zaten oluşmuşsa yanıt 422
Bu key ile daha önce bir fatura oluşturulmuş.olur; mevcut faturanın kimliği hatanınmeta.sales_invoice_idalanındadır. Mesaj metnine değil bu alana güvenin. - Geçersiz UUID gönderirseniz 422
Gönderilen key geçerli bir UUID formatında değil.döner.
Anahtarla faturayı doğrudan sorgulamak için ayrı bir uç nokta da vardır. Anahtar bulunamazsa yanıt gövdesiz 404 olur; JSON ayrıştırmaya çalışmayın.
curl -X GET "https://api.parasut.com/v4/{company_id}/sales_invoices/find_by_external_reference_key?external_reference_key=6f1c2a4e-9b3d-4e8a-a1f0-2c5d7e9b3a14" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Accept: application/json"Aşağıdaki Node.js örneği bu kuralları birleştirir. Önce tüm örneklerde kullanacağımız küçük bir istemci:
// parasut.js - tüm örneklerin kullandığı küçük istemci
const API = 'https://api.parasut.com/v4';
const COMPANY_ID = process.env.PARASUT_COMPANY_ID;
export async function parasut(path, init = {}) {
const res = await fetch(`${API}/${COMPANY_ID}${path}`, {
...init,
headers: {
Authorization: `Bearer ${await getAccessToken()}`, // kendi token deponuz
Accept: 'application/json',
'Content-Type': 'application/json',
...init.headers,
},
signal: AbortSignal.timeout(30_000),
});
const text = await res.text();
let body;
try { body = text ? JSON.parse(text) : undefined; } catch { body = text; }
return { status: res.status, body };
}
export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));Ardından faturayı yalnızca bir kez oluşturan fonksiyon:
import { randomUUID } from 'node:crypto';
import { parasut, sleep } from './parasut.js';
export async function createInvoiceOnce(record, body) {
// 1) Anahtarı istekten ÖNCE üretip kalıcı olarak saklayın
if (!record.externalKey) {
record.externalKey = randomUUID();
await db.save(record);
}
body.data.relationships.external_reference_key = {
data: { type: 'external_reference_keys', attributes: { key: record.externalKey } },
};
for (let attempt = 0; attempt < 3; attempt++) {
let res;
try {
res = await parasut('/sales_invoices', { method: 'POST', body: JSON.stringify(body) });
} catch {
await sleep(2 ** attempt * 1000); // zaman aşımı: aynı anahtarla tekrar
continue;
}
if (res.status === 201) return res.body.data.id;
if (res.status === 422) {
// Fatura daha önce oluştuysa mevcut id meta alanında gelir
const hit = res.body?.errors?.find((e) => e.meta?.sales_invoice_id);
if (hit) return String(hit.meta.sales_invoice_id);
throw new Error('Doğrulama hatası: ' + JSON.stringify(res.body?.errors));
}
if (res.status >= 500) { await sleep(2 ** attempt * 1000); continue; }
throw new Error(`HTTP ${res.status}`); // 400, 401, 403: tekrar denemeyin
}
// 2) Son kontrol: fatura oluşmuş ama yanıt kaybolmuş olabilir
const found = await parasut(
`/sales_invoices/find_by_external_reference_key?external_reference_key=${record.externalKey}`);
if (found.status === 200) return found.body.data.id;
throw new Error('Fatura oluşturulamadı; iş aynı anahtarla sonra yeniden denenecek');
}Faturayı Okuma ve Listeleme
Tek bir faturayı GET /sales_invoices/{id} ile okursunuz. include parametresi şu değerleri kabul eder: category, contact, details, details.product, details.warehouse, payments, payments.transaction, tags, sharings, recurrence_plan, active_e_document. Listede olmayan bir değer uydurmayın.
Liste ucu filter[issue_date], filter[due_date], filter[contact_id], filter[invoice_id], filter[invoice_series], filter[item_type], filter[print_status] ve filter[payment_status] filtrelerini destekler. page[size] varsayılan 15, en fazla 25'tir. Kalemlerin tamamı için GET /sales_invoices/{id}/details ucu sayfalanmaz ve include almaz; ürün ve depo ilişkileri her zaman included içinde gelir.
# Bir carinin ödenmemiş faturaları, kalemleri ve ürünleriyle birlikte
curl -X GET "https://api.parasut.com/v4/{company_id}/sales_invoices?filter[contact_id]=123&filter[payment_status]=overdue&include=details.product&sort=-issue_date&page[size]=25" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Accept: application/json"Faturayı Güncelleme
Satış faturası PUT /sales_invoices/{id} ile güncellenir (PATCH değil). Gövde, oluşturmadaki ile aynı JSON:API yapısındadır ve aynı salt okunur alan kuralı geçerlidir. Resmî kural dosyasına göre ilişkilerde mevcut kayıtların id'si dolu, yeni kalemlerin id'si boş gönderilir; mevcut kalemlerin kimliklerini GET /sales_invoices/{id}/details ile alabilirsiniz.
İptal, Geri Alma, Arşivleme ve Silme

Paraşüt'te "iptal" ile "silme" farklı işlemlerdir. Hangisini ne zaman kullanacağınız muhasebe sürecinize bağlıdır; aşağıdaki tablo yalnızca API davranışını özetler.
| İşlem | Uç nokta | Belgelenen davranış |
|---|---|---|
| İptal | DELETE /sales_invoices/{id}/cancel | Faturayı iptal eder, silmez; 200 ile faturayı döner |
| İptali geri al | PATCH /sales_invoices/{id}/recover | Fatura iptalden önceki item_type değerine döner, bilinmiyorsa invoice olur; iptal edilmemiş faturada 422 |
| Arşivle | PATCH /sales_invoices/{id}/archive | 200 ile faturayı döner |
| Arşivden çıkar | PATCH /sales_invoices/{id}/unarchive | 200 ile faturayı döner |
| Sil | DELETE /sales_invoices/{id} | 204, gövdesiz |
| Proformayı faturaya çevir | PATCH /sales_invoices/{id}/convert_to_invoice | Stok hareketleri varsayılan depodan oluşur |
# İptal (silmez, 200 ile faturayı döner)
curl -X DELETE "https://api.parasut.com/v4/{company_id}/sales_invoices/{id}/cancel" \
-H "Authorization: Bearer <ACCESS_TOKEN>" -H "Accept: application/json"
# İptali geri al
curl -X PATCH "https://api.parasut.com/v4/{company_id}/sales_invoices/{id}/recover" \
-H "Authorization: Bearer <ACCESS_TOKEN>" -H "Accept: application/json"
# Arşivle / arşivden çıkar
curl -X PATCH "https://api.parasut.com/v4/{company_id}/sales_invoices/{id}/archive" \
-H "Authorization: Bearer <ACCESS_TOKEN>" -H "Accept: application/json"
# Kalıcı silme (204, gövdesiz)
curl -X DELETE "https://api.parasut.com/v4/{company_id}/sales_invoices/{id}" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Peşin Satış ve Tahsilat
Fatura şemasında payment_account_id, payment_date ve payment_description alanları da vardır. Belgeye göre bunlar yalnızca peşin faturalarda ödeme oluşturmak içindir. Normal bir faturada bu alanları göndermeyin; tahsilatı fatura oluştuktan sonra POST /sales_invoices/{id}/payments ile ekleyin. Aynı faturaya birden fazla kısmi tahsilat eklenebilir.
Faturanın PDF'i Nereden Alınır?
Satış faturası uç noktalarının listesinde doğrudan bir PDF ucu yoktur. PDF, fatura resmileştirildikten sonra oluşan e-belge üzerinden alınır: e-Arşiv için GET /e_archives/{id}/pdf, e-Fatura için GET /e_invoices/{id}/pdf. Yanıt PDF dosyasının kendisi değil, url ve expires_at alanlarını taşıyan bir JSON'dur. Resmileştirme adımları ve PDF alma ayrıntıları e-Fatura ve e-Arşiv gönderme rehberinde anlatılıyor.
Sık Yapılan Hatalar
| Hata | Sonuç | Doğrusu |
|---|---|---|
| currency: TRY | Doğrulama hatası | TRL, USD, EUR veya GBP |
| İlişki id değerini sayı göndermek | JSON:API kuralına aykırı | id değerleri string: "123" |
| invoice_no göndermek | Salt okunur alan | invoice_series + invoice_id |
| Tekrar denemede yeni UUID üretmek | Mükerrer fatura riski | Aynı anahtarı yeniden kullanın |
| order_no var, order_date yok | Resmileştirmede sorun | İkisini birlikte gönderin ya da hiç göndermeyin |
| page[size]=100 | Limit aşımı | Satış faturası listesinde en fazla 25 |
| Peşin olmayan faturada payment_account_id | Beklenmeyen ödeme kaydı | Tahsilatı payments ucuyla ekleyin |
Sık Sorulan Sorular
Paraşüt API ile fatura keserken cari ve ürün önceden var olmalı mı?
Evet. Fatura isteği müşteriyi ve kalem ürünlerini kimlikleriyle ilişkilendirir. Cari ve ürünler bir kez oluşturulup tekrar tekrar kullanılır; her siparişte yeni ürün açmak yerine kendi sisteminizde bir eşleme tablosu tutun. Bu yaklaşım e-ticaret siparişini Paraşüt'e aktarma rehberinde ayrıntılı anlatılıyor.
Fatura numarasını kendim verebilir miyim?
Fatura numarası invoice_series (seri) ve invoice_id (sıra, integer) alanlarıyla verilir. invoice_no alanı salt okunurdur; gönderseniz de dikkate alınmaz, yanıtta okursunuz.
Aynı siparişin iki kez faturalanmasını nasıl engellerim?
Her fatura için bir UUID üretip istekten önce saklayın ve external_reference_key ilişkisiyle gönderin. Aynı anahtarla gelen ikinci istek yeni fatura açmaz; 422 döner ve mevcut faturanın kimliği meta.sales_invoice_id alanında gelir. Anahtarla arama için find_by_external_reference_key ucunu kullanabilirsiniz.
Dövizli satış faturası nasıl kesilir?
currency alanına USD, EUR ya da GBP yazıp exchange_rate ile kuru gönderirsiniz. Kurun nereden alınacağı ve tahsilattaki kur farkı için dövizli fatura ve döviz kuru rehberine bakın.
İptal ettiğim faturayı geri alabilir miyim?
Evet, PATCH /sales_invoices/{id}/recover iptal edilmiş faturayı önceki türüne döndürür. İptal edilmemiş bir faturada bu istek 422 döner. Silinen bir fatura için ise belgede geri alma ucu yoktur.
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 ile cari oluşturma · Paraşüt ürün ve stok yönetimi · e-Fatura ve e-Arşiv gönderme · Tahsilat ve ödeme kaydı · İrsaliye ve e-İrsaliye · Hata kodları ve rate limit