Sending transactions
Send each invoice or credit note as one document to POST /transactions, with all its lines inside it. Invoice fields go on the document once. Lines carry only what can differ within an invoice.
Batches are fine
Send as many documents in one call as fit, up to 500 lines or 5 MB. A whole month sent in a run of calls after month-end close is fine, and so is one document per call as invoices are posted. Both use the same endpoint and are processed the same way. Each document in a call is accepted or refused on its own, so one bad document never holds back the rest.
Document fields
| Field | Required | Notes |
|---|---|---|
client_ref |
yes | Your id for the client legal entity |
document_id |
yes | Your permanent id for the document. Never reused. A credit note has its own |
document_type |
yes | invoice or credit_note |
source_updated_at |
yes | When the document last changed in your ERP. Orders versions |
action |
yes | upsert or void |
invoice_number |
yes | As printed. On a credit note, its own number |
invoice_date |
yes | On a credit note, its own date |
currency |
yes | Invoice currency |
tax_jurisdiction |
yes | The country of the client's VAT registration the document falls under. For OSS and IOSS, the registration country, not the consumer's. See Tax codes |
counterparty_name |
yes | The other party: the customer on a sale, the supplier on a purchase |
counterparty_country |
yes | Where the customer or supplier is established |
lines |
yes | At least one |
counterparty_vat_number |
when the tax code needs it | With country prefix. Needed for B2B cross-border and reverse charge |
tax_point_date |
when it differs from the invoice date | The date of supply or payment. PL and CZ use it to set the period |
original_invoice_number, original_invoice_date |
on a credit note, whenever it credits one invoice | The invoice being credited. Links the two |
e_invoice_reference |
when e-invoiced | The clearance id, for example KSeF (PL) or SDI (IT) |
ship_from_country, ship_to_country |
goods that cross a border | Where the goods left from and arrived. For OSS goods, ship_to_country is the consumer's country |
Line fields
| Field | Required | Notes |
|---|---|---|
line_id |
yes | Your id for the line, for example the ERP entry id, or its sequence number. Unique within the document and kept the same across versions |
net |
yes | As printed. Usually negative on a credit note, and negative on a discount line |
vat |
yes | The VAT charged on the line, as printed. Zero when none |
vat_rate |
yes | Percent: 23, not 0.23 |
tax_code |
yes | A VAT IT tax code, or your own code where VAT IT keeps the mapping for that client |
goods_or_services |
yes | goods or services |
tax_jurisdiction |
rarely | Only when this line falls under a different registration than the document |
ship_from_country, ship_to_country |
rarely | Only when this line's goods moved between different countries than the document's, for example an invoice shipped from two warehouses |
- Gross is never sent. It is always net plus VAT.
- Amounts are signed as printed. Send each line with the sign it has in your ERP. A discount line on an invoice is negative, and a line on a credit note that reverses a discount is positive.
- Item lines or tax summary lines. Both are accepted. Line
vatmust add up to the invoice's VAT per rate. Where your ERP rounds at invoice level, put the rounding difference on one line. - One tax per line. Countries with more than one tax on a line come later, without breaking this shape.
How signs are checked
VAT IT checks the sign of the document, not of each line. It adds up the lines' net and their vat:
document_type |
Sum of net |
Sum of vat |
|---|---|---|
invoice |
zero or more | zero or more |
credit_note |
zero or less | zero or less |
If either sum has the wrong sign, the document is refused with total_sign_invalid. VAT IT works these totals out from the lines, so you never send them.
Example
{
"documents": [
{
"client_ref": "KD-10042",
"document_id": "INV-2026-000981",
"document_type": "invoice",
"source_updated_at": "2026-11-02T09:12:44Z",
"action": "upsert",
"invoice_number": "FV/2026/11/981",
"invoice_date": "2026-11-02",
"tax_point_date": "2026-10-30",
"currency": "PLN",
"tax_jurisdiction": "PL",
"counterparty_name": "Nowak Sp. z o.o.",
"counterparty_country": "PL",
"counterparty_vat_number": "PL7010001454",
"e_invoice_reference": "5252248481-20261102-ABC123-45",
"lines": [
{"line_id": "1", "net": "10000.00", "vat": "2300.00", "vat_rate": "23", "tax_code": "sale_domestic", "goods_or_services": "goods"},
{"line_id": "2", "net": "500.00", "vat": "40.00", "vat_rate": "8", "tax_code": "sale_domestic", "goods_or_services": "goods"}
]
}
]
}
The values are illustrative only.
The response
Whenever the body can be read, the call returns 200 with one result per document, in the order sent.
outcome |
Meaning |
|---|---|
accepted |
Stored. Processing follows in the next cycle |
duplicate |
Same key and body as before. The original result is returned |
refused |
Not stored. Fix it and resend with a newer source_updated_at |
A document is accepted or refused as a whole. If one of its lines is refused, the document is refused, and the other documents in the call are unaffected. Fix the refused one and resend it alone: resending the others is harmless.
Reading the result
The reads are optional. If you only send, the response to each call is enough: fix and resend the documents it refused.
GET /transactions/{client_ref}/{document_id}shows each line's status, errors, warnings and the returns it feeds.GET /transactions?client_ref=...&updated_since=...lists every document that changed. Poll it to stay in step. Every status, validity and filing change movesupdated_at.GET /transactions?client_ref=...&level=line&validity=errorlists every line in error, without reading whole documents.- To reconcile a period, filter
GET /transactionsbyinvoice_date_from,invoice_date_toandstatus, and compare with your ledger.