Ir al contenido

Insertar facturas

POST /api/v1/facturas/insertar

Es el endpoint central de la integración. Recibe un lote (batch) de facturas, las valida en un orden estricto, las agrupa por manifiesto y las inserta. Una sola llamada puede traer facturas de varios manifiestos distintos.

AtributoValor
MétodoPOST
Ruta/api/v1/facturas/insertar
AutenticaciónRequerida (ApiKey)
Content-Typeapplication/json
Rate limit5 por minuto por IP (ver rate limits)

El cuerpo es un array JSON de facturas no vacío. Ver el esquema completo de la factura y el esquema de la línea.

  • Método POST, URL https://<dominio-hosana>/api/v1/facturas/insertar
  • Pestaña Headers: ApiKey con tu clave
  • Pestaña BodyrawJSON, y pega el array de facturas (ejemplo real):
[
{
"Nfactura": "002-001-01-04001001",
"NumeroManifiesto": "800002",
"FechaFactura": "2026-06-05T00:00:00.000Z",
"FechaVencimiento": "2026-06-05T00:00:00.000Z",
"Almacen": "OAC",
"Vendedorid": "11983",
"Vendedor": "WALTER REYNALDO MARADIAGA",
"Clienteid": "98065401",
"Cliente": "PULPERIA ASLYN",
"Rtn": "12181986001440",
"Cai": "2F0037-619ACD-2A66E0-63BE03-0909DC-56",
"Depto": "LA PAZ",
"Municipio": "Santiago de Puringla",
"Direccion": "BARRIO LAS BRISAS, SANTIAGO DE PURINGLA, LA PAZ",
"Tel": "97689223",
"NumeroPedido": "6915760",
"NumeroRuta": "230",
"NumeroFacturaLX": "4001001",
"TipoPago": "CONTADO",
"DiasCred": 0,
"TipoFactura": "FAC",
"EstadoFactura": 1,
"ImporteGrabado": 878.29,
"ImporteGravado_ISV15": 131.75,
"ImporteGravado_Total": 1010.04,
"Isv15": 131.75,
"Isv18": 0.0,
"DescuentosRebajas": 0.0,
"Total": 1010.04,
"LineasFactura": [
{
"Id": 3001001,
"InvoiceId": 700101,
"NumeroLinea": 1,
"ProductoId": "30110205",
"ProductoDesc": "ORISOL LIGHT OLIVA 410 mL 1/24 HN",
"UniVenta": "UN",
"TipoProducto": "A",
"CantidadFracciones": 6.0,
"CantidadCaja": 0.0,
"FactorConversion": 24,
"Precio": 748.8,
"PrecioUnidadMinVenta": 31.2,
"Subtotal": 187.2,
"Descuento": 0.0,
"Impuesto": 28.08,
"PorcentajeImpuesto": 15.0,
"Total": 215.28,
"Peso": 2.3115
},
{
"Id": 3001002,
"InvoiceId": 700101,
"NumeroLinea": 2,
"ProductoId": "81800012",
"ProductoDesc": "KETCHUP 8X12X87GR",
"UniVenta": "UN",
"TipoProducto": "A",
"CantidadFracciones": 12.0,
"CantidadCaja": 0.0,
"FactorConversion": 96,
"Precio": 642.11,
"PrecioUnidadMinVenta": 6.6883,
"Subtotal": 80.26,
"Descuento": 0.0,
"Impuesto": 12.04,
"PorcentajeImpuesto": 15.0,
"Total": 92.3,
"Peso": 1.195
}
]
}
]

Una factura puede traer todas las líneas que necesite. Ver el catálogo completo de campos en el esquema de la factura.

Dos lotes reales para probar el endpoint de inmediato (impórtalos en Postman con Body → raw → JSON o úsalos con -d @archivo.json):

  • manifiesto-800003-pasa.jsonse inserta correctamente (200). Trae dos facturas con fechas distintas (2026-06-12 y 2026-05-20); la mezcla de fechas está permitida y ambas están dentro del rango de 30 días.
  • manifiesto-800004-rechaza.jsonse rechaza (422, FECHA_FACTURA_DEMASIADO_ANTIGUA). Una de sus facturas tiene fecha 2026-04-01, más de 30 días de antigüedad, y eso rechaza el manifiesto completo.

El detalle de cada caso, con su respuesta exacta, está en casos resueltos.

El endpoint procesa el batch en un orden estricto. Cada etapa es un filtro: si falla, detiene el proceso y devuelve un 422. Entender este orden es clave para diagnosticar rechazos.

