Códigos MH
Cuando el MH rechaza o anota un documento, devuelve códigos y observaciones que el API te expone en dte_state, codigo_msg, y observaciones. Esta página documenta los más frecuentes.
Estados del documento (dte_state)
| Estado | Significado | Acción |
|---|---|---|
"" (vacío) | Todo bien — MH aceptó con sello. | Ninguna. |
"PROCESADO" | MH recibió y procesó exitosamente. | Ninguna. |
"RECHAZADO" | MH rechazó el documento. No se generó sello. | Corregir datos y reintentar con mismo external_ref. |
"OBSERVADO" | MH aceptó con observaciones. El documento es válido pero tiene advertencias. | Revisar observaciones — informativo, no requiere acción obligatoria. |
Cuando rejected: true, dte_state es "RECHAZADO" (u ocasionalmente "OBSERVADO" si el MH lo trató como rechazo).
Códigos de mensaje (codigo_msg)
El MH devuelve un código numérico breve (típicamente 2-3 dígitos) que identifica el tipo de error.
| Código | Categoría | Descripción |
|---|---|---|
001 | Proceso | Documento aceptado. |
002 | Proceso | Documento procesado con observaciones. |
004 | Datos | Código de generación ya ha sido procesado. |
005 | Autorización | Emisor no autorizado. |
006 | Firma | Firma digital inválida o vencida. |
089 | Validación | Código de actividad económica inválido. |
092 | Validación | Tipo de documento del receptor inválido. |
094 | Validación | Número de documento del receptor inválido. |
095 | Validación | NRC del receptor inválido o inexistente. |
096 | Schema | Error al construir schema / JSON no cumple formato requerido. Suele indicar campo faltante o tipo incorrecto en el payload. |
097 | Validación | Fecha de emisión inválida. |
098 | Validación | Monto total no coincide con suma de líneas. |
099 | Validación | Retención calculada no coincide con normativa. |
096 es el más comúnIndica que el JSON generado no cumple el esquema JSON Schema del MH. El mensaje en observaciones te dice qué campo falla. Ejemplos reales:
"Campo #/receptor/correo no cumple el formato requerido"→ email del cliente inválido"El valor del campo #/resumen/totalNoGravado debe ser tipo numero"→ dato numérico mal tipado"Campo #/receptor/nrc es requerido"→ falta NRC en CCF
Observaciones frecuentes
observaciones es un array de strings. Cada string es un mensaje independiente del MH — puede venir uno solo o varios simultáneos. Muestra todos al usuario final; no filtres.
Categoría: Receptor (cliente)
"Campo #/receptor/correo no cumple el formato requerido"
Email del cliente malformado. Valida con regex ^[\w\.-]+@[\w\.-]+\.\w+$ antes de enviar, o pide al usuario que lo verifique.
"Campo #/receptor/nrc no cumple el formato requerido"
NRC debe ser un string numérico de 2-8 dígitos. Sin guiones.
"El valor del campo #/receptor/codActividad no está en el catálogo"
El activity_code no existe en el catálogo CAT-019 del MH. Consulta Catálogos MH para buscar el correcto.
"Combinación departamento/municipio inválida"
El department + municipality no corresponde a un municipio real. Ver el catálogo vivo /catalogs/departments.
Categoría: Montos
"El valor del campo #/resumen/totalNoGravado debe ser tipo numero"
Un monto que debería ser numérico viene como string u otro tipo. Caso poco común — si aparece, reporta a Ocote con el external_ref.
"Suma de líneas no coincide con total declarado"
Error de cálculo interno; raro pero posible. Reporta a Ocote con el external_ref.
Categoría: Emisor
"Firma digital vencida"
El certificado del emisor caducó. No es un error del cliente API — Ocote debe actualizar el firmador en la configuración de la empresa. Contacta a Ocote.
"Emisor no autorizado"
La empresa configurada no está habilitada para emitir el tipo de DTE solicitado. Contacta a Ocote.
Cómo manejar rechazos en tu integración
r = requests.post(".../invoice", json=payload, headers=h)
data = r.json()
if data["rejected"]:
# 1. Guardar en log el codigo_msg y observaciones para diagnostico
logger.error(f"MH rechazo: {data['codigo_msg']} - {data['dte_state']}")
logger.error(f"Observaciones: {data['observaciones']}")
# 2. Mostrar al usuario las observaciones tal cual (estan en espanol)
show_to_user("\n".join(data["observaciones"]))
# 3. NO reintentar automaticamente - el usuario debe corregir datos
return
- Guarda siempre
codigo_msgyobservacionesen tu log, aunque el usuario resuelva en vivo. Es información valiosa para detectar patrones. - Muestra
observacionesliteral al usuario — los mensajes están en español y son relativamente legibles. Mejor que "error genérico". - No reintentes el rechazo en un bucle. A diferencia del timeout o 429, rechazo = datos malos = requiere acción humana.
Cuando el código no es conocido
Si recibes un codigo_msg que no está en esta tabla, revisa observaciones — ahí viene la descripción humana del error. El código numérico es secundario; la observación es el mensaje que realmente describe el problema. El MH periódicamente agrega códigos nuevos; este listado cubre los que hemos visto con frecuencia pero no es exhaustivo.
Ver también
- Respuestas y estados — semántica completa del response (incluye
dte_state,codigo_msg,observaciones). - Retry e idempotencia — patrón para corregir y reenviar tras rechazo.