Qué es Facturae 3.2.2 y por qué lo exige la Administración
Facturae es el formato oficial de factura electrónica en España, definido por el Ministerio de Hacienda y la Agencia Tributaria. Existe desde 2007, pero su uso se volvió obligatorio para facturas a la Administración Pública con la Ley 25/2013 y, más recientemente, la Ley Crea y Crece amplía esa obligación al sector privado.
La versión actual es Facturae 3.2.2 — un fichero XML que sigue un esquema XSD publicado por la AEAT. Para que FACe (el Punto General de Entrada de Facturas Electrónicas de la AGE) lo acepte, ese XML debe ir firmado digitalmente con XAdES-BES usando un certificado reconocido, normalmente emitido por la FNMT.
Si alguno de tus clientes factura a un ayuntamiento, a una Comunidad Autónoma, a un ministerio o a cualquier organismo público, ese cliente necesita Facturae firmado. Y si tu ERP no lo genera, ese cliente tiene un problema que probablemente resuelve fuera de tu software.
La firma XAdES-BES: lo que hace difícil la implementación
La parte más complicada de Facturae no es el XML en sí — es la firma. XAdES-BES (XML Advanced Electronic Signatures — Basic Electronic Signature) es el estándar ETSI EN 319 132 para firmar documentos XML. No es lo mismo que una firma PDF, ni que una firma XMLDSig básica.
La diferencia clave es el bloque <xades:QualifyingProperties>. Una firma XMLDSig básica solo garantiza que el contenido no fue modificado. XAdES-BES añade metadatos cualificados: quién firmó (el certificado completo del firmante), cuándo firmó (timestamp de firma), y bajo qué política. Sin estos elementos, FACe devuelve un error de firma inválida aunque el certificado sea válido y el XML esté bien formado.
Además, hay decisiones técnicas que no son obvias:
- El algoritmo de canonicalización debe ser XML-C14N (Canonical XML 1.0), no la versión exclusiva.
- El algoritmo de firma debe ser RSA-SHA256 — SHA-1 está rechazado desde 2023.
- La referencia debe apuntar al elemento raíz
<fe:Facturae>conURI=""y una transformaciónenveloped-signature. - El certificado FNMT debe incluirse completo en Base64 dentro de
<ds:X509Certificate>, no solo el thumbprint. - El NIF del titular del certificado debe coincidir exactamente con el
TaxIdentificationNumberdel emisor en el XML.
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" y el bloque QualifyingProperties.
Implementarlo desde cero: las 6 partes del trabajo
Si decides implementar Facturae internamente en tu ERP, esto es lo que tienes por delante:
FileHeader, Parties, Invoices, InvoiceLine, tipos impositivos, periodos... Hay casos no obvios como múltiples tipos de IVA en la misma factura o retenciones IRPF..p12 (PKCS#12) protegidos con contraseña. Tienes que cargarlos, extraer la clave privada y el certificado X.509, y gestionar su almacenamiento seguro. También tienes que manejar la caducidad (2 años para persona jurídica) y avisar al usuario antes de que expire.signxml o lxml + xmlsec, pero la configuración XAdES correcta requiere código adicional. En PHP, Java y .NET hay más opciones, pero todas requieren entender la especificación ETSI para configurarlas correctamente.El coste real de hacerlo internamente
Aquí está la comparativa honesta entre implementarlo desde cero y usar una API:
| Concepto | Implementación propia | API REST (FacturaX) |
|---|---|---|
| Tiempo hasta producción | 3–6 semanas de desarrollo | 1 día de integración |
| Conocimiento requerido | XML avanzado, criptografía XAdES, certificados PKCS#12, esquema Facturae | HTTP REST, JSON básico |
| Gestión de certificados | Almacenamiento seguro, rotación, avisos de caducidad — a implementar | El usuario sube su .p12 una vez, FacturaX lo gestiona |
| Cambios normativos | Tu equipo actualiza el código | Transparente — FacturaX absorbe los cambios |
| Coste por factura | Coste de desarrollo amortizado + mantenimiento | Desde 0,09€/factura (volumen) |
| Custodia legal 4 años | A implementar por separado | Incluida en cada factura firmada |
| Validación antes de FACe | A implementar | Automática en cada llamada |
Si son cientos o miles al mes, la integración tiene ROI claro. Si son decenas, la API es más eficiente. Hablamos.
La alternativa: API REST, un día de integración
La API de FacturaX acepta un JSON con los campos de la factura (o directamente un PDF) y devuelve el XML Facturae 3.2.2 firmado con XAdES-BES, listo para subir a FACe. No tienes que saber nada de XAdES, de certificados PKCS#12 ni del esquema Facturae.
El flujo de integración desde tu ERP es:
- El usuario de tu ERP sube su certificado
.p12una vez en su perfil (guardado cifrado con Fernet AES-128) - Cuando genera una factura para la Administración, tu ERP llama a
POST /convertcon el JSON de la factura - La respuesta incluye el XML firmado (
xml), el fichero en Base64 (xml_base64) y el nombre del fichero (filename) - Tu ERP descarga el
.xsigy el usuario lo sube a FACe — o lo envías tú automáticamente si integras con la API de FACe
El endpoint acepta tres modos según cómo tengas los datos en tu ERP:
- Modo PDF — envías el PDF de la factura en
multipart/form-data. La IA extrae los campos automáticamente. Útil si recibes facturas de proveedores en PDF. - Modo JSON — envías los campos estructurados directamente. Es el modo natural para un ERP donde ya tienes los datos.
- Modo log_id — si ya extrajiste la factura con
/extract, puedes generar el XML firmado con{"from_log_id": 123}sin consumir un crédito adicional. Todo el flujo de dos pasos cuenta como una sola factura.
Ejemplos de código: Python, PHP y Node.js
Los tres ejemplos hacen lo mismo: envían una factura JSON a la API y guardan el XML firmado en disco.
import requests, json API_KEY = "fct_tu_api_key" BASE_URL = "https://api.facturax.app" HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"} factura = { # Emisor (tu cliente) "vendor_name": "Servicios Tech S.L.", "vendor_vat": "B12345678", "vendor_address": "Calle Mayor 1", "vendor_city": "Madrid", "vendor_postal_code": "28001", # Receptor (organismo público) "buyer_name": "Ayuntamiento de Alcobendas", "buyer_vat": "P2800200B", # Datos de la factura "invoice_number": "F2024-001", "invoice_date": "2024-06-15", "due_date": "2024-07-15", "payment_method": "transferencia", "iban": "ES21 0049 0001 5021 0001 3303", # Importes "subtotal": 1000.00, "declared_tax_rate": 21, "tax": 210.00, "total": 1210.00, # Líneas de factura "line_items": [ { "description": "Mantenimiento software junio 2024", "quantity": 1, "unit_price": 1000.00, "total": 1000.00, "tax_rate": 21 } ] } r = requests.post(f"{BASE_URL}/convert", headers=HEADERS, json=factura) r.raise_for_status() data = r.json() # Guardar el XML firmado with open(data["filename"], "w", encoding="utf-8") as f: f.write(data["xml"]) print(f"Firmado: {data['filename']} · Signed: {data['signed']}")
<?php $API_KEY = 'fct_tu_api_key'; $BASE_URL = 'https://api.facturax.app'; $factura = [ // Emisor 'vendor_name' => 'Servicios Tech S.L.', 'vendor_vat' => 'B12345678', 'vendor_address' => 'Calle Mayor 1', 'vendor_city' => 'Madrid', 'vendor_postal_code' => '28001', // Receptor 'buyer_name' => 'Ayuntamiento de Alcobendas', 'buyer_vat' => 'P2800200B', // Factura 'invoice_number' => 'F2024-001', 'invoice_date' => '2024-06-15', 'payment_method' => 'transferencia', 'subtotal' => 1000.00, 'declared_tax_rate' => 21, 'tax' => 210.00, 'total' => 1210.00, 'line_items' => [[ 'description' => 'Mantenimiento software junio 2024', 'quantity' => 1, 'unit_price' => 1000.00, 'total' => 1000.00, 'tax_rate' => 21 ]] ]; $ch = curl_init("$BASE_URL/convert"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($factura), CURLOPT_HTTPHEADER => [ "X-API-Key: $API_KEY", 'Content-Type: application/json', ], ]); $response = json_decode(curl_exec($ch), true); curl_close($ch); // Guardar el XML firmado file_put_contents($response['filename'], $response['xml']); echo "Firmado: {$response['filename']}\n";
const API_KEY = 'fct_tu_api_key'; const BASE_URL = 'https://api.facturax.app'; const factura = { // Emisor vendor_name: 'Servicios Tech S.L.', vendor_vat: 'B12345678', vendor_address: 'Calle Mayor 1', vendor_city: 'Madrid', vendor_postal_code: '28001', // Receptor buyer_name: 'Ayuntamiento de Alcobendas', buyer_vat: 'P2800200B', // Factura invoice_number: 'F2024-001', invoice_date: '2024-06-15', payment_method: 'transferencia', subtotal: 1000.00, declared_tax_rate: 21, tax: 210.00, total: 1210.00, line_items: [{ description: 'Mantenimiento software junio 2024', quantity: 1, unit_price: 1000.00, total: 1000.00, tax_rate: 21 }] }; const res = await fetch(`${BASE_URL}/convert`, { method: 'POST', headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify(factura) }); const data = await res.json(); // Guardar el XML firmado const fs = require('fs'); fs.writeFileSync(data.filename, data.xml, 'utf8'); console.log(`Firmado: ${data.filename} · signed: ${data.signed}`);
curl -X POST https://api.facturax.app/convert \ -H "X-API-Key: fct_tu_api_key" \ -H "Content-Type: application/json" \ -d '{ "vendor_name": "Servicios Tech S.L.", "vendor_vat": "B12345678", "buyer_name": "Ayuntamiento de Alcobendas", "buyer_vat": "P2800200B", "invoice_number": "F2024-001", "invoice_date": "2024-06-15", "subtotal": 1000.00, "declared_tax_rate": 21, "tax": 210.00, "total": 1210.00, "line_items": [{ "description": "Mantenimiento software junio 2024", "quantity": 1, "unit_price": 1000.00, "total": 1000.00, "tax_rate": 21 }] }'
La respuesta en todos los casos es un JSON con el campo xml (el XML Facturae firmado como string), xml_base64 (para descargas binarias), signed (booleano), filename y log_id para referencia futura.
payment_method o vendor_country) está documentada en facturax.app/api-docs, junto con la colección Postman descargable.
¿Cuándo implementarlo tú y cuándo usar una API?
No es una respuesta única para todos. Depende del volumen y del perfil de tus clientes:
Tiene sentido implementarlo internamente si...
- Tienes miles de facturas al mes para la Administración y el coste por llamada API es significativo.
- Necesitas integración directa con FACe vía servicios web (no solo generar el XML).
- Tienes un equipo con experiencia en criptografía XML y capacidad de mantenimiento.
- Tu producto es un ERP especializado en contratación pública donde Facturae es el core.
Tiene sentido usar la API si...
- Facturae es una funcionalidad secundaria que necesitan solo algunos de tus clientes.
- Quieres salir al mercado rápido sin dedicar semanas a la implementación.
- No tienes un perfil técnico con experiencia en XAdES en el equipo.
- Quieres que los cambios normativos (VeriFACTU, nuevas versiones del esquema) los absorba el proveedor.
- El volumen mensual es menor de unos pocos miles de facturas — la economía de la API es mejor que el desarrollo propio.
Documentación completa, ejemplos de código en Python, PHP y Node.js, y soporte por email. El Pack S (9,90€, 15 facturas) cubre el desarrollo y las pruebas.
Preguntas frecuentes
¿Cuánto tiempo lleva implementar Facturae 3.2.2 con XAdES-BES desde cero?
Entre 3 y 6 semanas para un programador con experiencia en XML y criptografía, sin contar las pruebas con FACe ni la gestión de certificados. Los imprevistos más frecuentes son la canonicalización XML (que no es trivial) y los casos especiales del esquema Facturae (múltiples tipos de IVA, facturas rectificativas, IRPF).
¿Qué es la firma XAdES-BES y por qué la exige FACe?
XAdES-BES (XML Advanced Electronic Signatures — Basic Electronic Signature) es el estándar ETSI de firma digital para XML. FACe lo exige por el Real Decreto 1619/2012 para garantizar la autenticidad e integridad de las facturas. Sin esta firma, FACe rechaza el fichero aunque el XML sea formalmente correcto.
¿Puedo integrar Facturae en mi ERP con una API REST?
Sí. La API de FacturaX recibe un JSON con los campos de la factura y devuelve el XML Facturae 3.2.2 firmado con XAdES-BES. La integración se puede completar en un día de desarrollo. La documentación completa y la colección Postman están disponibles en facturax.app/api-docs.
¿Qué pasa cuando cambia la normativa de Facturae?
Si implementas Facturae internamente, cualquier cambio normativo requiere actualizar tu código. La Ley Crea y Crece, VeriFACTU y las posibles nuevas versiones del esquema XSD son cambios que ya están en el horizonte. Si usas la API de FacturaX, esos cambios los absorbemos nosotros sin que tengas que tocar tu integración.
¿Necesito un certificado FNMT para cada cliente de mi ERP?
Sí. Cada empresa emisora debe tener su propio certificado FNMT porque FACe verifica que el NIF del certificado coincida con el NIF del emisor en el XML. Con la API de FacturaX (planes Pro y Business), puedes subir y gestionar múltiples certificados de clientes con la función de certificados gestionados — cada cliente sube su .p12 una vez y tú lo referencias con un certificate_id en cada llamada.