> ## Documentation Index
> Fetch the complete documentation index at: https://api-doc.fidly.be/llms.txt
> Use this file to discover all available pages before exploring further.

# Documents

> Invoices, credit notes and received documents: read, issue, correct, send, import.

A `document` is either an invoice / credit note **generated** by the company, or a
document **received** from outside (Peppol, mail, manual upload, Odoo, Billit...).
Both come through the same resource; `origin` says where a document came from
(`fidly` for documents generated in Fidly, `api` for files imported through this API,
otherwise the import channel).

<Info>
  `origin: api` only applies to files **imported** through the API, not to documents
  **created** through it. A created document always carries `origin: fidly`, as if
  it had been issued in the interface.
</Info>

## Listing documents

`GET /documents` — paginated list, newest first. Filters: `origin`, `type`,
`accounting_status`, `payment_status`, `issue_date_from`, `issue_date_to`.
Enumeration filters accept comma-separated lists (`origin=manual,peppol`). Quotes
are not documents and never appear; proforma invoices do.

## Retrieving a document

`GET /documents/{id}` — the full document, including its `lines`,
`allowance_charges` and `attachments`.

`GET /documents/{id}/file?format=pdf|xml` — the file bytes, served as an attachment.
`format=pdf` is the default; `format=xml` returns the UBL/Peppol version when it
exists. `404` if the requested version does not exist.

Each document carries its **lines**: `sequence`, `item_name`, `item_description`,
`quantity`, `unit_price`, `unit_code`, `vat_code`, `vat_rate`, `subtotal`, `total`
(both excl. VAT), `tax_amount`, `section_name`, `item_properties`,
`item_identification`, `product_code` and per-line `allowance_charges`. Document-level
`allowance_charges` (discounts and charges applied after the lines) sit at the root.

### Send statuses

An issued document carries two send statuses, one per channel:

* `peppol_status`: `not_sent`, `sending`, `sent` (delivered to the network),
  `accepted` (acknowledged by the recipient), `rejected` (refused by the
  recipient) or `failed`. A `rejected` or `failed` document can be sent again.
* `mail_status`: `not_sent`, `sent` or `failed`.

Both are `null` on a received document: it was never sent by the company.

## Creating a document

`POST /documents` — requires the create permission on documents. Creatable types: `sale_invoice`, `sale_credit_note`,
`purchase_invoice`, `purchase_credit_note`, `proforma_invoice`. The response is the
complete document (`201`), exactly as a later `GET` returns it.

You provide what identifies the document and what is invoiced; everything else is
computed. Sending a computed field is refused with a `422` naming it:

| Computed field                                                           | How                                                                                                                        |
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `id`, `origin`                                                           | UUID assigned at creation; `origin` is always `fidly`.                                                                     |
| `document_number`                                                        | Rendered from the journal's numbering format and sequence. A proforma is not numbered: its number is literally `proforma`. |
| `payment_remittance`, `is_remittance_structured`                         | Belgian structured communication (OGM/VCS), always structured.                                                             |
| `create_date`, `due_date`                                                | `due_date` always derives from `issue_date` + payment terms.                                                               |
| `tax_exclusive_amount`, `tax_amount`, `total_amount`, `remaining_amount` | Computed from the lines.                                                                                                   |
| `payment_status`                                                         | Derived from the remaining amount.                                                                                         |
| `accounting_status`                                                      | Always `waiting` at creation.                                                                                              |
| `peppol_status`, `mail_status`                                           | Always `not_sent` at creation: nothing is sent — see *Sending a document*.                                                 |
| `pdf_available`, `xml_available`                                         | `false` in the response; the files are rendered asynchronously.                                                            |

Optional inputs: `journal_id`, `delivery_location_id`, `issue_date`,
`delivery_date`, `tax_point_date`, `period_start`/`period_end`, `payment_terms`,
`order_number`, `despatch_reference`, `buyer_reference`, `legal_notice`, `currency`
(default `EUR`, validated against the currency list), `paid_amount` (default 0) and
document-level `allowance_charges`.

