Skip to main content

Qué es

Si tu sistema ya arma el CFDI 4.0 (por ejemplo tu ERP), no tienes por qué desarmarlo a JSON para que Ipsofactura lo vuelva a construir del otro lado. POST /cfdi/timbrar-xml recibe tu XML sin sellar y hace exactamente lo que no puedes hacer tú: sellarlo con el CSD que tenemos en custodia y timbrarlo, por el mismo pipeline que usa la ruta JSON.
Reparto de trabajo: tú armas el documento, nosotros lo sellamos y timbramos. No envíes Sello, NoCertificado ni Certificado — el sellado es nuestro.

El documento es la fuente de verdad

A diferencia de POST /cfdi/timbrar (donde tú describes un comprobante y Ipsofactura completa los defaults que falten), aquí el XML es un comprobante terminado. Un valor que ya viene en el documento no se sobrescribe. Los únicos huecos que Ipsofactura llena son:
  • Folio, si el documento no lo trae: se asigna el siguiente consecutivo para ese emisor + serie (igual que en la ruta JSON).
  • Datos del emisor que el documento deje vacíos, cuando el XSD del SAT los permite opcionales (en la práctica esto casi nunca aplica: cfdv40.xsd ya exige Nombre y RegimenFiscal del emisor).
Todo lo demás —incluido, por ejemplo, el DomicilioFiscalReceptor de un receptor extranjero— se conserva tal cual lo trae el documento. Si eso produce un rechazo del SAT, la respuesta es honesta: es el documento que enviaste, no uno que reescribimos.

Request

cURL
El ejemplo de arriba es real y se puede timbrar tal cual en sandbox, pero su Fecha está fija (2026-08-11T12:00:00). El SAT sólo acepta un comprobante fechado dentro de una ventana de 72 horas hacia atrás y 5 minutos hacia adelante respecto al momento en que se timbra — así que para usarlo: decodifica el base64, ajusta Fecha a un valor dentro de esa ventana, y vuelve a codificar a base64 antes de enviarlo.
La respuesta es la misma TimbrarResponse de POST /cfdi/timbrar, sin importar si el documento era un ingreso, egreso o complemento de pago.

Ruteo por tipo de comprobante

Ipsofactura lee TipoDeComprobante del propio XML para decidir el pipeline:
  • I (ingreso) / E (egreso) → se timbra directo, igual que POST /cfdi/timbrar.
  • P (pago) → se rutea al pipeline de complemento de pago: la parcialidad y el saldo se recalculan a partir del historial de pagos ya registrados contra los CFDI que el documento relaciona, no de lo que el XML declare. Si necesitas declarar tú los saldos —porque llevas tu propia contabilidad, o aplicaste un egreso antes del REP— usa POST /cfdi/complemento-pago, que sí los acepta explícitos.
La ventana de 72 h / 5 min del SAT se mide sobre la Fecha del documento, no sobre el instante en que llega la request — si tu ERP despacha una cola con retraso, re-fecha el XML antes de reenviarlo.

Tope de tamaño

El XML decodificado no puede exceder 2 MB (dos órdenes de magnitud por encima del CFDI más grande que hemos visto). Por encima del tope, la respuesta es 413 REQUEST_TOO_LARGE.

Rechazos con 400

XSD_VALIDATION_ERROR
UNSUPPORTED_CFDI_CONTENT

Sandbox

En el sandbox, este endpoint devuelve un CFDI completo — construido con el mismo conversor que usa producción, con cfdi:Conceptos, cfdi:Impuestos y su TimbreFiscalDigital, no un stub recortado. La única diferencia es que el material de sellado es obviamente falso:
  • Sello y Certificado llevan marcadores SANDBOX_… (no son una firma real).
  • NoCertificado reutiliza el serial real del CSD que registraste, así que sigue teniendo forma de dato válido.
  • La validación criptográfica de ese sello falla a propósito — un CFDI de sandbox no tiene, ni debe tener, validez fiscal.
  • El PDF que acompaña al CFDI de sandbox sigue siendo un placeholder de una página.
Esto te deja probar tu parser contra la forma real de un CFDI timbrado, sin arriesgar que tu integración sólo funcione contra un documento simplificado que producción nunca envía.

Ver también