Saltearse al contenido

Emite un CFE a partir de un XML pre-armado (formato DGI nativo)

POST
/v1/cfe/emitir-xml

Endpoint para proveedores de facturación electrónica con integración previa a DGI.

Pensado específicamente para proveedores y empresas que ya operaban contra los servicios SOAP de DGI de forma directa y quieren migrar a Host Factura sin reescribir su armador de XML. El cliente envía el contenido del CFE como si fuera a DGI directo, y Host Factura se encarga exclusivamente de la firma, asignación de CAE, numeración y envío del sobre.

⚠️ Responsabilidad de mantener el armador actualizado

DGI actualiza periódicamente los formatos de CFE (nuevos campos obligatorios, cambios en validaciones, modificaciones en los XSD). Cuando esto ocurre:

  • Host Factura actualiza las validaciones XSD del sobre de su lado tan pronto como DGI publica el cambio, y rechaza los CFE que no cumplan con el formato vigente devolviendo 400 XML_EXTERNO_INVALIDO con el detalle del error reportado por DGI.
  • Tu sistema debe mantener su armador de XML al día con los formatos publicados por DGI. Si tu armador queda desactualizado respecto al esquema vigente, tus emisiones empezarán a fallar incluso cuando antes funcionaban.

Recomendamos suscribirte a las comunicaciones de DGI sobre cambios en e-Factura (circulares, actualizaciones de los documentos Formato_CFE_vXX y Formato_Sobre_vXX) y planificar una validación de tu armador cada vez que DGI publique una nueva versión.

⚠️ Validación de montos a cargo del cliente

Host Factura no recalcula los totales ni valida la consistencia interna de los montos. Confiamos en que los valores de Totales (subtotal, IVA por tasa, total) y los MontoItem del detalle son correctos.

Tu sistema es responsable de garantizar que:

  • MntNetoIVATasaBasica, MntNetoIVATasaMin y MntNetoIVAOtra se calculen correctamente según la tasa aplicada a cada ítem (IndFact).
  • IVATasaBasica (22%) e IVATasaMin (10%) coincidan con la aplicación correcta sobre los netos.
  • MntTotal sea exactamente la suma de los netos, IVA, percepciones y descuentos.
  • Los redondeos respeten las reglas que exige DGI (típicamente 2 decimales, redondeo bancario).
  • En CFE en moneda extranjera, TpoCambio esté presente y el equivalente en pesos sea consistente.

Si los montos no cierran, DGI rechaza el CFE con el código de error correspondiente y la emisión queda persistida con estado: rechazado y motivoRechazo con el detalle.

A quién está dirigido

  • Proveedores de facturación electrónica que ya tenían un flujo productivo emitiendo CFE directamente contra los servicios SOAP de DGI, con su propio motor de armado de XML.
  • Empresas con desarrollos legacy que invirtieron tiempo en armar el XML según el formato DGI y no quieren rehacer el mapeo a un payload JSON estructurado.
  • Implementaciones que necesitan campos avanzados de DGI (mandante en Cuenta Ajena, retenciones detalladas en e-Resguardo, IndCompraMEReventa en Boleta de Entrada, etc.) que el payload JSON del endpoint estándar no expone con el mismo grado de detalle.

Cuándo NO usar este endpoint

  • Estás arrancando una integración desde cero → usá POST /v1/cfe/emitir (mucho más simple, Host Factura calcula totales, IVA, redondeos por vos).
  • Querés usar el catálogo de productos/clientes de Host Factura → usá el payload JSON.
  • No tenés un armador XML ya construido → la curva de aprendizaje del formato DGI es alta; preferí JSON.

Responsabilidades

Tu sistema (proveedor):

  • Arma el XML del CFE siguiendo el formato oficial DGI (XSDs CFE.xsd, CFEType.xsd).
  • Calcula totales, IVA, retenciones, redondeos.
  • Provee los datos del Emisor, Receptor, Detalle, Totales, Adenda.
  • Si emite Notas de Crédito o Notas de Débito, incluye el nodo Referencia con los datos del comprobante referenciado.

Host Factura:

  • Asigna serie y número desde el CAE vigente, en transacción con lock (sin colisiones).
  • Inyecta el nodo CAEData con los datos del CAE asignado.
  • Firma el XML con xmldsig usando el certificado digital (CVA) de la cuenta.
  • Arma el sobre, lo valida contra el XSD oficial y lo envía vía SOAP a DGI.
  • Persiste el CFE emitido y devuelve el resultado en la respuesta.

Elementos que Host Factura inyecta (NO los envíes en tu XML)