<Note>
  Two exceptions, accepted without error but without effect: `due_date`, always
  recomputed from the payment terms, and, on each line, `sequence`, `subtotal`,
  `total` and `tax_amount`, always recomputed. Lines are ordered by their position in
  the `lines` array.
</Note>

### Lines

1 to 500 lines, at most 50 document-level discounts/charges. Per line,
`item_name`, `quantity`, `unit_price` and `vat_code` are required.

* `unit_price` is never negative; `quantity` may be. The **document total** must
  stay positive (`422 invalid_amount` otherwise) — a credit note also totals a
  positive amount, its `type` carries the direction.
* `unit_code` (`unit` by default — `C62`) is validated against the units list, `currency`
  against the currency list — see [Reference lists](/en/concepts/reference-lists).
* Amounts are computed as `subtotal = quantity × unit_price`,
  `total = subtotal + charges − discounts` (excl. VAT), and
  `tax_amount = total × vat_rate / 100`. Document VAT is totalled per (code, rate)
  pair, rounding once per rate.
* `item_properties` is a flat key/value object; `item_identification` is a GTIN.

### VAT codes

`vat_code` names the regime, `vat_rate` carries the percentage:

| `vat_code`        | Regime                                    | Rate                                           | Combinable |
| ----------------- | ----------------------------------------- | ---------------------------------------------- | ---------- |
| `standard`        | Standard rate, domestic sale with VAT     | > 0 required                                   | ✅          |
| `exempted`        | Exempt sale (art. 44 §2 Belgian VAT Code) | 0 (may be omitted)                             | ✅          |
| `zero_rated`      | 0 % sale                                  | 0 (may be omitted)                             | ✅          |
| `reverse_charge`  | Reverse charge (co-contractor)            | 0 (may be omitted)                             | ✅          |
| `intra_community` | Intra-EU sale                             | 0 (may be omitted)                             | ❌          |
| `export`          | Export outside the EU                     | 0 (may be omitted)                             | ❌          |
| `not_subject`     | Not subject to VAT                        | **none** — `vat_rate` must be absent or `null` | ❌          |

<Note>
  Combinable codes (✅) are line-level regimes: they mix freely on one domestic
  invoice. The other three (❌) are **whole-invoice** regimes — an intra-EU sale, an
  export, or a seller not subject to VAT — that decide the legal mentions of the
  document, its VAT category in the Peppol XML and how your accountant declares it. A
  domestic line has no place on such an invoice: a ❌ code must be carried by *all*
  lines, and never together with another ❌ code (`422` otherwise). Issue one document
  per regime.
</Note>

<Note>
  Any positive `vat_rate` is accepted with `standard`, but the legal Belgian rates are
  6, 12 and 21 %.
</Note>

### Discounts and charges

Each entry carries a required `type` (`allowance` = discount, `charge` = extra
charge) and an amount expressed either as a fixed `amount` or a `rate` (percentage).
When both are present, `rate` wins and the amount is recomputed. On a **line**, the
`rate` applies to the line's `subtotal`; on the **document**, to the total of the
lines. A free-text `reason` names the entry: optional on a line, **required** on
the document.

A **document-level** discount or charge is split across the document's VAT rates,
in proportion to what the lines of each rate total, and the VAT of each rate is
computed on that adjusted base. Example: €1,000 of lines at 21 % and €500 at 6 %,
with a €150 discount → €100 is deducted from the 21 % base and €50 from the 6 %
base.

Two conditions follow; if either is not met, `422 invalid_allowance_charge`:

* The document total, discount or charge included, must stay positive or zero
  (`422 invalid_amount` otherwise). In particular, a fixed amount cannot apply to
  lines totalling 0.
* No VAT rate may total a negative amount (for instance a negative-quantity line
  that outweighs the other lines of the same rate): the discount would have no
  consistent base to split over. In that case, put the discount or charge on the
  lines rather than on the document.

