Saltar al contenido principal

Convenciones

Esta página describe las reglas transversales del API. Léela una vez y aplican a todos los endpoints.

Identificadores de documento

Cada DTE emitido por el API tiene tres identificadores, pensados para usos distintos:

IdentificadorQuién lo asignaFormatoUso típico
document_idOcoteUUID v4Identificador interno del API. Se devuelve en el response.
external_refTexto libre (máx. 100 caracteres)Identificador que tú controlas. Idempotencia y retry.
control_numberMH (fijado por Ocote)DTE-{tipo}-{sucursal}{pos}-{15 dígitos}Correlativo fiscal. El que aparece en el JSON firmado y en el ticket.

Reglas importantes

  • El external_ref es único por empresa. Dos emisiones del mismo API key con el mismo external_ref se referencian al mismo documento. Ver Retry e idempotencia.
  • El control_number nunca se regenera. Una vez asignado, queda fijo aun en reintentos.
  • Los endpoints que reciben un id_or_ref aceptan los tres identificadores indistintamente — UUID o tu external_ref funcionan en /status/{id}, /invalidate/{id}, /file/{id}, etc.

El external_ref es opcional pero altamente recomendado

Si no envías external_ref en el request, Ocote asigna uno automáticamente derivado del UUID. Funciona, pero pierdes la capacidad de encontrar el documento desde tu lado sin guardar el UUID devuelto, y pierdes la ventana de retry idempotente ante un timeout de red.

Recomendación: genera tu propio external_ref con una convención legible (VENTA-2026-000042, INV-A1B2C3, lo que sea), guárdalo antes de llamar al API, y mándalo en cada request.

HTTP status codes

El API sigue convenciones REST estándar con un matiz importante para emisión:

CódigoCuándo ocurreQué hacer
200 OKLa operación se registró en Ocote. Puede ser éxito, contingencia o rechazo por MH.Leer los flags del body.
400 Bad RequestError de validación del payload (campo faltante, NIT mal formado, cantidad negativa).Corregir el payload.
401 UnauthorizedAPI key ausente, inválida o empresa desactivada.Revisar la key.
404 Not FoundDocumento no encontrado por id_or_ref, o ruta inexistente.Verificar el identificador.
409 ConflictOperación incompatible con el estado actual (p.ej. invalidar un documento ya invalidado).Consultar /status/{id} para ver el estado actual.
429 Too Many RequestsRate limit excedido.Esperar y respetar Retry-After. Ver Rate limits.
500 Internal Server ErrorError no controlado en Ocote.Reintentar tras unos segundos; si persiste, contactar a Ocote.
200 OK no siempre significa "MH aceptó"

Un response 200 con rejected: true significa: Ocote registró tu petición correctamente, pero el MH rechazó el documento por datos del receptor. Corrige y reintenta con el mismo external_ref.

Ver la tabla completa de flags en Respuestas y estados.

Encoding y content-type

  • Request: todas las peticiones POST deben llevar Content-Type: application/json y body codificado en UTF-8.
  • Response: JSON UTF-8. Los acentos y caracteres españoles no requieren escape.
  • Archivos descargados: ticket en application/pdf, JSON en application/json (con el JWS completo incluyendo sello MH).

Fechas y zonas horarias

  • Todas las fechas en responses son ISO 8601 con zona horaria. Ocote corre en America/El_Salvador (UTC-6). Ejemplo: 2026-04-21T10:42:15-06:00.
  • Para filtros por fecha en endpoints de listado/consulta, usa formato YYYY-MM-DD (p.ej. date_from=2026-04-01). Se interpretan en hora local (UTC-6).
  • No necesitas enviar fechas en los requests de emisión. Ocote asigna el timestamp de posteo al momento de recibir la petición aceptada.

Montos, moneda y decimales

  • Moneda única: USD. El API no acepta ni devuelve montos en otra moneda. El Salvador opera en dólar como moneda de curso legal para DTE.
  • Todos los precios unitarios se envían con IVA incluido en ventas locales (tipos 01, 03, 14). Ocote desglosa internamente base gravable y 13 % de IVA.
  • Decimales: acepta hasta 2 decimales en montos y hasta 8 decimales en cantidades (para unidades fraccionarias de tipo peso/volumen). Los montos se redondean a 2 decimales en el DTE final según reglas del MH.
  • No envíes símbolos de moneda ni separadores de miles. Correcto: 100.00. Incorrecto: "$100", "1,000.00".

Valores enumerados que no son autodescriptivos

Algunos campos del MH usan códigos numéricos que no se pueden inferir de contexto. Los más frecuentes:

CampoValoresSignificado
doc_type"01", "03", "05", "11", "14"Factura, CCF, Nota de Crédito, Factura Exportación, Sujeto Excluido.
type_contributor1, 2, 3, 4Pequeño, Mediano, Grande, Gobierno.
transaction_condition1, 2, 3Contado, Crédito, Otro.
payment_method"01", "02", "03", "05", "08", …Efectivo, Tarjeta Débito, Tarjeta Crédito, Transferencia, Dinero Electrónico. Ver Catálogos MH.

Campos opcionales vs omitidos

El API distingue entre campo ausente (no lo mandaste) y campo enviado con valor vacío. Regla práctica:

  • Si un campo es opcional y no lo necesitas, omítelo. No mandes "email": "" ni "email": null si no quieres email; simplemente no incluyas la llave.
  • Los valores null explícitos se aceptan como equivalentes a "omitido" en la mayoría de campos, pero algunos validadores del MH los rechazan. Prefiere omitir.

Bug conocido: phone

Actualmente el campo customer.phone no se mapea correctamente al modelo interno y puede causar un error de validación si lo envías. Workaround: omite el campo phone del objeto customer hasta que el fix esté desplegado.

Este bug no afecta la emisión de ningún documento, solo el mapeo del teléfono del receptor al registro del cliente en Ocote. El MH no requiere teléfono en ningún tipo de DTE.

Versionado

El API está en su primera versión pública estable. No hay prefijo de versión en la URL (/api/connect/... no /api/connect/v1/...). Los cambios futuros seguirán estas reglas:

  • Aditivos no rompen. Agregar campos nuevos al response o parámetros opcionales al request nunca requerirá cambios del cliente.
  • Cambios que rompen se comunican con preaviso y se introducen en una ruta /api/connect/v2/ dedicada, manteniendo la v1 por un periodo de deprecación.

Ver Changelog para el historial de cambios.