Verifactu
The Anti-Fraud Law (Law 11/2021) mandates that businesses must ensure their invoicing and accounting systems meet strict technical standards to prevent data manipulation. Royal Decree 1007/2023 further defines the technical requirements for digital billing systems, emphasizing the need for standardized invoice formats that can be easily accessed by tax authorities.
These regulations require businesses to adopt systems capable of securely generate, store, and transmit invoices and tax reports. B2Brouter’s REST API is a technical solution that allows your business to easily be 100% compliant offloading the complexities to B2Brouter and focusing on the business logic of your system.
What is Verifactu?
Section titled “What is Verifactu?”Verifactu is a compliance system designed to simplify the process of meeting the invoicing requirements outlined in the Anti-Fraud Law. It ensures that all invoices generated by businesses are securely transmitted to the Tax Authority to be verified for integrity and authenticity by both the Tax Authority and the Customer. The Spanish Tax Authority is the AEAT (“Agencia Estatal de Administración Tributaria”).
Verifactu involves the immediate, real-time submission of invoice data to the AEAT as soon as the invoice is issued. This ensures that no modifications can be made after the invoice is issued, guaranteeing its security. Additionally, the invoice contains a QR code, allowing the recipient to verify its tax compliance through the AEAT’s platform.
In practice, Verifactu requires:
- Creating a tax report XML file.
- Computing a digital fingerprint for each tax report. This includes a hash chain to create a tamper-proof trail.
- Assembling a “Libro de Registro” or Ledger that may include one or several tax reports.
- Authenticated submission of the “Libro de Registro” to the AEAT using a qualified electronic certificate.
- Obeying the rate-limited requests established by the AEAT (max. 1 call per minute, unless submitting >1,000 invoices).
- Generating a QR code for invoice verification, to be included in the issued invoice.
- Processing the responses from the AEAT for each tax report.
With B2Brouter you can abstract much of the complexities of this process, and comply with the Anti-Fraud Law by issuing just a few REST calls to the API. You will not need your own qualified electronic certificate because B2Brouter is a Social Collaborator in Tax Management. You just need a valid B2Brouter API Key.
Testing environments: Use sandbox for initial API testing and payload validation — sandbox simulates Verifactu submissions so you can exercise the invoice lifecycle without hitting the AEAT. For full end-to-end testing with the AEAT test endpoint, use the staging environment (
api-staging.b2brouter.net).
Setting up and working with B2Brouter API
Section titled “Setting up and working with B2Brouter API”The first step is to set up the configuration for Verifactu for each one of the Companies for which you want to submit tax reports. You can refer to the Tax Report Settings Guide for a full description of the process. The call for configuring an account is as follows:
Example request
curl --request GET \ --url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_settings \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'Content-Type: application/json' \ --data '{ "tax_report_setting": { "code": "Verifactu", "start_date": "2025-04-06", "auto_generate": true, "auto_send": true, "reason_vat_exempt": "E1", "special_regime_key": "01", "reason_no_subject": "N1", "credit_note_code": "R1" } }'Invoice API
Section titled “Invoice API”If you are using B2Brouter for both issuing invoices and reporting them to the Tax Authority, once you have correctly configured your account, you can operate as you’d normally do for issuing invoices.
The issued invoices will contain the mandatory QR code, and the Verifactu tax report will be sent automatically to the AEAT. This is an example of a call to create and issue an Invoice:
Example request:
curl --request POST \ --url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'content-type: application/json' \ --data '{ "send_after_import": true, "invoice": { "date": "2025-04-15", "due_date": "2025-05-15", "number": 48, "payment_method": 1, "contact": { "name": "contact.name", "address": "contact.address", "city": "contact.city", "province": "contact.province", "postalcode": "28938", "country": "es", "email": "john.doe@example.com", "language": "es", "currency": "EUR", "terms": "15", "payment_method": 1, "tin_scheme": 9920, "tin_value": "ESA28388510" }, "invoice_lines_attributes": [ { "quantity": 10.0, "description": "line.description", "price": 11.0, "unit": 9, "taxes_attributes": [ { "name": "IVA", "percent": 21.0, "category": "S" } ] } ] } }'In the response of the POST REST call for creating an invoice, if send_after_import is set to true, you will find a section tax_report_ids in which you’ll find the ID of the tax report associated with the invoice.
You can access the complete tax report, including the QR code, its identifier, its state, and all other fields, by calling the get tax report endpoint with the tax report ID provided in the response of the create invoice call.
The state of the tax report will always be processing in the response of the POST call. You must check the life cycle of the tax report. You have two options:
-
The recommended way is using the tax report web hook. Final states: registered, error, and registered_with_errors
-
Polling the state of the tax report with get tax report endpoint until the state field is registered, error or registered_with_errors.
Tax Report API
Section titled “Tax Report API”If you are not using B2Brouter for issuing invoices, need more control over the process of generating and processing tax reports, or expect a high volume of tax reports, you can use the new Tax Report API.
This API is designed for maximum control by the user and high availability. You can either submit tax reports as a JSON payload or as a valid Verifactu XML.
The structure of the JSON format for B2Brouter tax reports is based on PEPPOL Continuous Transaction Control (CTC). B2Brouter Tax Report API is an universal API not only geared towards Verifactu but designed to handle tax reporting around the World.
It’s important to notice that tax reports have tax breakdowns (“desglose” in Spanish) which are the sum of the lines of the original invoice with the same tax. Is the responsibility of your system to aggregate the invoice lines by tax type and category and provide the necessary tax breakdowns. The TaxReport model does not perform any calculations.
Create a Tax Report
Section titled “Create a Tax Report”To create a tax report you have to call the create tax report endpoint. A minimal and valid tax report:
Example request:
curl --request POST \ --url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_reports \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'Content-Type: application/json' \ --data '{ "tax_report": { "type": "Verifactu", "invoice_date": "2025-04-03", "invoice_number": "11", "description": "Lorem ipsum...", "customer_party_tax_id": "B12345678", "customer_party_country": "es", "customer_party_name": "Ejemplo S.L.", "tax_inclusive_amount": 121.0, "tax_amount": 21.0, "invoice_type_code": "F1", "currency": "EUR", "tax_breakdowns": [ { "name": "IVA", "category": "S", "non_exemption_code": "S1", "percent": 21.0, "taxable_base": 100.0, "tax_amount": 21.0, "special_regime_key": "01" } ] } }'The response of this POST call will already contain the field qr with the QR code (codified in base64) that you must include in the invoice for your customer. The content of this QR code is a URL that will allow your customer to check that AEAT has received and processed the tax report derived from their invoice.
Import a Tax Report from an XML Verifactu file
Section titled “Import a Tax Report from an XML Verifactu file”If your system is capable of generating Verifactu XML files (a RegistroAlta or a RegistroAnulacion from the SuministroInformacion namespace), you can import them directly using the API via the import endpoint.
If the creation of the Tax Report is successful, the response will already contain the QR code and the identifier for the Tax Report. Note that B2Brouter will perform the chaining process regardless of any chaining information in the imported XML.
Check the state of a Tax Report
Section titled “Check the state of a Tax Report”The state field of the tax report will always be processing after a successful creation. Check the life cycle via:
-
The tax report web hook. Final states: registered, error, annulled, and registered_with_errors
-
Polling the get tax report endpoint until the state field is any of: registered, error, annulled, and registered_with_errors.
Get the XML representation of the tax report
Section titled “Get the XML representation of the tax report”Use the download tax report endpoint. B2Brouter also includes a field named xml_base64 in the GET response. Note that the XML can only be generated after the tax report has been chained.
Error checking
Section titled “Error checking”B2Brouter has implemented an exhaustive set of validations of Verifactu tax reports based on the specifications published by the AEAT. If there is a validation error the tax report will not be created. You’ll receive a 422: Unprocessable Entity response with all errors in JSON format.
Cancel a Tax Report
Section titled “Cancel a Tax Report”To cancel a tax report (“anulación” in Spanish) use the DELETE verb calling the annullation endpoint.
Correct a Tax Report
Section titled “Correct a Tax Report”To correct a tax report (“subsanación” in Spanish) use the PATCH or PUT verbs calling the correction endpoint.
Ledgers API
Section titled “Ledgers API”Verifactu tax reports are not send individually to the AEAT, they are bundled in a “Libro de Registro” (Ledger in B2Brouter terms), that contains up to 1000 tax reports. Each tax report has a ledger_id field that identifies the internal ID for the ledger. With that ID you can:
- Retrieve the XML representation of the Ledger by calling the download ledger endpoint.
- Retrieve the XML response of the AEAT to the Ledger sent by B2Brouter by calling the download response endpoint.
Equivalence between B2Brouter internal tax report fields and Verifactu XML nodes
Section titled “Equivalence between B2Brouter internal tax report fields and Verifactu XML nodes”| B2Brouter Field (based on PEPPOL CTC) | Verifactu XML Node |
|---|---|
| invoice_date | IDFactura > FechaExpedicionFactura |
| invoice_number | IDFactura > NumSerieFactura |
| invoice_series_code | IDFactura > NumSerieFactura |
| supplier_party_name | NombreRazonEmisor |
| external_reference | RefExterna |
| correction | Subsanacion |
| annullation | Anulación |
| previously_refused | RechazoPrevio |
| invoice_type_code | TipoFactura |
| amend_type | TipoFactura |
| amended_number | FacturasRectificadas > IDFacturaRectificada > NumSerieFactura |
| amended_series_code | FacturasRectificadas > IDFacturaRectificada > NumSerieFactura |
| amended_date | FacturasRectificadas > IDFacturaRectificada > FechaExpedicionFactura |
| amended_tax_exclusive | ImporteRectificacion > BaseRectificada |
| amended_tax_amount | ImporteRectificacion > CuotaRectificada |
| tax_point_date | FechaOperacion |
| description | DescripcionOperacion |
| simplified_art7273 | FacturaSimplificadaArt7273 |
| ticket | FacturaSinIdentifDestinatarioArt61d |
| macrodata | Macrodato |
| issued_by_third_party_or_receiver | EmitidaPorTerceroODestinatario |
| third_party_name | Tercero > NombreRazon |
| third_party_tax_id | Tercero > NIF |
| customer_party_name | Destinatarios > IDDestinatario > NombreRazon |
| customer_party_tax_id | Destinatarios > IDDestinatario > NIF or IDOtro > IDType |
| customer_party_country | Destinatarios > IDDestinatario > IDOtro > CodigoPais |
| customer_party_tax_scheme | Destinatarios > IDDestinatario > IDOtro > IDType |
| tax_breakdowns[].name | Desglose > DetalleDesglose > Impuesto |
| tax_breakdowns[].special_regime_key | Desglose > DetalleDesglose > ClaveRegimen |
| tax_breakdowns[].non_exemption_code | Desglose > DetalleDesglose > CalificacionOperacion |
| tax_breakdowns[].exemption_code | Desglose > DetalleDesglose > OperacionExenta |
| tax_breakdowns[].percent | Desglose > DetalleDesglose > TipoImpositivo |
| tax_breakdowns[].taxable_base | Desglose > DetalleDesglose > BaseImponibleOimporteNoSujeto |
| tax_breakdowns[].taxable_base_at_cost | Desglose > DetalleDesglose > BaseImponibleACoste |
| tax_breakdowns[].tax_amount | Desglose > DetalleDesglose > CuotaRepercutida |
| tax_amount | CuotaTotal |
| tax_inclusive_amount | ImporteTotal |
| previous_id | Encadenamiento > RegistroAnterior > NumSerieFactura |
XSD files
Section titled “XSD files”| XSD file | Description |
|---|---|
| SuministroLR.xsd | Operaciones de alta y anulación de los sistemas Verifactu y no Verifactu |
| RespuestaSuministro.xsd | Respuesta de las operaciones de los sistemas Verifactu y no Verifactu |
| ConsultaLR.xsd | Operación de consulta de registros de facturación Verifactu |
| RespuestaConsultaLR.xsd | Respuesta de la operación de consulta de registros de facturación Verifactu |
| SuministroInformacion.xsd | Definición de tipos comunes |
Code descriptions for specific fields
Section titled “Code descriptions for specific fields”Invoice type codes
Section titled “Invoice type codes”Param: invoice_type_code
| Code | Description |
|---|---|
| F1 | Factura (art. 6, 7.2 y 7.3 del RD 1619/2012) |
| F2 | Factura Simplificada y Facturas sin identificación del destinatario (art. 6.1.d) RD 1619/2012 |
| F3 | Factura emitida en sustitución de facturas simplificadas facturadas y declaradas |
| R1 | Factura Rectificativa (Error fundado en derecho y Art. 80 Uno Dos y Seis LIVA) |
| R2 | Factura Rectificativa (Art. 80.3) |
| R3 | Factura Rectificativa (Art. 80.4) |
| R4 | Factura Rectificativa (Resto) |
| R5 | Factura Rectificativa en facturas simplificadas |
Special regime key codes
Section titled “Special regime key codes”Param: special_regime_key
| Code | Description |
|---|---|
| 01 | Operación de régimen general. |
| 02 | Exportación. |
| 03 | Operaciones a las que se aplique el régimen especial de bienes usados, objetos de arte, antigüedades y objetos de colección. |
| 04 | Régimen especial del oro de inversión. |
| 05 | Régimen especial de las agencias de viajes. |
| 06 | Régimen especial grupo de entidades en IVA (Nivel Avanzado) |
| 07 | Régimen especial del criterio de caja. |
| 08 | Operaciones sujetas al IPSI / IGIC. |
| 17 | Operación acogida a alguno de los regímenes previstos en el Capítulo XI del Título IX (OSS e IOSS) |
| 18 | Recargo de equivalencia. |
| 19 | Operaciones de actividades incluidas en el Régimen Especial de Agricultura, Ganadería y Pesca (REAGYP) |
| 20 | Régimen simplificado |
Non exemption codes
Section titled “Non exemption codes”Param: non_exemption_code
| Code | Description |
|---|---|
| S1 | Operación Sujeta y No exenta - Sin inversión del sujeto pasivo. |
| S2 | Operación Sujeta y No exenta - Con Inversión del sujeto pasivo. |
Non subject codes
Section titled “Non subject codes”Param: no_subject_code
| Code | Description |
|---|---|
| N1 | Operación No Sujeta artículo 7, 14, otros. |
| N2 | Operación No Sujeta por Reglas de localización. |
Exemption codes
Section titled “Exemption codes”Param: exemption_code
| Code | Description |
|---|---|
| E1 | Exenta por el artículo 20 |
| E2 | Exenta por el artículo 21 |
| E3 | Exenta por el artículo 22 |
| E4 | Exenta por los artículos 23 y 24 |
| E5 | Exenta por el artículo 25 |
| E6 | Exenta por otros |
Customer ID Type
Section titled “Customer ID Type”Param: customer_party_tax_scheme
| Code | Description |
|---|---|
| 02 | EU VAT ID (NIF-IVA) |
| 03 | Passport |
| 04 | Official ID issued by country of residence |
| 05 | Certificate of residence |
| 06 | Other supporting document |
| 07 | Not registered |