Skip to main content

MH Normative 2.0 (DTE v2)

The MH published the DTE Compliance Normative version 2.0 on May 25, 2026. All issuing companies must migrate to the v2 schemas before December 1, 2026.

The migration is performed by Ocote on the server, company by company. For your integration it is transparent in most cases: you don't need to change anything to keep issuing documents.

The essentials

Once your company is migrated to v2, you can keep your current integration as is. The only visible change is the district field, and it is optional thanks to an automatic fallback.

What does NOT change

  • Endpoint URLs (/invoice, /suex, /credit-memo, /invalidate/...).
  • Authentication (Authorization: Bearer odt_...).
  • HTTP methods and status codes.
  • Response shapes (same fields).
  • Idempotency via external_ref.
  • Existing payload fields: everything that worked before still works.
  • control_number format (DTE-XX-XXXXXXXX-NNNNNNNNNNNNNNN, 31 characters).

The only visible change: the district (CAT-008)

Starting in v2, the MH requires every receptor (customer) and the issuer to declare its district (CAT-008 catalog). Until now you sent only department and municipality; going forward the district is required by the MH.

To preserve backward compatibility, the API treats it as optional in the payload:

  • If you do not send a district, the API uses the issuing company's district as a fallback for the customer. The document is still accepted by the MH.
  • If you do send a district, the exact one you specify is used.
Recommendation

The fallback prevents emission failures, but does not represent the customer's real address; over time the printed or PDF representation may show a district that does not match. It is recommended to update your integration to send each customer's real district before your company migrates to v2.

How to send the district

Option A — district_code + department + municipality (recommended). Use the official CAT-008 district code (2 digits). All three values are required to resolve a unique district, because the same code can exist under different municipalities.

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

Option B — district (UUID). Resolve the UUID once via GET /catalogs/districts/search?q=<name> and store it; the API derives department and municipality automatically.

Option C — only department + municipality (legacy). If you don't have district data yet, you can keep your current payload; the system will use the issuing company's district as a fallback.

Discovering district values

GET /catalogs/districts/search?q=<name> returns the district with its parent codes ready to use:

{
"results": [
{
"id": "00000000-0000-0000-0000-000000000000",
"name": "Sample District",
"text": "Sample District - MUNICIPALITY EAST, DEPARTMENT",
"municipality_code": "22",
"department_code": "06"
}
]
}

Correlative number behavior (informational)

v2 introduces stricter MH rules on how correlatives (the last 15 digits of numeroControl) are assigned. There is nothing to do in your integration: it is enforced server-side. Per rule 7.1.2, the company chooses one of three modes:

ModeBehavior
Centralized (default)A single global series per company and fiscal year; all document types and points of sale share the counter.
Per establishmentOne series per branch; document types and points of sale within that branch share the counter.
Per POS + document typeOne series per (point of sale, document type) combination. Requires explicit MH authorization.
Correlatives with gaps

Under the Centralized or Per establishment modes you may observe correlatives that "skip" (e.g. 100, 101, then 150) because the company also issued CCFs, credit notes, or excluded subjects in between. This is expected and compliant. Idempotency via external_ref does not change.

Error handling

Previously, if the signing module had an internal error, the API could return success: true with a malformed control_number (containing a Unix timestamp), and the MH would reject those documents asynchronously.

Now, any internal failure during signing returns a clear HTTP 500 immediately. Make sure your retry logic handles 5xx responses, in addition to the existing MH-rejection handling (codes 004, 024, 096, etc.).

Migration checklist

When Ocote notifies you that a specific company will migrate to v2:

  1. Audit your customer database: identify those that have department and municipality but no district; look up the real district via /catalogs/districts/search.
  2. Update your payload builders: include district_code (plus department and municipality) in customer and subject.
  3. Test in the company's test environment before switching to production (the test environment is isolated from production for that NIT).
  4. Confirm your error handler treats HTTP 5xx as retriable.

Nothing else needs to change: authentication, retries, error parsing, and storage schema all remain the same.

See also