flowchart TD
    Start([POST /facturas/insertar]) --> Auth{ApiKey válido?}
    Auth -- no --> E401[401]
    Auth -- sí --> P1{1· body es array no vacío?}
    P1 -- no --> E422a[422 body inválido]
    P1 -- sí --> P2{2· campos obligatorios?}
    P2 -- no --> E422b[422 errores·]
    P2 -- sí --> P3{3· fechas V1/V2/V3?}
    P3 -- no --> EF[422 FECHAS_INVALIDAS<br/>rechaza TODO el batch]
    P3 -- sí --> P4{4· manifiestos de hoy/nuevos?}
    P4 -- no --> EM[422 MANIFIESTOS_FECHA_INVALIDA<br/>rechaza TODO el batch]
    P4 -- sí --> P5{5· batch duplicado hoy?}
    P5 -- sí --> Dup[200 resumen original]
    P5 -- no --> P6[6· persistir payload]
    P6 --> P7[7· procesar por manifiesto]
    P7 --> P8{hubo rechazos?}
    P8 -- sí --> R422[422 manifiestos_rechazados·<br/>+ inserta los válidos]
    P8 -- no --> OK[200 éxito]

    style EF fill:#fde3e3,stroke:#c0392b,color:#7a271a
    style EM fill:#fde3e3,stroke:#c0392b,color:#7a271a
    style OK fill:#dcf3e3,stroke:#1f8a4c,color:#14532d
    style Dup fill:#dcf3e3,stroke:#1f8a4c,color:#14532d

El detalle paso a paso está en la guía del pipeline de inserción. A continuación, las respuestas posibles.

HTTP 200 — todas las facturas se procesaron sin rechazos por manifiesto.

{
"success": true,
"message": "Facturas procesadas correctamente.",
"batch_uuid": "9f1c2a7e-3b4d-4f1a-8c2e-1a2b3c4d5e6f",
"manifiestos": ["800002"],
"resumen": {
"recibidas": 42,
"insertadas": 40,
"actualizadas": 0,
"sin_cambios": 0,
"pendientes_revision": 2,
"rechazadas": 0
}
}
CampoTipoDescripción
batch_uuidstringIdentificador único del lote procesado. Guárdalo para soporte y trazabilidad.
manifiestosarrayNúmeros de manifiesto presentes en el batch.
resumen.recibidasintegerTotal de facturas recibidas en el batch.
resumen.insertadasintegerFacturas nuevas creadas.
resumen.actualizadasintegerFacturas existentes actualizadas.
resumen.sin_cambiosintegerFacturas idénticas a las ya existentes (no se tocaron).
resumen.pendientes_revisionintegerFacturas que llegaron con diferencias respecto a la versión existente y quedaron en revisión manual por Hosana.
resumen.rechazadasintegerFacturas rechazadas (0 en una respuesta 200).

Advertencias y conflictos (pendientes_revision)

Sección titulada «Advertencias y conflictos (pendientes_revision)»

Si una factura ya existía en el mismo manifiesto pero llega con campos distintos, no se sobrescribe automáticamente: se registra un conflicto para revisión manual de Hosana y se cuenta en pendientes_revision. La respuesta incluye un arreglo advertencias:

{
"advertencias": [
{
"factura": "002-001-01-04001001",
"manifiesto": "800002",
"campos_con_cambio": ["total", "client_name"],
"mensaje": "Factura recibida con diferencias respecto a la versión existente. Pendiente de revisión por Hosana."
}
]
}

HTTP 422 — el batch se procesó pero uno o más manifiestos fueron rechazados durante el procesamiento (paso 7). Las facturas de manifiestos válidos se insertan; las de manifiestos rechazados, no.

{
"success": false,
"motivo": "ALMACENES_DESCONOCIDOS",
"message": "Uno o más manifiestos fueron rechazados por contener almacenes no registrados en el sistema.",
"batch_uuid": "9f1c2a7e-...",
"manifiestos": ["800002", "800003"],
"resumen": {
"recibidas": 240, "insertadas": 118, "actualizadas": 0,
"sin_cambios": 0, "pendientes_revision": 0, "rechazadas": 122
},
"manifiestos_rechazados": [
{
"manifiesto": "800003",
"total_facturas": 122,
"motivo": "ALMACENES_DESCONOCIDOS",
"almacenes_desconocidos": [
{ "almacen": "OAX", "facturas": ["002-001-01-04002050", "002-001-01-04002051"], "cantidad": 2 }
]
}
]
}
  • Si todos los manifiestos rechazados comparten el mismo motivo, motivo trae ese código único y message es específico.
  • Si hay motivos distintos, motivo es "MOTIVOS_MIXTOS" y debes leer el campo motivo de cada entrada dentro de manifiestos_rechazados[].

Los tres motivos posibles en esta etapa son ALMACENES_DESCONOCIDOS, MANIFIESTO_CERRADO y FACTURAS_DUPLICADAS_EN_OTRO_MANIFIESTO. Cada uno tiene una forma de payload propia, documentada en el catálogo de errores.

A diferencia del caso anterior, estos rechazos ocurren antes de insertar nada: ninguna factura entra. Son las validaciones de los pasos 3 y 4.

HTTP 422 con motivo: "FECHAS_INVALIDAS". Las validaciones de fecha son por factura y atómicas por manifiesto: una sola factura futura o demasiado antigua rechaza el manifiesto completo. Un manifiesto puede mezclar fechas (eso ya no se rechaza por defecto).

Este ejemplo corresponde al archivo de prueba manifiesto-800004-rechaza.json: trae una factura de hace más de 30 días.

