Saltar al contenido principal

Normativa MH 2.0 (DTE v2)

El MH publicó la Normativa de Cumplimiento de DTE versión 2.0 el 25 de mayo de 2026. Todas las empresas emisoras deben migrar a los esquemas v2 antes del 1 de diciembre de 2026.

La migración la realiza Ocote en el servidor, empresa por empresa. Para tu integración es transparente en la mayoría de los casos: no necesitás cambiar nada para seguir emitiendo.

Lo esencial

Cuando tu empresa esté migrada a v2, podés mantener tu integración actual tal cual. El único cambio visible es el campo distrito, y es opcional gracias a un fallback automático.

Qué NO cambia

  • URLs de los endpoints (/invoice, /suex, /credit-memo, /invalidate/...).
  • Autenticación (Authorization: Bearer odt_...).
  • Métodos HTTP y códigos de estado.
  • Forma de los responses (mismos campos).
  • Idempotencia por external_ref.
  • Campos de payload existentes: todo lo que funcionaba, sigue funcionando.
  • Formato del control_number (DTE-XX-XXXXXXXX-NNNNNNNNNNNNNNN, 31 caracteres).

El único cambio visible: el distrito (CAT-008)

A partir de v2, el MH exige que cada receptor (cliente) y el emisor declaren su distrito (catálogo CAT-008). Hasta ahora se enviaba solo department y municipality; en adelante el distrito es requerido por el MH.

Para preservar la compatibilidad, el API lo trata como opcional en el payload:

  • Si no envías distrito, el API usa como fallback el distrito de la empresa emisora para el cliente. El documento igual es aceptado por el MH.
  • Si envías distrito, se usa exactamente el que indiques.
Recomendación

El fallback evita fallos de emisión, pero no representa la dirección real del cliente; con el tiempo la representación impresa o en PDF puede mostrar un distrito que no corresponde. Se recomienda actualizar tu integración para enviar el distrito real de cada cliente antes de que tu empresa migre a v2.

Cómo enviar el distrito

Opción A — district_code + department + municipality (recomendada). Usa el código oficial de distrito CAT-008 (2 dígitos). Se requieren los tres valores para resolver un distrito único, porque un mismo código puede existir bajo distintos municipios.

"customer": {
"name": "SERVICIOS DEMO, S.A. DE C.V.",
"nit": "06141234567890",
"nrc": "1234567",
"document_type": "36",
"address": "Boulevard de ejemplo, San Salvador",
"activity_code": "78300",
"district_code": "17",
"department": "06",
"municipality": "22"
}

Opción B — district (UUID). Resolvé el UUID una vez con GET /catalogs/districts/search?q=<nombre> y guardalo; el API deriva department y municipality automáticamente.

Opción C — solo department + municipality (legado). Si aún no tenés datos de distrito, podés mantener tu payload actual; el sistema usará el distrito de la empresa emisora como fallback.

Descubrir los valores de distrito

GET /catalogs/districts/search?q=<nombre> devuelve el distrito con los códigos padre listos para usar:

{
"results": [
{
"id": "00000000-0000-0000-0000-000000000000",
"name": "Distrito de Ejemplo",
"text": "Distrito de Ejemplo - MUNICIPIO ESTE, DEPARTAMENTO",
"municipality_code": "22",
"department_code": "06"
}
]
}

Comportamiento de los correlativos (informativo)

v2 introduce reglas más estrictas del MH sobre cómo se asignan los correlativos (los 15 dígitos finales del numeroControl). No hay nada que hacer en tu integración: se aplica del lado del servidor. Según la regla 7.1.2, la empresa elige uno de tres modos:

ModoComportamiento
Centralizado (por defecto)Una única serie global por empresa y ejercicio fiscal; todos los tipos de documento y puntos de venta comparten el contador.
Por establecimientoUna serie por sucursal; los tipos de documento y puntos de venta de esa sucursal comparten el contador.
Por PV + tipo de documentoUna serie por combinación (punto de venta, tipo de documento). Requiere autorización expresa del MH.
Correlativos con saltos

Bajo los modos Centralizado o Por establecimiento podés observar correlativos que "saltan" (p. ej. 100, 101 y luego 150) porque la empresa también emitió CCF, notas de crédito o sujetos excluidos en medio. Es esperado y cumple la normativa. La idempotencia por external_ref no cambia.

Manejo de errores

Antes, si el módulo de firma tenía un error interno, el API podía devolver success: true con un control_number mal formado (con un timestamp Unix), y el MH rechazaba esos documentos de forma asíncrona.

Ahora, cualquier fallo interno durante la firma devuelve un HTTP 500 claro de inmediato. Asegurate de que tu lógica de reintentos maneje respuestas 5xx, además del manejo de rechazos del MH (códigos 004, 024, 096, etc.).

Checklist de migración

Cuando Ocote te notifique que una empresa específica migrará a v2:

  1. Audita tu base de clientes: identifica los que tienen department y municipality pero sin distrito; busca el distrito real vía /catalogs/districts/search.
  2. Actualiza tus generadores de payload: incluí district_code (más department y municipality) en customer y subject.
  3. Prueba en el ambiente de test de la empresa antes de pasar a producción (el test está aislado de producción para ese NIT).
  4. Confirma que tu manejador de errores trata los HTTP 5xx como reintentables.

Nada más necesita cambiar: autenticación, reintentos, parsing de errores y esquema de almacenamiento se mantienen igual.

Ver también