Integración por REST local
Referencia técnica para integración por rest local dentro del cliente local.
Objetivo
Esta guía describe cómo integrar un sistema externo con el cliente local usando HTTP local, ya sea por loopback o por la IP del equipo en la LAN.
Si estás empezando la integración, primero lee Bandeja REST del cliente local. Esa guía funciona como punto de entrada y esta página queda como referencia operativa endpoint por endpoint.
Está pensada para integradores. Cubre:
- URL base
- endpoints disponibles
- contratos de request y response
- códigos HTTP y códigos funcionales
- ruteo por punto de emisión
- reimpresión y PDF
URL base
Base URL por defecto:
http://127.0.0.1:18787
Notas:
- por defecto escucha por loopback
- puede configurarse para escuchar en la IP del equipo o en
0.0.0.0 - el puerto puede configurarse
- si el canal REST está desactivado en el perfil, no responderá
Ejemplos de configuracion:
127.0.0.1:18787para consumo local192.168.1.50:18787para consumo desde la LAN0.0.0.0:18787para escuchar en todas las interfaces del equipo
Endpoints disponibles
| Método | Endpoint | Uso |
|---|---|---|
GET | /health | Estado rápido del daemon |
GET | /state | Estado local |
POST | /enqueue | Encolado asíncrono |
POST | /status-cfe | Consulta de estado por uuid o folio |
POST | /sign-cfe | Numeración y firma con respuesta inmediata |
POST | /reprint | Reimpresión |
POST | /pdf | Generación de PDF |
GET | /proximo-serie-nro | Próximo folio local |
POST | /validar-xml | Validación local |
POST | /existe-constancia | Verificación de existencia local |
Ruteo por punto de emisión
Hay dos modos:
- usar el perfil activo del daemon
- enviar
cod_comercioycod_terminalpara enrutar a un punto específico
Regla:
- si se envía uno, deben enviarse ambos
Modos de integración REST
La bandeja REST local admite dos formas de integración:
- sincrónica:
POST /sign-cfe - asíncrona:
POST /enqueue+POST /status-cfe
Integración sincrónica
Usar POST /sign-cfe cuando la aplicación necesita cerrar la operación en la misma llamada y recibir de inmediato:
serienumerocfe_firmado- datos de CAE y QR
Casos típicos:
- cajas o POS que no pueden continuar sin el folio asignado
- flujos donde se imprime o muestra el comprobante en el acto
- integraciones que necesitan persistir enseguida el XML firmado
Tradeoff:
- el request espera a que termine la numeración y la firma local
- la respuesta es más rica, pero no desacopla el trabajo
Integración asíncrona
Usar POST /enqueue cuando la aplicación solo necesita entregar el trabajo al módulo local y continuar.
El flujo recomendado es:
- enviar
POST /enqueue - guardar el
uuid - consultar
POST /status-cfehasta obtener un estado final
Casos típicos:
- integraciones desacopladas
- lotes o colas internas del ERP
- escenarios donde el host no necesita el XML firmado en la misma llamada
- integraciones que priorizan resiliencia del punto de emisión sobre respuesta inmediata
Tradeoff:
- la respuesta inicial solo confirma aceptación local en bandeja
- el resultado fiscal o funcional se obtiene después consultando estado
Regla práctica de elección
Elegir sincrónico si el integrador necesita el folio y el XML firmado en el acto.
Elegir asíncrono si el integrador puede trabajar con un uuid propio y consultar luego el resultado.
GET /health
Ejemplo:
curl http://127.0.0.1:18787/health
Respuesta esperada:
{
"ok": true,
"service": "modulo_local_daemon",
"rest_base_url": "http://127.0.0.1:18787",
"state": {
"running": true
}
}
GET /state
Devuelve el estado operativo local del daemon.
Uso recomendado:
- monitoreo local
- diagnóstico técnico
- soporte
POST /enqueue
Encola un comprobante para procesamiento local asíncrono.
Ejemplo:
{
"tipo_cfe": 111,
"uuid": "externo-123",
"cod_comercio": "1",
"cod_terminal": "1",
"xml": "<CFE xmlns=\"http://cfe.dgi.gub.uy\" version=\"1.0\">...</CFE>",
"adenda": "Texto opcional",
"emails": ["[email protected]"],
"impresora": "CajaFiscal;FORMATO=personalizado;COPIAS=2",
"send_now": true
}
Respuesta:
{
"ok": true,
"stored_path": "/ruta/local/inbox/20260318121000123-externo-123.json",
"uuid": "externo-123",
"send_now": true
}
Importante:
/enqueueconfirma aceptación local del trabajo- no implica que el comprobante ya haya quedado emitido
- para conocer el avance posterior se debe consultar
POST /status-cfeusando el mismouuid
POST /status-cfe
Consulta el estado local de un comprobante previamente encolado o firmado.
Permite buscar por:
uuidtipo_cfe+serie+numero
Si se trabaja con varios puntos de emisión, conviene reenviar también:
cod_comerciocod_terminal
Ejemplo por uuid:
{
"uuid": "externo-123",
"cod_comercio": "1",
"cod_terminal": "1"
}
Respuesta cuando sigue en cola:
{
"ok": true,
"found": true,
"uuid": "externo-123",
"tipo_cfe": 111,
"serie": "A",
"numero": 301,
"estado": "queued",
"estado_descripcion": "En cola de envio",
"codigo_respuesta": "11",
"mensaje_respuesta": "En cola de envio",
"codigo_terminal": "1",
"codigo_comercio": "1",
"retry_count": 0,
"attempt_count": 1,
"last_error": null,
"last_error_code": null,
"last_error_stage": null,
"last_attempt_at": null,
"next_retry_at": null,
"confirmed_at": null,
"fecha_firma_cfe": "2026-03-18T12:10:00Z",
"NroConstanciaCae": "90160001010",
"numero_inicial_cae": "301",
"numero_final_cae": "400",
"vencimiento_cae": "2028-01-01"
}
Respuesta cuando ya fue aceptado:
{
"ok": true,
"found": true,
"uuid": "externo-123",
"tipo_cfe": 111,
"serie": "A",
"numero": 301,
"estado": "accepted",
"estado_descripcion": "Aceptado",
"codigo_respuesta": "00",
"mensaje_respuesta": "Aceptado",
"codigo_terminal": "1",
"codigo_comercio": "1",
"retry_count": 0,
"attempt_count": 1,
"last_error": null,
"last_error_code": null,
"last_error_stage": null,
"last_attempt_at": "2026-03-18T12:10:03Z",
"next_retry_at": null,
"confirmed_at": "2026-03-18T12:10:05Z",
"fecha_firma_cfe": "2026-03-18T12:10:00Z",
"NroConstanciaCae": "90160001010",
"numero_inicial_cae": "301",
"numero_final_cae": "400",
"vencimiento_cae": "2028-01-01"
}
POST /sign-cfe
Numerar, firmar y responder en el acto.
Precondición operativa:
- el equipo donde corre el módulo local debe estar en la zona horaria
America/Montevideo - el offset efectivo debe ser
GMT-3(-03:00)
Si esa condición no se cumple, la firma se rechaza con codigo_respuesta = "96".
Si send_now dispara el envío SOAP inmediato y no hay un timeout explícito configurado, el módulo local usa 60000 ms por defecto para EnviarCfeFirmado.
Ejemplo:
{
"tipo_cfe": 111,
"uuid": "externo-123",
"cod_comercio": "1",
"cod_terminal": "1",
"xml": "<CFE xmlns=\"http://cfe.dgi.gub.uy\" version=\"1.0\">...</CFE>",
"adenda": "Texto opcional",
"emails": ["[email protected]"],
"impresora": "CajaFiscal;FORMATO=personalizado;COPIAS=2",
"send_now": false
}
Respuesta de éxito:
{
"Uuid": "externo-123",
"TipoCfe": "111",
"Serie": "A",
"Numero": "301",
"CodigoRespuesta": "00",
"MensajeRespuesta": "CFE firmado y encolado",
"CodigoTerminal": "1",
"CodigoComercio": "1",
"NumeroInicialCae": "301",
"NumeroFinalCae": "400",
"VencimientoCae": "2028-01-01",
"CfeFirmado": "<CFE>...</CFE>",
"DatosCodigoQr": "https://...",
"CodigoSeguridad": "ZmCpqT",
"FechaFirmaCfe": "2026-03-18T12:10:00Z",
"ImagenQr": null,
"NroConstanciaCae": "90160001010"
}
Respuesta de error:
{
"Uuid": "externo-123",
"TipoCfe": "111",
"Serie": null,
"Numero": null,
"CodigoRespuesta": "31",
"MensajeRespuesta": "validation error: ...",
"CodigoTerminal": "1",
"CodigoComercio": "1",
"NumeroInicialCae": null,
"NumeroFinalCae": null,
"VencimientoCae": null,
"CfeFirmado": null,
"DatosCodigoQr": null,
"CodigoSeguridad": null,
"FechaFirmaCfe": null,
"ImagenQr": null,
"NroConstanciaCae": null
}
Códigos funcionales
codigo_respuesta | Interpretación |
|---|---|
00 | Éxito |
01 | Petición denegada |
03 | Comercio inválido |
12 | Requerimiento inválido |
30 | Error en formato |
31 | Error en formato de CFE |
89 | Terminal inválida |
96 | Error interno, de firma, permisos, rango, persistencia o SOAP |
Códigos HTTP
| HTTP | Interpretación |
|---|---|
200 | Operación procesada; revisar codigo_respuesta |
422 | Request inválido o XML rechazado tempranamente |
500 | Error interno del daemon |
Regla recomendada:
- considerar emitido solo si HTTP
200,codigo_respuesta = "00"yserie+numeroson válidos
POST /reprint
Encola la reimpresión de un comprobante emitido.
Se puede localizar por:
uuid- o
tipo_cfe + serie + numero
Ejemplo:
{
"uuid": "externo-123",
"impresora": "CajaFiscal;FORMATO=personalizado;COPIAS=2",
"variant": "personalizado",
"fallback_copies": 2,
"cod_comercio": "1",
"cod_terminal": "1"
}
Respuesta:
{
"ok": true,
"queued": true
}
HTTP esperado:
202 Accepted
POST /pdf
Genera el PDF de un comprobante ya emitido y devuelve el binario.
Ejemplo:
{
"uuid": "externo-123",
"impresora": "pdf",
"variant": "personalizado",
"fallback_copies": 1,
"cod_comercio": "1",
"cod_terminal": "1"
}
Respuesta:
HTTP 200Content-Type: application/pdf- body binario
GET /proximo-serie-nro
Ejemplo:
curl "http://127.0.0.1:18787/proximo-serie-nro?tipo_cfe=111&cod_comercio=1&cod_terminal=1"
Respuesta con rango activo:
{
"ok": true,
"tipo_cfe": 111,
"serie": "A",
"numero_siguiente": 301,
"numero_desde": 301,
"numero_hasta": 400,
"disponibles": 100,
"vencimiento_cae": "2028-01-01"
}
POST /validar-xml
Request:
{
"xml": "<CFE xmlns=\"http://cfe.dgi.gub.uy\" version=\"1.0\">...</CFE>"
}
Respuesta:
{
"ok": true,
"valido": false,
"issues": []
}
POST /existe-constancia
Request:
{
"tipo_cfe": 111,
"serie": "A",
"numero": 301,
"cod_comercio": "1",
"cod_terminal": "1"
}
Respuesta cuando existe:
{
"ok": true,
"existe": true,
"codigo_respuesta": "00",
"estado": "Accepted",
"uuid": "externo-123",
"tipo_cfe": 111,
"serie": "A",
"numero": 301
}
Impresoras
Los endpoints que usan impresora aceptan una especificación textual.
Ejemplos:
CajaFiscalCajaFiscal;COPIAS=2CajaFiscal;FORMATO=personalizado;COPIAS=2CajaFiscal;FORMATO=escpos;COPIAS=2CajaFiscal;FORMATO=rollo;COPIAS=2
Parámetros:
| Parámetro | Descripción |
|---|---|
| nombre base | Nombre o destino de impresora |
FORMATO=personalizado | Plantilla HTML/PDF |
FORMATO=escpos | Impresión térmica ESC/POS RAW |
FORMATO=roll, FORMATO=rollo, FORMATO=tml | PDF tipo ticket usando parser .tml |
COPIAS=2 | Cantidad de copias |
Errores frecuentes
uuid es obligatorioxml es obligatoriotipo_cfe debe ser mayor a cerocod_comercio es obligatorio para enrutar por punto de emisióncod_terminal es obligatorio para enrutar por punto de emisiónNo se encontró perfil para cod_comercio=X cod_terminal=Y- errores de firma
- errores de rango
- errores SOAP
Recomendaciones
- usar
uuidúnicos - no considerar éxito solo por HTTP
200 - en
/sign-cfe, revisar siemprecodigo_respuesta - usar
/enqueuesi el flujo es asíncrono - usar
/sign-cfesi el flujo es síncrono - registrar
uuid,tipo_cfe,serie,numero,codigo_respuestaymensaje_respuesta