ElementoUbicaciónCómo se asigna
IdDoc.SerieDentro del nodo del CFETomado del CAE vigente para el tipo+sucursal
IdDoc.NroDentro del nodo del CFEPróximo número secuencial del CAE (padding a 7 dígitos)
CAEDataDentro del nodo del CFEDatos del CAE asignado (CAE_ID, DNro, HNro, FecVenc)
SignatureNivel raíz <CFE>Firma XML DSig con el certificado digital (CVA) de la cuenta

Si tu XML incluye alguno de estos elementos, respondemos con 400 XML_EXTERNO_CAMPO_PROHIBIDO. Esto es defensivo: evitamos que un proveedor pegue por error el XML completo (con CAE y firma ya inyectados) y termine duplicando datos o firmando dos veces.

Estructura del XML esperado

Aceptamos el envoltorio <CFE xmlns="http://cfe.dgi.gub.uy" version="1.0"> o directamente el nodo interno pelado (<eTck>...).

Estructura mínima (válida para cualquier tipo):

<CFE xmlns="http://cfe.dgi.gub.uy" version="1.0">
  <eTck>  <!-- nodo según TipoCFE; ver mapeo abajo -->
    <Encabezado>
      <IdDoc>
        <TipoCFE>101</TipoCFE>     <!-- obligatorio, numérico -->
        <FchEmis>2026-05-17</FchEmis>
        <FmaPago>1</FmaPago>       <!-- 1=contado, 2=crédito -->
      </IdDoc>
      <Emisor>...</Emisor>
      <Receptor>...</Receptor>     <!-- obligatorio en e-Factura y otros -->
      <Totales>...</Totales>
    </Encabezado>
    <Detalle>
      <Item>...</Item>
    </Detalle>
    <Referencia>...</Referencia>   <!-- obligatorio en Notas de Crédito y Notas de Débito -->
  </eTck>
</CFE>

Mapeo TipoCFE → Nodo XML

El nombre del nodo interno debe corresponder al TipoCFE declarado.

TipoCFEDescripciónNodo XML
101e-TicketeTck
102Nota de Crédito de e-TicketeTck
103Nota de Débito de e-TicketeTck
111e-FacturaeFact
112Nota de Crédito de e-FacturaeFact
113Nota de Débito de e-FacturaeFact
121e-Factura ExportacióneFact_Exp
122Nota de Crédito de e-Factura ExportacióneFact_Exp
123Nota de Débito de e-Factura ExportacióneFact_Exp
124e-Remito ExportacióneRem_Exp
131-133e-Ticket por Cuenta Ajena y sus NotaseTck
141-143e-Factura por Cuenta Ajena y sus NotaseFact
151-153e-Boleta de Entrada y sus NotaseBoleta
181e-RemitoeRem
182e-ResguardoeResg

Emisión de Notas de Crédito y Notas de Débito vía XML

Cuando emitís una Nota de Crédito (tipos 102, 112, 122, 132, 142, 152) o una Nota de Débito (tipos 103, 113, 123, 133, 143, 153), tu XML debe incluir el nodo Referencia dentro del cuerpo del CFE con los datos del comprobante que estás referenciando. Host Factura no completa ese nodo automáticamente en este endpoint (a diferencia del endpoint JSON, donde alcanza con enviar referenciaId).

Estructura del nodo Referencia (resumen — ver XSDs DGI para el detalle):

<Referencia>
  <NroLinRef>1</NroLinRef>
  <TpoDocRef>111</TpoDocRef>     <!-- tipo del CFE referenciado -->
  <Serie>A</Serie>
  <NroCFERef>0001234</NroCFERef>
  <FechaCFEref>2026-05-15</FechaCFEref>
</Referencia>

Importante: el campo FechaCFEref debe ser exactamente la FchEmis del CFE original, no la fecha de creación interna. Si difieren, DGI rechaza con código de error E12.

Numeración

El número se asigna dentro de una transacción con lock sobre la tabla cfes_emitidos. Mantiene consistencia entre llamadas concurrentes y respecto del endpoint JSON: ambos endpoints consumen del mismo CAE secuencialmente, sin colisiones de numeración.

Alcance de sucursal

  • Si tu solicitud se resuelve a una sucursal (por cabecera x-branch-id o por ApiAccess fijado a una sucursal), Host Factura sobrescribe el valor de Emisor.CdgDGISucur con el dgiCode de esa sucursal, incluso si tu XML declaraba otro valor. Esto garantiza coherencia entre el alcance del ApiAccess y el código de sucursal declarado a DGI.
  • Si no hay sucursal resuelta, se respeta el CdgDGISucur que viene en tu XML (típicamente el código de casa central).

