Document types & amounts
This page is the one to read before you build invoicing. It explains which document types exist, what you send, and exactly how amounts are computed, down to the grosz. Getting the money math right is the whole game, so the rules are spelled out precisely.
The golden rule: you send prices, we compute values
For every line you send a unit price and a VAT rate. The backend computes the rest. You never compute amountOfVat or grossValue yourself, and you shouldn't, because the server is the single source of truth and your numbers would only drift.
| You send | Meaning |
|---|---|
quantity |
line quantity (required) |
netPrice or grossPrice |
unit price before/after VAT (one of them) |
vatRate |
VAT rate (number like 23, or a special code) |
discount |
optional, percent 0–100 |
The backend returns, per line:
| Field | How it is computed |
|---|---|
netValue |
round2(quantity × netPrice × (1 − discount/100)) |
amountOfVat |
round2(netValue × rate / 100) |
grossValue |
round2(netValue + amountOfVat) |
If you send grossPrice instead, the math runs "downwards": grossValue first, then netValue = round2(gross / (1 + rate/100)) and amountOfVat = round2(gross − netValue).
Rounding is half-up, to the grosz
All money is rounded to 2 decimal places using round half-up:
round2(x) = Math.round((x + Number.EPSILON) * 100) / 100
So 0.345 → 0.35 (not 0.34). The order of operations matters at the boundary: VAT is round2((net × rate) / 100), never net × (rate/100). The two differ by a grosz on some values. The same algorithm runs in our fakturnia-shared library, so a document created through the API is identical, to the grosz, to one created in the web app.
Per line,
netValue + amountOfVatalways equalsgrossValue. Summed over a document,netSum + vatSum === grossSum. If you ever see a one-grosz mismatch, it is a bug. Report it.
VAT rates
vatRate accepts a numeric percentage from the dictionary or a special code. Numeric rates: 23, 22, 8, 7, 5, 4, 3. Special codes (VAT = 0, net = gross):
| Code | Meaning |
|---|---|
zw |
exempt (zwolnione) |
np I, np II |
not subject (nie podlega) |
oo |
reverse charge (odwrotne obciążenie) |
0 WDT |
intra-community supply |
0 EX |
export |
0 KR |
domestic 0% |
There is no bare 0 and no bare np; use the codes above. An unknown rate returns 400 VALIDATION_ERROR.
Document types
Ten document types are available through the API. Five legacy types are intentionally not exposed (use annotations instead, see below).
| Type | What it is | Required extra fields |
|---|---|---|
Faktura |
Non‑KSeF invoice, for VAT‑exempt entities (available until end of 2026) | - |
FakturaVAT |
VAT invoice, the equivalent of a basic invoice in KSeF | - |
FakturaProforma |
Proforma | - |
FakturaProformaBezVAT |
Proforma without VAT | - |
FakturaZaliczka |
Advance invoice | linkedOrderId, advancePaymentAmount |
FakturaRozliczeniowa |
Settlement (final) invoice | linkedAdvanceInvoiceIds |
FakturaKorygujaca |
Correction | correctedInvoiceId |
FakturaKorygujacaZaliczkowa |
Correction of an advance | correctedInvoiceId, correctionAdvanceAmountAfter |
FakturaKorygujacaRozliczeniowa |
Correction of a settlement | correctedInvoiceId, correctionSettlementAmountAfter |
Zamowienie |
Order | - |
KSeF and the future of invoice types.
FakturaVATis the equivalent of a basic invoice in KSeF.Fakturais a document that does not go to KSeF, dedicated to VAT‑exempt entities — available until the end of 2026. From January 1, 2027 theFakturatype will no longer be available, andFakturaVATwill become theFakturaPodstawowatype; for a transitional period both types may be used (legacy mode). For now, useFakturaVATexclusively as the equivalent of a basic invoice.
Annotations
Special VAT settlement modes are not separate document types. Following KSeF FA(3), you set them in the annotations object on the document, regardless of its type. Available options:
| Annotation | Field | Meaning |
|---|---|---|
| Split payment (MPP) | isSplitPayment |
Marks the invoice as split payment. Mutually exclusive with the cash method. A dedicated VAT account can go in the payment fields (vatAccountNumber and friends). |
| Reverse charge | isReverseCharge |
VAT is settled by the buyer. |
| Cash method | isCashMethod |
VAT settled at the moment of payment. Mutually exclusive with split payment. |
| Self-invoicing | isSelfInvoicing |
The invoice is issued by the buyer on the seller's behalf. |
| Margin procedure | marginProcedure |
Object { enabled, procedureId }. Procedure ids (travel agency, second-hand goods, works of art, antiques) are listed in the marginProcedures dictionary. |
| VAT exemption | vatExemption |
Object { enabled, exemptionType, legalBasis, showOnDocument }. Types A/B/C are described in the vatExemptionTypes dictionary. |
Conflicting combinations (e.g. split payment together with the cash method) return 422 ANNOTATIONS_VALIDATION_ERROR (see Errors). The full field reference with request examples is in Creating documents → Annotations.
Additional information: invoice to a receipt
Next to annotations, KSeF FA(3) defines an "Additional information" section (additionalInfo). The API currently exposes one field there:
| Field | Meaning |
|---|---|
additionalInfo.isInvoiceToReceipt |
Invoice issued to a receipt (art. 109(3d) of the Polish VAT Act). |
Setting isInvoiceToReceipt: true on a FakturaVAT adds the <FP>1</FP> flag to the document sent to KSeF and an "invoice to a receipt" annotation on the PDF. It is still a regular VAT invoice, not a separate document type; amounts are computed the same way. KSeF has no field for the receipt number, so the API does not accept one either. Details in Creating documents → Additional information.
Standard invoices
Faktura, FakturaVAT, FakturaProforma, FakturaProformaBezVAT, Zamowienie are computed straight from their lines: grossSum = Σ grossValue, and so on. Nothing surprising.
Advance invoices (FakturaZaliczka)
An advance invoice books a prepayment against an order (Zamowienie). It is not the sum of the order's lines; its value is the advancePaymentAmount.
- You pass
linkedOrderId(an existingZamowienie) andadvancePaymentAmount. - The backend splits the advance proportionally across the order's VAT rates and that breakdown becomes the invoice's lines.
- Therefore
grossSum === advancePaymentAmount, withnetSum/vatSumderived from the proportional split.
{
"documentType": "FakturaZaliczka",
"issueDate": "2026-06-04",
"buyerId": "681a…",
"linkedOrderId": "6820…",
"advancePaymentAmount": 615
}
The advance may not exceed the order's gross value (422 ADVANCE_EXCEEDS_ORDER_VALUE).
Settlement invoices (FakturaRozliczeniowa)
A settlement (final) invoice closes out an order after one or more advances. Its gross is the amount still to pay, not the full line total.
- You pass
linkedAdvanceInvoiceIds(the advance invoices being settled) and the fullproducts. - The backend computes
settlementAmount = max(0, Σ products.gross − Σ advances.gross)(unless you setisSettlementAmountManual: trueand supplysettlementAmount). grossSum === settlementAmount;netSum/vatSumcome from splitting that amount proportionally over the product VAT rates.
So if your lines total 6 150 gross and the linked advances total 2 460, the settlement invoice's grossSum is 3 690, not 6 150.
Corrections
The full guide to issuing corrections via the API is in the Correcting documents article — the three correction types, zeroing out an invoice, and the proportional VAT rules.
A correction restates a document. products is the full "after" state, the whole document as it should look after correction. The backend computes the delta; you do not send "+/−" lines.
FakturaKorygujaca: totals are the line delta:Σ after − Σ corrected. To cancel a document, send the same lines withquantity: 0.FakturaKorygujacaZaliczkowacorrects an advance. Totals are the advance-amount delta:grossSum = correctionAdvanceAmountAfter − advancePaymentAmountBefore, with VAT from the difference of the two proportional breakdowns.FakturaKorygujacaRozliczeniowacorrects a settlement.grossSum = correctionSettlementAmountAfter − settlementAmountBefore, VAT proportional.
{
"documentType": "FakturaKorygujaca",
"issueDate": "2026-06-04",
"correctedInvoiceId": "681a…",
"correctionReason": "Zmiana ilości",
"products": [
{ "name": "Usługa", "unitOfMeasure": "szt.", "quantity": 2, "netPrice": 100, "vatRate": 23 }
]
}
Why this matters for integrators. Advance, settlement and correction
grossSumvalues are notΣ products. If you mirror invoice totals in your own system, take them from the API response, don't recompute from the lines.
Payment & dates
| Field | Notes |
|---|---|
paymentMethodId |
1 Cash · 2 Card · 3 Voucher · 4 Cheque · 5 Credit · 6 Transfer · 7 Mobile |
paymentStatusId |
0 Paid (requires additionalPaymentDate) · 1 To pay (requires paymentDeadlineDate) |
bankAccounts[] |
for transfer; first becomes the primary account, the rest are shown as additional |
issueDate, subDate, subDateTo |
issue date / sale date or period (ISO YYYY-MM-DD) |
paymentInstallments, paymentDeadlineTerm |
instalments and a due date in words for an unpaid invoice, see Creating documents |
exchangeRate, exchangeRateDate, exchangeRateTable |
the NBP rate of the business day before the tax point, its day and table |
Mark a payment that arrived after the issue with PATCH /documents/{id}/payment-status, receivedPayment: RECEIVED and an optional paymentDate (the day of the payment, YYYY-MM-DD; by default today in Poland, never in the future). For the cash method it is the tax point (art. 21 ust. 1 of the VAT Act). GET /documents/{id} returns it as payment.receivedPaymentDate.
Deleting and editing documents
Whether a document can still be changed or removed depends on its KSeF status. DELETE /documents/{id} and document updates follow the same rule:
- Once a document has a KSeF number (it was confirmed in KSeF), it is final. Delete or update returns
400 KSEF_CONFIRMED. - While a document is sent to KSeF and awaiting confirmation, it is locked. Delete or update returns
400 KSEF_SENT. - If the KSeF send failed (the document carries a KSeF error), the lock is lifted. You can delete the document or fix it and send again.
Documents that were never sent to KSeF can always be deleted or edited. Check a document's state with GET /documents/{id}/ksef-status before attempting a change.
Validate before you commit
POST /documents/validate runs the full pipeline (numbering, totals, party resolution) and returns a preview without persisting. Use it to show users a confirmation screen or to dry-run an integration.
See the API reference for the complete request schema and every field.