Aller au contenu

Envoyer et lire une facture

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}/invoices

Obligatoires : 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).

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/invoices
Authorization: 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.

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.

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 :

{
"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.