> ## 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

> Factures, notes de crédit et documents reçus : lire, émettre, corriger, envoyer, importer.

Un `document` est soit une facture / note de crédit **générée** par l'entreprise,
soit un document **reçu** de l'extérieur (Peppol, mail, dépôt manuel, Odoo,
Billit...). Les deux passent par la même ressource ; `origin` dit d'où vient un
document (`fidly` pour les documents générés dans Fidly, `api` pour les fichiers
importés via cette API, sinon le canal d'import).

<Info>
  `origin: api` ne concerne que les fichiers **importés** via l'API, pas les
  documents **créés** via l'API. Un document créé porte toujours `origin: fidly`,
  comme s'il avait été émis dans l'interface.
</Info>

## Lister les documents

`GET /documents` — liste paginée, du plus récent au plus ancien. Filtres : `origin`,
`type`, `accounting_status`, `payment_status`, `issue_date_from`, `issue_date_to`.
Les filtres d'énumération acceptent des listes séparées par des virgules
(`origin=manual,peppol`). Les devis ne sont pas des documents et n'apparaissent
jamais ; les proformas, si.

## Récupérer un document

`GET /documents/{id}` — le document complet, avec ses `lines`, ses
`allowance_charges` et ses `attachments`.

`GET /documents/{id}/file?format=pdf|xml` — les octets du fichier, servis en pièce
jointe. `format=pdf` par défaut ; `format=xml` renvoie la version UBL/Peppol quand
elle existe. `404` si la version demandée n'existe pas.

Chaque document porte ses **lignes** : `sequence`, `item_name`, `item_description`,
`quantity`, `unit_price`, `unit_code`, `vat_code`, `vat_rate`, `subtotal`, `total`
(tous deux hors TVA), `tax_amount`, `section_name`, `item_properties`,
`item_identification`, `product_code` et les `allowance_charges` de ligne. Les
`allowance_charges` du document (remises et majorations appliquées après les
lignes) sont à la racine.

### Statuts d'envoi

Un document émis porte deux statuts d'envoi, un par canal :

* `peppol_status` : `not_sent`, `sending`, `sent` (remis au réseau), `accepted`
  (acquitté par le destinataire), `rejected` (refusé par le destinataire) ou
  `failed`. Un document `rejected` ou `failed` peut être renvoyé.
* `mail_status` : `not_sent`, `sent` ou `failed`.

Les deux valent `null` sur un document reçu : il n'a jamais été envoyé par
l'entreprise.

## Créer un document

`POST /documents` — droit requis : création de documents. Types créables : `sale_invoice`, `sale_credit_note`,
`purchase_invoice`, `purchase_credit_note`, `proforma_invoice`. La réponse est le
document complet (`201`), tel qu'un `GET` ultérieur le renverra.

Vous fournissez ce qui identifie le document et ce qui est facturé ; tout le reste
est calculé. Envoyer un champ calculé est refusé par un `422` qui le nomme :

| Champ calculé                                                            | Comment                                                                                                                                  |
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `id`, `origin`                                                           | UUID attribué à la création ; `origin` vaut toujours `fidly`.                                                                            |
| `document_number`                                                        | Rendu par le format de numérotation et la séquence du journal. Un proforma n'est pas numéroté : son numéro est littéralement `proforma`. |
| `payment_remittance`, `is_remittance_structured`                         | Communication structurée belge (OGM/VCS), toujours structurée.                                                                           |
| `create_date`, `due_date`                                                | `due_date` dérive toujours d'`issue_date` + conditions de paiement.                                                                      |
| `tax_exclusive_amount`, `tax_amount`, `total_amount`, `remaining_amount` | Calculés depuis les lignes.                                                                                                              |
| `payment_status`                                                         | Déduit du restant dû.                                                                                                                    |
| `accounting_status`                                                      | Toujours `waiting` à la création.                                                                                                        |
| `peppol_status`, `mail_status`                                           | Toujours `not_sent` à la création : rien n'est envoyé — voir *Envoyer un document*.                                                      |
| `pdf_available`, `xml_available`                                         | `false` dans la réponse ; les fichiers sont rendus de façon asynchrone.                                                                  |

Entrées optionnelles : `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`
(défaut `EUR`, validée contre la liste des devises), `paid_amount` (défaut 0) et les
`allowance_charges` de document.

<Note>
  Deux exceptions, acceptées sans erreur mais sans effet : `due_date`, toujours
  recalculée depuis les conditions de paiement, et, sur chaque ligne, `sequence`,
  `subtotal`, `total` et `tax_amount`, toujours recalculés. L'ordre des lignes est
  celui du tableau `lines`.
</Note>

### Lignes

De 1 à 500 lignes, au plus 50 remises/majorations de document. Par ligne,
`item_name`, `quantity`, `unit_price` et `vat_code` sont obligatoires.

* `unit_price` n'est jamais négatif ; `quantity` peut l'être. Le **total du
  document** doit rester positif (`422 invalid_amount` sinon) — une note de crédit
  totalise aussi un montant positif, son `type` porte le sens.
* `unit_code` (`unité` par défaut — `C62`) est validé contre la liste des unités, `currency`
  contre celle des devises — voir
  [Listes de référence](/fr/concepts/reference-lists).
* Les montants sont calculés : `subtotal = quantity × unit_price`,
  `total = subtotal + majorations − remises` (hors TVA), et
  `tax_amount = total × vat_rate / 100`. La TVA du document est totalisée par couple
  (code, taux), avec un seul arrondi par taux.
* `item_properties` est un objet plat clé/valeur ; `item_identification` un GTIN.

### Codes TVA

`vat_code` nomme le régime, `vat_rate` porte le pourcentage :

| `vat_code`        | Régime                                 | Taux                                    | Combinable |
| ----------------- | -------------------------------------- | --------------------------------------- | ---------- |
| `standard`        | Taux normal, vente domestique avec TVA | > 0 obligatoire                         | ✅          |
| `exempted`        | Vente exonérée (art. 44 §2 Code TVA)   | 0 (peut être omis)                      | ✅          |
| `zero_rated`      | Vente à 0 %                            | 0 (peut être omis)                      | ✅          |
| `reverse_charge`  | Autoliquidation (cocontractant)        | 0 (peut être omis)                      | ✅          |
| `intra_community` | Vente intracommunautaire UE            | 0 (peut être omis)                      | ❌          |
| `export`          | Exportation hors UE                    | 0 (peut être omis)                      | ❌          |
| `not_subject`     | Non assujetti                          | **aucun** — `vat_rate` absent ou `null` | ❌          |

<Note>
  Les codes combinables (✅) sont des régimes de ligne : ils se mélangent librement
  sur une même facture domestique. Les trois autres (❌) sont des régimes de
  **facture entière** — une vente intracommunautaire, une exportation ou un vendeur
  non assujetti — qui déterminent les mentions légales du document, sa catégorie de
  TVA dans le XML Peppol et la façon dont votre comptable la déclare. Une ligne
  domestique n'a pas sa place sur une telle facture : un code ❌ doit être porté par
  *toutes* les lignes, et jamais avec un autre code ❌ (`422` sinon). Émettez un
  document distinct par régime.
</Note>

<Note>
  Tout `vat_rate` strictement positif est accepté avec `standard`, mais les taux
  légaux belges sont 6, 12 et 21 %.
</Note>

### Remises et majorations

Chaque entrée porte un `type` obligatoire (`allowance` = remise, `charge` =
majoration) et un montant exprimé soit en `amount` fixe, soit en `rate`
(pourcentage). Si les deux sont présents, `rate` l'emporte et le montant est
recalculé. Sur une **ligne**, le `rate` s'applique au `subtotal` de la ligne ; sur
le **document**, au total des lignes. Un `reason` libre nomme l'entrée :
facultatif sur une ligne, **obligatoire** sur le document.

Une remise ou majoration de **document** est répartie entre les taux de TVA du
document, au prorata de ce que les lignes de chaque taux totalisent, et la TVA de
chaque taux est calculée sur cette base corrigée. Exemple : 1 000 € de lignes à
21 % et 500 € à 6 %, avec une remise de 150 € → 100 € sont déduits de la base à
21 % et 50 € de celle à 6 %.

Deux conditions en découlent ; si l'une des deux n'est pas respectée,
`422 invalid_allowance_charge` :

* Le total du document, remise ou majoration comprise, doit rester positif ou nul
  (`422 invalid_amount` sinon). En particulier, un montant fixe ne peut pas
  s'appliquer à des lignes qui totalisent 0.
* Aucun taux de TVA ne peut totaliser un montant négatif (par exemple une ligne à
  quantité négative qui dépasse les autres lignes du même taux) : la remise n'aurait
  plus de base cohérente à répartir. Dans ce cas, portez la remise ou la majoration
  sur les lignes plutôt que sur le document.

### Conditions de paiement et dates

* `payment_terms` s'écrit `<jours>-<base>` avec `df` (date du document), `fm` (1er
  du mois suivant) ou `mf` (fin du mois où tombe le délai) : `30-df`, `0-df`...
  Résolution : valeur du corps → conditions du tiers (sociétés uniquement) →
  `30-df`. `due_date` en dérive toujours.
