Tworzenie dokumentów
To kompletny przewodnik po POST /external/v1/documents, jedynym endpoincie, którego potrzebujesz, aby wystawić fakturę, proformę, zaliczkę, rozliczenie, korektę lub zamówienie. Opisuje każde pole żądania, sposób ustalania stron transakcji, logikę zależną od typu dokumentu oraz adnotacje KSeF. Reguły obliczeń kwot i pełną mapę typów znajdziesz w Typy dokumentów i kwoty; tutaj skupiamy się na tym, jak zbudować poprawne żądanie.
Jeśli dopiero zaczynasz, najpierw przejdź Szybki start. Pełny, generowany maszynowo schemat każdego pola jest w Dokumentacji API.
Zanim zaczniesz
- Scope: tworzenie dokumentów wymaga uprawnienia
documents:create. Zobacz Uwierzytelnianie. - Słowniki to źródło prawdy: zamiast wpisywać wartości enumów na sztywno, pobierz je raz z
GET /external/v1/dictionaries. Endpoint zwraca aktualne listy: typy dokumentów, metody i statusy płatności, stawki VAT, kody GTU, waluty, kody krajów, procedury marży, typy zwolnienia z VAT oraz nagłówki dat. Dzięki temu Twoja integracja nie rozjedzie się, gdy dodamy nową pozycję. - Idempotencja: każde tworzenie warto wysyłać z nagłówkiem
Idempotency-Key, żeby ponowienie po przerwanej sieci nie utworzyło duplikatu. Szczegóły w Idempotentność.
Pełny spis wartości. Wszystkie enumy (typy dokumentów, stawki VAT, waluty, kody krajów, GTU, procedury marży, …) opisuje strona Słowniki, a na żywo pobierzesz je z
GET /dictionaries. Słownik zwraca dokładnie te typy dokumentów, które przyjmuje ten endpoint.
Minimalne żądanie
Sam endpoint utworzy dokument już przy documentType, issueDate, nabywcy z nip i name oraz jednej pozycji. To jednak nie wystarczy do realnego obiegu: żeby faktura przeszła wysyłkę do KSeF, nabywca musi mieć pełny adres (street, postalCode, city, countryCode). Dlatego za minimum sensowne uznajemy przykład z adresem. Sprzedawca jest dobierany automatycznie z profilu Twojej firmy.
curl -s -X POST https://prod.api.fakturnia.pl/external/v1/documents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c1f1d6e-0b2a-4a1e-9b1a-2a4e9b1a2a4e" \
-d '{
"documentType": "FakturaVAT",
"issueDate": "2026-06-04",
"buyer": {
"nip": "5261040828",
"name": "Acme Corp Sp. z o.o.",
"street": "Marszałkowska 1",
"postalCode": "00-001",
"city": "Warszawa",
"countryCode": "PL"
},
"products": [
{ "name": "Usługa programistyczna", "unitOfMeasure": "szt.", "quantity": 1, "netPrice": 100, "vatRate": 23 }
]
}'
Adres nabywcy a KSeF. Bez adresu dokument utworzy się i pobierzesz go jako PDF, ale wysyłka do KSeF zostanie odrzucona. Jeśli wystawiasz faktury z myślą o KSeF, traktuj adres nabywcy jako wymagany. Dla osoby fizycznej (
type: "person") podajfirstNameilastNamezamiastname.
Wszystko poza tym jest opcjonalne i ma rozsądne wartości domyślne. Poniżej rozkładamy żądanie na części.
Numerowanie dokumentów
Numer dokumentu składa się z trzech części: symbolu, numeru kolejnego (documentNo) i części datowej (numberingFormat). Najczęstszy układ to numer, a po nim data:
{symbol}{documentNo}{numberingFormat}
Na przykład symbol FV/, numer 4 i format /MM/YYYY dla daty wystawienia 2026-05-07 dają FV/4/05/2026. Część datowa może też stać przed numerem — zależnie od wybranego formatu (patrz „Dostępne formaty" niżej).
Trzy elementy numeru
| Element | Pole | Co to jest |
|---|---|---|
| Symbol | symbol |
Prefiks, np. FV/, KOR/. Domyślnie z ustawień firmy dla danego typu dokumentu. |
| Numer kolejny | documentNo |
Liczba całkowita ≥ 1. To ona rośnie w obrębie okresu. |
| Format | numberingFormat |
Wzór części „datowej", np. /MM/YYYY. Domyślnie z ustawień, fallback /MM/YYYY. |
W formacie podstawiane są tylko dwa tokeny: MM (miesiąc, dwie cyfry) i YYYY (rok), oba z daty wystawienia. Pozostałe znaki (ukośniki, myślniki, litery) wstawiane są dosłownie:
numberingFormat |
issueDate |
Część datowa | Pełny numer (symbol FV/, nr 4) |
|---|---|---|---|
/MM/YYYY |
2026-05-07 |
/05/2026 |
FV/4/05/2026 |
/YYYY |
2026-05-07 |
/2026 |
FV/4/2026 |
-MM-YYYY |
2026-05-07 |
-05-2026 |
FV/4-05-2026 |
Dostępne formaty
Pozycja części datowej zależy od wzoru: w wariancie prefiksowym data stoi po numerze, w sufiksowym przed numerem. Oba są wspierane. W kolumnie „Układ" NUMER oznacza documentNo.
Miesięczne (zawierają MM):
| Format | Układ | Przykład (symbol FV/, nr 4, 2026-05) |
|---|---|---|
/MM/YYYY |
NUMER/MM/YYYY | FV/4/05/2026 |
/YYYY/MM |
NUMER/YYYY/MM | FV/4/2026/05 |
-MM-YYYY |
NUMER-MM-YYYY | FV/4-05-2026 |
-YYYY-MM |
NUMER-YYYY-MM | FV/4-2026-05 |
YYYY/MM/ |
YYYY/MM/NUMER | FV/2026/05/4 |
MM/YYYY/ |
MM/YYYY/NUMER | FV/05/2026/4 |
YYYY-MM- |
YYYY-MM-NUMER | FV/2026-05-4 |
MM-YYYY- |
MM-YYYY-NUMER | FV/05-2026-4 |
Roczne (tylko YYYY):
| Format | Układ | Przykład (symbol FV/, nr 4, 2026) |
|---|---|---|
/YYYY |
NUMER/YYYY | FV/4/2026 |
-YYYY |
NUMER-YYYY | FV/4-2026 |
YYYY/ |
YYYY/NUMER | FV/2026/4 |
YYYY- |
YYYY-NUMER | FV/2026-4 |
Domyślny format to /MM/YYYY. numberingFormat jest walidowany względem powyższej listy, więc nieznany wzór jest odrzucany z 400 VALIDATION_ERROR. Gdy podajesz documentNumber ręcznie, musi on odpowiadać układowi wybranego formatu (inaczej 422 NUMBER_MISMATCH).
Numeracja automatyczna (domyślnie)
Gdy nie podasz documentNo, numer nadaje backend:
symbol,numberingFormati tryb numeracji są brane z ustawień Twojej firmy (każde z nich możesz nadpisać w żądaniu).- Tryb numeracji to miesięczny (domyślny) albo roczny (ustawienie
numberingType). Decyduje, kiedy licznik się zeruje: miesięczny prowadzi osobny licznik dla każdego miesiąca (okresYYYY-MM), roczny dla każdego roku (okresYYYY). - Licznik jest atomowy i osobny dla każdej kombinacji
firma + typ dokumentu + okres. Pierwszy dokument w okresie dostaje1, kolejny2, i tak dalej. - Korekty współdzielą jeden licznik:
FakturaKorygujaca,FakturaKorygujacaZaliczkowaiFakturaKorygujacaRozliczeniowanumerują się wspólnie.
Trzymaj format zgodny z trybem. Przy numeracji miesięcznej format powinien zawierać
MM(np./MM/YYYY). Unikalność numeru jest sprawdzana w obrębie okresu wynikającego z formatu, więc użycie/YYYYprzy trybie miesięcznym zlewa miesiące w jeden okres.
Numeracja ręczna
Aby narzucić własny numer, podaj documentNo (≥ 1). Backend złoży pełny numer z symbolu, numeru i części datowej zgodnie z układem wybranego formatu i sprawdzi, czy numer jest wolny: w swojej serii (typ dokumentu, format numeracji, miesiąc albo rok daty wystawienia) i na całym koncie, bo ten sam pełny numer nie może mieć dwóch dokumentów, także różnych typów albo z różnych miesięcy.
Opcjonalnie dołóż documentNumber (pełny string) jako asekurację: musi dokładnie odpowiadać temu, co backend złoży z symbol/documentNo/numberingFormat, inaczej dostaniesz 422 NUMBER_MISMATCH. To zabezpieczenie przed rozjechaniem się Twojego numeru z naszym.
{
"documentType": "FakturaVAT",
"issueDate": "2026-05-07",
"symbol": "FV/",
"documentNo": 4,
"numberingFormat": "/MM/YYYY",
"documentNumber": "FV/4/05/2026"
}
Numeracja ręczna nie rusza licznika automatycznego. Jeśli mieszasz oba tryby, pilnuj ciągłości numerów po swojej stronie, żeby nie powstały luki ani kolizje.
Pola numeracji
| Pole | Typ | Wymagane | Uwagi |
|---|---|---|---|
symbol |
string | nie | Prefiks. Domyślnie z ustawień dla danego typu dokumentu. |
documentNo |
number | nie | Numer kolejny ≥ 1. Pominięty → numeracja automatyczna. |
numberingFormat |
string | nie | Wzór części datowej (tokeny MM, YYYY). Domyślnie z ustawień. |
documentNumber |
string | nie | Pełny numer do walidacji 1:1 z documentNo. |
Błędy numeracji
| Status | Kod | Przyczyna |
|---|---|---|
422 |
NUMBER_MISMATCH |
documentNumber nie pasuje do numeru, jaki API składa dla układu wybranego formatu (PREFIX {symbol}{documentNo}{date} albo SUFFIX {symbol}{date}{documentNo}). |
409 |
DOCUMENT_NUMBER_EXISTS |
Numer jest zajęty w swojej serii albo inny dokument na koncie ma już ten sam pełny numer. |
Anatomia żądania
Nagłówek dokumentu
| Pole | Typ | Wymagane | Uwagi |
|---|---|---|---|
documentType |
string | tak | Jeden z dziesięciu typów (patrz Typy dokumentów). Niezmienny przy edycji. |
issueDate |
string | tak | Data wystawienia, format ISO 8601 YYYY-MM-DD. |
issuePlace |
string | nie | Miejsce wystawienia. Domyślnie z ustawień firmy. |
currency |
string | nie | Kod waluty, domyślnie PLN. Lista w słowniku currencies. |
exchangeRate |
number | nie | Kurs dla walut innych niż PLN: kurs średni NBP z ostatniego dnia roboczego przed powstaniem obowiązku podatkowego (zwykle przed datą sprzedaży), a gdy faktura jest wcześniejsza, przed datą wystawienia (art. 31a ustawy o VAT). |
exchangeRateDate |
string | nie | Dzień tabeli NBP, z której jest exchangeRate (YYYY-MM-DD). |
exchangeRateTable |
string | nie | Numer tabeli NBP, np. 190/A/NBP/2026. Drukowany na PDF obok kursu. |
comments |
string | nie | Uwagi widoczne na dokumencie. W KSeF trafiają do stopki faktury: Stopka/Informacje/StopkaFaktury. |
invoiceName |
string | nie | Nazwa wyświetlana dokumentu. Domyślnie generowana z typu i numeru. |
Strony transakcji: sprzedawca, nabywca, odbiorca
To najczęstsze źródło pytań od integratorów, więc opiszemy je dokładnie.
Sprzedawca (seller) nigdy nie jest przesyłany w żądaniu. Zawsze pochodzi z profilu firmy uwierzytelnionego użytkownika (user.company): nazwa, NIP, VAT UE, REGON, KRS, BDO, adres oraz dane osobowe dla jednoosobowej działalności. Aby zmienić dane sprzedawcy, zaktualizuj profil firmy, nie żądanie. NIP nabywcy nie może być równy NIP sprzedawcy (422).
Nabywca (buyer) jest wymagany i przekazujesz go na jeden z dwóch sposobów:
buyerId(string): identyfikator istniejącego kontrahenta. Pobierz go zGET /external/v1/clients.buyer(obiekt): osadzone dane. Backend wyszukuje zapisanego kontrahenta (po NIP, numerze VAT UE albo nazwie), a gdy go nie znajdzie, tworzy nowego kontrahenta z przekazanych pól. Dzięki temu nie musisz osobno zakładać kontrahenta przed wystawieniem faktury. Identyfikatory podlegają zasadom identyfikatorów kontrahenta: kontrahenta z innego kraju UE podaj jako"identifierType": "vatEu", "vatUE": "DE123456789", nie wnip. Ustalony typ i przeniesione numery zwracadata.warnings, a sprzeczne dane422 PARTY_IDENTIFIER_INVALID.
Podaj buyerId albo buyer, nie oba naraz.
Odbiorca (receiver) jest opcjonalny i wskazuje odrębny podmiot, który faktycznie odbiera fakturę, inny niż nabywca. Typowy przypadek to struktury jednostek samorządu terytorialnego: nabywcą jest jednostka nadrzędna posiadająca NIP (np. Gmina), a odbiorcą jednostka podległa, która realnie otrzymuje fakturę (np. oddział urzędu albo Przedszkole Publiczne). Przekazujesz go tak samo jak nabywcę: receiverId lub osadzony obiekt receiver. Gdy pominiesz odbiorcę, faktura nie ma osobnego odbiorcy i odbiorcą pozostaje nabywca.
Odbiorca może być dodatkowo identyfikowany przez identyfikator wewnętrzny: ustaw identifierType na idWew i podaj idWew (format NIP-NNNNN, np. 5252248481-16006), co przydaje się m.in. dla jednostek samorządu terytorialnego (JST). Sprzedawca takiej opcji nie ma, w KSeF jest zawsze identyfikowany numerem NIP.
Obiekt kontrahenta (buyer / receiver)
Ten sam zestaw pól obowiązuje dla buyer i receiver. Wszystkie pola poza wskazaniem typu są opcjonalne, ale dla poprawnej faktury podaj dane identyfikujące i adresowe.
| Pole | Typ | Uwagi |
|---|---|---|
id |
string | Identyfikator istniejącego kontrahenta (alternatywa dla buyerId/receiverId). |
type |
string | company (domyślnie) lub person. Określa, czy używasz name, czy firstName + lastName. |
name |
string | Nazwa firmy (dla company). |
firstName, lastName |
string | Imię i nazwisko (dla person). |
nip |
string | Polski NIP (10 cyfr; spacje, myślniki i prefiks PL są usuwane, sprawdzana jest cyfra kontrolna). Używany do wyszukania lub utworzenia kontrahenta. Zagraniczny numer VAT podaj w vatUE. |
vatUE |
string | Numer VAT UE z prefiksem kraju, np. DE123456789; zapisywany wielkimi literami, bez spacji. |
pesel |
string | PESEL osoby fizycznej. |
regon, krs, bdo |
string | Pozostałe identyfikatory rejestrowe. |
street, postalCode, city |
string | Adres. |
country, countryCode |
string | Kod ISO 3166-1 alpha-2 (np. PL, DE), domyślnie PL. Lista w słowniku countries. Do KSeF i PDF liczy się sam countryCode; nazwa country jest opcjonalna (na PDF i tak dobierana z kodu). |
email, phoneNumber, www |
string | Dane kontaktowe. |
identifierType |
string | Który identyfikator wskazuje stronę w KSeF: nip (pole nip), vatEu (vatUE), other (countryCode i otherTaxIdentifier), idWew (idWew, tylko odbiorca), none (brak). Bez niego API ustala typ z danych, zob. Identyfikatory kontrahenta. |
otherTaxIdentifier |
string | Identyfikator podatkowy przy identifierType: "other", do 50 znaków, razem z countryCode (w KSeF KodKraju + NrID). |
idWew |
string | Identyfikator wewnętrzny (format NIP-NNNNN, np. 5252248481-16006), używany gdy identifierType: "idWew". Może go mieć tylko odbiorca: w schemacie FA(3) IDWew istnieje wyłącznie dla odbiorcy (Podmiot3), nie dla nabywcy ani sprzedawcy. |
fieldsToShow |
string[] | Które pola pokazać na dokumencie, np. ["nip","regon"]. |
Przykład nabywcy będącego osobą fizyczną:
{
"buyer": {
"type": "person",
"firstName": "Jan",
"lastName": "Kowalski",
"street": "Długa 5",
"postalCode": "80-001",
"city": "Gdańsk",
"countryCode": "PL"
}
}
Wskazówka. Jeśli wystawiasz wiele dokumentów temu samemu kontrahentowi, utwórz go raz przez API kontrahentów i potem przekazuj
buyerId. Szybciej i bez ryzyka rozjechania danych przy literówce w nazwie.
Pozycje (products)
Tablica pozycji. Wymagana dla faktur standardowych, rozliczeniowych i korekt. Dla faktury zaliczkowej pozycje są wyliczane z powiązanego zamówienia, więc jej nie przesyłasz (patrz niżej).
Dla każdej pozycji przesyłasz cenę i stawkę VAT, a backend dolicza wartości. Nie wyliczaj netValue, amountOfVat ani grossValue samodzielnie - serwer jest jedynym źródłem prawdy. Dokładne wzory i zasady zaokrąglania opisuje Typy dokumentów i kwoty.
| Pole | Typ | Wymagane | Uwagi |
|---|---|---|---|
name |
string | tak | Nazwa towaru lub usługi. |
unitOfMeasure |
string | tak | Jednostka, np. szt., godz., kg. Dowolny tekst, sugerowane wartości w słowniku. |
quantity |
number | tak | Ilość. Dla korekt dozwolone 0 (anulowanie pozycji). |
vatRate |
number | string | tak | Stawka VAT (patrz Stawki VAT). |
netPrice |
number | tak* | Cena jednostkowa netto. |
grossPrice |
number | tak* | Cena jednostkowa brutto (alternatywa dla netPrice). |
discount |
number | nie | Rabat procentowy 0-100. |
gtu |
string | nie | Kod GTU (GTU_01 do GTU_13). Walidowany względem słownika. |
pkwiu |
string | nie | Symbol PKWiU. |
marginPurchaseValue |
number | nie | Tylko vatRate: "marza": kwota nabycia całej pozycji brutto w PLN. Patrz Procedura marży. |
marginTaxRate |
number | nie | Tylko vatRate: "marza": stawka VAT od marży, 23, 8, 5 albo 0. Domyślnie 23. |
* Podaj netPrice lub grossPrice. Jeśli przekazujesz grossPrice, obliczenia idą „w dół” od brutto.
Stawki VAT
vatRate przyjmuje liczbową stawkę procentową albo kod specjalny (string). Pełną listę z opisami zwraca słownik vatRates.
- Stawki procentowe:
23,22,8,7,5,4,3. - Kody specjalne (VAT = 0, netto = brutto):
zw(zwolnione),np Iinp II(nie podlega),oo(odwrotne obciążenie),0 KR(krajowa 0%),0 WDT(wewnątrzwspólnotowa dostawa),0 EX(eksport),marza(procedura marży, opis niżej).
Nie istnieje samo 0 ani samo np. Nieznana stawka zwraca 400 VALIDATION_ERROR.
Procedura marży
Faktura w procedurze marży (art. 119 ustawy o VAT dla biur podróży, art. 120 dla towarów używanych, dzieł sztuki, przedmiotów kolekcjonerskich i antyków) pokazuje nabywcy tylko kwoty brutto. Pozycję w marży oznaczasz stawką vatRate: "marza":
- podaj
grossPrice(cena brutto);netValue=grossValue,amountOfVat=0, - dokument musi mieć
annotations.marginProcedurezenabled: trueiprocedureId(1biura podróży,2towary używane,3dzieła sztuki,4przedmioty kolekcjonerskie i antyki); bez tego422 MARGIN_LINE_WITHOUT_PROCEDURE, - w KSeF pozycja nie ma stawki (
P_12), a jej suma trafia doP_13_11, - na jednej fakturze możesz łączyć pozycje w marży ze zwykłymi stawkami (np. biuro podróży: usługi nabyte dla turysty w marży i własna usługa 23%).
Podatek należy się od marży, czyli różnicy między ceną a kwotą nabycia. Fakturnia liczy go do rejestru VAT i JPK_V7 (pola K_19 i K_20 dla 23%, MR_T lub MR_UZ), dlatego przy pozycji możesz podać:
marginPurchaseValue: kwota nabycia całej pozycji brutto w PLN (towar używany: cena zakupu; biuro podróży: koszty usług kupionych bezpośrednio dla turysty). Nie trafia na fakturę ani do KSeF. Bez niej raporty nie liczą VAT od marży tej pozycji i pokazują ostrzeżenie, a MCP ostrzegaMARGIN_PURCHASE_MISSING. Kwotę możesz dopisać później przezPUT /documents/{id},marginTaxRate: stawka VAT od marży,23(domyślnie),8,5albo0.
Kwotę nabycia możesz podać także po wystawieniu, również gdy faktura jest już w KSeF: PATCH /documents/{id}/margin-purchases z { "lines": [{ "index": 0, "marginPurchaseValue": 1500, "marginTaxRate": 23 }] }, gdzie index to pozycja w products (od 0). Zmieniają się tylko te dwa pola, treść faktury zostaje. Pominięty klucz zostawia zapisaną wartość, null ją czyści. Pozycja spoza procedury marży daje 422 MARGIN_LINE_NOT_FOUND, zła wartość 422 MARGIN_PURCHASES_INVALID. To samo robi narzędzie MCP updateDocumentMarginPurchases.
Gdy w PUT albo PATCH /documents/{id} wysyłasz pozycję w marży bez marginPurchaseValue i marginTaxRate, zostają wartości zapisane przy pozycji o tej samej nazwie.
VAT od marży = marża × stawka / (100 + stawka). Ujemna marża daje podatek 0 i nie zmniejsza marży z innych pozycji. W korekcie pominięte pola marży są przejmowane z korygowanej pozycji o tej samej nazwie, a kwota nabycia proporcjonalnie do ilości (zwrot jednej z trzech sztuk zmniejsza ją o jedną trzecią).
Pozycja w marży nie ma netto, więc w netSum dokumentu liczy się kwotą brutto (KSeF wykazuje ją osobno w P_13_11). Podgląd POST /documents/validate podaje tę sprzedaż w preview.totals.marginSaleSum, a netto zwykłych stawek to netSum minus marginSaleSum. Bez pozycji w marży marginSaleSum wynosi 0.
{
"documentType": "FakturaVAT",
"annotations": { "marginProcedure": { "enabled": true, "procedureId": 3 } },
"products": [
{ "name": "Obraz olejny", "unitOfMeasure": "szt.", "quantity": 1, "grossPrice": 5000, "vatRate": "marza", "marginPurchaseValue": 3000, "marginTaxRate": 23 }
]
}
Płatność
| Pole | Typ | Uwagi |
|---|---|---|
paymentMethodId |
number | 1 Gotówka · 2 Karta · 3 Bon · 4 Czek · 5 Kredyt · 6 Przelew · 7 Płatność mobilna |
paymentStatusId |
number | 0 Zapłacono · 1 Do zapłaty |
paymentDeadlineDate |
string | Termin płatności. Wymagany, gdy paymentStatusId = 1. |
additionalPaymentDate |
string | Data zapłaty. Wymagana, gdy paymentStatusId = 0. |
additionalPaymentDateHeaderId |
number | Nagłówek nad datą zapłaty (słownik paymentDateHeaders). |
paymentDescription |
string | Dodatkowy opis płatności. |
showPaymentSection |
boolean | Czy pokazać sekcję płatności na PDF. Domyślnie true. |
Zapłaty częściowe (partialPayments)
Gdy do dnia wystawienia nabywca zapłacił część kwoty (albo całość w kilku wpłatach), przekaż listę wpłat. Trafiają one do KSeF jako ZaplataCzesciowa i na PDF jako informacja o wpłatach.
| Pole | Typ | Znaczenie |
|---|---|---|
partialPayments[].amount |
number | kwota wpłaty brutto |
partialPayments[].date |
string | data wpłaty (YYYY-MM-DD), nie późniejsza niż issueDate |
partialPayments[].paymentMethodId |
number | forma wpłaty jak paymentMethodId (opcjonalna) |
Reguły zależą od paymentStatusId:
1(Do zapłaty): suma wpłat musi być mniejsza niż kwota do zapłaty; reszta ma termin zpaymentDeadlineDate.0(Zapłacono): co najmniej dwie wpłaty o sumie równej kwocie do zapłaty;additionalPaymentDatenie jest wtedy wymagane.
Niezgodne wpłaty odpowiadają 422 PARTIAL_PAYMENTS_INVALID. Gdy faktura ma zapłaty częściowe, data zapłaty całości (additionalPaymentDate) nie trafia do KSeF: daty są przy wpłatach. W odpowiedzi payment zawiera partialPayments (pusta lista, gdy ich nie ma), paidAmount (zapłacono według treści faktury) i amountDue (pozostało do zapłaty), więc integracja nie musi tego liczyć.
{
"paymentMethodId": 6,
"paymentStatusId": 1,
"paymentDeadlineDate": "2026-10-10",
"partialPayments": [
{ "amount": 500, "date": "2026-09-20", "paymentMethodId": 6 }
]
}
Raty (paymentInstallments)
Faktura płatna w kilku terminach podaje raty: każda z datą i kwotą. W KSeF każda rata to osobny TerminPlatnosci, a kwoty rat trafiają do DodatkowyOpis (FA(3) nie ma pola na kwotę raty). Na PDF raty zastępują pojedynczy termin.
| Pole | Typ | Znaczenie |
|---|---|---|
paymentInstallments[].date |
string | termin raty (YYYY-MM-DD) |
paymentInstallments[].amount |
number | kwota raty brutto, w walucie dokumentu |
Reguły: tylko przy paymentStatusId = 1, co najmniej dwie raty, najwyżej 100, terminy po kolei i nie wcześniej niż issueDate, suma równa kwocie do zapłaty. Bez paymentDeadlineDate terminem płatności dokumentu jest pierwsza rata. Niezgodne raty odpowiadają 422 PAYMENT_INSTALLMENTS_INVALID. Pusta lista ([]) w PUT albo PATCH usuwa raty. Raty równe policzy za Ciebie API: patrz Równe raty.
{
"paymentStatusId": 1,
"paymentMethodId": 6,
"paymentInstallments": [
{ "date": "2026-10-20", "amount": 615 },
{ "date": "2026-11-20", "amount": 615 }
]
}
Równe raty (paymentInstallmentsSplit)
Gdy raty mają być równe, nie licz kwot u siebie: podaj liczbę rat, a API podzieli kwotę do zapłaty po wyliczeniu sum dokumentu. Dokument dostaje zwykłe paymentInstallments (są w odpowiedzi, w podglądzie validate i w KSeF), a samego podziału API nie zapisuje.
| Pole | Typ | Znaczenie |
|---|---|---|
paymentInstallmentsSplit.count |
number | liczba rat, od 2 do 100 (wymagana) |
paymentInstallmentsSplit.firstDate |
string | termin pierwszej raty (YYYY-MM-DD), nie wcześniej niż issueDate; domyślnie paymentDeadlineDate z żądania, a bez niego issueDate + 14 dni |
paymentInstallmentsSplit.every |
string | month (co miesiąc, domyślnie), week (co 7 dni) albo 14days (co 14 dni) |
Jak API liczy raty:
- Kwota do zapłaty (brutto, w walucie dokumentu; na fakturze rozliczeniowej kwota po rozliczeniu zaliczek) jest zamieniana na grosze raz, z zaokrągleniem połówki od zera.
- Każda rata dostaje tyle samo groszy, a reszta z dzielenia trafia do ostatniej: 100,00 na 3 raty to 33,33 + 33,33 + 33,34. Suma rat zawsze równa się kwocie do zapłaty.
- Raty miesięczne liczone są od pierwszej daty: 31 stycznia, 28 lutego, 31 marca.
- Terminem płatności dokumentu (
paymentDeadlineDate) staje się data pierwszej raty.
Reguły: tylko przy paymentStatusId = 1. Przy tworzeniu bez paymentStatusId dokument dostaje status Do zapłaty; status 0 (Zapłacono) odpowiada błędem. Nie łącz z paymentInstallments: raty o różnych kwotach (np. większa pierwsza rata) podaj w paymentInstallments. PATCH bez products dzieli kwotę zapisaną w dokumencie, a bez firstDate zaczyna od zapisanego terminu płatności. Kwota mniejsza niż grosz na ratę, zła liczba rat, data albo odstęp odpowiadają 422 PAYMENT_INSTALLMENTS_INVALID, z opisem w errors[0], np. „Kwota jest za mała na tyle rat.”. null oznacza brak podziału.
Żądanie (fragment POST /external/v1/documents, suma brutto 1 230,00 zł):
{
"documentType": "FakturaVAT",
"issueDate": "2026-10-07",
"paymentMethodId": 6,
"paymentStatusId": 1,
"paymentInstallmentsSplit": { "count": 3, "firstDate": "2026-10-31", "every": "month" }
}
Odpowiedź (fragment data.document):
{
"paymentDeadlineDate": "2026-10-31",
"paymentInstallments": [
{ "date": "2026-10-31", "amount": 410 },
{ "date": "2026-11-30", "amount": 410 },
{ "date": "2026-12-31", "amount": 410 }
]
}
POST /external/v1/documents/validate zwraca te same raty w preview.paymentInstallments, więc możesz je pokazać przed wystawieniem.
Termin opisowy (paymentDeadlineTerm)
Termin płatności można też opisać słowami, np. „14 dni od dnia wystawienia faktury”. W KSeF trafia do TerminOpis, na PDF obok daty terminu.
| Pole | Typ | Znaczenie |
|---|---|---|
paymentDeadlineTerm.count |
number | liczba jednostek, liczba całkowita od 1 |
paymentDeadlineTerm.unit |
string | day, week albo month |
paymentDeadlineTerm.event |
string | od czego liczy się termin: issue (wystawienie faktury), invoice_delivery (doręczenie faktury), supply (dostawa towaru lub wykonanie usługi), acceptance (odbiór) |
paymentDeadlineDate nadal podaje datę terminu; przy event: issue powinna wynikać z opisu. null w PUT albo PATCH usuwa opis.
Kilka wpłat na fakturze zaliczkowej (advanceReceipts)
Faktura zaliczkowa może dokumentować kilka wpłat otrzymanych przed jej wystawieniem (w KSeF ZaliczkaCzesciowa, przykład 16 z broszury MF). Każda wpłata to osobny moment powstania obowiązku podatkowego (art. 19a ust. 8 ustawy o VAT), więc raport VAT i JPK ujmują je osobno.
| Pole | Typ | Znaczenie |
|---|---|---|
advanceReceipts[].date |
string | dzień otrzymania wpłaty, nie późniejszy niż issueDate |
advanceReceipts[].amount |
number | kwota wpłaty brutto |
advanceReceipts[].exchangeRate |
number | w walucie obcej: kurs NBP z dnia roboczego przed otrzymaniem wpłaty (wymagany) |
advanceReceipts[].exchangeRateDate |
string | dzień tabeli NBP tego kursu (opcjonalny) |
Reguły: tylko FakturaZaliczka z paymentStatusId = 0, od dwóch do 31 wpłat, suma równa advancePaymentAmount. Data otrzymania zapłaty dokumentu to data ostatniej wpłaty. Niezgodne wpłaty odpowiadają 422 ADVANCE_RECEIPTS_INVALID.
Rachunki bankowe
Dla płatności przelewem (paymentMethodId = 6) podaj bankAccounts, chyba że masz domyślny rachunek w ustawieniach firmy. Pierwszy rachunek staje się głównym, pozostałe są wyświetlane jako dodatkowe.
| Pole | Typ | Wymagane | Uwagi |
|---|---|---|---|
accountNumber |
string | tak | Numer rachunku / IBAN. |
bank |
string | nie | Nazwa banku. |
swift |
string | nie | Kod SWIFT dla przelewów zagranicznych. |
accountDescription |
string | nie | Opis rachunku na dokumencie. |
bankOwnAccountType |
number | nie | 1, 2 lub 3. |
Dla mechanizmu podzielonej płatności możesz dodatkowo podać osobne konto VAT: vatAccountNumber, vatAccountBank, vatAccountSwift, vatAccountDescription. Samą adnotację MPP włączasz przez annotations.isSplitPayment (patrz niżej).
Daty sprzedaży
| Pole | Typ | Uwagi |
|---|---|---|
subDate |
string | Data sprzedaży / dostawy / wykonania usługi (KSeF P_6), z subDateTo początek okresu. Dla typów KSeF i zamówień domyślnie równa issueDate. Korekta bez subDate przejmuje datę albo okres sprzedaży faktury korygowanej. |
subDateTo |
string | Koniec okresu sprzedaży, np. usługa za cały miesiąc (KSeF OkresFa). null w PUT albo PATCH wraca do jednej daty. |
subDateHeaderId |
number | Nagłówek nad datą sprzedaży (słownik additionalDateHeaders). |
taxPointRule |
string | Obowiązek podatkowy z chwilą wystawienia faktury (art. 19a ust. 5 ustawy o VAT): art19a5p3a usługi budowlane, art19a5p3b dostawa książek i prasy, art19a5p3c druk książek i prasy, art19a5p4 media, telekomunikacja, najem, dzierżawa, leasing, ochrona, stała obsługa prawna i biurowa. Nie trafia do KSeF; decyduje o okresie w raportach VAT i o dniu kursu waluty. null oznacza zwykłe zasady. |
Daty dokumentu nie mogą sobie przeczyć. API odrzuca z 422 DOCUMENT_DATES_INVALID (komunikaty w errors):
- okres sprzedaży, którego początek jest późniejszy niż koniec;
- datę sprzedaży późniejszą o więcej niż 60 dni od daty wystawienia (art. 106i ust. 7; okresu rozliczeniowego to nie dotyczy);
- datę zapłaty (
additionalPaymentDate) późniejszą niż data wystawienia przypaymentStatusId = 0. Zapłatę, która wpłynęła po wystawieniu, oznacz przezPATCH /documents/{id}/payment-statuszpaymentDate; - korektę z datą wystawienia wcześniejszą niż faktura korygowana.
Termin płatności wcześniejszy niż data wystawienia jest dozwolony (spóźniona faktura może mieć termin, który już minął).
Adnotacje (annotations)
Adnotacje to nowoczesny sposób oznaczania szczególnych trybów rozliczenia VAT zgodny z KSeF FA(3). Zastępują dawne, osobne typy faktur (jak FakturaMPP czy FakturaVATMarza), których API celowo nie udostępnia.
| Pole | Typ | Znaczenie |
|---|---|---|
isSplitPayment |
boolean | Mechanizm podzielonej płatności (MPP). Wyklucza się z isCashMethod. |
isReverseCharge |
boolean | Odwrotne obciążenie. |
isCashMethod |
boolean | Metoda kasowa. Wyklucza się z isSplitPayment. |
isSelfInvoicing |
boolean | Samofakturowanie. |
marginProcedure |
obiekt | Procedura marży. |
vatExemption |
obiekt | Zwolnienie z VAT. |
Procedura marży (annotations.marginProcedure):
| Pole | Typ | Uwagi |
|---|---|---|
enabled |
boolean | Włącza procedurę. |
procedureId |
number | Wymagane, gdy enabled = true. Wartości w słowniku marginProcedures (np. biuro podróży, towary używane, dzieła sztuki, antyki). |
Zwolnienie z VAT (annotations.vatExemption):
| Pole | Typ | Uwagi |
|---|---|---|
enabled |
boolean | Włącza zwolnienie. |
exemptionType |
string | A (przepis ustawy), B (dyrektywa 2006/112/WE) lub C (inna podstawa prawna). |
legalBasis |
string | Podstawa prawna jako tekst, np. art. 43 ust. 1 pkt 19 ustawy o VAT. |
showOnDocument |
boolean | Czy pokazać adnotację na PDF. Nie wpływa na dokument wysyłany do KSeF (zwolnienie i tak trafia do P_19/P_19A/P_19B/P_19C) — steruje wyłącznie wyświetlaniem na PDF. |
{
"annotations": {
"isSplitPayment": true,
"vatExemption": {
"enabled": true,
"exemptionType": "A",
"legalBasis": "art. 113 ust. 1 ustawy o VAT"
}
}
}
Informacje dodatkowe (additionalInfo)
Sekcja KSeF FA(3) „Informacje dodatkowe” — osobna od adnotacji. Na razie udostępnia jedno pole:
| Pole | Typ | Znaczenie |
|---|---|---|
isInvoiceToReceipt |
boolean | Faktura do paragonu (art. 109 ust. 3d ustawy o VAT). |
Gdy ustawisz isInvoiceToReceipt: true, w dokumencie wysyłanym do KSeF zostaje ustawiona flaga <FP>1</FP>, a na PDF pojawia się adnotacja „Faktura do paragonu, o której mowa w art. 109 ust. 3d ustawy”. Typ dokumentu (RodzajFaktury) nie zmienia się — to zwykła faktura VAT z dodatkowym oznaczeniem, a nie osobny typ dokumentu. KSeF nie przewiduje pola na numer paragonu, więc API również go nie przyjmuje.
{
"additionalInfo": {
"isInvoiceToReceipt": true
}
}
Podpisy (signatures)
Opcjonalne bloki podpisu na PDF.
{
"signatures": {
"issuerSignature": { "show": true, "value": "Jan Nowak" },
"receiverSignature": { "show": false }
}
}
Pozostałe pola
ksefJst(boolean): dokument dla jednostki samorządu terytorialnego na potrzeby KSeF.
Wartości domyślne z ustawień konta
Dokument wystawiony przez API wygląda tak samo jak nowy dokument z edytora w aplikacji: pola, których nie wyślesz, dostają wartości z ustawień konta. Pole wysłane jawnie zawsze wygrywa (także pusty tekst "" albo pusty obiekt {}), a useAccountDefaults: false wyłącza wszystkie wartości domyślne konta.
| Pole | Wartość domyślna |
|---|---|
comments |
Uwagi z ustawień dokumentów. |
issuePlace |
Miejsce wystawienia z ustawień, a gdy puste: miasto z adresu firmy. |
signatures |
Pola podpisów według ustawienia podpisów; wystawia imię i nazwisko właściciela albo pracownika, który wystawia dokument. |
annotations |
Automatyczne adnotacje (jeśli włączone): mechanizm podzielonej płatności z rachunkiem VAT (vatAccountNumber z rachunku oznaczonego jako VAT) i procedura marży. Korekty ich nie dostają. |
annotations.vatExemption |
Pozycje ze stawką zw dostają podstawę zwolnienia z ustawień (typ A/B/C i podstawa prawna). |
paymentMethodId |
6 (przelew) z domyślnym rachunkiem. |
paymentStatusId |
Domyślny status z ustawień: zapłacono (data zapłaty = data wystawienia) albo do zapłaty (termin = data wystawienia). |
products[].vatRate |
Domyślna stawka VAT konta. |
taxPointRule |
Domyślna reguła z ustawienia „Obowiązek podatkowy z datą wystawienia faktury” (Ustawienia → Autouzupełnianie), gdy jest włączone i ma wybraną regułę. Dotyczy faktur sprzedaży w KSeF poza korektami i fakturą zaliczkową; jawne taxPointRule: null jej nie dostaje. |
| sprzedawca | Widoczne pola sprzedawcy z ustawień (fieldsToShow). |
| PKWiU na wydruku | Według ustawienia wyświetlania PKWiU. |
Odpowiedź POST /documents/validate wymienia każdą zastosowaną wartość w preview.appliedDefaults (kod, pole, zdanie do pokazania użytkownikowi), a preview.resolved pokazuje płatność, miejsce, uwagi i adnotacje po ich uwzględnieniu.
Korekty przejmują z korygowanej faktury nabywcę, odbiorcę, walutę z kursem i adnotacje (z rachunkiem VAT), jeśli ich nie wyślesz, więc buyerId jest dla nich opcjonalne. Faktury, do której istnieje korekta, nie zmienisz ani nie usuniesz (409 DOCUMENT_CORRECTED): zmianę wprowadza kolejna korekta. Po usunięciu ostatniej korekty faktura znowu jest edytowalna.
FakturaProformaBezVAT ma zawsze VAT 0 na pozycjach, jak w aplikacji, niezależnie od wysłanej stawki. FakturaZaliczka nie ma daty sprzedaży.
Typy dokumentów i ich pola
Endpoint przyjmuje dziesięć typów. Poniżej tylko pola dodatkowe wymagane dla danego typu; reguły obliczeń sum dla zaliczek, rozliczeń i korekt opisuje Typy dokumentów i kwoty.
| Typ | Czym jest | Wymagane dodatkowo |
|---|---|---|
Faktura |
Faktura poza KSeF (podmioty zwolnione, do końca 2026) | - |
FakturaVAT |
Faktura VAT (odpowiednik faktury podstawowej KSeF) | - |
FakturaProforma |
Proforma | - |
FakturaProformaBezVAT |
Proforma bez VAT | - |
FakturaZaliczka |
Faktura zaliczkowa | linkedOrderId, advancePaymentAmount (bez products) |
FakturaRozliczeniowa |
Faktura rozliczeniowa (końcowa) | linkedAdvanceInvoiceIds, products |
FakturaKorygujaca |
Faktura korygująca | correctedInvoiceId, products (pełny stan „po”); nabywca z korygowanej faktury |
FakturaKorygujacaZaliczkowa |
Korekta zaliczki | correctedInvoiceId, correctionAdvanceAmountAfter |
FakturaKorygujacaRozliczeniowa |
Korekta rozliczenia | correctedInvoiceId, correctionSettlementAmountAfter |
Zamowienie |
Zamówienie | - |
Pola powiązane z typami specjalnymi:
| Pole | Typ | Dla |
|---|---|---|
linkedOrderId |
string | FakturaZaliczka: powiązane Zamowienie. |
advancePaymentAmount |
number | FakturaZaliczka: kwota zaliczki (≥ 0.01, ≤ wartość zamówienia). |
linkedAdvanceInvoiceIds |
string[] | FakturaRozliczeniowa: rozliczane faktury zaliczkowe. |
settlementAmount |
number | FakturaRozliczeniowa: kwota do rozliczenia (auto, chyba że isSettlementAmountManual). |
isSettlementAmountManual |
boolean | FakturaRozliczeniowa: wymusza ręczną settlementAmount. |
correctedInvoiceId |
string | Korekty: korygowana faktura. |
correctionReason |
string | Korekty: przyczyna (tekst). |
correctionAdvanceAmountAfter |
number | FakturaKorygujacaZaliczkowa: nowa kwota zaliczki po korekcie. |
correctionSettlementAmountAfter |
number | FakturaKorygujacaRozliczeniowa: nowa kwota rozliczenia po korekcie. |
Sumy nie zawsze równają się
Σ products. Dla zaliczek, rozliczeń i korektgrossSumwynika z osobnej logiki. Jeśli odwzorowujesz kwoty u siebie, bierz je z odpowiedzi API, nie wyliczaj ponownie. Pełne wzory w Typy dokumentów i kwoty.
Walidacja na sucho
POST /external/v1/documents/validate uruchamia pełny proces (numeracja, ustalenie stron, sumy) i zwraca podgląd bez zapisywania dokumentu. Idealne, by pokazać użytkownikowi ekran potwierdzenia albo przetestować integrację bez zaśmiecania konta.
Idempotencja
Wyślij nagłówek Idempotency-Key (zalecany UUID v4). Powtórzenie tego samego żądania z tym samym kluczem zwróci zapisaną odpowiedź zamiast tworzyć duplikat (cache 24 h). To samo żądanie z innym ciałem i tym samym kluczem zwróci błąd. Zobacz Idempotentność.
Najczęstsze błędy
| Status | Kod | Przyczyna |
|---|---|---|
400 |
VALIDATION_ERROR |
Niepoprawny lub brakujący typ, data, stawka VAT, struktura pól. |
403 |
NO_PERMISSIONS |
Token bez scope documents:create. |
404 |
DOCUMENT_NOT_FOUND |
Nieznany buyerId, receiverId, correctedInvoiceId lub linkedOrderId. |
409 |
DOCUMENT_NUMBER_EXISTS |
Numer dokumentu już istnieje. |
422 |
DOCUMENT_DATES_INVALID |
Daty dokumentu przeczą sobie albo ustawie o VAT (patrz Daty sprzedaży). |
422 |
PAYMENT_INSTALLMENTS_INVALID |
Raty nie zgadzają się z kwotą, terminami albo statusem płatności, albo paymentInstallmentsSplit nie da się wykonać. |
422 |
ADVANCE_RECEIPTS_INVALID |
Wpłaty zaliczki nie zgadzają się z kwotą zaliczki, datami albo kursami. |
422 |
- | Reguły domenowe: zaliczka większa niż zamówienie, isSplitPayment razem z isCashMethod, brak wymaganej daty płatności, NIP nabywcy równy NIP sprzedawcy. |
Pełna lista kodów i koperta błędu są w Błędy.
Co dalej
- Typy dokumentów i kwoty: reguły obliczeń, zaokrąglanie, logika zaliczek, rozliczeń i korekt.
- Wysyłka do KSeF: wyślij gotowy dokument i poczekaj na wynik.
- Idempotentność: bezpieczne ponawianie zapisów.
- Limity zapytań i Paginacja: limity tworzenia i pobierania list.
- Błędy: kody i obsługa.
- Dokumentacja API: pełny schemat każdego pola.