Envoyer et lire une facture
La facture que vous envoyez
Section intitulée « La facture que vous envoyez »Chaque envoi demande une clé d’idempotence : voir Idempotence. Pour envoyer jusqu’à 100 factures d’un coup, voir le lot. Pour un avoir, voir Avoirs.
POST /businesses/{business}/invoicesObligatoires : items (1 à 1 000 lignes), currency, et l’un des deux : l’en-tête Idempotency-Key ou external_reference. Tout autre champ peut être omis ; le tableau dit ce que devient chaque champ omis.
| Champ | Sens | S’il est omis |
|---|---|---|
external_reference |
Votre référence, 100 caractères au plus, unique dans l’entreprise. C’est une clé d’idempotence à part entière. | aucune ; l’en-tête est alors obligatoire |
sale_time |
L’instant de la vente, conservé et restitué tel quel. La DGI n’a pas ce champ : il ne lui est pas transmis. | rien ne le remplace |
currency |
CDF ou USD. Obligatoire. |
|
exchange_rate |
La valeur en CDF de 1 USD (par exemple 2800) : 12 chiffres avant la virgule et 6 après, au plus. C’est le taux que reçoit la DGI et qu’imprime la facture. |
Obligatoire avec USD (400 CURRENCY_RATE_REQUIRED). Refusé avec CDF, même à 1 (400 CURRENCY_RATE_NOT_ALLOWED). |
price_mode |
ttc ou ht : les prix unitaires incluent-ils la TVA. |
ttc. Une ligne en groupe M ou N fait passer toute la facture en ht, envoyé ou non. |
type |
Accepté et ignoré : une vente est toujours FV. FA et EA répondent 400 CREDIT_NOTE_ROUTE_REQUIRED : un avoir a sa propre route. |
FV |
format |
Accepté et ignoré : nous ne mettons en page aucun PDF pour vous. Il n’apparaît jamais dans defaults_applied. |
|
delivery |
Refusé (400 DELIVERY_NOT_AVAILABLE). Nous n’envoyons rien à vos clients : la remise de la facture vous appartient. |
|
target |
{ "entity_id": "ent_..." } : le point de vente qui normalise la facture. Le champ entity_id du corps est une forme plus ancienne de la même chose ; les deux, s’ils diffèrent, répondent 400 TARGET_CONFLICT. |
n’importe quel point de vente actif de l’entreprise |
client |
L’acheteur (ci-dessous). | un client anonyme de type PP, imprimé « Client Comptant » |
payments |
Comment la vente a été réglée : une liste de { "method", "amount", "currency" }, 20 au plus. method vaut ESPECES, VIREMENT, CARTEBANCAIRE, MOBILEMONEY, CHEQUES, CREDIT ou AUTRE. Les montants totalisent le total TTC de la facture (sinon 400 PAYMENTS_TOTAL_MISMATCH), dans la devise de la facture, deux décimales au plus. |
CREDIT pour tout le total : une facture impayée |
operator_name |
Le nom du caissier, transmis à la DGI et imprimé, 100 caractères au plus. | le trade_name de l’entreprise, sinon sa legal_name |
notes |
Texte libre, 2 000 caractères au plus. | aucune |
comment_lines |
{ "a": "...", ... "h": "..." } : les lignes de commentaire A à H de la DGI, transmises et imprimées telles quelles, 250 caractères chacune. Pour un client AO, la ligne A est client.exemption_certificate. |
aucune |
items[] |
Les lignes (ci-dessous). Obligatoire. |
client
| Champ | Sens | S’il est omis |
|---|---|---|
type |
PP, PM, PC, PL ou AO (les types de client de la DGI). |
PP, même si un NIF est donné : pour une société, indiquez PM. |
name |
255 caractères au plus. Obligatoire pour PM, PC, PL, AO (400 CLIENT_NAME_REQUIRED). |
pour PP : anonyme, aucun nom n’est envoyé à la DGI, la facture imprime « Client Comptant » |
nif |
Une lettre, sept chiffres, une lettre. Obligatoire pour PM, PC, PL. |
aucun |
address, phone, email |
Facultatifs. | aucun |
exemption_certificate |
La référence du certificat d’exonération, obligatoire pour un client AO (400 EXEMPTION_CERTIFICATE_REQUIRED). |
items[]
| Champ | Sens | S’il est omis |
|---|---|---|
description |
500 caractères au plus. Obligatoire. | |
quantity |
Supérieure à zéro. Obligatoire. | |
unit_price |
Zéro ou plus. Obligatoire. | |
tax_group |
Le code du groupe de taxation de la DGI. | B pour une entreprise assujettie à la TVA, ce qu’est toute entreprise créée par cette API, sinon A. Pour un client AO, toujours D. |
item_type |
BIE (bien), SER (service) ou TAX (taxes et redevances, groupes L et N seulement). |
BIE |
code |
Votre code article, 50 caractères au plus, transmis et imprimé. | aucun |
unit |
20 caractères au plus, transmise et imprimée. | aucune |
discount_type |
% ou FIXED. |
déduit du champ de remise que vous avez envoyé |
discount_percent |
De 0 à 100. | aucune remise |
discount_amount |
Une remise pour toute la ligne, deux décimales au plus, inférieure au montant de la ligne. | aucune remise |
tax_specific_value, tax_specific_amount |
Une taxe spécifique : par unité (230) ou en pourcentage (10%), et son montant pour toute la quantité. Envoyés ensemble. |
aucune |
La taxe est calculée au niveau du groupe de taxation, selon les règles de la DGI, et la facture conserve le résultat. Vous ne calculez jamais la TVA pour nous, et vous ne devez pas la recalculer pour imprimer (Imprimer votre facture).
defaults_applied
Section intitulée « defaults_applied »Chaque réponse liste, dans defaults_applied, les champs que vous avez omis et que nous avons remplis par un défaut. Valeurs possibles sur une facture : payments, price_mode, target, operator_name, client.type, client.name, items[].tax_group, items[].item_type. Sur un avoir : type, items, target, operator_name. La liste est la même sur la première réponse, sur un rejeu et sur toute lecture ultérieure. Elle est vide si vous avez tout envoyé. format n’y figure jamais.
POST /api/platform/v1/businesses/ext:plateforme-cust-001842/invoicesAuthorization: Bearer rpa_pk_live_...Content-Type: application/json{ "external_reference": "VENTE-2026-000123", "sale_time": "2026-11-05T08:12:00Z", "currency": "CDF", "client": { "type": "PM", "name": "Mbuyi Services SARL", "nif": "A7654321C", "address": "5 rue de la Paix, Gombe" }, "items": [ { "description": "Ramette papier A4", "code": "PAP-A4", "unit": "pce", "quantity": 2, "unit_price": 11600, "tax_group": "B" } ], "payments": [ { "method": "ESPECES", "amount": 23200 } ], "operator_name": "Sarah Kabeya", "notes": "Merci de votre confiance."}Réponse 202 :
{ "object": "invoice", "data": { "id": "inv_2xJ4kQ9nVbT7mLpR3sYz", "normalisation_status": "pending", "created_at": "2026-11-05T08:12:41Z", "external_reference": "VENTE-2026-000123", "nid": "CD01004159-1", "sale_time": "2026-11-05T08:12:00Z", "defaults_applied": ["target", "items[].item_type"], "type": "FV", "business": { "id": "biz_2xJ4kQ9nVbT7mLpR3sYz", "external_id": "plateforme-cust-001842" } }, "meta": { "request_id": "req_04" }}(price_mode, operator_name, payments, client.type, client.name et tax_group ont tous été envoyés : ils ne sont pas dans defaults_applied.)
Si l’entreprise n’a aucun point de vente capable de normaliser (aucun actif, pas de jeton, jeton expiré, pas de NIF), l’appel répond 409 aussitôt (NO_ENTITY, ENTITY_INACTIVE, ENTITY_NO_DGI_TOKEN, ENTITY_TOKEN_EXPIRED, BUSINESS_NO_NIF) et n’enregistre rien. Corrigez, puis renvoyez le même appel.
Lire une facture
Section intitulée « Lire une facture »| Route | Effet |
|---|---|
GET /businesses/{business}/invoices/{invoice} |
Une facture ou un avoir. {invoice} est notre identifiant ou ext:<external_reference>. Toutes ses lignes (jusqu’à 1 000). |
GET /businesses/{business}/invoices?external_reference=INV%2F2026%2F0001 |
Une référence qui ne tient pas dans un chemin (elle contient /, %, ;, \, ou est un segment .) se lit ici : zéro ou une ligne. La réponse d’une telle facture, et ses événements, portent read_by_reference, l’adresse exacte pour la lire. |
GET /businesses/{business}/invoices?status=&from=&to=&cursor=&limit= |
Du plus récent au plus ancien. status vaut pending, normalised ou failed ; from est inclus et to exclu, sur l’heure de création. Une page contient au plus 100 lignes par facture (Imprimer votre facture). Une liste est une vue : une facture enregistrée en retard peut apparaître derrière un curseur déjà passé. Réconciliez avec le flux d’événements, pas avec les listes. |
Le vocabulaire des états
Section intitulée « Le vocabulaire des états »Une réponse de facture et un événement disent normalisation_status :
| Valeur | Sens |
|---|---|
pending |
Acceptée et pas encore réglée : en file, ou en cours d’envoi à la DGI. Vous ne pouvez pas, et n’avez pas à, distinguer les deux. Cela peut durer tant qu’un point de vente est en pause à cause de son jeton. |
normalised |
La DGI l’a normalisée. normalised_at est renseigné et les champs d’impression sont présents. |
failed |
Définitif. reason_code et reason disent pourquoi, avec des mots que vous pouvez montrer à votre client. Elle ne sera jamais normalisée : renvoyez la vente avec une nouvelle clé et une nouvelle référence. |
normalised_at est l’instant, sur notre horloge, où nous avons enregistré le résultat. Pour la date à imprimer, utilisez dgi_date_time (Imprimer votre facture). payment_status (impaye, partiel, paye) peut être absent tant que la facture est pending.
Exemple de facture en échec
Section intitulée « Exemple de facture en échec »Exemple de facture en échec :
{ "id": "inv_2xJ4kQ9nVbT7mLpR3sZa", "normalisation_status": "failed", "created_at": "2026-11-05T09:30:00Z", "external_reference": "VENTE-2026-000124", "reason_code": "DGI_INVOICE_REJECTED", "reason": "La DGI a refusé cette facture : ...", "type": "FV", "business": { "id": "biz_2xJ4kQ9nVbT7mLpR3sYz", "external_id": "plateforme-cust-001842" }}L’ensemble des reason_code est ouvert (les refus propres de la DGI en font partie). Montrez reason, et gardez reason_code pour le support.