Skip to main content

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)

StateMeaningAction
"" (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.

CodeCategoryDescription
001ProcessDocument accepted.
002ProcessDocument processed with observations.
004DataGeneration code already processed.
005AuthorizationIssuer not authorized.
006SignatureInvalid or expired digital signature.
089ValidationInvalid economic activity code.
092ValidationInvalid recipient document type.
094ValidationInvalid recipient document number.
095ValidationInvalid or nonexistent recipient NRC.
096SchemaSchema build error / JSON doesn't meet required format. Usually indicates a missing field or wrong type in the payload.
097ValidationInvalid issuance date.
098ValidationTotal amount doesn't match sum of lines.
099ValidationComputed withholding doesn't match regulation.
The 096 code is the most common

Indicates 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_msg and observaciones, even if the user resolves live. It's valuable data to detect patterns.
  • Show observaciones verbatim 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