Invoices
Une Invoice représente une facture électronique en cours de traitement. C'est l'objet central de l'API EFact.
En bref
Pour créer une facture, envoyez ses données en JSON (numéro, date, devise, ICE de l'acheteur, lignes) : EFact recalcule les totaux, génère le XML UBL 2.1, applique la signature XAdES et transmet à la DGI. Une fois signée ou transmise, la facture est verrouillée ; toute modification renvoie une erreur 409et la régularisation passe par une facture d'avoir.
Simulateur DGI: la plateforme nationale d'e-facturation de la DGI n'est pas encore ouverte aux intégrations. La clairance (statuts ACCEPTED / REJECTED, référence, QR code) est aujourd'hui assurée par notre simulateur, qui reproduit le flux officiel. Le basculement vers la plateforme officielle se fera sans changement côté client. En savoir plus
Cycle de vie d'une facture
La normalisation UBL 2.1 et la signature XAdES sont exécutées de manière synchrone lors de la création : la facture retournée par POST est déjà SIGNED. Seule la transmission DGI est asynchrone.
| Statut | Description |
|---|---|
PENDING | Facture créée, encore modifiable, pas encore signée |
SIGNED | XML UBL 2.1 généré et signé XAdES - contenu verrouillé |
TRANSMITTED | Facture signée envoyée à la DGI, en attente de clairance |
ACCEPTED | DGI a accepté la facture - dgiReference et QR code disponibles |
REJECTED | DGI a rejeté la facture - dgiErrorCode / dgiErrorMessage disponibles |
FAILED | Échec technique de traitement ou de transmission |
CANCELLED | Annulée par l'appelant (uniquement possible depuis PENDING) |
POST/v1/invoices
Crée une nouvelle Invoice, génère le XML UBL 2.1 et le signe immédiatement, puis lance la transmission DGI en arrière-plan.
Request
POST /v1/invoices
Authorization: Bearer efact_sk_xxx
Content-Type: application/json{
"invoiceNumber": "INV-2026-042",
"invoiceDate": "2026-04-07",
"dueDate": "2026-05-07",
"currency": "MAD",
"paymentMethod": "BANK_TRANSFER",
"buyerIce": "002345678901234",
"buyerName": "Société Exemple SARL",
"buyerAddress": "12 Rue Hassan II, Casablanca 20000",
"lines": [
{
"lineNumber": 1,
"description": "Développement application web",
"quantity": 1,
"unitPrice": 12000.00,
"vatRate": 20
},
{
"lineNumber": 2,
"description": "Hébergement mensuel",
"quantity": 1,
"unitPrice": 2000.00,
"vatRate": 20
}
]
}Champs de la requête
Racine
| Champ | Type | Requis | Description |
|---|---|---|---|
invoiceNumber | string | Oui | Numéro de facture, unique par organisation (max 64 caractères) |
invoiceDate | string | Oui | Date d'émission, format YYYY-MM-DD |
currency | string | Oui | Code ISO 4217 (3 lettres majuscules), ex. MAD |
paymentMethod | string | Oui | BANK_TRANSFER, CASH, CHECK, CARD ou OTHER |
lines | array | Oui | Lignes de facturation (minimum 1) |
buyerIce | string | Non | ICE de l'acheteur - exactement 15 chiffres si fourni |
buyerName | string | Non | Raison sociale de l'acheteur |
buyerAddress | string | Non | Adresse de l'acheteur |
dueDate | string | Non | Date d'échéance, format YYYY-MM-DD |
lines[]
| Champ | Type | Requis | Description |
|---|---|---|---|
lineNumber | integer | Oui | Numéro de ligne (base 1, unique dans la facture) |
description | string | Oui | Désignation de la prestation ou du produit (max 512 caractères) |
quantity | number | Oui | Quantité (positive, décimales acceptées) |
unitPrice | number | Oui | Prix unitaire HT dans la devise de la facture (≥ 0) |
vatRate | number | Oui | Taux de TVA en pourcentage : 20 pour 20 % (0, 7, 10, 14, 20) |
unit | string | Non | Unité de mesure libre, ex. HOUR (max 32 caractères) |
Les totaux (amountHt, amountVat, amountTtc) sont calculés côté serveur à partir des lignes - ils ne sont jamais envoyés.
Response 201 Created
{
"code": "OK",
"message": "Invoice created",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"organizationId": "550e8400-e29b-41d4-a716-446655440001",
"invoiceNumber": "INV-2026-042",
"status": "SIGNED",
"issuerIce": "123456789012345",
"issuerName": "Votre Société SARL",
"buyerIce": "002345678901234",
"buyerName": "Société Exemple SARL",
"buyerAddress": "12 Rue Hassan II, Casablanca 20000",
"currency": "MAD",
"paymentMethod": "BANK_TRANSFER",
"amountHt": 14000.00,
"amountVat": 2800.00,
"amountTtc": 16800.00,
"invoiceDate": "2026-04-07",
"dueDate": "2026-05-07",
"dgiReference": null,
"dgiQrCode": null,
"createdAt": "2026-04-07T10:00:00Z",
"updatedAt": "2026-04-07T10:00:00Z"
},
"errors": null
}Erreurs spécifiques
| Code HTTP | Code erreur | Cause |
|---|---|---|
400 | VALIDATION_ERROR | Champ manquant ou mal formaté (buyerIce ≠ 15 chiffres, lines vide...) - détail champ par champ dans errors[] |
409 | INVOICE_NUMBER_EXISTS | invoiceNumber déjà utilisé dans votre organisation |
409 | MISSING_LEGAL_MENTIONS | Mentions légales de votre organisation incomplètes (ICE, IF, RC...) - à compléter dans le dashboard |
GET/v1/invoices/:id
Récupère les détails complets d'une facture.
Request
GET /v1/invoices/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer efact_sk_xxxResponse 200 OK
Une fois la facture acceptée :
{
"code": "OK",
"message": "Invoice found",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"invoiceNumber": "INV-2026-042",
"status": "ACCEPTED",
"buyerIce": "002345678901234",
"buyerName": "Société Exemple SARL",
"currency": "MAD",
"amountTtc": 16800.00,
"dgiReference": "DGI-2026-00042",
"dgiQrCode": "...",
"dgiQrPayload": "...",
"dgiSubmittedAt": "2026-04-07T10:00:05Z",
"dgiValidatedAt": "2026-04-07T10:04:23Z",
"createdAt": "2026-04-07T10:00:00Z",
"updatedAt": "2026-04-07T10:04:23Z"
},
"errors": null
}Retourne 404 INVOICE_NOT_FOUNDsi la facture n'existe pas ou appartient à une autre organisation.
GET/v1/invoices/:id/status
Objet de statut léger, conçu pour suivre la clairance DGI par polling.
Request
GET /v1/invoices/550e8400-e29b-41d4-a716-446655440000/status
Authorization: Bearer efact_sk_xxxResponse 200 OK
{
"code": "OK",
"message": "Status retrieved",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "ACCEPTED",
"dgiReference": "DGI-2026-00042",
"dgiQrPayload": "...",
"dgiErrorCode": null,
"dgiErrorMessage": null,
"dgiSubmittedAt": "2026-04-07T10:00:05Z",
"dgiValidatedAt": "2026-04-07T10:04:23Z",
"dgiRejectedAt": null
},
"errors": null
}Si la facture est REJECTED :
{
"code": "OK",
"message": "Status retrieved",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "REJECTED",
"dgiReference": null,
"dgiErrorCode": "ICE_MISMATCH",
"dgiErrorMessage": "ICE client inconnu dans le registre DGI",
"dgiRejectedAt": "2026-04-07T10:04:23Z"
},
"errors": null
}Interrogez cet endpoint jusqu'à obtenir un statut final (ACCEPTED, REJECTED ou FAILED). Un intervalle de quelques secondes suffit ; le SDK gère le back-off en cas de rate limiting.
GET/v1/invoices
Liste les factures de votre organisation, avec pagination par page.
Request
GET /v1/invoices?status=ACCEPTED&page=0&size=20&sort=createdAt,desc
Authorization: Bearer efact_sk_xxxParamètres
| Paramètre | Type | Description |
|---|---|---|
status | string | Filtrer par statut : PENDING, SIGNED, TRANSMITTED, ACCEPTED, REJECTED, FAILED, CANCELLED |
page | integer | Index de page, base 0 (défaut : 0) |
size | integer | Nombre de résultats par page (défaut : 20) |
sort | string | propriété,asc|desc (défaut : createdAt,desc) |
Response 200 OK
{
"code": "OK",
"message": "Invoices retrieved",
"data": {
"content": [
{ "id": "550e8400-...", "invoiceNumber": "INV-2026-042", "status": "ACCEPTED", ... },
{ "id": "660f9511-...", "invoiceNumber": "INV-2026-041", "status": "ACCEPTED", ... }
],
"page": 0,
"size": 20,
"totalElements": 42,
"totalPages": 3,
"first": true,
"last": false
},
"errors": null
}PUT/v1/invoices/:id
Modifie une facture PENDING. Tous les champs sont optionnels - seuls les champs fournis sont appliqués.
Request
PUT /v1/invoices/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer efact_sk_xxx
Content-Type: application/json
{
"dueDate": "2026-06-30",
"buyerAddress": "Nouvelle adresse, Casablanca"
}Une fois signée ou transmise, une facture est immuable : 409 INVOICE_NOT_EDITABLE sur toute tentative de modification.
DELETE/v1/invoices/:id
Annule une facture PENDING (statut CANCELLED). Une facture signée ou transmise ne peut plus être annulée (409).
Request
DELETE /v1/invoices/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer efact_sk_xxxArtefacts téléchargeables
Chaque facture traitée expose ses artefacts en téléchargement direct :
GET /v1/invoices/{id}/pdf # PDF de la facture (application/pdf)
GET /v1/invoices/{id}/xml # XML UBL 2.1 signé - l'original légal (application/xml)
GET /v1/invoices/{id}/qr # QR code de clairance DGI (image/png)Le XML signé est l'original légal de la facture ; les artefacts sont conservés pendant la durée d'archivage légale de 10 ans.
Questions fréquentes
Comment créer une facture électronique avec l'API EFact ?
Envoyez une requête à l'endpoint de création avec les données en JSON : numéro, date, devise, ICE de l'acheteur et lignes de facturation. EFact recalcule les totaux, génère le XML UBL 2.1, applique la signature XAdES et transmet à la DGI. La réponse contient l'identifiant de la facture et son statut initial.
Peut-on modifier une facture après sa création ?
Une facture reste modifiable tant qu'elle n'est pas signée ni transmise. Dès qu'elle est signée ou soumise à la DGI, son contenu est verrouillé pour garantir l'intégrité fiscale : toute tentative de modification renvoie une erreur 409. Pour corriger une facture transmise, il faut émettre une facture d'avoir ou rectificative.
Comment annuler une facture avec EFact ?
Une facture peut être annulée via l'endpoint d'annulation tant que son statut le permet. L'annulation est tracée dans le journal d'audit. Une facture déjà acceptée par la DGI ne s'efface pas : la régularisation passe par une facture d'avoir, conformément aux règles fiscales marocaines.