Si envías e-facturas a un cliente B2B alemán en 2026, el archivo cumple ZUGFeRD o rebota al recibirse. Lo mismo ocurre en Francia con Factur-X. El formato es un wrapper PDF/A-3 con un XML CII EN 16931 adjunto; generarlo desde cero no es trivial, y validarlo exige un motor de referencia.
Ese motor, en la práctica, es Mustang (mustangproject.org): un proyecto Java open-source que extrae el XML embebido desde un PDF/A-3 y lo valida contra EN 16931 Schematron. Tiene el soporte más profundo para ZUGFeRD y Factur-X entre las herramientas abiertas, y es lo que ejecutan muchos verificadores independientes.
Este post recorre los fallos que Mustang señala y una forma más rápida de ejecutarlo.
Qué comprueba Mustang realmente
Cuando pasas un PDF Factur-X o ZUGFeRD a Mustang, hace aproximadamente esto:
- Extrae el archivo embebido. PDF/A-3 almacena attachments en el name tree
/EmbeddedFiles. Mustang busca el nombre canónico (factur-x.xmlpara Factur-X,zugferd-invoice.xmlpara ZUGFeRD 2.x) y lee los bytes. - Comprueba AFRelationship. El adjunto debe declararse como
AFRelationship="Alternative"según la baseline Factur-X / ZUGFeRD. Cualquier otro valor (Source,Data,Supplement) falla. - Comprueba namespace XMP y versión. Factur-X 1.0 usa
urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#. ZUGFeRD 2.x usaurn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#. Namespace o versión incorrectos fallan. - Parsea el XML como Cross-Industry Invoice (CII). Debe ser XML well-formed y empezar con el root CII correcto (
rsm:CrossIndustryInvoice). - Ejecuta EN 16931 Schematron. Es el grueso de la validación: unas 200 reglas de negocio sobre semántica de campos, códigos obligatorios, cálculo de totales, lógica de IVA, identificadores de partes, etc.
Pass = la factura es aceptable para cualquier sistema AP conforme EN 16931 en la UE. Fail = la automatización AP del cliente la rechazará al recibirla y el equipo AR tendrá una excepción manual.
Los cinco fallos que más vemos
Aparecen una y otra vez en el lado Mustang de validator cuando los equipos prueban sus primeras e-facturas.
1. AFRelationship equivocado
ERROR: Embedded file factur-x.xml uses AFRelationship="Source",
expected "Alternative".
La especificación PDF permite varios relationship types para archivos adjuntos. Factur-X / ZUGFeRD exigen Alternative: el XML adjunto es una representación alternativa del contenido visible del PDF. Si tu generador usa Data (default habitual en muchas librerías), Mustang falla de inmediato. El PDF visual todavía se renderiza, pero el payload estructurado no sirve para el sistema AP.
2. Namespace XMP incorrecto o ausente
ERROR: XMP metadata missing fx:DocumentType or fx:DocumentFileName under
namespace urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#.
El paquete XMP del PDF debe declarar qué perfil Factur-X es (MINIMUM, BASIC, EN 16931, EXTENDED) y qué nombre de archivo debe buscarse. Es fácil omitirlo al escribir el wrapper PDF/A-3 a mano; el endpoint /api/v1/e-invoice/render de gPdf lo emite automáticamente.
3. XML CII bien formado, pero falla EN 16931 Schematron
ERROR: BR-CO-25 — In an invoice (BR-01) the
ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime is required when
ram:DocumentTypeCode is 380.
Aquí vive la mayoría de fallos reales. El XML es sintácticamente válido; fallan las reglas de negocio. Las reglas EN 16931 Schematron tienen IDs estables (BR-01, BR-CO-25, etc.) consultables en la especificación. Comunes:
- BR-01: la factura debe tener número único.
- BR-04: debe tener fecha de emisión.
- BR-05: debe tener invoice type code.
- BR-CO-25: payment terms obligatorios cuando el tipo es “Commercial invoice”.
- BR-Z-01: VAT category code debe ser uno de
S,Z,E,AE,K,G,O,L,M.
Corrige los datos fuente, reconstruye y vuelve a validar.
4. El wrapper PDF/A no valida
INFO: CII XML extracted and validates against EN 16931.
ERROR: PDF/A-3b conformance check failed: missing Output Intent.
En este caso Mustang pasa el XML, pero falla el PDF/A-3 subyacente. Causa típica: alguien generó bien el XML pero emitió un PDF normal en vez de PDF/A-3. El archivo embebido existe, pero no se cumplen las reglas de archivo. El validator de gpdf.com/validator/ lo detecta ejecutando veraPDF en paralelo: el fail aparece en la columna veraPDF mientras Mustang muestra XML pass.
5. Encoding / declaration no coinciden
ERROR: XML declares <?xml version="1.0" encoding="UTF-8"?> but the
embedded byte stream is UTF-8 with BOM. Mustang strict mode rejects BOM.
Es más común de lo que parece cuando la herramienta XML emite UTF-8 BOM y luego se incrusta raw. La solución: eliminar el BOM antes de embeberlo. El endpoint de e-invoice de gPdf normaliza esto.
Cómo ejecutar Mustang sin instalar Java
Instalar Java + Mustang CLI está bien para una comprobación puntual. Para verificación continua — cada factura generada, cada CI que afirma compliance — es fricción innecesaria.
gpdf.com/validator/ ejecuta Mustang en el navegador:
- Arrastra el PDF Factur-X / ZUGFeRD al área de upload.
- El validator extrae el XML embebido y ejecuta el Schematron engine de Mustang (compilado a JavaScript / WebAssembly, corriendo en Cloudflare Worker).
- El report Mustang vuelve junto al report PDF/A-3 de veraPDF, porque ambas capas deben pasar.
- Descarga el report JSON como evidencia de QA.
Sin login. Sin cuota. El mismo tipo de Mustang que instalarías vía Maven, servido como servicio público gratuito.
TL;DR
Mustang señala 5 fallos comunes; la mayoría se reduce a “el archivo fue generado por una herramienta que no emite un PDF/A-3 Factur-X / ZUGFeRD completo”. La E-invoice API de gPdf lo emite en una llamada. validator verifica el resultado con Mustang + veraPDF en paralelo.
La mayoría de bugs que atrapa Mustang están en el wrapper o en AFRelationship, no solo en la semántica XML. Generar bien el archivo resuelve gran parte; el validator es el recibo que lo prueba.