MH codes
When MH rejects or flags a document, it returns codes and observations that the API exposes as dte_state, codigo_msg, and observaciones. This page documents the most frequent ones.
Document states (dte_state)
| State | Meaning | Action |
|---|---|---|
"" (empty) | All good — MH accepted with stamp. | None. |
"PROCESADO" | MH received and processed successfully. | None. |
"RECHAZADO" | MH rejected the document. No stamp generated. | Correct data and retry with same external_ref. |
"OBSERVADO" | MH accepted with observations. Document is valid but has warnings. | Review observaciones — informational, no mandatory action. |
When rejected: true, dte_state is "RECHAZADO" (occasionally "OBSERVADO" if MH treated it as rejection).
Message codes (codigo_msg)
MH returns a brief numeric code (typically 2–3 digits) that identifies the error type.
| Code | Category | Description |
|---|---|---|
001 | Process | Document accepted. |
002 | Process | Document processed with observations. |
004 | Data | Generation code already processed. |
005 | Authorization | Issuer not authorized. |
006 | Signature | Invalid or expired digital signature. |
089 | Validation | Invalid economic activity code. |
092 | Validation | Invalid recipient document type. |
094 | Validation | Invalid recipient document number. |
095 | Validation | Invalid or nonexistent recipient NRC. |
096 | Schema | Schema build error / JSON doesn't meet required format. Usually indicates a missing field or wrong type in the payload. |
097 | Validation | Invalid issuance date. |
098 | Validation | Total amount doesn't match sum of lines. |
099 | Validation | Computed withholding doesn't match regulation. |
096 code is the most commonIndicates the generated JSON doesn't comply with MH's JSON Schema. The message in observaciones tells you which field fails. Real examples:
"Campo #/receptor/correo no cumple el formato requerido"→ invalid customer email"El valor del campo #/resumen/totalNoGravado debe ser tipo numero"→ numeric data mistyped"Campo #/receptor/nrc es requerido"→ NRC missing in CCF
Common observations
observaciones is an array of strings. Each string is an independent MH message — you may get one or several simultaneously. Show all to the end user; don't filter.
Category: Recipient (customer)
"Campo #/receptor/correo no cumple el formato requerido"
Customer email malformed. Validate with regex ^[\w\.-]+@[\w\.-]+\.\w+$ before sending, or ask the user to check.
"Campo #/receptor/nrc no cumple el formato requerido"
NRC must be a numeric string of 2–8 digits. No dashes.
"El valor del campo #/receptor/codActividad no está en el catálogo"
The activity_code doesn't exist in MH's CAT-019 catalog. Check MH catalogs for the correct one.
"Combinación departamento/municipio inválida"
The department + municipality doesn't correspond to an actual municipality. See the live /catalogs/departments catalog.
Category: Amounts
"El valor del campo #/resumen/totalNoGravado debe ser tipo numero"
A field that should be numeric came as a string or other type. Uncommon — if it appears, report to Ocote with the external_ref.
"Suma de líneas no coincide con total declarado"
Internal calculation error; rare but possible. Report to Ocote with the external_ref.
Category: Issuer
"Firma digital vencida"
Issuer certificate has expired. Not an API-client error — Ocote needs to update the company's signer configuration. Contact Ocote.
"Emisor no autorizado"
The configured company is not authorized to issue the requested DTE type. Contact Ocote.
How to handle rejections in your integration
r = requests.post(".../invoice", json=payload, headers=h)
data = r.json()
if data["rejected"]:
# 1. Log codigo_msg and observaciones for diagnosis
logger.error(f"MH rejection: {data['codigo_msg']} - {data['dte_state']}")
logger.error(f"Observations: {data['observaciones']}")
# 2. Show observaciones verbatim to the user (they're in Spanish)
show_to_user("\n".join(data["observaciones"]))
# 3. DO NOT retry automatically - user must fix the data
return
- Always log
codigo_msgandobservaciones, even if the user resolves live. It's valuable data to detect patterns. - Show
observacionesverbatim to the user — the messages are in Spanish and reasonably readable. Better than "generic error". - Don't retry rejection in a loop. Unlike timeout or 429, rejection = bad data = requires human action.
Messages are in Spanish
All MH observation messages, state names ("RECHAZADO", "OBSERVADO"), and error messages from the API come in Spanish. This is MH's operating language and the language displayed on the DTE documents themselves. If your application serves an English-speaking audience, you'll need to translate or wrap these messages on your side.
When the code is unknown
If you receive a codigo_msg not in this table, check observaciones — that's where the human description lives. The numeric code is secondary; the observation is the message that actually describes the problem. MH periodically adds new codes; this list covers the ones we've seen frequently but isn't exhaustive.
See also
- Responses and states — full response semantics (includes
dte_state,codigo_msg,observaciones). - Retry and idempotency — pattern for correcting and resending after rejection.