DGMaxClient
Python SDK for the DGMax API — electronic fiscal document (e-CF) processing for the Dominican Republic.
Build, validate, and submit e-CF payloads (E31–E47) against DGII's schema with typed Pydantic models, automatic retries, and a clean resource-oriented API. Skip the XSD, the hand-built JSON, and the DGII error decoding.
Installation
pip install dgmaxclient
Quick Start
from dgmaxclient import (
DGMaxClient,
DocumentCreateRequest,
CompanyRef,
ECFPayload,
ECFEncabezado,
ECFIdDoc,
ECFEmisor,
ECFComprador,
ECFTotales,
ECFDetallesItems,
ECFItem,
)
# Initialize the client
client = DGMaxClient(api_key="dgmax_xxx")
# List companies
companies = client.companies.list()
for company in companies.results:
print(f"{company.name} ({company.rnc})")
# Create an invoice (E32) — fully typed with IDE autocomplete
invoice = client.invoices.create(
DocumentCreateRequest(
company=CompanyRef(id="your-company-uuid"),
ecf=ECFPayload(
encabezado=ECFEncabezado(
id_doc=ECFIdDoc(tipo_ecf=32, e_ncf="E320000000001"),
emisor=ECFEmisor(
rnc_emisor="123456789",
razon_social_emisor="Mi Empresa SRL",
fecha_emision="15-01-2026",
),
comprador=ECFComprador(
razon_social_comprador="Cliente Final",
),
totales=ECFTotales(
monto_gravado_i1="847.46",
itbis1="18",
total_itbis1="152.54",
total_itbis="152.54",
monto_gravado_total="847.46",
monto_total="1000.00",
),
),
detalles_items=ECFDetallesItems(item=[
ECFItem(
numero_linea="1",
indicador_facturacion=1,
nombre_item="Servicio de consultoría",
cantidad_item="1",
precio_unitario_item="847.46",
monto_item="847.46",
),
]),
),
)
)
print(f"Invoice created: {invoice.encf}")
print(f"Status: {invoice.status}") # DocumentStatus.REGISTERED → PROCESSING → COMPLETED
Architecture
DGMaxClient uses the Facade Pattern with Composition to provide a clean, namespaced API. The main client acts as a single entry point that composes multiple resource objects, each responsible for a specific API domain.
┌─────────────────┐
│ DGMaxClient │ ◄── Facade (single entry point)
├─────────────────┤
│ .companies │───► CompaniesResource
│ .invoices │───► InvoicesResource
│ .purchases │───► PurchasesResource
│ .credit_notes │───► CreditNotesResource
│ ... │───► (other resources)
└─────────────────┘
│
▼
HTTP/Auth layer (shared)
This pattern enables:
- Clean API:
client.companies.list(),client.invoices.create({...}) - Shared configuration: All resources use the same authentication and settings
- Separation of concerns: Each resource handles its own domain logic
- Discoverability: IDE autocomplete works seamlessly
Features
Document Types (E31-E47)
The SDK supports all Dominican Republic electronic fiscal document types:
| Type | Resource | Description |
|---|---|---|
| E31 | client.fiscal_invoices |
Factura de Crédito Fiscal Electrónica |
| E32 | client.invoices |
Factura de Consumo Electrónica |
| E33 | client.debit_notes |
Nota de Débito Electrónica |
| E34 | client.credit_notes |
Nota de Crédito Electrónica |
| E41 | client.purchases |
Comprobante Electrónico de Compras |
| E43 | client.minor_expenses |
Comprobante Electrónico para Gastos Menores |
| E44 | client.special_regimes |
Comprobante Electrónico para Regímenes Especiales |
| E45 | client.governmental |
Comprobante Electrónico Gubernamental |
| E46 | client.exports |
Comprobante Electrónico para Exportaciones |
| E47 | client.payments_abroad |
Comprobante Electrónico para Pagos al Exterior |
Company Management
from dgmaxclient import CompanyCreate, CertificateCreate
# List companies
companies = client.companies.list()
# Get a specific company
company = client.companies.get("company-uuid")
# Create a company with certificate
company = client.companies.create(CompanyCreate(
name="Mi Empresa SRL",
trade_name="Mi Empresa",
rnc="123456789",
address="Calle Principal #123",
certificate=CertificateCreate(
name="certificate",
extension="p12",
content="base64-encoded-certificate",
password="certificate-password"
)
))
# Update a company
company = client.companies.update("company-uuid", {
"phone": "809-555-1234",
"email": "info@miempresa.com"
})
Document Operations
from dgmaxclient import DocumentFilters, DocumentStatus, PaginationParams
# List documents with pagination
invoices = client.invoices.list(
params=PaginationParams(limit=50, offset=0)
)
# List documents with filters
invoices = client.invoices.list(
filters=DocumentFilters(
status=DocumentStatus.COMPLETED,
date_from="2024-01-01",
date_to="2024-12-31",
search="123456789" # Search by RNC or eNCF
)
)
# Get a specific document
invoice = client.invoices.get("document-uuid")
# Create a document (see Quick Start for typed payload example)
invoice = client.invoices.create(payload)
Received Documents (Receptor Module)
# List received documents
received = client.received_documents.list()
# List with filters
from dgmaxclient import ReceivedDocumentFilters
received = client.received_documents.list(
filters=ReceivedDocumentFilters(
status="PENDING",
date_from="2024-01-01",
)
)
# Approve a received document
response = client.received_documents.approve("document-uuid")
if response.success:
print(f"Document approved: {response.approval_id}")
# Reject a received document
response = client.received_documents.reject(
"document-uuid",
rejection_reason="Invoice amount does not match purchase order"
)
# List commercial approvals received
approvals = client.received_documents.list_commercial_approvals()
Error Handling
The SDK provides a comprehensive exception hierarchy:
from dgmaxclient import (
DGMaxError,
DGMaxAuthenticationError,
DGMaxValidationError,
DGMaxRequestError,
DGMaxServerError,
DGMaxTimeoutError,
DGMaxConnectionError,
DGMaxRateLimitError,
)
try:
invoice = client.invoices.create(payload)
except DGMaxAuthenticationError as e:
print(f"Authentication failed: {e.message}")
print(f"Status code: {e.status_code}")
except DGMaxValidationError as e:
print(f"Validation error: {e.message}")
if e.response:
print(f"Details: {e.response}")
except DGMaxServerError as e:
print(f"Server error (will retry): {e.message}")
except DGMaxTimeoutError:
print("Request timed out")
except DGMaxConnectionError:
print("Connection error - check your network")
except DGMaxRateLimitError as e:
print(f"Rate limited. Retry after: {e.retry_after}s")
except DGMaxError as e:
print(f"General error: {e.message}")
Configuration
# Custom base URL (e.g. dev environment)
client = DGMaxClient(
api_key="dgmax_xxx",
base_url="https://dev.dgmax.do",
)
# Custom timeout (default: 30 seconds)
client = DGMaxClient(
api_key="dgmax_xxx",
timeout=60,
)
Automatic Retries
The SDK automatically retries requests on:
- Server errors (5xx)
- Connection errors
- Timeout errors
Retries use exponential backoff with jitter (max 3 attempts).
Type Safety
All responses are validated with Pydantic models:
from dgmaxclient import ElectronicDocument, DocumentStatus
invoice: ElectronicDocument = client.invoices.get("uuid")
# Type-safe access
print(invoice.id)
print(invoice.encf)
print(invoice.status) # DocumentStatus enum
if invoice.status == DocumentStatus.COMPLETED:
print("Document processed successfully")
Typed ECF submodels
Nested ECF fields — tabla_sub_descuento, otra_moneda_detalle, retencion, otra_moneda, transporte, informaciones_adicionales, subtotales, descuentos_o_recargos, paginacion, tabla_formas_pago, tabla_impuesto_adicional, tabla_telefono_emisor, mineria — are strongly typed. Typos, wrong nesting, and shape errors raise ValidationError at construction time instead of reaching DGII.
Dict-in still works. Build sub-objects as plain dicts — the SDK coerces them into the typed classes on the way in, so existing call sites get validation without changes:
ECFItem(
numero_linea="1",
indicador_facturacion=1,
nombre_item="Servicio",
monto_item="1800.00",
tabla_sub_descuento={
"sub_descuento": [
{"tipo_sub_descuento": "%", "sub_descuento_porcentaje": "10.00"},
],
},
otra_moneda_detalle={
"precio_otra_moneda": "100.00",
"descuento_otra_moneda": "10.00",
"monto_item_otra_moneda": "90.00",
},
)
# Typos and wrong shapes now fail fast:
ECFItem(..., tabla_sub_descuento=[{"sub_descuento_porcentaje": 10.0}])
# → ValidationError: tabla_sub_descuento must be a dict with sub_descuento key
Numeric inputs are coerced. Money fields (monto_item, itbis1, monto_total, precio_unitario_item, ...) and line numbers accept int, float, Decimal, or str, and serialize to the DGII-compliant string pattern automatically:
from decimal import Decimal
ECFTotales(monto_total=1000, itbis1=Decimal("152.54"))
# → monto_total="1000.00", itbis1="152.54"
ECFItem(numero_linea=1, ...) # → numero_linea="1"
Requirements
- Python 3.10+
- api-client >= 1.3.1
- pydantic >= 2.0
- tenacity >= 8.0
- requests >= 2.28.0
License
MIT License - see LICENSE file for details.
Metadata
Release files for dgmaxclient 1.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dgmaxclient-1.7.0.tar.gz | 76.7 kB | Without provenance | Details for dgmaxclient-1.7.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dgmaxclient-1.7.0-py3-none-any.whl | Python 3 | none | any | Details for dgmaxclient-1.7.0-py3-none-any.whl |
Total release size: 141.3 kB
Release files / dgmaxclient-1.7.0.tar.gz
| Download URL | dgmaxclient-1.7.0.tar.gz |
|---|---|
| Size | 76.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83e8eeff28294b130e8e714e4e15eeef0b50a2deba83af97e31fa7c9d2c32b1e
|
|
BLAKE2b-256 checksum How to use checksums |
0f6056e0d830a4fdcbfe4eff845b9153dbac83b077a53366c29653ba07f1a97e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.0
|
Release files / dgmaxclient-1.7.0-py3-none-any.whl
| Download URL | dgmaxclient-1.7.0-py3-none-any.whl |
|---|---|
| Size | 64.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b82ce51e95d1922bc8bf7bbdad45e9289014b0adab1af9d47214d294b0bb5c58
|
|
BLAKE2b-256 checksum How to use checksums |
350759e549e7e578589d76be880caafa20a2f7b7482fedafc301780e63387256
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.0
|
Security for dgmaxclient 1.7.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.