{
"success": false,
"motivo": "FECHAS_INVALIDAS",
"message": "Batch rechazado por errores de fecha en uno o más manifiestos.",
"accion_requerida": "Revise los manifiestos rechazados. Ninguna FechaFactura puede ser futura ni superar el rango configurado de antigüedad; una sola factura inválida rechaza el manifiesto completo.",
"resumen": { "total_recibidas": 2, "total_rechazadas": 2, "total_validas": 0, "insertadas": 0 },
"manifiestos_rechazados": [
{
"manifiesto": "800004",
"motivo": "FECHA_FACTURA_DEMASIADO_ANTIGUA",
"detalle": {
"hoy_servidor": "2026-06-13",
"limite_dias": 30,
"facturas_antiguas": {
"002-001-01-04004002": { "fecha": "2026-04-01", "dias": 73 }
},
"instruccion": "Una o más facturas superan el límite de 30 días de antigüedad. El manifiesto se rechaza completo; corrija el origen o cargue desde el panel administrativo."
},
"total_facturas": 2,
"facturas": ["002-001-01-04004001", "002-001-01-04004002"]
}
],
"manifiestos_validos": []
}

Cada manifiesto rechazado trae su propio motivo y un detalle con la lista de facturas afectadas:

Sub-motivoCampo en detalleForma
FECHA_FACTURA_FUTURAfacturas_futuras{ "Nfactura": "YYYY-MM-DD" }
FECHA_FACTURA_DEMASIADO_ANTIGUAfacturas_antiguas{ "Nfactura": { "fecha": "...", "dias": N } }
FECHA_FACTURA_INVALIDAfacturas_afectadas["Nfactura", ...]
FECHAS_MEZCLADAS (solo si Hosana activa el modo estricto)fechas_encontradas, facturas_por_fechalistas por fecha

Ver el detalle de cada regla en reglas de fecha.

HTTP 422 con motivo: "MANIFIESTOS_FECHA_INVALIDA". Ocurre cuando se intenta agregar facturas a un manifiesto que ya existe en Hosana y fue creado en un día anterior:

{
"success": false,
"motivo": "MANIFIESTOS_FECHA_INVALIDA",
"message": "Batch rechazado completamente. Contiene facturas de manifiestos creados en días anteriores que ya no aceptan nuevas facturas.",
"accion_requerida": "Corrija el batch separando los manifiestos afectados. Los manifiestos válidos deben reenviarse en un batch independiente sin mezclar con los del día anterior.",
"resumen": { "total_recibidas": 240, "total_rechazadas": 122, "total_validas": 118, "insertadas": 0 },
"manifiestos_rechazados": [
{
"manifiesto": "800003",
"fecha_original": "2026-06-05",
"fecha_intento": "2026-06-06",
"total_facturas": 122,
"facturas_afectadas": ["002-001-01-04002050", "002-001-01-04002051"],
"instruccion": "El manifiesto #800003 fue creado el 2026-06-05 y ya no acepta facturas nuevas. Reenvíe estas facturas en un nuevo número de manifiesto."
}
],
"manifiestos_no_afectados": [
{
"manifiesto": "800002",
"total_facturas": 118,
"facturas": ["002-001-01-04001001", "..."],
"nota": "Este manifiesto es válido pero fue rechazado por venir en el mismo batch que manifiestos de días anteriores. Reenvíelo en un batch independiente."
}
]
}

HTTP 200 con success: true. Si reenvías un batch idéntico (mismo contenido exacto) el mismo día, Hosana lo detecta por hash y no reprocesa — devuelve el resumen del proceso original:

{
"success": true,
"message": "Este batch ya fue procesado anteriormente el día de hoy.",
"batch_uuid": "9f1c2a7e-...",
"resumen": { "recibidas": 240, "insertadas": 235, "actualizadas": 0, "sin_cambios": 3, "pendientes_revision": 2, "rechazadas": 0 }
}

Esto hace seguro reintentar ante un timeout sin riesgo de duplicar facturas.

HTTP 422 con un arreglo errores:

{
"success": false,
"message": "El payload contiene errores de validación.",
"errores": [
"Factura #3: falta el campo obligatorio 'Almacen'.",
"Factura 002-001-01-04001099: 'LineasFactura' no puede estar vacío."
]
}

HTTP 422:

{ "success": false, "message": "El body debe ser un array JSON de facturas no vacío." }

HTTP 500. Hosana notifica automáticamente a su equipo técnico. El batch_uuid permite rastrear el caso:

{
"success": false,
"message": "Error interno al procesar las facturas. El equipo técnico ha sido notificado.",
"batch_uuid": "9f1c2a7e-..."
}
HTTPsuccessSituación
200trueProcesado sin rechazos, o batch duplicado ya procesado.
422falseBody inválido, estructura inválida, fechas inválidas, manifiestos viejos, o rechazos por manifiesto.
401falseApiKey ausente o inválido.
429Rate limit superado (5/min).
500falseError interno en Hosana.

Los casos resueltos recogen escenarios completos (batch mixto, almacén inválido, fechas mezcladas) con el request y el response exactos.