This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
FiscalAPI Python SDK - Official SDK for integrating with FiscalAPI, Mexico's electronic invoicing (CFDI 4.0) and fiscal services platform. Simplifies integration with SAT (Mexico's tax authority) for invoice creation, tax certificate management, payroll invoices (CFDI de Nomina), and bulk downloads.
# Install dependencies
pip install -r requirements.txt
# Build distribution packages
pip install setuptools wheel twine build
python -m build
# Verify packages before publishing
twine check dist/*
# Publish to PyPI (requires PYPI_API_TOKEN)
twine upload --username __token__ --password $PYPI_API_TOKEN dist/*
# Clean build artifacts
rm -rf dist build fiscalapi.egg-infoVersion management: Version is defined in setup.py (line 5: VERSION = "X.Y.Z"). CI/CD is manually triggered via workflow_dispatch.
FiscalApiClient (Facade)
│
├── FiscalApiSettings (Configuration)
│
└── Services (all inherit from BaseService)
├── invoice_service.py → Invoice CRUD, PDF, XML, cancellation, SAT status
├── people_service.py → Person/entity management
│ ├── employee_service.py → Employee data (sub-service for payroll)
│ └── employer_service.py → Employer data (sub-service for payroll)
├── product_service.py → Product/service catalog
├── tax_file_service.py → CSD/FIEL certificate uploads
├── api_key_service.py → API key management
├── catalog_service.py → SAT catalog searches
├── stamp_service.py → Stamp/validation credit ledger transactions
├── sat_validation_service.py → SAT validations (structure, seals, status, 69-B blacklists)
├── manifest_service.py → Manifest signing with the taxpayer's FIEL
└── download_*_service.py → Bulk download management
Entry Point Pattern:
from fiscalapi import FiscalApiClient, FiscalApiSettings
settings = FiscalApiSettings(api_url="...", api_key="...", tenant="...")
client = FiscalApiClient(settings=settings)
# Access services through client
client.invoices.create(invoice)
client.people.get_list(page_num, page_size)
client.stamps.get_list(page_num, page_size)
client.sat_validations.validate(request)
client.manifests.sign(request)Located in fiscalapi/models/:
- common_models.py - Base DTOs:
ApiResponse[T],PagedList[T],ValidationFailure,FiscalApiSettings - fiscalapi_models.py - Domain models:
Invoice,Person,Product,TaxFile, payroll complements, stamp transactions and the ledger enums (CreditType,StampTransactionType,StampTransactionStatus) - comercio_exterior_models.py - Comercio Exterior complement, reached through
InvoiceComplement.comercio_exterior - carta_porte_models.py - Carta Porte complement, reached through
InvoiceComplement.carta_porte - manifest_models.py -
SignManifestRequest/SignManifestResponse - sat_validation_models.py - SAT validation models and the
SatValidationTypeIds/SatValidationStatusIdsid enums
Key Pattern - Field Aliasing: Models use Pydantic Field(alias="...") for API JSON field mapping. When serializing, use by_alias=True and exclude_none=True.
All types are exported from the main package (fiscalapi/__init__.py):
from fiscalapi import Invoice, Person, Product, FiscalApiClient, ApiResponseAlso available via submodules:
from fiscalapi.models import Invoice, Person
from fiscalapi.services import InvoiceService, StampService- By References - Use pre-created object IDs (faster, less data transfer)
- By Values - Send all field data directly (self-contained, no prior setup)
- Service method receives domain object
BaseService.send_request()serializes to JSON (excludes None, uses aliases)- URL constructed:
{base_url}/api/{version}/{endpoint} - Headers added:
X-TENANT-KEY,X-API-KEY,X-TIME-ZONE - Response deserialized to
ApiResponse[T]with typed data
- Development (localhost/127.0.0.1): SSL verification disabled
- Production: Uses certifi certificate store
fiscalapi/__init__.py- Central exports for every public type (models + services); keep__all__free of duplicatesfiscalapi/services/base_service.py- HTTP client, serialization, response handlingfiscalapi/services/fiscalapi_client.py- Main client facadesetup.py- Package metadata, version, and dependencies
All example files are located in the examples/ directory:
examples/examples.py- General usage examples (all invoice types)examples/ejemplos-facturas-de-nomina.py- Payroll invoice examples (13 types)examples/ejemplos-facturas-de-complemento-pago.py- Payment complement examplesexamples/ejemplos-timbres.py- Stamp and validation credit examplesexamples/ejemplos-validaciones-sat.py- SAT validation examplesexamples/ejemplos-firma-manifiestos.py- Manifest signing examplesexamples/ejemplos-factura-impuestos-locales-valores.py- Local taxes examples (by values)examples/ejemplos-factura-impuestos-locales-referencias.py- Local taxes examples (by references)examples/ejemplos-factura-carta-porte-valores.py- Carta Porte examples (by values)examples/ejemplos-factura-carta-porte-referencias.py- Carta Porte examples (by references)examples/ejemplos-factura-comercio-exterior-valores.py- Comercio Exterior examples (by values)examples/ejemplos-factura-comercio-exterior-referencias.py- Comercio Exterior examples (by references)
There is no unit test suite: changes are verified by running these examples against a live API.
- Python >= 3.9 (CI/CD uses Python 3.9.13)
- pydantic >= 2.0.0 (validation & serialization)
- requests >= 2.0.0 (HTTP client)
- urllib3 >= 1.0.0
- certifi >= 2023.0.0 (certificate store for non-local hosts)
# Create virtual environment with Python 3.9+
python -m venv venv
# Activate (Windows)
.\venv\Scripts\activate
# Activate (Linux/Mac)
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt- Use
model_config = ConfigDict(...)instead ofclass Config: - Use
list[T]anddict[K,V](Python 3.9+ built-in generics) instead ofList[T]andDict[K,V] - Use
default_factory=listfor mutable defaults, neverdefault=[] - All Field() calls should have explicit
default=...for required fields - Do not use
json_encoders: it is deprecated in Pydantic v2 and redundant here, becauseBaseService.send_requestserializes withmodel_dump(mode="json", ...), which already rendersDecimalas a JSON string anddatetimeas ISO-8601
Monetary and rate fields are decimal.Decimal, never float. The SAT validates the scale
(number of decimal places) of several CFDI attributes and rejects comprobantes that do not match,
so trailing zeros must survive serialization. Build values from string literals with the exact
scale (Decimal("0.160000"), not Decimal(0.16)); model_dump(mode="json") preserves them.
Known rejections caused by the wrong scale: CFDI40179 on taxRate (6 decimals) and CCE122
on valorDolares (feeds the computed TotalUSD, 2 decimals).
- All service methods must have return type annotations
- Use
Optional[T]only for truly optional fields ApiResponse[T]supports any type T (not just BaseModel subclasses)
- Create service class inheriting from
BaseServiceinfiscalapi/services/ - Export from
fiscalapi/services/__init__.py - Export from
fiscalapi/__init__.py - Add an attribute to
FiscalApiClient.__init__(the facade uses plain attributes, not properties)
- API Documentation: https://docs.fiscalapi.com
- Test Certificates: https://docs.fiscalapi.com/recursos/descargas
- Postman Collection: https://documenter.getpostman.com/view/4346593/2sB2j4eqXr