Python SDK: coming soon officially. This documentation describes the API as it will be available; official release is being finalized.

DGI simulator: the national DGI e-invoicing platform is not yet open to integrations. Clearance (ACCEPTED / REJECTED status, DGI reference, QR code) currently runs on our simulator, which reproduces the official flow. Moving to the official platform requires no change on the client side. Learn more

Morocco E-Invoicing Python SDK

efact

Official EFact SDK for Python ≥ 3.9, typed with Pydantic v2. All invoice operations are exposed under efact.invoices.*.

In short

The official EFact Python SDK (efact) creates and tracks DGI-compliant electronic invoices in Python. Once published on PyPI, install via pip install efact, initialize with your secret API key efact_sk_ loaded from the environment, and issue invoices: EFact manages UBL 2.1 XML generation, XAdES signatures, and DGI transmission server-side.

Installation

Installation
pip install efact

Initialization

Basic

Basic Initialization
import os
from efact import EFact

# The secret key identifies AND authenticates your organization.
# Never commit it: load it from the environment.
efact = EFact(os.environ["EFACT_SECRET_KEY"])

efact.invoices.create(data)

Signature

Method Signature
efact.invoices.create(data: CreateInvoiceRequest | dict) -> InvoiceResponse

Complete Example

Complete Example
invoice = efact.invoices.create({
    "invoiceNumber": "INV-2026-001",
    "invoiceDate": "2026-06-24",
    "currency": "MAD",
    "paymentMethod": "BANK_TRANSFER",
    "buyerIce": "001234567890123",
    "buyerName": "Client SARL",
    "buyerAddress": "Casablanca",
    "lines": [
        {
            "lineNumber": 1,
            "description": "Consulting services",
            "quantity": 1,
            "unitPrice": 1000,
            "vatRate": 20,
        },
    ],
})

print(invoice.id, invoice.status)

efact.invoices.retrieve(invoice_id)

Retrieve Invoice
invoice = efact.invoices.retrieve("550e8400-e29b-41d4-a716-446655440000")
print(invoice.status, invoice.invoice_number)

Throws EFactNotFoundError (HTTP 404) if the invoice does not exist or does not belong to the tenant.

efact.invoices.retrieve_status(invoice_id)

Retrieve Status
status = efact.invoices.retrieve_status("550e8400-e29b-41d4-a716-446655440000")
print(status.status, status.dgi_reference)

efact.invoices.list(...)

List Invoices
page = efact.invoices.list(page=0, size=20, status="ACCEPTED")
for inv in page.content:
    print(inv.invoice_number, inv.status)

Pagination uses page/size parameters rather than cursor pagination.

efact.invoices.update(invoice_id, data)

Update Invoice
# Only for an invoice at PENDING status - otherwise 409
updated = efact.invoices.update("550e8400-e29b-41d4-a716-446655440000", {
    "dueDate": "2026-07-31",
    "buyerAddress": "New address, Casablanca",
})

efact.invoices.cancel(invoice_id)

Cancel Invoice
# Only for an invoice at PENDING status
cancelled = efact.invoices.cancel("550e8400-e29b-41d4-a716-446655440000")
print(cancelled.status)  # 'CANCELLED'

Error handling

The SDK throws typed errors, all inheriting from EFactError. Always wrap calls in a try/except block:

Error Handling
from efact import (
    EFactApiError,
    EFactAuthError,
    EFactValidationError,
    EFactNotFoundError,
    EFactConflictError,
    EFactConnectionError,
    EFactTimeoutError,
)

try:
    efact.invoices.create(data)
except EFactValidationError as e:
    print("Invalid fields:", e.errors)
except EFactAuthError:
    print("API key missing or invalid.")
except EFactConflictError:
    print("Invoice not editable (status ≠ PENDING).")
except EFactApiError as e:
    print(e.code, e.http_status, e)
except EFactTimeoutError:
    print("Timed out.")
except EFactConnectionError:
    print("Network failure.")

Error hierarchy

ClassHTTP codeWhen
EFactConfigError-Configuration error (before any network call)
EFactAuthError401 / 403API key missing or invalid
EFactNotFoundError404Resource not found (including cross-tenant requests)
EFactValidationError400 / 422Invalid request payload
EFactConflictError409E.g. modifying or cancelling an invoice that is no longer PENDING
EFactApiError5xxServer-side API error
EFactTimeoutError-Request timed out before response was received
EFactConnectionError-Network failure

Idempotent requests (GET, PUT, DELETE) are retried automatically with exponential back-off on timeout, network failure, or HTTP 408/425/429/500/502/503/504 statuses, up to max_retries times. POST (creation) is never retried automatically.

Django Integration

views.py
# views.py
import os
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from efact import EFact

efact = EFact(os.environ["EFACT_SECRET_KEY"])

@require_POST
def create_invoice(request):
    import json
    body = json.loads(request.body)

    invoice = efact.invoices.create({
        "invoiceNumber": body["invoiceNumber"],
        "invoiceDate": body["invoiceDate"],
        "currency": "MAD",
        "paymentMethod": "BANK_TRANSFER",
        "buyerIce": body.get("buyerIce"),
        "buyerName": body.get("buyerName"),
        "buyerAddress": body.get("buyerAddress"),
        "lines": body["lines"],
    })

    return JsonResponse({"id": invoice.id, "status": invoice.status})

Invoice statuses

The status reflects the complete lifecycle, aligned with the backend:

StatusMeaning
PENDINGCreated, editable, not yet transmitted
SIGNEDSigned (XAdES), awaiting transmission
TRANSMITTEDSubmitted to the DGI platform, awaiting clearance
ACCEPTEDValidated by the DGI - reference and QR code available
REJECTEDRejected by the DGI - inspect errorCode / errorMessage
FAILEDTechnical processing failure
CANCELLEDCancelled (only possible while PENDING)

Frequently asked questions

How do I install the EFact Python SDK?

The efact package is currently being published to PyPI; once available, install it with pip install efact and import the client (from efact import EFact). Initialize it with your secret key loaded from the environment, then create and track invoices via its methods. The SDK manages UBL 2.1 generation, signing, and transmission via the EFact server.

Is the EFact Python SDK compatible with web frameworks?

Yes. The Python SDK works in any application, including web frameworks such as Django or FastAPI: instantiate the client at startup, load the API key from an environment variable, and call its methods from views or background tasks to create and track invoices.

How do I track an invoice status with the Python SDK?

After creation, query the invoice status using the client's dedicated method with a polling interval of 2 to 5 seconds. Once the status is ACCEPTED, you can retrieve the DGI reference and artifacts. In production, webhooks replace polling for final status notifications.