### Payment terms and dates

* `payment_terms` is `<days>-<base>` with base `df` (from document date), `fm` (from
  the 1st of next month) or `mf` (end of the month the delay lands in): `30-df`,
  `0-df`... Resolution: body value → the relation's terms (companies only) →
  `30-df`. `due_date` always derives from it.
* `issue_date` is never in the future and follows the numbering order of the
  journal:

  * on **creation**, the day of the last numbered document of the journal, or later;
  * on **update**, between the document numbered just before and the one numbered
    just after, their dates included (with no next document, the upper bound is
    today).

  For a journal whose counter resets every month or year, the date must also stay
  within the period the number was drawn from. Otherwise `422 invalid_issue_date`,
  the message gives the expected bound.
* `delivery_date` is the date the delivery actually happened: today or earlier.

### Journal and relation

* `journal_id` is optional: by default the document takes the default journal
  matching its type (sales invoice, sales credit note, purchase invoice...). A
  journal of another category than the type's is refused (`422 invalid_journal`).
* `relation_id` must designate a relation of the company, and that relation must
  have an address (`422 invalid_relation`). Whether it is a company or a contact is
  read from the relation itself and returned as `relation_type`.
* `purchase_invoice` and `purchase_credit_note` are self-billing: they require the
  company's self-billing setting (`422 self_billing_disabled` otherwise).

### After creation

The PDF (and, when the type allows, the Peppol XML) is rendered **asynchronously**:
`pdf_available` / `xml_available` are `false` in the `201` response and flip to
`true` shortly after — the files are usually available within 5 to 7 seconds. Poll
`GET /documents/{id}`. Nothing is sent to the customer at creation — see *Sending*
below.

## Sending a document

<Warning>
  Sending by e-mail is not yet available through the API: only sending over Peppol
  is.
</Warning>

Requires the update permission on documents. Two ways:

* `POST /documents/{id}/send` with body `{"peppol": true}` — send an existing
  document.
* `POST /documents?send_peppol=true` — create and send in one call (also requires
  the create permission).

The response is `202`: sending is asynchronous, the API acknowledges the hand-off,
not the delivery. The document is re-rendered before leaving, so attachments added
since creation are embedded in the XML.

* Transportable types: `sale_invoice`, `sale_credit_note`, `purchase_invoice`,
  `purchase_credit_note`. A proforma is refused (`422 not_sendable`), and a received
  document too (`409`).
* A document already sent returns `409 already_sent`; a **failed** send
  (`peppol_status` at `failed` or `rejected`) can be retried with the same call.
* A body that requests no channel (empty or all-false) is refused (`422`).
* If the send workflow cannot be started, `422 send_failed` is returned. With
  `?send_peppol=true` the document **was** created and numbered — the message gives
  its id, and the recovery is `POST /documents/{id}/send`, never a second creation.

Network-level requirements (company registered on Peppol, customer being a company
with a VAT number or identifier) are checked by the send workflow itself; the
outcome is read afterwards in the document's `peppol_status`, not in the `202`
response: `sending`, then `sent` once delivered to the network, then `accepted` or
`rejected` depending on the recipient's answer — or `failed` if the send did not go
through.

## Updating a document

`PATCH /documents/{id}` — requires the update permission on documents. Only the fields present in the body are applied; the response
is the full document. Allowed as long as the document has left **nowhere**:

* not sent to the customer (`mail_status` and `peppol_status` at `not_sent`),
* not transmitted to accounting.

Otherwise `409 document_not_editable`, naming the blocking condition. Only
documents with `origin: fidly` can be modified.

### Read-only fields

`type` and `journal_id` no longer change after creation (`422 field_not_editable`
on change). To change the type or the journal, delete and recreate the document.

### Clearing a field

Only optional fields accept `null`: it clears them (or detaches the delivery address
for `delivery_location_id`). On a required field, `null` is refused
(`422 validation_error`) — omit it to leave it untouched.

