TicketBAI
La Ley Antifraude (Ley 11/2021) y su posterior regulación técnica Real Decreto 1007/2023 obligan a las empresas a garantizar que sus sistemas de facturación y contabilidad cumplan estrictos requisitos técnicos para evitar la manipulación de datos. Para las empresas ubicadas en Euskadi, la Ley Antifraude impone el uso de TicketBAI. La API REST de TicketBAI de B2Brouter es una solución técnica que permite a tu empresa cumplir al 100% la normativa de forma sencilla, delegando las complejidades en B2Brouter y centrándote en la lógica de negocio de tu sistema.
B2Brouter ofrece una cómoda API para presentar informes fiscales TicketBAI, lo que te permite cumplir fácilmente con la legislación de las tres Haciendas Forales del País Vasco (Araba, Bizkaia y Gipuzkoa). Nuestra API abstrae las diferencias entre ellas y te ofrece una interfaz unificada que cumple plenamente con las tres Haciendas Forales. Puedes comprobar el estado de B2Brouter como “software garante” en las tres Haciendas Forales: Araba, Bizkaia, Gipuzkoa.
¿Qué es TicketBAI?
Sección titulada «¿Qué es TicketBAI?»TicketBAI es un proyecto conjunto de las Diputaciones Forales del País Vasco (Gipuzkoa, Bizkaia y Araba) y el Gobierno Vasco para controlar la facturación y garantizar el cumplimiento fiscal. Exige que todos los autónomos y empresas usen software de facturación certificado que genere un código único y un código QR para cada factura. Este sistema tiene como objetivo prevenir el fraude fiscal garantizando que todas las transacciones económicas se registren y transmitan a las autoridades fiscales en tiempo real o casi real.
TicketBAI implica:
- Generar un identificador TicketBAI único, un código QR para cada factura y un informe fiscal que incluye la información relevante de la factura.
- Firmar digitalmente cada informe fiscal usando un certificado electrónico cualificado.
- Encadenar criptográficamente los informes fiscales para garantizar la inmutabilidad y evitar la manipulación.
- Presentar el informe fiscal a la Hacienda Foral correspondiente en el formato y plazo especificados.
- Procesar las respuestas de la Hacienda Foral para cada presentación.
Con B2Brouter puedes abstraer gran parte de las complejidades de este proceso y cumplir con la Ley Antifraude emitiendo solo unas pocas llamadas REST a la API. No necesitarás tu propio certificado electrónico cualificado porque B2Brouter es Colaborador Social en la Gestión Tributaria. Solo necesitas una clave API de B2Brouter válida.
B2Brouter ofrece dos modalidades de operación según si usas B2Brouter tanto para emitir facturas como para reportarlas a la Hacienda Foral, o solo para el reporte fiscal.
Entornos de prueba: Usa el sandbox para las pruebas iniciales de la API y la validación de payloads — el sandbox simula las presentaciones de TicketBAI para que puedas ejercitar el ciclo de vida de la factura sin contactar con la Hacienda Foral del País Vasco. Para pruebas completas de extremo a extremo con el endpoint de pruebas de la autoridad, usa el entorno de staging (
api-staging.b2brouter.net).
Configuración y trabajo con la API de B2Brouter
Sección titulada «Configuración y trabajo con la API de B2Brouter»El primer paso es configurar TicketBAI para cada una de las empresas para las que quieras presentar informes fiscales. Puedes consultar la Guía de Tax Report Settings para una descripción completa del proceso. La llamada para configurar una cuenta es la siguiente:
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": "tbai", "enabled": true, "start_date": "2025-04-06", "auto_generate": true, "auto_send": true, "reason_vat_exempt": "E1", "special_regime_key": "01", "reason_no_subject": "OT", "credit_note_code": "R1", "delegation": "gipuzkoa", "type_operation": "services" } }'API de facturas
Sección titulada «API de facturas»Si usas B2Brouter tanto para emitir facturas como para reportarlas a la Hacienda Foral, una vez que hayas configurado correctamente tu cuenta, puedes operar como lo harías normalmente para emitir facturas.
Las facturas emitidas contendrán el código QR obligatorio, y el informe fiscal TicketBAI se enviará automáticamente a la Hacienda Foral correspondiente. Este es un ejemplo de llamada para crear y emitir una factura:
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": "Ejemplo S.L.", "address": "Calle Falsa 123, 3ºA", "city": "Madrid", "province": "Madrid", "postalcode": "28001", "country": "es", "email": "john.doe@example.com", "language": "es", "currency": "EUR", "terms": "15", "payment_method": 1, "tin_scheme": 9920, "tin_value": "ESB12345678" }, "invoice_lines_attributes": [ { "quantity": 10.0, "description": "Servicio de Ejemplo", "price": 11.0, "unit": 9, "taxes_attributes": [ { "name": "IVA", "percent": 21.0, "category": "S" } ] } ] } }'En la respuesta de la llamada REST POST para crear una factura, si send_after_import está establecido en true, encontrarás una sección tax_report_ids con el ID del informe fiscal asociado a la factura. Ten en cuenta que tax_report_ids es un array porque cada factura puede tener más de un informe fiscal para cancelaciones y correcciones.
Puedes acceder al informe fiscal completo, incluido el código QR, su identificador, su estado y todos los demás campos, llamando al endpoint de obtención de informe fiscal con el ID del informe fiscal proporcionado en la respuesta de la llamada de creación de factura.
El estado del informe fiscal siempre será processing en la respuesta de la llamada POST porque el proceso en segundo plano que encadena y envía el informe fiscal se realiza de forma independiente. Por esta razón debes verificar el ciclo de vida del informe fiscal. Tienes dos opciones:
-
La forma recomendada es usando el webhook de informe fiscal. El webhook emitirá una llamada POST a tu sistema cada vez que el estado del informe fiscal alcance un estado final: registered, error y registered_with_errors
-
Consultar el estado del informe fiscal con el endpoint de obtención de informe fiscal hasta que el campo state sea registered, error o registered_with_errors.
API de informes fiscales para TicketBAI
Sección titulada «API de informes fiscales para TicketBAI»Si no usas B2Brouter para emitir facturas, necesitas más control sobre el proceso de generación y procesamiento de informes fiscales, o esperas un alto volumen de informes fiscales, puedes usar la API de informes fiscales.
La estructura del formato JSON para los informes fiscales de B2Brouter se basa en PEPPOL Continuous Transaction Control (CTC). La API de informes fiscales de B2Brouter es una API universal no solo orientada a TicketBAI sino diseñada para gestionar el reporte fiscal en todo el mundo.
Es importante tener en cuenta que para TicketBAI, los informes fiscales tienen líneas de informe fiscal que representan las líneas individuales de la factura original. El modelo TaxReport no realiza ningún cálculo. Debes proporcionar todas las cifras relevantes en el informe fiscal.
Crear un informe fiscal
Sección titulada «Crear un informe fiscal»Para crear un informe fiscal debes llamar al endpoint crear informe fiscal. Un informe fiscal mínimo y válido se puede crear con la siguiente llamada POST:
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": "TicketBai", "invoice_date": "2025-04-03", "invoice_number": "11", "description": "Consulting service", "customer_party_tax_id": "B12345678", "customer_party_country": "es", "customer_party_postalcode": "28001", "customer_party_address": "Calle Falsa 123, 3ºA", "customer_party_name": "Ejemplo S.L.", "tax_inclusive_amount": 121.0, "tax_amount": 21.0, "invoice_type_code": "F1", "currency": "EUR", "tax_report_lines": [ { "quantity": 1.0, "description": "Service", "price": 100.0, "tax_inclusive_amount": 121.0, "tax_exclusive_amount": 100.0, "tax_amount": 21.0 } ], "tax_breakdowns": [ { "category": "S", "non_exempt": true, "non_exemption_code": "S1", "percent": 21, "taxable_base": 100.0, "tax_amount": 21.0 } ] } }'La respuesta a esta llamada POST devolverá inmediatamente el informe fiscal en estado processing. Los campos qr e identifier serán null inicialmente. Para recuperarlos, realiza peticiones GET posteriores hasta que el state cambie a signed o registered.
El siguiente es un ejemplo más complejo de un TicketBAI con dos líneas, un descuento global, IVA del 21% e IRPF del 15%:
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": "TicketBai", "invoice_date": "2025-08-13", "invoice_number": "2025-128", "description": "Invoice with multiple lines, global discount, IVA, and IRPF (tax withheld)", "customer_party_tax_id": "B12345678", "customer_party_country": "es", "customer_party_postalcode": "28001", "customer_party_address": "Calle Falsa 123, 3ºA", "customer_party_name": "Ejemplo S.L.", "tax_inclusive_amount": 217.8, "tax_amount": 37.8, "tax_amount_withheld": 27, "invoice_type_code": "F1", "currency": "EUR", "tax_report_lines": [ { "quantity": 1, "description": "Product A", "price": 100, "tax_inclusive_amount": 121, "tax_exclusive_amount": 100, "tax_amount": 21 }, { "quantity": 2, "description": "Product B", "price": 50, "tax_inclusive_amount": 121, "tax_exclusive_amount": 100, "tax_amount": 21 }, { "quantity": 1, "description": "Global Discount 10%", "price": -20, "tax_inclusive_amount": -24.2, "tax_exclusive_amount": -20, "tax_amount": -4.2 } ], "tax_breakdowns": [ { "category": "S", "non_exempt": true, "non_exemption_code": "S1", "percent": 21, "taxable_base": 180, "tax_amount": 37.8 } ] } }'Puntos clave a tener en cuenta:
- Cálculo de impuestos a nivel de línea: TicketBAI exige que todos los impuestos se calculen por línea.
- Descuentos y cargos globales como líneas: Los descuentos globales deben tratarse como líneas separadas con un
priceytax_inclusive_amountnegativos. - Impuestos retenidos: Los impuestos retenidos (p. ej., IRPF) se reportan a nivel de documento usando el campo
tax_amount_withheld.
Verificar el estado de un informe fiscal
Sección titulada «Verificar el estado de un informe fiscal»Debes verificar el ciclo de vida del informe fiscal. Tienes dos opciones:
-
La forma recomendada es usando el webhook de informe fiscal. Los estados finales son: registered, error, annulled y registered_with_errors
-
Consultar el estado del informe fiscal llamando al endpoint de obtención de informe fiscal hasta que el campo state sea alguno de: registered, error, annulled y registered_with_errors.
Obtener la representación XML del informe fiscal
Sección titulada «Obtener la representación XML del informe fiscal»Si necesitas la representación XML del informe fiscal, usa el endpoint de descarga del informe fiscal. B2Brouter también incluye un campo llamado xml_base64 en la respuesta GET. Ten en cuenta que el XML solo se puede generar después de que el informe fiscal haya sido encadenado (un proceso asíncrono).
Verificación de errores
Sección titulada «Verificación de errores»B2Brouter ha implementado un exhaustivo conjunto de validaciones de los informes fiscales TicketBAI basado en las especificaciones publicadas por las Haciendas Forales Vascas. Si hay un error de validación, el informe fiscal no se creará. Recibirás una respuesta 422: Unprocessable Entity con todos los errores en formato JSON.
Cancelar un informe fiscal
Sección titulada «Cancelar un informe fiscal»Para cancelar un informe fiscal usa el verbo DELETE llamando al endpoint de anulación. Verifica la evolución de la cancelación procesando el webhook de informe fiscal o consultando el endpoint de obtención de informe fiscal hasta que el campo state del informe fiscal sea error o annulled.
Listar informes fiscales
Sección titulada «Listar informes fiscales»Para listar tus informes fiscales usa el verbo GET en el endpoint de listado de informes fiscales.
Equivalencia entre los campos internos del informe fiscal de B2Brouter y los nodos XML de TicketBAI
Sección titulada «Equivalencia entre los campos internos del informe fiscal de B2Brouter y los nodos XML de TicketBAI»| Campo B2Brouter (basado en PEPPOL CTC) | Nodo XML TicketBAI |
|---|---|
supplier_party_tax_id | Sujetos > Emisor > NIF |
supplier_party_name | Sujetos > Emisor > ApellidosNombreRazonSocial |
customer_party_tax_id (si customer_party_tax_scheme es nil) | Sujetos > Destinatarios > IDDestinatario > NIF |
customer_party_country | Sujetos > Destinatarios > IDDestinatario > IDOtro > CodigoPais |
customer_party_tax_scheme | Sujetos > Destinatarios > IDDestinatario > IDOtro > IDType |
customer_party_tax_id (si customer_party_tax_scheme está presente) | Sujetos > Destinatarios > IDDestinatario > IDOtro > ID |
customer_party_name | Sujetos > Destinatarios > IDDestinatario > ApellidosNombreRazonSocial |
customer_party_postalcode | Sujetos > Destinatarios > IDDestinatario > CodigoPostal |
customer_party_address | Sujetos > Destinatarios > IDDestinatario > Direccion |
invoice_series_code | Factura > CabeceraFactura > SerieFactura |
invoice_number | Factura > CabeceraFactura > NumFactura |
invoice_date | Factura > CabeceraFactura > FechaExpedicionFactura |
ticket | Factura > CabeceraFactura > FacturaSimplificada |
is_amend | Factura > CabeceraFactura > FacturaRectificativa |
invoice_type_code (si is_amend) | Factura > CabeceraFactura > FacturaRectificativa > Codigo |
amended_series_code | Factura > CabeceraFactura > FacturasRectificadasSustituidas > IDFacturaRectificadaSustituida > SerieFactura |
amended_number | Factura > CabeceraFactura > FacturasRectificadasSustituidas > IDFacturaRectificadaSustituida > NumFactura |
amended_date | Factura > CabeceraFactura > FacturasRectificadasSustituidas > IDFacturaRectificadaSustituida > FechaExpedicionFactura |
tax_point_date | Factura > DatosFactura > FechaOperacion |
description | Factura > DatosFactura > DescripcionFactura |
tax_report_lines[].description | Factura > DatosFactura > DetallesFactura > IDDetalleFactura > DescripcionDetalle |
tax_report_lines[].quantity | Factura > DatosFactura > DetallesFactura > IDDetalleFactura > Cantidad |
tax_report_lines[].price | Factura > DatosFactura > DetallesFactura > IDDetalleFactura > ImporteUnitario |
tax_report_lines[].tax_inclusive_amount | Factura > DatosFactura > DetallesFactura > IDDetalleFactura > ImporteTotal |
tax_inclusive_amount | Factura > DatosFactura > ImporteTotalFactura |
tax_amount_withheld | Factura > DatosFactura > RetencionSoportada |
special_regime_key | Factura > DatosFactura > Claves > IDClave > ClaveRegimenIvaOpTrascendencia |
tax_breakdowns[].taxable_base (no exento) | Factura > TipoDesglose > DesgloseFactura > Sujeta > NoExenta > DetalleNoExenta > DesgloseIVA > DetalleIVA > BaseImponible |
tax_breakdowns[].percent | Factura > TipoDesglose > DesgloseFactura > Sujeta > NoExenta > DetalleNoExenta > DesgloseIVA > DetalleIVA > TipoImpositivo |
tax_breakdowns[].tax_amount | Factura > TipoDesglose > DesgloseFactura > Sujeta > NoExenta > DetalleNoExenta > DesgloseIVA > DetalleIVA > CuotaImpuesto |
Archivos XSD
Sección titulada «Archivos XSD»Esquemas TicketBAI proporcionados por las Haciendas Forales Vascas.
| Archivo XSD | Descripción |
|---|---|
| ticketbaiv1-2-2.xsd | Esquema principal de TicketBAI |
| anula_ticketbaiv1-2-2.xsd | Esquema para el suministro de facturas |
Descripciones de códigos para campos específicos
Sección titulada «Descripciones de códigos para campos específicos»Códigos de tipo de factura
Sección titulada «Códigos de tipo de factura»Parámetro: invoice_type_code
| Código | Descripción |
|---|---|
| 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 |
Códigos de clave de régimen especial
Sección titulada «Códigos de clave de régimen especial»Parámetro: special_regime_key
| Código | Descripción |
|---|---|
| 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. |
| 51 | Operaciones en recargo de equivalencia. |
| 52 | Operaciones en régimen simplificado. |
Códigos de no exención
Sección titulada «Códigos de no exención»Parámetro: non_exemption_code
| Código | Descripción |
|---|---|
| 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. |
Códigos de exención
Sección titulada «Códigos de exención»Parámetro: exemption_code
| Código | Descripción |
|---|---|
| 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 |
Códigos de no sujeción
Sección titulada «Códigos de no sujeción»Parámetro: no_subject_code
| Código | Descripción |
|---|---|
| OT | No sujeto por el artículo 7 de la Norma Foral de IVA Otros supuestos de no sujeción. |
| RL | No sujeto por reglas de localización. |
| VT | No sujeto, ventas realizadas por cuenta de terceros. |
| IE | No sujeto en el TAI por reglas de localización, pero repercute impuesto extranjero. |
Tipo de identificación del cliente
Sección titulada «Tipo de identificación del cliente»Parámetro: customer_party_tax_scheme
| Código | Descripción |
|---|---|
| 02 | NIF-IVA de la UE |
| 03 | Pasaporte |
| 04 | Documento de identidad oficial emitido por el país de residencia |
| 05 | Certificado de residencia |
| 06 | Otro documento acreditativo |