Cómo valida Host Factura tu XML

Después de inyectar IdDoc.Serie, IdDoc.Nro, CAEData y la firma digital, Host Factura ejecuta estas validaciones antes de enviar el sobre a DGI:

  1. Validación XSD del sobre completo contra los esquemas oficiales DGI vigentes. Si el sobre no es válido, respondemos con 400 XML_EXTERNO_INVALIDO y el detalle del error de schema. Esto cubre estructura, tipos de datos, cardinalidades y restricciones declaradas en los XSD.
  2. Verificación de firma digital sobre el CFE armado (que Host Factura mismo agrega, por lo que esto solo falla si hay un problema con el certificado digital de la cuenta).
  3. Envío a DGI del sobre firmado. DGI realiza una segunda capa de validaciones (consistencia de montos, cuadre de IVA, validez de la referencia para Notas de Crédito y Notas de Débito, etc.). Si DGI rechaza el sobre, persistimos el CFE con estado: rechazado y el motivoRechazo correspondiente.

Host Factura no valida la consistencia matemática de los montos — esa responsabilidad es tuya (ver sección “Validación de montos a cargo del cliente” arriba). Tampoco modifica los datos de tu XML más allá de los campos listados en la tabla de elementos inyectados.

Cuerpo con el XML del CFE en formato DGI nativo (sin CAE, sin firma, sin Serie/Nro)

object
xml
required
string
>= 1 characters
Ejemplos

XML de e-Ticket (tipo 101)

CFE armado por el cliente al formato DGI nativo. El nodo eTck corresponde al tipo 101. NO incluye CAEData, Signature, IdDoc.Serie ni IdDoc.Nro — los inyecta Host Factura.

{
"xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<CFE xmlns=\"http://cfe.dgi.gub.uy\" version=\"1.0\">\n <eTck>\n <Encabezado>\n <IdDoc>\n <TipoCFE>101</TipoCFE>\n <FchEmis>2026-05-17</FchEmis>\n <FmaPago>1</FmaPago>\n </IdDoc>\n <Emisor>\n <RUCEmisor>210000000000</RUCEmisor>\n <RznSoc>MI EMPRESA S.A.</RznSoc>\n <NomComercial>Mi Empresa</NomComercial>\n <CdgDGISucur>1</CdgDGISucur>\n <DomFiscal>18 de Julio 1234</DomFiscal>\n <Ciudad>Montevideo</Ciudad>\n <Departamento>Montevideo</Departamento>\n </Emisor>\n <Totales>\n <TpoMoneda>UYU</TpoMoneda>\n <MntNetoIVATasaBasica>200</MntNetoIVATasaBasica>\n <IVATasaBasica>22</IVATasaBasica>\n <MntTotal>244</MntTotal>\n <CantLinDet>1</CantLinDet>\n <MntPagar>244</MntPagar>\n </Totales>\n </Encabezado>\n <Detalle>\n <Item>\n <NroLinDet>1</NroLinDet>\n <IndFact>3</IndFact>\n <NomItem>Café americano</NomItem>\n <Cantidad>2.000</Cantidad>\n <UniMed>UN</UniMed>\n <PrecioUnitario>100.00</PrecioUnitario>\n <MontoItem>200.00</MontoItem>\n </Item>\n </Detalle>\n </eTck>\n</CFE>"
}

CFE emitido con éxito. Misma estructura de respuesta que POST /v1/cfe/emitir.

object
data
required
object
id
required
string format: uuid
tipo
required
integer
tipoDescripcion
required
string
serie
required
string
nullable
numero
required
integer
nullable
rutEmisor
required
string
nullable
moneda
required
string
tipoCambio
required
number
nullable
subtotal
required
number
iva
required
number
total
required
number
adenda
required
string
anulado
required
boolean
estado
required
string
Allowed values: pendiente aceptado rechazado observado
estadoDgi
required
string
nullable
fechaEstadoDgi
required
string
nullable
motivoRechazo
required
string
nullable
codigoRespuesta
required
string
codigoSucursalDgi
required
number
nullable
clienteId
required
string
nullable
cuentaId
required
string format: uuid
sucursalId
required
string format: uuid
nullable
puntoEmisionId
required
string format: uuid
nullable
referenciaId
required
string format: uuid
nullable
uuidReferencia
required
string format: uuid
sobreId
required
string format: uuid
nullable
reporteDiarioId
required
string format: uuid
nullable
cfeRecurrenteId
required
string format: uuid
nullable
fechaCreacion
required
string
fechaActualizacion
required
string
Ejemplos

