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).
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.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) oufailed. Un documentrejectedoufailedpeut être renvoyé.mail_status:not_sent,sentoufailed.
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 :
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.
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.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_pricen’est jamais négatif ;quantitypeut l’être. Le total du document doit rester positif (422 invalid_amountsinon) — une note de crédit totalise aussi un montant positif, sontypeporte le sens.unit_code(unitépar défaut —C62) est validé contre la liste des unités,currencycontre celle des devises — voir Listes de référence.- Les montants sont calculés :
subtotal = quantity × unit_price,total = subtotal + majorations − remises(hors TVA), ettax_amount = total × vat_rate / 100. La TVA du document est totalisée par couple (code, taux), avec un seul arrondi par taux. item_propertiesest un objet plat clé/valeur ;item_identificationun GTIN.
Codes TVA
vat_code nomme le régime, vat_rate porte le pourcentage :
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.Tout
vat_rate strictement positif est accepté avec standard, mais les taux
légaux belges sont 6, 12 et 21 %.Remises et majorations
Chaque entrée porte untype 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_amountsinon). 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_termss’écrit<jours>-<base>avecdf(date du document),fm(1er du mois suivant) oumf(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_dateen dérive toujours. -
issue_daten’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).
422 invalid_issue_date, le message donne la borne attendue. -
delivery_dateest la date où la livraison a effectivement eu lieu : aujourd’hui ou antérieure.
Journal et tiers
journal_idest 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_iddoit 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 dansrelation_type.purchase_invoiceetpurchase_credit_noterelèvent de l’auto-facturation : ils exigent le paramètre d’auto-facturation de la société (422 self_billing_disabledsinon).
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
Droit requis : modification de documents. Deux voies :POST /documents/{id}/sendavec 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).
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àfailedourejected) 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=truele document est créé et numéroté — le message donne son id, et la reprise estPOST /documents/{id}/send, jamais une seconde création.
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_statusetpeppol_statusànot_sent), - pas transmis à la comptabilité.
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 acceptentnull : 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.
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.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.
- 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.