TicketBAI
La loi anti-fraude (loi 11/2021) et sa réglementation technique ultérieure décret royal 1007/2023 imposent aux entreprises de garantir que leurs systèmes de facturation et de comptabilité respectent des normes techniques strictes afin d’empêcher la manipulation des données. Pour les entreprises situées en Euskadi, la loi anti-fraude impose l’utilisation de TicketBAI. L’API REST TicketBAI de B2Brouter est une solution technique qui permet à votre entreprise d’être facilement conforme à 100 %, en confiant la complexité à B2Brouter et en vous concentrant sur la logique métier de votre système.
B2Brouter fournit une API pratique pour soumettre les déclarations fiscales TicketBAI afin que vous puissiez vous conformer facilement à la législation des trois administrations fiscales basques (Araba, Bizkaia et Gipuzkoa). Notre API masque les différences entre elles et vous offre une interface unifiée totalement conforme aux trois administrations fiscales. Vous pouvez consulter le statut de B2Brouter en tant que « software garante » auprès des trois administrations fiscales : Araba, Bizkaia, Gipuzkoa.
Qu’est-ce que TicketBAI ?
Section titled “Qu’est-ce que TicketBAI ?”TicketBAI est un projet commun des conseils provinciaux basques (Gipuzkoa, Bizkaia et Araba) et du gouvernement basque destiné à contrôler la facturation et à garantir la conformité fiscale. Il impose à tous les travailleurs indépendants et aux entreprises d’utiliser un logiciel de facturation certifié qui génère un code unique et un code QR pour chaque facture. Ce système vise à prévenir la fraude fiscale en garantissant que toutes les transactions économiques sont enregistrées et transmises à l’administration fiscale en temps réel ou quasi réel.
TicketBAI implique :
- la génération d’un identifiant TicketBAI unique, d’un code QR pour chaque facture et d’une déclaration fiscale contenant les informations pertinentes de la facture ;
- la signature numérique de chaque déclaration fiscale au moyen d’un certificat électronique qualifié ;
- l’enchaînement cryptographique des déclarations fiscales afin de garantir leur immutabilité et d’empêcher toute altération ;
- l’envoi de la déclaration fiscale à l’administration fiscale basque compétente (Hacienda Foral) dans un format déterminé et dans les délais ;
- le traitement des réponses de l’administration fiscale pour chaque envoi.
Avec B2Brouter, vous pouvez abstraire une grande partie de la complexité de ce processus et vous conformer à la loi anti-fraude en n’émettant que quelques appels REST à l’API. Vous n’aurez pas besoin de votre propre certificat électronique qualifié, car B2Brouter est un collaborateur social dans la gestion fiscale. Vous avez simplement besoin d’une clé API B2Brouter valide.
B2Brouter fournit deux modes de fonctionnement selon que vous utilisez B2Brouter à la fois pour émettre les factures et pour les déclarer à l’administration fiscale, ou uniquement pour la déclaration fiscale.
Environnements de test : utilisez le sandbox pour les premiers tests API et la validation des payloads. Le sandbox simule les soumissions TicketBAI afin que vous puissiez tester le cycle de vie complet d’une facture sans contacter l’administration fiscale basque. Pour des tests de bout en bout avec le point de terminaison de test de l’administration, utilisez l’environnement de staging (
api-staging.b2brouter.net).
Configurer et utiliser l’API B2Brouter
Section titled “Configurer et utiliser l’API B2Brouter”La première étape consiste à configurer TicketBAI pour chacune des entreprises pour lesquelles vous souhaitez soumettre des déclarations fiscales. Vous pouvez consulter le guide des paramètres de déclaration fiscale pour une description complète du processus. L’appel pour configurer un compte est le suivant :
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 factures
Section titled “API de factures”Si vous utilisez B2Brouter à la fois pour émettre des factures et pour les déclarer à l’administration fiscale, une fois votre compte correctement configuré, vous pouvez travailler comme d’habitude pour l’émission de factures.
Les factures émises contiendront le code QR obligatoire, et la déclaration fiscale TicketBAI sera envoyée automatiquement à l’administration fiscale compétente. Voici un exemple d’appel permettant de créer et d’émettre une facture :
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" } ] } ] } }'Dans la réponse de l’appel REST POST de création d’une facture, si send_after_import est défini sur true, vous trouverez une section tax_report_ids dans laquelle figure l’identifiant de la déclaration fiscale associée à la facture. Notez que tax_report_ids est un tableau parce qu’une même facture peut avoir plus d’une déclaration fiscale en cas d’annulation ou de correction.
Vous pouvez accéder à la déclaration fiscale complète, y compris le code QR, son identifiant, son état et tous les autres champs, en appelant le point de terminaison d’obtention d’une déclaration fiscale avec l’identifiant de déclaration fiscale fourni dans la réponse à l’appel de création de facture.
L’état de la déclaration fiscale sera toujours processing dans la réponse à l’appel POST, car le processus en arrière-plan qui enchaîne et envoie la déclaration fiscale est exécuté de manière indépendante. C’est pourquoi vous devez vérifier le cycle de vie de la déclaration fiscale. Vous avez deux options :
- la méthode recommandée consiste à utiliser le webhook de déclaration fiscale. Le webhook enverra un appel POST à votre système chaque fois que l’état de la déclaration fiscale atteindra un état final : registered, error et registered_with_errors ;
- interroger l’état de la déclaration fiscale avec le point de terminaison d’obtention d’une déclaration fiscale jusqu’à ce que le champ state soit registered, error ou registered_with_errors.
API Tax Report pour TicketBAI
Section titled “API Tax Report pour TicketBAI”Si vous n’utilisez pas B2Brouter pour émettre des factures, si vous avez besoin de davantage de contrôle sur le processus de génération et de traitement des déclarations fiscales, ou si vous prévoyez un volume élevé de déclarations, vous pouvez utiliser l’API Tax Report.
La structure du format JSON des déclarations fiscales B2Brouter est basée sur PEPPOL Continuous Transaction Control (CTC). L’API Tax Report de B2Brouter est une API universelle, non seulement orientée TicketBAI mais conçue pour gérer les déclarations fiscales dans le monde entier.
Il est important de noter que, pour TicketBAI, les déclarations fiscales comportent des tax report lines, qui représentent les lignes individuelles de la facture d’origine. Le modèle TaxReport n’effectue aucun calcul. Vous devez fournir toutes les valeurs pertinentes dans la déclaration fiscale.
Créer une déclaration fiscale
Section titled “Créer une déclaration fiscale”Pour créer une déclaration fiscale, vous devez appeler le point de terminaison create tax report. Une déclaration fiscale minimale et valide peut être créée avec l’appel POST suivant :
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 réponse à cet appel POST sera renvoyée immédiatement avec la déclaration fiscale dans l’état processing. Les champs qr et identifier seront initialement null. Pour les récupérer, effectuez ensuite des requêtes GET jusqu’à ce que l’état passe à signed ou registered.
L’exemple suivant est plus complexe : un TicketBAI avec deux lignes, une remise globale, une TVA de 21 % et un IRPF de 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 } ] } }'Points clés à prendre en compte :
- Calcul fiscal au niveau ligne : TicketBAI exige que toutes les taxes soient calculées ligne par ligne.
- Remises et charges globales comme lignes : les remises globales doivent être traitées comme des lignes séparées avec un
pricenégatif et untax_inclusive_amountnégatif. - Taxes retenues à la source : les taxes retenues (par ex. IRPF) sont déclarées au niveau du document à l’aide du champ
tax_amount_withheld.
Vérifier l’état d’une déclaration fiscale
Section titled “Vérifier l’état d’une déclaration fiscale”Vous devez vérifier le cycle de vie de la déclaration fiscale. Vous avez deux options :
- la méthode recommandée consiste à utiliser le webhook de déclaration fiscale. Les états finaux sont : registered, error, annulled et registered_with_errors ;
- interroger l’état de la déclaration fiscale en appelant le point de terminaison get tax report jusqu’à ce que le champ state soit l’un des suivants : registered, error, annulled et registered_with_errors.
Obtenir la représentation XML de la déclaration fiscale
Section titled “Obtenir la représentation XML de la déclaration fiscale”Si vous avez besoin de la représentation XML de la déclaration fiscale, utilisez le point de terminaison download tax report. B2Brouter inclut également un champ nommé xml_base64 dans la réponse du GET. Notez que le XML ne peut être généré qu’après l’enchaînement de la déclaration fiscale (processus asynchrone).
Contrôle des erreurs
Section titled “Contrôle des erreurs”B2Brouter a mis en place un ensemble exhaustif de validations des déclarations fiscales TicketBAI basées sur les spécifications publiées par les administrations fiscales basques. En cas d’erreur de validation, la déclaration fiscale ne sera pas créée. Vous recevrez une réponse 422: Unprocessable Entity avec toutes les erreurs au format JSON.
Annuler une déclaration fiscale
Section titled “Annuler une déclaration fiscale”Pour annuler une déclaration fiscale, utilisez le verbe DELETE en appelant le point de terminaison d’annulation. Vérifiez l’évolution de l’annulation soit en traitant le webhook de déclaration fiscale, soit en interrogeant le point de terminaison get tax report jusqu’à ce que le champ state de la déclaration fiscale soit error ou annulled.
Lister les déclarations fiscales
Section titled “Lister les déclarations fiscales”Pour lister vos déclarations fiscales, utilisez le verbe GET sur le point de terminaison list tax reports.
Équivalence entre les champs internes de déclaration fiscale B2Brouter et les nœuds XML TicketBAI
Section titled “Équivalence entre les champs internes de déclaration fiscale B2Brouter et les nœuds XML TicketBAI”| Champ B2Brouter (basé sur PEPPOL CTC) | Nœud XML TicketBAI |
|---|---|
supplier_party_tax_id | Sujetos > Emisor > NIF |
supplier_party_name | Sujetos > Emisor > ApellidosNombreRazonSocial |
customer_party_tax_id (si customer_party_tax_scheme est 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 présent) | 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 (non exonéré) | 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 |
Fichiers XSD
Section titled “Fichiers XSD”Schémas TicketBAI fournis par les administrations fiscales basques.
| Fichier XSD | Description |
|---|---|
| ticketbaiv1-2-2.xsd | Schéma principal TicketBAI |
| anula_ticketbaiv1-2-2.xsd | Schéma pour la soumission des annulations |
Descriptions de code pour des champs spécifiques
Section titled “Descriptions de code pour des champs spécifiques”Codes de type de facture
Section titled “Codes de type de facture”Param : invoice_type_code
| Code | Description |
|---|---|
| F1 | Facture (art. 6, 7.2 et 7.3 du RD 1619/2012) |
| F2 | Facture simplifiée et factures sans identification du destinataire (art. 6.1.d du RD 1619/2012) |
| F3 | Facture émise en substitution de factures simplifiées facturées et déclarées |
| R1 | Facture rectificative (erreur fondée en droit et art. 80 un, deux et six LIVA) |
| R2 | Facture rectificative (art. 80.3) |
| R3 | Facture rectificative (art. 80.4) |
| R4 | Facture rectificative (autres cas) |
| R5 | Facture rectificative pour des factures simplifiées |
Codes de clé de régime spécial
Section titled “Codes de clé de régime spécial”Param : special_regime_key
| Code | Description |
|---|---|
| 01 | Opération relevant du régime général. |
| 02 | Exportation. |
| 03 | Opérations soumises au régime spécial des biens d’occasion, objets d’art, antiquités et objets de collection. |
| 04 | Régime spécial de l’or d’investissement. |
| 05 | Régime spécial des agences de voyages. |
| 06 | Régime spécial du groupe d’entités en TVA (niveau avancé) |
| 07 | Régime spécial du critère de caisse. |
| 08 | Opérations soumises à l’IPSI / IGIC. |
| 51 | Opérations sous régime du recargo de equivalencia. |
| 52 | Opérations sous régime simplifié. |
Codes de non-exonération
Section titled “Codes de non-exonération”Param : non_exemption_code
| Code | Description |
|---|---|
| S1 | Opération soumise et non exonérée - sans autoliquidation. |
| S2 | Opération soumise et non exonérée - avec autoliquidation. |
Codes d’exonération
Section titled “Codes d’exonération”Param : exemption_code
| Code | Description |
|---|---|
| E1 | Exonérée par l’article 20 |
| E2 | Exonérée par l’article 21 |
| E3 | Exonérée par l’article 22 |
| E4 | Exonérée par les articles 23 et 24 |
| E5 | Exonérée par l’article 25 |
| E6 | Exonérée pour d’autres motifs |
Codes de non-soumission
Section titled “Codes de non-soumission”Param : no_subject_code
| Code | Description |
|---|---|
| OT | Non soumis en vertu de l’article 7 de la norme forale TVA. Autres cas de non-soumission. |
| RL | Non soumis en raison des règles de localisation. |
| VT | Non soumis, ventes réalisées pour le compte de tiers. |
| IE | Non soumis dans le TAI selon les règles de localisation, mais avec répercussion d’un impôt étranger. |
Type d’identifiant client
Section titled “Type d’identifiant client”Param : customer_party_tax_scheme
| Code | Description |
|---|---|
| 02 | Numéro de TVA intracommunautaire (NIF-IVA) |
| 03 | Passeport |
| 04 | Pièce d’identité officielle émise par le pays de résidence |
| 05 | Certificat de résidence |
| 06 | Autre document justificatif |