CFE emitido y aceptado por DGI

{
"data": {
"id": "8a7b6c5d-4e3f-2a1b-9c8d-7e6f5a4b3c2d",
"tipo": 111,
"tipoDescripcion": "e-Factura",
"serie": "A",
"numero": 1234,
"rutEmisor": "210000000000",
"moneda": "UYU",
"tipoCambio": 1,
"subtotal": 12500,
"iva": 2750,
"total": 15250,
"adenda": "Forma de pago: transferencia BROU 001-1234567-89",
"anulado": false,
"estado": "aceptado",
"estadoDgi": "A",
"fechaEstadoDgi": "2026-05-17T13:42:11.000Z",
"motivoRechazo": null,
"codigoRespuesta": "00",
"codigoSucursalDgi": 1,
"clienteId": null,
"cuentaId": "1d2e3f4a-5b6c-7d8e-9f0a-1b2c3d4e5f6a",
"sucursalId": null,
"puntoEmisionId": null,
"referenciaId": null,
"uuidReferencia": "9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"sobreId": "2a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d",
"reporteDiarioId": null,
"cfeRecurrenteId": null,
"fechaCreacion": "2026-05-17T13:42:10.000Z",
"fechaActualizacion": "2026-05-17T13:42:11.000Z"
}
}

XML inválido o con campos prohibidos.

CódigoCuándo
XML_EXTERNO_INVALIDOEl XML no parsea, falta IdDoc, TipoCFE no es numérico, el nodo inner no coincide con el tipo, o el sobre construido falla validación XSD
XML_EXTERNO_TIPO_NO_SOPORTADOEl TipoCFE declarado no está en el mapa de tipos soportados
XML_EXTERNO_CAMPO_PROHIBIDOIncluiste CAEData, Signature, IdDoc.Serie o IdDoc.Nro
object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español

string
details

Información adicional (puede ser objeto, array o string)

nullable
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

XML mal formado o sin IdDoc

{
"error": {
"code": "XML_EXTERNO_INVALIDO",
"message": "El CFE debe contener un nodo IdDoc"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}

No autenticado. La API_KEY no fue enviada o no es válida.

Códigos posibles: API_AUTH_HEADER_MISSING, API_AUTH_HEADER_INVALID, API_ACCESS_INVALID.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español

string
details

Información adicional (puede ser objeto, array o string)

nullable
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Falta header Authorization

{
"error": {
"code": "API_AUTH_HEADER_MISSING",
"message": "Se esperaba la cabecera Authorization con esquema Bearer"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}

Acceso prohibido. La credencial es válida pero el plan o el alcance de sucursal lo impiden.

Códigos posibles: API_FEATURE_DISABLED, API_ACCESS_BRANCH_MISMATCH.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español

string
details

Información adicional (puede ser objeto, array o string)

nullable
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

El plan no incluye API

{
"error": {
"code": "API_FEATURE_DISABLED",
"message": "El plan de esta cuenta no tiene habilitado el acceso a la API"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}

Sin CAE disponible para asignar serie y numeración.

Mismas causas que POST /v1/cfe/emitir: NO_CAE_VIGENTE, CAE_RANGE_EXHAUSTED.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español

string
details

Información adicional (puede ser objeto, array o string)

nullable
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

No hay CAE vigente para el tipo solicitado

{
"error": {
"code": "NO_CAE_VIGENTE",
"message": "No hay CAE vigente para eTck"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}

Rate limit excedido. Esperá los segundos indicados en el header Retry-After.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español

string
details

Información adicional (puede ser objeto, array o string)

nullable
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Excediste el rate limit

{
"error": {
"code": "API_RATE_LIMITED",
"message": "Se superó el límite de requests para esta API key"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}
Retry-After
integer
Ejemplo
42

Segundos hasta que se libera la ventana de rate limit

DGI rechazó el sobre o no respondió correctamente. El payload incluye el motivo reportado por DGI. Reintentá tras corregir el XML; los reintentos no consumen número.

object
error
required
object
code
required

Identificador estable del error en SCREAMING_SNAKE_CASE

string
message
required

Mensaje legible en español

string
details

Información adicional (puede ser objeto, array o string)

nullable
requestId

UUID de correlación; mismo valor que el header X-Request-ID

string format: uuid
Ejemplos

Sobre rechazado por DGI

{
"error": {
"code": "INTERNAL_ERROR",
"message": "DGI rechazó el envío (código 89): Sobre con estructura no válida"
},
"requestId": "5b2c7c8a-1f6e-4d29-9a0b-7c3a8d1e2f4c"
}