### Changing the date or the payment terms

Changing `issue_date` obeys the same numbering rules as creation; changing it or
`payment_terms` recomputes the `due_date`.

### Recompute and re-render

Every update recomputes the document (VAT, totals, payment status) and re-renders
its PDF/XML.

### Editing lines, discounts and charges

`lines` and `allowance_charges` **replace** the stored lists entirely; omitting them
leaves the stored lists untouched.

<Info>
  For technical reasons, `lines` and `allowance_charges` are always sent back whole:
  a single line or a single discount cannot be modified on its own. Take the array of
  the document you read, edit it and send it back complete — its computed fields
  (`sequence`, `subtotal`, `total`, `tax_amount`) can stay in: they are recomputed,
  the value you send is ignored.
</Info>

<Warning>
  There is no version token: the last writer wins. A `lines` array built from a stale
  read overwrites changes made in the meantime.
</Warning>

### Recording a payment

`paid_amount` records a payment Fidly cannot see by itself (cash, an untracked bank
account). A `PATCH` carrying **only** `paid_amount` is accepted whatever the state
of the document — already sent, already exported — and regenerates nothing. It
drives `payment_status` and `remaining_amount`. Declaring more than the total is
refused (`422 invalid_paid_amount`). Adding any other editable field makes the
request an ordinary modification, subject to the lock above.

## Attachments

`POST /documents/{id}/attachments` — requires the update permission on documents. Add files to a document the company issued:
`multipart/form-data`, the `files` field repeated, **10 files max per call, 10 MB
for the whole call**. The response is the full document with its `attachments` list.

| Extension       | Stored type                                                         |
| --------------- | ------------------------------------------------------------------- |
| `.pdf`          | `application/pdf`                                                   |
| `.csv`          | `text/csv`                                                          |
| `.png`          | `image/png`                                                         |
| `.jpg`, `.jpeg` | `image/jpeg`                                                        |
| `.xlsx`         | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
| `.ods`          | `application/vnd.oasis.opendocument.spreadsheet`                    |

* **All-or-nothing**: one refused file fails the whole call and nothing is attached.
* A filename already attached is refused (`409 duplicate_attachment`).
* Strictly additive: attached files can never be removed or replaced through the API.
* Attaching does not regenerate the stored PDF/XML. Attachments enter the Peppol XML
  at **send** time, which re-renders the document. Attaching stays possible on a
  document already sent or exported.
* A received document does not take attachments (`409`).

## Importing a file

`POST /documents/import` — requires the create permission on documents. The API
equivalent of dropping a file in the interface: `multipart/form-data` with a single
`file` field.

### Allowed files

Accepted extensions: `.pdf`, `.xml`, `.jpg`, `.jpeg`, `.png`. Any other extension
is refused (`415`).

### File size

10 MB maximum per file; above that, `413`.

### Processing

`?type=` (query) drives the processing: `purchase` (default), `sale` or `other`.
Returns `201` with only the `id` of the created document: at creation the document
is deliberately empty (`type: unknown`, `null` amounts) until the asynchronous chain
has parsed it (Peppol parsing for XML, AI analysis for PDF/images). Follow the
enrichment with `GET /documents/{id}`. Only `type=other` is final at creation
(`other_document`, archived without analysis).

Errors: `415` refused extension, `413` file too big, `400` unknown type, `409` file
already imported.

## Deleting a document

`DELETE /documents/{id}` — requires the delete permission on documents. Returns `204`. Deletion is definitive, and only possible when:

* the document holds the **highest number of its sequence** (journal + type) in the
  current numbering window — otherwise `409 document_not_deletable`, naming both
  numbers. Deleting the last returns its number to the sequence. A proforma has no
  number, so this rule does not apply to it;
* it has left nowhere (same conditions as `PATCH`): not sent, not transmitted to
  accounting, not a received document;
* nothing is attached to it (`409 document_in_use`): no bank reconciliation, no
  invoiced timesheet line, no payment intent.
