API de VeriFactu: cómo se integra de verdad
Todo lo que necesita un equipo de desarrollo para conectar un sistema de facturación con la AEAT: ciclo completo del registro de facturación, campos del registro de alta según la Orden HAC/1177/2024, anulaciones, rectificativas, diferencias reales entre las dos modalidades y cómo se expone el flujo dentro de ERPNext.
VeriFactu se integra por servicio web SOAP con certificado electrónico, no por API REST. Por cada factura, tu sistema genera un registro de facturación de alta, lo encadena con la huella SHA-256 del registro anterior, imprime el QR en el documento y lo remite en lotes de hasta 1.000 registros respetando el tiempo de espera que devuelve la propia Agencia en cada respuesta.
De la factura al acuse de la AEAT, paso a paso
Conviene tener el ciclo entero en la cabeza antes de escribir la primera línea, porque la mitad de los problemas de integración vienen de haber colocado un paso en el sitio equivocado. Este es el orden real, tal y como lo describen el RD 1007/2023 y los esquemas publicados por la Agencia.
- 01
Se cierra la factura en el sistema emisor
El reglamento exige que el registro de facturación se genere en el momento de expedir la factura, no en un proceso posterior. Si tu arquitectura contempla un job nocturno que recorre lo facturado durante el día, ya estás fuera de la norma aunque el XML sea impecable.
- 02
Se rellenan los datos identificativos
IDVersion, el bloque IDFactura con NIF del emisor, serie y fecha de expedición, el nombre o razón social del emisor, el tipo de factura y una descripción de la operación que no puede quedarse vacía.
- 03
Se construye el desglose fiscal
Hasta doce líneas, cada una con impuesto, clave de régimen y calificación de la operación o causa de exención, más base, tipo y cuota. Es el punto donde se traduce la configuración de impuestos de tu ERP a los códigos de la Agencia, y donde más tiempo se va en un proyecto real.
- 04
Se cierra el encadenamiento
O el registro es el primero de la cadena, y entonces se marca como tal, o incorpora el emisor, la serie, la fecha y la huella del registro inmediatamente anterior. El orden lo marca la generación del registro, no el número de factura.
- 05
Se identifica el sistema informático
Un bloque completo con el productor del software, el nombre y la versión del sistema, el número de instalación y tres indicadores sobre si puede operar en modo multiobligado. Va en cada registro, no en la cabecera.
- 06
Se sella la fecha y hora de generación
En ISO-8601 con huso horario explícito. Es un campo con peso jurídico: fija el momento en que el registro existió y ordena la cadena.
- 07
Se calcula la huella
SHA-256 sobre la cadena de campos concatenados en un orden estricto, con TipoHuella a 01. El detalle del algoritmo, con la cadena previa y un ejemplo completo, está en nuestra guía específica del hash.
- 08
Se firma, sólo si operas en NO VERI*FACTU
La firma electrónica XAdES del registro es la contrapartida a no enviar nada a la Agencia. En modalidad VERI*FACTU el propio envío hace de anclaje temporal y la firma no se exige.
- 09
Se imprime el QR en la factura
Obligatorio en las dos modalidades y en todas las facturas expedidas por un sistema informático de facturación, incluidas las simplificadas.
- 10
Se remite el lote a la AEAT
Mensaje RegFactuSistemaFacturacion con una cabecera que identifica al obligado tributario y hasta 1.000 registros de alta o de anulación mezclados. Transporte SOAP sobre HTTPS con TLS mutuo.
- 11
Se procesa la respuesta línea a línea
La Agencia devuelve un CSV de presentación, un estado global del envío y un estado por cada registro. Un envío puede volver como ParcialmenteCorrecto: hay que leer las líneas, no sólo la cabecera.
- 12
Se respeta el control de flujo
Cada respuesta trae el tiempo que hay que esperar antes del siguiente envío. Ignorarlo es la forma más rápida de que la Agencia empiece a rechazarte peticiones.
Los pasos 07 y 09 están documentados aparte con el nivel de detalle que piden: el algoritmo de la huella y el encadenamiento, en cómo se calcula el hash SHA-256 de cada registro; la especificación del código de barras bidimensional, en qué contiene exactamente el QR y cómo se imprime.
Los campos del registro de facturación de alta
El contenido del registro lo fija el RD 1007/2023 y lo desarrolla la Orden HAC/1177/2024; la forma exacta, orden incluido, la fija el esquema SuministroInformacion.xsd que publica la Agencia. Esta tabla sigue ese orden. El XSD es la referencia buena: cuando una documentación de terceros y el esquema no coincidan, manda el esquema.
| Campo | Tipo / valores | Oblig. | Notas de implementación |
|---|---|---|---|
| IDVersion | Enumerado | Sí | Versión del formato de registro que publica la AEAT. Viaja en todos los registros y permite versionar el esquema sin romper integraciones antiguas. |
| IDFactura | Bloque | Sí | Contiene IDEmisorFactura (NIF de 9 caracteres), NumSerieFactura (hasta 60 caracteres) y FechaExpedicionFactura en formato dd-mm-yyyy. Es la clave funcional del registro. |
| RefExterna | Texto (60) | No | Referencia libre del emisor. El sitio natural para meter tu identificador interno y poder casar la respuesta de la AEAT con tu propia tabla. |
| NombreRazonEmisor | Texto (120) | Sí | Nombre o razón social de quien expide la factura. |
| Subsanacion | S / N | No | Se marca con S cuando el registro corrige a otro que ya se remitió. No sustituye al anterior: se añade a la cadena. |
| RechazoPrevio | N / S / X | No | Indica si el registro que se subsana fue rechazado previamente y por quién. Va de la mano de Subsanacion. |
| TipoFactura | F1, F2, F3, R1-R5 | Sí | F1 factura completa, F2 simplificada, F3 sustitutiva de simplificadas y R1 a R5 las rectificativas. Determina qué otros campos pasan a ser obligatorios. |
| TipoRectificativa | S / I | No | S para rectificativa por sustitución, I para rectificativa por diferencias (incremental). Obligatorio en cuanto TipoFactura es R1-R5. |
| FacturasRectificadas | Lista (máx. 1.000) | No | Identificación de las facturas que se rectifican, con emisor, serie y fecha de expedición de cada una. |
| FacturasSustituidas | Lista (máx. 1.000) | No | Identificación de las facturas simplificadas que sustituye una F3. |
| ImporteRectificacion | Bloque | No | Importes rectificados: base, cuota y recargo. Tiene sentido cuando la rectificativa es por sustitución. |
| FechaOperacion | Fecha | No | Fecha de la operación cuando difiere de la de expedición. |
| DescripcionOperacion | Texto (500) | Sí | Descripción funcional de la operación. Es obligatorio y no admite quedarse vacío, así que conviene mapearlo a algo con sentido y no a una constante. |
| FacturaSimplificadaArt7273 | S / N | No | Marca la factura simplificada cualificada de los artículos 7.2 y 7.3 del Reglamento de facturación. |
| FacturaSinIdentifDestinatarioArt61d | S / N | No | Marca la factura completa emitida sin identificar al destinatario al amparo del artículo 6.1.d. |
| Macrodato | S / N | No | Marca el registro cuando el importe total supera el umbral de gran cuantía que fija la AEAT. |
| EmitidaPorTerceroODestinatario | D / T | No | D cuando la expide el destinatario (autofacturación), T cuando la expide un tercero. |
| Tercero | Bloque | No | Nombre o razón social y NIF (o identificador extranjero) del tercero que expide. |
| Destinatarios | Lista (máx. 1.000) | No | Cada destinatario con nombre o razón social y NIF, o bien un identificador de otro país. Las validaciones de la AEAT lo exigen en la práctica para las facturas completas. |
| Cupon | S / N | No | Indica que la operación incorpora un cupón. |
| Desglose | Bloque (máx. 12 líneas) | Sí | Hasta doce líneas DetalleDesglose. Es la parte que más trabajo da al mapear desde un ERP, porque hay que traducir tu configuración de impuestos a los códigos de la AEAT. |
| CuotaTotal | Importe 12,2 | Sí | Suma de cuotas repercutidas. Tiene que cuadrar con el desglose o el registro se rechaza. |
| ImporteTotal | Importe 12,2 | Sí | Importe total de la factura, con signo. |
| Encadenamiento | Bloque | Sí | O bien PrimerRegistro con valor S, o bien RegistroAnterior con el emisor, la serie, la fecha de expedición y la huella del registro precedente. |
| SistemaInformatico | Bloque | Sí | Identifica al productor del software y a la instalación concreta. Ver detalle más abajo. |
| FechaHoraHusoGenRegistro | dateTime ISO-8601 | Sí | Momento de generación del registro con huso horario explícito. Es el campo por el que se ordena la cadena. |
| NumRegistroAcuerdoFacturacion | Texto (15) | No | Número del acuerdo de facturación por el destinatario o por terceros autorizado por la AEAT. |
| IdAcuerdoSistemaInformatico | Texto (16) | No | Identificador del acuerdo sobre el sistema informático, cuando exista. |
| TipoHuella | 01 | Sí | 01 identifica SHA-256, el único algoritmo previsto por ahora. |
| Huella | Texto (64) | Sí | Hash hexadecimal del registro. Cómo se construye la cadena previa está detallado en la guía del algoritmo. |
| Signature | XAdES | No en el esquema | Opcional en el XSD, pero obligatorio de hecho para quien opera en modalidad NO VERI*FACTU. |
Fuentes: esquemas XSD de la AEAT, diseños de registro y Orden HAC/1177/2024. Longitudes y cardinalidades verificadas contra los XSD publicados en julio de 2026; revisa siempre la versión vigente antes de un despliegue.
El bloque SistemaInformatico, campo a campo
Es el bloque que más se subestima. No describe la factura: describe quién hizo el programa y qué instalación concreta lo está ejecutando. Si desarrollas software para clientes, aquí es donde te identificas ante la Agencia en cada registro que emiten ellos.
| Elemento | Qué lleva |
|---|---|
| NombreRazon | Nombre o razón social del productor del software (hasta 120 caracteres). |
| NIF o IDOtro | NIF español del productor o identificador equivalente si es extranjero. |
| NombreSistemaInformatico | Nombre comercial del sistema (hasta 30 caracteres). |
| IdSistemaInformatico | Código de dos caracteres que el propio productor asigna a cada uno de sus sistemas. |
| Version | Versión del sistema (hasta 50 caracteres). |
| NumeroInstalacion | Identificador de la instalación concreta (hasta 100 caracteres). Distingue una implantación de otra. |
| TipoUsoPosibleSoloVerifactu | S si el sistema únicamente puede operar en modalidad VERI*FACTU. |
| TipoUsoPosibleMultiOT | S si el sistema puede dar servicio a varios obligados tributarios. |
| IndicadorMultiplesOT | S si en esa instalación concreta está dando servicio a varios obligados. |
Esqueleto del mensaje de envío
Reducido a lo esencial para que se vea la estructura: una cabecera con el obligado tributario y la lista de registros. Los prefijos de espacio de nombres y el detalle completo salen del WSDL y de los XSD oficiales.
<RegFactuSistemaFacturacion>
<Cabecera>
<ObligadoEmision>
<NombreRazon>DISTRIBUCIONES LOPEZ SL</NombreRazon>
<NIF>B12345678</NIF>
</ObligadoEmision>
<RemisionVoluntaria>
<FechaFinVeriFactu>31-12-2027</FechaFinVeriFactu>
</RemisionVoluntaria>
</Cabecera>
<RegistroFactura>
<RegistroAlta>
<IDVersion>...</IDVersion>
<IDFactura>
<IDEmisorFactura>B12345678</IDEmisorFactura>
<NumSerieFactura>2027-A/000145</NumSerieFactura>
<FechaExpedicionFactura>15-01-2027</FechaExpedicionFactura>
</IDFactura>
<NombreRazonEmisor>DISTRIBUCIONES LOPEZ SL</NombreRazonEmisor>
<TipoFactura>F1</TipoFactura>
<DescripcionOperacion>Suministro de material de oficina</DescripcionOperacion>
<Destinatarios>...</Destinatarios>
<Desglose>...</Desglose>
<CuotaTotal>210.00</CuotaTotal>
<ImporteTotal>1210.00</ImporteTotal>
<Encadenamiento>
<RegistroAnterior>...</RegistroAnterior>
</Encadenamiento>
<SistemaInformatico>...</SistemaInformatico>
<FechaHoraHusoGenRegistro>2027-01-15T10:42:11+01:00</FechaHoraHusoGenRegistro>
<TipoHuella>01</TipoHuella>
<Huella>...</Huella>
</RegistroAlta>
</RegistroFactura>
</RegFactuSistemaFacturacion>Esqueleto simplificado con fines ilustrativos: faltan espacios de nombres y campos opcionales. No lo copies a producción sin validarlo contra los XSD vigentes.
Anulaciones, rectificativas y subsanaciones
Hay tres formas distintas de arreglar algo y se confunden con muchísima frecuencia. Ninguna de las tres borra nada: las tres añaden registros a la cadena. Si tu modelo de datos permite un DELETE sobre la tabla de registros, el diseño está mal desde el principio.
| Situación | Qué se genera | Campos que entran en juego |
|---|---|---|
| La factura deja de existir | Registro de facturación de anulación | IDVersion, IDFactura, SinRegistroPrevio, RechazoPrevio, GeneradoPor y Generador, Encadenamiento, SistemaInformatico, FechaHoraHusoGenRegistro, TipoHuella y Huella |
| La factura se corrige con otra factura | Nuevo registro de alta de tipo R1 a R5 | TipoRectificativa (S o I), FacturasRectificadas y, en las rectificativas por sustitución, ImporteRectificacion |
| Varias simplificadas se sustituyen por una completa | Nuevo registro de alta de tipo F3 | FacturasSustituidas |
| El registro remitido tenía un error, la factura no | Nuevo registro de alta con Subsanacion | Subsanacion a S y, si la AEAT lo había rechazado, RechazoPrevio |
El registro de anulación es notablemente más corto que el de alta: no lleva desglose, ni importes, ni destinatarios. Sólo identifica la factura que se anula, dice quién genera el registro y cierra con encadenamiento, sistema informático, fecha y huella. Eso sí, entra en la misma cadena que las altas y se envía en el mismo mensaje: un lote puede mezclar altas y anulaciones sin problema.
Sobre los tipos de rectificativa, la clasificación es la misma que ya se usa en el suministro inmediato de información: R1 para el error fundado en derecho y los supuestos del artículo 80, apartados uno, dos y seis de la Ley del IVA; R2 para el artículo 80.tres, concurso de acreedores; R3 para el artículo 80.cuatro, créditos incobrables; R4 para el resto; R5 para las rectificativas de facturas simplificadas. Y la distinción entre sustitutiva e incremental no es cosmética: la sustitutiva reemplaza el contenido y obliga a informar los importes que se rectifican; la incremental sólo declara la diferencia.
Un detalle que vale oro en producción: el campo SinRegistroPrevio del registro de anulación existe para el caso en que se anula una factura cuyo registro de alta nunca llegó a remitirse. Ocurre, y tenerlo previsto en el código evita un atasco incómodo en el cierre de un ejercicio.
VERI*FACTU frente a NO VERI*FACTU: qué cambia en el código
Se explica siempre como «enviar o no enviar», y esa simplificación despista a quien tiene que estimar el trabajo. La generación del registro es idéntica en las dos: mismos campos, misma huella, mismo encadenamiento, mismo QR. Lo que cambia es todo lo que rodea al registro una vez creado.
| Aspecto | VERI*FACTU | NO VERI*FACTU |
|---|---|---|
| Remisión a la AEAT | Continua y automática, con control de flujo | Ninguna de forma rutinaria; sólo ante requerimiento |
| Firma electrónica del registro | No exigida: el envío actúa de anclaje | Obligatoria, formato XAdES |
| Registro de eventos del sistema | No exigido | Obligatorio: arranques, paradas, anomalías, exportaciones |
| Conservación | La Agencia guarda copia de lo remitido | Íntegra a cargo del obligado, con capacidad de exportación |
| Certificado electrónico | Necesario para el canal SOAP | Necesario para firmar cada registro |
| Leyenda en la factura | Menciona que es una factura verificable en la sede de la AEAT | Sin esa mención |
| Trabajo de integración | Cliente SOAP, cola, reintentos, respuestas por línea | Firma, eventos, conservación, exportación y respuesta a requerimientos |
La cabecera del mensaje refleja esta decisión de forma explícita: el bloque RemisionVoluntaria incluye una FechaFinVeriFactu, que existe precisamente porque quien empieza a remitir de forma voluntaria queda vinculado a esa modalidad hasta el final del año natural. No es un interruptor que se pueda accionar cada semana.
Nuestra recomendación técnica, después de mirarlo con calma, es VERI*FACTU para la mayoría de negocios. No por comodidad: porque desplaza la carga de la prueba. Con los registros ya en la Agencia, tu obligación de conservación y de demostrar integridad se reduce muchísimo. La modalidad sin envío suena más cómoda y sale más cara de construir y de mantener.
Cómo se expone todo esto dentro de ERPNext
ERPNext corre sobre Frappe Framework, donde cada entidad de negocio es un DocType y cada DocType trae su API REST generada sola en /api/resource/{DocType}. Eso ya está cubierto en la guía de la API REST y los webhooks de ERPNext. Lo que interesa aquí es dónde encaja el ciclo de VeriFactu sin tocar el núcleo del producto.
La respuesta corta: en una app propia de Frappe, con tres piezas. Un DocType que hace de libro de registros de facturación, unos hooks que lo alimentan desde el ciclo de vida de la factura y un trabajador en segundo plano que se encarga del envío y de las respuestas.
1. Los hooks sobre el ciclo de vida de la factura
En Frappe, los eventos de documento se declaran en hooks.py. El registro de alta se genera cuando la factura se valida y el de anulación cuando se cancela, que es exactamente la semántica que pide el reglamento.
# hooks.py de tu app
doc_events = {
"Sales Invoice": {
"on_submit": "mi_app.verifactu.registro.generar_alta",
"on_cancel": "mi_app.verifactu.registro.generar_anulacion",
},
}2. El envío, siempre fuera de la petición del usuario
Nadie debería quedarse mirando una rueda girando mientras se negocia un TLS con la AEAT. El registro se persiste de forma síncrona, porque tiene que existir en el mismo momento de expedir; el envío se encola y lo procesa un worker, que es lo que permite agrupar hasta 1.000 registros, respetar el tiempo de espera y reintentar sin efectos secundarios.
frappe.enqueue(
"mi_app.verifactu.envio.enviar_lote",
queue="short",
job_name="verifactu-envio",
enqueue_after_commit=True,
)3. Qué queda expuesto hacia fuera
- API REST del libro de registros. Al ser un DocType, el registro de facturación queda consultable en
/api/resource/con los filtros y la paginación estándar de Frappe. Auditoría y conciliación sin escribir un endpoint. - Métodos propios. Cualquier función Python decorada con
@frappe.whitelist()se publica en/api/method/{ruta.a.la.funcion}: reenviar un lote, forzar una subsanación o exportar la cadena de un ejercicio. - Webhooks. El DocType Webhook de Frappe dispara un POST a un sistema externo cuando cambia el estado de un registro, sin necesidad de que nadie haga polling contra el ERP.
- Trazas. Los fallos de envío quedan en el Error Log de Frappe y los disparos de webhook en su propio registro de peticiones, con el código de respuesta de cada destino.
Qué hace nuestro módulo sobre esa base
El módulo VeriFactu que hemos escrito para ERPNext sigue exactamente ese patrón: genera el registro dentro del ciclo de vida de la factura, calcula la huella y el encadenamiento, imprime el QR en la plantilla del documento, añade la leyenda obligatoria y remite a la Agencia en las dos modalidades previstas por el reglamento. Funciona sobre ERPNext v14, v15 y v16, y la declaración responsable de conformidad la firmamos nosotros como productores del software.
Los nombres exactos de los DocTypes y de los métodos publicados van en la documentación de instalación que entregamos con el módulo, porque cambian entre versiones y no queremos que alguien integre contra un nombre que aquí se quede desactualizado.
Si lo que necesitas es integrar VeriFactu en un sistema que no es ERPNext, el patrón sirve igual y el trabajo se concentra en tres sitios: el mapeo de tus impuestos al desglose, la serialización determinista de la cadena que se hashea y el control de flujo del envío. Ahí es donde entra nuestro equipo de desarrollo a medida sobre Frappe.
Ocho formas de romper la integración sin darte cuenta
- 01
Normalizar los valores antes de hashear
La huella se calcula sobre una cadena literal. Si en un sitio formateas el importe con dos decimales y en otro el redondeo cae distinto, o si cambias el formato de la fecha entre el cálculo y el envío, el hash deja de reproducirse y la cadena no se puede verificar. Serializa una sola vez, guarda la cadena previa y hashea siempre desde ahí.
- 02
Encadenar por número de factura
El orden de la cadena lo marca la fecha y hora de generación del registro, no la numeración de las series. Un sistema con varias series concurrentes que intente encadenar por número acaba con cadenas cruzadas imposibles de reparar.
- 03
Generar el registro en un proceso posterior
El clásico job nocturno que recorre las facturas del día. El XML puede ser perfecto y aun así el sistema no es conforme, porque el registro tiene que nacer con la factura.
- 04
No serializar el acceso a la última huella
Dos procesos que piden a la vez cuál fue el último registro y encadenan los dos sobre la misma huella. Es la condición de carrera más habitual y sólo aparece con concurrencia real, casi nunca en pruebas. Hace falta un bloqueo explícito o una secuencia serializada por obligado tributario.
- 05
Tratar AceptadoConErrores como un rechazo
El registro está admitido. Reenviarlo genera un duplicado; lo que toca es subsanar con un registro nuevo. El error contrario también existe: dar por bueno un envío mirando sólo el estado global sin recorrer las líneas.
- 06
Ignorar el tiempo de espera entre envíos
Reintentar en bucle contra la AEAT no acelera nada y sí te expone a que empiece a rechazarte. La respuesta te dice cuánto esperar; el cliente tiene que obedecer, y esa lógica vive en el worker, no en el hilo del usuario.
- 07
Un desglose que no cuadra con los totales
Las validaciones de la Agencia comprueban la coherencia entre las líneas del desglose y los importes de cabecera. Los descuentos a nivel de documento, los redondeos de línea y el recargo de equivalencia son la fuente clásica de descuadres de céntimos que tumban el registro entero.
- 08
Probar en preproducción con un certificado que no es el de producción
La Agencia segrega el punto de entrada según el tipo de certificado. Validar con uno y salir a producción con otro deja la sorpresa para el peor día posible. Y conviene desconfiar de cualquier proveedor que hable de estar «homologado por Hacienda»: esa lista no existe.
Sobre el último punto lo contamos entero en por qué no existe software homologado por la AEAT, y sobre lo que cuesta equivocarse, en el régimen sancionador del artículo 201 bis, que alcanza también al fabricante del programa.
— FAQ
Preguntas frecuentes sobre la integración con VeriFactu
Las dudas que salen siempre en la primera reunión técnica: canal, límites, certificados, respuestas de la Agencia y encaje en ERPNext.
Ver todas las preguntas¿VeriFactu tiene API REST con JSON?+−
¿Cuántos registros caben en un envío y con qué frecuencia hay que enviar?+−
¿Qué certificado hace falta y contra qué endpoint se envía?+−
¿Qué significa que la AEAT responda AceptadoConErrores?+−
¿Se puede borrar un registro ya enviado?+−
¿Qué cambia en el código entre modalidad VERI*FACTU y NO VERI*FACTU?+−
¿Cómo se engancha todo esto a ERPNext sin tocar el core?+−
El resto del cluster técnico de VeriFactu
- El algoritmo de la huella y el encadenamiento — orden exacto de los campos, cadena previa y ejemplo reproducible.
- Especificación del código QR — parámetros de la URL, dimensiones, posición en la factura y librerías.
- API REST y webhooks de ERPNext — autenticación, endpoints por DocType y eventos hacia sistemas externos.
- Guía completa de VeriFactu 2027 — el contexto normativo y el calendario, sin tecnicismos.
- Nuestro módulo VeriFactu para ERPNext — qué incluye y cómo se activa sobre una instalación existente.
¿Te toca construir esta integración?
Llevamos desde 2014 implantando ERPNext y hemos escrito el módulo VeriFactu completo, del cálculo de la huella al control de flujo del envío. Cuéntanos qué sistema tienes que adaptar y te decimos qué trabajo hay por delante, sin humo.