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

1
Pending
Créée
2
Signed
Signée XAdES
3
Transmitted
DGI
Accepted
Validé par la DGI
Rejected
Rejeté par la DGI

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.

StatutDescription
PENDINGFacture créée, encore modifiable, pas encore signée
SIGNEDXML UBL 2.1 généré et signé XAdES - contenu verrouillé
TRANSMITTEDFacture signée envoyée à la DGI, en attente de clairance
ACCEPTEDDGI a accepté la facture - dgiReference et QR code disponibles
REJECTEDDGI a rejeté la facture - dgiErrorCode / dgiErrorMessage disponibles
FAILEDÉchec technique de traitement ou de transmission
CANCELLEDAnnulé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

Request
POST /v1/invoices
Authorization: Bearer efact_sk_xxx
Content-Type: application/json
Body
{
  "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

ChampTypeRequisDescription
invoiceNumberstringOuiNuméro de facture, unique par organisation (max 64 caractères)
invoiceDatestringOuiDate d'émission, format YYYY-MM-DD
currencystringOuiCode ISO 4217 (3 lettres majuscules), ex. MAD
paymentMethodstringOuiBANK_TRANSFER, CASH, CHECK, CARD ou OTHER
linesarrayOuiLignes de facturation (minimum 1)
buyerIcestringNonICE de l'acheteur - exactement 15 chiffres si fourni
buyerNamestringNonRaison sociale de l'acheteur
buyerAddressstringNonAdresse de l'acheteur
dueDatestringNonDate d'échéance, format YYYY-MM-DD

lines[]

ChampTypeRequisDescription
lineNumberintegerOuiNuméro de ligne (base 1, unique dans la facture)
descriptionstringOuiDésignation de la prestation ou du produit (max 512 caractères)
quantitynumberOuiQuantité (positive, décimales acceptées)
unitPricenumberOuiPrix unitaire HT dans la devise de la facture (≥ 0)
vatRatenumberOuiTaux de TVA en pourcentage : 20 pour 20 % (0, 7, 10, 14, 20)
unitstringNonUnité 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

Response
{
  "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 HTTPCode erreurCause
400VALIDATION_ERRORChamp manquant ou mal formaté (buyerIce ≠ 15 chiffres, lines vide...) - détail champ par champ dans errors[]
409INVOICE_NUMBER_EXISTSinvoiceNumber déjà utilisé dans votre organisation
409MISSING_LEGAL_MENTIONSMentions 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

Request
GET /v1/invoices/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer efact_sk_xxx

Response 200 OK

Une fois la facture acceptée :

Accepted Response
{
  "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

Request
GET /v1/invoices/550e8400-e29b-41d4-a716-446655440000/status
Authorization: Bearer efact_sk_xxx

Response 200 OK

Accepted Status
{
  "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 :

Rejected Status
{
  "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

Request
GET /v1/invoices?status=ACCEPTED&page=0&size=20&sort=createdAt,desc
Authorization: Bearer efact_sk_xxx

Paramètres

ParamètreTypeDescription
statusstringFiltrer par statut : PENDING, SIGNED, TRANSMITTED, ACCEPTED, REJECTED, FAILED, CANCELLED
pageintegerIndex de page, base 0 (défaut : 0)
sizeintegerNombre de résultats par page (défaut : 20)
sortstringpropriété,asc|desc (défaut : createdAt,desc)

Response 200 OK

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

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

Request
DELETE /v1/invoices/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer efact_sk_xxx

Artefacts téléchargeables

Chaque facture traitée expose ses artefacts en téléchargement direct :

Artifacts
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.