* `issue_date` n'est jamais dans le futur et suit l'ordre de numérotation du
  journal :

  * à la **création**, le jour du dernier document numéroté du journal, ou après ;
  * à la **modification**, entre le document numéroté juste avant et celui numéroté
    juste après, leurs dates incluses (sans document suivant, la borne haute est la
    date du jour).

  Pour un journal dont le compteur redémarre chaque mois ou chaque année, la date
  doit en plus rester dans la période dont le numéro est tiré. Sinon
  `422 invalid_issue_date`, le message donne la borne attendue.
* `delivery_date` est la date où la livraison a effectivement eu lieu : aujourd'hui
  ou antérieure.

### Journal et tiers

* `journal_id` est optionnel : à défaut, le document prend le journal par défaut
  correspondant à son type (facture de vente, note de crédit de vente, facture
  d'achat...). Un journal d'une autre catégorie que celle du type est refusé
  (`422 invalid_journal`).
* `relation_id` doit désigner un tiers de l'entreprise, et ce tiers doit avoir une
  adresse (`422 invalid_relation`). Sa nature (société ou contact) est lue sur le
  tiers et renvoyée dans `relation_type`.
* `purchase_invoice` et `purchase_credit_note` relèvent de l'auto-facturation : ils
  exigent le paramètre d'auto-facturation de la société
  (`422 self_billing_disabled` sinon).

### Après la création

Le PDF (et, quand le type le permet, le XML Peppol) est rendu **de façon
asynchrone** : `pdf_available` / `xml_available` valent `false` dans la réponse
`201` et passent à `true` peu après — les fichiers sont généralement disponibles
dans les 5 à 7 secondes. Suivez `GET /documents/{id}`. Rien n'est envoyé au client à
la création — voir *Envoyer* ci-dessous.

## Envoyer un document

<Warning>
  L'envoi par e-mail n'est pas encore disponible via l'API : seul l'envoi par Peppol
  l'est.
</Warning>

Droit requis : modification de documents. Deux voies :

* `POST /documents/{id}/send` avec le corps `{"peppol": true}` — envoyer un document
  existant.
* `POST /documents?send_peppol=true` — créer et envoyer en un appel (exige aussi
  le droit de création).

La réponse est un `202` : l'envoi est asynchrone, l'API accuse la prise en charge,
pas la livraison. Le document est re-rendu avant de partir : les pièces jointes
ajoutées depuis la création entrent dans le XML.

* Types transportables : `sale_invoice`, `sale_credit_note`, `purchase_invoice`,
  `purchase_credit_note`. Un proforma est refusé (`422 not_sendable`), un document
  reçu aussi (`409`).
* Un document déjà parti renvoie `409 already_sent` ; un envoi en **échec**
  (`peppol_status` à `failed` ou `rejected`) se relance avec le même appel.
* Un corps qui ne demande aucun canal (vide ou tout à `false`) est refusé (`422`).
* Si le workflow d'envoi ne peut pas démarrer, `422 send_failed`. Avec
  `?send_peppol=true` le document **est** créé et numéroté — le message donne son
  id, et la reprise est `POST /documents/{id}/send`, jamais une seconde création.

Les exigences du réseau (société enregistrée sur Peppol, client société avec numéro
de TVA ou identifiant) sont vérifiées par le workflow d'envoi lui-même ; l'issue se
lit ensuite dans le `peppol_status` du document, pas dans la réponse `202` :
`sending`, puis `sent` une fois remis au réseau, puis `accepted` ou `rejected`
selon la réponse du destinataire — ou `failed` si l'envoi n'a pas abouti.

## Mettre à jour un document

`PATCH /documents/{id}` — droit requis : modification de documents. Seuls les champs présents dans le corps sont
appliqués ; la réponse est le document complet. Possible tant que le document n'est
parti **nulle part** :

* pas envoyé au client (`mail_status` et `peppol_status` à `not_sent`),
* pas transmis à la comptabilité.

Sinon `409 document_not_editable`, en nommant la condition qui bloque. Seuls les
documents d'`origin: fidly` peuvent être modifiés.

### Champs en lecture seule

`type` et `journal_id` ne changent plus après la création (`422 field_not_editable`
au changement). Pour changer le type ou le journal, il faut supprimer et recréer le
document.

### Effacer un champ

Seuls les champs optionnels acceptent `null` : cela les efface (ou détache l'adresse
de livraison pour `delivery_location_id`). Sur un champ obligatoire, `null` est
refusé (`422 validation_error`) — omettez-le pour ne pas y toucher.

### Changer la date ou les conditions de paiement

La modification d'`issue_date` obéit aux mêmes règles de numérotation qu'à la
création ; la changer, ou changer `payment_terms`, recalcule la `due_date`.

### Recalcul et régénération

Toute modification recalcule le document (TVA, totaux, statut de paiement) et
régénère son PDF/XML.

### Modifier les lignes, remises et majorations

`lines` et `allowance_charges` **remplacent** entièrement les listes stockées ; les
omettre laisse les listes stockées intactes.

<Info>
  Pour des raisons techniques, `lines` et `allowance_charges` se renvoient toujours
  en entier : il n'est pas possible de modifier une seule ligne ou une seule remise.
  Reprenez le tableau du document lu, éditez-le et renvoyez-le complet — ses champs
  calculés (`sequence`, `subtotal`, `total`, `tax_amount`) peuvent y rester : ils sont
  recalculés, la valeur envoyée est ignorée.
</Info>

<Warning>
  Il n'y a pas de jeton de version : le dernier écrivain gagne. Un tableau `lines`
  construit sur une lecture périmée écrase les modifications faites entre-temps.
</Warning>

### Enregistrer un paiement

`paid_amount` enregistre un encaissement que Fidly ne voit pas de lui-même
(espèces, compte bancaire non suivi). Un `PATCH` ne portant **que** `paid_amount`
est accepté quel que soit l'état du document — déjà envoyé, déjà exporté — et ne
régénère rien. Il pilote `payment_status` et `remaining_amount`. Déclarer plus que
le total est refusé (`422 invalid_paid_amount`). Ajouter tout autre champ éditable
fait de la requête une modification ordinaire, soumise au verrou ci-dessus.

## Pièces jointes

`POST /documents/{id}/attachments` — droit requis : modification de documents. Ajouter des fichiers à un document émis par
l'entreprise : `multipart/form-data`, le champ `files` répété, **10 fichiers max par
appel, 10 Mo pour l'appel entier**. La réponse est le document complet avec sa liste
`attachments`.

| Extension       | Type stocké                                                         |
| --------------- | ------------------------------------------------------------------- |
| `.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`                    |

* **Tout ou rien** : un fichier refusé fait échouer l'appel entier et rien n'est
  attaché.
* Un nom déjà attaché est refusé (`409 duplicate_attachment`).
* Strictement additif : une pièce jointe ne peut jamais être retirée ni remplacée
  via l'API.
* Attacher ne régénère pas le PDF/XML stockés. Les pièces jointes entrent dans le
  XML Peppol au moment de l'**envoi**, qui re-rend le document. Attacher reste
  possible sur un document déjà envoyé ou exporté.
* Un document reçu ne prend pas de pièce jointe (`409`).

## Importer un fichier

`POST /documents/import` — droit requis : création de documents. L'équivalent API
du glisser-déposer de l'interface : `multipart/form-data` avec un seul champ `file`.

### Fichiers autorisés

Extensions acceptées : `.pdf`, `.xml`, `.jpg`, `.jpeg`, `.png`. Toute autre
extension est refusée (`415`).

### Taille des fichiers

10 Mo maximum par fichier ; au-delà, `413`.

### Traitement

`?type=` (query) pilote le traitement : `purchase` (défaut), `sale` ou `other`.
Renvoie `201` avec le seul `id` du document créé : à la création, le document est
volontairement vide (`type: unknown`, montants `null`) tant que la chaîne asynchrone
ne l'a pas analysé (parsing Peppol pour les XML, analyse IA pour les PDF/images).
Suivez l'enrichissement via `GET /documents/{id}`. Seul `type=other` est final dès
la création (`other_document`, archivé sans analyse).

Erreurs : `415` extension refusée, `413` fichier trop gros, `400` type inconnu,
`409` fichier déjà importé.

## Supprimer un document

`DELETE /documents/{id}` — droit requis : suppression de documents. Renvoie `204`. La suppression est définitive, et
possible seulement quand :

* le document porte le **plus haut numéro de sa séquence** (journal + type) dans la
  fenêtre de numérotation courante — sinon `409 document_not_deletable`, en nommant
  les deux numéros. Supprimer le dernier rend son numéro à la séquence. Un proforma
  n'a pas de numéro : cette règle ne s'applique pas à lui ;
* il n'est parti nulle part (mêmes conditions que le `PATCH`) : pas envoyé, pas
  transmis à la comptabilité, pas un document reçu ;
* rien n'y est rattaché (`409 document_in_use`) : ni rapprochement bancaire, ni
  ligne de prestation facturée, ni intention de paiement.
