Todos los posts

Cómo facturar DIAN nativo en Colombia con FastAPI + MATIAS API (guía completa 2026)

2026-05-1012 min· Juan José Trujillo Cardozo

La facturación electrónica en Colombia tiene fama de ser un laberinto. No es falsa reputación: el portal de la DIAN cambia sin avisar, el flujo OAuth2 expira en momentos críticos, y la nomenclatura de municipios que usa el esquema XML no coincide con ningún listado público obvio. Este artículo documenta cómo lo resolvimos en TRUJO TECHNOLOGIES usando FastAPI + MATIAS API (LOPEZSOFT), con código real del stack en producción.

El problema con la facturación directa vía DIAN

La DIAN ofrece dos caminos: integración directa como operador habilitado (requiere certificación, proceso largo, costoso) o usar un operador tecnológico habilitado. MATIAS es uno de los operadores habilitados con API REST moderna. Para PyMEs colombianas que no tienen seis meses para certificarse, es la ruta correcta.

Lo que MATIAS resuelve: generación del XML UBL 2.1, firma digital con el certificado del contribuyente, envío al hub DIAN, gestión del CUFE (Código Único de Factura Electrónica) y el QR de verificación. Lo que tú resuelves: el modelo de datos de tu negocio, la lógica de retefuente, y la sincronización del catálogo de municipios.

Sincronización del catálogo de 1.122 municipios

El esquema DIAN requiere el código de municipio en formato específico. El catálogo oficial tiene 1.122 municipios (todos los del país). En TRUJO lo sincronizamos contra nuestra base de datos PostgreSQL 16 con un cron semanal. La lógica es un GET autenticado al endpoint de catálogos de MATIAS, seguido de un UPSERT por código de municipio. Usamos ON CONFLICT (codigo) DO UPDATE para mantener el catálogo actualizado sin duplicados.

El motivo del cron semanal: la DIAN actualiza los códigos cuando hay cambios administrativos territoriales. En la primera sincronización obtuvimos exactamente 1.122 registros.

El flujo OAuth2 con MATIAS

El token expira cada hora. En un sistema multi-tenant como TRUJO, donde múltiples comerciantes tienen sus propias credenciales DIAN, necesitamos un cache por tenant. Usamos Redis con TTL de 3.540 segundos (60 segundos menos que el tiempo de expiración real) para evitar usar un token vencido. La clave del cache es tenant_id + el identificador del cliente MATIAS, garantizando aislamiento entre tenants.

Este patrón evita hacer un request de autenticación en cada factura. Con un pool de conexiones de 20 workers en FastAPI, sin el cache el sistema haría hasta 20 requests de autenticación por segundo en hora pico.

Modelo de datos de la factura

El modelo Pydantic de la factura incluye los campos obligatorios DIAN: número con prefijo (ej: "FE"), NIT del cliente con validación de dígito de verificación, código de municipio DIAN, fecha, líneas de detalle con cantidad, valor unitario, descuento, porcentaje de IVA, y forma de pago (contado o crédito).

La validación del NIT limpia separadores y verifica que sea solo dígitos. El código de municipio se valida contra el catálogo sincronizado antes de enviar a MATIAS.

Retefuente: el detalle que más duele

La retefuente en Colombia depende del tipo de servicio, el valor de la transacción y si el comprador es agente retenedor. Varios de nuestros clientes institucionales lo son. Esto significa que retienen el 3.5% de honorarios en la fuente antes de pagar.

La lógica: si el comprador no es agente retenedor, retefuente = 0. Si el subtotal es menor a la base mínima de 4 UVT (aproximadamente $978.000 COP para 2026), retefuente = 0. Si supera ese umbral y es agente retenedor, se aplica 3.5% para honorarios o 1% para compras de bienes.

Endpoint de emisión en FastAPI

El endpoint POST /api/v1/invoices/emit sigue cuatro pasos: obtener token MATIAS para el tenant desde el cache Redis, construir el payload en formato MATIAS a partir del modelo de la factura, enviar a la API de MATIAS con timeout de 30 segundos, y persistir en PostgreSQL con el CUFE y número DIAN retornados. El response incluye el invoice_id interno, el CUFE, el número oficial asignado por DIAN, y la URL del PDF.

Lecciones aprendidas en producción

El error más común: timeout en hora pico del 28 al 31 de cada mes. La DIAN recibe millones de facturas en esos días y el hub se satura. Solución: queue asíncrono con Celery y reintentos con backoff exponencial (1s, 2s, 4s, máximo 3 intentos). El cliente ve el estado "procesando" mientras el worker reintenta en background.

El segundo error: municipios con tilde que no coincidían con el catálogo. Solucionado con normalización Unicode (NFKD decomposition, strip combining chars, uppercase) antes de comparar nombres de municipio.

Números reales

El tiempo promedio de aceptación DIAN vía MATIAS es de 1.8 segundos en horario normal. El único downtime significativo fue el 31 de marzo de 2026 (último día del trimestre fiscal): 23 minutos con timeouts, resueltos automáticamente por el queue de reintentos sin intervención manual. El catálogo de 1.122 municipios está verificado y actualizado semanalmente.

Si estás construyendo facturación electrónica en Colombia y tienes preguntas sobre el stack, escríbenos a [email protected].