This is the full developer documentation for B2Brouter Docs
# Documentation
> From your first invoice to advanced integrations — manuals, guides, use cases, and resources in one place.
Docs for everyone
B2Brouter\
documentation.
==============
From your first invoice to advanced integrations — manuals, guides, use cases, and resources in one place.
[Explore the user guide](/en/user-guide/overview/)
[Check the developer resources](/en/developers/overview/)
50+countries supported
22+invoice formats
Peppolaccess point since 2012
InvoiceF-2026-0418
Factur-X/ZUGFeRD
Total€ 1,248.50
InvoiceF-2026-0419
UBL 2.1
Total€ 1,248.50
send-invoice.sh
```
# Create a draft invoice
curl https://api.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
-u {API_KEY}: \
-H 'Content-Type: application/json' \
-d '{
"invoice": {
"contact_id": {CONTACT_ID},
"invoice_lines_attributes": [{
"description": "Consulting services",
}]
}
}'
↳ 201 Created
```
Start here
## Set up your account and start invoicing
[See the user guide ](/en/user-guide/overview/)
[Account set-upSet up your payment method, add a logotype, activate reception via email, enable tax reporting if needed, and configure your Peppol network access.](/en/user-guide/account-settings/setting-up-your-company/)
Send & receive
### Day-to-day invoicing
[Send invoicesCreate and send via any network.](/en/user-guide/sending-documents/create-electronic-invoice/)[Receive invoicesManage statuses from your inbox.](/en/user-guide/receiving-documents/receive-einvoices/)[Manage contactsClients & suppliers, with routing.](/en/user-guide/contacts/create-contact/)
Tax reporting & B2G
## Route to the right authority
[VerifactuAgencia TributariaSpain · Tax report](/en/user-guide/tax-report/verifactu/)[DGFiP e-Invoicing and e-ReportingDirection Générale des Finances PubliquesFrance · Tax report](/en/user-guide/tax-report/dgfip-e-invoicing-france/)[KSeFKrajowa Administracja Skarbowa (KAS)Poland · Tax report](/en/user-guide/tax-report/ksef/)[SIIAgencia TributariaSpain · Tax report](/en/user-guide/tax-report/sii/)[TBAIHacienda ForalSpain · Tax report](/en/user-guide/tax-report/ticketbai/)
[SDIAgenzia delle EntrateItaly · Tax report](/en/user-guide/routing-codes/sdi-recipient-code/)[PeppolInternational networkInternational · B2B](/en/user-guide/routing-codes/peppol-identifier/)[Chorus ProFrench public administrationsFrance · B2G](/en/user-guide/routing-codes/chorus-pro-service-code/)[FACeSpanish General Point of EntrySpain · B2G](/en/user-guide/routing-codes/dir3-codes/)[eFACTCatalan General Point of EntryCatalonia · B2G](/en/user-guide/routing-codes/dir3-codes/)
For developers
## Integrate without rewriting your stack.
[See all developer resources ](/en/developers/overview/)
[Get started with the B2Brouter APIAuthenticate and POST your first invoice.](/en/developers/overview/)[SandboxIntegrations in a secure environment.](/en/developers/testing/sandbox/)[SDKStart integrating with our PHP library.](/en/developers/start-using-our-api/sdks/)[GuidesStep-by-step integration guides.](/en/developers/essential-guides/account/)[Use CasesFind the right use case for your needs.](/en/developers/common-use-cases/send-invoices-end-to-end/)[Integrate without codeNo-code integration options based on file transfer.](/en/developers/integration-without-code/sftp/)
send-invoice.sh
```
# Create a draft invoice
curl https://api.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
-u {API_KEY}: \
-H 'Content-Type: application/json' \
-d '{
"invoice": {
"contact_id": {CONTACT_ID},
"invoice_lines_attributes": [{
"description": "Consulting services",
"quantity": "1",
"price": "100",
"taxes_attributes": [{ "name":"VAT","category":"S","percent":"21"}]
}]
}
}'
↳ 201 Created
```
Tools
## Build with confidence
[Invoice field mapperJSON to UBL, CII, FatturaPA, Facturae or KSeF.](/en/developers/tools/invoice-field-mapper/)[Code listsDocument types, transport types, ID schemes, and much more.](/en/developers/tools/code-list/)[Document validatorCheck that your invoice parses.](/en/developers/tools/validator/)[Need help?Open a support ticket →](/en/user-guide/support/open-incident-report/)
# User guide
> Step-by-step guides for everyday tasks — from signing up and setting up your company to sending invoices and managing everything in between.
App user guide
Get the most out of\
B2Brouter application.
----------------------
Step-by-step guides for everyday tasks — from signing up and setting up your company to sending invoices and managing everything in between.
AAcme Studio
Facturae 3.2
InvoiceF-2026-0418
Bill toAcme Studio
Hosting (annual)100,00 €
Subtotal100,00 €
VAT 21%21,00 €
Total121,00 €
AAcme Studio
UBL 2.1
InvoiceF-2026-0418
Bill toNorthwind Ltd
Design services€ 900.00
Hosting (annual)€ 240.00
Subtotal€ 1140.00
VAT 21%€ 239.40
Total€ 1,379.40
Sent via Peppol network
Start here
## Set up your account and start invoicing
[Account set-upSet up your payment method, add a logotype, activate reception via email, enable tax reporting if needed, and configure your Peppol network access.](/en/user-guide/account-settings/setting-up-your-company/)
Send & receive
### Day-to-day invoicing
[Send invoicesFill in your lines, taxes, and payment details, then deliver it to your client.](/en/user-guide/sending-documents/create-electronic-invoice/)[Receive invoicesManage statuses from your inbox.](/en/user-guide/receiving-documents/receive-einvoices/)[Manage contactsClients & suppliers, with routing.](/en/user-guide/contacts/create-contact/)
Your user profile
## Manage your user profile
[User profileManage your name, language, and preferences.](/en/user-guide/my-user-profile/user-profile/)[Managing & adding accountsRun several companies from one login.](/en/user-guide/my-user-profile/add-account/)[Manage your subscriptionView your current subscription, upgrade or switch tiers as your needs grow, and keep your billing details up to date.](/en/user-guide/subscription/subscribe-premium/)[Two-factor authenticationAdd a second layer of security to your login.](/en/user-guide/my-user-profile/two-factor-authentication/)[Retrieving your passwordReset access if you've forgotten it.](/en/user-guide/my-user-profile/reset-password/)[Recover email accessRegain access to a lost or changed email.](/en/user-guide/my-user-profile/recover-access/)
Did you know?
## More from B2Brouter
[B2Brouter for WooCommerceStep-by-step guide to connect your store and start generating compliant e-invoices automatically on every order.](/en/user-guide/connect/woocommerce/)[AI Connector (MCP)Connect your usual AI assistant to B2Brouter so it can query your invoices, tax reports and business figures with real data, without leaving the chat.](/en/user-guide/connect/mcp/)[eAccountingGuide for accountants who want to connect with their clients and receive invoices automatically in their accounting software. Requires a Premium plan on the client's account.](/en/user-guide/connect/eaccounting/)
Contacts
## Clients & suppliers
[Creating a contactAdd a client or supplier to invoice.](/en/user-guide/contacts/create-contact/)[Importing your contactsBring in many contacts at once.](/en/user-guide/contacts/import-contacts/)[Creating a branch of a contactAdd departments, offices, or units.](/en/user-guide/contacts/create-uo/)[Assigning routing numbersSet the codes that route to a client.](/en/user-guide/routing-codes/manage-routing-codes/)
[The B2Brouter directoryFind any company registered on B2Brouter or the Peppol network by tax ID or identifier. Verify a recipient is reachable before sending and retrieve their routing codes automatically.](/en/user-guide/contacts/b2brouter-directory/)
All you need for e-invoicing
## Working with invoices
[Send an invoiceIssue through the right network automatically.](/en/user-guide/sending-documents/create-electronic-invoice/)[Rounding and VATHow totals and taxes are calculated.](/en/user-guide/manage-documents/rounding-and-vat-calculation/)[Document view modesDisplay documents in different viewing modes.](/en/user-guide/manage-documents/document-view/)[Managing invoice statusTrack an invoice from draft to delivered.](/en/user-guide/sending-documents/managing-status-invoice/)[Status of issued invoicesWhat each state means and what to do.](/en/user-guide/sending-documents/status-issued-invoices/)[Recurring invoicesSchedule invoices that repeat on a cycle.](/en/user-guide/manage-documents/manage-recurring-invoices/)
Tax reporting & B2G
## Route to the right authority
[VerifactuAgencia TributariaSpain · Tax report](/en/user-guide/tax-report/verifactu/)[DGFiP e-Invoicing and e-ReportingDirection Générale des Finances PubliquesFrance · Tax report](/en/user-guide/tax-report/dgfip-e-invoicing-france/)[KSeFKrajowa Administracja Skarbowa (KAS)Poland · Tax report](/en/user-guide/tax-report/ksef/)[SIIAgencia TributariaSpain · Tax report](/en/user-guide/tax-report/sii/)[TBAIHacienda ForalSpain · Tax report](/en/user-guide/tax-report/ticketbai/)
[SDIAgenzia delle EntrateItaly · Tax report](/en/user-guide/routing-codes/sdi-recipient-code/)[PeppolInternational networkInternational · B2B](/en/user-guide/routing-codes/peppol-identifier/)[Chorus ProFrench public administrationsFrance · B2G](/en/user-guide/routing-codes/chorus-pro-service-code/)[FACeSpanish General Point of EntrySpain · B2G](/en/user-guide/routing-codes/dir3-codes/)[eFACTCatalan General Point of EntryCatalonia · B2G](/en/user-guide/routing-codes/dir3-codes/)
Document types available
## More than e-invoices
[Credit notesCorrect or refund an issued invoice.](/en/user-guide/sending-documents/create-credit-note/)[Self-invoiceCreate an invoice on behalf of a supplier.](/en/user-guide/sending-documents/self-invoice/)[Proforma invoiceIssue a preliminary, non-fiscal invoice.](/en/user-guide/sending-documents/create-proforma-invoice/)[Simplified invoicesIssue tickets and simplified receipts.](/en/user-guide/sending-documents/create-ticket/)[QuotesDraft estimates and turn them into invoices.](/en/user-guide/sending-documents/create-quotes/)[Orders & dispatch advicesReceive and manage purchase orders and dispatch advices.](/en/user-guide/sending-documents/create-dispatch-notes/)
Support
## Something not working?
[My invoice shows as sent but the receiver hasn't received itFind out why and how to resolve it.](/en/user-guide/troubleshooting/invoice-not-received/)[I need to change my account IDStep-by-step instructions to update it.](/en/user-guide/troubleshooting/change-tax-identification-number/)[I want to transfer my account to another userHow to hand over ownership of your account.](/en/user-guide/troubleshooting/transfer-account-ownership/)[Need help?Open a support ticket →](/en/user-guide/support/open-incident-report/)
# Developers resources overview
> Everything you need to integrate — authentication, versioning, guides, use cases, and the tools to send and receive compliant invoices anywhere.
Developers resources
Build on the\
B2Brouter API.
--------------
Everything you need to integrate — authentication, versioning, guides, use cases, and the tools to send and receive compliant invoices anywhere.
[API reference](https://developer.b2brouter.net/reference/get-invoice)
send-invoice.sh
```
# Create a draft invoice
curl https://api.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
-u {API_KEY}: \
-H 'Content-Type: application/json' \
-d '{
"invoice": {
"contact_id": {CONTACT_ID},
"invoice_lines_attributes": [{
"description": "Consulting services",
}]
}
}'
↳ 201 Created
```
JSON · REST
Start here
## Start using our API
[Read the introduction](/en/developers/start-using-our-api/introduction/)
[IntroductionEnvironments, authentication, API versioning, account identifiers, and rate limits — everything you need before your first API call.](/en/developers/start-using-our-api/introduction/)[Get startedEnd-to-end walkthrough: create accounts, activate transports, configure tax reporting, and send your first invoice.](/en/developers/start-using-our-api/get-started/)[PHP SDKZero-dependency PHP library for creating invoices, managing contacts, and handling tax reports — with automatic retries, pagination, and multi-country support.](/en/developers/start-using-our-api/sdks/)
Versioning
## Track API changes
[ChangelogEvery breaking and non-breaking change, by version date.](/en/developers/changelog/overview/)[Migration guidesStep-by-step instructions for upgrading between API versions.](/en/developers/upgrade-api/from-v2026-03-02-to-v2026-04-20/)
Testing
## Try it safely first
[SandboxExperiment with sample data and inspect every request.](/en/developers/testing/sandbox/)[Staging environmentA full API replica for pre-production testing.](/en/developers/testing/environments/)
Tools
## Build with confidence
[Invoice field mapperJSON to UBL, CII, FatturaPA, Facturae or KSeF.](/en/developers/tools/invoice-field-mapper/)[Code listsDocument types, transport types, ID schemes, and much more.](/en/developers/tools/code-list/)[Document validatorCheck that your invoice parses.](/en/developers/tools/validator/)[Need help?Discussions →](https://developer.b2brouter.net/discuss)
Essential guides
## Core concepts
Deep reference for the API objects you'll use in every integration.
[TransactionsWhat counts as a billable transaction — sending, receiving, and downloading documents — and the exceptions that don't.](/en/developers/essential-guides/transaction/)[AccountCreate and manage sub-accounts via API — with the billing and transaction-limit implications for eDocSync and eDocExchange subscriptions.](/en/developers/essential-guides/account/)[DirectoryLook up any entity by country, scheme, and identifier to resolve its transport type, document types, and country-specific routing codes.](/en/developers/essential-guides/b2brouter-directory/)[SchemesTIN, CIN, PIN, and EAS codes — the identifiers every contact and account requires.](/en/developers/essential-guides/schemes/)[TransportsConfigure how documents are delivered — Peppol, email, SFTP, FACe, and more.](/en/developers/essential-guides/transports/)[Payment methodsPayment codes and their equivalents in UBL, Facturae, FatturaPA, and CII — mapped to the standard BT-81.](/en/developers/essential-guides/payment-methods/)[Tax report settingsEnable automated submission for Verifactu, TicketBAI, SDI, KSeF, LHDN, and DGFiP.](/en/developers/essential-guides/tax-report-settings/)[WebhooksReceive real-time notifications for invoice and tax report state changes.](/en/developers/essential-guides/webhooks/)
Guides by country
## Tax reporting guides
[VerifactuAgencia TributariaSpain](/en/developers/guides-by-country/spain/verifactu/)[TBAIHacienda ForalSpain](/en/developers/guides-by-country/spain/ticketbai/)[DGFiP e-Invoicing and e-ReportingDirection Générale des Finances PubliquesFrance](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/)[KSeFKrajowa Administracja Skarbowa (KAS)Poland](/en/developers/guides-by-country/poland/ksef/)[ZATCAZakat - Tax and Customs AuthoritySaudi Arabia](/en/developers/guides-by-country/saudi-arabia/zatca/)[LHDNLembaga Hasil Dalam NegeriMalaysia](/en/developers/guides-by-country/malaysia/lhdn-tax-report/)
Use cases
## Frequent flows
[Common use casesEnd-to-end API walkthroughs for the most frequent tasks — send, receive, calculate, and import documents.](/en/developers/common-use-cases/send-invoices-end-to-end/)[Use cases by countryCountry-specific integration patterns for mandatory tax systems — TicketBAI, Verifactu, SDI, Chorus Pro, and FACe.](/en/developers/use-cases-by-country/submit-ticketbai/)
# Welcome to B2Brouter Docs
> All B2Brouter documentation in one place.
## Where do you want to start?
[Section titled “Where do you want to start?”](#where-do-you-want-to-start)
Documentation
Step-by-step guides to set up your account, send and receive documents, and manage payments.
Developers
Integrate B2Brouter into your application with our REST API. Browse essential guides and use cases.
Support
Have questions? Check the support section or contact our team.
# Invoice calculation
> How B2Brouter calculates net and gross amounts from quantity, price, taxes, discounts, and charges.
This guide explains how B2Brouter calculates **net** and **gross amounts** based on inputs such as **quantity**, **price**, **base quantity**, **taxes**, and optional **discounts** or **charges**, which can be provided as either **percentages** or **fixed amounts**. The calculations support adjustments at both the **line item** and **invoice** levels.
All calculations and rounding are done automatically by B2Brouter. The scope of this guide is to explain the process and what results you should expect.
## Required inputs
[Section titled “Required inputs”](#required-inputs)
### Invoice level
[Section titled “Invoice level”](#invoice-level)
These are global discounts and charges at the Invoice level, using `allowance_charges_attributes`:
* **Allowance (Discount)**: (Optional) `{allowance_charge_indicator: "allowance"}` with either:
* **percentage**: Percentage discount applied to the entire invoice.
* **amount**: Fixed discount amount applied to the entire invoice.
* **Charge**: (Optional) `{allowance_charge_indicator: "charge"}` with either:
* **percentage**: Percentage charge applied to the entire invoice.
* **amount**: Fixed charge amount applied to the entire invoice.
### Line level
[Section titled “Line level”](#line-level)
For each line (`invoice_lines_attributes`) you need to provide the following inputs:
* **Quantity** (`quantity`): Number of units.
* **Price** (`price`): Price for the given base quantity of units.
* **Base Quantity** (`base_quantity`): The number of units to which the price applies. Defaults to `1`. Use this when the price is quoted per multiple units (e.g., price per 100 pieces).
* **Taxes** (`taxes_attributes`):
* **Name**: Name of the tax (e.g. VAT, IVA, IRPF).
* **Category**: Tax category.
* **Percent**: Percentage of tax applied (can be positive or negative).
* **Allowance (Discount)**: (Optional) via `allowance_charges_attributes[]{allowance_charge_indicator: "allowance"}` with either:
* **percentage**: Percentage discount applied to the line.
* **amount**: Fixed discount amount applied to the line.
* **Charge**: (Optional) via `allowance_charges_attributes[]{allowance_charge_indicator: "charge"}` with either:
* **percentage**: Percentage charge applied to the line.
* **amount**: Fixed charge amount applied to the line.
* **Extension Amount**: (Optional) The total “net” amount of the Invoice line before applying Taxes. If it is not provided, the API will calculate it as explained in [Line-Level Net Amounts](#line-level-net-amounts).
## Calculations
[Section titled “Calculations”](#calculations)
### Account-Level Settings
[Section titled “Account-Level Settings”](#account-level-settings)
* **Rounding Method** (`rounding_method`): Rounding method used for further calculations. By default, it is set to **Round half up** (`half_up`)
* **Round Before Sum** (`round_before_sum`): Round line taxable amounts before sum. By default, it is set to `false`.
* **Apply Taxes per Line**(`apply_taxes_per_line`): If set to `true`, calculate *Invoice-Level* tax amount by applying taxes to each line and then adding them. Otherwise, add all lines’ taxable bases and apply the tax to the sum. By default, it is set to `false`.
* **Skip Line Taxable Base Rounding** (`skip_line_taxable_base_rounding`): When enabled, per-line taxable bases and tax amounts are **not** individually rounded — only the final accumulated tax total is rounded. By default, it is set to `false`.
> **Important:** This setting is intended exclusively for accounts whose ERP uses VAT-inclusive totals, as it deviates from the standard tax calculation. Requires `apply_taxes_per_line: true` to have any effect.
### Line level Net Amounts
[Section titled “Line level Net Amounts”](#line-level-net-amounts)
```plaintext
Line Base Amount = Quantity * Price / Base Quantity
```
When `base_quantity` is `1` (the default), this simplifies to `Quantity * Price`.
When `base_quantity` is greater than `1`, it allows prices to be expressed per multiple units. For example, if screws are priced at 12.50 per 100 pieces and you order 500:
```plaintext
Line Base Amount = 500 * 12.50 / 100 = 62.50
```
If **Extension Amount**(`extension_amount`) has been provided it will be used as the **line net amount** in all calculations. Otherwise, we calculate the **line net amount**.
#### Line with Discount and Charge
[Section titled “Line with Discount and Charge”](#line-with-discount-and-charge)
For each line item, we calculate the **net amount** by applying the discount and charge:
```plaintext
Line Net Amount = Line Base Amount - Line Discount Amount + Line Charge Amount
```
If our discount or charges represented as percentages, we calculate their amount as:
```plaintext
Line Charge Amount = Line Base Amount * (Line Charge Percentage / 100)
```
```plaintext
Line Discount Amount = Line Base Amount * (Line Discount Percentage / 100)
```
If our discount or charge amounts are fixed amounts, we can skip this step.
Finally, we calculate the **line net amount** by subtracting the discount and adding the charge:
```plaintext
Line Net Amount = Line Base Amount - Line Discount Amount + Line Charge Amount
```
*As the line net amount must be rounded to two decimals, please note that the different parts of the calculation must be rounded separately. For example, the result of `Line net amount` = `Line Base Amount` must be round to two decimals, and the `allowance/charge amounts` are also rounded separately.*
If line discount/charge amount are not provided, treat it as `0`.
```plaintext
Line Net Amount = Line Base Amount
```
### Invoice Level Subtotal
[Section titled “Invoice Level Subtotal”](#invoice-level-subtotal)
Sum the net amounts for all line items to calculate the **subtotal**. This calculation depends on the `round_before_sum` setting.
* **Round Before Sum** is set to `true`: **Rounding method** is applied to each **line net amount** before adding them all.
```plaintext
Subtotal = ∑(rounding_method(Line Net Amount))
```
* **Round Before Sum** is set to `false`: **Rounding method** is applied to the sum of all **line net amounts**.
```plaintext
Subtotal = rounding_method(∑(Line Net Amount))
```
If invoice-level discounts or charges are provided:
```plaintext
Subtotal = ∑(Line Net Amounts) - Invoice Discount Amount + Invoice Charge Amount
```
*If the percent is provided for the invoice level discount or charge, we will calculate their amounts similarly as in the line level calculation.*
### Tax calculation logic
[Section titled “Tax calculation logic”](#tax-calculation-logic)
After calculating the line’s net amount, apply taxes (if provided) based on the **taxes\_attributes**. A negative tax percent indicates a deduction. The taxes are applied based on the `apply_taxes_per_line` setting.
```plaintext
Line Taxable Amount (or Base) = Line Net Amount
```
**Apply Taxes per Line** is set to `true`: The taxes will be applied to each **line taxable amount (or base)** and then summed up all together for the Invoice total tax amount.
#### Tax Calculation for Each Line
[Section titled “Tax Calculation for Each Line”](#tax-calculation-for-each-line)
* Multiple taxes may apply to each line. Each tax is applied individually.
```plaintext
Line Tax Amount = Line Taxable Amount × (Tax Percent / 100)
```
* If the **Round Before Sum** setting is `active` the taxable amount for each line is **rounded** before the tax percentage is applied. Otherwise, it will be **rounded** after applying tax percentage.
```plaintext
Line Tax Amount = rounding_method(Line Taxable Amount) x (Tax Percent / 100) [Active]
Line Tax Amount = rounding_method(Line Taxable Amount x (Tax Percent / 100)) [Inactive]
```
**Summing Tax Amounts per Line**
The total tax amount for each line is the sum of all taxes applied to that line.
**Invoice-Level Tax Calculation**
The Invoice-Level Tax Amount calculation is based on the `apply_taxes_per_line` and `round_before_sum` setting. So expect 4 different approaches based on the settings:
1. `apply_taxes_per_line = true` and `round_before_sum = true`
```plaintext
Line Taxable Amount = rounding_method(Line Net Amount - Line Discount Amount)
Line Tax Amount = rounding_method(Line Taxable Amount x (Tax Percent / 100))
Invoice Tax Amount = ∑(Line Tax Amounts)
```
2. `apply_taxes_per_line = true` and `round_before_sum = false`
```plaintext
Line Taxable Amount = Line Net Amount - Line Discount Amount
Line Tax Amount = rounding_method(Line Taxable Amount x (Tax Percent / 100))
Invoice Tax Amount = ∑(Line Tax Amounts)
```
3. `apply_taxes_per_line = false` and `round_before_sum = true`
```plaintext
Line Taxable Amount = rounding_method(Line Net Amount - Line Discount Amount)
Invoice Tax Amount = Line Taxable Amounts x (Tax Percent / 100)
```
4. `apply_taxes_per_line = false` and `round_before_sum = false`
```plaintext
Line Taxable Amount = Line Net Amount - Line Discount Amount
Invoice Tax Amount = Line Taxable Amounts x (Tax Percent / 100)
```
**Invoice Level Total**:
```plaintext
Total = Subtotal + Invoice Tax Amount
```
## Rounding Methods
[Section titled “Rounding Methods”](#rounding-methods)
### Principles
[Section titled “Principles”](#principles)
**Decimal Precision**
All values are rounded to **two decimal places** (representing cents in most currencies).
**Rounding Rules**
Rounding Method setting defines how rounding is applied, and it can be set up between three different options:
* **Round half up** (`half_up`): Is the default rounding method.
* If the third decimal place is **5 or higher**, round the second decimal place **up**.
* If the third decimal place is **4 or lower**, leave the second decimal place **unchanged**.
* **Bankers rounding** (`bankers`): Round halfway values to the closest even value.
* If the third decimal place is **6 or higher**, round the second decimal place **up**.
* If the third decimal place is **4 or lower**, leave the second decimal place **down**.
* If the third decimal place is **5**, round the second decimal place to the closest even value. For example, `1.235` is rounded to `1.24`, but `1.225` is rounded to `1.22`.
* **Truncate** (`truncate`): Removes the decimal part of the number to round.
* The numbers `1.234` and `1.236` both will round to `1.23`.
### How Rounding is Applied in the API
[Section titled “How Rounding is Applied in the API”](#how-rounding-is-applied-in-the-api)
1. **Line-Level Calculations**: All line-level net amounts are rounded to two decimal places **before** any further calculations.
2. **Tax Calculations**: Tax amounts are calculated and rounded before being added to the line net amount.
3. **Subtotal and Invoice-Level Adjustments**: Subtotals are rounded before applying invoice-level adjustments.
### Summary
[Section titled “Summary”](#summary)
* First, apply rounding **separately** at each step (discounts, charges, and tax) to avoid compounding errors.
* Always round to two decimal places **before** using the value in subsequent calculations.
## Example API Calculation Flow
[Section titled “Example API Calculation Flow”](#example-api-calculation-flow)
### Example 1: Standard pricing
[Section titled “Example 1: Standard pricing”](#example-1-standard-pricing)
Settings:
* **Rounding Method**: `bankers`
* **Round before sum**: `true`
* **Apply taxes per line**: `true`
#### Input
[Section titled “Input”](#input)
```json
{
"invoice_lines_attributes": [
{
"quantity": 3,
"price": 33.275,
"taxes_attributes": [
{
"name": "VAT",
"category": "S",
"percent": 21
}
],
"allowance_charges_attributes": [
{ "allowance_charge_indicator": "allowance", "amount": 5 },
{ "allowance_charge_indicator": "charge", "percentage": 12.777 }
]
},
{
"quantity": 7,
"price": 5.355,
"taxes_attributes": [
{
"name": "VAT",
"category": "S",
"percent": 21
}
]
}
],
"allowance_charges_attributes": [
{ "allowance_charge_indicator": "charge", "amount": 3 }
],
"currency": "EUR"
}
```
#### Step-by-Step Calculation
[Section titled “Step-by-Step Calculation”](#step-by-step-calculation)
1. **Line Net Amount (before tax)**:
Line 1:
* Base Amount = `3 × 33.275€ = 99.825€`
* Allowance (discount) Amount = **5.00€**.
* Charge Amount = `99.825€ × 12.777/100 = 12.75464€` → Rounded to **12.75€**.
* Line Net Amount = `99.825€ - 5.00€ + 12.75€ = 107.575€` → Rounded to **107.58€**.
Line 2:
* Base Amount = `7 x 5.355€ = 37.485€` → Rounded to **37.48€**.
2. **Invoice Subtotal**:
* Sum of all Line Net Amount = `107.58€ + 37.48€ = 145.06€`
* Global Charge Amount = **3€**.
3. **Tax Calculation** (apply taxes per line = true):
* Line 1: `107.58€ × 21/100 = 22.5918€` → Rounded to **22.59€**.
* Line 2: `37.48€ × 21/100 = 7.8708€` → Rounded to **7.87€**.
* Total Tax = `22.59€ + 7.87€ = 30.46€`
4. **Invoice Total**:
* Invoice Subtotal = `145.06€ + 3€ = 148.06€`
* Invoice Total = `148.06€ + 30.46€ = 178.52€`
#### Output
[Section titled “Output”](#output)
```json
{
"subtotal": 148.06,
"taxes": [
{
"name": "VAT 21.00%",
"base": 145.06,
"amount": 30.46
}
],
"total": 178.52
}
```
### Example 2: Using base quantity for bulk pricing
[Section titled “Example 2: Using base quantity for bulk pricing”](#example-2-using-base-quantity-for-bulk-pricing)
Available from version 2026-03-02
#### Input
[Section titled “Input”](#input-1)
```json
{
"invoice_lines_attributes": [
{
"description": "Steel screws M6x20",
"quantity": 500,
"price": 12.5,
"base_quantity": 100,
"unit": 1,
"taxes_attributes": [{ "name": "VAT", "category": "S", "percent": 21 }]
},
{
"description": "A4 printing paper",
"quantity": 5000,
"price": 45.0,
"base_quantity": 1000,
"unit": 1,
"taxes_attributes": [{ "name": "VAT", "category": "S", "percent": 21 }]
}
],
"currency": "EUR"
}
```
#### Step-by-Step Calculation
[Section titled “Step-by-Step Calculation”](#step-by-step-calculation-1)
1. Line 1: `500 * 12.50 / 100 = 62.50€`
2. Line 2: `5000 * 45.00 / 1000 = 225.00€`
3. Subtotal: `62.50 + 225.00 = 287.50€`
4. VAT 21%: `287.50 × 21 / 100 = 60.375` → Rounded to **60.38€**
5. Total: `287.50 + 60.38 = 347.88€`
#### Output
[Section titled “Output”](#output-1)
```json
{
"subtotal": 287.5,
"taxes": [
{
"name": "VAT 21.00%",
"base": 287.5,
"amount": 60.38
}
],
"total": 347.88
}
```
**Note on API versions**
In API versions before 2026-03-02, the `price` field in responses contained the effective unit price (price / base\_quantity). Starting with version 2026-03-02, the API returns the original `price` and `base_quantity` separately.
# Issue a simplified invoice (ticket)
> How to issue a simplified invoice (ticket) using the B2Brouter API.
A simplified invoice (ticket) is a lightweight sales document typically delivered as a **PDF**. There is no universal XML standard for tickets, so for integrations the recommended approach is to **work with JSON** and let B2Brouter generate the PDF.
Use `"type": "IssuedSimplifiedInvoice"` to create a simplified invoice.
## Create the simplified invoice
[Section titled “Create the simplified invoice”](#create-the-simplified-invoice)
**Example request**
```shell
curl --request GET \
--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": false,
"invoice": {
"type": "IssuedSimplifiedInvoice",
"number": "T-2026-0001",
"date": "2026-01-21",
"currency": "EUR",
"file_reference": "POS-ORDER-12345",
"contact": {
"name": "Final consumer",
"country": "es"
},
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 10,
"description": "Coffee",
"taxes_attributes": [
{
"name": "VAT",
"percent": 21,
"category": "S"
}
]
}
]
}
}'
```
**Sample response (excerpt)**
```json
{
"invoice": {
"id": 123456,
"type": "IssuedSimplifiedInvoice",
"number": "T-2026-0001",
"state": "new",
"file_reference": "POS-ORDER-12345",
"document_type_code": "pdf.invoice"
}
}
```
## Send the simplified invoice (generate the PDF)
[Section titled “Send the simplified invoice (generate the PDF)”](#send-the-simplified-invoice-generate-the-pdf)
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/send_invoice/{INVOICE_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
## Download the PDF
[Section titled “Download the PDF”](#download-the-pdf)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/as/pdf.invoice?download=true' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
## Notes
[Section titled “Notes”](#notes)
* Use `file_reference` to store your own internal reference (order ID, ticket ID, POS transaction ID, etc.) and make reconciliation easier.
* If you want B2Brouter to send the PDF by email, set `contact_email_override` on the invoice before creating the invoice or calling `POST /invoices/send_invoice/{INVOICE_ID}`.
* To list simplified invoices: `GET /accounts/{ACCOUNT_ID}/invoices?type=IssuedSimplifiedInvoice`
# Peppol Network - Send self-billing
> How to send self-billing invoices through the Peppol network using B2Brouter.
Available from version 2025-10-13
Self-billing allows buyers to issue invoices on behalf of suppliers. B2Brouter represents self-billing as:
**Outbound**: `IssuedSelfInvoice` via Peppol BIS3 Self-Billing. **Inbound**: `ReceivedSelfInvoice` received over Peppol with reception enabled on the supplier’s account.
## 1. Preparation
[Section titled “1. Preparation”](#1-preparation)
See the parent guide [Send an invoice through Peppol](/en/developers/common-use-cases/peppol-network-send-with-json-payload/) for an overview and transport configuration details.
## 2. Create a self-billed invoice (IssuedSelfInvoice)
[Section titled “2. Create a self-billed invoice (IssuedSelfInvoice)”](#2-create-a-self-billed-invoice-issuedselfinvoice)
To issue a self-billing invoice via API:
1. Use `POST /accounts/{account}/invoices`.
2. Set `invoice.type` to `IssuedSelfInvoice`.
3. Ensure that the [Contact](https://developer.b2brouter.net/reference/create-contact) `transport_type_code` is set to `peppol` and provide the `pin_scheme` and `pin_value`.
**Behavior:**
* B2Brouter creates an `IssuedSelfInvoice`.
* When sending over Peppol, B2Brouter generates a **UBL BIS3 Self-Billing Invoice** (`xml.ubl.invoice.bis3.selfbilling`):
* The **supplier** in the UBL is your contact.
* The **customer** in the UBL is your company (account).
* The response JSON includes:
* `"type": "IssuedSelfInvoice"`
* `"document_type_code": "xml.ubl.invoice.bis3.selfbilling"` (read-only field showing the format used)
> You can also send self-billed invoices through other transports (for example, internal B2Brouter). However, if the contact and country configuration support Peppol self-billing, we recommend using Peppol because it provides better interoperability and traceability.
## 3. Listing and fetching self-billed invoices
[Section titled “3. Listing and fetching self-billed invoices”](#3-listing-and-fetching-self-billed-invoices)
Use standard invoice endpoints with the `type` filter:
* `type=IssuedSelfInvoice` – List self-billed invoices you issued
* `type=ReceivedSelfInvoice` – List self-billed invoices you received
**List invoices:** `GET /accounts/{ACCOUNT_ID}/invoices?type=IssuedSelfInvoice`
**Fetch invoice:** `GET /invoices/{INVOICE_ID}`
## 4. Tax reporting
[Section titled “4. Tax reporting”](#4-tax-reporting)
Self-billed invoices are included in **tax reports** (Verifactu, etc.) under the same rules as regular invoices:
* If a tax report is enabled for the company (e.g., Verifactu in Spain), self-billed invoices are reported automatically when applicable.
* In Verifactu, self-billing is reported in the name of the **emitting company** (`Buyer`).
* Additional tax-reporting behavior (e.g., Verifactu in Spain) may apply to self-billed invoices depending on the tax report settings of the emitting account.
* Self-billed invoices from sellers not subject to Verifactu are excluded from reporting.
## 5. Constraints and known limitations
[Section titled “5. Constraints and known limitations”](#5-constraints-and-known-limitations)
* **Invoice type vs document type**
* Self-billing **always** uses `invoice.type = IssuedSelfInvoice` for outbound documents.
* Attempting to send a regular `IssuedInvoice` using a self-billing document type (e.g., through a misconfigured contact) will fail with a `4xx` error:
* `"The self-billing document type cannot be used for ordinary invoices."`
* **Document type selection**
* `document_type_code` on the invoice is read-only.
* The effective document type is determined by:
* The contact configuration (`transport_type_code`, `document_type_code`), and/or
* Peppol SMP capabilities of the supplier.
**Recommendations for production use:**
* Test end-to-end in **sandbox** between two test accounts (use staging for large or cross-system flows).
* Verify both the **JSON API responses** and sent **UBL files**.
* Monitor the **integration logs** (API requests) for payloads, responses, and error details.
# Peppol Network - Send with JSON payload
> How to send invoices through the Peppol network using a JSON payload.
When your Peppol connection is activated, you unlock the ability to seamlessly transmit documents through this network. Let’s dive into the process of crafting an invoice using a JSON payload.
## Create an issued invoice
[Section titled “Create an issued invoice”](#create-an-issued-invoice)
In the example below, you’ll see how to generate an issued invoice with the delivery method set to **peppol**.
To send an invoice through Peppol, ensure that the [Contact](https://developer.b2brouter.net/reference/create-contact) `transport_type_code` is set to `peppol` and provide the `pin_scheme` and `pin_value`.
**Example request**
```shell
curl --request GET \
--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": false,
"invoice": {
"type": "IssuedInvoice",
"number": "1",
"date": "2024-01-21",
"due_date": "2024-03-01",
"terms": "custom",
"buyer_reference": "071110000000-001-50",
"payment_method": 58,
"iban": "ES9815013231061668117743",
"contact": {
"name": "Voorbeeld B.V.",
"address": "Kerkstraat 12", "postalcode": "1017 GM", "city": "Amsterdam",
"country": "nl",
"transport_type_code": "peppol",
"pin_scheme": "{PIN_SCHEME}", "pin_value": "{PIN_VALUE}"
},
"invoice_lines_attributes": [
{
"unit": 1,
"quantity": 1,
"price": 1,
"description": "Description line",
"taxes_attributes": [
{
"category": "S",
"name": "VAT",
"percent": 19
}
]
}
]
}
}'
```
Typically, these attributes are the minimum requirements for Peppol to send the document. However, some recipients may require additional fields such as `buyer_reference`, as is the case with certain city halls in Germany.
Upon generating the invoice, B2Brouter will automatically assign the most suitable document type for the recipient, if the recipient has the capacity to receive invoices.
## Create a self-billed invoice
[Section titled “Create a self-billed invoice”](#create-a-self-billed-invoice)
To create a self-billed invoice (where the buyer issues the invoice on behalf of the supplier), set `"type": "IssuedSelfInvoice"` in the invoice payload. See the [Self-billing guide](/en/developers/common-use-cases/peppol-network-send-self-billing/) for details.
## Create a credit note
[Section titled “Create a credit note”](#create-a-credit-note)
To create a credit note, use the amendment fields (`"is_credit_note": true`, `"amended_number"`, `"amended_date"`, etc.) to reference the original invoice.
For self-billed credit notes, combine `"type": "IssuedSelfInvoice"` with the amendment fields.
## Send the invoice
[Section titled “Send the invoice”](#send-the-invoice)
The next step is to send the invoice. Before sending, as a [Peppol Certified Service Provider](https://peppol.org/members/peppol-certified-service-providers/), we meticulously verify the presence of all required data and the document’s validity using official schematrons before transmission.
If the generated document is valid, it’s transmitted via our access point.
[Link to API Reference](https://developer.b2brouter.net/reference/send-invoice).
**Example request:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/send_invoice/{invoice-id} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json'
```
## Trace the invoice
[Section titled “Trace the invoice”](#trace-the-invoice)
Finally, you can check the events related to the invoice:
**Example request:**
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/events?invoice_id={INVOICE_ID}' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json'
```
# Receive integrate and manage recieved invoices
> How to receive, integrate, and manage incoming invoices through the B2Brouter API.
This guide explains how to:
* Receive invoices through different channels (Peppol, email, b2brouter, uploads).
* Integrate received invoices in your system (JSON, PDF, original file).
* Manage invoice lifecycle (acknowledge, accept/refuse/paid, etc.).
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* A company with a valid Taxcode Identifier (TIN) or Company Identifier (CIN).
* A registered account on B2Brouter’s test environment: [Register Here](https://app-staging.b2brouter.net/register)
* API permissions (Request via a support ticket: [Open a Ticket](https://www.b2brouter.net/docs/#/en/support/open-incident-report))
* In case of doubt about the registration process, please refer to the [User Guide](https://www.b2brouter.net/docs/#/en/README).
If you still need to create the account or enable reception transports, follow:
* Getting started (account setup):
* Transports guide:
## Key concepts (avoid ID confusion)
[Section titled “Key concepts (avoid ID confusion)”](#key-concepts-avoid-id-confusion)
* `ACCOUNT_ID` is used only in `/accounts/{ACCOUNT_ID}/...` endpoints.
* `INVOICE_ID` is the invoice identifier used in `/invoices/{INVOICE_ID}...` endpoints.
* In a **received invoice**, `invoice.contact` is the **issuer/supplier** (not your own account).
## 1) Ingest received invoices
[Section titled “1) Ingest received invoices”](#1-ingest-received-invoices)
### 1.1 Import a received invoice file (XML) (optional)
[Section titled “1.1 Import a received invoice file (XML) (optional)”](#11-import-a-received-invoice-file-xml-optional)
If you already have the received invoice file (for example, for testing, migrations, or backfills), you can import it with [Import an invoice from a file](https://developer.b2brouter.net/reference/import-invoice). Use the query param `issued=false` to import it as a **ReceivedInvoice**.
It is also possible to generate a received invoice with JSON using the call [Create an invoice](https://developer.b2brouter.net/reference/create-invoice), remember to inform `"type": "ReceivedInvoice"` when doing so.
### 1.2 Receive invoices through transports
[Section titled “1.2 Receive invoices through transports”](#12-receive-invoices-through-transports)
Once transports are enabled for the account, invoices will arrive automatically (for example from Peppol or email).
## 2) List received invoices
[Section titled “2) List received invoices”](#2-list-received-invoices)
**Example request:**
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices?type=ReceivedInvoice' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
**Sample response (excerpt):**
```json
{
"invoices": [
{
"id": 105337,
"number": "2",
"state": "new",
"total": 107.1,
"currency": "EUR"
},
{
"id": 105332,
"number": "1",
"state": "received",
"total": 178.5,
"currency": "EUR"
}
],
"total_count": 2,
"offset": 0,
"limit": 25
}
```
Received invoices may have different status: invoices imported will appear in state “new” and the ones received through different transports will normally appear in state “received”. They could also have “invalid” state if there is any validation issue.
To check all the available invoice states use the endpoint [List of available invoice status](https://developer.b2brouter.net/reference/get-invoice-states).
## 3) Get invoice details
[Section titled “3) Get invoice details”](#3-get-invoice-details)
To get the invoice in JSON format use the [Get an invoice](https://developer.b2brouter.net/reference/get-invoice) endpoint. Add a *Query Param* to include lines information:
**Example request:**
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}?include=lines' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
## 4) Download the original invoice file
[Section titled “4) Download the original invoice file”](#4-download-the-original-invoice-file)
To download the original legal invoice that has been received:
**Example request:**
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/as/original \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
You can also use `document_type_code` as `pdf.invoice` to generate a PDF view, or any other `document_type_code` from [Get document types](https://developer.b2brouter.net/reference/get-document-types) to get the invoice in any electronic invoice format.
## 5) Acknowledge the invoice
[Section titled “5) Acknowledge the invoice”](#5-acknowledge-the-invoice)
Use the endpoint [Mark an invoice as acknowledged](https://developer.b2brouter.net/reference/ack-invoice) to prevent the invoice from being listed when getting the list of received invoices.
## 6) Switch invoice state
[Section titled “6) Switch invoice state”](#6-switch-invoice-state)
Switch the invoice state to inform the sender that you have “accepted”, “refused”, or “paid” the invoice. You can also mark the invoice as “annotated” for internal tracking purposes.
Use the [Switch invoice state](https://developer.b2brouter.net/reference/mark-as-invoice) endpoint. You can add the Body Param `reason` to specify the reason of the rejection. If the invoice came from email, add the body param `"commit": "with_mail"` to inform the sender.
**Example request:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/mark_as \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"state": "refused",
"reason": "Missing PO number"
}'
```
**Sample Response:**
```plaintext
204 No Content
```
For more details, consult the [API Reference](https://developer.b2brouter.net/reference/).
# Send an email with an invoice file attached
> How to send an invoice by email with the document file attached.
## Create an issued invoice
[Section titled “Create an issued invoice”](#create-an-issued-invoice)
In the example below, you’ll learn how to generate an issued invoice with the delivery method set to *email*.
To send an invoice through email, ensure that the `transport_type_code` is set to `email` and provide a valid `email` (email address). Additionally, make sure to specify the `document_type_code` that you want to be attached to the email. In this example, it’s set to `xml.ubl.invoice.xrechnung.v22`
**Example request:**
```shell
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": false,
"invoice": {
"number": "2",
"date": "2024-03-27",
"due_date": "2024-03-29",
"terms": "custom",
"ponumber": "1",
"buyer_reference": "01002-000005228-65",
"contact": {
"name": "Muster GmbH",
"address": "Musterstraße 45",
"postalcode": "10115",
"city": "Berlin",
"province": "Berlin",
"country": "de",
"email": "john.doe@example.com",
"transport_type_code": "email",
"document_type_code": "xml.ubl.invoice.xrechnung.v22"
},
"invoice_lines_attributes": [
{
"unit": 1,
"quantity": 1,
"price": 1,
"description": "Description line",
"article_code": "Article Code",
"taxes_attributes": [
{
"category": "S",
"name": "MwSt",
"percent": 19
}
]
}
]
}
}'
```
## Send the invoice
[Section titled “Send the invoice”](#send-the-invoice)
Before transmission, we meticulously verify the presence of all required data and validate the document against the transport and document type requirements. This ensures that the invoice meets all necessary criteria for successful delivery.
[Link to API Reference](https://developer.b2brouter.net/reference/send-invoice)
**Example request:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/send_invoice/{invoice-id} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json'
```
## Trace your invoice
[Section titled “Trace your invoice”](#trace-your-invoice)
When it comes to invoices sent via email, traceability of the status is limited. You can primarily verify if the invoice has been successfully sent, in which case its status would be `sent`. Conversely, if an error occurred during the sending process, resulting in the invoice not being sent, the status would be changed to `error`. This status update indicates that the invoice encountered an issue and couldn’t be delivered.
# Send invoices (end-to-end)
> End-to-end guide for sending invoices via API - create contact, issue invoice, and retrieve files.
This guide shows a clean API flow to send invoices: create or reuse a contact, create or import an invoice, send it, and retrieve the final files.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* API Key: `X-B2B-API-Key`
* API Version: `X-B2B-API-Version`
* `ACCOUNT_ID`: the account that issues the invoice.
Important IDs:
* `ACCOUNT_ID`: owner account (path param in `/accounts/{ACCOUNT_ID}/...`).
* `CONTACT_ID`: recipient contact (stored inside the account).
* `INVOICE_ID`: invoice identifier (used in `/invoices/{INVOICE_ID}...` endpoints).
## 1 Get or create the recipient contact
[Section titled “1 Get or create the recipient contact”](#1-get-or-create-the-recipient-contact)
### 1.1 Lookup contacts (optional)
[Section titled “1.1 Lookup contacts (optional)”](#11-lookup-contacts-optional)
```bash
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts?offset=0&limit=25&name=Example%20Customer' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### 1.2 Lookup in the directory (optional)
[Section titled “1.2 Lookup in the directory (optional)”](#12-lookup-in-the-directory-optional)
If you know the recipient’s identifier and country, you can retrieve public information from the directory and use it to build the contact.
```bash
curl --request GET \
--url https://api-staging.b2brouter.net/directory/es/ESB63276174 \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### 1.3 Create a contact (if you don’t have one)
[Section titled “1.3 Create a contact (if you don’t have one)”](#13-create-a-contact-if-you-dont-have-one)
```bash
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"contact": {
"name": "Example Customer",
"country": "es",
"is_client": true,
"email": "billing@example.com",
"tin_scheme": "9920",
"tin_value": "ESB00000000",
"transport_type_code": "b2brouter",
"document_type_code": "json"
}
}'
```
Note: `CONTACT_ID` is returned in the response. Do not confuse it with `ACCOUNT_ID`.
## 2 Create (JSON) or import (XML) the invoice
[Section titled “2 Create (JSON) or import (XML) the invoice”](#2-create-json-or-import-xml-the-invoice)
### 2.1 Create invoice using JSON
[Section titled “2.1 Create invoice using JSON”](#21-create-invoice-using-json)
```bash
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 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"send_after_import": false,
"invoice": {
"type": "IssuedInvoice",
"contact_id": {CONTACT_ID},
"number": "INV-0001",
"date": "2026-01-21",
"terms": "custom",
"due_date": "2026-02-21",
"currency": "EUR",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 10,
"description": "Example service",
"taxes_attributes": [
{ "name": "VAT", "category": "S", "percent": 21 }
]
}
]
}
}'
```
### 2.2 Import invoice from a file (when you already generate XML)
[Section titled “2.2 Import invoice from a file (when you already generate XML)”](#22-import-invoice-from-a-file-when-you-already-generate-xml)
```bash
curl --request POST \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices/import?send_after_import=false' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/octet-stream' \
--data 'data:text/xml;name=invoice.xml;base64,PD94bWw+Li4u'
```
## 3 Send the invoice
[Section titled “3 Send the invoice”](#3-send-the-invoice)
```bash
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/send_invoice/{INVOICE_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
## 4 Retrieve the invoice and download final files
[Section titled “4 Retrieve the invoice and download final files”](#4-retrieve-the-invoice-and-download-final-files)
### 4.1 Get the invoice payload
[Section titled “4.1 Get the invoice payload”](#41-get-the-invoice-payload)
```bash
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}?include=lines' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### 4.2 Download legal/original invoice files
[Section titled “4.2 Download legal/original invoice files”](#42-download-legaloriginal-invoice-files)
See the [Download invoices](/en/developers/essential-guides/download-invoice/) guide.
## Related guides
[Section titled “Related guides”](#related-guides)
* [Payment methods](/en/developers/essential-guides/payment-methods/)
* [Invoice calculation](/en/developers/common-use-cases/invoice-calculation/)
* [Send via Peppol (JSON)](/en/developers/common-use-cases/peppol-network-send-with-json-payload/)
# Account
> How to list, create, update, and manage accounts via the B2Brouter API.
The Accounts API allows you to list, create, retrieve, update, delete, and unarchive accounts.
## Accounts API and subscriptions
[Section titled “Accounts API and subscriptions”](#accounts-api-and-subscriptions)
Since functionality varies between eDocSync and eDocExchange subscriptions, it’s important to understand how adding accounts may impact your subscription’s costs and limits.
### eDocSync
[Section titled “eDocSync”](#edocsync)
With [eDocSync](https://www.b2brouter.net/global/b2bsync/), you can create and manage accounts for your users directly via the API.
Things to consider before adding accounts:
* **Cost:** Adding accounts may result in additional charges, depending on your current subscription and total number of accounts.
* **Transaction limits:** Adding accounts may affect your transaction limits, depending on your current subscription and total number of accounts. The group of accounts has a shared transaction limits. If you exceed the limit, extra charges will be applied per transaction.
* **Modifications:** The subscription transaction limit cannot be changed through the API. To request a change, please submit a support ticket to request a change in your subscription.
### eDocExchange
[Section titled “eDocExchange”](#edocexchange)
With [eDocExchange](https://www.b2brouter.net/global/edocexchange/), **you cannot create, delete/archive or unarchive accounts** via the API. Instead, this must be done through the web application:
1. Go to the **Developers tab**.
2. Click the **Edit group icon** next to your group name.
3. From there, you can **add or delete accounts**.
**Notes:** To add or remove accounts, you must have Admin or Owner permissions of the account with the subscription and the account you want to add. Before adding an account to the group, make sure it has been created with the Basic plan.
Things to consider before adding accounts:
* **Cost:** Adding an account results in a one-time additional cost.
* **Transaction limits:** The group of accounts has a shared transaction limits. If you exceed the limit, extra charges will be applied per transaction.
* **Modifications:** The transaction limit cannot be changed through the API. If the web application doesn’t offer the option to upgrade your tier in the subscription section, please submit a support ticket to request a change in your subscription.
**Example request:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-KEY: {API_KEY}' \
--header 'X-B2B-API-Version: {API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"account": {
"country": "de",
"rounding_method": "half_up",
"tin_scheme": 9930,
"tin_value": "DE123456789",
"name": "Muster GmbH",
"address": "Musterstraße 45",
"city": "Berlin",
"postalcode": "10115",
"province": "Berlin",
"email": "jane.doe@example.com"
}
}'
```
# B2Brouter Directory
> How to query the B2Brouter Directory to look up invoice receivers by country and identifier.
The B2Brouter Directory is a curated list of entities. Entities are mostly Invoice receivers. B2Brouter uses this directory to assign the correct Document type and Transport type to a Contact. [B2Brouter App](https://app.b2brouter.net/) has the directory already integrated for Contact creation, but to use the directory in your application you must relay on this API.
The B2Brouter Directory API does not allow enumeration of entities. You can only query a receiver if you already know its country and identifier:
* **Country**: The country of the entity. You can list all the available countries and their codes through API [here.](https://developer.b2brouter.net/reference/get-countries)
* **Scheme**: The EAS (Electronic Address Scheme) of the entity. To list entity schemes in B2Brouter you can use the [Schemes API.](https://developer.b2brouter.net/reference/get-schemes)
* **Id**: The identifier of the entity that can be a Tax Identification Number or any other identifier corresponding to the scheme or country.
## Entity lookup
[Section titled “Entity lookup”](#entity-lookup)
To obtain information about the transport method and document types that an entity can receive, perform a [Lookup](https://developer.b2brouter.net/reference/lookup-directory-by-country-and-scheme) in our directory. If the entity is listed, you will receive a JSON response containing basic entity details and specific information about document reception.
The response will include a key for each document type, such as Invoice, Order, Despatch Advice, etc. Each document will have the following details:
* **transport\_type\_code** to identify the [transport](https://developer.b2brouter.net/reference/get-transport-types) most commonly used to send documents to the entity.
* **document\_type\_code** to identify the most appropriate [document type](https://developer.b2brouter.net/reference/get-document-types) for sending documents.
* **other\_document\_type\_codes**, an array of other document types that the entity can receive.
For clients using *Peppol*, you can perform a [Lookup](https://developer.b2brouter.net/reference/lookup-directory-by-country-and-scheme) in our directory to list all the available Peppol document types that your client uses. Keep in mind that in case of transport type *Peppol*, the list of other document type codes may contain document types that B2Brouter is not able to generate. You can list all the document types that B2Brouter supports [here.](https://developer.b2brouter.net/reference/get-document-types)
## Country specific routing codes
[Section titled “Country specific routing codes”](#country-specific-routing-codes)
Some public entities that have organizational units or offices may have special codes. If you want to send an Invoice to public administration you may need to use those codes.
Different countries use different number of codes to designate public entities and offices. B2Brouter uses fields **cinX\_scheme** and **cinX\_value** for routing codes, where *X* is a number that can go from 1 to 5. The meaning of a value depends on its scheme.
Note: **8xxx** schemes are not part of the EAS code list. This range is specific to B2Brouter.
### Spain (DIR3 / DIRe)
[Section titled “Spain (DIR3 / DIRe)”](#spain-dir3--dire)
Spanish public entities use [DIR3](https://www.b2brouter.net/es/que-es-un-codigo-dir3/) (scheme **8014**) routing codes:
* **cin1\_value**: Accounting office (*Oficina contable*)
* **cin2\_value**: Managing body (*Órgano gestor*)
* **cin3\_value**: Processing unit (*Unidad tramitadora*)
* **cin4\_value**: Proposing body (*Órgano proponente*)
Spanish private entities use [DIRe](https://www.b2brouter.net/es/que-es-un-codigo-dire/) (scheme **8015**) routing codes:
* **cin5\_value**: DIRe code (*Código DIRe*)
### France
[Section titled “France”](#france)
French companies or public entities may use *code service* (scheme **8017**). [Learn more](https://www.b2brouter.net/us/electronic-invoice-france/)
* **cin1\_value**: Code service
### Croatia
[Section titled “Croatia”](#croatia)
Croatian entities may use *Croatian internal code business unit (cicbu)* (scheme **8016**)
* **cin1\_value**: *cicbu*
## EDIFACT routing codes
[Section titled “EDIFACT routing codes”](#edifact-routing-codes)
EDIFACT uses GLN codes (scheme **0088**):
* **cin1\_value**: Buyer (*Comprador*)
* **cin2\_value**: Delivery place (*Lugar de entrega*)
* **cin3\_value**: Invoice receiver (*Receptor de la factura*)
* **cin4\_value**: Payer (*Pagador*)
* **cin5\_value**: Final recipient (*Destinatario final*)
## Lookup from Directory
[Section titled “Lookup from Directory”](#lookup-from-directory)
**Example request:**
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/directory/es/ESB12345678 \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
# Download invoices
> How to download legal, original, and exported invoice files and attachments.
When working with invoices, it is important to differentiate between:
* **Legal invoice file**: the **final official file** that was delivered (issued invoices) or received (received invoices).
* **Original invoice file**: the **first file received by B2Brouter** for that invoice (for issued invoices, this is typically an imported source file; for received invoices, this is the invoice received from the issuer).
* **Exporting** an invoice to a specific format: B2Brouter generates a document from the invoice data (it may differ from the legal/original file).
* **Attachments**: additional files linked to the invoice (PDFs, etc.). Attachments are **not** the invoice itself.
Depending on what you need, you will either:
* Call a **direct download endpoint** (for example, `/invoices/{INVOICE_ID}/as/original` or `/invoices/{INVOICE_ID}/as/legal`), or
* Use a **download path returned in the invoice JSON** (for example, `download_legal_url` or `attachments[].link`).
`GET /invoices/{id}/as/legal` returns the archived legal document (e.g. the signed and timestamped version) as stored, without regenerating it. Like `/as/original`, this call does not generate a billable transaction — see [Transaction: View\_as](/en/developers/essential-guides/transaction/#view_as).
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* An API key with access to the invoice’s project/account.
* The internal B2Brouter `invoice.id` (you can obtain it from list endpoints).
## Step 1: Get the invoice payload (optional but recommended)
[Section titled “Step 1: Get the invoice payload (optional but recommended)”](#step-1-get-the-invoice-payload-optional-but-recommended)
Call [Get invoice](https://developer.b2brouter.net/reference/get-invoice) to retrieve the invoice JSON and locate the relevant fields:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}.json \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### Issued invoices: legal file via `download_legal_url` (IssuedInvoice / IssuedSelfInvoice)
[Section titled “Issued invoices: legal file via download\_legal\_url (IssuedInvoice / IssuedSelfInvoice)”](#issued-invoices-legal-file-via-download_legal_url-issuedinvoice--issuedselfinvoice)
After an issued invoice is sent, the response can include `download_legal_url`, which points to the legal file delivered to the receiver (for example, the exact XML submitted to a network/platform, or the legal PDF).
`download_legal_url` is a **relative path** (not a full URL), for example:
```json
{
"invoice": {
"download_legal_url": "/attachments/download/{ATTACHMENT_ID}/{FILENAME}"
}
}
```
### Received invoices: original/legal file via `/invoices/{id}/as/original`
[Section titled “Received invoices: original/legal file via /invoices/{id}/as/original”](#received-invoices-originallegal-file-via-invoicesidasoriginal)
For received invoices, the original file is also the legal file. Download it directly with:
```shell
GET /invoices/{INVOICE_ID}/as/original
```
### Issued or received invoices: legal file via `/invoices/{id}/as/legal`
[Section titled “Issued or received invoices: legal file via /invoices/{id}/as/legal”](#issued-or-received-invoices-legal-file-via-invoicesidaslegal)
As a one-step alternative to `download_legal_url`, you can call `/invoices/{INVOICE_ID}/as/legal` directly for either issued or received invoices. It returns the archived legal document (e.g. the signed and timestamped version) as stored, without regenerating it:
```shell
GET /invoices/{INVOICE_ID}/as/legal
```
### Attachments (both issued and received): `attachments[].link`
[Section titled “Attachments (both issued and received): attachments\[\].link”](#attachments-both-issued-and-received-attachmentslink)
The invoice JSON can include an `attachments` array. Each entry contains a `link` you can use to download the stored attachment.
`attachments[].link` is a **relative path** (not a full URL), for example:
```json
{
"invoice": {
"attachments": [
{
"link": "/attachments/download/{ATTACHMENT_ID}/{FILENAME}",
"content_type": "application/pdf"
}
]
}
}
```
For a deeper explanation of attachments and legal PDFs, see: [Manage invoice attachments](https://developer.b2brouter.net/docs/attachments_guide).
## Step 2: Download the file
[Section titled “Step 2: Download the file”](#step-2-download-the-file)
The URL fields returned by the API (`download_legal_url`, `attachments[].link`) are **relative paths**. Prefix them with your environment base URL.
### Download using `download_legal_url` (issued invoices, legal)
[Section titled “Download using download\_legal\_url (issued invoices, legal)”](#download-using-download_legal_url-issued-invoices-legal)
```shell
curl --request GET \
--url https://api-staging.b2brouter.net{download_legal_url} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
### Download using `attachments[].link` (attachments)
[Section titled “Download using attachments\[\].link (attachments)”](#download-using-attachmentslink-attachments)
```shell
curl --request GET \
--url https://api-staging.b2brouter.net{attachment_link} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
### Download using `/invoices/{id}/as/original` (original)
[Section titled “Download using /invoices/{id}/as/original (original)”](#download-using-invoicesidasoriginal-original)
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/as/original \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
### Download using `/invoices/{id}/as/legal` (legal)
[Section titled “Download using /invoices/{id}/as/legal (legal)”](#download-using-invoicesidaslegal-legal)
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/as/legal \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
## Notes
[Section titled “Notes”](#notes)
* For **received invoices**, `/invoices/{INVOICE_ID}/as/original` returns the file received from the issuer (original = legal).
* For **issued invoices**, `/invoices/{INVOICE_ID}/as/original` only works when the invoice has an original file (for example, imported XML). If the invoice was created directly from a JSON payload, there may be no original file to download.
* `/invoices/{INVOICE_ID}/as/legal` works for both issued and received invoices and does not require a prior call to fetch `download_legal_url`.
* Neither `/as/original` nor `/as/legal` generates a billable transaction, since no document regeneration takes place. See [Transaction: View\_as](/en/developers/essential-guides/transaction/#view_as).
## Related endpoints
[Section titled “Related endpoints”](#related-endpoints)
* [Get invoice](https://developer.b2brouter.net/reference/get-invoice)
* [Get an invoice with a specific document type](https://developer.b2brouter.net/reference/get-invoice-with-document-type)
* [Get received invoice (list)](https://developer.b2brouter.net/reference/get-received-invoice)
# Error codes
> Reference table of B2Brouter API error codes with causes and recommended actions.
| `error.code` | HTTP | Typical cause | What to do |
| ------------------------------------ | ---: | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unauthorized` | 401 | Missing/invalid API key, or key from a different environment | Send the key in header `X-B2B-API-Key` and make sure it matches the environment (sandbox, staging, or production). |
| `missing_api_key` | 401 | That API Key has not been provided in the header | Send the key in header `X-B2B-API-Key` and make sure it matches the environment (sandbox, staging, or production). |
| `invalid_api_key` | 401 | The provided API Key is not a valid one | Send a valid key in header `X-B2B-API-Key` and make sure it matches the environment (sandbox, staging, or production). |
| `expired_api_key` | 401 | The provided API Key has been expired | Send a valid and active key in header `X-B2B-API-Key` |
| `no_account_group` | 403 | Valid key but no group/permissions linked to it | Ask to enable/assign permissions for this key (staging accounts require a support request; sandbox permissions are automatic). |
| `resource_missing` | 404 | The resource ID doesn’t exist or is not accessible in your tenant | Recheck the endpoint and the ID you’re sending. |
| `invalid_request_params` | 400 | Malformed JSON, wrong types or unexpected structure | Validate the payload before sending. |
| `invalid_api_version` ¹ | 400 | API Version `{api_version}` is not supported | Validate the payload before sending. |
| `missing_api_version` ¹ | 400 | API Version is not specified neither in Header nor IntegrationGroup | Validate the payload before sending. |
| `api_version_subdomain_mismatch` ¹ | 400 | The subdomain does not match with API Version specified in the header `X-B2B-API-Version` or the associated with the API Key | Change the subdomain to a correct one. Use app.b2brouter or app-staging.b2brouter for legacy version (v2025-01-01). Use api.b2brouter or api-staging.b2brouter for all other versions starting from v2025-10-13. |
| `parameter_blank`, `parameter_empty` | 422 | Required fields missing/blank | Populate required fields and resend. |
| `parameter_inclusion` | 422 | Value not in the permitted catalogue (e.g., an invalid `scheme`) | Check allowed values (see *Schemes Guide* / lists) and correct. |
| `parameter_taken` | 422 | A unique value already exists (e.g., invoice number) | Use a new value or GET first to avoid duplicates. |
| `parameter_cannot_change` | 422 | Attempt to edit an immutable field | Remove the field from the update or create a new resource. |
| `conflict` | 409 | Current resource state rejects the operation | Resolve the state conflict, then retry. |
| *(no code)* | 429 | You exceeded the rate limit | Implement exponential backoff, cache, and prefer webhooks over polling. |
¹ Available from version 2025-10-13
***
## Fast examples
[Section titled “Fast examples”](#fast-examples)
### 1) 401 Unauthorized (wrong/missing API key or wrong environment)
[Section titled “1) 401 Unauthorized (wrong/missing API key or wrong environment)”](#1-401-unauthorized-wrongmissing-api-key-or-wrong-environment)
**Example Request:**
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts?offset=0&limit=25' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
**Sample Response:**
```json
{
"error": {
"code": "unauthorized",
"message": "Invalid API Key provided, or the API key might not have the necessary permissions for this action.",
"type": "invalid_request_error"
}
}
```
Make sure you’re using the right key for your environment (sandbox `test_…`, staging, or production) and sending it in `X-B2B-API-Key`.
***
### 2) 404 Not Found (Resource doesn’t exist or is not accessible)
[Section titled “2) 404 Not Found (Resource doesn’t exist or is not accessible)”](#2-404-not-found-resource-doesnt-exist-or-is-not-accessible)
**Example Request:**
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/directory/es/ES1234567890 \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
**Sample Response:**
```json
{
"error": "Not found"
}
```
***
### 3) 422 Unprocessable Entity (validation errors)
[Section titled “3) 422 Unprocessable Entity (validation errors)”](#3-422-unprocessable-entity-validation-errors)
**Example Request:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/transports \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json'
'
```
**Sample Response:**
```json
{
"error": {
"code": "parameter_taken",
"message": "Transport type code has already been taken and Peppol Endpoint ID has already been taken",
"param": "transport_type_code",
"request_log_url": "https://api-staging.b2brouter.net/api_requests/123456",
"type": "invalid_request_error"
}
}
```
Validation failures return **HTTP 422 (Unprocessable Entity)** with field-level error details, so you can fix the input and resend the request if necessary.
***
## Request tracing
[Section titled “Request tracing”](#request-tracing)
Every logged API response includes the header **`X-B2B-API-Request-Id`** with the unique identifier of the stored request log. You can use this value to:
* Correlate a response with its server-side log entry.
* Reference it in support tickets for faster investigation.
* Include it in your own application logs for end-to-end tracing.
Error responses also include a `request_log_url` field in the JSON body that links directly to the request log.
***
## Error-handling best practices
[Section titled “Error-handling best practices”](#error-handling-best-practices)
* **Authentication:** keep separate keys per environment and always send `X-B2B-API-Key`.
* **Rate limiting:** use **exponential backoff**, cache responses, and prefer **webhooks** to polling when possible.
* **Controlled vocabularies:** when a value must come from a catalogue (e.g., `scheme`), check the **Schemes** API/guide first.
# Invoices attachments
> How to add, read, and interpret invoice attachments via API or XML.
Available from version 2025-10-13
This guide explains how to add, read, and interpret attachments when working with invoices via API or XML.
## Choose a workflow
[Section titled “Choose a workflow”](#choose-a-workflow)
* **Create with JSON**: [create the invoice](https://developer.b2brouter.net/reference/create-invoice) first, then add attachments with [add\_attachment](https://developer.b2brouter.net/reference/add-attachment-invoice) or [add\_attachments](https://developer.b2brouter.net/reference/add-attachments-invoice).
* **Import XML with embedded attachments**: include attachments inside the XML and import the file.
* **Import PDF with embedded XML** (received invoices): if the PDF contains an embedded XML (Factur-X/ZUGFeRD), B2Brouter can extract it and create the received invoice.
## Important principles
[Section titled “Important principles”](#important-principles)
* **XML is the legal, structured source**. Your PDF must be consistent with the XML data. Do not try to change the XML structure to match a custom PDF layout; the XML must follow standardized schemas so any receiver can process it.
* **Attachments are complementary**. A PDF (or any attachment) is not an electronic invoice and does not enable automated processing by itself. Use attachments as supporting documents, not as the primary data source.
## Add attachments to an existing invoice
[Section titled “Add attachments to an existing invoice”](#add-attachments-to-an-existing-invoice)
### Add a single attachment (recommended for PDF preview)
[Section titled “Add a single attachment (recommended for PDF preview)”](#add-a-single-attachment-recommended-for-pdf-preview)
First, you must [create](https://developer.b2brouter.net/reference/create-invoice) or [import](https://developer.b2brouter.net/reference/import-invoice) an invoice.
After creating the invoice, an attachment is added by making a **POST** request to the `/invoices/{id}/add_attachment` endpoint. The request must send the raw file bytes as the body and include the file name using the `filename` query parameter.
If the attached PDF should be used as the visual and legal representation of the invoice, the `use_as_pdf_view=true` parameter must also be provided.
```shell
curl --request POST \
--url 'https://api-staging.b2brouter.net/invoices/12345/add_attachment?filename=invoice.pdf&use_as_pdf_view=true'
```
**Notes**:
* The API expects `Content-Type: application/octet-stream`.
* Raw bytes are recommended; base64 or data-URI payloads also work.
* `use_as_pdf_view=true` marks the PDF as the visual/legal PDF (only one per invoice).
* `use_as_pdf_view=true` only applies to **PDF uploads**. Do not try to mark an XML (UBL/Facturae/CII/etc.) as `use_as_pdf_view=true`: it will **not** become the PDF view.
* The PDF view is available for PDF-based document types such as `pdf.invoice`, `pdf.invoice.signed`, and hybrid PDF/A types like `pdf.a.invoice.with.xml.cii.cross_industry_invoice.facturx.en16931` and `pdf.a.invoice.with.xml.cii.cross_industry_invoice.facturx.extended`.
* If you modify the invoice later, the same PDF is still used unless replaced.
### Add multiple attachments (ZIP)
[Section titled “Add multiple attachments (ZIP)”](#add-multiple-attachments-zip)
Use the ZIP endpoint when you need to upload multiple files in one call.
```shell
zip attachments.zip file1.pdf file2.xml
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/12345/add_attachments
```
If you need to set a PDF as the invoice preview, first upload that PDF using the `add_attachment` endpoint with the `use_as_pdf_view=true` query parameter, and then upload the rest with the `add_attachments` endpoint.
## Add attachments when importing an invoice XML
[Section titled “Add attachments when importing an invoice XML”](#add-attachments-when-importing-an-invoice-xml)
If you already generate XML (UBL, Facturae, CII, etc.), you can embed attachments in the XML and import the file. The attachments will be extracted and stored automatically.
### UBL (Peppol)
[Section titled “UBL (Peppol)”](#ubl-peppol)
```xml
ATT-1
BASE64_DATA
```
### Facturae
[Section titled “Facturae”](#facturae)
```xml
NONE
PDF
BASE64
invoice.pdf
BASE64_DATA
```
### CII / ZUGFeRD
[Section titled “CII / ZUGFeRD”](#cii--zugferd)
```xml
BASE64_DATA
```
**Encoding details**:
* Base64 is the default encoding.
* If the XML includes an explicit encoding code, supported values are `BASE64`, `BER`, and `DER`.
* In Facturae, `AttachmentCompressionAlgorithm` can be `ZIP` or `GZIP`.
* If compression is not defined, content is decoded as plain base64.
## Use your own PDF in ZUGFeRD / Factur-X
[Section titled “Use your own PDF in ZUGFeRD / Factur-X”](#use-your-own-pdf-in-zugferd--factur-x)
To send a hybrid PDF (PDF + embedded XML) using your own layout:
1. Choose a hybrid document type in the contact (for example: `pdf.a.invoice.with.xml.cii.cross_industry_invoice.facturx.en16931`).
2. [Create the invoice](https://developer.b2brouter.net/reference/create-invoice) as usual.
3. [Upload your PDF](#add-a-single-attachment-recommended-for-pdf-preview) with `use_as_pdf_view=true`.
4. [Send the invoice](https://developer.b2brouter.net/reference/send-invoice).
Contact example:
```json
{
"contact": {
"name": "Example GmbH",
"tin_scheme": "9930",
"tin_value": "DE8******3",
"transport_type_code": "email",
"document_type_code": "pdf.a.invoice.with.xml.cii.cross_industry_invoice.facturx.en16931"
}
}
```
To list available document types, use the [document types endpoint](https://developer.b2brouter.net/reference/get-document-types) `GET /document_types`.
## How attachments appear in API responses
[Section titled “How attachments appear in API responses”](#how-attachments-appear-in-api-responses)
* The `attachments` array in [get invoice endpoint](https://developer.b2brouter.net/reference/get-invoice) `GET /invoices/{id}` includes the normal files you uploaded to the invoice (it does not include the PDF marked as `pdf_view`).
* In general, a PDF uploaded with `use_as_pdf_view=true` is stored as the visual/legal PDF (`pdf_view`) and does not appear in the `attachments` array.
* **Exception**: for received invoices, if the original document is a PDF and its document type is not an `attached_document_type`, that original PDF is also exposed in the `attachments` array.
* After sending an issued invoice, `download_legal_url` points to the legal PDF (generated by B2Brouter or your `pdf_view` if provided). You can also fetch it directly with `GET /invoices/{id}/as/legal`, which returns the archived legal document as stored and does not generate a billable transaction — see [Download invoices](/en/developers/essential-guides/download-invoice/) and [Transaction: View\_as](/en/developers/essential-guides/transaction/#view_as).
## Read attachments from received invoices
[Section titled “Read attachments from received invoices”](#read-attachments-from-received-invoices)
When you retrieve a received invoice, the response includes an `attachments` array with download links:
```json
{
"attachments": [
{
"link": "/attachments/download/253526/2025-02-10_1_original.pdf",
"content_type": "application/pdf"
}
]
}
```
To download the file use the endpoint `GET /attachments/download/{id}/{filename}`
Include your API key header when downloading. The `id` comes from the link.
## Delete attachments
[Section titled “Delete attachments”](#delete-attachments)
Deletion is possible from the web app, but there is no public API endpoint. If an attachment was imported incorrectly and you need an automated flow, the recommended approach is to delete the invoice and submit it again without the wrong attachment.
## Limits and transport constraints
[Section titled “Limits and transport constraints”](#limits-and-transport-constraints)
* B2Brouter allows up to 50 MB per file. If you send by email, the total attachment size is limited to 40 MB. Some networks may enforce lower limits (10–20 MB), depending on the channel.
* Expected formats: PDF and XML, plus common office files (for example CSV). Executable or script formats are blocked (for example `.exe`, `.js`, `.bat`, `.sh`, `.jar`, `.ps1`, `.html`, `.gif`).
* Some transports are configured as “without embedded attachments” (detached), so attachments are not included in the XML sent to the receiver.
* Some recipient systems ignore embedded attachments even if they are present.
* If the invoice is not successfully sent, the recipient will not see the PDF regardless of attachments.
* The endpoint `add_attachment` requires `Content-Type: application/octet-stream` and a `filename` query parameter.
# Payment Methods
> Reference for B2Brouter payment method codes and their equivalents in UBL, Facturae, FatturaPA, and CII.
This guide describes the internal payment method codes in B2Brouter and their corresponding encoding in UBL, Facturae, and FatturaPA.
**Equivalencies:**
* **B2Brouter internal field:** `payment_method`
* **Facturae field:** `PaymentMeans` (element at `/facturae:Facturae/Invoices/Invoice/PaymentDetails/Installment/PaymentMeans`)
* **UBL field:** `cbc:PaymentMeansCode` (element at `/Invoice/cac:PaymentMeans/cbc:PaymentMeansCode`)
* **FatturaPA field:** `ModalitaPagamento` (element at `/p:FatturaElettronica/FatturaElettronicaBody/DatiPagamento/DettaglioPagamento/ModalitaPagamento`)
* **CII field:** `TypeCode` (element at `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:TypeCode`)
* **Business Terms ID:** `BT-81`
## 1. Payment Codes Table
[Section titled “1. Payment Codes Table”](#1-payment-codes-table)
| Method | Internal Reference | UBL | Facturae | FatturaPA | Description |
| ------------------ | ------------------ | --- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **CASH** | **1** | 10 | 01 | MP01 | Payment by currency (including bills and coins) in circulation, including checking account deposits. |
| **DEBIT** | **2** | 49 | 02 | MP19 | Direct debit (The amount is to be, or has been, directly debited **from** the customer’s bank account). |
| **HOLD** | **3** | 8 | 03 | MP12 | Indicates that the bank should hold the payment for collection by the beneficiary or other instructions. |
| **TRANSFER** | **4** | 30 | 04 | MP05 | Bank transfer (The amount is to be, or has been, directly **credited to** the supplier’s bank account). |
| **AWARDING** | **7** | ZZZ | 07 | | Payment by assignment or award |
| **PAGARE N O** | **10** | 60 | 10 | MP06 | Non transferable promissory note. Payment by an unconditional promise in writing made by one person to another, signed by the maker, engaging to pay on demand or at a fixed or determinable future time a sum certain in money, to order or to bearer. |
| **CHEQUE** | **11** | 20 | 11 | MP02 | Payment by a pre-printed form on which instructions are given to an account holder (a bank or building society) to pay a stated sum to a named recipient. |
| **REPOSITION** | **12** | ZZZ | 12 | MP22 | Open account reimbursement. |
| **SPECIAL/OTHER** | **13** | ZZZ | 13 | MP22 | Defines any other payment method; use `payment_method_text` for custom descriptions. |
| **COMPENSATION** | **14** | 97 | 14 | MP22 | Amounts which two partners owe to each other to be compensated in order to avoid useless payments. |
| **PLUSGIRO** | **15** | 50 | 15 | MP18 | A standard Swedish payment method using the postal giro system. |
| **CHEQUE B** | **17** | 23 | 17 | MP03 | Payment by a pre-printed form, which has been completed by a financial institution, on which instructions are given to an account holder (a bank or building society) to pay a stated sum to a named recipient. |
| **BANK CARD** | **19** | 48 | 19 | MP08 | Payment by means of a card issued by a bank or other financial institution. |
| **DEBIT TRANSFER** | **31** | 31 | 04 | MP13 | Payment by debit movement of funds from one account to another. |
| **CREDIT CARD** | **54** | 54 | 19 | MP08 | Payment made by means of credit card |
| **BANKGIRO** | **56** | 56 | 04 | MP03 | Payment method using the bank giro network. |
| **TRANSFER SEPA** | **58** | 58 | 04 | MP05 | SEPA credit transfer (Funds are **credited to** the supplier’s bank account). |
| **DEBIT SEPA** | **59** | 59 | 02 | MP19 | SEPA direct debit (Funds are **debited from** the customer’s bank account; mandate may be required). |
| **GIRO** | **101** | ZZZ | 13 | | Giro |
| **PAY NOW** | **102** | ZZZ | 13 | | Immediate payment (Pay Now) |
## 1.1 Additional required fields (API)
[Section titled “1.1 Additional required fields (API)”](#11-additional-required-fields-api)
Depending on the `payment_method`, you may need to provide extra fields so the invoice can be generated/exported correctly.\
This section focuses on universal bank/payment data requirements; some e-invoice formats may still restrict which payment codes are accepted.
### Universal requirements
[Section titled “Universal requirements”](#universal-requirements)
* Customer bank account (payer): **DEBIT (2)**, **DEBIT SEPA (59)**. Required in specific formats/flows, not universally. For example, `XRechnung \common_xrechnung_v23` requires invoice.contact\_iban, `XRechnung v3 also requires `mandate\_reference\_identifier, and `Facturae` requires the customer bank account (`contact.bank_account_number` or a valid `invoice.contact_iban`). This does not apply to `IssuedSimplifiedInvoice`.\`
* Payment terms: **DEBIT (2)** . Provide `terms` (it cannot be `0`/blank).
* Payment due date (most exports):\
If you set a `payment_method` and use `terms = "custom"`, you must provide `due_date` (except for some transports like PEPPOL and SDI). If you use a non-custom `terms`, the system derives `due_date` automatically.
* Company bank account (payee): **TRANSFER (4)**, **TRANSFER SEPA (58)**, **DEBIT TRANSFER (31)**, **PLUSGIRO (15)**, **BANKGIRO (56)**, **AWARDING (7)**.\
Provide `bank_account_id` (or include the `bank_account` object when creating/updating the invoice).
* Custom description: **SPECIAL/OTHER (13)**.\
Provide `payment_method_text` (it is only kept when `payment_method = 13`).
* All other payment methods do not require additional bank/payment fields.
### Special cases (invoice type)
[Section titled “Special cases (invoice type)”](#special-cases-invoice-type)
* **IssuedSelfInvoice**:
* **DEBIT (2)** / **DEBIT SEPA (59)** require `bank_account_id` (company bank account).
* Transfer-like methods (**TRANSFER (4)**, **TRANSFER SEPA (58)**, **DEBIT TRANSFER (31)**, **PLUSGIRO (15)**, **BANKGIRO (56)**, **AWARDING (7)**) require `invoice.contact_iban` (some PDF-only flows may not require it, but most e-invoice exports do).
* **IssuedSimplifiedInvoice**: **DEBIT (2)** / **DEBIT SEPA (59)** do not require `invoice.contact_iban`.
### Special cases (export format)
[Section titled “Special cases (export format)”](#special-cases-export-format)
* **Facturae**:
* If you set a `payment_method` and the invoice is not a credit note, you must provide either a valid `terms` (non-custom) or `due_date` (when using `terms = "custom"`).
* If the invoice is **DEBIT**/**DEBIT SEPA**, the customer bank account must be available (the contact’s `bank_account_number` or a valid `invoice.contact_iban`).
* If the invoice is **TRANSFER** (including transfer-like methods), the company bank account must be available (via `bank_account_id` / `bank_account`) and contain a valid account number/IBAN.
* **Svefaktura**: debit payments (**DEBIT**/**DEBIT SEPA**) are not supported.
* **FatturaPA**: **COMPENSATION (14)** is not allowed.
* **XRechnung (UBL/CII)**:
* If the invoice is **DEBIT**/**DEBIT SEPA**, you must provide `bank_assigned_creditor_reference` and `invoice.contact_iban`.
* For v3 profiles, `mandate_reference_identifier` is also required.
## 2. Using the Endpoints
[Section titled “2. Using the Endpoints”](#2-using-the-endpoints)
Below we show how to use the main endpoints to manage payments and bank accounts associated with contacts and invoices.
### 2.1. Create Bank Accounts
[Section titled “2.1. Create Bank Accounts”](#21-create-bank-accounts)
When creating a bank account, you must specify the account `type` to indicate which fields to use:
* **`number`**: use the `number` field only.
* `number`: Bank account number.
* **`iban`**: use `iban` and `bic`.
* `iban`: International Bank Account Number.
* `bic`: SWIFT code of the bank.
**Request Example:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/bank_accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json'
'
```
`sufix` is the suffix used for the creditor identifier in SEPA files.
**Sample Response:**
```json
{
"bank_account": {
"type": "number",
"id": 3296,
"name": "Ejemplo S.L.",
"number": "ES9121000418450200051332",
"sufix": 123,
"country": "es",
"created_at": "2025-07-14T11:37:02.000Z",
"updated_at": "2025-07-14T11:37:02.000Z"
}
}
```
Keep `bank_account.id` to reference this account in future requests.
[Bank Account API Reference](https://developer.b2brouter.net/reference/create-bank-account)
### 2.2. Create Contact
[Section titled “2.2. Create Contact”](#22-create-contact)
Configuring the payment details for invoices sent to a contact is useful to avoid managing this information each time you need to send a document.
**Request Example:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json'
'
```
[Contact API Reference](https://developer.b2brouter.net/reference/create-contact)
### 2.3. Create Invoice
[Section titled “2.3. Create Invoice”](#23-create-invoice)
You don’t need to re-specify any payment fields that were already set on the contact unless you need to override them. To minimize API calls, include all required payment details—such as `payment_method`, `bank_account_id`, `payment_terms`, etc.—directly in the Create Invoice request instead of creating Contacts or Bank Accounts beforehand.
**Request Example:**
```shell
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'
'
```
And if you haven’t provided `bank_account_id`, you can always include the `bank_account` object:
```shell
{"invoice": {
...
"bank_account": {
"type": "number",
"number": "ES9121000418450200051332"
},
...
}}
'
```
# Schemes
> Guide to company identification schemes (TIN, CIN, PIN, EAS, ICD) used in the B2Brouter API.
This guide aims to provide a clear and comprehensive overview of the various identification schemes (`scheme`) that govern company codes via the B2Brouter API. It explains the concepts of TIN, CIN, PIN, and taxcode, as well as the EAS and ICD schemes and the `country` field, so that any integrator knows exactly what information to supply in each scenario.
Every official company identification number has a corresponding scheme to indicate the type of TIN, CIN, or PIN it represents.
You can review each identifier using the GET `/schemes` endpoint, which returns the scheme number, whether it is a fiscal identifier, and whether it is included in the valid identifiers lists for EAS and ICD.
**Sample response from `/schemes` Endpoint:**
```json
{
"code": "9914",
"name": "UID",
"country": "at",
"eas": true,
"icd": false,
"taxcode": true
}
```
* **code**: Scheme identification number (in this case `9914`).
* **name**: Official name of the identifier (in this case `UID`).
* **country**: Country corresponding to the identifier (`at` for Austria).
* **eas**: Indicates if it belongs to the EAS list (in this case `true`).
* **icd**: Indicates if it belongs to the ICD list (in this case `false`).
* **taxcode**: Indicates whether the identifier is a TIN (`true`) or CIN (`false`). Here, `true` means it is a TIN (Tax Identification Number).
## Basic Definitions
[Section titled “Basic Definitions”](#basic-definitions)
### **TIN (Tax Identifier Number)**
[Section titled “TIN (Tax Identifier Number)”](#tin-tax-identifier-number)
The primary fiscal identifier for a company, valid for both domestic and international invoicing. To verify whether an identifier is the Tax Identifier Number for a given country, use the B2Brouter schemes endpoint.
For Europe, the [VIES](https://ec.europa.eu/taxation_customs/vies/#/vat-validation) portal validates identifiers of EU member states. All European TINs begin with the country’s ISO code.
If `"taxcode": true`, the identifier is considered a TIN and can be used for fiscal invoicing.
**Examples:**
1. German VAT ID:
```json
"tin_scheme": 9930,
"tin_value": "DE123456789"
```
2. Spanish NIF:
```json
"tin_scheme": 9920,
"tin_value": "ESB12345678"
```
3. French VAT number:
```json
"tin_scheme": 9957,
"tin_value": "FR12345678901"
```
4. Italian Partita IVA:
```json
"tin_scheme": 9906,
"tin_value": "IT12345678901"
```
> **Note:** If the ISO 3166-1 alpha-2 country code is not already included in the `tin_value`, it will be automatically prepended when saving a TIN.
### **CIN (Company Identifier Number)**
[Section titled “CIN (Company Identifier Number)”](#cin-company-identifier-number)
Any valid company identifier other than the Tax Identifier Number, such as identifiers for subdivisions, public administrations, bank accounts, etc. To create an account or contact without specifying a TIN, set `"taxcode": false`. All schemes with `taxcode: false` are treated as CINs. A company can have multiple CINs.
**Examples:**
1. French SIRET:
```json
"cin_scheme": 0009,
"cin_value": "1234567890"
```
2. Global Location Number (GLN):
```json
"cin_scheme": 0088,
"cin_value": "1234567890"
```
3. Swedish Organisationsnummer:
```json
"cin_scheme": 0007,
"cin_value": "1234567890"
```
> **Note:** In Sweden, to invoice at the fiscal level you must enable the `registered_for_empl_tax` boolean once approved for F-tax.
### **PIN (Peppol Identifier Number)**
[Section titled “PIN (Peppol Identifier Number)”](#pin-peppol-identifier-number)
Used for the business terms “Seller electronic address ID” (BT‑34) and “Buyer electronic address ID” (BT‑49), where `scheme` indicates the identifying scheme (e.g., KVK with code 0088).
The company identifier used as the Peppol mailbox address. A PIN can be either a TIN or a CIN, provided it appears in the EAS list. You may retrieve PINs via the schemes endpoint. A company can have multiple PINs and connect to Peppol through multiple billing software.
**Implementation:**
* **Peppol Transport:** Add your PIN in the Transports settings by enabling your Peppol connection. [API Reference - POST create-transport](https://developer.b2brouter.net/reference/create-transport)
* **Contacts and Invoices:** Specify a PIN when creating the contact or directly on the invoice. [API Reference - POST create-contact](https://developer.b2brouter.net/reference/create-contact) **Example:**
```json
"pin_scheme": 0208,
"pin_value": "BCE/KBO/ZDU"
```
## Supported Schemes
[Section titled “Supported Schemes”](#supported-schemes)
1. **EAS (EN 16931 – Electronic Address Scheme)** Official list of electronic address identifier schemes recognized by the European e‑invoicing standard (EN 16931). Maintained by Digital Europe (successor to CEF since January 2022) and updated twice yearly to add new schemes or ISO 6523 ICD‑registered codes relevant to electronic addressing. This list enables new Peppol identifiers.
2. **ICD (ISO 6523 – International Code Designator)** Global registry of entity identifier schemes maintained by ISO. Each assigning authority (e.g., government registries or coding services) has a unique numeric code (ICD). This serves as the basis for unique organization identification worldwide, including Peppol. Many EAS schemes come from this catalog, since only ISO 6523 entries can be promoted to EAS.
**Examples:**
* DUNS Number (D\&B – global):
```json
"cin_scheme": "0060",
"cin_value": "123456789"
```
* French SIRENE:
```json
"cin_scheme": "0002",
"cin_value": "12345678900000"
```
3. **B2Brouter Internal Schemes** For countries without official ISO 6523 ICD or EAS entries, B2Brouter defines internal schemes. These numeric codes start with **8XXX** and allow fiscal or commercial identifiers not covered by any global registry. Note that these codes may be temporary until an official scheme is approved.
4. **Universal Scheme (0001)** A generic fallback scheme for cases where a company’s fiscal ID does not belong to any recognized official scheme (EAS, ISO 6523 ICD, or internal). Can be used as `tin_scheme` or `cin_scheme` for national or commercial identifiers without formal assignment.
> **Warning:** Avoid using scheme `0001` whenever possible. It may cause validation or legal issues if authorities require officially regulated coding. Always verify if an official scheme exists first.
5. **Underspecified scheme (9999)** Use this scheme when you don’t know which scheme corresponds to an identifier. The backend will attempt to resolve it automatically to the most logical scheme (TIN or CIN) based on the value and country provided.
> **Note:** `0001` and `9999` solve different problems. `0001` means the identifier has *no scheme* (it’s unspecified and stays that way). `9999` means the scheme is unknown to you, but you’re asking B2Brouter to *resolve* it to the correct TIN or CIN scheme.
If the value is `null` or the code cannot be matched to any known scheme, the API returns a `422` error.
**Example:**
Request:
```json
{
"name": "Voorbeeld B.V.",
"cin_scheme": 9999,
"cin_value": "12345678",
"country": "nl"
}
```
Response:
```json
{
"cin_scheme": 0106,
"cin_value": "12345678",
"country": "nl"
}
```
## API Usage Examples
[Section titled “API Usage Examples”](#api-usage-examples)
1. **Create a Peppol Transport** `POST /accounts/{ACCOUNT_ID}/transports`
```json
{
"transport": {
"code": "peppol",
"enabled": true,
"reception": true,
"pin_scheme": 9934,
"pin_value": "123456789"
}
}
```
2. **Create an Account with CIN** `POST /accounts`
```json
{
"name": "Voorbeeld B.V.",
"cin_scheme": "0106",
"cin_value": "12345678",
"country": "nl",
"address": "Kerkstraat 12",
"city": "Amsterdam",
"postalcode": "1017 GM",
"email": "john.doe@example.com"
}
```
3. **Lookup Directory by Scheme** `GET /directory/{scheme}/{value}`
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/directory/9920/ESP0801900B \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
## Resources
[Section titled “Resources”](#resources)
1. [API Reference for Lookup “Schemes”](https://developer.b2brouter.net/reference/get-schemes)
2. [EAS Codes List (EN 16931)](https://peppol.eu/specs/eas)
3. [ICD Codes List (ISO 6523)](https://docs.peppol.eu/poacc/billing/3.0/codelist/ICD/)
4. [VIES Portal for EU VAT ID Validation](https://ec.europa.eu/taxation_customs/vies/#/vat-validation)
# Tax report attributes
> How to correctly fill the taxes_attributes array when creating invoices via the API.
## Taxes Attributes (`taxes_attributes`)
[Section titled “Taxes Attributes (taxes\_attributes)”](#taxes-attributes-taxes_attributes)
This guide explains how to correctly fill the `taxes_attributes` array when creating invoices via the API so integrators know which tax names and categories are expected per country.
> **Important**
>
> * The percentages shown are B2Brouter defaults to ease integration; **tax rates can change**. Always validate against the current law of each country.
> * Tax names must match the expected values in this guide. Do **not** invent new names (e.g., `"Exento"`, `"Umsatzsteuer"`, `"Sales tax"`).
> * Many formats reuse `comment` as the exemption/“not subject” reason. Fill it whenever you set 0%/exempt/reverse-charge unless your account already has the correct default reason.
Use this guide together with:
* [Invoice calculation](https://developer.b2brouter.net/docs/invoice_calculation)
* [JSON invoice mapping](https://developer.b2brouter.net/docs/mapping_json_invoice_to_cii)
## Things to keep in mind
[Section titled “Things to keep in mind”](#things-to-keep-in-mind)
* Use only these fields inside each `taxes_attributes` object: `name` (string), `category` (string, enum), `percent` (float), `comment` (string or null). No other keys such as `amount`, `base`, `type`, etc.
* Percentages are defaults we expect; laws change. If your business case uses a different rate, send that rate but keep the correct `name` and `category`.
* For withholdings/retentions, use **negative** `percent` and the appropriate tax name (`IRPF`, `IRNR`, `IRPEF`, etc.).
* `comment` is used as the exemption/“not subject” reason in many formats (Facturae/Verifactu, Peppol categories, etc.). Provide a reason whenever `percent` is 0 and the operation is exempt/not subject/reverse charge (example: `comment: "E2"`).
* Account settings impact calculation: `apply_taxes_per_line` (boolean) controls whether taxes are applied per line or on the sum; `rounding_method` controls rounding.
## Structure of `taxes_attributes`
[Section titled “Structure of taxes\_attributes”](#structure-of-taxes_attributes)
Each invoice line (`invoice_lines_attributes`) can contain an array of taxes:
```json
"taxes_attributes": [
{
"name": "VAT",
"category": "S",
"percent": 21.0,
"comment": "Standard VAT"
}
]
```
For each tax object:
* `name` (required): Tax name (must match one of the expected values for the company’s country, see [Country reference](#country-reference-valid-tax-names-and-default-rates)).
* `category` (recommended): Tax category code (see [Tax categories](#tax-categories)).
* `percent` (required): Tax percentage. Use negative values for withholdings or retentions.
* `comment` (optional but recommended for 0%/exemption/reverse charge): Free text reason used by several formats and tax authorities.
Multiple taxes can be applied to the same line by adding multiple objects to `taxes_attributes`.
Only these fields are valid. Do **not** include `amount`, `base`, or other custom fields; they are calculated internally to avoid inconsistencies.
| field | type | required | description |
| ---------- | -------------- | ----------- | -------------------------------------------------------------- |
| `name` | string | yes | Tax name (must match the country defaults) |
| `category` | string, enum | recommended | Tax category code (see list below) |
| `percent` | float | yes | Tax percentage (negative for withholdings) |
| `comment` | string or null | optional | Reason/explanation, required for many exempt/not-subject cases |
## Account-level tax settings that affect calculations
[Section titled “Account-level tax settings that affect calculations”](#account-level-tax-settings-that-affect-calculations)
* `apply_taxes_per_line` (boolean): When `true`, taxes are applied per line and then summed; when `false`, bases are summed and tax is applied to the total.
* `rounding_method`: Rounding strategy (`half_up` by default). See [Invoice calculation](https://developer.b2brouter.net/docs/invoice_calculation#rounding-methods).
## Tax categories
[Section titled “Tax categories”](#tax-categories)
B2Brouter uses tax categories aligned with the PEPPOL/UNCL5305 codelist and, for some countries, additional codes required by the local tax authority.
Common category codes:
* `S` – Standard rate.
* `H` – High rate.
* `AA` – Reduced rate.
* `AAA` – Super reduced rate.
* `Z` – Zero rated supply (0% but subject to VAT).
* `E` – Exempt (0% and exempt from VAT).
* `AE` – Reverse charge / “Inversión del sujeto pasivo”.
* `G` – Free export item, VAT not charged.
* `K` – Intra-community supply of goods/services (VAT exempt within EEA).
* `NS` – Not subject.
* `O` – Outside scope of tax / other operations.
Only these category values are accepted; other codes will be rejected.
Singapore uses additional GST category codes (see [Singapore](#singapore-sg)), and Italy uses FatturaPA `N..` “natura” codes (see [Italy](#italy-it)).
## General recommendations
[Section titled “General recommendations”](#general-recommendations)
* Use the **country’s default tax names** from the tables below (they match B2Brouter’s default configuration used when a company is created). Keep names/codes even if your rate differs.
* For **standard VAT**, use the main VAT name and `category: "S"` with the appropriate `percent`.
* For **reduced rates**, use `category: "AA"` or `category: "AAA"` according to the defaults shown.
* For **zero-rated or exempt operations**:
* Use `percent: 0`.
* Choose `category` based on the legal situation:
* `Z`: taxable but 0%.
* `E`: exempt.
* `AE`: reverse charge.
* Local codes for specific regimes (Italy `N..`, Singapore GST categories, etc.).
* For **withholdings/retentions**, use **negative** percentages (e.g., `-19` for Spanish IRPF) and the tax name for the withholding (e.g., `IRPF`, `IRNR`, `IRPEF`).
* When integrating with **tax reporting** (Verifactu, TicketBAI, LHDN, etc.), make sure your `taxes_attributes` are compatible with the corresponding guide.
## Country reference – valid tax names and default rates
[Section titled “Country reference – valid tax names and default rates”](#country-reference--valid-tax-names-and-default-rates)
The following tables list the **expected tax names** per country with the **default rates** and categories we preload. Use them as reference for naming and categories; adjust `percent` if legislation or your scenario requires it.
Percentages may change over time; always verify with current local legislation. Rates and categories are aligned by position in each row (first rate → first category, etc.).
### Andorra (`ad`)
[Section titled “Andorra (ad)”](#andorra-ad)
| name | percent | category | notes |
| ---- | --------------- | ------------- | ---------------------------------------------------- |
| IGI | 4.5, 2.5, 1, 0 | S, AA, AAA, Z | VAT (standard/reduced/0%). |
| IRNR | 10, 5, 1.5, -20 | S, AA, AAA, S | Non‑resident income tax (withholding when negative). |
### Austria (`at`)
[Section titled “Austria (at)”](#austria-at)
| name | percent | category | notes |
| ---- | ------------- | ------------ | ----- |
| MwSt | 20, 10, 13, 0 | S, AA, AA, Z | VAT. |
### Australia (`au`)
[Section titled “Australia (au)”](#australia-au)
| name | percent | category | notes |
| ---- | ------- | -------- | ----------------------- |
| GST | 10, 0 | S, E | Goods and Services Tax. |
### Belgium (`be`)
[Section titled “Belgium (be)”](#belgium-be)
| name | percent | category | notes |
| ---- | ------------ | ------------------- | ------------------------------------------------ |
| TVA | 21, 6, 12, 0 | S, AA, AA, Z, E, AE | VAT (standard, reduced, exempt, reverse charge). |
### Bulgaria (`bg`)
[Section titled “Bulgaria (bg)”](#bulgaria-bg)
| name | percent | category | notes |
| ---- | -------- | -------- | ----- |
| ДДС | 20, 9, 0 | S, AA, Z | VAT. |
### Switzerland (`ch`)
[Section titled “Switzerland (ch)”](#switzerland-ch)
| name | percent | category | notes |
| ---- | ---------------- | ------------ | -------------------------- |
| VAT | 8.1, 2.6, 3.8, 0 | S, AA, AA, E | VAT; 0% typically exports. |
### Cyprus (`cy`)
[Section titled “Cyprus (cy)”](#cyprus-cy)
| name | percent | category |
| ---- | ----------- | ------------ |
| ΦΠΑ | 19, 5, 9, 0 | S, AA, AA, Z |
### Czech Republic (`cz`)
[Section titled “Czech Republic (cz)”](#czech-republic-cz)
| name | percent | category |
| ---- | --------- | -------- |
| DPH | 21, 12, 0 | S, AA, Z |
### Germany (`de`)
[Section titled “Germany (de)”](#germany-de)
| name | percent | category | notes |
| ---- | -------- | -------- | ----- |
| USt | 19, 7, 0 | S, AA, Z | VAT. |
### Denmark (`dk`)
[Section titled “Denmark (dk)”](#denmark-dk)
| name | percent | category |
| ---- | ------- | -------- |
| VAT | 25, 0 | S, Z, E |
### Estonia (`ee`)
[Section titled “Estonia (ee)”](#estonia-ee)
| name | percent | category |
| ---- | -------- | -------- |
| km | 22, 9, 0 | S, AA, Z |
### Spain (`es`)
[Section titled “Spain (es)”](#spain-es)
Spain has several VAT regimes depending on the **postal code** of the company:
* Mainland/Balearic Islands: IVA.
* Canary Islands: IGIC.
* Ceuta/Melilla: IPSI.
`comment` is used for the exemption/not-subject reason in Facturae/Verifactu. If you send `percent: 0` for an exempt or not-subject operation, add the reason in `comment` (example: `comment: "E2"`). If your account already sets the default reason, you can omit it.
#### Mainland/Balearic Islands (default)
[Section titled “Mainland/Balearic Islands (default)”](#mainlandbalearic-islands-default)
| name | percent | category | notes |
| ---- | ----------------------- | ------------------------------- | -------------------------------- |
| IVA | 21, 10, 7.5, 5, 4, 2, 0 | S, AA, AA, AA, AAA, AAA, Z/E/AE | VAT. |
| IRPF | -19, -15, -7 | S | Personal income tax withholding. |
#### Canary Islands (`IGIC`) / Ceuta & Melilla (`IPSI`)
[Section titled “Canary Islands (IGIC) / Ceuta & Melilla (IPSI)”](#canary-islands-igic--ceuta--melilla-ipsi)
Examples (postal‑code dependent):
| name | percent | category | notes |
| ---- | --------------------- | ------------------ | ------------------------------------- |
| IGIC | 15, 9.5, 7, 3, 1.5, 0 | H, H, S, AA, AA, Z | Canary Islands VAT‑equivalent. |
| IPSI | 10, 8, 4, 2, 1, 0.5 | H, H, S, AA, AA, S | Local indirect tax for Ceuta/Melilla. |
| IRPF | -19, -15, -7 | S | Withholding. |
> **Notes (Spain)**
>
> * Use `IVA`/`IGIC`/`IPSI` with `percent: 0` and the correct `category` (`E`, `Z`, or `AE`) plus a `comment` reason for exempt/not subject (e.g., `E2`).
> * Use negative `percent` for IRPF withholdings.
See also: [Verifactu](https://developer.b2brouter.net/docs/verifactu) and [TicketBAI](https://developer.b2brouter.net/docs/submit_ticketbai).
### Finland (`fi`)
[Section titled “Finland (fi)”](#finland-fi)
| name | percent | category |
| ---- | --------------- | -------------- |
| ALV | 25.5, 10, 14, 0 | S, AA, AA, Z/E |
### France (`fr`)
[Section titled “France (fr)”](#france-fr)
| name | percent | category |
| ---- | ------------------- | -------------------- |
| TVA | 20, 5.5, 10, 2.1, 0 | S, AA, AA, AAA, Z/AE |
### United Kingdom (`gb`)
[Section titled “United Kingdom (gb)”](#united-kingdom-gb)
| name | percent | category |
| ---- | -------- | -------- |
| VAT | 20, 5, 0 | S, AA, Z |
### Greece (`gr`)
[Section titled “Greece (gr)”](#greece-gr)
| name | percent | category |
| ---- | ------------ | ------------ |
| ΦΠΑ | 24, 6, 13, 0 | S, AA, AA, Z |
### Croatia (`hr`)
[Section titled “Croatia (hr)”](#croatia-hr)
| name | percent | category |
| ---- | ------------ | ------------ |
| PDV | 25, 5, 13, 0 | S, AA, AA, Z |
### Hungary (`hu`)
[Section titled “Hungary (hu)”](#hungary-hu)
| name | percent | category |
| ---- | ------------ | ------------ |
| ÁFA | 27, 5, 18, 0 | S, AA, AA, Z |
### Ireland (`ie`)
[Section titled “Ireland (ie)”](#ireland-ie)
| name | percent | category |
| ---- | ------------------- | ---------------- |
| VAT | 23, 9, 13.5, 4.8, 0 | S, AA, AA, AA, Z |
### Iceland (`is`)
[Section titled “Iceland (is)”](#iceland-is)
| name | percent | category |
| ---- | --------- | ---------- |
| VSK | 24, 11, 0 | S, AA, Z/E |
### Italy (`it`)
[Section titled “Italy (it)”](#italy-it)
| name | percent | category | notes |
| ----- | -------------------- | ---------------------------------- | ------------------------------------------------ |
| IVA | 22, 10, 5, 4, 0 | S, AA, AA, AAA, `N..` / E / Z / AE | VAT. |
| IRPEF | negative (e.g., -20) | S | Withholding tax (when applicable via FatturaPA). |
For **FatturaPA**, non‑taxable operations use `category` codes like `N1`, `N2.1`, `N2.2`, `N3.1`, `N3.2`, `N3.3`, `N3.4`, `N3.5`, `N3.6`, `N4`, `N5`, `N6.1`… (see your FatturaPA configuration). They must be sent as 0% VAT with `name: "IVA"` and `category: "N.."`.
> **Recommendation (Italy)**
>
> * For standard operations: `name: "IVA"`, `category: "S"`, `percent: 22`.
> * For non‑taxable operations: `name: "IVA"`, `percent: 0`, and the appropriate `N..` code in `category`.
See also: [Send an invoice through SDI](https://developer.b2brouter.net/docs/send_an_invoice_through_SDI).
### Japan (`jp`)
[Section titled “Japan (jp)”](#japan-jp)
| name | percent | category | notes |
| ---- | -------- | ------------ | ------------------------- |
| VAT | 10, 8, 0 | S, AA, E/G/O | Japanese consumption tax. |
### Lithuania (`lt`)
[Section titled “Lithuania (lt)”](#lithuania-lt)
| name | percent | category |
| ---- | ----------- | ------------ |
| PVM | 21, 5, 9, 0 | S, AA, AA, Z |
### Luxembourg (`lu`)
[Section titled “Luxembourg (lu)”](#luxembourg-lu)
| name | percent | category |
| ---- | --------------- | ---------------- |
| TVA | 17, 3, 8, 14, 0 | S, AA, AA, AA, Z |
### Latvia (`lv`)
[Section titled “Latvia (lv)”](#latvia-lv)
| name | percent | category |
| ---- | ------------ | ------------ |
| PVM | 21, 5, 12, 0 | S, AA, AA, Z |
### Monaco (`mc`)
[Section titled “Monaco (mc)”](#monaco-mc)
| name | percent | category |
| ---- | ------------------- | -------------------- |
| TVA | 20, 5.5, 10, 2.1, 0 | S, AA, AA, AAA, Z/AE |
### Malta (`mt`)
[Section titled “Malta (mt)”](#malta-mt)
| name | percent | category |
| ---- | ----------- | ------------ |
| VAT | 18, 5, 7, 0 | S, AA, AA, Z |
### Malaysia (`my`)
[Section titled “Malaysia (my)”](#malaysia-my)
| name | percent | category | notes |
| ---- | -------------- | --------------- | ---------------------- |
| SST | 10, 8, 6, 5, 0 | T, T, T, T, E/O | Sales and Service Tax. |
See also: [Submit tax report to LHDN](https://developer.b2brouter.net/docs/submit_tax_report_to_lhdn).
### Netherlands (`nl`)
[Section titled “Netherlands (nl)”](#netherlands-nl)
| name | percent | category |
| ---- | -------- | ----------- |
| BTW | 21, 9, 0 | S, AA, Z/AE |
### Norway (`no`)
[Section titled “Norway (no)”](#norway-no)
| name | percent | category |
| ---- | ------------- | -------------- |
| MVA | 25, 15, 12, 0 | S, AA, AA, Z/E |
### New Zealand (`nz`)
[Section titled “New Zealand (nz)”](#new-zealand-nz)
| name | percent | category |
| ---- | ------- | -------- |
| GST | 15, 0 | S, Z/E |
### Poland (`pl`)
[Section titled “Poland (pl)”](#poland-pl)
| name | percent | category |
| ---- | ----------- | ------------ |
| PTU | 23, 5, 8, 0 | S, AA, AA, Z |
### Portugal (`pt`)
[Section titled “Portugal (pt)”](#portugal-pt)
| name | percent | category |
| ---- | ------------ | ------------ |
| IVA | 23, 6, 13, 0 | S, AA, AA, Z |
### Romania (`ro`)
[Section titled “Romania (ro)”](#romania-ro)
| name | percent | category |
| ---- | ----------- | ------------ |
| TVA | 19, 9, 5, 0 | S, AA, AA, Z |
### Sweden (`se`)
[Section titled “Sweden (se)”](#sweden-se)
| name | percent | category |
| ---- | ------------ | -------------- |
| Moms | 25, 6, 12, 0 | S, AA, AA, Z/E |
### Saudi Arabia (`sa`)
[Section titled “Saudi Arabia (sa)”](#saudi-arabia-sa)
| name | percent | category |
| -------------------- | ------- | -------- |
| ضريبة القيمة المضافة | 15, 0 | S, Z/E/O |
### Singapore (`sg`)
[Section titled “Singapore (sg)”](#singapore-sg)
Singapore uses specific GST categories mandated by IRAS. The tax name is always `GST`; the **category** encodes the GST treatment.
| name | percent | category | notes |
| ---- | ------- | --------- | ----------------------------------------------- |
| GST | 9 | SR | Standard‑rated supply. |
| GST | 0 | ZR | Zero‑rated supply. |
| GST | 0 / 9 | SRCA-S | Customer accounting – sales. |
| GST | 9 | SRCA-C | Customer accounting – purchase. |
| GST | 9 | SRRC | Reverse charge. |
| GST | 9 | SROVR-RS | Overseas vendor registration – remote services. |
| GST | 9 | SROVR-LVG | Overseas vendor registration – low‑value goods. |
| GST | 9 | SRLVG | Local low‑value goods. |
| GST | 0 | ES33 | Exempt: regulation 33. |
| GST | 0 | ESN33 | Exempt: non‑regulation 33. |
| GST | 9 | DS | Disregarded supplies. |
| GST | 0 | OS | Out‑of‑scope. |
| GST | 0 | NG | Non‑GST charge. |
> **Recommendation (Singapore)**
>
> * Always use `name: "GST"`.
> * Use the appropriate IRAS GST category in `category` and set `percent` accordingly (usually `9` or `0`).
### Slovenia (`si`)
[Section titled “Slovenia (si)”](#slovenia-si)
| name | percent | category |
| ---- | ---------- | -------- |
| DDV | 22, 9.5, 0 | S, AA, Z |
### Slovakia (`sk`)
[Section titled “Slovakia (sk)”](#slovakia-sk)
| name | percent | category |
| ---- | --------- | -------- |
| DPH | 20, 10, 0 | S, AA, Z |
### United States (`us`)
[Section titled “United States (us)”](#united-states-us)
| name | percent | category | notes |
| ---- | ------- | -------- | ------------------------------------------ |
| VAT | 0 | O | Used to indicate “no VAT” / outside scope. |
> **Recommendation (US)**
>
> * For companies **without** VAT ID, use a single tax with `name: "VAT"`, `percent: 0`, `category: "O"`, and optionally `comment: "Company without VAT ID"`.
> * Do not create local sales tax names in `taxes_attributes`; VAT handling is modeled as 0% outside scope.
## Examples
[Section titled “Examples”](#examples)
### Example – Spanish invoice line with VAT and IRPF
[Section titled “Example – Spanish invoice line with VAT and IRPF”](#example--spanish-invoice-line-with-vat-and-irpf)
```json
"taxes_attributes": [
{
"name": "IVA",
"category": "S",
"percent": 21.0
},
{
"name": "IRPF",
"category": "S",
"percent": -15.0
}
]
```
### Example – German exempt sale within EU (reverse charge)
[Section titled “Example – German exempt sale within EU (reverse charge)”](#example--german-exempt-sale-within-eu-reverse-charge)
```json
"taxes_attributes": [
{
"name": "USt",
"category": "AE",
"percent": 0.0,
"comment": "Intra‑community supply – reverse charge"
}
]
```
### Example – Singapore standard‑rated GST
[Section titled “Example – Singapore standard‑rated GST”](#example--singapore-standardrated-gst)
```json
"taxes_attributes": [
{
"name": "GST",
"category": "SR",
"percent": 9.0
}
]
```
# Tax report settings
> How to configure tax report settings for country-specific tax authority submissions.
Configure your Tax Report Settings to enable the submission of tax reports to your country’s tax authority. You can list, create, update, and delete these settings as needed.
The B2Brouter API currently supports Tax Report Settings for the following countries:
* \[BETA] France (DGFiP - Direction Générale des Finances Publiques)
* Spain (Verifactu and TicketBAI)
* Italy (SDI - Sistema di Interscambio)
* Malaysia (LHDN - Lembaga Hasil Dalam Negeri)
## Set up Tax Report Settings
[Section titled “Set up Tax Report Settings”](#set-up-tax-report-settings)
B2Brouter allows you to set up Tax Report Setting only for Tax Authorities that are within your country. You must set the **start\_date** to configure when Tax Reporting will begin. If **start\_date** field is not set B2Brouter will assume it’s the current day.
### Auto-generate
[Section titled “Auto-generate”](#auto-generate)
You can automatically generate Tax Reports after the invoice has been sent by setting up **auto\_generate** using [JSON Payload](https://developer.b2brouter.net/reference/create-tax-report-setting). Some Tax Authorities, in order to comply the legal requirements, obligate to generate the Tax Report as soon as the invoice has been sent. In that case you will not be able to configure automatic generation of the Tax Report.
If you want to manually generate each Tax Report, set **auto\_generate** to ***false***.
### Auto-send
[Section titled “Auto-send”](#auto-send)
If you have configured to generate Tax Reports automatically you can also automate the sending process with **auto\_send**. Once the Tax Report has been created it will automatically be sent to the Tax Authority.
### Special requirements
[Section titled “Special requirements”](#special-requirements)
Each Tax Authority from each country may have different [special settings](https://developer.b2brouter.net/reference/create-tax-report-setting) to comply.
#### \[BETA] France
[Section titled “\[BETA\] France”](#beta-france)
Creating a DGFiP Tax Report Setting triggers the full onboarding process for the French e-invoicing reform. When the setting is created with `enabled: true`, B2Brouter automatically registers your company in the PPF’s Annuaire (directory) and creates a Peppol 0225 transport for receiving invoices.
You cannot configure the **auto\_generate** parameter, as it is automatically set to **true** for legal compliance. Similarly, **auto\_send** defaults to **true**.
For detailed instructions on the complete France e-invoicing workflow, see the [\[BETA\] DGFiP e-Invoicing and e-Reporting guide](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/).
Example:
```shell
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": "dgfip",
"start_date": "2026-09-01",
"type_operation": "services",
"email": "tax-notifications@exemplar.fr",
"enterprise_size": "pme",
"naf_code": "62"
}
}'
```
Fields explanation:
* **type\_operation**: Type of your business operations (`services`, `goods`, or `mixed`). Determines the process code used in the PPF flux.
* **email**: Contact email for tax-related notifications.
* **enterprise\_size**: Size of the company. Accepted values: `ge` (large), `eti` (mid-size), `pme` (SME), `micro`. Required unless **annuaire\_only** is `true`.
* **naf\_code**: 2-digit French NAF activity code (e.g. `62` for IT services). Required unless **annuaire\_only** is `true`.
* **annuaire\_only** *(boolean, optional)*: When `true`, B2Brouter only registers the company in the PPF Annuaire and enables Peppol reception, without generating tax reports.
#### Italy
[Section titled “Italy”](#italy)
SDI has no special requirements to set up the Tax Report Setting, you simply need to create the setting and begin to report your taxes.
Example:
```shell
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": "sdi",
"start_date": "2024-06-06",
"auto_generate": true,
"auto_send": true
}
}'
```
#### Spain
[Section titled “Spain”](#spain)
##### Verifactu
[Section titled “Verifactu”](#verifactu)
Verifactu accepts several special settings, dealing with the default values for some items, such as the **special\_regime\_key**. You cannot configure **auto\_generate** parameter, as for legal obligations of Verifactu it’s automatically set to **true** and will generate tax report before issuing the invoice.
Example:
```shell
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-06-06",
"auto_generate": true,
"auto_send": true,
"reason_vat_exempt": "E1",
"special_regime_key": "01",
"reason_no_subject": "N1",
"credit_note_code": "R1",
"type_operation": "services"
}
}'
```
##### TicketBAI
[Section titled “TicketBAI”](#ticketbai)
TicketBAI has some special settings. Upon creating the Tax Report Setting you must specify the **delegation** to which you will report your taxes (Araba, Gipuzkoa, Bizkaia) and set the special fields for TicketBAI.
You cannot configure **auto\_generate** parameter, as for legal obligations of TicketBAI it’s automatically set to **true** and will generate tax report once the invoice has entered the state sent.
Also, each Basque delegation has some special requirements:
* **Bizkaia**: You must set the **epigraph** of your company in order to be able to generate the correct Tax Report.
* **Gipuzkoa**: In order to send Tax Reports you must fill and sent the following documentation to The Provincial Council of Gipuzkoa:
* Foral Order approving the terms of social collaboration in the sending of TicketBAI files generated in compliance with the TicketBAI obligation. [Download](https://www.gipuzkoa.eus/documents/2456431/13761128/03+III+eranskina_Atxikimendu+agiria_TicketBAI.pdf/d03119dc-a616-08c9-094d-b08a04a4dba6)
* Authorisation to send the TicketBAI files generated for compliance with the TicketBAI obligation. [Download](https://www.gipuzkoa.eus/documents/2456431/13761128/II+eranskina_bidalketa+baimena_TicketBAI.pdf/b797d11c-a0ef-4a48-ed00-b71171876f8b)
* You must incorporate the data from these forms into the computer files of the Foral Treasury for tax purposes. If you do not include the indicated data, the documents sent will be refused by the Provincial Council of Gipuzkoa.
Example:
```shell
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",
"start_date": "2024-06-06",
"auto_generate": true,
"auto_send": true,
"delegation": "gipuzkoa",
"reason_vat_exempt": "E1",
"special_regime_key": "01",
"reason_no_subject": "OT",
"credit_note_code": "R1",
"type_operation": "services",
"epigraph": "147410"
}
}'
```
#### Malaysia
[Section titled “Malaysia”](#malaysia)
To send your tax reports to LHDN, B2Brouter must be registered as an intermediary ERP on the MyInvois portal. This registration is required to authorize us to submit tax reports on your behalf. You can find detailed instructions [here](https://app.b2brouter.net/documents/Myinvois_Portal_Add_Intermediary_B2Brouter_Guidelines.pdf).
Once B2Brouter is registered as an intermediary ERP for your tax profile in MyInvois Portal, you can enable LHDN tax reporting settings in your B2Brouter account to start reporting invoices to LHDN.
Example:
```shell
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": "lhdn",
"msic": "01111",
"type_operation": "services",
"classification_code": "001"
}
}'
```
Fields explanation:
* **msic**: Malaysian business classification code, required by LHDN.
* **type\_operation**: Type of your business operations (services or goods). Default filled with this value if it’s not specified in your invoice.
* **classification\_code**: Specific code related to your product line category. Default filled with this value if it’s not specified in your invoice.
## Modify Tax Report Settings
[Section titled “Modify Tax Report Settings”](#modify-tax-report-settings)
Once you have set up your settings you can modify them in case you made a mistake or simply want to change some of the parameters. However keep in mind that once you reach the **start\_date** you will not be able to modify some of the parameters.
### TicketBAI
[Section titled “TicketBAI”](#ticketbai-1)
If your tax reporting period has began you will not be able to modify the **delegation** you have set up for TicketBAI reporting.
## Delete Tax Report Settings
[Section titled “Delete Tax Report Settings”](#delete-tax-report-settings)
In case you want to delete your Tax Report Setting for any reason you must be aware that once you have created your first tax report from any of your invoices you will not be able to delete the Settings. This restriction is applied for the legal requirements of the Tax Authorities. When [Listing all](https://developer.b2brouter.net/reference/get-tax-report-settings) or [Retrieving a single](https://developer.b2brouter.net/reference/get-tax-report-setting) Tax Report Setting the **locked** field will indicate if there have been created any tax reports with this setting.
## Sending Tax Reports
[Section titled “Sending Tax Reports”](#sending-tax-reports)
After configuring your Tax Report Settings you will be able to generate and send Tax Reports on the **start\_date** day. For further information on each type of tax report, please consult the relevant guides:
* [\[BETA\] DGFiP e-Invoicing and e-Reporting](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/)
* [Verifactu](/en/developers/guides-by-country/spain/verifactu/)
* [TicketBAI](/en/developers/guides-by-country/spain/ticketbai/)
* [LHDN](/en/developers/guides-by-country/malaysia/lhdn-tax-report/)
# Transaction
> Definition and explanation of transactions and billable events in B2Brouter.
## Definition of transaction
[Section titled “Definition of transaction”](#definition-of-transaction)
A transaction is an action performed on a document, such as its sending or reception. This action may include validation, registration, and tracking of the document to ensure that established requirements are met and that the document is correctly transferred.
### We consider a document
[Section titled “We consider a document”](#we-consider-a-document)
* Invoice (simplified, self-invoice, regular)
* Quote
* Order
* Delivery note
* Tax report
### Billable transactions
[Section titled “Billable transactions”](#billable-transactions)
Any of the following is considered a billable transaction:
* Send a document.
* Receive a document.
* Transform a document for download (API and APP).
### Exceptions
[Section titled “Exceptions”](#exceptions)
* Downloading a PDF copy of an issued invoice is not considered billable.
* State changes are not considered documents; therefore, actions on state changes are not counted as transactions.
* The event `error_sending`: Sending error (Status: Error, Corner C2) is not considered a transaction.
### Specific cases
[Section titled “Specific cases”](#specific-cases)
* **Tax report enabled:** When an invoice is sent and the tax report is enabled, the corresponding report is automatically generated and sent. Whether that report is billed depends on the tax authority — see [Billing per tax authority](#billing-per-tax-authority) below.
* **SDI case:** When a cross-border invoice is sent, we charge for the sending of the invoice to the tax authority. In this case, the tax report is the invoice sent to the tax authority.
### Billing per tax authority
[Section titled “Billing per tax authority”](#billing-per-tax-authority)
Enabling automatic tax reporting does not have the same cost in every regime. Three of them bill one transaction per report, and the other two do not bill the report at all or bill it in aggregate:
| Tax authority | Billing of the tax report | Effect per invoice |
| ------------- | ------------------------------ | ---------------------------------------------------------- |
| Verifactu | One transaction per tax report | Sending an invoice generates two billable transactions |
| TicketBAI | One transaction per tax report | Sending an invoice generates two billable transactions |
| SII | One transaction per tax report | Sending an invoice generates two billable transactions |
| KSeF | Not billed | No additional transaction |
| DGFiP | One transaction per ledger | No transaction per invoice, one per aggregated declaration |
**Ledgers.** For Verifactu and TicketBAI, a ledger counts as many transactions as it contains records: a ledger with 10 records counts as 10 individual transactions. This does not apply to the other two authorities that use ledgers. For DGFiP the whole ledger counts as a single transaction, and for KSeF the ledger is not billed at all.
DGFiP F10 declarations are grouped per civil period according to the declarant’s VAT regime. Each grouped declaration generates a single transaction, whatever the number of invoices it contains: a declaration with 200 invoices counts the same as one with two.
### API endpoints that generate a billable transaction
[Section titled “API endpoints that generate a billable transaction”](#api-endpoints-that-generate-a-billable-transaction)
* Send invoice
* Create an issued invoice (with `send_after_import: true`)
* Import an invoice from a file (with `send_after_import: true`)
* Get an invoice with a specific document type (except in PDF format)
* Generate a tax report from an Invoice
* Create a Tax Report
* Import a Tax Report
* Annulate a tax report
> **Note:** the four tax-report endpoints above are billed according to the tax authority. See [Billing per tax authority](#billing-per-tax-authority).
### Examples of other API endpoints that currently do not generate a billable transaction
[Section titled “Examples of other API endpoints that currently do not generate a billable transaction”](#examples-of-other-api-endpoints-that-currently-do-not-generate-a-billable-transaction)
* Create an issued invoice (with `send_after_import: false`)
* Import an invoice from a file (with `send_after_import: false`)
* Switch invoice state
* Validation
* Mark an invoice as acknowledged
* Lookup (Directory)
### Transaction counters by document type
[Section titled “Transaction counters by document type”](#transaction-counters-by-document-type)
Transactions can be registered by counting any of the following events (1 event = 1 transaction).
#### List of events
[Section titled “List of events”](#list-of-events)
* `success_sending`: Successful sending (Status: Sent, Corner C2)
* `success_clearing`: Clearance process completed successfully (ZATCA)
* `error_clearing`: Error during the clearance process (ZATCA)
* `notification_c2_state_downloaded`: Invoice downloaded. Corresponds to corner C2. When the transport is “Download” and the user clicks the *Issue and Download* button (Status: Downloaded)
### Outgoing
[Section titled “Outgoing”](#outgoing)
#### Issued invoices of different types (`sendable_type: Invoice`)
[Section titled “Issued invoices of different types (sendable\_type: Invoice)”](#issued-invoices-of-different-types-sendable_type-invoice)
**API endpoints:**
* [Send invoice](https://developer.b2brouter.net/reference/send-invoice)
* [Import invoice](https://developer.b2brouter.net/reference/import-invoice) (with parameter `send_after_import: true`)
**Events:**
* `success_sending`
* `error_sending`
**Non-API related events:**
* `notification_c2_state_downloaded`
#### Quotes (`sendable_type: Quote`)
[Section titled “Quotes (sendable\_type: Quote)”](#quotes-sendable_type-quote)
**API endpoints:** Quotes cannot be sent via API.
**Non-API related events:**
* `success_sending`
* `error_sending`
#### Delivery notes (`sendable_type: DespatchAdvice`)
[Section titled “Delivery notes (sendable\_type: DespatchAdvice)”](#delivery-notes-sendable_type-despatchadvice)
**API endpoints:** Delivery notes cannot be sent via API.
**Non-API related events:**
* `success_sending`
* `error_sending`
#### Orders (`sendable_type: Order`)
[Section titled “Orders (sendable\_type: Order)”](#orders-sendable_type-order)
**API endpoints:**
* [Mark as processed](https://app.b2brouter.net/orders/%7Bid%7D/mark_as) (this endpoint does not generate any transaction)
**Non-API related events:** When accepting or rejecting an order (UI only), an order response is sent that generates one of these events:
* `success_sending`
* `error_sending`
#### Tax reports (`sendable_type: TaxReport`)
[Section titled “Tax reports (sendable\_type: TaxReport)”](#tax-reports-sendable_type-taxreport)
**API endpoints:**
* [Send invoice](https://developer.b2brouter.net/reference/send-invoice) (if the invoice requires the tax report)
* [Create tax report](https://developer.b2brouter.net/reference/create-tax-report)
* [Import tax report](https://developer.b2brouter.net/reference/import-tax-report)
* [Modify tax report](https://developer.b2brouter.net/reference/modify-tax-report)
* [Annulate tax report](https://developer.b2brouter.net/reference/annulate-tax-report)
* [Annulate tax report (legacy)](https://developer.b2brouter.net/v2025-01-01/reference/new-annulate-tax-report)
**Events:**
* `success_sending`
* `error_sending`
* `success_clearing`
* `error_clearing`
**Notes:** When the `tax_authority` is Verifactu or TicketBAI (Bizkaia), in addition to sending the TaxReport, a Ledger is also sent.
#### Ledger
[Section titled “Ledger”](#ledger)
**API endpoints:** Same as those for TaxReport.
**Events:**
* `success_sending`
* `error_sending`
### Incoming
[Section titled “Incoming”](#incoming)
#### Received invoices of different types (`sendable_type: Invoice`)
[Section titled “Received invoices of different types (sendable\_type: Invoice)”](#received-invoices-of-different-types-sendable_type-invoice)
A transaction is generated when a “received invoice” is received. When the received invoice is manually created or imported via the platform, it is not counted as a transaction.
**API endpoints:**
* [Import invoice](https://developer.b2brouter.net/reference/import-invoice) (with `issued=false`, corresponding to `from_net: 'uploaded'`)
#### Orders (`sendable_type: Order`)
[Section titled “Orders (sendable\_type: Order)”](#orders-sendable_type-order-1)
**API endpoints:** Orders cannot be generated or sent via API.
**Notes:** A transaction is generated when an order is received. When imported via the platform, it is not counted as a transaction.
### View\_as
[Section titled “View\_as”](#view_as)
Calling `GET /invoices/{id}/as/{document_type_code}` generates a new billable transaction whenever the document is freshly produced from the underlying invoice data. The following values of `document_type_code` are exceptions and do not generate a transaction:
* **`original`** — returns the document exactly as it was originally submitted or imported, served from storage. No regeneration takes place.
* **`legal`** — returns the archived legal document (e.g. the signed and timestamped version) as stored. No regeneration takes place.
* **`pdf.invoice`** and **`pdf.invoice.signed`** — although the PDF is rendered on demand, downloading a PDF copy of an issued invoice is not considered billable.
For any other value (e.g. `xml.ubl.invoice.bis3`, `xml.facturae.3.2.2`, etc.), a new transaction is registered on every call, because the document is regenerated from the database object.
```shell
curl --request GET \
--url https://api.b2brouter.net/invoices/{id}/as/legal \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
```shell
curl --request GET \
--url https://api.b2brouter.net/invoices/{id}/as/xml.ubl.invoice.bis3 \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
> **Note:** If you only need to retrieve the document you already sent or received, use `/as/legal` or `/as/original` instead.
## How to monitor your transactions usage
[Section titled “How to monitor your transactions usage”](#how-to-monitor-your-transactions-usage)
If you are a **Partner** or **Reseller**, you can monitor the transaction consumption of each integration group directly from the B2Brouter portal.
When you provision an integration group, whether because you manage a single business group or operate as an **eDocSync**, the most efficient way to monitor total and per-account transaction usage is through the following API endpoint: `GET /accounts`
#### Example request
[Section titled “Example request”](#example-request)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts?offset=0&limit=25' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
#### Example response
[Section titled “Example response”](#example-response)
```json
{
"accounts": [
{
"id": 12345,
"name": "Ejemplo S.L.",
...
"transactions_count": 24,
"transactions_count_previous_period": 41,
"transactions_limit": 100
},
{
"id": 23456,
"name": "Muster GmbH",
...
"transactions_count": 32,
"transactions_count_previous_period": 9,
"transactions_limit": 100
},
{
"id": 34567,
"name": "Esempio S.r.l.",
...
"transactions_count": 124,
"transactions_count_previous_period": 326,
"transactions_limit": 100
}
...
]
}
```
Each account includes:
* **transactions\_count** → Transactions consumed in the current billing period.
* **transactions\_count\_previous\_period** → Transactions consumed in the previous billing period.
* **transactions\_limit** → Maximum number of transactions included in the subscription.
By controlling this data, you can automatically manage the consumption of each account and the overall total. This allows you to automate payments based on transaction volume for your end clients, or to monitor your usage against the agreed quota and increase your transaction limit before exceeding it.
# Transports
> How to configure and use transport types (email, Peppol, SFTP, FACe, etc.) in B2Brouter.
B2Brouter sends and receives documents using a [Transport](https://developer.b2brouter.net/reference/create-transport). For instance: email, Peppol, SFTP, FACe, etc.
Each [Contact](https://developer.b2brouter.net/reference/create-contact) uses a Transport. B2Brouter sends documents to a Contact using its Transport.
There are some transports that require additional configurations, like credentials. You can use the [Transport](https://developer.b2brouter.net/reference/create-transport) object to manage these configurations.
## Configure a Transport
[Section titled “Configure a Transport”](#configure-a-transport)
The `code` and `enabled` attributes are common to all transports:
1. `code` is the unique identifier of a Transport type. There is a list of available [B2Brouter Transport types codes](https://developer.b2brouter.net/reference/get-transport-types).
2. `enabled` enables or disables a Transport in the context of an Account
### b2brouter transport
[Section titled “b2brouter transport”](#b2brouter-transport)
You can send and receive documents to and form other accounts within B2Brouter. It is an internal transport in the sense that it does not go through any external network, like Peppol or email.
To enable internal B2Brouter reception create a Transport with `code` **b2brouter** and `reception` set to true.
#### Example request:
[Section titled “Example request:”](#example-request)
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/transports \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json'
'
```
### Peppol transport
[Section titled “Peppol transport”](#peppol-transport)
You can send and receive documents to and from any Peppol participant.
To enable Peppol create a Transport with `code` **peppol**. If you want to send and receive documents set `reception` to true. If you just want to send documents, set `reception` to false.
You do not need to enable reception to be able to receive responses to the documents you send. For instance you will always have the capacity to receive a Message Level Response or an Invoice Response.
The Peppol transport publishes your reception capabilities in our SMP (Service Metadata Publisher).
#### Example request:
[Section titled “Example request:”](#example-request-1)
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/transports \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"transport": {
"code": "peppol",
"enabled": true,
"reception": true,
"standard_documents": true,
"invoice": true,
"credit_note": true,
"self_billing": false,
"application_response": true,
"pin_scheme": "{YOUR_IDENTIFICATION_NUMBER}",
"pin_value": "{YOUR_IDENTIFICATION_SCHEME}"
}
}'
```
### email transport
[Section titled “email transport”](#email-transport)
B2brouter processes documents sent to the email address of the Account. The email address of an account is *\[the\_account’s\_company\_ID]@b2brouter.email*. To enable email reception create a Transport with `code` **email** and `reception` set to true.
### Credentials
[Section titled “Credentials”](#credentials)
Some transports need credentials. To be able to use those transports you need to setup their credentials first, with its `code` and your `username` and `password`. For instance you will need credentials to use:
* **es.pecam** - Spain - Autonomous community of Castilla la Mancha
* **es.eusko\_jaurlaritza** - Spain - Eusko Jaurlaritza (Basque Government)
Some need an additional `edi_identifier`.
# Webhooks
> How to verify and handle B2Brouter webhook signatures and events.
To ensure the webhook is authentic and sent by B2Brouter, verify the signature included in the `X-B2Brouter-Signature` header. Here is how you can verify the signature on your server:
1. **Extract the Header Information**: The `X-B2Brouter-Signature` header contains two components:
* `t`: The UNIX timestamp when the payload was signed.
* `s`: The HMAC-SHA256 signature. Example header:
```plaintext
t=1732530076,s=9f117925ea533a2ea74b19699e03ab5de5664b4ff021d3d01eaff8868ed29e4d
```
2. **Recreate the Payload**: Combine the received timestamp (`t`) and the raw JSON payload (`data`) using a period (`.`):
```plaintext
payload = "#{timestamp}.#{data}"
```
3. **Compute the HMAC-SHA256 Signature**: Use the shared `signature_key` and the payload to compute the signature:
```plaintext
computed_signature = HMAC-SHA256(signature_key, payload)
```
4. **Compare Signatures**: Compare the `computed_signature` with the `s` value from the header. If they match, the webhook is valid.
### Examples
[Section titled “Examples”](#examples)
1. **PHP**:
```plaintext
$headers = getallheaders();
$header = $headers['X-B2Brouter-Signature'];
preg_match('/t=([^,]+),s=(.+)/', $header, $matches);
$timestamp = $matches[1];
$signatureHash = $matches[2];
$recalculatedHash = hash_hmac(
'sha256',
$timestamp . '.' . file_get_contents('php://input'),
'your_webhook_secret'
);
$isValid = hash_equals($signatureHash, $recalculatedHash);
```
## Events
[Section titled “Events”](#events)
* **`ledger.state_change`** — fired when a ledger (DGFiP Flux 10, Verifactu) transitions to a new state. See [Submit a Flux-10 e-reporting file](/en/developers/use-cases-by-country/submit-flux10-ereporting-file/#webhooks-recommended) for the payload shape and the list of states.
# DGFiP e-Invoicing and e-Reporting
> How to comply with the French DGFiP e-invoicing and e-reporting reform using B2Brouter.
From September 2026, all French companies must route their invoices and TVA data through a certified platform. B2Brouter simplifies this: you send your invoice data via a REST API call, and B2Brouter handles PPF registration, document generation (UBL/CII/Factur-X), routing to your clients, and tax reporting to the DGFiP — all through a single integration.
B2Brouter is a certified **Plateforme Agréée (PA)** for the French DGFiP e-invoicing reform. Connect via REST API and B2Brouter handles the entire compliance stack on your behalf:
| What B2Brouter does for you | Details |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **PPF registration** | Publishes your SIREN/SIRET to the Annuaire automatically on activation |
| **Flux 1: B2B e-invoicing** | Generates UBL/CII/Factur-X, transmits to PPF, routes to buyer’s platform |
| **Flux 6: Invoice lifecycle** | Manages CDAR status messages (Déposée, Reçue, Approuvée, Refusée, Encaissée) |
| **Flux 10: e-Reporting** | Aggregates B2C and cross-border B2B transactions (intra-EU and extra-EU) into Ledgers sent to the PPF at the frequency set by your Tax Report Setting’s `vat_regime` |
| **Peppol 0225 reception** | Receives invoices from any French or Peppol-connected platform |
| **Document generation** | You send invoice data as JSON — B2Brouter generates the compliant UBL/CII/Factur-X document and transmits it to the PPF. No XML generation required on your side |
| **Input formats** | JSON REST API, Factur-X PDF/A-3 (with embedded CII XML), UBL 2.1 XML, CII XML |
| **Legal archiving** | All transmitted documents (invoices, tax reports, CDAR messages) are stored by B2Brouter for the legally required 10-year retention period. No additional storage setup required on your side |
**The path to your first invoice**: [create your account](#create-your-company-account) → [activate DGFiP](#activate-the-tax-report-setting) → [create a contact](#create-a-contact) → [send your first invoice](#issue-invoices).
***
> **Regulatory context**: The [French e-invoicing reform](https://www.impots.gouv.fr/facturation-electronique-702) mandates that all French companies use a certified **Plateforme Agréée (PA)** or the government’s **PPF** (Portail Public de Facturation) to transmit invoices and report TVA data to the DGFiP, starting September 2026. As a certified PA, B2Brouter handles the PPF connection entirely on your behalf — no SFTP, no electronic certificates, no direct PPF integration required.
## Who is this guide for?
[Section titled “Who is this guide for?”](#who-is-this-guide-for)
There are two main use cases for integrating with B2Brouter for France e-invoicing:
**eDocExchange** — For companies or groups of companies that integrate their management software (ERP, accounting platform) directly with B2Brouter. The onboarding process (account creation, Tax Report Settings configuration) is typically performed once per company through the **web UI**. Day-to-day operations (issuing invoices, tracking their lifecycle) happen through the **API**. To add further company accounts to your integration group, follow the same onboarding wizard from the same B2Brouter user account — no separate API call required.
**eDocSync** — For software vendors and ERP providers who want to offer DGFiP compliance to their own customers from within their product. This is B2Brouter’s **white-label / embedded / marque blanche** model: B2Brouter operates entirely in the background, end customers interact exclusively with the vendor’s interface and are unaware of B2Brouter. The software vendor is responsible for account provisioning, invoice submission, and lifecycle tracking via the B2Brouter API. End customers do not need a B2Brouter login or subscription.
For eDocSync, account provisioning volume determines the right plan:
* **Few companies (reseller model)**: Add each client company as an account in your B2Brouter integration group via the web UI, following the standard onboarding wizard. This works well for tens of companies and shares a single API key.
* **100+ companies**: Contact our [Sales team](https://www.b2brouter.net/global/contact/) or open a Support Ticket to discuss a dedicated eDocSync plan with bulk provisioning (volume-based pricing for editors).
In both cases, all France-specific compliance features (Annuaire registration, Flux 1/6/10 transmission, CDAR lifecycle) work identically.
> **B2Brouter is itself a Plateforme Agréée**: you integrate **with** B2Brouter — it is not a relay or connector to another PA. If your SIREN is currently registered with a different PA, activating the Tax Report Setting on the SIREN account fails: you need a migration code (see [Change PA](#change-pa)). To bring in only one establishment while keeping that existing registration, see Scenario 2 in [Account structure](#account-structure-parent-and-organisational-units). You cannot use B2Brouter as a pass-through to submit invoices under a different PA’s certification.
> **Routing to recipients on other PAs**: When your buyer is registered with a different PA, B2Brouter routes the invoice via the standard **Peppol four-corner model** (C2 → C3): B2Brouter’s Peppol access point (C2) looks up the recipient’s Peppol address in the Annuaire and delivers the document to the buyer’s access point (C3) — regardless of which PA they use. No additional configuration is required on your side.
***
> **Testing environments**: Use [sandbox](/en/developers/testing/sandbox/) for initial API testing and payload validation — DGFiP submissions are simulated in sandbox, no fictitious SIRET required. For full end-to-end testing with the DGFiP QAS (qualification) environment, use B2Brouter’s staging environment as described below.
## Before you start: choose your environment
[Section titled “Before you start: choose your environment”](#before-you-start-choose-your-environment)
| Environment | B2Brouter app | B2Brouter API | Chorus Pro portal |
| ------------------ | ----------------------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------- |
| **Production** | [app.b2brouter.net](https://app.b2brouter.net/register) | `https://api.b2brouter.net` | [chorus-pro.gouv.fr](https://chorus-pro.gouv.fr) |
| **Staging (test)** | [app-staging.b2brouter.net](https://app-staging.b2brouter.net/register) | `https://api-staging.b2brouter.net` | [qualif.chorus-pro.gouv.fr](https://qualif.chorus-pro.gouv.fr) |
> Do not mix environments. Production uses real SIREN/SIRET numbers and connects to the DGFiP production Annuaire. Staging uses fictitious test identifiers and connects to the DGFiP QAS (qualification) environment. API keys, accounts, and contacts are not shared between environments.
### Option A: Production pilot
[Section titled “Option A: Production pilot”](#option-a-production-pilot)
The production pilot is no longer available, it ended on 01.09.2026.
Register at [app.b2brouter.net](https://app.b2brouter.net/register) to start a production integration. When you activate the DGFiP Tax Report Setting, your company’s SIREN/SIRET is published to the real PPF Annuaire, making it discoverable by any platform in the French e-invoicing ecosystem.
This option is designed for real B2B invoices starting September 2026. In the meantime, the DGFiP will clear QAS-period records before the reform goes live — so any invoices you send during your pilot will not create compliance obligations. This is the right starting point if you have already chosen B2Brouter and want to test the full integration with your actual company data.
### Option B: Staging (recommended for evaluation)
[Section titled “Option B: Staging (recommended for evaluation)”](#option-b-staging-recommended-for-evaluation)
Register at [app-staging.b2brouter.net](https://app-staging.b2brouter.net/register) to use B2Brouter’s test environment, which is connected to the DGFiP’s QAS (qualification) environment.
This is the best option if:
* You are evaluating multiple platforms before committing
* Your SIREN/SIRET is already published in the Annuaire with another PA and you don’t want to [change PA](#change-pa) yet
* You prefer not to associate your company identifier with test activity
The staging environment is self-provisioned: once you have test identifiers (see below), you can start end-to-end testing in under 24 hours.
> **Staging SIRET validation**: In the staging environment, any valid 14-digit number is accepted as a SIRET without checksum validation. The fictitious identifiers from the Chorus Pro QAS CSV are pre-registered in the DGFiP QAS annuaire and work end-to-end. In production, SIRET format and checksum are validated.
> **Important**: The DGFiP does not allow real SIREN or SIRET numbers in their QAS environment. You must use fictitious test identifiers. You can obtain these yourself via the Chorus Pro QAS portal (free, 5-minute process) or request a pre-assigned set by opening a Support Ticket in the staging app.
> **One account per SIREN**: B2Brouter creates one account per SIREN (the TVA number key is derived from the SIREN). If you provide a SIRET, the SIREN is extracted from it. Two different SIRETs from the same company (same SIREN) will resolve to the same account. If your company operates from multiple establishments (different SIRETs), these are modelled as **organisational units** within the same B2Brouter account — not as separate accounts. Contact [Support](https://www.b2brouter.net/docs/#/en/support/open-incident-report) if you need to configure multi-establishment invoicing. Keep this in mind when selecting rows from the CSV: pick SIRETs with distinct SIRENs (first 9 digits) for each independent company you need to test.
#### Getting test identifiers via Chorus Pro QAS
[Section titled “Getting test identifiers via Chorus Pro QAS”](#getting-test-identifiers-via-chorus-pro-qas)
The [Chorus Pro QAS portal](https://qualif.chorus-pro.gouv.fr) lets you generate a “Matelas de données” (data set) — a CSV file containing fictitious SIREN/SIRET identifiers pre-registered in the DGFiP test environment. Follow these steps:
1. Go to [qualif.chorus-pro.gouv.fr](https://qualif.chorus-pro.gouv.fr) → **Entreprise** tab → **Créer mon compte**.
* You can use a temporary email address (e.g. [temp-mail.io](https://temp-mail.io)).
* Use any name; this account is purely for obtaining test identifiers.
2. Check your inbox for the **“Initialisation de mot de passe Chorus Pro”** email and set your password (link valid 60 minutes).
3. Log in → go to **Domaines** → **Matelas de données** → click **Générer un matelas de données** and confirm.
4. Go to **Consultation du matelas de données**. Wait until both statuses show **“Disponible”**:
* *Statut pour Chorus Pro*: Disponible
* *Statut pour l’annuaire de facturation PPF*: Disponible
* Generation typically takes a few minutes. Refresh the page to check.
5. Click **“Générer et télécharger le fichier CSV du matelas structures et utilisateurs”** to download your test identifiers.
The CSV contains multiple fictitious SIREN/SIRET numbers in rows labelled **“Privé”** and **“Public”**. Use only rows from the **“Privé”** (private sector) section for your test accounts. Public sector entities (`SIREN 'Public'`) are rejected by the DGFiP QAS annuaire — attempting to activate e-Reporting on a public-sector account returns HTTP 422 (“La création d’une ligne annuaire n’est pas possible pour une entité associée à un SIREN ‘Public’”). This is correct behaviour: public entities use Chorus Pro directly, not a PA.
Use one private-sector SIREN for your emitter account and another for your test contact.
**Once you have your test identifiers:**
1. Register at [app-staging.b2brouter.net](https://app-staging.b2brouter.net/register) and activate your account.
2. Create your company account using a SIREN from the CSV (see [Create your company account](#create-your-company-account)).
3. Subscribe to an eDocExchange plan (staging subscriptions are simulated — no charge).
4. Enable DGFiP Tax Report Settings (see [Activate the Tax Report Setting](#activate-the-tax-report-setting)).
* ⚠️ After activating the DGFiP Tax Report Setting, your company’s Annuaire registration takes up to **24 hours** to propagate — both in staging and in production. This is a DGFiP infrastructure constraint, not a B2Brouter delay. You will not be able to send invoices until the following day.
5. The following day: create a test contact using a second SIREN from the CSV (see [Create a contact](#create-a-contact)).
6. Send your first invoice (see [Issuing Invoices](#issue-invoices)).
### API credentials
[Section titled “API credentials”](#api-credentials)
Authentication uses a static **API key** passed in the `X-B2B-API-Key` HTTP header. There is no OAuth2 flow — generate your API key in the B2Brouter UI under Settings → API Keys. Keep it confidential and never include it in client-side code. Also set `X-B2B-API-Version` in every request.
Minimum API version: 2026-03-02
The DGFiP Tax Report Settings endpoint and all DGFiP-specific fields require **`X-B2B-API-Version: 2026-03-02`** or later. Earlier versions will not expose these endpoints. The invoice and account endpoints work from `2025-01-01` onwards.
> **Base URL**: All examples below use `https://api-staging.b2brouter.net`. For production, replace with `https://api.b2brouter.net`.
***
## Account structure: Parent and organisational units
[Section titled “Account structure: Parent and organisational units”](#account-structure-parent-and-organisational-units)
In France, every company has a 9-digit **SIREN** identifying it as a legal entity, and every establishment or service has a 14-digit **SIRET** derived from that SIREN. B2Brouter follows the same hierarchy, with a **parent account** and, under it, an **organisational unit (UO)** for each establishment or service. Invoices are issued from the parent account or from a specific UO, depending on the issuing establishment.
Caution
This structure has no relation to B2Brouter account groups. Organisational units are establishments or offices within a single SIREN account, not independent accounts in a group.
### The golden rule
[Section titled “The golden rule”](#the-golden-rule)
**The parent account is always a SIREN.** Any SIRET, Code Routage or Suffix (see [Identifiers and schemes](#identifiers-and-schemes)) is always modelled as a UO under that parent, never as an independent account: this is what keeps the published identifiers of the parent and its UOs consistent.
If you create an account with a SIRET, B2Brouter does not reject it: it derives the SIREN from the first 9 digits, creates the parent account if it does not exist yet, and attaches the SIRET as a UO. What the call returns is the UO. Converting an existing parent to a SIRET with a `PUT` does return `422 parameter_matriu_must_be_siren`.
**Every UO has its own Tax Report Setting.** A UO never reports through the parent account: if it does not have its own Tax Report Setting enabled, it does not report. That said, when you create a UO under a parent that already has DGFiP active, B2Brouter clones an own Tax Report Setting for it so that it starts reporting with no extra steps. If you don’t want it to report, disable it (`enabled: false`) once created. See [Scenarios and cases](#scenarios-and-cases) to pick your configuration.
#### Contacts
[Section titled “Contacts”](#contacts)
**The golden rule applies to contacts too.** French contacts (clients and suppliers) follow the same hierarchy and the same `cin_scheme` values: the contact parent is always the SIREN, and any SIRET, Code Routage or Suffix is a UO. The system technically allows a contact parent with an identifier other than the SIREN, but doing so breaks the UOs you later add for the same client. To create them, see [Create a contact](#create-a-contact).
Accounts and contacts created with a SIRET as parent
* **Accounts**: until July 2026 the platform allowed creating a parent account with a SIRET. Those must be normalised: replace the parent’s identifier with the SIREN (`cin_scheme="0002"`, `cin_value` = the 9-digit SIREN) and create the corresponding SIRET as a UO under that parent.
* **Contacts**: until June 2026 the same applied to contacts. Normalise them the same way, putting the SIREN on the parent contact and the SIRET as a UO under it.
### Identifiers and schemes
[Section titled “Identifiers and schemes”](#identifiers-and-schemes)
#### Identifiers in B2Brouter
[Section titled “Identifiers in B2Brouter”](#identifiers-in-b2brouter)
In B2Brouter, French accounts, organisational units and contacts are defined by a principal identifier (`cin_scheme` + `cin_value`). SIRET UOs with several internal services add a sub-identifier (`routing_codes.cin1_scheme` + `routing_codes.cin1_value`) to address a specific service. These are two different things: the **Suffix** is a principal `cin_scheme`, whereas the **Code Routage** always goes in the sub-identifier and never as a principal `cin_scheme`.
**Account, UO or contact principal identifier** (`cin_scheme` + `cin_value`):
| `cin_scheme` | `cin_value` | Meaning | Example |
| ------------ | ---------------------------------------------- | ------------------------ | ---------------- |
| `0002` (`2`) | **SIREN**, 9 digits + Luhn | Legal entity | `123456789` |
| `0009` (`9`) | **SIRET**, 14 digits (SIREN + 5 digits) + Luhn | Establishment | `12345678900012` |
| `8040` | **Suffix**, alphanumeric `[A-Za-z0-9_\-\/]+` | UO without its own SIRET | `SUF01`, `ADMIN` |
**Sub-identifier or routing code** (`routing_codes.cin1_scheme` + `routing_codes.cin1_value`):
| `routing_codes.cin1_scheme` | `routing_codes.cin1_value` | Meaning | Example |
| --------------------------- | --------------------------------------------------------------------- | ---------------------- | -------------------------- |
| `0224` (`224`) | **Code Routage**, alphanumeric `[A-Za-z0-9_\-\/]+` (min. 1 character) | Service inside a SIRET | `COMPTA`, `SERV01`, `A123` |
#### Identifier published at the Annuaire
[Section titled “Identifier published at the Annuaire”](#identifier-published-at-the-annuaire)
On the Peppol network, by contrast, the French participant is always identified by scheme `0225 (FRCTC Electronic Address)`, the code reserved for French electronic addresses. Its value is always a composition published at the Annuaire, never a bare identifier: a SIRET without the SIREN in front is not publishable. Schemes `0224` and `8040` are not Peppol schemes: they only serve to build that value.
**Peppol participant identifier** (`pin_scheme` + `pin_value`):
| `pin_scheme` | `pin_value` | Meaning |
| ------------ | ------------------ | ------------------------------- |
| `0225` | `SIREN` | Whole entity (Parent) |
| `0225` | `SIREN_SIRET` | Establishment |
| `0225` | `SIREN_SIRET_` | Service inside an establishment |
| `0225` | `SIREN_` | UO without its own SIRET |
Each composition corresponds to one or two of the cases in the [Case table](#case-table). To see the API fields that produce it, see [Add establishments as organisational units](#add-establishments-as-organisational-units-optional).
***
## Scenarios and cases
[Section titled “Scenarios and cases”](#scenarios-and-cases)
B2Brouter covers three configuration scenarios. Which one applies depends on what you want to do from B2Brouter. If you don’t know whether your SIREN is already registered with another PA, check it at the Annuaire with the [Directory lookup](#optional-verify-recipient-routing-directory-lookup).
**Do you want the SIREN to report from B2Brouter?**
| Answer | Scenario | What to do |
| ------------------------------------------------------------------ | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Yes** | **Scenario 1.** The whole company reports | Activate the Tax Report Setting on the SIREN parent account. Establishments, services and internal units are modelled with **Cases 1, 2 and 3**. If the SIREN is already registered with another PA, you must first migrate it to B2Brouter with a migration code (see [Migrating from another PA or to another one](#change-pa)). |
| **No**, I want one specific establishment to report from B2Brouter | **Scenario 2.** Only one establishment reports | Create the establishment as a UO with the SIRET and activate the Tax Report Setting on that UO only, not on the SIREN parent account. The SIREN’s registration with the other PA is left untouched. **Cases 4 and 5**. |
| **No**, I only want to issue from B2Brouter | **Scenario 3.** Issue from B2Brouter, receive at your PA | **Only available on a Suffix UO**: create it under the SIREN and activate its Tax Report Setting with `issue_only: true`. Reception stays with the other PA. **Case 6**, see [Tax Report Setting modes](#tax-report-setting-modes). |
### Case table
[Section titled “Case table”](#case-table)
| Case | Organisational unit (UO) | Tax Report Setting | Annuaire address (`pin_value`) | When you need it |
| ---------------------------------------------- | ------------------------- | -------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------- |
| **1. Établissement** Scenario 1 | SIRET | Parent:`ON` UO:`ON`1 | `SIREN_SIRET` | You have establishments and they all report from the company. |
| **2. Service** Scenario 1 | SIRET + Code Routage | Parent:`ON` UO:`ON`1 | `SIREN_SIRET_` | You want to address a specific department inside an establishment. |
| **3. Suffix** Scenario 1 | Suffix | Parent:`ON` UO:`ON`1 | `SIREN_` | You have a unit without its own SIRET that must receive and report at its own address. |
| **4. Établissement-only** Scenario 2 | SIRET | Parent:`OFF` UO:`ON` | `SIREN_SIRET` | The SIREN is already registered with another PA and you are only bringing one establishment to B2Brouter. |
| **5. Établissement-only + Service** Scenario 2 | SIRET + Code Routage | Parent:`OFF` UO:`ON` | `SIREN_SIRET_` | Like Case 4, but addressing a department inside that establishment. |
| **6. Issue-only** Scenario 3 | Suffix with `issue_only`2 | Parent:`OFF` UO:`ON` | `SIREN_` | You want to issue from B2Brouter while keeping reception at your current PA. |
1 When you create a UO under a parent that already has DGFiP active, B2Brouter clones an own Tax Report Setting for it. See [The golden rule](#the-golden-rule).
2 A Suffix UO publishes a 0225 transport and receives invoices like any other address (Case 3). What removes reception is the `issue_only` flag on its Tax Report Setting, which is **only accepted on a Suffix UO** (see [Tax Report Setting modes](#tax-report-setting-modes)).
***
## Create your company account
[Section titled “Create your company account”](#create-your-company-account)
This section explains how to create the parent account with the SIREN and how to add organisational units to it. The model behind it is in [Account structure](#account-structure-parent-and-organisational-units).
### Create the parent account with the SIREN
[Section titled “Create the parent account with the SIREN”](#create-the-parent-account-with-the-siren)
Create the account with [`POST /accounts`](https://developer.b2brouter.net/reference/create-account). If it already exists, retrieve its `id` with [`GET /accounts`](https://developer.b2brouter.net/reference/get-accounts) and skip the creation.
###### Fields specific to a French account:
[Section titled “Fields specific to a French account:”](#fields-specific-to-a-french-account)
| Field | Value | Purpose |
| ------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `country` | `"fr"` | Enables FR validation and routing |
| `cin_scheme` | `"0002"` | SIREN, the legal entity. The parent is always a SIREN: see [The golden rule](#the-golden-rule) for what happens if you send a SIRET |
| `cin_value` | 9-digit SIREN | Company identifier, Luhn-validated |
| `tin_scheme` | `9957` | ISO 6523 code for the French tax identifier |
| `tin_value` | `FR{kk}{siren}` | TVA number that appears in the UBL. You don’t have to compute the `kk` checksum |
Derivation between SIREN and TVA number
If you provide only one of the two, B2Brouter derives the other **when you activate the DGFiP Tax Report Setting**, not when you create the account. If you don’t provide `tin_value` and don’t activate the Tax Report Setting, the field stays empty and invoice issuing fails.
**Example request:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"account": {
"country": "fr",
"name": "Exemplar SAS",
"address": "10 Rue Imaginaire",
"city": "Paris",
"postalcode": "75001",
"province": "Île-de-France",
"email": "john.doe@example.com",
"tin_value": "FR32123456789",
"tin_scheme": 9957,
"cin_scheme": "0002",
"cin_value": "123456789",
"rounding_method": "half_up"
}
}'
```
**Example response:**
```json
{
"account": {
"id": 83428,
"name": "Exemplar SAS",
"tin_value": "FR32123456789",
"tin_scheme": 9957,
"cin_scheme": "0002",
"cin_value": "123456789",
"address": "10 Rue Imaginaire",
"city": "Paris",
"postalcode": "75001",
"province": "Île-de-France",
"country": "fr",
"currency": "EUR",
"email": "john.doe@example.com",
"rounding_method": "half_up",
"created_at": "2026-06-10T09:54:38.000Z",
"updated_at": "2026-06-10T09:54:38.000Z"
}
}
```
### Add establishments as organisational units (optional)
[Section titled “Add establishments as organisational units (optional)”](#add-establishments-as-organisational-units-optional)
Minimum API version
Creating a UO by passing `parent_id` requires API `2026-06-26` or later; in earlier versions `parent_id` is a read-only field. For French accounts there is an alternative that works on any version: create the account with the SIRET and let B2Brouter derive the parent and the UO (see [The golden rule](#the-golden-rule)).
If your company structure requires UOs (see the [case table](#case-table)), create each UO with [`POST /accounts`](https://developer.b2brouter.net/reference/create-account) adding `parent_id` (the id of the parent account you created earlier).
If the parent account already has the DGFiP Tax Report Setting active, the new UO receives its own copy and starts reporting straight away. If you don’t want it to report, disable its Tax Report Setting (`enabled: false`) once created (see [The golden rule](#the-golden-rule)).
###### Restrictions:
[Section titled “Restrictions:”](#restrictions)
* A UO **cannot** carry `tin_value` or `tin_scheme`: it inherits them from the parent and they must not be repeated.
* A UO **cannot** be the parent of another UO: there is only one level of depth.
###### Fields required in every UO payload:
[Section titled “Fields required in every UO payload:”](#fields-required-in-every-uo-payload)
| Field | Value | Purpose |
| ---------------------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `parent_id` | Parent account id | Attaches the UO to the SIREN parent: this is what makes it a UO |
| `cin_scheme` and `cin_value` | Depending on the UO type | The UO’s own identifier; see [Creation fields and Annuaire result](#creation-fields-and-annuaire-result). A SIRET always starts with the 9 digits of the parent SIREN, by INSEE definition: if the parent is `123456789`, valid SIRETs take the form `123456789XXXXX` |
| `country` | `"fr"` | Enables FR validation and routing |
| `email`, `address`, `city`, `postalcode`, `province` | The UO’s contact details and address | Required on any account, UOs included |
Without these fields, creation returns **HTTP 422** (`"Region/Province/Country can't be blank"`).
#### Creation fields and Annuaire result
[Section titled “Creation fields and Annuaire result”](#creation-fields-and-annuaire-result)
| UO type | Fields to send to `POST /accounts` | Annuaire result |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| **Établissement** (Cases 1 and 4) | `parent_id` (SIREN parent) + `cin_scheme="0009"` + `cin_value=` | `SIREN_SIRET` |
| **Service** (Cases 2 and 5) | `parent_id` (SIREN parent) + `cin_scheme="0009"` + `cin_value=` + `routing_codes.cin1_scheme="224"` + `routing_codes.cin1_value=` | `SIREN_SIRET_` |
| **Suffix** (Cases 3 and 6) | `parent_id` (SIREN parent) + `cin_scheme="8040"` + `cin_value=` | `SIREN_` |
#### Payload examples
[Section titled “Payload examples”](#payload-examples)
**Cases 4, 5 and 6**: the payload does not distinguish them from their equivalents. What changes is where the Tax Report Setting is activated and, in Case 6, the `issue_only` flag. See [Scenarios and cases](#scenarios-and-cases).
##### Cases 1 and 2: Établissement UO with optional Service
[Section titled “Cases 1 and 2: Établissement UO with optional Service”](#cases-1-and-2-établissement-uo-with-optional-service)
> For an establishment without a Code Routage (Case 1), omit the `routing_codes` block.
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"account": {
"name": "Exemplar SAS, Paris Branch",
"parent_id": {PARENT_ACCOUNT_ID},
"cin_scheme": "0009",
"cin_value": "12345678900012",
"routing_codes": { "cin1_scheme": "224", "cin1_value": "COMPTA" },
"country": "fr",
"email": "paris-branch@example.com",
"address": "1 Rue de la Paix",
"city": "Paris",
"postalcode": "75001",
"province": "Île-de-France"
}
}'
```
##### Case 3: Suffix UO
[Section titled “Case 3: Suffix UO”](#case-3-suffix-uo)
> The payload for Case 6 (Issue-only) is identical to Case 3: what separates them is not the `POST /accounts` but the Tax Report Setting you activate afterwards, with `issue_only: true`. That flag is only accepted on a UO with `cin_scheme="8040"`. See [Tax Report Setting modes](#tax-report-setting-modes).
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"account": {
"name": "Exemplar SAS, Administrative Unit",
"parent_id": {PARENT_ACCOUNT_ID},
"cin_scheme": "8040",
"cin_value": "SUF01",
"country": "fr",
"email": "admin-unit@example.com",
"address": "1 Rue de la Paix",
"city": "Paris",
"postalcode": "75001",
"province": "Île-de-France"
}
}'
```
***
## Activate the Tax Report Setting
[Section titled “Activate the Tax Report Setting”](#activate-the-tax-report-setting)
Available from version 2026-03-02
This is the key onboarding step. When you create a Tax Report Setting with `code: "dgfip"`, B2Brouter automatically:
1. **Registers your company in the PPF’s Annuaire**: your identifier becomes discoverable by any platform in the French e-invoicing ecosystem.
2. **Creates a Peppol `0225 (FRCTC Electronic Address)` transport**: your company is enabled to receive electronic invoices from any Peppol-connected platform in France.\
⚠️ If the account you activate on already has a Peppol transport, it will be **replaced** by the new 0225 transport during activation.
> **Identifier already registered with another PA:** If the SIREN you activate on was previously registered with another PA, B2Brouter will close the existing Annuaire entry and open a new one. If you need to keep that existing registration, do **not** activate on the SIREN; activate on a SIRET UO instead (see “Where to activate” below). Update any existing integration that references the old transport identifier.
> **Where to activate the Tax Report Setting**: `{ACCOUNT_ID}` can be the **SIREN parent**, a **SIRET UO**, or **both**. Every account (parent or UO) that has its own Tax Report Setting is published in the Annuaire under its own address. For accounts in the French TVA territory (FR, and the DROM GP/MQ/RE) **there is no inheritance between UO and parent** (see [The golden rule](#the-golden-rule)): without its own Tax Report Setting the UO does not report, and with an own Tax Report Setting disabled it doesn’t either. Three configurations:
>
> 1. **Tax Report Setting on the SIREN only**: the company is published as `SIREN` (standard case, Scenario 1). New UOs under this parent automatically receive their own cloned Tax Report Setting on creation (see the note below), so they normally report too.
> 2. **Tax Report Setting on the SIREN and on one or more SIRET UOs**: the company is published both as `SIREN` and as each `SIREN_SIRET` (one Annuaire address per activated account). **All active DGFiP Tax Report Settings under the same SIREN must share the same `vat_regime`**: activating one with a different value returns `422 dgfip_vat_regime_taken_per_siren`.
> 3. **Tax Report Setting on a SIRET UO only, not on the SIREN**: only that establishment is published (`SIREN_SIRET`); the SIREN itself is not (Scenario 2). Use this when the SIREN is already registered with another PA and you want to keep that registration.
>
> See [Account structure](#account-structure-parent-and-organisational-units) for the full model.
> **If you create a UO while the parent is already active**: B2Brouter automatically creates its own DGFiP Tax Report Setting (with `start_date` tomorrow), so it starts reporting with no extra steps. If the parent doesn’t have DGFiP active at that moment, the UO is created without any Tax Report Setting; once you activate the parent later, the UO gets its own. The UO never looks at the parent’s setting to decide whether to report.
The `start_date` determines when tax reporting begins. From that date, invoices you issue will generate tax reports and be transmitted to the PPF.
**Example request:**
```shell
curl --request POST \
--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": "dgfip",
"start_date": "2026-09-01",
"type_operation": "services",
"naf_code": "62",
"enterprise_size": "eti",
"email": "jane.doe@example.com"
}
}'
```
**Sample response:**
```json
{
"tax_report_setting": {
"code": "dgfip",
"start_date": "2026-09-01",
"auto_generate": true,
"auto_send": true,
"enabled": true,
"type_operation": "services",
"naf_code": "62",
"enterprise_size": "eti",
"email": "jane.doe@example.com",
"locked": false,
"created_at": "2026-06-10T10:15:22.000Z",
"updated_at": "2026-06-10T10:15:22.000Z"
}
}
```
### DGFiP Tax Report Settings fields
[Section titled “DGFiP Tax Report Settings fields”](#dgfip-tax-report-settings-fields)
| Field | Type | Required | Description |
| ------------------- | ------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code` | string | Yes | Must be `"dgfip"`. |
| `start_date` | date | Yes | When tax reporting begins. Must be today or a future date. Defaults to **tomorrow** if omitted. |
| `type_operation` | string | Yes | Default operation type for this company: `"services"`, `"goods"`, or `"mixed"`. Determines the DGFiP process code (S1/B1 etc.) used in tax reports. Choose `"mixed"` if your company sells both goods and services. On a mixed invoice, the Flux 1 tax report is emitted under the **services** process code (`S1`/`S2`/`S4`/`S7`) and reports only the service breakdowns; the DGFiP has no dedicated “M” cadre for Flux 1 (see [Process codes](#process-codes)). This setting can be updated after activation. |
| `naf_code` | string | Yes | The company’s [NAF/APE code](https://www.insee.fr/fr/information/2120875) (Nomenclature d’Activités Française). This is the 2-digit section code assigned by INSEE that identifies the company’s main economic activity (e.g. `"62"` for IT and software services, `"47"` for retail, `"86"` for health). The DGFiP uses this code for tax reporting classification. You can find your NAF code on your company’s Kbis extract or on [sirene.fr](https://www.sirene.fr/). |
| `enterprise_size` | string | Yes | The company’s size category as defined by [INSEE](https://www.insee.fr/fr/metadonnees/definition/c1057). Allowed values: `"micro"` (Microentreprise — < 10 employees, CA ≤ 2 M€), `"pme"` (PME — 10 to 249 employees, CA ≤ 50 M€), `"eti"` (ETI — 250 to 4 999 employees, CA ≤ 1.5 Md€), or `"ge"` (Grande Entreprise — 5 000+ employees or CA > 1.5 Md€). |
| `vat_regime` | string | Yes, unless `annuaire_only=true` | The company’s TVA regime. Determines the Flux 10 (e-Reporting) transmission frequency. Without a valid `vat_regime`, no declarable invoices can be transmitted: the request returns **HTTP 422**. See the [regime table](#tva-regimes-and-transmission-frequency). |
| `reason_vat_exempt` | string | No | Default TVA exemption reason code for this company. Defaults to `"VATEX-FR-FRANCHISE"` (franchise en base de TVA). Set this if your company operates under a specific TVA exemption regime. See [TVA-exempt lines](#tva-exempt-lines-category-e) and [Franchise en base de TVA](#franchise-en-base-de-tva-vatex-fr-franchise) for the full list of accepted codes. |
| `email` | string | No | Contact email for tax-related notifications. |
| `auto_generate` | boolean | No | Always `true` for DGFiP (legal obligation). Cannot be changed. |
| `auto_send` | boolean | No | Automatically transmit tax reports to the PPF. Defaults to `true`. |
| `enabled` | boolean | No | Whether the setting is active. Defaults to `true`. Annuaire registration only occurs when `true`. |
| `annuaire_only` | boolean | No | When `true`, the account is only registered with the Annuaire of the PPF for reception. No Flux 1 tax reports are generated and no CDAR messages are sent; `enterprise_size` and `naf_code` become optional. Defaults to `false`. See [Tax Report Setting modes](#tax-report-setting-modes). |
#### TVA regimes and transmission frequency
[Section titled “TVA regimes and transmission frequency”](#tva-regimes-and-transmission-frequency)
| Regime | Description |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `reel_normal_mensuel` | Transactions every ten days (days 1-10, 11-20, 21-end of month); payments monthly. |
| `reel_normal_trimestriel` | Monthly. |
| `simplifie` | Monthly. Submission deadline: the 26th of the following month. Regime abolished on 2027-01-01; treated as `reel_normal_trimestriel` afterwards. |
| `franchise_en_base` | Bimonthly (calendar bimonths: Jan-Feb, Mar-Apr, May-Jun, Jul-Aug, Sep-Oct, Nov-Dec). Submission deadline: the 26th of the following month. |
The 26th is our own deadline
The 26th-of-the-month deadline for the `simplifie` and `franchise_en_base` regimes is a B2Brouter decision, not a DGFiP deadline: the deposit has to be made before the end of each month.
> If the Annuaire registration fails (invalid SIREN/SIRET, or a temporary DGFiP service outage), the Tax Report Setting creation is rolled back and an error is returned. Correct the issue and retry.
[Tax Report Settings - API Reference](https://developer.b2brouter.net/reference/create-tax-report-setting)
### Tax Report Setting modes
[Section titled “Tax Report Setting modes”](#tax-report-setting-modes)
If you followed the steps above, you already have the **full mode** and don’t need to read the rest of this section. The other two modes are deviations for specific situations.
| Mode | Where the Tax Report Setting lives | Annuaire | Peppol 0225 reception | Tax reports | Responses (CDAR) |
| -------------------------------------------------------------------------------------- | ---------------------------------- | -------- | ------------------------------ | ----------- | ---------------------------------------------- |
| **Full** (default) | Parent or UO | yes | yes | yes | sent and received |
| **[`annuaire_only`](#annuaire_only-receive-only)** | Parent or UO | yes | yes | no | none sent |
| **[`issue_only`](#issue_only-issue-from-b2brouter-while-reception-stays-where-it-is)** | Suffix UO only | yes | no (stays with the current PA) | yes | received if you enable `cdar` on the transport |
The send-only Peppol transport **is not a Tax Report Setting mode**: it is the issuing vehicle that accompanies `issue_only`, and you create it separately.
#### `annuaire_only`: receive only
[Section titled “annuaire\_only: receive only”](#annuaire_only-receive-only)
**When you need it:** your company is not required to issue yet, but you want to be listed at the Annuaire so suppliers can send you invoices.
It registers the company with the Annuaire and creates the 0225 transport for reception, but generates no tax reports and sends no CDAR messages. `enterprise_size` and `naf_code` are no longer required.
```shell
curl --request POST \
--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": "dgfip",
"start_date": "2026-09-01",
"annuaire_only": true,
"email": "jane.doe@example.com"
}
}'
```
When you want to start issuing, update the Tax Report Setting to full mode with a `PATCH`.
#### `issue_only`: issue from B2Brouter while reception stays where it is
[Section titled “issue\_only: issue from B2Brouter while reception stays where it is”](#issue_only-issue-from-b2brouter-while-reception-stays-where-it-is)
**When you need it:** you want to issue from a Suffix UO at B2Brouter while reception stays with the PA that already holds your SIREN. If you would rather bring it over, see [Change PA](#change-pa).
This flag is **only accepted on a Suffix UO** (`cin_scheme="8040"`) under the SIREN parent. Activating it on the parent returns `422 dgfip_issue_only_suffix_uo_only`.
Why only on a Suffix UO
A Suffix is not a receiving identifier for invoices: it hangs off a SIREN and cannot receive them at an address of its own, so the only coherent configuration is issuing without receiving. The parent and SIRET UOs are receiving identifiers and must keep receiving: if you could set the flag there, the account would be left with no address at which to receive its suppliers’ invoices.
On activation, B2Brouter **publishes your line at the PPF Annuaire** (the French administration’s directory that routes Flux 1) with the `SIREN_` address, but **creates no Peppol transport**. The line the other PA publishes for your SIREN stays intact: whatever you publish, you publish under `SIREN_`, which is a different identifier.
##### Enabling Peppol issuing
[Section titled “Enabling Peppol issuing”](#enabling-peppol-issuing)
You have to create the transport yourself for the Suffix UO, with the composed identifier and with **`cdar` as the only document type enabled**. Without a transport, the first send fails with `422 "To use Peppol Network you need to configure this connection to your account"`.
**Three fields are critical:**
* **`reception: true`** publishes the suffix to the SML. It is required, because CDARs arrive over Peppol and without publication they would not find you.
* **`cdar: true`, and no other document type.** This is what makes the mode issue-only: you receive the responses to the invoices you issue, but not invoices, which must keep arriving at your current PA.
* **`pin_value`** must be the `{parent SIREN}_{suffix}` composition with `pin_scheme: 225`, the same address B2Brouter has already published on the Annuaire line.
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{UO_ACCOUNT_ID}/transports \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"transport": {
"code": "peppol",
"enabled": true,
"reception": true,
"cdar": true,
"pin_scheme": 225,
"pin_value": "123456789_SUF01"
}
}'
```
The result is a transport that issues and receives CDARs only. The registration the other PA holds for your SIREN is untouched and keeps receiving your suppliers’ invoices.
Publishing the suffix does not take you off the other PA
CDARs arrive over Peppol, so the suffix has to be published to the SML for the responses to your invoices to find you. This does not affect the other PA: it publishes the `SIREN` and you publish `SIREN_`, which are different Peppol participants. What keeps you issue-only is not being unpublished, it is having only `cdar` enabled.
If you later want to receive through B2Brouter as well, disable `issue_only`.
***
## Change PA
[Section titled “Change PA”](#change-pa)
A Peppol identifier can only be active with one provider at a time, and the party that updates the Annuaire line is always **the incoming PA**, not the outgoing one.
There are two ways to move it:
* **With a migration code**, the automatic and recommended path, because it does not interrupt service. The code is always issued by the PA that currently publishes the identifier. At B2Brouter, whoever leaves can request one (see [Leaving B2Brouter](#leaving-b2brouter)) and whoever arrives can supply theirs (see [Coming to B2Brouter](#coming-to-b2brouter)).
* **Manually**, when the other PA offers no code: write to it asking it to contact the new PA to carry out the transfer. **Every French PA is required to offer this path**, so having no code does not leave you stuck.
### Unpublishing, deregistering and migrating
[Section titled “Unpublishing, deregistering and migrating”](#unpublishing-deregistering-and-migrating)
Three operations that often go by the same name and have different effects:
| Operation | Effect | Still registered in the SML? |
| --------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------- |
| Unpublish from the **Peppol Directory** | Removes the entry from the searchable catalogue | Yes: it keeps sending and receiving |
| Unpublish from **Peppol** | Removes the participant registration from the SML | No: this is the deregistration that frees the identifier |
| **Migrate** with a code | Transfers the registration to the new PA without deregistering | Yes, with no interruption |
Only the second one frees the identifier. As long as the SML registration exists, activating the Tax Report Setting on that identifier will keep failing with `422`, even if the other PA has unpublished the directory entry. This is the most common confusion: if they assure you they have unpublished you and activation keeps failing, ask them to confirm they removed the SML registration and not just the directory entry.
The check B2Brouter runs on activation is **a live DNS query against the SML**, not a query against a database of ours. So there is no guaranteed delay after a deregistration, because propagation depends on the SML and DNS, but you can retry the activation whenever you like and you will get the real state at that moment. A migration code avoids all of this, because it goes through no deregistration.
### Coming to B2Brouter
[Section titled “Coming to B2Brouter”](#coming-to-b2brouter)
Activating the Tax Report Setting on an identifier published by another provider fails with **422**. Repeat the same call adding the code that provider gave you:
* [`POST /tax_report_settings`](https://developer.b2brouter.net/reference/create-tax-report-setting) with `peppol_migration_code`. This is the main route for France: it moves the identifier as part of the same activation.
* [`POST /transports`](https://developer.b2brouter.net/reference/create-transport) or `PUT /transports/{code}` with `migration_code`, if you are creating the transport directly.
Both fields are **write-only** and never appear in a `GET`. If the code doesn’t work, the call fails with `migration_code_malformed`, `migration_code_not_applicable` or `migration_code_rejected`, the latter carrying the real SML reason. No partial state is left behind: if the migration fails, the transport is not created.
### Leaving B2Brouter
[Section titled “Leaving B2Brouter”](#leaving-b2brouter)
[`POST /accounts/{ACCOUNT_ID}/transports/peppol/migration_code`](https://developer.b2brouter.net/reference/create-peppol-migration-code) returns the code to hand to the gaining provider. It is idempotent: repeating the call returns the same code until you cancel it with [`DELETE`](https://developer.b2brouter.net/reference/delete-peppol-migration-code) on the same path, and a later `POST` issues a new one.
It returns **409 `not_published_by_b2brouter`** if the transport is send-only, since the identifier is then not published by us.
The read-only `migration_pending` field on transport `GET`s tells you whether a code has been issued and not yet cancelled. The code itself is never exposed in any `GET`.
Limitations
* In **sandbox** migration codes are unavailable: the call returns `409 migration_unavailable`.
* Countries with a centralised SMP (Singapore, Malaysia) return `409`.
* Edit permission on transports is required.
***
## Create a contact
[Section titled “Create a contact”](#create-a-contact)
A French B2B contact needs **routing identifiers** (how the invoice reaches the recipient) and **tax identification** (how the recipient appears in the UBL XML).
| Field | Value | Purpose |
| --------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cin_scheme` | `"0002"` (SIREN, default) or `"0009"` (SIRET, to address a specific establishment) | Organisation ID: used to look up the recipient in the Annuaire |
| `cin_value` | SIREN or SIRET | Organisation ID: the identifier registered in the Annuaire |
| `tin_scheme` | `9957` | Tax ID: ISO 6523 code for French fiscal identifier |
| `tin_value` | `FR{kk}{siren}` (e.g. `"FR78225214234"`) | Tax ID: French TVA number in the UBL XML |
| `pin_scheme` | `"0225"` | EAS Peppol of the FR participant (FRCTC Electronic Address) — **required** for French contacts |
| `pin_value` | Recipient’s Annuaire composition | Contact’s Peppol identifier; follows the same composition as the parent at the Annuaire (`SIREN`, `SIREN_SIRET`, `SIREN_SIRET_` or `SIREN_` — see [Identifier levels](#identifiers-and-schemes)) |
| `country` | `"fr"` | Required for DGFiP routing logic |
| `currency` | `"EUR"` | Default currency for this contact’s invoices |
| `transport_type_code` | `"peppol"` | **Recommended**: ensures delivery via the Peppol network |
| `document_type_code` | `"xml.ubl.invoice.frcius.v1"` | **Recommended**: France CIUS UBL invoice format |
> **Transport and document type**: For French contacts registered in the Annuaire, we recommend explicitly setting `transport_type_code: "peppol"` and `document_type_code: "xml.ubl.invoice.frcius.v1"`. Without `pin_value`, creation returns **HTTP 422** (“Peppol Endpoint ID can’t be blank”). You can verify that a contact is registered in the Annuaire before creating it by using the [Directory lookup](#optional-verify-recipient-routing-directory-lookup) or the [official Peppol Directory](https://directory.peppol.eu/).
> **Contacts in Belgium, Germany, and other EU countries**: For B2B invoices to non-French companies, use their national identifier scheme (e.g. `"0208"` for Belgian KBO/BCE, `"0190"` for German Leitweg-ID, `"0184"` for Dutch KVK) and the appropriate `country` code. If the recipient has an active Peppol access point, B2Brouter routes the invoice via standard Peppol BIS 3.0 — no configuration needed. For transactions with any non-`"fr"` contact, Flux 10 cross-border e-Reporting is generated automatically.
**Example request:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"contact": {
"name": "Client Exemple SARL",
"address": "25 Avenue de la République",
"city": "Lyon",
"postalcode": "69001",
"country": "fr",
"currency": "EUR",
"language": "fr",
"cin_scheme": "0002",
"cin_value": "987654321",
"tin_scheme": 9957,
"tin_value": "FR05987654321",
"pin_scheme": "0225",
"pin_value": "987654321",
"transport_type_code": "peppol",
"document_type_code": "xml.ubl.invoice.frcius.v1"
}
}'
```
[POST Contact - API Reference](https://developer.b2brouter.net/reference/create-contact)
> **Addressing a specific establishment**: if the client has several establishments and you need to identify one in particular, use `cin_scheme: "0009"` with the 14-digit SIRET and set `pin_value` to the `SIREN_SIRET` composition (e.g. `987654321_98765432100011`) instead of the bare SIREN.
> **Multi-level contacts**: if your client has several establishments or services that need separate identifiers, create the parent contact first at SIREN level and then add UOs with `parent_id`. See [Account structure](#account-structure-parent-and-organisational-units) for the golden rule and applicable structures.
### Alternative: inline contact on the invoice (without the contacts module)
[Section titled “Alternative: inline contact on the invoice (without the contacts module)”](#alternative-inline-contact-on-the-invoice-without-the-contacts-module)
Instead of creating a contact separately and referencing it with `contact_id`, you can send an inline `contact` object directly inside `POST /accounts/{ACCOUNT_ID}/invoices`, with all of the recipient’s data:
```json
{
"invoice": {
"type": "IssuedInvoice",
"contact": {
"name": "Client Exemple SARL",
"country": "fr",
"currency": "EUR",
"language": "fr",
"cin_scheme": "0009",
"cin_value": "98765432100011",
"tin_scheme": 9957,
"tin_value": "FR05987654321",
"pin_scheme": "0225",
"pin_value": "987654321_98765432100011",
"transport_type_code": "peppol",
"document_type_code": "xml.ubl.invoice.frcius.v1"
}
}
}
```
> ⚠️ **Key warning**: always pass the correct identifiers (`tin` / `cin` / `pin`) with the correct FR composition (SIREN parent + UO), to avoid breaking parent↔UO consistency (see [The golden rule](#the-golden-rule)). From API version `2026-04-20`, the system **rejects (422)** a non-simplified inline contact that provides neither `tin_value` nor `cin_value`. `currency` and `language` are mandatory on any contact; without them the creation fails regardless. The inline contact also accepts `routing_codes.cin1_scheme` / `routing_codes.cin1_value` to identify a service inside a SIRET (Case 2), with the same values (`"224"` and the Code Routage) as a contact created through the contacts module.
### Optional: Verify recipient routing (Directory lookup)
[Section titled “Optional: Verify recipient routing (Directory lookup)”](#optional-verify-recipient-routing-directory-lookup)
B2Brouter routes invoices automatically. To inspect how a recipient will be routed before sending — for example to confirm they are registered in the Annuaire — use the Directory lookup (Flux 11):
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/directory/fr/0009/98765432100011 \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
Asynchronous response available from version 2026-06-26
For French contacts the first call may return **HTTP 202** with a `polling_url`. Repeat the request to that URL until you get HTTP 200 with the resolved data.
**Resolved response: example:**
```json
{
"external_company": {
"country": "fr",
"cin_scheme": "0009",
"cin_value": "98765432100011",
"information_flags": ["FR_ASSUJETTI_ACTIVE"],
"source": ["peppol", "annuaire"]
}
}
```
**France-specific fields**:
| Field | Values | Meaning |
| ------------------- | --------------------------------- | ------------------------------------------------------------------------------------ |
| `information_flags` | `FR_ASSUJETTI_ACTIVE` | Recipient is registered and active at the PPF Annuaire |
| `information_flags` | `FR_ASSUJETTI_INACTIVE` | Recipient has an out-of-date registration or no assigned PA |
| `information_flags` | `FR_ASSUJETTI_UNKNOWN` | The PPF has no conclusive information (e.g. 404) |
| `source` | array of `"peppol"`, `"annuaire"` | Sources consulted. If both appear, B2Brouter found the recipient in both directories |
> This lookup is **informational**: B2Brouter already resolves routing automatically when creating an invoice (see [Annuaire verification and tax report generation](#annuaire-verification-and-tax-report-generation)). Use it to inspect before sending or to display the recipient’s status in your UI.
[GET Lookup Directory - API Reference](https://developer.b2brouter.net/reference/lookup-directory-by-country-and-scheme)
### Annuaire verification and tax report generation
[Section titled “Annuaire verification and tax report generation”](#annuaire-verification-and-tax-report-generation)
B2Brouter automatically verifies whether French contacts (`country: "fr"`) are registered in the DGFiP Annuaire. This verification determines the tax reporting flow:
* **Contact registered in Annuaire** (`in_dgfip_annuaire: true` or not yet verified): the invoice generates a **Flux 1** tax report (domestic B2B e-invoicing).
* **Contact NOT registered in Annuaire** (`in_dgfip_annuaire: false`): the invoice **does not generate a tax report**. This prevents invalid Flux 1 submissions for recipients that the PPF cannot route to.
* **Contact not yet verified** (`in_dgfip_annuaire: nil`): B2Brouter is permissive — the invoice proceeds and generates a tax report. Verification happens asynchronously in the background.
> If you create a French contact and immediately send an invoice, the Annuaire verification may not have completed yet. This is by design — B2Brouter does not block invoice creation while verification is pending. If the contact turns out to be unregistered, future invoices to that contact will not generate Flux 1 tax reports until the contact registers with a PA.
> The Annuaire check only applies to **French domestic contacts** (`country: "fr"`). Non-French contacts always follow the Flux 10 e-Reporting path regardless of Annuaire status.
> **Important: both companies must be in the DGFiP Annuaire**: for an FR → FR sending with a **Flux 1** tax report, both the **issuer** and the **receiver** must be registered in the DGFiP Annuaire. If one of them is not, the tax report cannot complete and the invoice is sent **without a tax declaration**.
>
> How to check DGFiP Annuaire registration:
>
> * **Production** → [facturation.chorus-pro.gouv.fr/annuaire](https://facturation.chorus-pro.gouv.fr/annuaire/#/)
> * **Staging (QAS)** → [qas.facturation.chorus-pro.gouv.fr/annuaire](https://qas.facturation.chorus-pro.gouv.fr/annuaire/#/)
>
> ⚠️ The `annuaire-entreprises.data.gouv.fr` website is for general fiscal data; it **does not guarantee DGFiP Annuaire registration** — they are different databases.
>
> From **September 2026** this permissive behaviour will stop applying: tax declarations will simply **fail** if the identifier is not listed as DGFiP-activable, even when the SIREN/SIRET is valid.
***
## Issue invoices
[Section titled “Issue invoices”](#issue-invoices)
Once your company is onboarded and the DGFiP Tax Report Setting is enabled, creating invoices works through the standard Invoice API. B2Brouter handles all France-specific requirements automatically: tax report generation, UBL/CII/Factur-X formatting, and PPF transmission via Flux 1.
Use `send_after_import: true` to create and transmit in one step. Set it to `false` if you want to create the invoice first and review it before sending — in that case, the invoice stays in `new` state until you trigger transmission via a separate call or through the B2Brouter UI.
> **Invoice date**: for accounts with an active DGFiP Tax Report Setting, the invoice `date` cannot be a **future date**. A future date returns **HTTP 422** (“Only invoices with today’s date are allowed”). The dates in the examples below are illustrative; replace them with the current date when testing.
> **Invoice number**: the invoice `number` is limited to **20 characters** maximum. A longer number is rejected with **HTTP 422** (`number` too long).
> **Sequential invoicing**: The API processes one invoice per request. For bulk or batch scenarios, iterate through your invoice list and call the endpoint for each document. The API supports concurrent requests — you can parallelise multiple POSTs without waiting for each response before starting the next.
### Standard B2B invoice
[Section titled “Standard B2B invoice”](#standard-b2b-invoice)
The most common case: a domestic French B2B invoice with standard TVA (20%).
```shell
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": {
"type": "IssuedInvoice",
"contact_id": 1313228381,
"number": "FA-2026-0048",
"date": "2026-09-15",
"due_date": "2026-10-15",
"currency": "EUR",
"payment_method": 4,
"bank_account_id": {YOUR_BANK_ACCOUNT_ID},
"remittance_information": "FA-2026-0048 — Exemplar SAS, SAS au capital de 50 000 EUR, RCS Paris 123 456 789",
"payment_method_text": "Virement bancaire",
"payment_terms": "Net 30 jours à compter de la date de facture. Pénalité de retard : 12% annuel.",
"invoice_lines_attributes": [
{
"quantity": 10.0,
"description": "Consulting services — architecture review",
"price": 150.0,
"unit": 9,
"taxes_attributes": [
{ "name": "TVA", "percent": 20.0, "category": "S" }
]
},
{
"quantity": 5.0,
"description": "Training sessions — API integration",
"price": 200.0,
"unit": 9,
"taxes_attributes": [
{ "name": "TVA", "percent": 20.0, "category": "S" }
]
}
]
}
}'
```
The `tax_report_ids` array in the response contains the ID of the generated tax report. Use it to track the submission lifecycle (see [Check the state of a Tax Report](#check-the-state-of-a-tax-report)).
[POST Invoice - API Reference](https://developer.b2brouter.net/reference/create-invoice)
### France-specific invoice fields
[Section titled “France-specific invoice fields”](#france-specific-invoice-fields)
#### Payment information (required native fields)
[Section titled “Payment information (required native fields)”](#payment-information-required-native-fields)
Three payment fields are mandatory for French B2B e-invoices (`IssuedInvoice`). Provide them as direct API fields:
| Field | DGFiP field | Description | Required |
| ------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| `remittance_information` | PMD | Payment reference and legal mentions (company registration, share capital, RCS) | **Yes: B2B domestic and cross-border** |
| `payment_method_text` | PMT | Textual description of the payment method | **Yes: B2B domestic and cross-border** |
| `payment_terms` | AAB | Due date, late payment penalties, discount conditions | **Yes: B2B domestic and cross-border** |
| `bank_account_id` | — | Id of the bank account associated with your B2Brouter company. **Required** when `payment_method` is bank transfer (code `4`) | **Yes, for bank transfer payments** |
> If any required field is missing, the request returns **HTTP 422** with explicit error messages for each absent field. These are not silent — the invoice is never created. Correct the missing fields and resubmit.
> **Bank account**: create the bank account associated with your company first (from the web UI or via API), and reference its id in the invoice with `bank_account_id`. The bank account holds IBAN and BIC and is the canonical source — do not pass them as free text inside `payment_method_text`.
> **B2C invoices** (`IssuedSimplifiedInvoice`) do not require these fields.
> **Importing from other formats (UBL, CII, Factur-X)**: For these integrations, use the `extra_info` field with structured tags:
>
> ```plaintext
> #PMD# FA-2026-0048 — Exemplar SAS, RCS Paris 123 456 789
> #PMT# Credit Transfer, IBAN FR00 0000 0000 0000 0000 0000 000, BIC XXXXFRPP
> #AAB# Net 30 jours. Pénalité de retard : 12% annuel.
> ```
>
> B2Brouter extracts these tags and maps them to the corresponding native fields automatically.
#### TVA-exempt lines (category E)
[Section titled “TVA-exempt lines (category E)”](#tva-exempt-lines-category-e)
When a line has `category: "E"` (TVA-exempt), you must also provide a `comment` with the applicable `VATEX-FR-CGI261-*` exemption code (DGFiP BT-121):
> ⚠️ **Silent blocking**: If `comment` is omitted on a category E line, the invoice is **created** (HTTP 200) but **never transmitted to the PPF**. The response will contain a non-empty `errors` array — check it even on successful responses.
**DGFiP TVA exemption codes (BT-121)**
| Code | Legal reference | Tax category | Description |
| -------------------------- | --------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `VATEX-FR-FRANCHISE` | Art. 293 B CGI | Z (zero-rate) | Franchise en base de TVA |
| `VATEX-FR-CNWVAT` | — | E (exempt) | Avoir net de taxe — credit note without TVA (supplier waives the TVA adjustment). Credit notes only (261/381/396, rule G6.21) |
| `VATEX-FR-AE` | — | E (exempt) | Autoliquidation (reverse charge) |
| `VATEX-FR-CGI261-1` | Art. 261-1° CGI | E (exempt) | Soins et services médicaux |
| `VATEX-FR-CGI261-2` | Art. 261-2° CGI | E (exempt) | Services paramédicaux |
| `VATEX-FR-CGI261-3` | Art. 261-3° CGI | E (exempt) | Enseignement scolaire, universitaire et formation professionnelle |
| `VATEX-EU-F` / `-I` / `-J` | — | E (exempt) | Régime de la marge (margin scheme) |
> **EU-level exemption codes**: besides the `VATEX-FR-*` national codes above, the DGFiP also accepts the `VATEX-EU-*` codelist (UNCL5305 / EN16931 extensions). The **régime de la marge** (margin scheme) uses `VATEX-EU-F`, `VATEX-EU-I` or `VATEX-EU-J` on a category `E` line. Provide the applicable code in the line `comment`.
#### Franchise en base de TVA (VATEX-FR-FRANCHISE)
[Section titled “Franchise en base de TVA (VATEX-FR-FRANCHISE)”](#franchise-en-base-de-tva-vatex-fr-franchise)
Companies operating under the **franchise en base de TVA** regime operate at **zero-rate** (tax category `Z`), not exempt (category `E`). For franchise invoices, set `percent: 0.0` and `category: "E"` with `comment: "VATEX-FR-FRANCHISE"` on each tax line — B2Brouter transcodes the category to `Z` automatically:
```json
{
"taxes_attributes": [
{
"name": "TVA",
"percent": 0.0,
"category": "E",
"comment": "VATEX-FR-FRANCHISE"
}
]
}
```
#### Out-of-scope operations (category O)
[Section titled “Out-of-scope operations (category O)”](#out-of-scope-operations-category-o)
Some operations fall **outside the scope of TVA** and must carry tax **category `O`** with `percent: 0.0`, not category `E` (exempt) or `Z` (zero-rate). Two common cases:
* **Détaxe** (operations outside the TVA field): the line carries no TVA.
* **Débours** (reimbursable expenses advanced on behalf of the client): passed through without TVA.
```json
{
"taxes_attributes": [
{ "name": "TVA", "percent": 0.0, "category": "O" }
]
}
```
The Flux 1 tax report synthesises the corresponding category `O` TaxSubtotal automatically. The `NS` category is also accepted and transcoded to `O`.
### Credit note
[Section titled “Credit note”](#credit-note)
From API version `2026-04-20`, reference the original invoice with `invoice_references` to issue a credit note:
```json
"invoice_references": [
{
"reference_type": "amend",
"number": "FA-2026-0048",
"date": "2026-09-15",
"reason": "Annule et remplace",
"correction_method": "01"
}
]
```
B2Brouter generates a UBL CreditNote with type code `381` and includes the billing reference.
```shell
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": {
"type": "IssuedInvoice",
"contact_id": 1313228381,
"number": "NC-2026-001",
"date": "2026-09-25",
"due_date": "2026-10-25",
"is_credit_note": true,
"invoice_references": [
{
"reference_type": "amend",
"number": "FA-2026-0048",
"date": "2026-09-15",
"reason": "Annule et remplace",
"correction_method": "01"
}
],
"currency": "EUR",
"payment_method": 4,
"bank_account_id": {YOUR_BANK_ACCOUNT_ID},
"remittance_information": "NC-2026-001 — annule et remplace FA-2026-0048. Exemplar SAS",
"payment_method_text": "Virement bancaire",
"payment_terms": "Remboursement sous 30 jours.",
"invoice_lines_attributes": [
{
"quantity": -1.0,
"description": "Annulation partielle — Consulting services",
"price": 500.0,
"unit": 9,
"taxes_attributes": [
{ "name": "TVA", "percent": 20.0, "category": "S" }
]
}
]
}
}'
```
> **Legacy fields**: in API versions prior to `2026-04-20`, a credit note was expressed with `is_amend: true` + `amended_number` + `amended_date` instead of `invoice_references`. These fields remain accepted in those versions for backward compatibility, but are now legacy: use `invoice_references` for new integrations.
***
### Process codes
[Section titled “Process codes”](#process-codes)
The process code determines the PPF flux cadre. B2Brouter assigns it automatically based on `type_operation` in the Tax Report Setting and the invoice characteristics.
| Process Code | Type of Operation | Description |
| ------------ | ----------------- | --------------------------------------------- |
| S1 | Services | Standard invoice for services |
| B1 | Goods | Standard invoice for goods |
| M1 | Mixed | Standard invoice for mixed operations |
| S2 | Services | Paid invoice for services |
| B2 | Goods | Paid invoice for goods |
| M2 | Mixed | Paid invoice for mixed operations |
| S4 | Services | Invoice with payments on account (services) |
| B4 | Goods | Invoice with payments on account (goods) |
| M4 | Mixed | Invoice with payments on account (mixed) |
| S7 | Services | Correction of a registered invoice (services) |
| B7 | Goods | Correction of a registered invoice (goods) |
> **No “M” cadre in Flux 1**: although `type_operation` accepts `"mixed"`, the Flux 1 tax report never carries an M-series process code. A mixed invoice resolves to the corresponding **services** code (`S1`/`S2`/`S4`/`S7`) and reports only its service breakdowns. The M rows above are listed for reference but are not produced by Flux 1.
### Document formats
[Section titled “Document formats”](#document-formats)
| Document Type Code | Format | Description |
| ----------------------------------------------------------- | ------------------ | ---------------------------------------------- |
| `xml.ubl.invoice.frcius.v1` | UBL XML | France CIUS Peppol invoice (Flux 1 / Annuaire) |
| `xml.cii.cross_industry_invoice.frcius.v1` | CII XML | France CIUS CII invoice |
| `xml.cii.cross_industry_invoice.facturx.fr.all_profiles.v1` | Factur-X (CII XML) | France CIUS Factur-X invoice (all profiles) |
### Importing Factur-X and UBL XML
[Section titled “Importing Factur-X and UBL XML”](#importing-factur-x-and-ubl-xml)
If your system already generates Factur-X or UBL XML invoices, submit them directly using the document import endpoint:
```plaintext
POST /accounts/{id}/invoices/import
```
Set `Content-Type` to:
* `application/pdf`: for Factur-X (PDF/A-3 with embedded CII XML)
* `application/xml`: for standalone UBL 2.1 or CII XML
***
## Invoice Lifecycle
[Section titled “Invoice Lifecycle”](#invoice-lifecycle)
> **France uses a Y-/5-corner model, not clearance.** The invoice is transmitted to the buyer regardless of whether a Flux 1 tax report is registered; the PPF does not pre-clear invoices before they reach the buyer. The Invoice and the Tax Report are **separate lifecycles** that only coincide at the Flux 1 delivery. A tax report error does not undo an invoice that has already reached the buyer.
### Invoice states
[Section titled “Invoice states”](#invoice-states)
| State | DGFiP CDV | Description |
| ---------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| sending | — | The invoice has been created and is queued for PPF transmission. |
| sent | `200: Déposée` | The invoice has been successfully deposited at the PPF. |
| registered | `202: Reçue` | The PPF has validated and forwarded the invoice to the buyer. |
| accepted | `205: Approuvée` | The buyer has approved the invoice. |
| refused | `210: Refusée` | The buyer has rejected the invoice. |
| paid | `212: Encaissée` | The invoice payment has been confirmed. |
| error | — | An error occurred during transmission or PPF validation. The `errors` field in the invoice response contains the PPF rejection reason. If the invoice was rejected **before registration** (state `sending` or `sent`), delete it, correct the issue, and submit a new one; errored invoices cannot be retransmitted directly. A **registered** invoice (CDV `202` or later) cannot be deleted and resubmitted: it is already in the buyer’s lifecycle. Correct it with a credit note or a correction (process code `S7`/`B7`), or annul it. |
### Tracking invoice states
[Section titled “Tracking invoice states”](#tracking-invoice-states)
#### Webhooks (recommended)
[Section titled “Webhooks (recommended)”](#webhooks-recommended)
Configure webhook endpoints in the B2Brouter UI under **Settings → Webhooks**. Once configured, B2Brouter sends an HTTP POST to your endpoint each time the invoice reaches a new state.
[Invoice Status WebHooks - API Reference](https://developer.b2brouter.net/reference/getnewinvoicestatechange)
#### Polling
[Section titled “Polling”](#polling)
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Accept: application/json'
```
[GET Invoice - API Reference](https://developer.b2brouter.net/reference/get-invoice)
### Download the original Invoice document
[Section titled “Download the original Invoice document”](#download-the-original-invoice-document)
After an invoice reaches `sent` state, the `GET /invoices/{id}` response includes a `download_legal_url` field. Use it to download the invoice document that was transmitted:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net{download_legal_url} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
As a one-step alternative, you can call `GET /invoices/{id}/as/legal` directly, without first fetching `download_legal_url` from the invoice payload. It returns the archived legal document as stored and does not generate a billable transaction — see [Download invoices](/en/developers/essential-guides/download-invoice/) and [Transaction: View\_as](/en/developers/essential-guides/transaction/#view_as).
### Encaissée state: payment confirmation
[Section titled “Encaissée state: payment confirmation”](#encaissée-state-payment-confirmation)
Encaissée is the **last state of the CDV cycle** of a French B2B invoice, right after the buyer’s approval. It indicates that payment has been confirmed (see the full CDV mapping in the [Invoice states](#invoice-states) table).
B2Brouter propagates the payment confirmation to the PPF via one of two paths, depending on the invoice nature:
| Case | Path | Mechanism |
| ------------------------------------------------------------------------------------------------ | --------------------- | ----------------------------------------------------------------------------------------------- |
| **Domestic FR → FR** (B2Brouter detects `company.country == "fr"` and `contact.country == "fr"`) | CDAR | Sends a CDV `212` message (state transition) without generating an additional tax report |
| **Cross-border** (B2B intra-EU / extra-EU) or **B2C** (`IssuedSimplifiedInvoice`) | Flux 10 payments file | Generates a tax report in Ledger C (payments) (see [Three ledgers model](#three-ledgers-model)) |
#### Mark an invoice as paid via API
[Section titled “Mark an invoice as paid via API”](#mark-an-invoice-as-paid-via-api)
When payment is confirmed, mark the invoice at the PPF with a `mark_as` call:
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/mark_as \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{"state":"paid"}'
```
#### Partial payments
[Section titled “Partial payments”](#partial-payments)
Partial payments are recorded with the `POST /accounts/{ACCOUNT_ID}/payments` endpoint, passing `amount` and `invoice_id`. Repeat the call for each payment you receive; once the sum of recorded amounts covers the invoice total, the invoice automatically transitions to `paid`.
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/payments \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"payment": {
"invoice_id": {INVOICE_ID},
"amount": 900.00
}
}'
```
> Requires `X-B2B-API-Version: 2026-03-02` or later.
For a single full payment, you can also call `mark_as` directly with `state: "paid"` (see [Mark an invoice as paid via API](#mark-an-invoice-as-paid-via-api)); there is no need to go through `POST /accounts/{ACCOUNT_ID}/payments` first.
Alternatively, handle payments from the **B2Brouter web app**: open the invoice → **Create payment** → enter the paid amount.
> **Payment notes**:
>
> * You can pass `full: true` instead of `amount` to register the full outstanding payment: B2Brouter calculates the pending amount at the time the call executes and never exceeds the invoice total. If you pass an explicit `amount` instead, there is no overpayment guard: it is your responsibility not to exceed the outstanding total.
> * Deleting a payment does not reopen the invoice: it stays in the `paid` state.
> * If the payment generated a Flux 10 tax report (cross-border or B2C invoice), a `PUT`/`DELETE` on that payment returns **HTTP 422** because the already-submitted declaration is locked. The domestic FR-FR case (CDV `212`, no additional tax report) has no such lock.
***
## Check the state of a Tax Report
[Section titled “Check the state of a Tax Report”](#check-the-state-of-a-tax-report)
The tax report tracks the technical lifecycle of the submission to the PPF. Both XML generation and PPF transmission are asynchronous.
**Flux 1: Domestic B2B invoices:**
| State | Description |
| -------------- | -------------------------------------------------------------------------------- |
| `new` | Tax report created, queued for transmission |
| `sent` | Document deposited to PPF via SFTP |
| `acknowledged` | PPF has received and validated the file (CDV condition 500: Reçue) |
| `registered` | ✅ **Terminal**: Invoice accepted and registered by the DGFiP (CDV condition 300) |
| `refused` | ❌ **Terminal**: Rejected by the DGFiP (CDV condition 301) |
| `error` | ❌ **Terminal**: Transmission or PPF processing error |
| `annulled` | Invoice has been annulled after registration |
**Flux 10: Cross-border B2B and B2C invoices (e-Reporting):**
| State | Description |
| -------------- | ------------------------------------------------------------------------------- |
| `new` | Tax report created, accumulated for the daily ledger batch |
| `sent` | Ledger deposited to PPF via SFTP |
| `acknowledged` | PPF has received the ledger (CDV condition 500: Reçue) |
| `registered` | ✅ **Terminal**: Ledger accepted and registered by the DGFiP (CDV condition 300) |
| `refused` | ❌ **Terminal**: Rejected by the DGFiP (CDV condition 301) |
| `error` | ❌ **Terminal**: Transmission or PPF processing error |
#### Polling
[Section titled “Polling”](#polling-1)
Query the state using the tax report ID from `tax_report_ids` in the invoice response:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/tax_reports/{TAX_REPORT_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
[GET Tax Report - API Reference](https://developer.b2brouter.net/reference/get-tax-report)
***
## e-Reporting (Flux 10)
[Section titled “e-Reporting (Flux 10)”](#e-reporting-flux-10)
Flux 10 covers transactions outside the scope of the domestic B2B e-invoicing obligation that must still be reported to the DGFiP. B2Brouter handles Flux 10 automatically.
> If you already generate your own F10 `` and only need an accredited channel to deposit it, see [Submit a Flux-10 e-reporting file](/en/developers/use-cases-by-country/submit-flux10-ereporting-file/) instead.
### B2C transactions
[Section titled “B2C transactions”](#b2c-transactions)
Sales to non-TVA-registered individuals in France. Use `"type": "IssuedSimplifiedInvoice"`. The `contact_id` and payment fields are **not required** for B2C invoices.
### Cross-border transactions (B2B intra-EU and extra-EU)
[Section titled “Cross-border transactions (B2B intra-EU and extra-EU)”](#cross-border-transactions-b2b-intra-eu-and-extra-eu)
Sales to or purchases from companies established outside France must be reported via Flux 10. Use the standard `IssuedInvoice` or `ReceivedInvoice` type and set the counterparty’s `country` to the relevant non-`"fr"` value. B2Brouter detects the cross-border nature automatically.
> **French overseas territories**: the DROM départements **Guadeloupe (`gp`), Martinique (`mq`) and La Réunion (`re`)** are treated as **domestic France**. Invoices to contacts there generate a **Flux 1** tax report and carry `IdentificationCode FR`, exactly like mainland France. The other overseas territories (Guyane `gf`, Mayotte `yt`, Polynésie française `pf`, Nouvelle-Calédonie `nc`, TAAF `tf`, Saint-Pierre-et-Miquelon `pm`) fall outside the French TVA territory and follow the **Flux 10** e-Reporting path.
### Flux 10 Ledgers
[Section titled “Flux 10 Ledgers”](#flux-10-ledgers)
B2Brouter groups Flux 10 tax reports into **Ledgers** sent to the PPF. The sending frequency is **not always daily**: it depends on the `vat_regime` configured on the Tax Report Setting (see [DGFiP Tax Report Settings fields](#dgfip-tax-report-settings-fields)), every ten days or monthly depending on the regime, or bimonthly for `franchise_en_base`. In **staging**, ledgers are deposited daily to make testing easier; in **production**, they follow the regime’s real periodicity. You can identify Flux 10 tax reports by a non-null `ledger_id` in the tax report response.
#### Three ledgers model
[Section titled “Three ledgers model”](#three-ledgers-model)
Each ledger is identified by two internal dimensions:
* **DGFiP role** (`SE` or `BY`):
* `SE` (Seller / émission): for sales you issue.
* `BY` (Buyer / réception): for intracommunity purchases you declare as the buyer.
* **Mode** (`transactions` or `payments`):
* `transactions`: invoice ledgers (process codes S1 / B1 / M1), document type `xml.ledger.dgfip.transactions`.
* `payments`: payment ledgers (process codes S2 / B2 / M2, TVA on services accrues at collection), document type `xml.ledger.dgfip.payments`.
The combination of roles and modes yields **up to 3 ledgers** per account and reporting period:
| Ledger | Role | Mode | Content |
| ------------ | ---- | -------------- | ------------------------------------------------------------------------------ |
| A: Émission | `SE` | `transactions` | Issued invoices (B2C and cross-border B2B) |
| B: Réception | `BY` | `transactions` | Intracommunity / extra-EU purchases declared as the buyer |
| C: Payments | `SE` | `payments` | Confirmed payments of your issued invoices (cross-border / B2C Encaissée case) |
> The pair **(BY, payments) does not exist**: payment information for your purchases is generated by the seller on their side; you do not declare it.
Each tax report links to a single `ledger_id` (it cannot belong to more than one ledger at a time). Grouping happens per account + reporting period + (role, mode) under a lock to avoid duplicates.
***
## Receiving Invoices
[Section titled “Receiving Invoices”](#receiving-invoices)
As a French company registered in the Annuaire with a Peppol 0225 transport, you automatically receive electronic invoices from other French and Peppol-connected platforms.
### Updating received invoice state
[Section titled “Updating received invoice state”](#updating-received-invoice-state)
When you receive an invoice, report its status to the PPF by updating the invoice state:
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/mark_as \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{"state":"accepted"}'
```
Valid target states for received invoices: `accepted` (205 Approuvée), `refused` (210 Refusée).
[Switch invoice state - API Reference](https://developer.b2brouter.net/reference/mark-as-invoice)
***
## Related guides
[Section titled “Related guides”](#related-guides)
* [Send invoices to Chorus Pro (B2G)](/en/developers/use-cases-by-country/send-invoices-to-chorus-pro/): circuit for invoices addressed to the French **public sector**.
***
## Further resources
[Section titled “Further resources”](#further-resources)
* [API Reference](https://developer.b2brouter.net/reference)
* [Tax Report Settings Guide](https://developer.b2brouter.net/docs/tax_report_settings_guide)
* [Support](https://www.b2brouter.net/docs/#/en/support/open-incident-report)
* [DGFiP: French e-invoicing reform](https://www.impots.gouv.fr/facturation-electronique-702)
# Supported Use Cases
> Overview of the 44 French e-invoicing reform use cases (XP Z12-014) and their support status in B2Brouter.
The French e-invoicing reform defines a set of specific business use cases that Accredited Platforms (Plateformes Agréées) may be required — or may choose — to support. These cases cover a wide range of invoicing scenarios: invoices with additional data, invoices involving third parties, and invoices with specific lifecycle requirements.
According to the XP Z12-014 standard, the use cases described in Chapter 3 are subject to each platform’s commercial policy — platforms are under no obligation to support all of them. Only the nominal invoice exchange case and lifecycle management via Credit Notes and Corrected Invoices are mandatory for all Accredited Platforms.
## Legend
[Section titled “Legend”](#legend)
| Status | Description |
| :------------ | :-------------------------------------------------------------------------------------------------- |
| `Covered` | This use case is fully supported. |
| `Covered *` | Supported by B2Brouter, but not yet fully activated by the receiving institution (DGFiP or Peppol). |
| `In Progress` | In the roadmap and available in the next API version. |
| `Planned` | Planned for the September 1st deadline. |
| `Evaluation` | Not yet in the roadmap. We are assessing whether we will support it. |
| `On Demand` | Not currently planned. Contact us if you need it and we will evaluate it. |
## Use Cases
[Section titled “Use Cases”](#use-cases)
This table reflects the current status of use case coverage and **will be updated as development progresses**.
> Last updated: 22.06.2026
| Case | Category | Description | Status |
| :--- | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------- |
| 1 | Multi-order/Multi-delivery | **Multi-order** (per line, via `cac:OrderLineReference` / `cac:DocumentReference`) — *Multi-commande (par ligne)* | `Covered` |
| 1 | Multi-order/Multi-delivery | **Multi-delivery** (header-level, multiple delivery notes) — *Multi-livraison (en-tête)* | `In Progress` |
| 2 | Invoice already paid by a third party or the buyer | [**Invoice already paid by the buyer or a third party**](#case-2) — *Facture déjà payée par l’acheteur ou un tiers payeur* | `Covered *` |
| 3 | Invoice to be paid by a third party | [**Invoice to be paid by a third party designated at invoicing**](#case-3) — *Facture à régler par un tiers payeur connu à la facturation* | `Covered *` |
| 4 | Invoice to be paid by a third party | **Invoice to be paid by the buyer and partially covered by a known third party (subsidy, insurance…)** — *Facture partiellement couverte par un tiers connu (subvention, assurance…)* | `Evaluation` |
| 5 | Expenses paid by third parties with invoice | [**Employee expense invoices in the company’s name (e-invoicing)**](#case-5) — *Frais payés par des salariés avec facture au nom de l’entreprise* | `Covered *` |
| 6 | Expenses paid by third parties without invoice | [**Employee expenses without an invoice (e-reporting of off-invoice transaction data)**](#case-6) — *Frais payés par des salariés sans facture adressée à l’entreprise* | `Covered` |
| 7 | Invoice paid by a third party | [**Invoice following a purchase paid with a corporate card**](#case-7) — *Facture suite à un achat payé par carte d’achat* | `Covered *` |
| 8 | Invoice payable to a third party | [**Invoices payable to a third party determined at the time of invoicing (factoring, cash pooling)**](#case-8) — *Factures payables à un tiers connu à la facturation (affacturage, cash pooling)* | `Covered *` |
| 9 | Invoice payable to a third party | [**Invoice payable to a known third party who also manages ordering/receipt or invoicing (Distributor/Depositary)**](#case-9) — *Facture payable à un tiers gérant aussi la commande/réception ou la facturation* | `Covered *` |
| 10 | Invoice payable to a third party | [**Invoice payable to a factor unknown when the invoice is created (subrogation)**](#case-10) — *Facture cédée à un factor inconnu à l’émission (cas de subrogation)* | `Covered *` |
| 11 | Invoice with “addressed to” different from the buyer | **Invoice with “Invoicee” other than buyer** — *Facture avec destinataire différent de l’acheteur* | `In Progress` |
| 12 | Transparent intermediary | **Transparent intermediary as invoice manager for the buyer principal** — *Intermédiaire transparent gérant les factures pour le compte de l’acheteur* | `In Progress` |
| 13 | Subcontracting invoice for direct payment | **Invoice to be paid by a third party: subcontracting with direct payment or payment delegation** — *Facture payable par un tiers : sous-traitance avec paiement direct ou délégation* | `Evaluation` |
| 14 | Co-contracting invoice | **Invoice to be paid by a third party: co-contracting** — *Facture payable par un tiers : co-traitance* | `Evaluation` |
| 15 | Invoice following an order/payment by a third party on behalf of the buyer | **Sales invoice following an order (and possible payment) by a third party on behalf of the buyer** — *Facture de vente suite à une commande (et paiement) d’un tiers pour le compte de l’acheteur* | `In Progress` |
| 16 | Invoice following an order/payment by a third party on behalf of the buyer | [**Disbursement invoice for reimbursement of the sales invoice paid by the third party**](#case-16) — *Facture de débours pour remboursement de la facture de vente payée par le tiers* | `Covered` |
| 17a | Invoice issued by a third party, payment intermediary | [**Invoice payable to a third party, payment intermediary (e.g. Marketplace)**](#case-17a) — *Facture payable à un tiers intermédiaire de paiement (ex. Marketplace)* | `Covered *` |
| 17b | Invoice issued by a third party, payment intermediary and invoicing mandate | **Invoice payable to a third party, payment intermediary with billing mandate** — *Facture payable à un tiers intermédiaire de paiement et mandat de facturation* | `In Progress` |
| 18 | Debit notes | [**Debit note management**](#case-18) — *Gestion des notes de débit* | `Covered` |
| 19a | Invoices issued under third-party mandate | **Invoice issued under billing mandate** — *Facture émise sous mandat de facturation* | `Evaluation` |
| 19b | Self-billing | [**Self-billing**](#case-19b) — *Autofacturation* | `Covered *` |
| 20 | Pre-payment invoice | **Prepayment invoice and final invoice after prepayment (advance already paid)** — *Facture d’acompte et facture finale après acompte déjà payé* | `In Progress` |
| 21 | Pre-payment invoice | **Prepayment invoice and final invoice after prepayment (advance to pay)** — *Facture d’acompte et facture finale après acompte à payer* | `In Progress` |
| 22a | Invoice with allowance | [**Invoice paid with early payment discount — services with TVA due on receipt of payment**](#case-22) — *Facture payée avec escompte — services avec TVA sur encaissement* | `Covered` |
| 22b | Invoice with allowance | [**Invoice paid with early payment discount — deliveries of goods (or services with TVA on debits)**](#case-22) — *Facture payée avec escompte — livraisons de biens (ou services avec TVA sur débits)* | `Covered` |
| 23 | Self-billing between an individual and a professional | **Self-billing between a private individual and a professional** — *Autofacturation entre un particulier et un professionnel* | `On Demand` |
| 24 | Deposit (“Arrhes”) | [**Management of deposits (Arrhes)**](#case-24) — *Gestion des arrhes* | `Covered` |
| 25 | Gift vouchers and cards | [**Management of gift vouchers and gift cards**](#case-25) — *Gestion des bons d’achat et cartes cadeaux* | `Covered` |
| 26 | Invoices with contractual reservation clauses | **Invoices with contractual reserve clause** — *Factures avec clause de réserve contractuelle* | `In Progress` In staging in late July |
| 27 | Toll tickets | [**Management of toll receipts**](#case-27) — *Gestion des tickets de péage* | `Covered` |
| 28 | Restaurant receipts | **Management of restaurant bills** — *Gestion des notes de restaurant* | `In Progress` In staging in early July |
| 29 | Single Taxable Entity and members of the Single Taxable Entity | **Single taxable entity (Article 256 C CGI)** — *Assujetti unique au sens de l’article 256 C du CGI* | `Evaluation` |
| 30 | E-reporting transaction subject to an invoice or “TVA already collected” | **TVA already collected — transactions initially processed under B2C e-reporting, subject to subsequent invoicing** — *TVA déjà collectée — transactions initialement traitées en e-reporting B2C, soumises à facturation ultérieure* | `In Progress` |
| 31 | Mixed invoices | **”Mixed” invoices with a main transaction and an ancillary transaction** — *Factures “mixtes” mentionnant une transaction principale et une transaction accessoire* | `Covered` |
| 32 | Management of monthly payments | **Monthly payments** — *Paiements mensualisés* | `In Progress` In staging in early July |
| 33 | TVA regime on the margin | **Transactions subject to the TVA margin scheme** — *Transactions soumises au régime de la marge* | `Covered` |
| 34 | Partial payment receipt and cancellation of payment receipt | **Partial receipt of payment and cancellation of receipt of payment** — *Encaissement partiel et annulation d’encaissement* | `In Progress` In staging in late July |
| 35 | Author’s notes | **Author’s notes** — *Notes d’auteur* | `On Demand` |
| 36 | Professional secrecy | **Transactions subject to professional secrecy and exchange of sensitive data** — *Transactions soumises au secret professionnel et échanges de données sensibles* | `Covered` |
| 37 | Joint ventures | **SEP (Sociétés en Participation / Joint Ventures)** — *SEP (Sociétés en Participation)* | `Evaluation` |
| 38 | Invoice sub-lines | **Invoices with sub-lines and line groupings** — *Factures avec sous-lignes et regroupements de lignes* | `Evaluation` |
| 39 | Multi-Vendors | **Transparent intermediary consolidating sales from multiple sellers — Multi-Vendor Invoice** — *Intermédiaire transparent regroupant les ventes de plusieurs vendeurs — Facture multi-vendeurs* | `Evaluation` |
| 40 | Offsetting between cross-flows | **Grouped payments, netting, or compensation in the event of cross-purchases/sales** — *Paiements groupés, compensation en cas d’achats/ventes croisés* | `On Demand` |
| 41 | Barter practices | **Barter companies (inter-company bartering)** — *Sociétés de troc (troc inter-entreprises)* | `On Demand` |
| 42 | Tax exemption | **Tax exemption management** — *Gestion de la détaxe* | `Covered` |
| 43 | International B2B operations | **E-reporting for international B2B** — *E-reporting pour le B2B international* | `In Progress` In staging in early July |
| 43a | International B2B operations | **Triangular transactions** — *Transactions triangulaires* | `In Progress` |
| 43b | International B2B operations | **Stock transfers treated as intra-Community supply** — *Transferts de stocks traités comme des livraisons intracommunautaires* | `In Progress` |
| 44 | Operations with French DROM / COM / TAAF | **Transactions with entities established in the DROMs/COMs/TAAFs** — *Transactions avec des entités établies dans les DROM/COM/TAAF* | `Covered` |
***
## Implementation Examples
[Section titled “Implementation Examples”](#implementation-examples)
### Introduction
[Section titled “Introduction”](#introduction)
In each use case you will find a JSON example with the fields you need to fill in. If you haven’t done so yet, we recommend reading [the DGFiP guide](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/) first.
### Covered
[Section titled “Covered”](#covered)
#### Case 6
[Section titled “Case 6”](#case-6)
##### Employee expenses without an invoice (e-reporting of off-invoice transaction data) — *Frais payés par des salariés sans facture adressée à l’entreprise*
[Section titled “Employee expenses without an invoice (e-reporting of off-invoice transaction data) — Frais payés par des salariés sans facture adressée à l’entreprise”](#employee-expenses-without-an-invoice-e-reporting-of-off-invoice-transaction-data--frais-payés-par-des-salariés-sans-facture-adressée-à-lentreprise)
The sale takes place between the vendor and the employee as a private individual, so it is not subject to the e-invoicing obligation but to the vendor’s B2C e-reporting. The employee may subsequently request a B2B invoice to recover TVA, which corresponds to use case 30.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedSimplifiedInvoice",
"type_code": "380",
"number": "TPV-2026-000341",
"date": "2026-06-15",
"due_date": "2026-06-15",
"currency": "EUR",
"payment_method": 48,
"extra_info": "#PMT# Carte bancaire #AAB# Comptant",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 100.0,
"description": "Fournitures de bureau",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 16
[Section titled “Case 16”](#case-16)
##### Disbursement invoice for reimbursement of the sales invoice paid by the third party — *Facture de débours pour remboursement de la facture de vente payée par le tiers*
[Section titled “Disbursement invoice for reimbursement of the sales invoice paid by the third party — Facture de débours pour remboursement de la facture de vente payée par le tiers”](#disbursement-invoice-for-reimbursement-of-the-sales-invoice-paid-by-the-third-party--facture-de-débours-pour-remboursement-de-la-facture-de-vente-payée-par-le-tiers)
*Débours* invoices fall outside the scope of the reform, but if exempt lines (`category: "O"`) are combined with TVA-liable lines (`category: "S"`), the mixed invoice falls within the scope of e-invoicing and must be transmitted.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"date": "2026-06-15",
"due_date": "2026-07-15",
"currency": "EUR",
"type_operation": "services",
"payment_method": 30,
"extra_info": "#PMD# Ref paiement FAC-2026-000516 #PMT# Virement bancaire #AAB# 30 jours net",
"contact_id": 1313573293,
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 800.0,
"description": "Remboursement facture de vente reglee pour le compte du client (debours)",
"taxes_attributes": [
{
"name": "TVA",
"category": "O",
"percent": 0,
"comment": "Debours — hors champ TVA art. 267 II CGI"
}
]
},
{
"quantity": 1,
"price": 50.0,
"description": "Commission de gestion du tiers",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 18
[Section titled “Case 18”](#case-18)
##### Debit note management — *Gestion des notes de débit*
[Section titled “Debit note management — Gestion des notes de débit”](#debit-note-management--gestion-des-notes-de-débit)
Debit notes do not constitute an invoice and fall outside the scope of e-invoicing. In practice, if the buyer issues one under a billing mandate, it is transmitted as a self-billed credit note (`type_code: "261"`) referencing the original invoice. The amount is positive; the credit note nature is already expressed by the `type_code` and the reference.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedSelfInvoice",
"type_code": "261",
"number": "ND-2026-000077",
"date": "2026-06-15",
"due_date": "2026-06-15",
"currency": "EUR",
"type_operation": "services",
"payment_method": 97,
"extra_info": "#PMD# Compensation ND-2026-000077 #PMT# Compensation entre parties #AAB# Avoir auto-facture (261), mandat de facturation requis",
"invoice_references": [
{
"reference_type": "amend",
"number": "FAC-2026-000201",
"series_code": "FAC",
"date": "2026-05-20",
"correction_method": "02"
}
],
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 150.0,
"description": "Regularisation en faveur de l'ACHETEUR",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 22
[Section titled “Case 22”](#case-22)
##### Invoice paid with early payment discount (escompte) — *Facture payée avec escompte*
[Section titled “Invoice paid with early payment discount (escompte) — Facture payée avec escompte”](#invoice-paid-with-early-payment-discount-escompte--facture-payée-avec-escompte)
A conditional early-payment discount is not applied to the invoice: at issue time the discount may never be taken, so the invoice carries full totals and the escompte only as the mandatory payment-terms mention (`#AAB#`, BT-20). Do not encode the escompte as an exempt (`E`) allowance with `VATEX-FR-CNWVAT` on the invoice: DGFiP rule G6.21 reserves that code to credit notes (types 261, 381, 396) and B2Brouter rejects it with a 422 on invoices.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-000305",
"type_operation": "services",
"payment_method": 30,
"extra_info": "#PMD# Ref paiement FAC-2026-000305 #PMT# Virement bancaire #AAB# Paiement à 30 jours. Escompte 20,00 EUR pour paiement sous 10 jours, net de taxe (renonciation à la récupération de la TVA)",
"contact_id": 1313573293,
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 1000.0,
"description": "Prestation de conseil",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
If the customer pays within the discount period, issue a credit note for the discount amount referencing the original invoice. For **22b** (goods, or services with TVA on debits) the mention “net de taxe” means the TVA is not adjusted: the credit note carries category `E` with `VATEX-FR-CNWVAT` — the only document type where that code is legal. For **22a** (services with TVA due on receipt of payment) issue a regular credit note with TVA (category `S`, percent 20) instead.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "381",
"number": "AV-2026-000305",
"type_operation": "services",
"payment_method": 30,
"extra_info": "#PMD# Ref paiement AV-2026-000305 #PMT# Virement bancaire #AAB# Escompte net de taxe",
"contact_id": 1313573293,
"invoice_references": [
{
"reference_type": "amend",
"number": "FAC-2026-000305",
"date": "2026-06-01",
"correction_method": "02"
}
],
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 20.0,
"description": "Escompte pour paiement anticipé, net de taxe",
"taxes_attributes": [{ "name": "Escompte", "category": "E", "percent": 0, "comment": "VATEX-FR-CNWVAT" }]
}
]
}
}
```
#### Case 24
[Section titled “Case 24”](#case-24)
##### Management of deposits (Arrhes) — *Gestion des arrhes*
[Section titled “Management of deposits (Arrhes) — Gestion des arrhes”](#management-of-deposits-arrhes--gestion-des-arrhes)
Deposits (*arrhes*) are an indemnity with no consideration, outside the scope of TVA and not transmitted via e-invoicing or e-reporting. In commercial practice, a price advance (subject to TVA, `type_code: "386"`) is more common; the example illustrates that case.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "386",
"number": "ACOMPTE-2026-0009",
"type_operation": "goods",
"payment_method": 30,
"extra_info": "#PMD# Ref paiement ACOMPTE-2026-0009 #PMT# Virement bancaire #AAB# Comptant",
"contact_id": 1313573293,
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 300.0,
"description": "Acompte sur commande",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 25
[Section titled “Case 25”](#case-25)
##### Management of gift vouchers and gift cards — *Gestion des bons d’achat et cartes cadeaux*
[Section titled “Management of gift vouchers and gift cards — Gestion des bons d’achat et cartes cadeaux”](#management-of-gift-vouchers-and-gift-cards--gestion-des-bons-dachat-et-cartes-cadeaux)
Gift vouchers and cards may be single-purpose (place of taxation and TVA rate known at issuance) or multi-purpose (place and TVA undetermined). The sale of the voucher is invoiced separately; this case covers only the management fee, which is a standard B2B service. (This case is pending regulatory definition and may change in future versions.)
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "COM-2026-000128",
"type_operation": "services",
"payment_method": 30,
"extra_info": "#PMD# Ref paiement COM-2026-000128 #PMT# Virement bancaire #AAB# 30 jours net",
"contact_id": 1313573293,
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 50.0,
"description": "Commission / frais de gestion sur bons cadeaux",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 27
[Section titled “Case 27”](#case-27)
##### Management of toll receipts — *Gestion des tickets de péage*
[Section titled “Management of toll receipts — Gestion des tickets de péage”](#management-of-toll-receipts--gestion-des-tickets-de-péage)
Toll tickets are treated as B2C transactions and reported via e-reporting, as the vendor does not identify the customer. If the toll is managed via a subscription or card that identifies the buyer, the transaction falls within the scope of e-invoicing and must be invoiced as F1.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedSimplifiedInvoice",
"number": "PEAGE-2026-00874",
"date": "2026-06-15",
"due_date": "2026-06-15",
"currency": "EUR",
"payment_method": 48,
"extra_info": "#PMT# Carte bancaire #AAB# Comptant — Ticket de peage : client non identifie (e-reporting B2C)",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 10.0,
"description": "Passage peage autoroute",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
***
### Covered \*
[Section titled “Covered \*”](#covered-)
Covered \*
The following use cases are supported by B2Brouter, but not yet fully activated by the receiving institution (DGFiP or Peppol). Their structure may change at the time of activation by the institution.
#### Case 2
[Section titled “Case 2”](#case-2)
##### Invoice already paid by the buyer or a third party — *Facture déjà payée par l’acheteur ou un tiers payeur*
[Section titled “Invoice already paid by the buyer or a third party — Facture déjà payée par l’acheteur ou un tiers payeur”](#invoice-already-paid-by-the-buyer-or-a-third-party--facture-déjà-payée-par-lacheteur-ou-un-tiers-payeur)
Invoice already paid (e.g. an order paid in advance): the amount paid is reported in `payments_on_account`, which matches the TVA-inclusive total and brings the outstanding amount to 0. The payment method is whichever was used (the example shows a bank transfer); only if it is a transfer (`payment_method: 4`) must you provide `bank_account_id` .
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-000777",
"date": "2026-06-15",
"due_date": "2026-06-15",
"currency": "EUR",
"type_operation": "goods",
"payment_method": 4,
"payments_on_account": 1200.0,
"bank_account_id": "",
"extra_info": "#PMD# Ref paiement CMD-2026-000777 #PMT# Virement bancaire #AAB# Facture deja payee — Net a payer 0",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 1000.0,
"description": "Commande payee a la commande",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 3
[Section titled “Case 3”](#case-3)
##### Invoice to be paid by a third party designated at invoicing — *Facture à régler par un tiers payeur connu à la facturation*
[Section titled “Invoice to be paid by a third party designated at invoicing — Facture à régler par un tiers payeur connu à la facturation”](#invoice-to-be-paid-by-a-third-party-designated-at-invoicing--facture-à-régler-par-un-tiers-payeur-connu-à-la-facturation)
Standard B2B invoice; the third-party payer is managed by the buyer (transparent to the seller, not reported on the invoice). The payment method is whichever applies (the example uses a bank transfer); only if it is a transfer (`payment_method: 4`) must you provide `bank_account_id`.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-000312",
"date": "2026-06-15",
"due_date": "2026-07-15",
"currency": "EUR",
"type_operation": "services",
"payment_method": 4,
"bank_account_id": "",
"extra_info": "#PMD# Ref FAC-2026-000312 #PMT# Virement bancaire #AAB# 30 jours net — Reglement par un tiers payeur a l'initiative de l'ACHETEUR",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 500.0,
"description": "Prestation de services",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 5
[Section titled “Case 5”](#case-5)
##### Employee expense invoices in the company’s name (e-invoicing) — *Frais payés par des salariés avec facture au nom de l’entreprise*
[Section titled “Employee expense invoices in the company’s name (e-invoicing) — Frais payés par des salariés avec facture au nom de l’entreprise”](#employee-expense-invoices-in-the-companys-name-e-invoicing--frais-payés-par-des-salariés-avec-facture-au-nom-de-lentreprise)
Invoice in the company’s name advanced by the employee, usually already paid (`payments_on_account`) by whatever method they used (the example shows a card payment). A registration number or identifier is referenced, not personal data (GDPR).
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-000455",
"date": "2026-06-15",
"due_date": "2026-06-15",
"currency": "EUR",
"type_operation": "goods",
"payment_method": 19,
"payments_on_account": 60.0,
"extra_info": "#PMD# Ref FAC-2026-000455 #PMT# Carte bancaire #AAB# Frais collaborateur — facture au nom de l'entreprise, matricule XXXX (deja payee, avancee par le salarie)",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 50.0,
"description": "Fournitures de bureau (note de frais)",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 7
[Section titled “Case 7”](#case-7)
##### Invoice following a purchase paid with a corporate card — *Facture suite à un achat payé par carte d’achat*
[Section titled “Invoice following a purchase paid with a corporate card — Facture suite à un achat payé par carte d’achat”](#invoice-following-a-purchase-paid-with-a-corporate-card--facture-suite-à-un-achat-payé-par-carte-dachat)
Invoice already paid with a lodged card (carte logée): the card details go in `card_account_attributes` (masked card number — last digits only) and the amount paid in `payments_on_account`.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-000628",
"date": "2026-06-15",
"due_date": "2026-06-15",
"currency": "EUR",
"type_operation": "services",
"payment_method": 19,
"payments_on_account": 240.0,
"card_account_attributes": {
"account_number": "",
"network": "VISA",
"holder_name": ""
},
"extra_info": "#PMD# Ref transaction CL-2026-33012 #PMT# Carte d'achat (carte logee) #AAB# Facture deja payee — Net a payer 0",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 200.0,
"description": "Nuitee hotel (deplacement professionnel)",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 8
[Section titled “Case 8”](#case-8)
##### Invoices payable to a third party determined at the time of invoicing (factoring, cash pooling) — *Factures payables à un tiers connu à la facturation (affacturage, cash pooling)*
[Section titled “Invoices payable to a third party determined at the time of invoicing (factoring, cash pooling) — Factures payables à un tiers connu à la facturation (affacturage, cash pooling)”](#invoices-payable-to-a-third-party-determined-at-the-time-of-invoicing-factoring-cash-pooling--factures-payables-à-un-tiers-connu-à-la-facturation-affacturage-cash-pooling)
Payment to a factor (or cash pooling) determined at invoicing: their account is referenced in `bank_account`. For factoring, `type_code: 393` and a subrogation mention in #AAB#.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "393",
"number": "FAC-2026-000801",
"date": "2026-06-15",
"due_date": "2026-07-15",
"currency": "EUR",
"type_operation": "services",
"payment_method": 4,
"bank_account": {
"type": "iban",
"iban": "FRXX XXXX XXXX XXXX XXXX XXXX XXX",
"bic": "XXXXXXXX",
"name": ""
},
"extra_info": "#PMD# Ref FAC-2026-000801 #PMT# Virement bancaire #AAB# 30 jours net — Facture cedee a l'affactureur, subrogation (art. 1346 Code civil)",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 1000.0,
"description": "Prestation de services (facture affacturee)",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 9
[Section titled “Case 9”](#case-9)
##### Invoice payable to a known third party who also manages ordering/receipt or invoicing (Distributor/Depositary) — *Facture payable à un tiers gérant aussi la commande/réception ou la facturation*
[Section titled “Invoice payable to a known third party who also manages ordering/receipt or invoicing (Distributor/Depositary) — Facture payable à un tiers gérant aussi la commande/réception ou la facturation”](#invoice-payable-to-a-known-third-party-who-also-manages-orderingreceipt-or-invoicing-distributordepositary--facture-payable-à-un-tiers-gérant-aussi-la-commanderéception-ou-la-facturation)
Like case 8 but without factoring: `type_code: 380` and no subrogation mention; the beneficiary (distributor/depositary) is referenced in `bank_account`.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-000844",
"date": "2026-06-15",
"due_date": "2026-07-15",
"currency": "EUR",
"type_operation": "services",
"payment_method": 4,
"bank_account": {
"type": "iban",
"iban": "FRXX XXXX XXXX XXXX XXXX XXXX XXX",
"bic": "XXXXXXXX",
"name": ""
},
"extra_info": "#PMD# Ref FAC-2026-000844 #PMT# Virement bancaire #AAB# 30 jours net — Beneficiaire du paiement : tiers (distributeur / depositaire)",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 1000.0,
"description": "Prestation de services",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 10
[Section titled “Case 10”](#case-10)
##### Invoice payable to a factor unknown when the invoice is created (subrogation) — *Facture cédée à un factor inconnu à l’émission (cas de subrogation)*
[Section titled “Invoice payable to a factor unknown when the invoice is created (subrogation) — Facture cédée à un factor inconnu à l’émission (cas de subrogation)”](#invoice-payable-to-a-factor-unknown-when-the-invoice-is-created-subrogation--facture-cédée-à-un-factor-inconnu-à-lémission-cas-de-subrogation)
Since the factoring company is still unknown at issuance, an ordinary B2B invoice is sent (`type_code: 380`, the seller’s account).
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-000867",
"date": "2026-06-15",
"due_date": "2026-07-15",
"currency": "EUR",
"type_operation": "services",
"payment_method": 4,
"bank_account_id": "",
"extra_info": "#PMD# Ref FAC-2026-000867 #PMT# Virement bancaire #AAB# 30 jours net",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 1000.0,
"description": "Prestation de services",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 17a
[Section titled “Case 17a”](#case-17a)
##### Invoice payable to a third party, payment intermediary (e.g. Marketplace) — *Facture à payer à un tiers, intermédiaire de paiement (par exemple sur Marketplace)*
[Section titled “Invoice payable to a third party, payment intermediary (e.g. Marketplace) — Facture à payer à un tiers, intermédiaire de paiement (par exemple sur Marketplace)”](#invoice-payable-to-a-third-party-payment-intermediary-eg-marketplace--facture-à-payer-à-un-tiers-intermédiaire-de-paiement-par-exemple-sur-marketplace)
Marketplace sale already paid: the intermediary is the beneficiary of the payment (`bank_account`) and the amount goes in `payments_on_account`. The intermediary’s commission is a separate invoice.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_code": "380",
"number": "FAC-2026-001017",
"date": "2026-06-15",
"due_date": "2026-06-15",
"currency": "EUR",
"type_operation": "goods",
"payment_method": 4,
"payments_on_account": 1200.0,
"bank_account": {
"type": "iban",
"iban": "FRXX XXXX XXXX XXXX XXXX XXXX XXX",
"bic": "XXXXXXXX",
"name": ""
},
"extra_info": "#PMD# Ref FAC-2026-001017 #PMT# Virement bancaire (encaissement via intermediaire de paiement) #AAB# Facture deja payee — vente via place de marche, Net a payer 0",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 1000.0,
"description": "Vente via place de marche",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
#### Case 19b
[Section titled “Case 19b”](#case-19b)
##### Self-billing — *Autofacturation*
[Section titled “Self-billing — Autofacturation”](#self-billing--autofacturation)
Self-billing: the buyer issues on behalf of the seller (type: “IssuedSelfInvoice”, `type_code: 389`), with dedicated numbering per seller and a mention of the mandate in #AAB#. The payment method is whichever applies (the example uses a bank transfer); only if it is a transfer must you provide bank\_account.
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedSelfInvoice",
"type_code": "389",
"number": "AF-2026-000045",
"date": "2026-06-15",
"due_date": "2026-07-15",
"currency": "EUR",
"type_operation": "goods",
"payment_method": 4,
"bank_account": {
"type": "iban",
"iban": "FRXX XXXX XXXX XXXX XXXX XXXX XXX",
"bic": "XXXXXXXX",
"name": ""
},
"extra_info": "#PMD# Ref AF-2026-000045 #PMT# Virement bancaire #AAB# 30 jours net — Facture etablie par l'ACHETEUR au nom et pour le compte du VENDEUR (auto-facturation, mandat de facturation)",
"contact_id": "",
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 1000.0,
"description": "Livraison de biens (auto-facturee)",
"taxes_attributes": [{ "name": "TVA", "category": "S", "percent": 20 }]
}
]
}
}
```
# SDI (FatturaPA)
> Complete guide to issue and receive Italian electronic invoices through the SDI in FatturaPA format with the B2Brouter API.
The SDI (*Sistema di Interscambio*) is Italy’s mandatory e-invoicing platform, run by the *Agenzia delle Entrate*. All invoices between Italian operators —B2B, B2C and B2G— must be issued and received through the SDI in **FatturaPA** format (the Italian national structured XML format).
B2Brouter’s REST API generates the FatturaPA XML, transmits it to the SDI and retrieves the acknowledgements, so your integration can focus on business logic.
Simplified invoice
For low-value transactions (up to €400) there is a separate reduced XML format: see the [Fattura Semplificata](#fattura-semplificata) section below.
## What is the SDI?
[Section titled “What is the SDI?”](#what-is-the-sdi)
The SDI is the central hub every Italian electronic invoice passes through. In practice:
* Invoices are issued in **FatturaPA 1.2** format (`xml.fatturapa.1.2`), an Italian XML format.
* The SDI validates the document against the official schema (XSD) and delivers it to the recipient.
* Each accepted invoice receives a registration identifier (*IdentificativoSdI*).
* If the recipient cannot receive the document electronically, the SDI makes it available in their *cassetto fiscale*.
E-invoicing has been mandatory for all Italian companies since January 2019.
## Recipient identification
[Section titled “Recipient identification”](#recipient-identification)
To route an invoice, the SDI needs to know **where** to deliver it. There are several ways, and it is important not to confuse them:
| Concept | Field in B2Brouter | What it is |
| ----------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **Codice Destinatario** | `recipient_code` | Routing code assigned by the SDI to the recipient. **7 characters** for private companies, **6** for public administrations (*Codice Ufficio*). |
| **PEC** (*Posta Elettronica Certificata*) | `certified_email` | Certified email, an alternative to the Codice Destinatario when the recipient does not have one. |
| **Codice Fiscale / Partita IVA** | `cin_value` / `tin_value` | The recipient’s tax identifier (not a routing code). |
| **CUP / CIG** | `file_reference` (CUP) / `lot_reference` (CIG) | Project and tender codes required in certain operations with the public sector; they do not route the invoice. |
Default Codice Destinatario
If the Italian contact has neither a Codice Destinatario nor a PEC, B2Brouter automatically uses `0000000` (seven zeros). The SDI then makes the invoice available in the buyer’s *cassetto fiscale*.
If the buyer is identified by **PEC** (and not by a Codice Destinatario), leave `recipient_code` as `0000000` and set the PEC address in `certified_email`.
To **receive** invoices in B2Brouter through the SDI, your supplier must use B2Brouter’s Codice Destinatario: **`EPKDZJB`** (after registering your company with the *Agenzia delle Entrate*).
## B2B, B2C and B2G
[Section titled “B2B, B2C and B2G”](#b2b-b2c-and-b2g)
The technical flow is the same in all three cases; what changes is the recipient’s identification:
* **B2B** (company): identified by the **Partita IVA** (`tin_scheme: 9906`). The `recipient_code` is **7 characters** (or `0000000` if the customer does not have one).
* **B2C** (private consumer): identified by the **Codice Fiscale** (`cin_scheme: 9907`). They usually have no Codice Destinatario, so `0000000` is used and the SDI makes the invoice available in the citizen’s *cassetto fiscale*. On exempt invoices the [bollo](#bollo) may apply.
* **B2G** (public administration): identified by the **Codice Ufficio**, **6 characters**. Mark the contact as `public_sector: true` and set the 6-character code in `recipient_code`. With the public sector, *split payment* often applies (see [Esigibilità IVA and split payment](#esigibilit%C3%A0-iva-and-split-payment)).
B2Brouter validates the code length (7 private / 6 public sector) at send time.
## Before you start
[Section titled “Before you start”](#before-you-start)
* An Italian company with a **valid Partita IVA and a complete address** (validated at send time, not when the account is created).
* A B2Brouter test account: [Register here](https://app-staging.b2brouter.net/register).
* API permissions (request them by opening a [support ticket](https://www.b2brouter.net/docs/#/en/support/open-incident-report)).
API version
In the `X-B2B-API-Version` header, always use the **latest available version** of the API. The examples in this guide depend on it (some field names have changed between versions).
> Start with the [sandbox](/en/developers/testing/sandbox/) for your first payload tests. Move to **staging** (`api-staging.b2brouter.net`) when you want to test the full submission to the SDI test environment.
## Enabling the SDI
[Section titled “Enabling the SDI”](#enabling-the-sdi)
For an account to issue to the SDI, it must be enabled with a *tax report setting* for the `sdi` authority. See the [Tax Report Settings Guide](/en/developers/essential-guides/tax-report-settings/) for the general details.
```shell
curl --request POST \
--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": "sdi"
}
}'
```
Response:
```json
{
"tax_report_setting": {
"code": "sdi",
"start_date": null,
"auto_generate": true,
"auto_send": true,
"enabled": true,
"locked": false
}
}
```
On creation, B2Brouter enables `enabled`, `auto_generate` and `auto_send` by default, so FatturaPA invoices are generated and sent to the SDI automatically.
## Create the Italian company
[Section titled “Create the Italian company”](#create-the-italian-company)
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"account": {
"country": "it",
"rounding_method": "half_up",
"tin_value": "20339260422",
"tin_scheme": 211,
"name": "Test Sender Italy",
"address": "Milano street, 1",
"city": "Milano",
"postalcode": "20019",
"province": "MI",
"email": "john.doe@example.com",
"tax_regime": "RF01"
}
}'
```
The `tax_regime` field states the issuer’s tax regime (see [Regime fiscale](#regime-fiscale)); if omitted, `RF01` (ordinary) is applied.
## Create the customer contact
[Section titled “Create the customer contact”](#create-the-customer-contact)
When you create an Italian customer:
* Use `tin_value` for the **Partita IVA** with `tin_scheme: 9906`.
* Use `cin_value` for the **Codice Fiscale** with `cin_scheme: 9907` ([scheme list](https://docs.peppol.eu/poacc/upgrade-3/codelist/eas/)). One of the two identifiers is enough.
* The contact’s **address and postal code** are required to send to the SDI.
* `transport_type_code` must be `it.sdi`.
* `document_type_code` must be `xml.fatturapa.1.2`.
* If the contact has a Codice Destinatario, set it in `recipient_code` (7 chars private / 6 public sector). If not, `0000000` is used. Optionally, set the buyer’s PEC in `certified_email`.
Schemes and province
Your **issuing account** identifies the Partita IVA with `tin_scheme: 211` (Italian numeric scheme), whereas on **contacts** the Partita IVA uses the Peppol EAS `9906` and the Codice Fiscale uses `9907`. The `province` must be the **2-letter code** of the province (`MI` for Milano, `RM` for Roma…), not the full name.
Field names
Use `recipient_code` and `certified_email`. The Italian aliases `codice_destinatario` and `posta_elettronica_certificata` are only accepted in API version `2025-01-01`; from `2025-10-13` onwards they are not processed.
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"contact": {
"tin_scheme": "9906",
"tin_value": "IT12345678901",
"name": "Esempio S.r.l.",
"address": "Via Inventata 88",
"postalcode": "00184",
"city": "Roma",
"province": "RM",
"country": "it",
"transport_type_code": "it.sdi",
"document_type_code": "xml.fatturapa.1.2",
"recipient_code": "{CODICE_DESTINATARIO}"
}
}'
```
## Create and send an invoice
[Section titled “Create and send an invoice”](#create-and-send-an-invoice)
```shell
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": {
"type": "IssuedInvoice",
"invoice_lines_attributes": [
{
"quantity": 10,
"price": 47,
"description": "Test Item",
"taxes_attributes": [
{ "name": "IVA", "percent": 22 }
]
}
],
"number": "1",
"date": "2025-01-22",
"due_date": "2025-02-22",
"contact_id": {YOUR_CONTACT_ID}
}
}'
```
Notes:
* The invoice number can be at most **20 characters**.
* Each line must have a description and **at least one tax**.
* `due_date` is **optional** for the `it.sdi` transport.
`send_after_import` and `auto_send`
These are two independent things: `send_after_import: true` makes the invoice **be sent when it is created** instead of staying as a draft; `auto_send` (on the SDI *tax report setting*) makes the SDI **tax report** be sent automatically when the invoice is sent.
### Additional fields for Italian invoices
[Section titled “Additional fields for Italian invoices”](#additional-fields-for-italian-invoices)
* **`type_document`** (on the invoice): sets the FatturaPA `TipoDocumento`. Optional; if omitted, B2Brouter assigns it automatically. See [Document type](#document-type-tipodocumento).
* **`receipt_reference`** (on **each line**, not on the invoice): reference to a receipt/scontrino linked to the line, with the fields `identifier`, `number` and `date`. Optional, FatturaPA only:
```json
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 47,
"description": "Test Item",
"taxes_attributes": [{ "name": "IVA", "percent": 22 }],
"receipt_reference": {
"identifier": "SCONTRINO-001",
"number": "42",
"date": "2025-01-20"
}
}
]
```
## Document type (TipoDocumento)
[Section titled “Document type (TipoDocumento)”](#document-type-tipodocumento)
The `TipoDocumento` (`type_document`) classifies the invoice (fattura, nota di credito, autofattura…). B2Brouter assigns it automatically based on the invoice type and content, but you can force it with a valid code (`TD01`–`TD06`, `TD16`–`TD27`).
Behavior:
* If you **do not** set `type_document`, it is assigned automatically: `TD01` by default, `TD04` for credit notes/amendments, `TD16` when there are taxes with Natura `N6`, and `TD17`/`TD18`/`TD19` for self-invoices (see [Autofattura](#autofattura-and-reverse-charge)).
* If you set it with a **valid** code, it is emitted as-is. If the value is not valid, it is ignored and the automatic assignment applies (no error is raised).
To see how each JSON field maps to the FatturaPA XML nodes, use the [invoice field mapper](https://docs.b2brouter.net/en/developers/tools/invoice-field-mapper/?document_type=fatturapa).
## VAT nature codes (Natura)
[Section titled “VAT nature codes (Natura)”](#vat-nature-codes-natura)
When an operation carries no VAT (exempt, non-taxable, reverse charge…), FatturaPA does not use a percentage but a **Natura** code (`N1`–`N7`). In B2Brouter it is set in `category` inside `taxes_attributes`, with `percent: 0`:
```json
"taxes_attributes": [
{ "name": "IVA", "percent": 0, "category": "N2.1" }
]
```
| Code | Meaning |
| --------------- | -------------------------------------------------------------------------------------------- |
| `N1` | Excluded pursuant to art. 15 |
| `N2.1` / `N2.2` | Not subject (art. 7-bis and others) |
| `N3.1`–`N3.6` | Non-taxable (exports, intra-EU supplies, San Marino…) |
| `N4` | Exempt |
| `N5` | Margin scheme / VAT not shown |
| `N6.1`–`N6.9` | Reverse charge (*inversione contabile*) — see [Autofattura](#autofattura-and-reverse-charge) |
| `N7` | VAT chargeable in another EU state |
B2Brouter fills in the reason associated with each Natura code automatically; you only need to set the `category`.
Use the subdivided codes
Always use the **subdivided** code (`N2.1`, `N3.2`, `N6.9`…). The generic codes `N2`, `N3` and `N6` have been deprecated since 2021 and **are not accepted**.
## Regime fiscale
[Section titled “Regime fiscale”](#regime-fiscale)
The **issuer’s** tax regime is set in the account’s `tax_regime` field. The default is `RF01` (ordinary). Accepted values:
| Code | Regime | | Code | Regime |
| ------ | ---------------------------- | - | ------ | ------------------------------------- |
| `RF01` | Ordinario | | `RF12` | Agriturismo |
| `RF02` | Contribuenti minimi | | `RF13` | Vendite a domicilio |
| `RF04` | Agricoltura e pesca | | `RF14` | Rivendita beni usati / oggetti d’arte |
| `RF05` | Vendita sali e tabacchi | | `RF15` | Agenzie di vendite all’asta |
| `RF06` | Commercio fiammiferi | | `RF16` | IVA per cassa P.A. |
| `RF07` | Editoria | | `RF17` | IVA per cassa |
| `RF08` | Servizi telefonia pubblica | | `RF18` | Altro |
| `RF09` | Documenti di trasporto/sosta | | `RF19` | Regime forfettario |
| `RF10` | Intrattenimenti e giochi | | | |
| `RF11` | Agenzie viaggi e turismo | | | |
## Modalità di pagamento
[Section titled “Modalità di pagamento”](#modalità-di-pagamento)
The payment method is set with the `payment_method` field (on the contact or the invoice) and B2Brouter maps it to the FatturaPA `ModalitaPagamento` code (`MPxx`). See the [Payment Method Guide](/en/developers/essential-guides/payment-methods/) for the `payment_method` values.
Most common mappings:
| `MP` FatturaPA | Payment method |
| -------------- | ---------------------------------- |
| `MP01` | Cash (contanti) |
| `MP02` | Cheque (assegno) |
| `MP05` | Bank transfer (bonifico) |
| `MP06` | Promissory note (vaglia cambiario) |
| `MP08` | Credit/debit card |
Note
B2Brouter generates a **subset** of the `MP` codes (not all of MP01–MP23). If you need a specific code that is not generated, contact support.
## Esigibilità IVA and split payment
[Section titled “Esigibilità IVA and split payment”](#esigibilità-iva-and-split-payment)
Esigibilità indicates when the VAT becomes due. It is set in the `vat_payability` field of the **contact** (default `I`):
| Value | Meaning |
| ----- | ----------------------------------------- |
| `I` | Immediate (default) |
| `D` | Deferred (*esigibilità differita*) |
| `S` | Split payment (*scissione dei pagamenti*) |
The value **`S` (split payment)** typically applies to invoices to the **public sector**: the buyer pays the VAT directly to the tax authority, so B2Brouter deducts it from the amount due (the subtotal and the VAT are shown, but the `payable_amount` does not include the VAT).
## Ritenuta d’acconto
[Section titled “Ritenuta d’acconto”](#ritenuta-dacconto)
For professionals subject to withholding (*ritenuta d’acconto*), set `amounts_withheld_reason` (the payment reason) and the withheld amount on the invoice. B2Brouter deducts the amount from the amount due and generates the `DatiRitenuta` block in the XML.
There are two ways to state the amount:
* **`amounts_withheld`**: the exact amount to withhold. Recommended when the withholding base is not the total (for example, only the taxable base).
* **`withheld_percent`**: a percentage that B2Brouter applies to the **invoice total (VAT included)**. Note: if you expect the withholding on the taxable base, use `amounts_withheld`.
```json
{
"invoice": {
"type": "IssuedInvoice",
"withheld_percent": 20,
"amounts_withheld_reason": "A",
"...": "..."
}
}
```
With the example above on a base with VAT, the response returns the computed `amounts_withheld` and the `payable_amount` reduced by that amount.
Current limitations
FatturaPA supports **a single withholding per invoice**. B2Brouter emits the withholding type as `RT01` and the stated payment reason.
## Bollo
[Section titled “Bollo”](#bollo)
The bollo (*imposta di bollo*) is a **€2.00** stamp duty required on **VAT-free** invoices exceeding **€77.47**.
B2Brouter applies it **automatically** when all of these conditions are met:
* The company has the bollo option enabled (`apply_stamp_duty`), which is configured on the company (not through this API).
* The issuer is Italian and the total exceeds €77.47.
* There is at least one tax with an applicable exemption Natura: `N2.1`, `N2.2`, `N3.5`, `N3.6` or `N4`.
* The document is not an integration/self-invoice (`TD16`–`TD19`).
When it applies, the XML includes `` with `BolloVirtuale: SI` and `ImportoBollo: 2.00`. You can also add it manually as a line with the description “bollo”, price 2 and tax `N1` at 0%.
## Credit and debit notes
[Section titled “Credit and debit notes”](#credit-and-debit-notes)
To correct an invoice that has already been issued:
* **Nota di credito** (credit note): create the invoice with `is_credit_note: true`. B2Brouter assigns `TD04` automatically.
* **Nota di debito** (debit note): set `type_document: "TD05"`.
To link the original invoice (so the SDI relates the documents), add a reference with `invoice_references` of type `amend`, or set `amended_invoice_number` and `amended_invoice_date`.
Note
A nota di credito created with `is_credit_note: true` generates the `TipoDocumento` `TD04` in FatturaPA 1.2 format.
## Receiving invoices
[Section titled “Receiving invoices”](#receiving-invoices)
Your suppliers’ invoices can reach B2Brouter through different reception channels:
* **SDI**: register B2Brouter’s Codice Destinatario (**`EPKDZJB`**) with the *Agenzia delle Entrate*; Italian suppliers issue towards this code.
* **Peppol or email**: enable the corresponding reception transport on the account to receive international suppliers that issue through those channels.
Whatever the channel, the invoice enters as a **received invoice** (`ReceivedInvoice`). To query them: `GET /accounts/{id}/received_invoices` (list) and `GET /received_invoices/{id}` (get).
When the supplier has **not** gone through the SDI (typically a foreign supplier), the purchase must be declared to the SDI as an **autofattura**. If you have a reception transport enabled, B2Brouter does it **automatically** when the invoice arrives; if you receive them in other software, you register them yourself (via the API) and they are integrated all the same. See the next section.
## Autofattura and reverse charge
[Section titled “Autofattura and reverse charge”](#autofattura-and-reverse-charge)
When you buy from a foreign supplier (or in internal *reverse charge* operations), Italian law requires you to **self-invoice**: declare the operation to the SDI on your own behalf.
In B2Brouter, the autofattura **is not created as a separate document**: it is generated automatically from the **received invoice**. The flow is:
1. Make sure the account has the [SDI enabled](#enabling-the-sdi) (with `auto_generate`, which is the default).
2. Get the supplier’s invoice into the account as a **received invoice**: if you have a reception transport (Peppol/email) enabled, it arrives on its own; if you receive it in other software, register it via the API with `POST /accounts/{id}/invoices` and `type: "ReceivedInvoice"`. Set the `type_operation` (`services` or `goods`) and the appropriate VAT Natura (e.g. `N6.x` for reverse charge); the supplier goes inside the `contact` object.
3. On creation, B2Brouter automatically generates an **integration tax report** (the autofattura), exports it in FatturaPA format and, if the SDI has `auto_send`, sends it. You can query it with the tax reports endpoint.
```shell
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 '{
"invoice": {
"type": "ReceivedInvoice",
"number": "RIC-001",
"date": "2026-07-22",
"type_operation": "services",
"contact": {
"tin_scheme": "9930",
"tin_value": "DE137869841",
"name": "Fornitore Estero GmbH",
"address": "Berlin str 1",
"postalcode": "10115",
"city": "Berlin",
"country": "de"
},
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 500,
"description": "Consulenza estera",
"taxes_attributes": [{ "name": "IVA", "percent": 0, "category": "N6.9" }]
}
]
}
}'
```
The integration’s `TipoDocumento` is computed **automatically**:
| Situation | Code |
| ----------------------------------------------------- | ------ |
| Internal reverse charge (taxes with Natura `N6`) | `TD16` |
| Purchase of **services** from a non-resident supplier | `TD17` |
| Intra-EU purchase of **goods** | `TD18` |
| Purchase of **goods** from an extra-EU supplier | `TD19` |
How the code is chosen
Natura `N6` takes **priority**: if a line carries a tax with category `N6.x`, `TD16` is emitted regardless of `type_operation`. To get `TD17`/`TD18`/`TD19`, register the received invoice **without** `N6` —with the self-assessed Italian VAT (e.g. `percent: 22`)— and then `type_operation` decides: `services` → `TD17`, intra-EU `goods` → `TD18`, extra-EU `goods` → `TD19`.
B2Brouter automatically fills in the `DatiFattureCollegate` block (reference to the received invoice) and marks the document as a self-invoice (`SoggettoEmittente: CC`).
## Fattura Semplificata
[Section titled “Fattura Semplificata”](#fattura-semplificata)
The *Fattura Elettronica Semplificata* (art. 21-bis of DPR 633/72) is a reduced XML format for low-value transactions: the total cannot exceed **€400**. It is legally equivalent to an ordinary invoice, but uses its own structure (`FatturaElettronicaSemplificata`, version FSM10) with fewer required fields —for example, it does not export payment data—. Like any Italian invoice, it must be submitted to the SDI.
In B2Brouter it is created as an `IssuedInvoice` with the document type `xml.fattura.semplificata.1.0.2` (on the invoice or the contact).
### Validation rules
[Section titled “Validation rules”](#validation-rules)
| Requirement | Value |
| ------------------------------- | ----------------------------------------------------------- |
| Maximum invoice total | €400 |
| Buyer’s tax identifier | Codice Fiscale **or** Partita IVA (one of the two required) |
| Buyer’s address and postal code | Required |
| VAT per line | Exactly one tax per line |
| Global discounts or charges | Not allowed (use line-level discounts) |
### Create a simplified invoice
[Section titled “Create a simplified invoice”](#create-a-simplified-invoice)
```shell
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": {
"type": "IssuedInvoice",
"document_type_code": "xml.fattura.semplificata.1.0.2",
"number": "S-2025-001",
"date": "2025-09-10",
"contact_id": 123456789,
"invoice_lines_attributes": [
{
"quantity": 2,
"price": 50.00,
"description": "Servizi di consulenza",
"taxes_attributes": [{ "name": "IVA", "percent": 22 }]
}
]
}
}'
```
### Document type (TipoDocumento)
[Section titled “Document type (TipoDocumento)”](#document-type-tipodocumento-1)
For the simplified format, B2Brouter assigns the `TipoDocumento` **automatically**; do not set `type_document`:
* `TD07` — Fattura Semplificata (default).
* `TD08` — Nota di Credito Semplificata (when `is_credit_note: true` or an amendment).
If you set a **valid** code from the ordinary FatturaPA (`TD01`–`TD06`, `TD16`–`TD27`), it will be emitted as-is inside the simplified XML and the SDI will **reject** it (XSD error), because the simplified format only accepts `TD07`, `TD08` and `TD09`. `TD09` (Nota di Debito Semplificata) is not currently supported.
Note
The simplified format does not export payment data: `due_date`, `payment_method`, etc. are accepted by the API but do not appear in the XML.
## Checking the invoice status
[Section titled “Checking the invoice status”](#checking-the-invoice-status)
With `send_after_import: true`, the invoice is sent to the SDI through the `it.sdi` transport and goes through `sending` → `sent`. If the SDI (or the recipient) rejects it, it moves to `error` with the reason. When the SDI returns the registration identifier (*IdentificativoSdI*), it becomes available in `to_net_id`.
Issued vs. self-invoices
**Issued** invoices do not generate any *tax report*: the submission to the SDI is the transport itself. **Self-invoices** generated from received invoices, on the other hand, are *tax reports* (see [Autofattura](#autofattura-and-reverse-charge)) and are queried with the tax reports endpoint.
### Option 1: query a single invoice
[Section titled “Option 1: query a single invoice”](#option-1-query-a-single-invoice)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}?include=lines' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### Option 2: query several invoices
[Section titled “Option 2: query several invoices”](#option-2-query-several-invoices)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices?offset=0&limit=25' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
When the invoice reaches its final state, mark it as acknowledged to remove it from the active list:
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/ack \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
## Additional resources
[Section titled “Additional resources”](#additional-resources)
**B2Brouter**
* [Invoice field mapper (FatturaPA)](https://docs.b2brouter.net/en/developers/tools/invoice-field-mapper/?document_type=fatturapa)
* [Payment Method Guide](/en/developers/essential-guides/payment-methods/)
* [API Reference](https://developer.b2brouter.net/reference/)
**Official documentation (Agenzia delle Entrate)**
* [FatturaPA format — XSD schemas, XSLT stylesheets and specifications](https://www.fatturapa.gov.it/it/norme-e-regole/documentazione-fattura-elettronica/formato-fatturapa/)
* [Sistema di Interscambio (SDI) documentation](https://www.fatturapa.gov.it/it/norme-e-regole/DocumentazioneSDI/)
* [Technical specifications of the format (Agenzia delle Entrate)](https://www.agenziaentrate.gov.it/portale/specifiche-tecniche-versione-1.9)
# LHDN tax report
> How to submit tax reports to the Malaysian LHDN (Inland Revenue Board) via B2Brouter.
# Submit Tax Report to LHDN (Malaysia) using B2Brouter API
[Section titled “Submit Tax Report to LHDN (Malaysia) using B2Brouter API”](#submit-tax-report-to-lhdn-malaysia-using-b2brouter-api)
This guide explains how to activate and generate a tax report for the Inland Revenue Board of Malaysia (LHDN) through the B2Brouter API.
> **Testing environments**: Use [sandbox](/en/developers/testing/sandbox/) for initial API testing and payload validation — sandbox simulates LHDN submissions so you can exercise the full invoice lifecycle without connecting to any real authority. For full end-to-end testing against LHDN’s own test endpoint, use the staging environment (`api-staging.b2brouter.net`).
## Overview: Clearance process
[Section titled “Overview: Clearance process”](#overview-clearance-process)
The LHDN (Lembaga Hasil Dalam Negeri, or Inland Revenue Board of Malaysia) tax reporting process operates as a clearance model. This means each invoice issued must be reported to and approved by the tax authority.
When using B2Brouter, each tax report sent to the LHDN will be automatically cleared through this authority. Thus, the invoice can only be considered legally valid after successful clearance by LHDN.
Below are the steps to configure and manage tax report submissions to LHDN using the B2Brouter API.
### Step 1: Account Requirements for Malaysia
[Section titled “Step 1: Account Requirements for Malaysia”](#step-1-account-requirements-for-malaysia)
To report invoices to LHDN via B2Brouter, your B2Brouter account must be properly configured as a **Malaysian entity**.
#### Mandatory Fields for Malaysian Accounts
[Section titled “Mandatory Fields for Malaysian Accounts”](#mandatory-fields-for-malaysian-accounts)
When creating your account via API (`POST /accounts`), the following fields are required and must meet Malaysian-specific constraints:
| Field | Description | Notes |
| ------------ | ------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `country` | ISO country code | Must be `my` (Malaysia) |
| `tin_value` | Taxpayer Identification Number | Must follow Malaysian format |
| `tin_scheme` | TIN Scheme Code | Must be `8028` — *Nombor Cukai Pendapatan (ITN)* |
| `cin_value` | Company or Person Identifier | Must be either a Business Registration Number or National Registration Identity Card Number |
| `cin_scheme` | CIN Scheme Code | Use `8031` for *Business Registration Number (BRN)* or `8029` for *National Registration Identity Card Number (NRIC)* |
| `province` | State/Region | Must match official LHDN state codes |
| `phone` | Company contact number | Required for tax validation |
Full reference: [Create Account API](https://developer.b2brouter.net/reference/create-account)
##### Example JSON Payload (Create Malaysian Account)
[Section titled “Example JSON Payload (Create Malaysian Account)”](#example-json-payload-create-malaysian-account)
```json
{
"account": {
"name": "My Malaysia Business",
"email": "john.doe@example.com",
"phone": "60123456789",
"address": "123 Jalan Ampang",
"city": "Kuala Lumpur",
"postalcode": "50088",
"province": "10",
"country": "my",
"tin_value": "ITN123456789",
"tin_scheme": 8028,
"cin_value": "BRN123456789",
"cin_scheme": 8031
}
}
```
### Step 2: Register B2Brouter as an Intermediary ERP in MyInvois Portal
[Section titled “Step 2: Register B2Brouter as an Intermediary ERP in MyInvois Portal”](#step-2-register-b2brouter-as-an-intermediary-erp-in-myinvois-portal)
To send your tax reports to MyInvois, B2Brouter must be registered as an intermediary ERP on the MyInvois portal. This registration is required to authorize us to submit tax reports on your behalf. You can find detailed instructions [here](https://app.b2brouter.net/documents/Myinvois_Portal_Add_Intermediary_B2Brouter_Guidelines.pdf).
### Step 3: Activate LHDN Tax Reporting in B2Brouter
[Section titled “Step 3: Activate LHDN Tax Reporting in B2Brouter”](#step-3-activate-lhdn-tax-reporting-in-b2brouter)
You must enable the LHDN tax reporting settings in your B2Brouter account to start reporting invoices to LHDN.
API Endpoint to create tax report settings for LHDN:
```http
POST https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_settings
Content-Type: application/json
X-B2B-API-Key: {YOUR_API_KEY}
X-B2B-API-Version: {YOUR_API_VERSION}
{
"tax_report_setting": {
"code": "lhdn",
"msic": "01111",
"type_operation": "services",
"classification_code": "001"
}
}
```
Fields explanation:
* **msic**: Malaysian business classification code, required by LHDN.
* **type\_operation**: Type of your business operations (services or goods). Default filled with this value if it’s not specified in your invoice.
* **classification\_code**: Specific code related to your product line category. Default filled with this value if it’s not specified in your invoice.
To correctly configure tax reporting for Malaysia, you must use official codes provided by the LHDN. You can find them here: [LHDN Code List](https://sdk.myinvois.hasil.gov.my/codes/)
### Step 4: Create and issue invoices
[Section titled “Step 4: Create and issue invoices”](#step-4-create-and-issue-invoices)
Create an invoice at B2Brouter using our API. You have two options:
#### 1. Create an issued invoice using a [JSON payload](https://developer.b2brouter.net/reference/create-invoice)
[Section titled “1. Create an issued invoice using a JSON payload”](#1-create-an-issued-invoice-using-a-json-payload)
**Example request:**
```shell
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'
```
#### 2. Create an issued invoice by importing a [supported file format](https://developer.b2brouter.net/reference/import-invoice)
[Section titled “2. Create an issued invoice by importing a supported file format”](#2-create-an-issued-invoice-by-importing-a-supported-file-format)
**Example request:**
```shell
curl --request POST \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices/import?send_after_import=false&transport_type_code_for_contact=email&document_type_code_for_contact=xml.ubl.invoice.pint.billing.my.v1' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/octet-stream' \
--data 'data:text/xml;name=Invoice-23432234533.xml;base64,{BASE64_RAW_XML}'
```
### Step 5: Submit tax report
[Section titled “Step 5: Submit tax report”](#step-5-submit-tax-report)
If you issued automatically the invoice that you just created (by setting the parameter **send\_after\_import** in the API call), the tax report corresponding to the invoice will be generated automatically and sent to the Tax Authority for validation.
Alternatively, if you want more control over the issuing process, you can explicitly generate the tax report by calling the [generate tax report method](https://developer.b2brouter.net/reference/generate-tax-report-from-invoice).
### Step 6: Check the status of the tax report
[Section titled “Step 6: Check the status of the tax report”](#step-6-check-the-status-of-the-tax-report)
The process of sending tax reports to the LHDN is asynchronous — we generate and submit the tax report in a background process.
You will have to check the status of the tax report we send on your behalf. You can either:
* Check a [specific Tax Report](https://developer.b2brouter.net/reference/get-tax-report) by using its ID.
* Get a list of tax reports associated with an invoice ID by calling the [list tax reports method](https://developer.b2brouter.net/reference/get-tax-reports).
Because of the asynchronous nature of the tax report sending process, there is no guarantee that when you check the status it will already have the response of the Tax Authority. It usually takes a few seconds to complete the transaction. The transaction is complete if the status is **registered** or **error**. We recommend implementing an [exponential back off strategy](https://en.wikipedia.org/wiki/Exponential_backoff) for retries.
# KSeF
> How to comply with Poland's KSeF mandatory e-invoicing system using the B2Brouter API.
Available from version 2025-10-13
The KSeF (Krajowy System e-Faktur - National e-Invoice System) is Poland’s mandatory electronic invoicing system operated by the Polish Ministry of Finance. KSeF ensures the integrity and authenticity of invoices through structured XML format submission and digital certification.
These regulations require businesses operating in Poland to adopt systems capable of securely generating, validating, and transmitting invoices to the KSeF platform. B2Brouter’s REST API provides a technical solution that allows your business to be compliant with KSeF, offloading the complexities to B2Brouter and allowing you to focus on your business logic.
## What is KSeF?
[Section titled “What is KSeF?”](#what-is-ksef)
KSeF is Poland’s centralized e-invoicing platform that became mandatory for VAT taxpayers. It ensures that all invoices are:
* Validated against official XML schemas (FA structure)
* Digitally certified using qualified certificates
* Securely stored by the Polish tax authority
* Verifiable through an official receipt (UPO - Urzędowe Poświadczenie Odbioru)
In practice, KSeF requires:
* Creating a structured invoice XML file conforming to FA schema variant 3 - FA(3)
* Authenticating with the KSeF platform using a qualified electronic certificate linked to your Polish NIP
* Submitting the invoice to KSeF for validation and registration
* Receiving and storing the UPO (Official Receipt) as proof of registration
* Including the QR code with the KSeF reference number on issued invoices
With B2Brouter you can abstract much of the complexities of this process and comply with KSeF. You will need to provide your qualified electronic certificate (PKCS#12 format), and B2Brouter will handle authentication, submission, and UPO retrieval.
B2Brouter generates tax reports using the **FA(3)** format (Form variant 3, schema version 1-0E), which is the current standard for KSeF submissions.
## Setting up KSeF with B2Brouter API
[Section titled “Setting up KSeF with B2Brouter API”](#setting-up-ksef-with-b2brouter-api)
The first step is to configure KSeF for each account (identified by Polish NIP) for which you want to submit tax reports. You can refer to the [Tax Report Settings Guide](/en/developers/essential-guides/tax-report-settings/) for a full description of the process.
### B2Brouter Environments and KSeF Integration
[Section titled “B2Brouter Environments and KSeF Integration”](#b2brouter-environments-and-ksef-integration)
> **Start with [sandbox](/en/developers/testing/sandbox/)** for initial API testing and payload validation — KSeF submissions are simulated in sandbox, so no KSeF certificate is required. Switch to the staging environment when you are ready to test with a real KSeF certificate against the KSeF test or demo environment.
B2Brouter connects to different KSeF environments based on your configuration:
| B2Brouter Environment | Available KSeF Environments | Certificate Requirements |
| ------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| **Production API** `api.b2brouter.net` | **KSeF Production** only | Production KSeF certificate |
| **Staging API** `api-staging.b2brouter.net` | **KSeF Test** (default) **KSeF Demo** (configurable) | **Test**: Test KSeF certificate or self-signed **Demo**: Pre-production KSeF certificate |
**For development and testing:**
* Use the **Staging API** (`api-staging.b2brouter.net`)
* By default, it connects to KSeF’s **test environment**
* Accepts **test KSeF certificates** (recommended)
* Also accepts **self-signed certificates** for quick development and integration testing
* You can optionally configure the **demo environment** by setting `environment: "demo"` when creating the tax report setting
* Requires a **pre-production KSeF certificate**
* For **production**, only **production KSeF certificates** will work
### Certificate Requirements
[Section titled “Certificate Requirements”](#certificate-requirements)
The authentication towards KSeF uses digital certificates in PKCS#12 format (.p12 or .pfx). The type of certificate required depends on the KSeF environment:
**For Production Environment:**
* **Production KSeF certificate**
* Client must log in using their qualified signature or trusted profile (Profil Zaufany)
* Access the KSeF section and generate a dedicated KSeF certificate linked to their NIP
* Certificate subject must match your Polish NIP or be authorized via ZAW-FA
* Password/PIN for the certificate
**For Demo Environment (Pre-production):**
* **Pre-production KSeF certificate**
* Certificate must be in PKCS#12 format (.p12 or .pfx)
* Certificate subject must match your Polish NIP or be authorized via ZAW-FA
* Password/PIN for the certificate
**For Test Environment (Development):**
* **Test KSeF certificate** (recommended) or **self-signed certificate** (for development only)
* Certificate must be in PKCS#12 format (.p12 or .pfx)
* For self-signed certificates: certificate serial number should match the VAT number (NIP)
* Password/PIN for the certificate
**Example: Encoding your certificate**
```bash
# Encode your certificate to base64 without line breaks
base64 -w 0 your-certificate.p12 > certificate-base64.txt
```
### Creating KSeF Tax Report Setting
[Section titled “Creating KSeF Tax Report Setting”](#creating-ksef-tax-report-setting)
You need to configure the KSeF integration for each company you want to submit structured invoices for. This configuration defines how B2Brouter handles invoice generation and submission to KSeF on behalf of your system. You must configure these settings per company before submitting any invoice.
* This configuration step will onboard the client to KSeF and perform a connection test.
* If the certificate or configuration is invalid, the endpoint will return an error.
**Example request:**
```shell
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": "ksef",
"type_operation": "services",
"credit_note_code": "1",
"certificate": "MIIKZAIBAzCCCh4GCSqGSIb3DQEHAaCCCg8Egg...",
"certificate_pin": "YourCertificatePassword",
"auto_generate": true,
"auto_send": true,
"enabled": true
}
}'
```
**Parameters:**
* `code`: Must be “ksef”
* `type_operation`: Default operation type (“services” or “goods”)
* `credit_note_code`: Default correction effect code (“1”, “2”, or “3”)
* `certificate`: Your PKCS#12 certificate encoded in base64
* `certificate_pin`: Password for the certificate
* `auto_generate`: Automatically generate tax reports for invoices
* `auto_send`: Automatically send tax reports to KSeF
## Working with KSeF Tax Reports
[Section titled “Working with KSeF Tax Reports”](#working-with-ksef-tax-reports)
B2Brouter provides two ways to work with KSeF:
1. **Tax Report API**: For direct control over tax report creation and submission (RECOMMENDED).
2. **Invoice API**: If you use B2Brouter for issuing invoices, tax reports are automatically generated.
### Tax Report API
[Section titled “Tax Report API”](#tax-report-api)
The Tax Report API gives you maximum control over the KSeF submission process. You can create tax reports by providing structured JSON data that B2Brouter will convert to FA(3) XML format.
The structure of the JSON format for B2Brouter tax reports is based on [PEPPOL Continuous Transaction Control (CTC)](https://peppol.org/documentation/technical-documentation/ctc-documentation/). This means that B2Brouter Tax Report API is a universal API not only geared towards KSeF but designed to handle tax reporting around the world.
**Important:** Tax reports for KSeF use **tax\_report\_lines** and **tax\_breakdowns**. Each tax\_report\_line represents an invoice line item with its own quantity, position, unit\_code, price, and tax information.
#### Create a Tax Report
[Section titled “Create a Tax Report”](#create-a-tax-report)
To create a KSeF tax report, call the [create tax report](https://developer.b2brouter.net/reference/create-tax-report) endpoint.
**Example: Basic VAT Invoice**
```shell
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": "KSeF",
"invoice_type_code": "VAT",
"invoice_date": "2025-11-07",
"tax_point_date": "2025-11-07",
"invoice_number": "F/2025/11/001",
"description": "Consulting services",
"customer_party_name": "Example Company Sp. z o.o.",
"customer_party_tax_id": "1234567890",
"customer_party_address": "Address 1",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 1230.00,
"tax_amount": 230.00,
"type_operation": "services",
"tax_report_lines": [
{
"position": 1,
"quantity": 10,
"unit_code": "EA",
"description": "Consulting hours",
"price": 100.00,
"tax_code": "23",
"tax_exclusive_amount": 1000.00,
"tax_amount": 230.00,
"tax_inclusive_amount": 1230.00
}
],
"tax_breakdowns": [
{
"name": "PTU",
"category": "S",
"percent": 23.0,
"taxable_base": 1000.0,
"tax_amount": 230.00
}
]
}
}'
```
**Example: Corrective Invoice (KOR)**
```shell
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": "KSeF",
"invoice_type_code": "KOR",
"amend_type": "2",
"invoice_date": "2025-11-08",
"tax_point_date": "2025-11-07",
"invoice_number": "FK/2025/11/001",
"description": "Correction of invoice F/2025/11/001",
"amended_ksef_number": "1234567890-20251107-ABCD1234-EF",
"amended_date": "2025-11-07",
"amended_number": "F/2025/11/001",
"customer_party_name": "Example Company Sp. z o.o.",
"customer_party_tax_scheme": "9945",
"customer_party_tax_id": "1234567890",
"customer_party_address": "Address 1",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 1230.00,
"tax_amount": 230.00,
"type_operation": "services",
"tax_report_lines": [
{
"position": 1,
"quantity": 10,
"unit_code": "EA",
"description": "Consulting hours",
"price": 100.00,
"tax_code": "23",
"tax_exclusive_amount": 1000.00,
"tax_amount": 230.00,
"tax_inclusive_amount": 1230.00,
"ksef_amended": true
},
{
"position": 1,
"quantity": 10,
"unit_code": "PCE",
"description": "Consulting hours - new line",
"price": 100.00,
"tax_code": "23",
"tax_exclusive_amount": 1000.00,
"tax_amount": 230.00,
"tax_inclusive_amount": 1230.00
}
],
"tax_breakdowns": [
{
"name": "PTU",
"category": "S",
"percent": 23.0,
"taxable_base": 1000.0,
"tax_amount": 230.00
}
]
}
}'
```
**Example: Exempt Export of Goods - VAT Invoice (foreign currency)**
```shell
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": "KSeF",
"invoice_type_code": "VAT",
"invoice_date": "2025-11-07",
"tax_point_date": "2025-11-07",
"invoice_number": "F/2025/11/003",
"description": "Sample Exempt VAT Invoice - Export of Goods",
"customer_party_name": "EFG Ltd.",
"customer_party_tax_id": "1234567890",
"customer_party_address": "Flower (St) 1, Seattle, WA 99999",
"customer_party_country": "us",
"currency": "USD",
"exchange_rate": 3.65,
"tax_inclusive_amount": 8000,
"tax_amount": 0,
"type_operation": "goods",
"tax_report_lines": [
{
"position": 1,
"quantity": 20,
"unit_code": "szt.",
"description": "lodówka Zimnotech mk1",
"price": 400,
"tax_code": "0 EX",
"tax_exclusive_amount": 8000,
"tax_amount": 0,
"tax_inclusive_amount": 8000
}
],
"tax_breakdowns": [
{
"name": "PTU",
"category": "E",
"percent": 0.0,
"taxable_base": 8000,
"tax_amount": 0,
"comment": "reason why exempt"
}
]
}
}'
```
**Example: VAT Invoice (full payment received at time of issue) - Zaplacono**
```shell
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": "KSeF",
"invoice_type_code": "VAT",
"invoice_date": "2025-11-07",
"tax_point_date": "2025-11-07",
"payment_date": "2025-11-07",
"payable_amount": 0,
"invoice_number": "F/2025/11/004",
"description": "Sample VAT Invoice (full payment has been received at the time of issue)",
"customer_party_name": "Example Company Sp. z o.o.",
"customer_party_tax_scheme": "9945",
"customer_party_tax_id": "1234567890",
"customer_party_address": "Address 1",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 1230.00,
"tax_amount": 230.00,
"type_operation": "goods",
"tax_report_lines": [
{
"position": 1,
"quantity": 10,
"unit_code": "EA",
"description": "product 1",
"price": 100.00,
"tax_code": "23",
"tax_exclusive_amount": 1000.00,
"tax_amount": 230.00,
"tax_inclusive_amount": 1230.00
}
],
"tax_breakdowns": [
{
"name": "PTU",
"category": "S",
"percent": 23.0,
"taxable_base": 1000.0,
"tax_amount": 230.00
}
]
}
}'
```
**Example: ZAL Invoice (advance payment)**
```shell
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": "KSeF",
"invoice_type_code": "ZAL",
"invoice_date": "2025-11-07",
"tax_point_date": "2025-11-07",
"payment_date": "2025-11-07",
"payable_amount": 0,
"invoice_number": "F/2025/11/005",
"description": "Sample ZAL Invoice (full payment has been received at the time of issue)",
"customer_party_name": "Example Company Sp. z o.o.",
"customer_party_tax_scheme": "9945",
"customer_party_tax_id": "1234567890",
"customer_party_address": "Address 1",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 1230.00,
"tax_amount": 230.00,
"type_operation": "goods",
"tax_report_lines": [
{
"position": 1,
"quantity": 10,
"unit_code": "EA",
"description": "product 1",
"price": 100.00,
"tax_code": "23",
"tax_exclusive_amount": 1000.00,
"tax_amount": 230.00,
"tax_inclusive_amount": 1230.00
}
],
"tax_breakdowns": [
{
"name": "PTU",
"category": "S",
"percent": 23.0,
"taxable_base": 1000.0,
"tax_amount": 230.00
}
]
}
}'
```
**Example: ROZ Invoice (settlement of advance payments)**
Available from version 2026-04-20
A ROZ invoice settles the remaining amount after all advances. It references previous ZAL invoices via `prepayment_references`. Each reference maps to a `FakturaZaliczkowa` element in the FA(3) XML.
```shell
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": "KSeF",
"invoice_type_code": "ROZ",
"invoice_date": "2026-08-17",
"tax_point_date": "2026-09-17",
"invoice_number": "FV2026/08/12",
"description": "Settlement invoice",
"customer_party_name": "Jan Kowalski",
"customer_party_tax_id": "1234567890",
"customer_party_address": "ul. Przykładowa 1",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 306899.80,
"tax_amount": 22720.76,
"type_operation": "goods",
"prepayment_references": [
{ "registration_code": "9999999999-20260215-8BEF280C8D35-4D" },
{ "number": "FZ2026/03/200" }
],
"tax_report_lines": [
{
"position": 1,
"quantity": 1,
"unit_code": "C62",
"description": "mieszkanie 50m^2",
"price": 280177.59,
"tax_code": "8",
"tax_exclusive_amount": 280177.59,
"tax_amount": 22414.21,
"tax_inclusive_amount": 302591.80
},
{
"position": 2,
"quantity": 1,
"unit_code": "C62",
"description": "usługi dodatkowe",
"price": 4001.55,
"tax_code": "23",
"tax_exclusive_amount": 4001.55,
"tax_amount": 306.55,
"tax_inclusive_amount": 4308.00
}
],
"tax_breakdowns": [
{ "name": "PTU", "category": "S", "percent": 8.0, "taxable_base": 280177.59, "tax_amount": 22414.21 },
{ "name": "PTU", "category": "S", "percent": 23.0, "taxable_base": 4001.55, "tax_amount": 306.55 }
]
}
}'
```
* `prepayment_references` — use `registration_code` for the KSeF number (`NrKSeFFaZaliczkowej`) or `number` for the invoice number (`NrFaZaliczkowej`) when the ZAL was issued outside KSeF.
**Example: KOR\_ZAL Invoice (correction of advance payment)**
Available from version 2026-04-20
A KOR\_ZAL corrects a previously issued ZAL invoice. Use `previous_advance_total` to indicate the original advance amount (`P_15ZK` in FA(3) XML).
```shell
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": "KSeF",
"invoice_type_code": "KOR_ZAL",
"amend_type": "1",
"invoice_date": "2026-03-17",
"tax_point_date": "2026-02-15",
"invoice_number": "FK2026/03/7",
"description": "Correction of advance invoice FZ2026/02/150",
"amended_ksef_number": "9999999999-20260215-8BEF280C8D35-4D",
"amended_date": "2026-02-15",
"amended_number": "FZ2026/02/150",
"customer_party_name": "Jan Kowalski",
"customer_party_tax_id": "1234567890",
"customer_party_address": "ul. Przykładowa 1",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 25000.00,
"tax_amount": 4674.80,
"previous_advance_total": 20000.00,
"type_operation": "goods",
"tax_report_lines": [
{
"position": 1,
"quantity": 1,
"unit_code": "C62",
"description": "mieszkanie 50m^2",
"price": 300000,
"tax_code": "23",
"tax_exclusive_amount": 20325.20,
"tax_amount": 4674.80,
"tax_inclusive_amount": 25000.00
}
],
"tax_breakdowns": [
{ "name": "PTU", "category": "S", "percent": 23.0, "taxable_base": 20325.20, "tax_amount": 4674.80 }
]
}
}'
```
* `previous_advance_total: 20000.00` — the gross total of the original ZAL being corrected (maps to `P_15ZK`).
**Example: JST Invoice (local government unit)**
Available from version 2026-04-20
An invoice addressed to a subordinate local government unit (JST). The buyer (`Podmiot2`) is the parent entity; the actual recipient is a `Podmiot3` with role `8`.
```shell
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": "KSeF",
"invoice_type_code": "VAT",
"invoice_date": "2026-03-23",
"tax_point_date": "2026-03-23",
"invoice_number": "F/2026/JST/001",
"description": "Services for local government unit",
"customer_party_name": "Gmina Kraków",
"customer_party_tax_id": "6762450871",
"customer_party_address": "Plac Wszystkich Świętych 3-4",
"customer_party_country": "pl",
"customer_party_jst": true,
"third_party_name": "Szkoła Podstawowa nr 7",
"third_party_tax_id": "7654321098",
"third_party_tax_scheme": "9945",
"currency": "PLN",
"tax_inclusive_amount": 1230.00,
"tax_amount": 230.00,
"type_operation": "services",
"tax_report_lines": [
{
"position": 1,
"quantity": 10,
"unit_code": "EA",
"description": "Consulting services",
"price": 100.00,
"tax_code": "23",
"tax_exclusive_amount": 1000.00,
"tax_amount": 230.00,
"tax_inclusive_amount": 1230.00
}
],
"tax_breakdowns": [
{ "name": "PTU", "category": "S", "percent": 23.0, "taxable_base": 1000.0, "tax_amount": 230.00 }
]
}
}'
```
* `customer_party_jst: true` — sets `Podmiot2/JST` to `1` in the FA(3) XML.
* `third_party_name` / `third_party_tax_id` — the JST recipient, rendered as `Podmiot3` with role `8`.
* For VAT group member invoices, use `customer_party_gv: true` instead (role `10`).
**Example: UPR Invoice**
For UPR, the `customer_party_tax_id` and `customer_party_country` fields are required.
```shell
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": "KSeF",
"invoice_type_code": "UPR",
"ticket": true,
"invoice_date": "2025-11-07",
"tax_point_date": "2025-11-07",
"invoice_number": "F/2025/11/006",
"description": "Sample UPR Invoice",
"customer_party_tax_id": "1234567890",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 12.30,
"tax_amount": 2.30,
"type_operation": "goods",
"tax_report_lines": [
{
"position": 1,
"quantity": 1,
"unit_code": "EA",
"description": "product 1",
"price": 10.00,
"tax_code": "23",
"tax_exclusive_amount": 10.00,
"tax_amount": 2.30,
"tax_inclusive_amount": 12.30
}
],
"tax_breakdowns": [
{
"name": "PTU",
"category": "S",
"percent": 23.0,
"taxable_base": 10.0,
"tax_amount": 2.30
}
]
}
}'
```
**Example: Domestic Reverse Charge Invoice**
```shell
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": "KSeF",
"invoice_type_code": "VAT",
"invoice_date": "2025-11-07",
"tax_point_date": "2025-11-07",
"invoice_number": "F/2025/11/007",
"description": "Construction services - reverse charge",
"customer_party_name": "Example Construction Sp. z o.o.",
"customer_party_tax_id": "1234567890",
"customer_party_address": "Address 1",
"customer_party_country": "pl",
"currency": "PLN",
"tax_inclusive_amount": 5000.00,
"tax_amount": 0.00,
"type_operation": "services",
"tax_report_lines": [
{
"position": 1,
"quantity": 1,
"unit_code": "EA",
"description": "Construction services per contract X",
"price": 5000.00,
"tax_code": "oo",
"tax_exclusive_amount": 5000.00,
"tax_amount": 0.00,
"tax_inclusive_amount": 5000.00
}
],
"tax_breakdowns": [
{
"name": "VAT",
"category": "AE",
"percent": 0.0,
"taxable_base": 5000.0,
"tax_amount": 0.0,
"scope": "domestic"
}
]
}
}'
```
**Example: Cross-border Reverse Charge (Services)**
```shell
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": "KSeF",
"invoice_type_code": "VAT",
"invoice_date": "2025-11-07",
"tax_point_date": "2025-11-07",
"invoice_number": "F/2025/11/008",
"description": "Consulting services - cross-border",
"customer_party_name": "Example Company GmbH",
"customer_party_tax_id": "DE123456789",
"customer_party_address": "Berlin Street 1, 10115 Berlin",
"customer_party_country": "de",
"currency": "EUR",
"exchange_rate": 4.30,
"tax_inclusive_amount": 3000.00,
"tax_amount": 0.00,
"type_operation": "services",
"tax_report_lines": [
{
"position": 1,
"quantity": 20,
"unit_code": "HUR",
"description": "IT consulting services",
"price": 150.00,
"tax_code": "np I",
"tax_exclusive_amount": 3000.00,
"tax_amount": 0.00,
"tax_inclusive_amount": 3000.00
}
],
"tax_breakdowns": [
{
"name": "VAT",
"category": "AE",
"percent": 0.0,
"taxable_base": 3000.0,
"tax_amount": 0.0
}
]
}
}'
```
#### Response Structure
[Section titled “Response Structure”](#response-structure)
The response will include the tax report with state “sending”:
```json
{
"tax_report": {
"id": 12345,
"type": "KSeF",
"state": "sending",
"fingerprint": "a3f2c1b9e8d7...",
"identifier": "https://ksef-test.mf.gov.pl/client-app/invoice/5792000046/2025-11-07/a3f2c1b9e8d7...",
"qr": "iVBORw0KGgoAAAANSUhEUgAA...",
...
}
}
```
**Important:** Unlike traditional clearance models, B2Brouter can generate the `fingerprint`, `identifier` (QR code URL), and `qr` (QR code image) **immediately** when creating the tax report, without waiting for KSeF confirmation. This is possible because these fields are calculated from data we already have:
* **`fingerprint`**: SHA-256 hash of the FA(3) XML document
* **`identifier`**: URL constructed from: supplier NIP + invoice date + fingerprint (Base64URL format)
* **`qr`**: QR code image encoding the `identifier` URL
**QR Code Label:**
* While the tax report is in `sending` state (before KSeF confirmation), the QR code label should display **“OFFLINE”**
* Once the tax report reaches `registered` state and you receive the `to_net_id` (KSeF reference number), you should display the **KSeF number** under the QR code instead
**Best Practice:** Although the QR code data is available immediately, it is **recommended to wait** until the tax report reaches `registered` state before printing/sending invoices to customers. This ensures the invoice has been successfully accepted by KSeF and avoids potential issues if the submission fails.
#### Check Tax Report State
[Section titled “Check Tax Report State”](#check-tax-report-state)
The submission to KSeF is asynchronous. You must monitor the tax report state:
**Option 1: Using Webhooks (Recommended)**
Configure a [tax report webhook](https://developer.b2brouter.net/reference/getnewtaxreportstatechange) to receive notifications when the tax report state changes.
`registered` (with `to_net_id` populated) is the only positive terminal state — it confirms KSeF has accepted and registered the invoice. `error` is **not necessarily terminal**: it can be temporal (a transport/connectivity failure that B2Brouter retries automatically) or permanent (a KSeF business validation rejection that will not be retried). See [Distinguishing temporal vs. permanent errors](#distinguishing-temporal-vs-permanent-errors) before treating an `error` webhook as a definitive outcome.
**Option 2: Polling**
Poll the [get tax report endpoint](https://developer.b2brouter.net/reference/get-tax-report) until the state is `registered` or a permanent `error`:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/tax_reports/{TAX_REPORT_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
#### Understanding the Registered State Response
[Section titled “Understanding the Registered State Response”](#understanding-the-registered-state-response)
When the tax report reaches `registered` state, the response includes the official KSeF reference number:
```json
{
"tax_report": {
"id": 12345,
"type": "KSeF",
"state": "registered",
"to_net_id": "1234567890-20251107-ABCD1234-EF",
"fingerprint": "a3f2c1b9e8d7...",
"identifier": "https://ksef-test.mf.gov.pl/client-app/invoice/1234567890/2025-11-07/a3f2c1b9e8d7...",
"qr": "iVBORw0KGgoAAAANSUhEUgAA...",
...
}
}
```
| Field | Available | Description |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `to_net_id` | After KSeF confirmation | **KSeF Reference Number** - The unique identifier assigned by KSeF. Display this number under the QR code on printed/PDF invoices. |
| `fingerprint` | Immediately on creation | **SHA-256 hash** of the submitted FA(3) XML document. Used for document integrity verification and QR code generation. |
| `identifier` | Immediately on creation | **QR code URL** - The complete URL that the QR code points to. Constructed from: supplier NIP + invoice date + fingerprint. |
| `qr` | Immediately on creation | **QR code image** - Base64-encoded PNG image of the QR code. Can be embedded in invoices immediately, even before KSeF confirmation. |
#### Download the UPO (Official Receipt)
[Section titled “Download the UPO (Official Receipt)”](#download-the-upo-official-receipt)
Once the tax report reaches `registered` state, you can download the UPO using the [download response endpoint](https://developer.b2brouter.net/reference/download-tax-report-response):
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/tax_reports/{TAX_REPORT_ID}/download_response \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Accept: application/xml'
```
#### Download the FA(3) XML
[Section titled “Download the FA(3) XML”](#download-the-fa3-xml)
You can also download the complete FA(3) XML that was sent to KSeF:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/tax_reports/{TAX_REPORT_ID}/download \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Accept: application/xml'
```
Alternatively, the `xml_base64` field in the tax report response contains the XML encoded in base64.
#### Error Handling
[Section titled “Error Handling”](#error-handling)
If the tax report reaches `error` state, check the tax report details for error information:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/tax_reports/{TAX_REPORT_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
Before treating this `error` as a final, undelivered outcome, see [Distinguishing temporal vs. permanent errors](#distinguishing-temporal-vs-permanent-errors) — some errors are retried automatically and the tax report can still reach `registered`.
#### Distinguishing temporal vs. permanent errors
[Section titled “Distinguishing temporal vs. permanent errors”](#distinguishing-temporal-vs-permanent-errors)
Not every `error` state is definitive. B2Brouter classifies errors into two categories:
* **Temporal errors** — caused by transport or connectivity issues rather than KSeF rejecting the invoice: request timeouts, DNS/TCP failures, or HTTP `401`, `403`, `503` responses from KSeF. These are retried automatically with exponential backoff (60s → 120s → 240s → 480s → …). A tax report can legitimately go through `sending` → `error` → `sending` → `registered` as a result.
* **Permanent errors** — KSeF rejected the invoice on business validation grounds (e.g. schema or data validation failure). These are not retried; the tax report will stay in `error` state.
Because of this, do not treat an `error` webhook or polling result as a final “undelivered” verdict on its own. Before acting on it:
1. Check whether `to_net_id` is populated — if it is, the tax report has since reached `registered` and the earlier `error` was transient.
2. Inspect the error description on the tax report for timeout, DNS/TCP, or `401`/`403`/`503` patterns, which indicate a temporal error that may still resolve on a later automatic retry.
3. Only treat `error` as final once retries are exhausted and the error description points to a KSeF business validation rejection rather than a transport failure.
## Equivalence between B2Brouter Tax Report fields and KSeF FA(3) XML nodes
[Section titled “Equivalence between B2Brouter Tax Report fields and KSeF FA(3) XML nodes”](#equivalence-between-b2brouter-tax-report-fields-and-ksef-fa3-xml-nodes)
Changed in version 2026-03-02
Mapping corrections and new fields documented below.
Changed in version 2026-04-20
Added JST/GV, supplier identifiers, supplier contact, advance payment fields.
The structure of the JSON format for B2Brouter tax reports is based on [PEPPOL Continuous Transaction Control (CTC)](https://peppol.org/documentation/technical-documentation/ctc-documentation/). The fields used in the JSON payload have different names than in the FA(3) XML representation. This is because B2Brouter Tax Report API is a universal API designed to handle tax reporting worldwide, not just KSeF.
### Main Invoice Fields
[Section titled “Main Invoice Fields”](#main-invoice-fields)
| B2Brouter Field | KSeF FA(3) XML Node |
| -------------------------------------- | --------------------------------------------------------------------------------------- |
| type | Must be “KSeF” |
| invoice\_type\_code | Fa > RodzajFaktury |
| invoice\_date | Fa > P\_1 |
| invoice\_number | Fa > P\_2 |
| tax\_point\_date | Fa > P\_6 |
| customer\_party\_name | Podmiot2 > DaneIdentyfikacyjne > Nazwa |
| customer\_party\_tax\_id | Podmiot2 > DaneIdentyfikacyjne > NIP (PL) or NrVatUE (EU) or NrID (non-EU) |
| customer\_party\_country | Podmiot2 > DaneIdentyfikacyjne > KodUE or KodKraju |
| supplier\_party\_name | Podmiot1 > DaneIdentyfikacyjne > Nazwa |
| supplier\_party\_tax\_id | Podmiot1 > DaneIdentyfikacyjne > NIP |
| supplier\_party\_country | Podmiot1 > PrefiksPodatnika |
| description | Stopka > Informacje > StopkaFaktury |
| currency | Fa > KodWaluty |
| exchange\_rate | Fa > KursWalutyZ |
| payment\_date | Fa > Platnosc > TerminPlatnosci > Termin (unpaid) or Fa > Platnosc > DataZaplaty (paid) |
| payable\_amount | When set to 0 with payment\_date, marks invoice as paid (Zaplacono=1) |
| payment\_means\_type\_code | Fa > Platnosc > FormaPlatnosci |
| payment\_account\_identifier | Fa > Platnosc > RachunekBankowy > NrRB |
| payment\_service\_provider\_identifier | Fa > Platnosc > RachunekBankowy > SWIFT |
| amended\_ksef\_number | Fa > DaneFaKorygowanej > NrKSeFFaKorygowanej |
| tax\_inclusive\_amount | Fa > P\_15 (total amount) |
| customer\_party\_jst | Podmiot2 > JST (`1` when true, `2` when false) |
| customer\_party\_gv | Podmiot2 > GV (`1` when true, `2` when false) |
| third\_party\_name | Podmiot3 > DaneIdentyfikacyjne > Nazwa |
| third\_party\_tax\_id | Podmiot3 > DaneIdentyfikacyjne > NIP (PL), NrVatUE (EU), or NrID (non-EU) |
| purchase\_order\_date | Fa > WarunkiTransakcji > Zamowienia > DataZamowienia |
| despatch\_advice\_reference | Fa > WZ |
| supplier\_contact\_email | Podmiot1 > DaneKontaktowe > Email |
| supplier\_contact\_phone | Podmiot1 > DaneKontaktowe > Telefon |
| supplier\_party\_regon | Stopka > Rejestry > REGON |
| supplier\_party\_krs | Stopka > Rejestry > KRS |
| supplier\_party\_bdo | Stopka > Rejestry > BDO |
| previous\_advance\_total | Fa > P\_15ZK (KOR\_ZAL only) |
| prepayment\_references | Fa > FakturaZaliczkowa (ROZ/KOR\_ROZ only) |
### Tax Report Lines (FaWiersz)
[Section titled “Tax Report Lines (FaWiersz)”](#tax-report-lines-fawiersz)
| B2Brouter Field | KSeF FA(3) XML Node |
| ---------------------- | ------------------------------------------- |
| position | FaWiersz > NrWierszaFa |
| description | FaWiersz > P\_7 |
| unit\_code | FaWiersz > P\_8A |
| quantity | FaWiersz > P\_8B |
| price | FaWiersz > P\_9A |
| discount\_amount | FaWiersz > P\_10 |
| tax\_code | FaWiersz > P\_12 |
| tax\_exclusive\_amount | FaWiersz > P\_11 |
| tax\_amount | FaWiersz > P\_11Vat |
| ksef\_amended | FaWiersz > StanPrzed (marks corrected line) |
### Tax Breakdowns
[Section titled “Tax Breakdowns”](#tax-breakdowns)
| B2Brouter Field | KSeF FA(3) XML Node |
| -------------------- | ------------------------------------------------------------------------ |
| name | Tax scheme name (e.g., “PTU”, “VAT”) |
| category | Tax category: “S” (standard), “E” (exempt), “AE” (reverse charge) |
| percent | Tax rate percentage |
| taxable\_base | P\_13\_x (taxable amount per rate bucket) |
| tax\_amount | P\_14\_x (tax amount per rate bucket) |
| comment | Adnotacje > Zwolnienie > P\_19A (exemption reason, when category is “E”) |
| exemption\_code | P\_12 override — accepts `zw` |
| no\_subject\_code | P\_12 override — accepts `np I`, `np II` |
| non\_exemption\_code | P\_12 override — accepts `0 KR`, `0 WDT`, `0 EX`, `oo` |
The three `*_code` fields are optional. When unset, B2Brouter derives the P\_12 code from `category`, `percent`, `scope` and `type_operation`. Set one of them to pin the code explicitly.
### FA(3) Structure Overview
[Section titled “FA(3) Structure Overview”](#fa3-structure-overview)
```xml
FA
3
B2Brouter
```
## Code Descriptions for KSeF-Specific Fields
[Section titled “Code Descriptions for KSeF-Specific Fields”](#code-descriptions-for-ksef-specific-fields)
### Invoice Type Codes
[Section titled “Invoice Type Codes”](#invoice-type-codes)
Param: `invoice_type_code`
| Code | Description | Used for |
| -------- | ----------------------------------------------- | ---------------- |
| VAT | Basic invoice | Regular invoices |
| KOR | Corrective invoice | Corrections |
| ZAL | Advance payment invoice (Article 106f sec. 4) | Advance payments |
| ROZ | Invoice per Article 106f sec. 3 | Settlements |
| UPR | Simplified invoice (Article 106e sec. 5 item 3) | Simplified |
| KOR\_ZAL | Corrective invoice for advance payment | Corrections |
| KOR\_ROZ | Corrective invoice for ROZ | Corrections |
### Credit Note Effect Codes
[Section titled “Credit Note Effect Codes”](#credit-note-effect-codes)
Type of effect of correction in VAT records. Used for corrective invoices (KOR, KOR\_ZAL, KOR\_ROZ). Param: `credit_note_code` (in Tax Report Setting)
| Code | Description |
| ---- | ----------------------------------------------------------------------------------- |
| 1 | Correction with effect on the date of recognition of the original invoice |
| 2 | Correction with effect on the date of issue of the corrective invoice |
| 3 | Correction with effect on a different date (or different dates for different items) |
### Tax Codes
[Section titled “Tax Codes”](#tax-codes)
VAT rates and categories used in Poland. Used at line level. Param: `tax_code` (in tax\_report\_lines)
| Code | Description |
| ----- | --------------------------------------------------------------- |
| 23 | Standard VAT rate 23% |
| 22 | Standard VAT rate 22% |
| 8 | Reduced VAT rate 8% |
| 7 | Reduced VAT rate 7% |
| 5 | Reduced VAT rate 5% |
| 4 | Reduced VAT rate 4% |
| 3 | Reduced VAT rate 3% |
| 0 WDT | Intra-Community Supply 0% (Wewnątrzwspólnotowa Dostawa Towarów) |
| 0 EX | Export 0% |
| 0 KR | Domestic zero-rated 0% |
| zw | Exempt from VAT (zwolniona) |
| oo | Reverse charge domestic (odwrotne obciążenie) |
| np I | Not subject to VAT - cross-border services (nie podlega) |
| np II | Not subject to VAT - domestic services |
### Payment Means Codes
[Section titled “Payment Means Codes”](#payment-means-codes)
Param: `payment_means_type_code`
| Code | Description |
| ---- | ------------- |
| 1 | Cash |
| 2 | Card |
| 3 | Bon |
| 4 | Mobile |
| 5 | Check |
| 6 | Bank transfer |
| 7 | Other |
### Operation Types
[Section titled “Operation Types”](#operation-types)
Param: `type_operation` (in Tax Report Setting)
| Code | Description |
| -------- | ----------- |
| services | Services |
| goods | Goods |
# ZATCA
> How to comply with ZATCA e-invoicing requirements in Saudi Arabia using B2Brouter.
The [ZATCA e-Invoicing Resolution](https://zatca.gov.sa/en/E-Invoicing/Introduction/Pages/default.aspx) (Zakat, Tax and Customs Authority) mandates that businesses operating in Saudi Arabia must generate and submit electronic invoices in UBL XML format. B2Brouter’s REST API allows your business to be fully compliant with ZATCA, handling XML generation, hash chaining, and clearance on your behalf.
## What is ZATCA e-Invoicing?
[Section titled “What is ZATCA e-Invoicing?”](#what-is-zatca-e-invoicing)
ZATCA e-Invoicing (Fatoorah) requires:
* Generating invoices in UBL 2.1 XML format, with Saudi-specific extensions.
* Computing a SHA-256 hash chain linking each invoice to the previous one.
* Submitting each invoice to the ZATCA Clearance API for validation and digital signing.
* Embedding the QR code returned by ZATCA in the issued invoice.
With B2Brouter you can offload all of this. You issue invoices or create tax reports via REST calls, and B2Brouter handles chaining, clearance, and QR code generation.
## Account (Company) Requirements
[Section titled “Account (Company) Requirements”](#account-company-requirements)
ZATCA invoices are issued by Saudi Arabian companies. The account must have `country: "sa"`. The following fields are **required** for SA accounts:
| API param | Description | Notes |
| ------------ | ----------------------------------- | -------------------------- |
| `name` | Company legal name | Required |
| `country` | Must be `"sa"` | Required |
| `address` | Street name | Required |
| `address2` | Building number | Required — 4-digit integer |
| `city` | City | Required |
| `postalcode` | Postal code | Required |
| `province` | District | Required |
| `tin_value` | VAT registration number (15 digits) | Required |
| `tin_scheme` | Must be `8025` (SA VAT) | Required |
> **Note:** The fields `address2` and `province` correspond to the Saudi address concepts of *building number* and *district* respectively. Use `address2` and `province` as the API param names — the API will return errors referencing these names.
## Contact Requirements
[Section titled “Contact Requirements”](#contact-requirements)
Contacts (buyers) for ZATCA invoices must also have correct address data. The following fields apply when `country` is `"sa"`:
| API param | Description | Notes |
| ------------ | --------------------------------------------------------------------------- | ------------------------------------------- |
| `name` | Buyer legal name | Required |
| `country` | ISO-2 country code | Required |
| `address` | Street name | Required for SA contacts with ZATCA enabled |
| `address2` | Building number | Required for SA contacts with ZATCA enabled |
| `city` | City | Required for SA contacts with ZATCA enabled |
| `postalcode` | Postal code | Required for SA contacts with ZATCA enabled |
| `province` | District | Required for SA contacts with ZATCA enabled |
| `tin_value` | VAT registration number | Required for B2B invoices |
| `tin_scheme` | `8025` for SA VAT; see [Buyer ID types](#buyer-id-types) for foreign buyers | Required |
## Invoice Requirements
[Section titled “Invoice Requirements”](#invoice-requirements)
ZATCA invoices use SAR (Saudi Riyal) as currency. Taxes must use `name: "VAT"` and `category: "S"` for the standard 15% rate.
**Minimal invoice example:**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'Content-Type: application/json' \
--data '{
"send_after_import": true,
"invoice": {
"date": "2025-01-15",
"number": "INV-001",
"currency": "SAR",
"payment_method": 1,
"contact": {
"name": "Buyer Company Ltd",
"address": "King Fahd Road",
"address2": "1234",
"city": "Riyadh",
"postalcode": "12345",
"province": "Riyadh",
"country": "sa",
"tin_value": "310175397400003",
"tin_scheme": 8025,
"language": "en",
"currency": "SAR"
},
"invoice_lines_attributes": [
{
"quantity": 1.0,
"description": "Professional services",
"price": 100.0,
"taxes_attributes": [
{
"name": "VAT",
"percent": 15.0,
"category": "S"
}
]
}
]
}
}'
```
## Tax Categories and Exemption Codes
[Section titled “Tax Categories and Exemption Codes”](#tax-categories-and-exemption-codes)
ZATCA defines four VAT categories. For categories **E** (Exempt) and **Z** (Zero rated), a valid exemption reason code (`comment` on the tax line) is **required** — the invoice will be rejected without it.
| Category | Description | Rate | Exemption code required? |
| -------- | --------------- | ---- | ---------------------------------------------------- |
| `S` | Standard rate | 15% | No |
| `E` | Exempt from VAT | 0% | **Yes** — VATEX-SA-29, VATEX-SA-29-7, or VATEX-SA-30 |
| `Z` | Zero rated | 0% | **Yes** — see table below |
| `O` | Out of scope | 0% | No |
The exemption code is set in the `comment` field of the tax line:
```json
{
"name": "VAT",
"percent": 0.0,
"category": "Z",
"comment": "VATEX-SA-32"
}
```
### Valid VATEX-SA-\* Codes
[Section titled “Valid VATEX-SA-\* Codes”](#valid-vatex-sa--codes)
#### Category E — Exempt from VAT
[Section titled “Category E — Exempt from VAT”](#category-e--exempt-from-vat)
| Code | Description |
| --------------- | ----------------------------------------------------------------------- |
| `VATEX-SA-29` | Financial services mentioned in Article 29 of the VAT Regulations |
| `VATEX-SA-29-7` | Life insurance services mentioned in Article 29 of the VAT Regulations |
| `VATEX-SA-30` | Real estate transactions mentioned in Article 30 of the VAT Regulations |
#### Category Z — Zero rated
[Section titled “Category Z — Zero rated”](#category-z--zero-rated)
| Code | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------- |
| `VATEX-SA-32` | Export of goods |
| `VATEX-SA-33` | Export of services |
| `VATEX-SA-34-1` | The international transport of Goods |
| `VATEX-SA-34-2` | International transport of passengers |
| `VATEX-SA-34-3` | Services directly connected and incidental to a Supply of international passenger transport |
| `VATEX-SA-34-4` | Supply of a qualifying means of transport |
| `VATEX-SA-34-5` | Any services relating to Goods or passenger transportation (Article 25 of the VAT Regulations) |
| `VATEX-SA-35` | Medicines and medical equipment |
| `VATEX-SA-36` | Qualifying metals |
| `VATEX-SA-EDU` | Private education to citizen |
| `VATEX-SA-HEA` | Private healthcare to citizen |
| `VATEX-SA-MLTRY` | Supply of qualified military goods |
| `VATEX-SA-DIPLOMAT` | Qualified Supplies to Diplomatic Missions |
| `VATEX-SA-DUTYFREE` | Qualified Supply of Goods in Duty Free area |
| `VATEX-SA-32(bis)` | Supply under Customs Suspension Arrangement |
| `VATEX-SA-ROYALDECREE` | Supply on which the Government bears the VAT |
#### Category O — Out of scope
[Section titled “Category O — Out of scope”](#category-o--out-of-scope)
| Code | Description |
| -------------- | ----------------------------------------------------------------------- |
| `VATEX-SA-OOS` | Services outside scope of tax / Not subject to VAT (free text per case) |
## Buyer ID Types
[Section titled “Buyer ID Types”](#buyer-id-types)
For foreign buyers (non-SA), use `tin_scheme` to specify the identifier type:
| `tin_scheme` | Description |
| ------------ | ---------------------------------- |
| `8025` | Saudi VAT registration number |
| `0002` | EU VAT number |
| `0003` | Passport number |
| `0004` | Official ID (country of residence) |
| `0005` | Certificate of residence |
| `0006` | Other document |
| `0007` | Not registered |
## Tax Report Lifecycle
[Section titled “Tax Report Lifecycle”](#tax-report-lifecycle)
After creating or sending an invoice, ZATCA clearance is asynchronous. The `tax_report` will be in `processing` state initially.
Monitor progress via:
* **Webhook** (recommended): Subscribe to the [tax report state change webhook](https://developer.b2brouter.net/reference/getnewtaxreportstatechange). Final states are `registered`, `error`, `registered_with_errors`.
* **Polling**: Call the [get tax report endpoint](https://developer.b2brouter.net/reference/get-tax-report) until the state is final.
## Field Mapping: Invoice API → ZATCA XML
[Section titled “Field Mapping: Invoice API → ZATCA XML”](#field-mapping-invoice-api--zatca-xml)
| Invoice API field | ZATCA XML element |
| ------------------- | ----------------------------- |
| `number` | `cbc:ID` |
| `date` | `cbc:IssueDate` |
| `company.tin_value` | Supplier `cbc:CompanyID` |
| `company.address2` | Supplier `cbc:BuildingNumber` |
| `company.province` | Supplier `cbc:District` |
| `contact.tin_value` | Customer `cbc:CompanyID` |
| `contact.address2` | Customer `cbc:BuildingNumber` |
| `contact.province` | Customer `cbc:District` |
| `tax.percent` | `cbc:Percent` |
| `tax.category` | `cbc:ID` in `cac:TaxCategory` |
| `tax.comment` | `cbc:TaxExemptionReasonCode` |
| `invoice.total` | `cbc:TaxInclusiveAmount` |
| `invoice.subtotal` | `cbc:TaxExclusiveAmount` |
# Report VAT records to the AEAT via SII (Suministro Inmediato de Información) using the B2Brouter API
> SII Guide
The Spanish [Suministro Inmediato de Información](https://www.agenciatributaria.es/AEAT.internet/Inicio/La_Agencia_Tributaria/Campanas/_Campanas_/Suministro_inmediato_de_informacion/Suministro_inmediato_de_informacion.shtml) (SII) system requires registered businesses to keep their VAT books — issued invoices, received invoices, capital goods, intra-EU operations, cash transactions over €6,000, and others — submitted in near-real-time to the AEAT (“Agencia Estatal de Administración Tributaria”). SII is mandatory for *Grandes Empresas* (turnover > €6,010,121.04), VAT-group members, REDEME participants, and other businesses that have opted in.
B2Brouter offers a JSON Tax Report API for SII so your system doesn’t have to deal with SOAP, AEAT certificates, batched submissions, response parsing, or the per-book XML wrappers. You issue REST calls; B2Brouter handles the rest, batching records into AEAT “Libros de Registro” and submitting them under a Social Collaborator certificate.
## What is SII?
[Section titled “What is SII?”](#what-is-sii)
SII is a tax-reporting system distinct from invoicing — there is no QR code on the underlying invoice and no consumer-facing verification step. Each record represents one book entry (a single invoice you issued, a single invoice you received, one capital good’s annual prorrata, etc.). The AEAT collects records over the year and uses them for VAT cross-checking.
SII has nine book types (“libros”), each with its own AEAT XML wrapper and validation rules:
| Book type | What it tracks | A0 (alta) | A1 (modificación) | B (baja / annullation) |
| ---------------- | -------------------------------------------------- | --------- | ----------------- | ---------------------- |
| Expedida | Issued invoices | ✓ | ✓ | ✓ |
| Recibida | Received invoices | ✓ | ✓ | ✓ |
| Inversion | Capital goods (bienes de inversión) | ✓ | ✓ | ✓ |
| Intracomunitaria | Intra-EU operations | ✓ | ✓ | ✓ |
| Metalico | Cash transactions ≥ €6,000 / counterparty / period | ✓ | ✓ | ✓ |
| Seguro | Insurance operations | ✓ | ✓ | ✓ |
| Viajesagencia | Travel agency special regime | ✓ | ✓ | ✓ |
| Cobro | Payments received against issued invoices | ✓ | — | — |
| Pago | Payments made against received invoices | ✓ | — | — |
`Cobro` and `Pago` return HTTP 422 on PATCH and DELETE — the AEAT XSD provides no A1 or B variants for those books.
Expedida and Recibida cover 98%+ of the volume in production. The remaining seven books support the long tail of VAT-reporting requirements.
## Setting up and working with the B2Brouter API
[Section titled “Setting up and working with the B2Brouter API”](#setting-up-and-working-with-the-b2brouter-api)
The first step is to enable the SII tax authority for the company that will be reporting. You can refer to the [Tax Report Settings Guide](https://docs.b2brouter.net/en/developers/essential-guides/tax-report-settings/) for a full description. The minimal call:
**Example request:**
```shell
curl --request POST \
--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": "sii",
"start_date": "2026-04-15",
"auto_send": true
}
}'
```
Once a setting exists for `code: "sii"` on the account, all SII Tax Report API calls under that account use it.
## Tax Report API
[Section titled “Tax Report API”](#tax-report-api)
The Tax Report API is one endpoint family that handles all nine SII books. The book is selected by the `book_type` field on the request body. The `type` field is `SiiDocument` for every SII record regardless of book.
### Create a Tax Report
[Section titled “Create a Tax Report”](#create-a-tax-report)
`POST /accounts/{ACCOUNT_ID}/tax_reports` creates one SII record. The same endpoint is used by Verifactu, TicketBai, and KSeF; the `type` field switches which validation set + which AEAT XML wrapper applies.
The structure of the JSON payload is based on [PEPPOL Continuous Transaction Control (CTC)](https://peppol.org/documentation/technical-documentation/ctc-documentation/) — the CTC field names do *not* match the AEAT XML node names. The [Equivalence between B2Brouter internal tax report fields and SII XML nodes](#equivalence-between-b2brouter-internal-tax-report-fields-and-sii-xml-nodes) section below lists the mapping for each book.
Tax reports have **tax breakdowns** (“desglose” in Spanish), not invoice lines. Your system aggregates the underlying invoice lines by tax type/category and submits the breakdown sums. B2Brouter performs validation but does not compute breakdowns from line data.
### Lifecycle
[Section titled “Lifecycle”](#lifecycle)
After a successful `POST /tax_reports`, the response carries a `state: "processing"` and a `ledger_id`. SII records are batched into “Libros de Registro” (Ledgers) on the server side and submitted to the AEAT asynchronously. To track the lifecycle:
* **Recommended**: configure the [tax report webhook](https://developer.b2brouter.net/reference/getnewtaxreportstatechange). B2Brouter calls your endpoint when the record reaches a final state (`registered`, `error`, `registered_with_errors`, `annulled`, or `refused`).
* **Polling**: call `GET /tax_reports/{id}` repeatedly until `state` reaches one of the final values.
The AEAT response is reflected in three SII-specific fields on the record once received:
* `csv` — the AEAT-assigned *Código Seguro de Verificación*
* `fecha_presentacion` — the AEAT-assigned presentation timestamp
* `estado` — the internal SII state. It starts at `Nuevo`, moves to `Enviando` while in flight, and settles on a terminal value: `Correcto`, `Incorrecto`, `AceptadoConErrores`, or `Error`.
For error states the record’s `errors` array is populated with the AEAT rejection, each entry carrying a `code` and a `description`:
```json
{
"errors": [
{
"code": "4102",
"description": "El NIF no está identificado en el censo de la AEAT"
}
]
}
```
### Get the XML representation
[Section titled “Get the XML representation”](#get-the-xml-representation)
`GET /tax_reports/{id}/download` returns the per-record XML that B2Brouter will (or did) include in the AEAT submission. The XML is generated at create time and persisted, so it’s available immediately after `POST`. The bundle that actually goes on the wire is the concatenated Ledger XML — see [Ledgers API](#ledgers-api).
### Correct a Tax Report (A1)
[Section titled “Correct a Tax Report (A1)”](#correct-a-tax-report-a1)
`PATCH /tax_reports/{id}` with the corrected fields creates a sibling record with `operacion: A1` (modificación). The original record stays in place; the new sibling is linked via `corrected_by_id` on the original and is what gets re-submitted to the AEAT.
The book a record belongs to is fixed at create time — `book_type` cannot be changed on an A1 sibling. Cobro and Pago return 422; their AEAT XSD does not define an A1 wrapper.
### Cancel a Tax Report (B)
[Section titled “Cancel a Tax Report (B)”](#cancel-a-tax-report-b)
`DELETE /tax_reports/{id}` creates a sibling record with `operacion: B` (baja / annullation). The original is linked via `annulled_by_id`. Cobro and Pago return 422 — their AEAT XSD does not define a baja wrapper.
### List Tax Reports
[Section titled “List Tax Reports”](#list-tax-reports)
`GET /accounts/{ACCOUNT_ID}/tax_reports` returns the account’s tax reports — SII, Verifactu, TicketBai, KSeF, all in one list. Filter by date range or other criteria as documented in the [list tax reports endpoint](https://developer.b2brouter.net/reference/get-tax-reports).
***
## Expedida (issued invoices)
[Section titled “Expedida (issued invoices)”](#expedida-issued-invoices)
The most common SII book. The reporting company is the invoice issuer; the customer is the counterparty.
### Example request
[Section titled “Example request”](#example-request)
```shell
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": "SiiDocument",
"book_type": "Expedida",
"invoice_number": "INV-2026-001",
"invoice_date": "2026-04-15",
"fiscal_year": 2026,
"fiscal_period": 4,
"invoice_type_code": "F1",
"special_regime_key": "01",
"description": "Servicios de consultoría",
"customer_party_name": "Cliente Ejemplo S.L.",
"customer_party_tax_id": "B87654321",
"customer_party_country": "es",
"tax_inclusive_amount": 1210.0,
"tax_amount": 210.0,
"currency": "EUR",
"tax_breakdowns": [
{
"name": "IVA",
"category": "S",
"percent": 21.0,
"taxable_base": 1000.0,
"tax_amount": 210.0
}
]
}
}'
```
**Expedida-specific fields** (in addition to the common CTC fields):
| Field | Maps to AEAT element | Notes |
| ---------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------- |
| `simplified_art7273` | `FacturaSimplificadaArticulos7.2_7.3` | Boolean. Emits `S` when true. |
| `reg_previo_ggee` | `RegPrevioGGEEoREDEMEoCompetencia` | Boolean. |
| `macrodata` | `Macrodato` | Boolean (SII), unlike Verifactu’s string version. |
| `facturacion_disp_adicional_mercado_gas` | `FacturacionDispAdicionalTerceraYsextayDelMercadoOrganizadoDelGas` | Boolean. Expedida only. |
| `sin_identif_destinatario_art_6_1_d` | `FacturaSinIdentifDestinatarioAritculo6.1.d` | Boolean. Expedida only. |
| `succession_party_name` + `_tax_id` | `EntidadSucedida` > `NombreRazon` / `NIF` | Both required together when used. |
| `billing_agreement_number` | `NumRegistroAcuerdoFacturacion` | Max 15 chars. |
| `external_reference` | `RefExterna` | Max 60 chars. |
| `tax_point_date` | `FechaOperacion` | Optional; defaults to omitted. |
The AEAT XML wrapper is `SuministroLRFacturasEmitidas`. The five S/N flags above are optional and emit only when truthy.
***
## Recibida (received invoices)
[Section titled “Recibida (received invoices)”](#recibida-received-invoices)
Recibida has the **issuer/receiver flip** compared to Expedida: the external supplier is the issuer (recorded in `IDEmisorFactura`), and the reporting company is the receiver (Titular). Use `supplier_party_*` for the external supplier; `customer_party_*` is not required (the company defaults are used as the receiver).
### Example request
[Section titled “Example request”](#example-request-1)
```shell
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": "SiiDocument",
"book_type": "Recibida",
"invoice_number": "PROV-555",
"invoice_date": "2026-04-15",
"tax_point_date": "2026-04-10",
"fecha_contable": "2026-04-22",
"fiscal_year": 2026,
"fiscal_period": 4,
"invoice_type_code": "F1",
"special_regime_key": "01",
"description": "Compra de material",
"supplier_party_name": "Proveedor Test S.L.",
"supplier_party_tax_id": "A12345674",
"supplier_party_country": "es",
"tax_inclusive_amount": 1210.0,
"tax_amount": 210.0,
"cuota_deducible_in_cents": 21000,
"currency": "EUR",
"tax_breakdowns": [
{
"name": "IVA",
"category": "S",
"percent": 21.0,
"taxable_base": 1000.0,
"tax_amount": 210.0
}
]
}
}'
```
**Recibida-specific fields:**
| Field | Maps to AEAT element | Notes |
| --------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `supplier_party_tax_id` / `_name` | `IDEmisorFactura` > `NIF` / `Contraparte` > `NombreRazon` | The external supplier — the issuer of the invoice you received. |
| `supplier_party_country` | `IDOtro` > `CodigoPais` | When not `es`, the issuer is rendered via `IDOtro` instead of `NIF`. |
| `tax_point_date` | `FechaOperacion` | Operation/service date. Omitted when not supplied. |
| `fecha_contable` | `FechaRegContable` | Date the invoice was recorded in your accounting books. If absent, falls back to `tax_point_date` then `invoice_date`. |
| `cuota_deducible_in_cents` | `CuotaDeducible` | Deductible quota. When omitted, derived from `sum(tax_breakdowns.tax_amount)`. Set explicitly to override (partial deductibility, etc.). |
| `simplified_art7273` | `FacturaSimplificadaArticulos7.2_7.3` | Boolean. Same shape as Expedida. |
| `reg_previo_ggee` | `RegPrevioGGEEoREDEMEoCompetencia` | Boolean. |
| `macrodata` | `Macrodato` | Boolean. |
| `succession_party_*` | `EntidadSucedida` | Same as Expedida. |
| `billing_agreement_number` | `NumRegistroAcuerdoFacturacion` | Same as Expedida. |
| `external_reference` | `RefExterna` | Same as Expedida. |
The two `Expedida`-only flags (`facturacion_disp_adicional_mercado_gas`, `sin_identif_destinatario_art_6_1_d`) do **not** appear on Recibida — the AEAT XSD defines them only on `FacturaExpedidaType`.
The AEAT XML wrapper is `SuministroLRFacturasRecibidas`.
### Non-ES suppliers
[Section titled “Non-ES suppliers”](#non-es-suppliers)
When `supplier_party_country` is not `es`, the issuer is emitted as `IDOtro` instead of `NIF`:
```xml
FR
04
FR12345678901
```
The `Contraparte` block is also rendered with `IDOtro` to match.
***
## Other books (Inversion, Intracomunitaria, Metalico, Seguro, Viajesagencia, Cobro, Pago)
[Section titled “Other books (Inversion, Intracomunitaria, Metalico, Seguro, Viajesagencia, Cobro, Pago)”](#other-books-inversion-intracomunitaria-metalico-seguro-viajesagencia-cobro-pago)
The remaining seven books each cover a specific reporting requirement. They share the common CTC fields and the same lifecycle, but carry book-specific scalars. Refer to the [OpenAPI reference](https://developer.b2brouter.net/reference/) for the full field list.
### Inversion (capital goods)
[Section titled “Inversion (capital goods)”](#inversion-capital-goods)
Tracks the annual prorrata + regularization of capital goods. AEAT wrapper: `SuministroLRBienesInversion`. Required fields (in addition to the common CTC wrapper):
* `identificacion_bien` — asset identifier (`IdentificacionBien`, max 40 chars)
* `fecha_inicio_utilizacion` — date the asset entered use (`FechaInicioUtilizacion`)
* `prorrata_anual_definitiva` — definitive annual prorrata (`ProrrataAnualDefinitiva`, 0–100, 2 decimals)
Optional: `regularizacion_anual_deduccion_in_cents`, `identificacion_entrega`, `regularizacion_deduccion_efectuada_in_cents`, `external_reference`, `billing_agreement_number`, `succession_party_*`.
### Intracomunitaria (intra-EU operations)
[Section titled “Intracomunitaria (intra-EU operations)”](#intracomunitaria-intra-eu-operations)
AEAT wrapper: `SuministroLRDetOperacionIntracomunitaria`. Required: `clave_declarado` (enum `D` or `R`), `external_reference` (per AEAT XSD), plus `description` mapped to `DescripcionBienes`. Optional: `plazo_operacion`, `facturas_o_documentacion`.
### Metalico / Seguro / Viajesagencia (period-aggregated)
[Section titled “Metalico / Seguro / Viajesagencia (period-aggregated)”](#metalico--seguro--viajesagencia-period-aggregated)
These books are *period-aggregated*: a single record represents the total from one counterparty in one fiscal period. There is no `invoice_number` or `invoice_date` — the AEAT XSD omits the `IDFactura` block. The counterparty is supplied via `customer_party_*` (B2Brouter mirrors it onto `nombre_contraparte` server-side).
* **Metalico**: `SuministroLRCobrosMetalico` wrapper. Aggregate cash received from a counterparty ≥ €6,000 in a fiscal period.
* **Seguro**: `SuministroLROperacionesSeguros` wrapper. Adds a required `ClaveOperacion` mapped from `operation_type`.
* **Viajesagencia**: `SuministroLRAgenciasViajes` wrapper. Structurally identical to Metalico per the AEAT XSD.
### Cobro / Pago (payment tracking)
[Section titled “Cobro / Pago (payment tracking)”](#cobro--pago-payment-tracking)
Cobro: payments received against issued invoices. Pago: payments made against received invoices. Both carry a per-record array of payment events in `payload_data`:
```json
{
"payload_data": {
"cobros": [
{
"fecha": "2026-03-01",
"importe_in_cents": 50000,
"medio": "01",
"cuenta_o_medio": "ES1234..."
},
{
"fecha": "2026-04-01",
"importe_in_cents": 50000,
"medio": "01",
"cuenta_o_medio": "ES1234..."
}
]
}
}
```
(Use `pagos` instead of `cobros` for Pago.)
**No A1 or B variants exist for these books** per the AEAT XSD — PATCH and DELETE return 422.
***
## Bulk endpoint (asynchronous)
[Section titled “Bulk endpoint (asynchronous)”](#bulk-endpoint-asynchronous)
For high-volume integrations (point-of-sale aggregations, CSV-to-JSON migrations, etc.), use `POST /accounts/{ACCOUNT_ID}/tax_report_batches` to submit up to 5000 records in a single request. **Processing is asynchronous**: the call validates only the wrapper and returns **`202 Accepted`** with a batch id; the per-record build, validation and ledger attach run in a background job. You then poll the batch-status endpoint (or rely on the per-record `tax_report.state_change` webhook once the AEAT responds).
```shell
curl --request POST \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_batches' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"tax_reports": [
{ "type": "SiiDocument", "book_type": "Expedida", "invoice_number": "INV-001", ... },
{ "type": "SiiDocument", "book_type": "Expedida", "invoice_number": "INV-002", ... },
...
]
}'
```
**Accepted response (`202`):**
```json
{
"batch_id": 4567,
"status": "processing",
"total": 2,
"status_url": "https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_batches/4567"
}
```
**Poll the batch status** at `GET /accounts/{ACCOUNT_ID}/tax_report_batches/{BATCH_ID}`. While the job runs `status` is `"processing"`; once finished it is `"done"` and `results` is populated, in submission order:
```json
{
"batch_id": 4567,
"status": "done",
"summary": { "total": 3, "accepted": 2, "rejected": 1 },
"results": [
{ "index": 0, "status": "created", "tax_report": { "id": 12345, ... } },
{ "index": 1, "status": "error", "errors": [{ "code": "invalid_book_type", ... }] },
{ "index": 2, "status": "created", "tax_report": { "id": 12346, ... } }
]
}
```
**Semantics:**
* **Asynchronous**: the `202` only means the batch was accepted for processing — not that any record was created. Poll `status_url` until `status` is `"done"`, or consume the `tax_report.state_change` webhook for the AEAT outcome of each created record.
* **Partial success**: each record is validated independently in the job. A failure on one record (bad `book_type`, XSD-invalid XML, unsupported `type`) does not abort the batch — it appears as a per-record `error` in `results`.
* **Wrapper errors** (missing `tax_reports` key, not an array, more than 5000 records) return HTTP `400` synchronously, before anything is enqueued.
* **SII-only in v1**: this endpoint accepts only `type: "SiiDocument"`. A non-SII or missing `type` on any record is rejected **synchronously with HTTP `422`** (`code: "unsupported_type"`) before anything is enqueued — that’s an endpoint-contract error, distinct from per-record SII data errors (bad `book_type`, XSD-invalid), which are reported as partial failures in the batch status.
* **Ledger attach + send**: created records are attached to mode-segregated SII ledgers grouped by `(document_type_code, reporting-company TIN)` — i.e. by the *Titular* declared in the AEAT `Cabecera`, **not** by the per-record `supplier_party_tax_id`. For Recibida/Pago that field holds the external supplier, so records from different suppliers still share one ledger as long as they belong to the same book and reporting company. Ledgers are then submitted to the AEAT by the per-minute cron (see [Ledgers API](#ledgers-api)) — never on the request thread.
### Knowing when the batch has finished
[Section titled “Knowing when the batch has finished”](#knowing-when-the-batch-has-finished)
Two options, use either or both:
* **Poll** `status_url` until `status` is `"done"`.
* **Webhook** — subscribe an enabled webhook on your integration group to the **`sii_batch.finished`** event. When the ingest job finishes, B2Brouter POSTs:
```json
{
"code": "sii_batch.finished",
"triggered_at": 1748000000,
"data": {
"batch_id": 4567,
"status": "done",
"summary": { "total": 3, "accepted": 2, "rejected": 1 },
"status_url": "https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_batches/4567"
}
}
```
The webhook body carries only the summary (bounded regardless of batch size); `GET status_url` for the full per-record results. The AEAT outcome of each created record still arrives via its own `tax_report.state_change` webhook.
## Ledgers API
[Section titled “Ledgers API”](#ledgers-api)
SII tax reports are not sent individually to the AEAT — they are bundled into “Libros de Registro” (Ledger in B2Brouter terms), holding up to 5000 records per book / reporting company (Titular). Each tax report has a `ledger_id` field pointing to the internal Ledger ID. With that ID you can:
* Retrieve the XML representation of the Ledger via the [download ledger endpoint](https://developer.b2brouter.net/reference/download-ledger). This is the document B2Brouter sends to the AEAT.
* Retrieve the AEAT XML response to that Ledger via the [download response endpoint](https://developer.b2brouter.net/reference/download-response-ledger).
Modern SII Ledgers are flushed to the AEAT exclusively by the per-minute cron driven by the `Strategy::Sii` policy (60 seconds between submissions, or immediately once a Ledger reaches 5000 records). Neither `POST /tax_reports`, `PATCH`/`DELETE`, nor the bulk endpoint ever submit to the AEAT on the request thread — they attach the record(s) and return; the cron does the send. Expect up to \~60s between create and submission.
## Equivalence between B2Brouter internal tax report fields and SII XML nodes
[Section titled “Equivalence between B2Brouter internal tax report fields and SII XML nodes”](#equivalence-between-b2brouter-internal-tax-report-fields-and-sii-xml-nodes)
### Expedida (`SuministroLRFacturasEmitidas`)
[Section titled “Expedida (SuministroLRFacturasEmitidas)”](#expedida-suministrolrfacturasemitidas)
| B2Brouter Field | AEAT XML Node |
| ---------------------------------------- | ------------------------------------------------------------------------------------ |
| `fiscal_year` | `PeriodoLiquidacion > Ejercicio` |
| `fiscal_period` | `PeriodoLiquidacion > Periodo` |
| `supplier_party_tax_id` | `IDFactura > IDEmisorFactura > NIF` |
| `invoice_number` | `IDFactura > NumSerieFacturaEmisor` |
| `invoice_date` | `IDFactura > FechaExpedicionFacturaEmisor` |
| `invoice_type_code` | `FacturaExpedida > TipoFactura` |
| `tax_point_date` | `FacturaExpedida > FechaOperacion` |
| `special_regime_key` | `FacturaExpedida > ClaveRegimenEspecialOTrascendencia` |
| `billing_agreement_number` | `FacturaExpedida > NumRegistroAcuerdoFacturacion` |
| `tax_inclusive_amount` | `FacturaExpedida > ImporteTotal` |
| `description` | `FacturaExpedida > DescripcionOperacion` |
| `external_reference` | `FacturaExpedida > RefExterna` |
| `simplified_art7273` | `FacturaExpedida > FacturaSimplificadaArticulos7.2_7.3` |
| `succession_party_name` | `FacturaExpedida > EntidadSucedida > NombreRazon` |
| `succession_party_tax_id` | `FacturaExpedida > EntidadSucedida > NIF` |
| `reg_previo_ggee` | `FacturaExpedida > RegPrevioGGEEoREDEMEoCompetencia` |
| `macrodata` | `FacturaExpedida > Macrodato` |
| `facturacion_disp_adicional_mercado_gas` | `FacturaExpedida > FacturacionDispAdicionalTerceraYsextayDelMercadoOrganizadoDelGas` |
| `sin_identif_destinatario_art_6_1_d` | `FacturaExpedida > FacturaSinIdentifDestinatarioAritculo6.1.d` |
| `customer_party_name` | `FacturaExpedida > Contraparte > NombreRazon` |
| `customer_party_tax_id` | `FacturaExpedida > Contraparte > NIF` |
| `tax_breakdowns[*].percent` | `DetalleIVA > TipoImpositivo` |
| `tax_breakdowns[*].taxable_base` | `DetalleIVA > BaseImponible` |
| `tax_breakdowns[*].tax_amount` | `DetalleIVA > CuotaRepercutida` (omitted when zero) |
### Recibida (`SuministroLRFacturasRecibidas`)
[Section titled “Recibida (SuministroLRFacturasRecibidas)”](#recibida-suministrolrfacturasrecibidas)
| B2Brouter Field | AEAT XML Node |
| -------------------------------- | ----------------------------------------------------------------- |
| `fiscal_year` | `PeriodoLiquidacion > Ejercicio` |
| `fiscal_period` | `PeriodoLiquidacion > Periodo` |
| `supplier_party_tax_id` | `IDFactura > IDEmisorFactura > NIF` (or `IDOtro > ID` for non-ES) |
| `supplier_party_country` | `IDFactura > IDEmisorFactura > IDOtro > CodigoPais` (non-ES only) |
| `supplier_party_name` | `FacturaRecibida > Contraparte > NombreRazon` |
| `invoice_number` | `IDFactura > NumSerieFacturaEmisor` |
| `invoice_date` | `IDFactura > FechaExpedicionFacturaEmisor` |
| `invoice_type_code` | `FacturaRecibida > TipoFactura` |
| `tax_point_date` | `FacturaRecibida > FechaOperacion` |
| `special_regime_key` | `FacturaRecibida > ClaveRegimenEspecialOTrascendencia` |
| `billing_agreement_number` | `FacturaRecibida > NumRegistroAcuerdoFacturacion` |
| `tax_inclusive_amount` | `FacturaRecibida > ImporteTotal` |
| `description` | `FacturaRecibida > DescripcionOperacion` |
| `external_reference` | `FacturaRecibida > RefExterna` |
| `simplified_art7273` | `FacturaRecibida > FacturaSimplificadaArticulos7.2_7.3` |
| `succession_party_name` | `FacturaRecibida > EntidadSucedida > NombreRazon` |
| `succession_party_tax_id` | `FacturaRecibida > EntidadSucedida > NIF` |
| `reg_previo_ggee` | `FacturaRecibida > RegPrevioGGEEoREDEMEoCompetencia` |
| `macrodata` | `FacturaRecibida > Macrodato` |
| `tax_breakdowns[*].percent` | `DetalleIVA > TipoImpositivo` |
| `tax_breakdowns[*].taxable_base` | `DetalleIVA > BaseImponible` |
| `tax_breakdowns[*].tax_amount` | `DetalleIVA > CuotaSoportada` (omitted when zero) |
| `fecha_contable` | `FacturaRecibida > FechaRegContable` |
| `cuota_deducible_in_cents` | `FacturaRecibida > CuotaDeducible` |
### Tax breakdown `category` → desglose bucket
[Section titled “Tax breakdown category → desglose bucket”](#tax-breakdown-category--desglose-bucket)
Each `tax_breakdowns[*].category` (the standard PEPPOL/UN-CEFACT VAT category code) decides which AEAT desglose bucket the line lands in. This is the canonical signal — send the right `category`; the boolean flags are derived from it.
**Expedida** (`TipoDesglose > DesgloseFactura`):
| `category` | AEAT bucket |
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `S`, `Z`, `H`, `AA`, `AAA`, `N1` (taxable) | `Sujeta > NoExenta`, `TipoNoExenta` = `S1` |
| `AE` (reverse charge) | `Sujeta > NoExenta`, `TipoNoExenta` = `S2` |
| `E`, `K`, `N2.*`, `N3.2`, `N3.6`, `N4`, `N5`, `N7`, `IC` (exempt) | `Sujeta > Exenta > DetalleExenta` (`CausaExencion` ← `exemption_code`) |
| `NS`, `O`, `G`, `N3.1`, `N3.3`–`N3.5` (not subject) | `NoSujeta` (`ImporteTAIReglasLocalizacion` when `no_subject_code` = `RL`, else `ImportePorArticulos7_14_Otros`) |
A mix of `S1` and `S2` lines collapses to a single `TipoNoExenta` = `S3`. A line with no recognised category renders as `S1` (fully taxable).
**Recibida** (`DesgloseFactura`): reverse-charge lines (`category` `AE`) go in `InversionSujetoPasivo`; everything else in `DesgloseIVA`.
> **Not yet supported:** `DesgloseTipoOperacion` (the goods-vs-services split AEAT requires for some cross-border counterparties). Invoices that need it should use the legacy CSV path until a later release adds support — mirrors the real-estate `DatosInmueble` deferral.
## XSD files
[Section titled “XSD files”](#xsd-files)
The AEAT publishes the canonical SII XSDs. B2Brouter validates rendered XML against `SuministroInformacion.xsd` and `SuministroLR.xsd` (v1.1) on every send. The schemas live in this repo at `vendor/xsd-validator/lib/xsd/schemas/sii_v11/`.
## Code descriptions for specific fields
[Section titled “Code descriptions for specific fields”](#code-descriptions-for-specific-fields)
### `book_type`
[Section titled “book\_type”](#book_type)
The values are listed in [What is SII?](#what-is-sii) above. The book\_type drives the AEAT XML wrapper, the required-field set, and the validation rules.
### `invoice_type_code`
[Section titled “invoice\_type\_code”](#invoice_type_code)
For Expedida and Recibida:
* `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
* `F4` — Asiento resumen de facturas
* `F5` — Importaciones (DUA)
* `F6` — Otros justificantes contables
* `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` (`ClaveRegimenEspecialOTrascendencia`)
[Section titled “special\_regime\_key (ClaveRegimenEspecialOTrascendencia)”](#special_regime_key-claveregimenespecialotrascendencia)
A two-digit AEAT code describing the special regime applied to the operation. The most common values are `01` (régimen general), `02` (exportaciones), `03` (régimen especial bienes usados), `04` (régimen especial oro de inversión), `05` (régimen especial agencias de viajes), `06` (régimen especial grupo de entidades), `07` (régimen especial criterio de caja), `08` (operaciones IPSI / IGIC). See the [AEAT specifications](https://www.agenciatributaria.es/AEAT.internet/Inicio/La_Agencia_Tributaria/Campanas/_Campanas_/Suministro_inmediato_de_informacion/Suministro_inmediato_de_informacion.shtml) for the full list and the per-book applicability.
### `clave_declarado` (Intracomunitaria only)
[Section titled “clave\_declarado (Intracomunitaria only)”](#clave_declarado-intracomunitaria-only)
* `D` — Declarante
* `R` — Receptor
## Error checking
[Section titled “Error checking”](#error-checking)
B2Brouter validates SII payloads against the AEAT XSDs before any wire submission. A validation failure returns HTTP 422 with the failing fields itemized in JSON. If the AEAT itself rejects a submission (e.g. unknown counterparty NIF), the rejection is reflected in the record’s `state` (typically `error` or `registered_with_errors`) and the AEAT’s `error_code` / `error_description` fields. Configure the [tax report webhook](https://developer.b2brouter.net/reference/getnewtaxreportstatechange) to react to final states without polling.
# TicketBAI
> How to comply with the Basque Country TicketBAI e-invoicing regulation using B2Brouter.
The [Anti-Fraud Law (Law 11/2021)](https://www.boe.es/diario_boe/txt.php?id=BOE-A-2021-11473) and its subsequent technical regulation [Royal Decree 1007/2023](https://www.boe.es/buscar/act.php?id=BOE-A-2023-24840) mandates that businesses must ensure their invoicing and accounting systems meet strict technical standards to prevent data manipulation. For business located in Euskadi, Spain Anti-Fraud law mandates the use of TicketBAI. B2Brouter’s TicketBAI 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.
B2Brouter provides a convenient API for submitting TicketBAI tax reports so you can easily comply with the legislation of the three Basque Tax Authorities (Araba, Bizkaia, and Gipuzkoa). Our API abstracts away the differences between them and offers you an unified interface that is fully compliant with all three Tax Authorities. You can check the status of B2Brouter as *“software garante”* in all three Tax Authorities: [Araba](https://web.araba.eus/es/hacienda/ticketbai/listado-de-software), [Bizkaia](https://www.batuz.eus/es/registro-de-software?q=b2brouter), [Gipuzkoa](https://www.gipuzkoa.eus/es/web/ogasuna/ticketbai/listado-software).
## What is TicketBAI?
[Section titled “What is TicketBAI?”](#what-is-ticketbai)
TicketBAI is a joint project of the Basque Provincial Councils (Gipuzkoa, Bizkaia, and Araba) and the Basque Government to control invoicing and ensure tax compliance. It requires all self-employed individuals and companies to use certified invoicing software that generates a unique code and a QR code for each invoice. This system aims to prevent tax fraud by ensuring that all economic transactions are recorded and transmitted to the tax authorities in real-time or near real-time.
TicketBAI involves:
* Generating a unique TicketBAI identifier, a QR code for each invoice, and a tax report that includes the relevant information of the invoice.
* Digitally signing each tax report using a qualified electronic certificate.
* Chaining tax reports together cryptographically to ensure immutability and prevent tampering.
* Submitting the tax report to the relevant Basque Tax Authority (Hacienda Foral) in a specified format in a timely manner.
* Processing the responses from the Tax Authority for each submission.
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.
B2Brouter provides two modalities of operation depending on whether you are using B2Brouter for both issuing invoices and reporting them to the Tax Authority, or just for tax reporting.
> **Testing environments**: Use [sandbox](/en/developers/testing/sandbox/) for initial API testing and payload validation — sandbox simulates TicketBAI submissions so you can exercise the invoice lifecycle without hitting the Basque Tax Authority. For full end-to-end testing with the authority’s 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”](#setting-up-and-working-with-b2brouter-api)
The first step is to set up the configuration for TicketBAI for each one of the Companies for which you want to submit tax reports. You can refer to the [Tax Report Settings Guide](https://developer.b2brouter.net/docs/tax_report_settings_guide) for a full description of the process. The call for configuring an account is as follows:
```shell
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"
}
}'
```
### Invoice API
[Section titled “Invoice API”](#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 TicketBAI tax report will be sent automatically to the relevant Tax Authority. This is an example of a call to create and issue an Invoice:
```shell
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"
}
]
}
]
}
}'
```
In the response of the *POST* REST call for [creating an invoice](https://developer.b2brouter.net/reference/create-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. Note that **tax\_report\_ids** is an *array* because each invoice can have more than one tax report for cancellations and corrections.
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](https://developer.b2brouter.net/reference/get-tax-reports) 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 because the background process that chains and sends the tax report is performed independently. This is the reason why you **must** check the life cycle of the tax report. You have two options:
* The recommended way is using the [tax report web hook](https://developer.b2brouter.net/reference/getnewtaxreportstatechange). The web hook will issue a *POST* call to your system each time the state of the tax report reaches a **final** state: *registered*, *error*, and *registered\_with\_errors*
* Polling the state of the tax report with [get tax report endpoint](https://developer.b2brouter.net/reference/get-tax-reports) until the **state** field is *registered*, *error* or *registered\_with\_errors*.
### Tax Report API for TicketBAI
[Section titled “Tax Report API for TicketBAI”](#tax-report-api-for-ticketbai)
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 Tax Report API.
The structure of the *JSON* format for B2Brouter tax reports is based on [PEPPOL Continuous Transaction Control (CTC)](https://peppol.org/documentation/technical-documentation/ctc-documentation/). **B2Brouter Tax Report API is an universal API not only geared towards TicketBAI but designed to handle tax reporting around the World**.
It’s important to notice that for TicketBAI, tax reports have **tax report lines** which represent the individual lines of the original invoice. The TaxReport model does not perform any calculations. You must provide all relevant figures in the tax report.
#### Create a Tax Report
[Section titled “Create a Tax Report”](#create-a-tax-report)
To create a tax report you have to call the [create tax report](https://developer.b2brouter.net/reference/create-tax-report) endpoint. A minimal and valid tax report can be created by the following *POST* call:
```shell
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
}
]
}
}'
```
The response to this `POST` call will return immediately with the tax report in a `processing` state. The `qr` and `identifier` fields will be `null` initially. To retrieve them, make subsequent `GET` requests until the `state` changes to `signed` or `registered`.
The following is a more complex example of a TicketBAI with two lines, a global discount, IVA of 21%, and IRPF of 15%:
```shell
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
}
]
}
}'
```
Key points to consider:
* **Line-level Tax Calculation**: TicketBAI requires that all taxes are calculated on a per-line basis.
* **Global Discounts and Charges as Lines**: Global discounts must be treated as separate lines with a negative `price` and `tax_inclusive_amount`.
* **Withheld Taxes**: Withheld taxes (e.g., IRPF) are reported at the document level using the `tax_amount_withheld` field.
#### Check the state of a Tax Report
[Section titled “Check the state of a Tax Report”](#check-the-state-of-a-tax-report)
You **must** check the life cycle of the tax report. You have two options:
* The recommended way is using the [tax report web hook](https://developer.b2brouter.net/reference/getnewtaxreportstatechange). Final states are: *registered*, *error*, *annulled*, and *registered\_with\_errors*
* Polling the state of the tax report by calling the [get tax report endpoint](https://developer.b2brouter.net/reference/get-tax-report) 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”](#get-the-xml-representation-of-the-tax-report)
If you need the XML representation of the tax report, use the [download tax report endpoint](https://developer.b2brouter.net/reference/new-download-tax-report). 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 (an asynchronous process).
#### Error checking
[Section titled “Error checking”](#error-checking)
B2Brouter has implemented an exhaustive set of validations of TicketBAI tax reports based on the [specifications](https://www.euskadi.eus/web01-a2hdaura/es/contenidos/informacion/ticketbai_especificaciones/es_ticketbai/especificaciones.html) published by the Basque Tax Authorities. 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”](#cancel-a-tax-report)
To cancel a tax report use the *DELETE* verb calling the [annullation endpoint](https://developer.b2brouter.net/reference/annulate-tax-report). Check the evolution of the cancellation by either processing the [tax report web hook](https://developer.b2brouter.net/reference/getnewtaxreportstatechange) or polling the [get tax report endpoint](https://developer.b2brouter.net/reference/get-tax-reports) until the **state** field of the tax report is *error* or *annulled*.
#### List Tax Reports
[Section titled “List Tax Reports”](#list-tax-reports)
To list your tax reports use the *GET* verb in [list tax reports endpoint](https://developer.b2brouter.net/reference/get-tax-reports).
## Equivalence between B2Brouter internal tax report fields and TicketBAI XML nodes
[Section titled “Equivalence between B2Brouter internal tax report fields and TicketBAI XML nodes”](#equivalence-between-b2brouter-internal-tax-report-fields-and-ticketbai-xml-nodes)
| B2Brouter Field (based on PEPPOL CTC) | TicketBAI XML Node |
| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `supplier_party_tax_id` | `Sujetos > Emisor > NIF` |
| `supplier_party_name` | `Sujetos > Emisor > ApellidosNombreRazonSocial` |
| `customer_party_tax_id` (if `customer_party_tax_scheme` is 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` (if `customer_party_tax_scheme` is present) | `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` (if `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-exempt) | `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` |
## XSD files
[Section titled “XSD files”](#xsd-files)
TicketBAI schemas provided by the Basque Tax Authorities.
| XSD file | Description |
| --------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| [ticketbaiv1-2-2.xsd](https://www.batuz.eus/fitxategiak/batuz/ticketbai/ticketbaiv1-2-2.xsd) | Esquema principal de TicketBAI |
| [anula\_ticketbaiv1-2-2.xsd](https://www.batuz.eus/fitxategiak/batuz/ticketbai/anula_ticketbaiv1-2-2.xsd) | Esquema para el suministro de facturas |
## Code descriptions for specific fields
[Section titled “Code descriptions for specific fields”](#code-descriptions-for-specific-fields)
### Invoice type codes
[Section titled “Invoice type codes”](#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”](#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. |
| 51 | Operaciones en recargo de equivalencia. |
| 52 | Operaciones en régimen simplificado. |
### Non exemption codes
[Section titled “Non exemption codes”](#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. |
### Exemption codes
[Section titled “Exemption codes”](#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 |
### Non subject codes
[Section titled “Non subject codes”](#non-subject-codes)
Param: `no_subject_code`
| Code | Description |
| ---- | ------------------------------------------------------------------------------------ |
| 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. |
### Customer ID Type
[Section titled “Customer ID Type”](#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 |
# Verifactu
> How to comply with Spain's Verifactu anti-fraud invoicing regulation using B2Brouter.
The [Anti-Fraud Law (Law 11/2021)](https://www.boe.es/diario_boe/txt.php?id=BOE-A-2021-11473) mandates that businesses must ensure their invoicing and accounting systems meet strict technical standards to prevent data manipulation. [Royal Decree 1007/2023](https://www.boe.es/buscar/act.php?id=BOE-A-2023-24840) 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?”](#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](/en/developers/testing/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”](#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](https://developer.b2brouter.net/reference/tax_report_settings_guide) for a full description of the process. The call for configuring an account is as follows:
**Example request**
```shell
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”](#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:**
```shell
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](https://developer.b2brouter.net/reference/create-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](https://developer.b2brouter.net/reference/get-tax-reports) 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](https://developer.b2brouter.net/reference/getnewtaxreportstatechange). Final states: *registered*, *error*, and *registered\_with\_errors*
* Polling the state of the tax report with [get tax report endpoint](https://developer.b2brouter.net/reference/get-tax-reports) until the **state** field is *registered*, *error* or *registered\_with\_errors*.
### Tax Report API
[Section titled “Tax Report API”](#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)](https://peppol.org/documentation/technical-documentation/ctc-documentation/). **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”](#create-a-tax-report)
To create a tax report you have to call the [create tax report](https://developer.b2brouter.net/reference/get-tax-reports) endpoint. A minimal and valid tax report:
**Example request:**
```shell
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”](#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](https://developer.b2brouter.net/reference/import-tax-report).
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”](#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](https://developer.b2brouter.net/reference/getnewtaxreportstatechange). Final states: *registered*, *error*, *annulled*, and *registered\_with\_errors*
* Polling the [get tax report endpoint](https://developer.b2brouter.net/reference/get-tax-report) 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”](#get-the-xml-representation-of-the-tax-report)
Use the [download tax report endpoint](https://developer.b2brouter.net/reference/new-download-tax-report). 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”](#error-checking)
B2Brouter has implemented an exhaustive set of validations of Verifactu tax reports based on the [specifications](https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/Validaciones_Errores_Veri-Factu.pdf) 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”](#cancel-a-tax-report)
To cancel a tax report (“anulación” in Spanish) use the *DELETE* verb calling the [annullation endpoint](https://developer.b2brouter.net/reference/annulate-tax-report).
#### Correct a Tax Report
[Section titled “Correct a Tax Report”](#correct-a-tax-report)
To correct a tax report (“subsanación” in Spanish) use the *PATCH* or *PUT* verbs calling the [correction endpoint](https://developer.b2brouter.net/reference/modify-tax-report).
### Ledgers API
[Section titled “Ledgers API”](#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](https://developer.b2brouter.net/reference/download-ledger).
* Retrieve the XML response of the AEAT to the Ledger sent by B2Brouter by calling the [download response endpoint](https://developer.b2brouter.net/reference/download-response-ledger).
## Equivalence between B2Brouter internal tax report fields and Verifactu XML nodes
[Section titled “Equivalence between B2Brouter internal tax report fields and Verifactu XML nodes”](#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-files)
| XSD file | Description |
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [SuministroLR.xsd](https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SuministroLR.xsd) | Operaciones de alta y anulación de los sistemas Verifactu y no Verifactu |
| [RespuestaSuministro.xsd](https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/RespuestaSuministro.xsd) | Respuesta de las operaciones de los sistemas Verifactu y no Verifactu |
| [ConsultaLR.xsd](https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/ConsultaLR.xsd) | Operación de consulta de registros de facturación Verifactu |
| [RespuestaConsultaLR.xsd](https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/RespuestaConsultaLR.xsd) | Respuesta de la operación de consulta de registros de facturación Verifactu |
| [SuministroInformacion.xsd](https://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SuministroInformacion.xsd) | Definición de tipos comunes |
## Code descriptions for specific fields
[Section titled “Code descriptions for specific fields”](#code-descriptions-for-specific-fields)
### Invoice type codes
[Section titled “Invoice type codes”](#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”](#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”](#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”](#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”](#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”](#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 |
# B2Bconnector for Windows
> How to use the B2Bconnector Windows app to sync local folders with B2Brouter automatically.
**B2Bconnector** is a program for Windows systems that allows you to **synchronize local folders** with B2Brouter for the **automatic sending and receiving of electronic documents** (invoices, orders, responses, etc.).
This connector acts as a bridge between your file system and the platform, enabling B2Brouter to read and write documents from configured folders on your Windows computer or server.
## How it works
[Section titled “How it works”](#how-it-works)
Once installed and configured, B2Bconnector works automatically:
* At regular intervals (by default, every **1 minute**) it checks for new documents in the **sending folders** and uploads them to B2Brouter.
* When B2Brouter receives documents for your company, it places them in the local **receiving folders** on your computer.
This way you only need to drop your files into a folder: there is no need to manage servers, SFTP credentials or protocols.
The program is usually installed on the **server where the ERP runs** or on a computer that has access to its folders. It is the recommended option when you work with a **Windows desktop ERP** without API integration and prefer a fully local folder-based method, common in companies with *on-premise* infrastructure.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Operating system **Windows 7 or higher**.
> *Not compatible with Windows XP or Windows Server 2003.*
* An **active subscription to the eDocExchange product**.
## Access and configuration of the B2Bconnector add-on
[Section titled “Access and configuration of the B2Bconnector add-on”](#access-and-configuration-of-the-b2bconnector-add-on)
1. **Log in to your account** on [B2Brouter](https://app.b2brouter.net/en/login/) as an administrator user and select the **Developers** tab.
2. Go to **Connectors** and in the left-hand menu select **B2Bconnector**.
3. Click the **Download B2Bconnector** button to get the program installer.
4. Run the downloaded file:
* Windows may display a security warning.
* Accept and continue with the installation. It is a safe program.
* It may require administrator permissions.
### Advanced configuration (optional)
[Section titled “Advanced configuration (optional)”](#advanced-configuration-optional)
After installation, you can configure:
* **Local folder location** where the files will be synchronized.
* **Synchronization frequency** (time interval between checks).
To activate this option:
1. Check the **Configure** box in the *Advanced configuration* section.
2. Define the custom parameters according to your needs.
3. Finish by clicking **Activate**.
The parameters you can configure are as follows:
* **Synchronization**: set the waiting time before the system checks and attempts to import files. Simply indicate the number of minutes you want between one synchronization and the next.
* **B2Bconnector folder location**: you can create a new folder location to send the files you want to synchronize, different from the default folder created when installing B2Bconnector.
* **User and password**: you can enter the credentials of a new user so that they can access the B2Bconnector folder.
## Configuring the services
[Section titled “Configuring the services”](#configuring-the-services)
After installing and configuring B2Bconnector, you must activate the **sending (eSender)** and **receiving (eReceiver)** services you want to use:
[Configure the eSender and the eReceiver](/en/developers/integration-without-code/esender-ereceiver)
# eSender and eReceiver
> Configure the eSender and the eReceiver to send, receive and synchronise invoices, orders, status changes and declaration responses between your ERP and B2Brouter, and check the accepted formats.
Once you have a **connector** configured ([B2Bconnector](/en/developers/integration-without-code/b2bconnector) or [SFTP](/en/developers/integration-without-code/sftp)), it defines the **transport channel** between your ERP and B2Brouter. The **eSender** and the **eReceiver** define *which* documents travel through that channel and *in which format*:
* **Sending (eSender)**: which documents your ERP sends to B2Brouter and in which format.
* **Receiving (eReceiver)**: how and where B2Brouter drops the incoming documents for your ERP to pick up.
Your ERP drops files in a folder that B2Brouter watches (eSender), and B2Brouter drops files in another folder that your ERP watches (eReceiver). The logic is the same with any connector; only the way the files are physically transported changes.
> To enable this type of integration you need an **active subscription to the eDocExchange product**.
## Sending (eSender)
[Section titled “Sending (eSender)”](#sending-esender)
Each **folder** in the eSender corresponds to a different **format**. When your ERP drops a file in it, B2Brouter picks it up, interprets it according to that format and processes it.
| Parameter | Description |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Folder name** | Subfolder where the files are placed. It is created automatically inside the connector’s root folder. |
| **Format** | Electronic format of the invoices your ERP generates (see [Accepted formats](#accepted-invoice-formats)). |
| **Send automatically** | If enabled, the invoice is sent to the recipient right after importing it. If not, it is generated as a **draft** so you can review and edit it before issuing. |
* Each format needs its own folder.
* If an invoice is not imported, check that it is in the **correct folder** and that its **format matches** the one configured.
## Receiving (eReceiver)
[Section titled “Receiving (eReceiver)”](#receiving-ereceiver)
B2Brouter drops the incoming documents in a folder inside the **INBOX** of the active connector. There are five reception types, all sharing these common parameters:
| Parameter | Description |
| ----------------------------------------- | ---------------------------------------------------------------------- |
| **Connector** | Active connector used for the delivery. |
| **Folder name** | Folder inside INBOX where the files are placed. |
| **Add subfolder per recipient** | Creates a subfolder for each sending/receiving company. |
| **Add subfolder per organisational unit** | Creates a subfolder for each organisational unit (offices, branches…). |
Each type adds its own filters and delivery formats:
### Received invoices
[Section titled “Received invoices”](#received-invoices)
Invoices from suppliers.
* **Status filter**: all / accepted only / paid only / accepted or paid.
* **Format**: original (as the supplier sent it), PDF, or transformed to a specific format.
### Issued invoices
[Section titled “Issued invoices”](#issued-invoices)
A copy of the invoices you issue, delivered back to your ERP (useful to keep the legal file).
* **Format**: sent document (legal, signed invoice), PDF, or transformed to a specific format. You can combine several.
### Orders
[Section titled “Orders”](#orders)
Orders received from buyers.
* **Status filter**: all / accepted only.
* **Format**: original, PDF, or transformed to a specific format.
### Status changes
[Section titled “Status changes”](#status-changes)
Generates a file every time an issued invoice changes status, so your ERP knows the result of the delivery. It **does not send the invoices**, it only reports.
* **Status filter**: all / registered only / rejected only / registered or rejected.
* **Format**: CSV, ApplicationResponse (UBL XML), or Special (custom).
### Declaration responses
[Section titled “Declaration responses”](#declaration-responses)
Generates a file when a declaration sent to the administration changes status (submitted, accepted, rejected…).
* **Format**: ApplicationResponse (UBL XML) with the registration, validation or error information returned by the administration.
## Accepted invoice formats
[Section titled “Accepted invoice formats”](#accepted-invoice-formats)
You can import the invoices in different structured formats which enable B2Brouter to understand the data and turn them into the invoice format the recipient needs to receive. These are the different options available:
### XML
[Section titled “XML”](#xml)
Invoices can be imported in two different XML formats:
**Facturae**: Spanish electronic invoice format.
[Here you can consult the technical requirements that enable you to correctly generate invoices in Facturae XML format](https://www.facturae.gob.es/formato/Paginas/formato.aspx).
**UBL**: European electronic invoice format, used on the Peppol network.
[Here you can consult the technical requirements that enable you to correctly generate invoices in UBL XML format](https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/).
### CSV
[Section titled “CSV”](#csv)
With the integration of your system and B2Brouter you can also import the information using a CSV file. This file must comply with a specific structure which enables B2Brouter to understand the information and generate the document.
[Here you can download the technical specifications to create an invoice in .csv format](https://www.b2brouter.net/media/csv-en.pdf). [Here you can download the technical specifications to create the file with the SII records](https://www.b2brouter.net/media/sii-es.pdf).
### Excel format
[Section titled “Excel format”](#excel-format)
With the integration of your system and B2Brouter you can also import the information using an Excel file. This file must comply with a specific structure which enables B2Brouter to understand the information and generate the document.
[Here you can download the technical specifications to create the invoice in Excel format](https://www.b2brouter.net/media/excel-en.pdf).
### EDI format
[Section titled “EDI format”](#edi-format)
With the integration of your system and B2Brouter you can also import the information using an EDI file. In these cases, the characteristics of the EDI file to be used must be analysed.
[Contact](https://www.b2brouter.net/global/contact/) us if you want more information about importing EDI files.
### Other country-specific UBL formats
[Section titled “Other country-specific UBL formats”](#other-country-specific-ubl-formats)
**XRechnung/Zugferd**: These are the electronic invoice formats used in Germany. Both formats are accepted, although XRechnung is more widely used.
[Here you can see the technical specifications to create the invoice in XRechnung format](https://xeinkauf.de/).
**Chorus UBL**: It is the specific electronic invoice format used in France.
[Here you can see the technical specifications to create the invoice in Chorus UBL format](https://communaute.chorus-pro.gouv.fr/wp-content/uploads/2020/04/Specifications_Externes_Annexe_EDI_V4.20-1.pdf).
**Svefaktura**: It is the specific electronic invoice format used in Sweden.
[Here you can see the technical specifications to create the invoice in Svefaktura format](https://skr.se/download/18.427140af179361c4e462cb8f/1620641543411/Mappningsguide%20Svefaktura%201.0%20till%20EN16931-1.pdf).
**FatturaPA**: It is the specific electronic invoice format used in Italy.
[Here you can see the technical specifications to create the invoice in FatturaPA format](https://www.fatturapa.gov.it/export/documenti/Specifiche_tecniche_del_formato_FatturaPA_v1.2.1_EN.pdf).
# SFTP connection
> How to connect your ERP or management system to B2Brouter via SFTP for automated document exchange.
B2Brouter allows you to integrate your management or **ERP** system with the platform through different types of **connectors**. One of the available options is the connection via **SFTP server**, which enables the automated and bulk exchange of electronic documents.
> To activate this type of integration, you need an **active subscription to the eDocExchange product**.
There are two SFTP connection modes depending on where the server resides:
* **SFTP server** → B2Brouter acts as the server: your ERP connects to an SFTP account hosted on B2Brouter’s servers to upload and download documents.
* **SFTP client** → you manage the SFTP server: B2Brouter connects to your own server to deposit the received documents.
## SFTP server
[Section titled “SFTP server”](#sftp-server)
B2Brouter hosts an SFTP account with dedicated credentials for your company. Your ERP deposits the documents to be sent there and retrieves the received ones from the INBOX folder. This is the right mode when your ERP can connect to an external SFTP and you do not want to maintain your own server.
### Activation
[Section titled “Activation”](#activation)
1. Log in to your B2Brouter account and go to **Developers > Connectors**.
2. In the left-hand menu, select **SFTP server**.
3. Click **Activate**.
4. The system will display a **temporary password** in a banner. **Copy it and store it in a safe place.**
5. In your SFTP client, configure the connection details provided by B2Brouter:
| Field | Description |
| ------------ | --------------------------------------------------- |
| **Host** | Address of B2Brouter’s SFTP server |
| **Port** | Connection port shown when activating the connector |
| **User** | Automatically generated by B2Brouter |
| **Password** | Automatically generated by B2Brouter |
## SFTP client
[Section titled “SFTP client”](#sftp-client)
You manage the SFTP server, and B2Brouter connects as a client to deposit the received documents on it. This is the right mode when you already have your own SFTP server and prefer B2Brouter to deliver the documents there.
### Configuration
[Section titled “Configuration”](#configuration)
1. Log in to your B2Brouter account and go to **Developers > Connectors**.
2. In the left-hand menu, select **SFTP client**.
3. Provide the access details for your server:
| Field | Description |
| ------------- | ------------------------------------------------------------- |
| **Host** | Address of your SFTP server |
| **Port** | Usually 22 |
| **User** | Access credentials for your server |
| **Password** | Access credentials for your server |
| **Directory** | Destination path where the files will be deposited (optional) |
## Configuring the services
[Section titled “Configuring the services”](#configuring-the-services)
The connector is only the **transport channel** between your ERP and B2Brouter. To define **which documents are sent and received, and in which format**, you must configure the sending (**eSender**) and receiving (**eReceiver**) services:
[Configure the eSender and the eReceiver](/en/developers/integration-without-code/esender-ereceiver)
# FatturaPA Document Type Codes
> Reference of FatturaPA document type codes (TipoDocumento) for sending invoices to the Italian SDI.
When sending invoices to the Italian SDI (Sistema di Interscambio) in FatturaPA format, you can specify the document type using the `type_document` field. This field corresponds to the `TipoDocumento` element in the FatturaPA XML structure.
## Usage
[Section titled “Usage”](#usage)
The `type_document` field is optional when creating invoices. If not provided, B2Brouter will automatically assign an appropriate default value based on the invoice type (regular invoice, credit note, etc.).
### Setting type\_document via API
[Section titled “Setting type\_document via API”](#setting-type_document-via-api)
```json
{
"invoice": {
"type_document": "TD01",
"number": "1",
"date": "2025-01-26",
"contact_id": 12345,
"invoice_lines_attributes": [...]
}
}
```
## Valid FatturaPA Document Type Codes
[Section titled “Valid FatturaPA Document Type Codes”](#valid-fatturapa-document-type-codes)
The following codes are valid for Italian FatturaPA invoices:
### Invoice Types
[Section titled “Invoice Types”](#invoice-types)
| Code | Description (Italian) | Description (English) |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| **TD01** | Fattura | Standard invoice |
| **TD02** | Acconto/Anticipo su fattura | Down payment / Advance on invoice |
| **TD03** | Acconto/Anticipo su parcella | Down payment / Advance on proforma invoice |
| **TD04** | Nota di credito | Credit note |
| **TD05** | Nota di debito | Debit note |
| **TD06** | Parcella | Proforma invoice |
| **TD16** | Integrazione fattura reverse charge interno | Local reverse charge |
| **TD17** | Integrazione o autofattura per acquisti di servizi da soggetto non residente ai sensi dell’articolo 17, comma 2, DPR 633/1972 | Self-billing for EU services |
| **TD18** | Integrazione della fattura del fornitore UE per acquisto intracomunitario di beni | EU acquisition reverse charge |
| **TD19** | Integrazione o autofattura per acquisti di beni da soggetti non residenti ai sensi dell’articolo 17, comma 2, DPR 633/1972 | Not resident acquisition reverse charge |
| **TD20** | Autofattura per regolarizzazione e integrazione delle fatture | Self-billing |
| **TD21** | In caso di emissione di autofattura per regolarizzazione dello splafonamento | Regulated plafond self invoice |
| **TD22** | In caso di estrazione beni da Deposito IVA | VAT deposit |
| **TD23** | In caso di estrazione beni da Deposito IVA con versamento dell’IVA | VAT deposit with VAT payment |
| **TD24** | Fattura differita – art 21, 4 comma, lett. a), del DPR 633/72, quindi fattura differita di beni e servizi collegata a DDT per i beni o da idonea documentazione di prova dell’effettuazione per le prestazioni di servizio | Deferred invoice with DDT |
| **TD25** | Fattura differita di cui all’art. 21, 4 comma, lett. b), del DPR 633/72, quindi fattura differita per operazioni triangolari interne | Deferred invoice for internal triangular operations |
| **TD26** | Per la cessione di beni ammortizzabili e per passaggi interni | Sales of assets |
| **TD27** | In caso di fatture per auto consumo o per cessioni gratuite senza rivalsa | Free of charge or self-consumption |
## Special Considerations
[Section titled “Special Considerations”](#special-considerations)
### TD16-TD19 (Integration Documents)
[Section titled “TD16-TD19 (Integration Documents)”](#td16-td19-integration-documents)
These codes represent integration documents for reverse charge and similar mechanisms. When using these codes:
* They affect the calculation of the executable amount
* Special tax treatment applies
* The invoice represents a self-billing or integration scenario
### Default Behavior
[Section titled “Default Behavior”](#default-behavior)
If `type_document` is not specified:
* **TD01** is used for standard invoices
* **TD04** is used for credit notes (when `is_credit_note: true`)
* Other types must be explicitly set when needed
## Validation
[Section titled “Validation”](#validation)
Invalid codes will result in a validation error indicating the list of valid codes.
## Example: Complete Invoice with type\_document
[Section titled “Example: Complete Invoice with type\_document”](#example-complete-invoice-with-type_document)
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_document": "TD01",
"number": "FAT-2025-001",
"date": "2025-01-26",
"due_date": "2025-02-26",
"contact_id": 12345,
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 100.0,
"description": "Professional services",
"taxes_attributes": [
{
"name": "IVA",
"percent": 22
}
]
}
]
}
}
```
# Introduction
> Overview of B2Brouter JSON-to-XML mapping guides for UBL, CII, Facturae, FatturaPA, and KSeF formats.
Mapping guides show how each B2Brouter JSON invoice field maps to its equivalent element in the target XML format. Use them as a reference when building your integration to ensure your fields produce the correct output.
## Document type codes
[Section titled “Document type codes”](#document-type-codes)
[Peppol and CII Document Type Codes UN/CEFACT invoice type codes for PEPPOL BIS 3.0 and Cross Industry Invoice formats.](/en/developers/mapping-guides/peppol-and-cii-document-type-codes/)
[ ](/en/developers/mapping-guides/peppol-and-cii-document-type-codes/)
[FatturaPA Document Type Codes TipoDocumento codes for Italian FatturaPA invoices sent to SDI.](/en/developers/mapping-guides/fatturapa-document-type-codes/)
## Field mappings
[Section titled “Field mappings”](#field-mappings)
[JSON to UBL XML Field mapping for UBL 2.1 — EN 16931 and Peppol BIS 3.0.](/en/developers/mapping-guides/json-invoice-fields-to-ubl-xml-en16931-peppol-bis-3/)
[ ](/en/developers/mapping-guides/json-invoice-fields-to-ubl-xml-en16931-peppol-bis-3/)
[JSON to CII XML Field mapping for Cross Industry Invoice — Factur-X, ZUGFeRD, EN 16931.](/en/developers/mapping-guides/json-invoice-fields-to-cross-industry-invoice-cii-xml/)
[ ](/en/developers/mapping-guides/json-invoice-fields-to-cross-industry-invoice-cii-xml/)
[JSON to Facturae XML Field mapping for Facturae 3.2.2.](/en/developers/mapping-guides/json-invoice-fields-to-facturae-xml-3-2-2/)
[ ](/en/developers/mapping-guides/json-invoice-fields-to-facturae-xml-3-2-2/)
[JSON to KSeF FA(3) XML Field mapping for Polish KSeF FA(3) format.](/en/developers/mapping-guides/json-invoice-fields-to-ksef-fa3-xml/)
# JSON Invoice Fields to Cross Industry Invoice (CII) XML
> Mapping of B2Brouter JSON invoice fields to Cross Industry Invoice (CII) XML paths and EN 16931 Business Terms.
This guide details the mapping between the **B2Brouter Invoice API fields**, the corresponding **Business Terms (BT)** as defined by the European e-Invoicing standard (EN 16931), and the **XML paths** in the **Cross Industry Invoice (CII)** model.
Its purpose is to help understand how invoice data submitted in JSON format corresponds to an XML structure compliant with the Cross Industry Invoice (CII) model. This includes detailed field-level alignment between the invoice creation parameters in the API, the semantic definitions (BTs), and their exact representation in the CII XML.
## Header-Level Mapping
[Section titled “Header-Level Mapping”](#header-level-mapping)
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **CII XPath (Full)** |
| --------- | ----------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| BT-1 | Invoice Number | `invoice.number` | `rsm:ExchangedDocument/ram:ID` |
| BT-2 | Invoice Issue Date | `invoice.date` | `rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString` |
| BT-3 | Invoice Type Code | Defaults to 380 (invoice), 381 if `is_credit_note = true` | `rsm:ExchangedDocument/ram:TypeCode` |
| BT-5 | Invoice Currency | `invoice.currency` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceCurrencyCode` |
| BT-9 | Payment Due Date | `invoice.due_date` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/dt:DateTimeString` |
| BT-10 | Buyer Reference | `invoice.buyer_reference` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerReference` |
| BT-11 | Project Reference | `invoice.file_reference` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SpecifiedProcuringProject/ram:ID` |
| BT-12 | Contract Reference | `invoice.contract_number` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:ContractReferencedDocument/ram:IssuerAssignedID` |
| BT-13 | Order Reference | `invoice.ponumber` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerOrderReferencedDocument/ram:IssuerAssignedID` |
| BT-14 | Sales Order Reference | `invoice.sales_order_reference` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerOrderReferencedDocument/ram:IssuerAssignedID` |
| BT-15 | Referenced Receiving Advice | `invoice.receiving_advice_reference` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ReceivingAdviceReferencedDocument/ram:IssuerAssignedID` |
| BT-16 | Referenced Despatch Advice | `invoice.delivery_note_number` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:DespatchAdviceReferencedDocument/ram:IssuerAssignedID` |
| BT-19 | Accounting Cost | `invoice.buyer_accounting_reference` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ReceivableSpecifiedTradeAccountingAccount/ram:ID` |
| BT-22 | Note (Header) | `invoice.extra_info` | `ram:IncludedNote/ram:Content` |
| BT-25 | Preceding Invoice number | `invoice.amended_number` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:IssuerAssignedID` |
| BT-26 | Preceding Invoice issue date | `invoice.amended_date` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:InvoiceReferencedDocument/ram:FormattedIssueDateTime/udt:DateTimeString` |
| BT-73 | Invoicing Period Start | `invoice.invoicing_period_start` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:BillingSpecifiedPeriod/ram:StartDateTime/udt:DateTimeString` |
| BT-74 | Invoicing Period End | `invoice.invoicing_period_end` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:BillingSpecifiedPeriod/ram:EndDateTime/udt:DateTimeString` |
| BT-92 | Allowance Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", amount: X}` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:ActualAmount` |
| BT-94 | Allowance Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", percentage: X}` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CalculationPercent` |
| BT-97 | Allowance Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", description: "..."}` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:Reason` |
| BT-99 | Charge Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", amount: X}` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:ActualAmount` |
| BT-101 | Charge Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", percentage: X}` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CalculationPercent` |
| BT-102 | Apply Tax To Allowance/Charge | `allowance_charges_attributes[]{apply_taxes: true}` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CategoryTradeTax` |
| BT-104 | Charge Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", description: "..."}` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:Reason` |
| BT-113 | Total Prepaid Amount | `invoice.payments_on_account` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:TotalPrepaidAmount` |
## Payment-Level Mapping
[Section titled “Payment-Level Mapping”](#payment-level-mapping)
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **CII XPath (Full)** |
| --------- | ----------------------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BT-20 | Payment Terms | `invoice.payment_terms` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradePaymentTerms/ram:Description` |
| BT-81 | Payment Means Code | `invoice.payment_method` (check our code list) | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:TypeCode` |
| BT-83 | Payment ID | `invoice.remittance_information` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:PaymentReference` |
| BT-84 | Payee Creditor Financial Account IBAN | `invoice.bank_account.iban` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount/ram:IBANID` |
| BT-84-0 | Payee Creditor Financial Account Proprietary ID | `invoice.bank_account.number` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount/ram:ProprietaryID` |
| BT-85 | Payee Creditor Financial Account Name | `invoice.bank_account.name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount/ram:AccountName` |
| BT-86 | Payee Creditor Financial Account BIC | `invoice.bank_account.bic` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeeSpecifiedCreditorFinancialInstitution/ram:BICID` |
| BT-87 | Card Account Number ID | `invoice.card_account_attributes.account_number` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:ApplicableTradeSettlementFinancialCard/ram:ID` |
| BT-88 | Card Account Holder Name ID | `invoice.card_account_attributes.holder_name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:ApplicableTradeSettlementFinancialCard/ram:CardholderName` |
| BT-89 | Direct Debit Mandate ID | `invoice.mandate_reference_identifier` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradePaymentTerms/ram:DirectDebitMandateID` |
| BT-90 | Creditor Reference ID | `invoice.bank_assigned_creditor_reference` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:CreditorReferenceID` |
| BT-91 | Payee Debtor Financial Account IBAN | `invoice.contact_iban` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyDebtorFinancialAccount/ram:IBANID` |
## Parties Mapping
[Section titled “Parties Mapping”](#parties-mapping)
This mapping is described for an issued invoice; if you want to map received invoice swap the SellerTradeParty and BuyerTradeParty.
### SellerTradeParty
[Section titled “SellerTradeParty”](#sellertradeparty)
The `SellerTradeParty` is automatically populated based on your account and transports settings. This includes company name, legal identifiers, and contact data (from `/accounts` endpoint) and Peppol identifiers (from `/transports` endpoint).
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **CII XPath (Full)** |
| --------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| BT-27 | Seller Party Name | `account.name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:Name` |
| BT-28 | Seller Trade Business Name | `account.name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedLegalOrganization/ram:TradingBusinessName` |
| BT-29-0 | Seller Global ID | Filled with the first available value among `account.cin_value` and `transport.pin_value`, as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:GlobalID` |
| BT-29-1 | Seller Global ID Scheme | Filled with the first available value among `account.cin_scheme` and `transport.pin_scheme`, as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:GlobalID/@schemeID` |
| BT-30 | Seller Legal ID | Filled with the first available value among `account.cin_value`, `transport.pin_value`, and `account.tin_value`, as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedLegalOrganization/ram:ID` |
| BT-30-1 | Seller Legal ID Scheme | Filled with the first available value among `account.cin_scheme`, `transport.pin_scheme`, and `account.tin_scheme`, as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedLegalOrganization/ram:ID/@schemeID` |
| BT-31,BT-32 | Seller Tax Registration ID | `account.tin_value` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID` |
| BT-31-0,BT-32-0 | Seller Tax Registration ID Scheme | `account.tin_scheme` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID/@schemeID` |
| BT-34 | Seller Electronic Address ID | `transport.pin_value`. If Peppol Identifier Number is not present it will get `account.email` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:URIUniversalCommunication/ram:URIID` |
| BT-34-1 | Seller Electronic Address ID Scheme | `transport.pin_scheme`. If Peppol Identifier Number is not present it will be ‘EM’ scheme for email. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:URIUniversalCommunication/ram:URIID/@schemeID` |
| BT-35 | Seller Address Street 1 | `account.address` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress/ram:LineOne` |
| BT-36 | Seller Address Street 2 | `account.address2` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress/ram:LineTwo` |
| BT-37 | Seller Address City | `account.city` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress/ram:CityName` |
| BT-38 | Seller Address Postal Code | `account.postalcode` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress/ram:PostcodeCode` |
| BT-39 | Seller Address Country SubDivision Name | `account.province` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress/ram:CountrySubDivisionName` |
| BT-40 | Seller Address Country | `account.country` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:PostalTradeAddress/ram:CountryID` |
| BT-41 | Seller Contact Person Name | `account.contact_person` | |
| BT-42 | Seller Contact Person Phone | `account.phone` | |
### BuyerTradeParty
[Section titled “BuyerTradeParty”](#buyertradeparty)
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **CII XPath (Full)** |
| --------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| BT-44 | Buyer Party Name | `invoice.contact.name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:Name` |
| BT-45 | Buyer Trade Business Name | `invoice.contact.name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:SpecifiedLegalOrganization/ram:TradingBusinessName` |
| BT-46-0 | Buyer Global ID | Filled with the first available value among `invoice.contact.cin_value` and `invoice.contact.pin_value`, as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:GlobalID` |
| BT-46-1 | Buyer Global ID Scheme | Filled with the first available value among `invoice.contact.cin_scheme` and `invoice.contact.pin_scheme`, as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:GlobalID/@schemeID` |
| BT-47 | Buyer Legal ID | Filled with the first available value among `invoice.contact.cin_value`, `invoice.contact.pin_value` and `invoice.contact.tin_value` as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:SpecifiedLegalOrganization/ram:ID` |
| BT-47-1 | Buyer Legal ID Scheme | Filled with the first available value among `invoice.contact.cin_scheme`, `invoice.contact.pin_scheme` and `invoice.contact.tin_scheme`, as long as the value is in the ICD code list. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:SpecifiedLegalOrganization/ram:ID/@schemeID` |
| BT-48 | Buyer Tax Registration ID | `invoice.contact.tin_value` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:SpecifiedTaxRegistration/ram:ID` |
| BT-48-0 | Buyer Tax Registration ID Scheme | `invoice.contact.tin_scheme` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:SpecifiedTaxRegistration/ram:ID/@schemeID` |
| BT-49 | Buyer Electronic Address ID | `invoice.contact.pin_value`. If Peppol Identifier Number is not present it will get `invoice.contact.email` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:URIUniversalCommunication/ram:URIID` |
| BT-49-1 | Buyer Electronic Address ID Scheme | `invoice.contact.pin_scheme`. If Peppol Identifier Number is not present it will be ‘EM’ scheme for email. | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:URIUniversalCommunication/ram:URIID/@schemeID` |
| BT-50 | Buyer Address Street 1 | `invoice.contact.address` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress/ram:LineOne` |
| BT-51 | Buyer Address Street 2 | `invoice.contact.address2` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress/ram:LineTwo` |
| BT-52 | Buyer Address City | `invoice.contact.city` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress/ram:CityName` |
| BT-53 | Buyer Address Postal Code | `invoice.contact.postalcode` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress/ram:PostcodeCode` |
| BT-54 | Buyer Address Country SubDivision Name | `invoice.contact.province` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress/ram:CountrySubDivisionName` |
| BT-55 | Buyer Address Country | `invoice.contact.country` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty/ram:PostalTradeAddress/ram:CountryID` |
| BT-56 | Buyer Contact Person Name | `invoice.contact.contact_person` | |
| BT-57 | Buyer Contact Person Phone | `invoice.contact.phone` | |
## Delivery Mapping
[Section titled “Delivery Mapping”](#delivery-mapping)
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **CII XPath (Full)** |
| --------- | --------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| BT-70 | Delivery Party Name | `invoice.delivery_party_name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:Name` |
| BT-71 | Delivery Location ID | `invoice.delivery_location_id` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:ID` |
| BT-72 | Delivery Date | `invoice.delivery_date` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:SpecifiedSupplyChainEvent/ram:OccurrenceDateTime` |
| BT-75 | Delivery Address Street | `invoice.delivery_address` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:PostalTradeAddress/ram:LineOne` |
| BT-76 | Delivery Address Street 2 | `invoice.delivery_address2` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:PostalTradeAddress/ram:LineTwo` |
| BT-77 | Delivery City Name | `invoice.delivery_city` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:PostalTradeAddress/ram:CityName` |
| BT-78 | Delivery Postal Code | `invoice.delivery_postalcode` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:PostalTradeAddress/ram:PostcodeCode` |
| BT-79 | Delivery Country SubDivision Name | `invoice.delivery_province` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:PostalTradeAddress/ram:CountrySubDivisionName` |
| BT-80 | Delivery Country | `invoice.delivery_country` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeDelivery/ram:ShipToTradeParty/ram:PostalTradeAddress/ram:CountryID` |
## Invoice Line-Level Mapping
[Section titled “Invoice Line-Level Mapping”](#invoice-line-level-mapping)
All attributes listed in the following table are properties of objects contained in the array: `invoice.invoice_lines_attributes[]`. The `API Field` column refers to keys within each invoice line object.
| **BT ID** | **Business Term** | **API Field (invoice line object)** | **CII XPath (Full)** |
| --------- | --------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BT-126 | Invoice Line ID | `position` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:AssociatedDocumentLineDocument/ram:LineID` |
| BT-128 | Purchase Order Number | `ponumber` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:AdditionalReferencedDocument/ram:IssuerAssignedID` |
| BT-129 | Billed Quantity | `quantity` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity` |
| BT-130 | Billed Quantity (Unit code) | `unit` (check API Reference for code list) | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode` |
| BT-131 | Line Total Amount | `extension_amount` (if it’s not provided, it will be calculated) | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeSettlementLineMonetarySummation/ram:LineTotalAmount` |
| BT-132 | Order Line Reference | `sequence_number` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeAgreement/ram:BuyerOrderReferencedDocument/ram:LineID` |
| BT-133 | Accounting Cost | `contact_reference` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:ReceivableSpecifiedTradeAccountingAccount/ram:ID` |
| BT-134 | Invoicing Line Period Start | `invoicing_period_start` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:BillingSpecifiedPeriod/ram:StartDateTime` |
| BT-135 | Invoicing Line Period End | `invoicing_period_end` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:BillingSpecifiedPeriod/ram:EndDateTime` |
| BT-136 | Line Allowance Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", amount: X}` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:ActualAmount` |
| BT-138 | Line Allowance Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", percentage: X}` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CalculationPercent` |
| BT-139 | Line Allowance Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", description: "..."}` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:Reason` |
| BT-141 | Line Charge Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", amount: X}` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:ActualAmount` |
| BT-143 | Line Charge Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", percentage: X}` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:CalculationPercent` |
| BT-144 | Line Charge Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", description: "..."}` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeSettlement/ram:SpecifiedTradeAllowanceCharge/ram:Reason` |
| BT-146 | Price Amount | `price` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount` |
| BT-153 | Item Name | `description` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:Name` |
| BT-154 | Item Description | `notes` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:Description` |
| BT-155 | Seller Item ID | `article_code` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:SellerAssignedID` |
| BT-156 | Buyer Item ID | `article_code_buyer` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:BuyerAssignedID` |
| BT-157 | Item Standard ID | `article_code2` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:GlobalID` |
| BT-157-1 | Item Standard Scheme | `article_code2_scheme` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:GlobalID/@schemeID` |
| BT-158 | Item Classification Code | `classification_code` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:DesignatedProductClassification/ram:ClassCode` |
| BT-158-1 | Item Classification Code (scheme) | `classification_code_scheme` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:DesignatedProductClassification/ram:ClassCode/@listID` |
| BT-159 | Item Origin Country | `item_origin_country` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:OriginTradeCountry/ram:ID` |
| BT-160 | Additional Item Property (Name) | `additional_item_properties_attributes[].name` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:ApplicableProductCharacteristic/ram:Description` |
| BT-161 | Additional Item Property (Value) | `additional_item_properties_attributes[].value` | `rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem/ram:SpecifiedTradeProduct/ram:ApplicableProductCharacteristic/ram:Value` |
### Taxes Mapping
[Section titled “Taxes Mapping”](#taxes-mapping)
All attributes listed in the following table are properties of objects contained in the array: `invoice.invoice_lines_attributes[].taxes_attributes[]`. The `API Field` column refers to keys within each tax object.
| **BT ID** | **Business Term** | **API Field (tax object)** | **CII XPath (Full)** |
| --------- | --------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| BT-118-0 | Tax Type Code | `name` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:TypeCode` |
| BT-118 | Tax Category Code | `category` (check our code list) | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:CategoryCode` |
| BT-119 | Tax Rate Applicable Percent | `percent` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent` |
| BT-120 | Tax Exemption Reason | `comment` | `rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:ExemptionReason` |
# JSON Invoice Fields to Facturae XML (3.2.2)
> Mapping of B2Brouter JSON invoice fields to Facturae 3.2.2 XML paths and EN 16931 Business Terms.
This guide maps the B2Brouter Invoice API fields and the corresponding EN 16931 Business Terms (BT) to XML paths in Facturae 3.2.2. For clarity, XPaths are shown without namespace prefixes.
## Header-Level Mapping
[Section titled “Header-Level Mapping”](#header-level-mapping)
| BT | Business Term | API Field (B2Brouter) | Facturae XPath |
| ----- | ------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| BT-1 | Invoice number | `invoice.number` | `Invoices/Invoice/InvoiceHeader/InvoiceNumber` |
| — | Invoice series | `invoice.series_code` | `Invoices/Invoice/InvoiceHeader/InvoiceSeriesCode` |
| — | Document type | (derived) | `Invoices/Invoice/InvoiceHeader/InvoiceDocumentType` |
| — | Invoice class | (derived) | `Invoices/Invoice/InvoiceHeader/InvoiceClass` |
| BT-2 | Issue Date | `invoice.date` | `Invoices/Invoice/InvoiceIssueData/IssueDate` |
| BT-7 | Operation Date | `invoice.tax_point_date` (preferred) or `invoice.delivery_date` (fallback) | `Invoices/Invoice/InvoiceIssueData/OperationDate` |
| BT-5 | Currency | `invoice.currency` | `Invoices/Invoice/InvoiceIssueData/InvoiceCurrencyCode` |
| — | Language | `invoice.language` | `Invoices/Invoice/InvoiceIssueData/LanguageName` |
| — | Exchange Rate | `invoice.exchange_rate` | `Invoices/Invoice/InvoiceIssueData/ExchangeRateDetails/ExchangeRate` |
| — | Exchange Rate Date | `invoice.exchange_date` | `Invoices/Invoice/InvoiceIssueData/ExchangeRateDetails/ExchangeRateDate` |
| BT-22 | Note (Header) | `invoice.extra_info` | `Invoices/Invoice/AdditionalData/InvoiceAdditionalInformation` |
| — | Legal literals | `invoice.legal_literals` | `Invoices/Invoice/LegalLiterals/LegalReference` |
Notes:
* `ExchangeRateDetails` is only emitted when the invoice currency differs from EUR. `exchange_rate` and `exchange_date` can be set via the API; when not provided, they are derived from account/system settings.
* If both `tax_point_date` and `delivery_date` are present, `delivery_date` is appended to `InvoiceAdditionalInformation`.
* Several fields are packed into `InvoiceAdditionalInformation` (see [Additional Information Fields](#additional-information-fields)).
## Payment
[Section titled “Payment”](#payment)
| BT | Business Term | API Field (B2Brouter) | Facturae XPath |
| ----- | --------------------------------------- | ------------------------------------- | ---------------------------------------------------------------- |
| BT-9 | Due Date | `invoice.due_date` | `Invoices/Invoice/PaymentDetails/Installment/InstallmentDueDate` |
| — | Installment Amount | (calculated) | `.../Installment/InstallmentAmount` |
| BT-81 | Payment means | `invoice.payment_method` | `.../Installment/PaymentMeans` |
| BT-82 | Payment means text | `invoice.payment_method_text` | `.../Installment/CollectionAdditionalInformation` |
| BT-84 | Seller bank account (IBAN) | `invoice.bank_account.iban` | `.../Installment/AccountToBeCredited/IBAN` |
| BT-86 | Seller bank account (BIC) | `invoice.bank_account.bic` | `.../Installment/AccountToBeCredited/BIC` |
| BT-84 | Seller bank account (number, non-IBAN) | `invoice.bank_account.number` | `.../Installment/AccountToBeCredited/AccountNumber` |
| BT-91 | Buyer bank account (IBAN, direct debit) | `invoice.contact_iban` | `.../Installment/AccountToBeDebited/IBAN` |
| — | Buyer bank account (number, non-IBAN) | `invoice.contact_bank_account_number` | `.../Installment/AccountToBeDebited/AccountNumber` |
| — | Buyer bank account (BIC, direct debit) | `invoice.contact_bic` | `.../Installment/AccountToBeDebited/BIC` |
| BT-10 | Buyer Reference | `invoice.buyer_reference` | `.../Installment/DebitReconciliationReference` |
| — | Payment Reconciliation Reference | `invoice.payment_reconciliation_ref` | `.../Installment/PaymentReconciliationReference` |
Notes:
* `PaymentDetails/Installment` is emitted only when payment/terms are defined.
## References & Periods
[Section titled “References & Periods”](#references--periods)
Header-level commercial references (InvoiceIssueData):
| BT | Business Term | API Field (B2Brouter) | Facturae XPath |
| ----- | --------------------------- | ------------------------------------- | ---------------------------------------------------------------- |
| BT-13 | Purchase Order Reference | `invoice.ponumber` | `Invoices/Invoice/InvoiceIssueData/ReceiverTransactionReference` |
| BT-11 | File reference | `invoice.file_reference` | `Invoices/Invoice/InvoiceIssueData/FileReference` |
| — | Receiver contract reference | `invoice.receiver_contract_reference` | `Invoices/Invoice/InvoiceIssueData/ReceiverContractReference` |
Invoicing period (Header):
| BT | Business Term | API Field | Facturae XPath |
| ----- | ---------------------- | -------------------------------- | ------------------------------------------------------------- |
| BT-73 | Invoicing Period Start | `invoice.invoicing_period_start` | `Invoices/Invoice/InvoiceIssueData/InvoicingPeriod/StartDate` |
| BT-74 | Invoicing Period End | `invoice.invoicing_period_end` | `Invoices/Invoice/InvoiceIssueData/InvoicingPeriod/EndDate` |
Corrective invoices (rectified):
| BT | Field | API Field | Facturae XPath |
| ----- | ----------------------------- | ------------------------------------------- | ------------------------------------------------------------------------ |
| — | Method | (derived from `invoice.amended_number`) | `Invoices/Invoice/InvoiceHeader/Corrective/CorrectionMethod` |
| — | Method description | (derived) | `.../Corrective/CorrectionMethodDescription` |
| — | Reason code | `invoice.amend_reason` (defaults to `'16'`) | `.../Corrective/ReasonCode` |
| — | Reason description | (derived from `amend_reason` translation) | `.../Corrective/ReasonDescription` |
| — | Additional reason description | `invoice.extra_info` | `.../Corrective/AdditionalReasonDescription` |
| BT-25 | Corrected invoice number | `invoice.amended_number` | `.../Corrective/InvoiceNumber` (+ `.../InvoiceSeriesCode` if applicable) |
| BT-26 | Corrected invoice date | `invoice.amended_date` | `.../Corrective/InvoiceIssueDate` (3.2.2 only) |
| — | Tax period start | `invoice.amended_invoicing_period_start` | `.../Corrective/TaxPeriod/StartDate` |
| — | Tax period end | `invoice.amended_invoicing_period_end` | `.../Corrective/TaxPeriod/EndDate` |
Notes:
* `CorrectionMethod` is computed: `'03'` if `amended_number == '?'`, otherwise `'02'`.
## Parties Mapping (Issued Invoice)
[Section titled “Parties Mapping (Issued Invoice)”](#parties-mapping-issued-invoice)
If the invoice is received, swap SellerParty and BuyerParty.
### Seller Party (Issuer)
[Section titled “Seller Party (Issuer)”](#seller-party-issuer)
| BT | Business Term | API Field | Facturae XPath |
| ----- | --------------------- | ------------------------------ | --------------------------------------------------------------------------------------- |
| BT-27 | Seller Name | `account.name` | `Parties/SellerParty/LegalEntity/CorporateName` (or `Individual/Name` + `FirstSurname`) |
| BT-31 | Seller VAT ID | `account.tin_value` | `Parties/SellerParty/TaxIdentification/TaxIdentificationNumber` |
| — | Party Identification | `invoice.party_identification` | `Parties/SellerParty/PartyIdentification` |
| BT-35 | Seller Street | `account.address` | `Parties/SellerParty/LegalEntity/AddressInSpain/Address` (or `OverseasAddress/Address`) |
| BT-38 | Seller Postal Code | `account.postalcode` | `Parties/SellerParty/LegalEntity/AddressInSpain/PostCode` |
| BT-37 | Seller City | `account.city` | `Parties/SellerParty/LegalEntity/AddressInSpain/Town` |
| BT-39 | Seller Province | `account.province` | `Parties/SellerParty/LegalEntity/AddressInSpain/Province` |
| BT-40 | Seller Country | `account.country` | `Parties/SellerParty/LegalEntity/AddressInSpain/CountryCode` (ISO3) |
| BT-43 | Seller Email | `account.email` | `Parties/SellerParty/LegalEntity/ContactDetails/ElectronicMail` |
| BT-42 | Seller Phone | `account.phone` | `Parties/SellerParty/LegalEntity/ContactDetails/Telephone` |
| — | Seller Website | `account.website` | `Parties/SellerParty/LegalEntity/ContactDetails/WebAddress` |
| BT-41 | Seller Contact Person | `account.contact_person` | `Parties/SellerParty/LegalEntity/ContactDetails/ContactPersons` |
### Buyer Party (Receiver)
[Section titled “Buyer Party (Receiver)”](#buyer-party-receiver)
| BT | Business Term | API Field | Facturae XPath |
| ----- | -------------------------- | ------------------------------------ | ------------------------------------------------------------------------ |
| BT-44 | Buyer Name | `invoice.contact.name` | `Parties/BuyerParty/LegalEntity/CorporateName` (or `Individual/...`) |
| BT-48 | Buyer VAT ID | `invoice.contact.tin_value` | `Parties/BuyerParty/TaxIdentification/TaxIdentificationNumber` |
| BT-50 | Buyer Street | `invoice.contact.address` | `Parties/BuyerParty/LegalEntity/AddressInSpain/Address` |
| BT-53 | Buyer Postal Code | `invoice.contact.postalcode` | `Parties/BuyerParty/LegalEntity/AddressInSpain/PostCode` |
| BT-52 | Buyer City | `invoice.contact.city` | `Parties/BuyerParty/LegalEntity/AddressInSpain/Town` |
| BT-54 | Buyer Province | `invoice.contact.province` | `Parties/BuyerParty/LegalEntity/AddressInSpain/Province` |
| BT-55 | Buyer Country | `invoice.contact.country` | `Parties/BuyerParty/LegalEntity/AddressInSpain/CountryCode` |
| BT-58 | Buyer Email | `invoice.contact.email` | `Parties/BuyerParty/LegalEntity/ContactDetails/ElectronicMail` |
| BT-57 | Buyer Phone | `invoice.contact.phone` | `Parties/BuyerParty/LegalEntity/ContactDetails/Telephone` |
| BT-56 | Buyer Contact Person | `invoice.contact.contact_person` | `Parties/BuyerParty/LegalEntity/ContactDetails/ContactPersons` |
| — | Additional Contact Details | `invoice.contact_additional_details` | `Parties/BuyerParty/LegalEntity/ContactDetails/AdditionalContactDetails` |
Notes:
* Use `AddressInSpain` vs `OverseasAddress` depending on the country.
* `PartyIdentification` is only emitted when the value is a numeric string up to 10 digits.
## Administrative Centres (Spanish Public Sector)
[Section titled “Administrative Centres (Spanish Public Sector)”](#administrative-centres-spanish-public-sector)
For invoicing Spanish public administrations (FACe), administrative centres can be set with DIR3 codes:
| Field | API Field | Facturae XPath |
| -------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------- |
| Accounting Office | `invoice.oc` | `Parties/BuyerParty/AdministrativeCentres/AdministrativeCentre[RoleTypeCode='01']/CentreCode` |
| Management Body | `invoice.og` | `Parties/BuyerParty/AdministrativeCentres/AdministrativeCentre[RoleTypeCode='02']/CentreCode` |
| Processing Unit | `invoice.ut` | `Parties/BuyerParty/AdministrativeCentres/AdministrativeCentre[RoleTypeCode='03']/CentreCode` |
| Proposing Body | `invoice.op` | `Parties/BuyerParty/AdministrativeCentres/AdministrativeCentre[RoleTypeCode='04']/CentreCode` |
| Delivery Location ID | `invoice.delivery_location_id` | Used as `CentreCode` for role 04 when `op` is not set |
| Centre 1-3 Names | `invoice.cin1_name` … `invoice.cin3_name` | `AdministrativeCentre[RoleTypeCode='01'-'03']/Name` |
| Centre 4 Name | `invoice.cin4_name` | `AdministrativeCentre[RoleTypeCode='04']/Name` |
Notes:
* Each administrative centre also carries the address and contact details from the buyer party.
* Delivery address fields (`delivery_address`, `delivery_city`, `delivery_province`, `delivery_postalcode`) populate the address of the RoleTypeCode `04` centre.
## Taxes & Totals (Header)
[Section titled “Taxes & Totals (Header)”](#taxes--totals-header)
| BT | Business Term | API Field | Facturae XPath |
| ------ | ------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| BT-110 | VAT total (Outputs) | (calculated) | `Invoices/Invoice/InvoiceTotals/TotalTaxOutputs` + details in `Invoices/Invoice/TaxesOutputs/Tax` |
| — | Withheld (header) | `invoice.amounts_withheld_reason`, `invoice.withheld_percent` | `Invoices/Invoice/InvoiceTotals/AmountsWithheld/(WithholdingReason\|WithholdingRate\|WithholdingAmount)` |
| — | Gross amount before taxes | (calculated) | `Invoices/Invoice/InvoiceTotals/TotalGrossAmountBeforeTaxes` |
| BT-92 | Header Allowance Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", amount: X}` | `Invoices/Invoice/InvoiceTotals/GeneralDiscounts/Discount/DiscountAmount` |
| BT-94 | Header Allowance Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", percentage: X}` | `Invoices/Invoice/InvoiceTotals/GeneralDiscounts/Discount/DiscountRate` |
| BT-97 | Header Allowance Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", description: "..."}` | `Invoices/Invoice/InvoiceTotals/GeneralDiscounts/Discount/DiscountReason` |
| BT-99 | Header Charge Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", amount: X}` | `Invoices/Invoice/InvoiceTotals/GeneralSurcharges/Charge/ChargeAmount` |
| BT-104 | Header Charge Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", description: "..."}` | `Invoices/Invoice/InvoiceTotals/GeneralSurcharges/Charge/ChargeReason` |
| BT-113 | Prepaid Amount | `invoice.payments_on_account` | `Invoices/Invoice/InvoiceTotals/PaymentsOnAccount/PaymentOnAccount/PaymentOnAccountAmount` |
| — | Invoice total (payable) | (calculated) | `Invoices/Invoice/InvoiceTotals/InvoiceTotal` |
| — | Outstanding | (calculated) | `Invoices/Invoice/InvoiceTotals/TotalOutstandingAmount` |
| — | Executable | (calculated) | `Invoices/Invoice/InvoiceTotals/TotalExecutableAmount` |
Notes:
* `TaxesOutputs/Tax/TaxTypeCode` identifies the tax (e.g. `01` for VAT).
## Invoice Line-Level Mapping
[Section titled “Invoice Line-Level Mapping”](#invoice-line-level-mapping)
All attributes below are properties of objects in: `invoice.invoice_lines_attributes[]`.
| BT | Business Term | API Field (line) | Facturae XPath |
| ------ | -------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| BT-153 | Item name/description | `description` | `Invoices/Invoice/Items/InvoiceLine/ItemDescription` |
| BT-129 | Invoiced quantity | `quantity` | `.../Quantity` |
| BT-130 | Unit of measure | `unit` | `.../UnitOfMeasure` |
| BT-146 | Price amount (unit) | `price` | `.../UnitPriceWithoutTax` |
| BT-131 | Line extension amount | `extension_amount` (or calculated) | `.../GrossAmount` (or `.../TotalCost`) |
| BT-155 | Seller Item ID | `article_code` | `.../ArticleCode` (Facturae 3.2 only) |
| BT-154 | Item notes | `notes` | `.../AdditionalLineItemInformation` |
| BT-132 | Buyer line reference | `sequence_number` | `.../SequenceNumber` |
| BT-128 | Receiver transaction reference | `ponumber` (fallback: `invoice.ponumber`) | `.../ReceiverTransactionReference` |
| — | File reference | `file_reference` (fallback: `invoice.file_reference`) | `.../FileReference` |
| — | File date | `file_date` | `.../FileDate` |
| — | Delivery note number | `delivery_note_number` (fallback: `invoice.delivery_note_number`) | `.../DeliveryNotesReferences/DeliveryNote/DeliveryNoteNumber` |
| — | Delivery note date | `delivery_note_date` (fallback: `invoice.delivery_note_date`) | `.../DeliveryNotesReferences/DeliveryNote/DeliveryNoteDate` |
| — | Issuer contract reference | (from `invoice.contract_number`) | `.../IssuerContractReference` |
| — | Issuer contract date | `invoice.issuer_contract_date` | `.../IssuerContractDate` |
| — | Issuer transaction reference | `issuer_transaction_reference` | `.../IssuerTransactionReference` |
| — | Issuer transaction date | `issuer_transaction_date` | `.../IssuerTransactionDate` |
| — | Receiver contract reference | `receiver_contract_reference` (fallback: `invoice.receiver_contract_reference`) | `.../ReceiverContractReference` |
| — | Receiver contract date | `receiver_contract_date` | `.../ReceiverContractDate` |
| — | Receiver transaction date | `receiver_transaction_date` | `.../ReceiverTransactionDate` |
| BT-134 | Line Invoicing Period Start | `invoicing_period_start` | `.../LineItemPeriod/StartDate` |
| BT-135 | Line Invoicing Period End | `invoicing_period_end` | `.../LineItemPeriod/EndDate` |
| BT-151 | Tax type code (per line) | `taxes_attributes[].category` | `.../TaxesOutputs/Tax/TaxTypeCode` |
| BT-152 | Tax percent (per line) | `taxes_attributes[].percent` | `.../TaxesOutputs/Tax/TaxRate` |
| BT-120 | Tax exemption/non-subject reason | `taxes_attributes[].comment` | `.../SpecialTaxableEvent/SpecialTaxableEventReason` |
| — | Tax exemption/non-subject code | (derived: `'02'` if category `NS`, else `'01'`) | `.../SpecialTaxableEvent/SpecialTaxableEventCode` |
| BT-136 | Line Allowance Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", amount: X}` | `.../DiscountsAndRebates/Discount/DiscountAmount` |
| BT-138 | Line Allowance Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", percentage: X}` | `.../DiscountsAndRebates/Discount/DiscountRate` |
| BT-139 | Line Allowance Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", description: "..."}` | `.../DiscountsAndRebates/Discount/DiscountReason` |
| BT-141 | Line Charge Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", amount: X}` | `.../Charges/Charge/ChargeAmount` |
| BT-144 | Line Charge Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", description: "..."}` | `.../Charges/Charge/ChargeReason` |
Notes:
* Several line-level fields are packed into `AdditionalLineItemInformation`: `article_code2`, `classification_code`, `article_code_buyer`, `contact_reference`.
## Additional Information Fields
[Section titled “Additional Information Fields”](#additional-information-fields)
Several fields that have dedicated XML elements in UBL/CII are packed into `InvoiceAdditionalInformation` in Facturae. These are appended to `invoice.extra_info`:
| BT | API Field | Prefix in AdditionalInformation |
| ----- | --------------------------------------------------------------- | ----------------------------------- |
| BT-14 | `invoice.sales_order_reference` | ”Sales order reference:“ |
| BT-15 | `invoice.receiving_advice_reference` | ”Receiving advice reference:“ |
| BT-20 | `invoice.payment_terms` | ”Payment terms:“ |
| BT-83 | `invoice.remittance_information` | ”Remittance information:“ |
| BT-90 | `invoice.bank_assigned_creditor_reference` | ”Bank assigned creditor reference:“ |
| BT-89 | `invoice.mandate_reference_identifier` | ”Mandate reference identifier:“ |
| BT-19 | `invoice.buyer_accounting_reference` | ”Buyer accounting reference:“ |
| BT-72 | `invoice.delivery_date` (when `tax_point_date` is also present) | “Delivery date:“ |
## Factoring Assignment Data
[Section titled “Factoring Assignment Data”](#factoring-assignment-data)
For invoice factoring (cession of credit), the following fields populate the `FactoringAssignmentData` block:
| Field | API Field | Facturae XPath |
| -------------------- | --------------------------- | -------------------------------------------------------------------------------------- |
| Assignee tax ID | `invoice.fa_taxcode` | `FactoringAssignmentData/Assignee/TaxIdentification/TaxIdentificationNumber` |
| Assignee name | `invoice.fa_name` | `FactoringAssignmentData/Assignee/LegalEntity/CorporateName` |
| Assignee address | `invoice.fa_address` | `FactoringAssignmentData/Assignee/LegalEntity/AddressInSpain/Address` |
| Assignee postal code | `invoice.fa_postcode` | `FactoringAssignmentData/Assignee/LegalEntity/AddressInSpain/PostCode` |
| Assignee city | `invoice.fa_town` | `FactoringAssignmentData/Assignee/LegalEntity/AddressInSpain/Town` |
| Assignee province | `invoice.fa_province` | `FactoringAssignmentData/Assignee/LegalEntity/AddressInSpain/Province` |
| Assignee country | `invoice.fa_country` | `FactoringAssignmentData/Assignee/LegalEntity/AddressInSpain/CountryCode` |
| Payment due date | `invoice.fa_duedate` | `FactoringAssignmentData/PaymentDetails/Installment/InstallmentDueDate` |
| Payment amount | `invoice.fa_import` | `FactoringAssignmentData/PaymentDetails/Installment/InstallmentAmount` |
| Payment method | `invoice.fa_payment_method` | `FactoringAssignmentData/PaymentDetails/Installment/PaymentMeans` |
| Assignee IBAN | `invoice.fa_iban` | `FactoringAssignmentData/PaymentDetails/Installment/AccountToBeCredited/IBAN` |
| Assignee BIC | `invoice.fa_bic` | `FactoringAssignmentData/PaymentDetails/Installment/AccountToBeCredited/BIC` |
| Factoring clauses | `invoice.fa_clauses` | `FactoringAssignmentData/FactoringAssignmentClauses` |
| Additional info | `invoice.fa_info` | `FactoringAssignmentData/Assignee/LegalEntity/ContactDetails/AdditionalContactDetails` |
| Bank Code | `invoice.fa_bank_code` | `FactoringAssignmentData/PaymentDetails/Installment/AccountToBeCredited/BankCode` |
# JSON Invoice Fields to KSeF FA(3) XML
> Mapping of B2Brouter JSON invoice fields to KSeF FA(3) XML format for the Polish e-invoicing system.
Available from version 2025-10-13
This guide details the mapping between the **B2Brouter Invoice API fields**, the corresponding **Business Terms (BT)** as defined by the European e-Invoicing standard (EN 16931), and the **XML paths** in the **KSeF FA(3)** format (Polish National e-Invoice System).
Its purpose is to help understand how invoice data submitted in JSON format via the Invoice API corresponds to the KSeF FA(3) XML structure. Only fields that are actually mapped during tax report generation are included.
Note
KSeF uses a Poland-specific XML schema (FA variant 3, schema version 1-0E) which differs significantly from EN 16931 formats like UBL or CII. Not all EN 16931 Business Terms have a direct equivalent in KSeF, and KSeF introduces Poland-specific fields not present in the European standard.
Note
The seller/supplier information (Podmiot1) is automatically populated from your account settings. The buyer/customer information (Podmiot2) is populated from the invoice contact. If you need direct control over all fields, consider using the [Tax Report API](https://developer.b2brouter.net/reference/ksef) instead.
## Header-Level Mapping
[Section titled “Header-Level Mapping”](#header-level-mapping)
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **KSeF FA(3) XPath** |
| --------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| BT-1 | Invoice Number | `invoice.number` | `Fa/P_2` |
| BT-2 | Invoice Issue Date | `invoice.date` | `Fa/P_1` |
| BT-3 | Invoice Type Code | `invoice.type_document` Derived: defaults to `VAT`; `KOR` if amended invoice; `ZAL` if advance; `UPR` if simplified | `Fa/RodzajFaktury` |
| BT-5 | Invoice Currency | `invoice.currency` | `Fa/KodWaluty` |
| BT-7 | Tax Point Date | `invoice.tax_point_date` | `Fa/P_6` |
| BT-9 | Payment Due Date | `invoice.due_date` | `Fa/Platnosc/TerminPlatnosci/Termin` |
| BT-25 | Preceding Invoice Number | `invoice.invoice_references[reference_type=amend].number` (v2026-04-20+) or `invoice.amended_number` (older versions) | `Fa/DaneFaKorygowanej/NrFaKorygowanej` |
| BT-26 | Preceding Invoice Issue Date | `invoice.invoice_references[reference_type=amend].date` (v2026-04-20+) or `invoice.amended_date` (older versions) | `Fa/DaneFaKorygowanej/DataWystFaKorygowanej` |
| — | Exchange Rate | `invoice.exchange_rate` | `Fa/KursWalutyZ` |
| BT-10 | Buyer Reference | `invoice.buyer_reference` | `Podmiot2/NrKlienta` |
| BT-13 | Purchase Order Reference | `invoice.ponumber` | `Fa/WarunkiTransakcji/Zamowienia/NrZamowienia` |
| BT-92 | Purchase Order Date | `invoice.order_date` | `Fa/WarunkiTransakcji/Zamowienia/DataZamowienia` |
| BT-14 | Sales Order Reference | `invoice.sales_order_reference` | `Fa/DodatkowyOpis` (key-value pair) |
| BT-16 | Despatch Advice Reference | `invoice.delivery_note_number` | `Fa/WZ` |
| — | Correction Effect Code | `invoice.amend_code_tax` or `tax_report_setting.credit_note_code` | `Fa/TypKorekty` (only for KOR types) |
## Payment-Level Mapping
[Section titled “Payment-Level Mapping”](#payment-level-mapping)
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **KSeF FA(3) XPath** |
| --------- | ------------------------------ | ------------------------------------------------------ | ---------------------------------------- |
| BT-9 | Payment Due Date | `invoice.due_date` | `Fa/Platnosc/TerminPlatnosci/Termin` |
| BT-81 | Payment Means Code | `invoice.payment_method` (mapped to KSeF codes) | `Fa/Platnosc/FormaPlatnosci` |
| BT-84 | Payee Financial Account IBAN | `invoice.bank_account.iban` | `Fa/Platnosc/RachunekBankowy/NrRB` |
| BT-84-0 | Payee Financial Account Number | `invoice.bank_account.number` (fallback if no IBAN) | `Fa/Platnosc/RachunekBankowy/NrRB` |
| BT-85 | Payee Financial Account Name | `invoice.bank_account.name` | `Fa/Platnosc/RachunekBankowy/NazwaBanku` |
| BT-86 | Payee Financial Account BIC | `invoice.bank_account.bic` | `Fa/Platnosc/RachunekBankowy/SWIFT` |
| — | Paid Flag | Derived: `1` when `payable_amount = 0` and `total > 0` | `Fa/Platnosc/Zaplacono` |
| — | Payment Date (when paid) | `invoice.due_date` (when Zaplacono=1) | `Fa/Platnosc/DataZaplaty` |
> **Note:** KSeF does not support card account details (BT-87, BT-88), direct debit mandates (BT-89), creditor references (BT-90), payment terms text (BT-20), or remittance information (BT-83).
## Parties Mapping
[Section titled “Parties Mapping”](#parties-mapping)
This mapping is described for an issued invoice. If you want to map a received invoice, swap Podmiot1 and Podmiot2.
### Podmiot1 (Seller/Supplier)
[Section titled “Podmiot1 (Seller/Supplier)”](#podmiot1-sellersupplier)
The `Podmiot1` is automatically populated based on your account settings. This includes company name, NIP (Polish tax ID), and address.
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **KSeF FA(3) XPath** |
| --------- | -------------------------------- | -------------------------------------------- | ------------------------------------ |
| — | Seller Country Prefix | `account.country` (uppercased) | `Podmiot1/PrefiksPodatnika` |
| BT-31 | Seller Tax Registration ID (NIP) | `account.tin_value` (without country prefix) | `Podmiot1/DaneIdentyfikacyjne/NIP` |
| BT-27 | Seller Party Name | `account.name` | `Podmiot1/DaneIdentyfikacyjne/Nazwa` |
| BT-40 | Seller Address Country | `account.country` (uppercased) | `Podmiot1/Adres/KodKraju` |
| BT-35 | Seller Address Street | `account.address` | `Podmiot1/Adres/AdresL1` |
| BT-37,38 | Seller Postal Code + City | `account.postalcode` + `account.city` | `Podmiot1/Adres/AdresL2` |
| BT-29-0 | Seller GLN | `account.cin_value` (if `cin_scheme = 88`) | `Podmiot1/Adres/GLN` |
| BT-42 | Seller Contact Telephone | `account.phone` | `Podmiot1/DaneKontaktowe/Telefon` |
| BT-43 | Seller Contact Email | `account.email` | `Podmiot1/DaneKontaktowe/Email` |
| — | Seller REGON | `account.routing_codes.cinX` (scheme `8037`) | `Stopka/Rejestry/REGON` |
| — | Seller KRS | `account.routing_codes.cinX` (scheme `8036`) | `Stopka/Rejestry/KRS` |
| — | Seller BDO | `account.routing_codes.cinX` (scheme `8038`) | `Stopka/Rejestry/BDO` |
### Podmiot2 (Buyer/Customer)
[Section titled “Podmiot2 (Buyer/Customer)”](#podmiot2-buyercustomer)
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **KSeF FA(3) XPath** |
| --------- | -------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| BT-48 | Buyer Tax Registration ID | `invoice.contact.tin_value` | PL: `Podmiot2/DaneIdentyfikacyjne/NIP` EU: `Podmiot2/DaneIdentyfikacyjne/NrVatUE` Non-EU: `Podmiot2/DaneIdentyfikacyjne/NrID` |
| — | Buyer Tax Country (EU) | `invoice.contact.country` (uppercased) | `Podmiot2/DaneIdentyfikacyjne/KodUE` (EU only) |
| — | Buyer Tax Country (Non-EU) | `invoice.contact.country` (uppercased) | `Podmiot2/DaneIdentyfikacyjne/KodKraju` (non-EU only) |
| — | No Tax ID Flag | Derived: `1` when `contact.tin_value` is blank | `Podmiot2/DaneIdentyfikacyjne/BrakID` |
| BT-44 | Buyer Party Name | `invoice.contact.name` | `Podmiot2/DaneIdentyfikacyjne/Nazwa` |
| BT-55 | Buyer Address Country | `invoice.contact.country` (uppercased) | `Podmiot2/Adres/KodKraju` |
| BT-50 | Buyer Address Street | `invoice.contact.address` | `Podmiot2/Adres/AdresL1` |
| BT-52,53 | Buyer Postal Code + City | `invoice.contact.postalcode` + `invoice.contact.city` | `Podmiot2/Adres/AdresL2` |
| BT-46-0 | Buyer GLN | `invoice.contact.cin_value` (if `cin_scheme = 88`) | `Podmiot2/Adres/GLN` |
## Third Party Mapping (Podmiot3)
[Section titled “Third Party Mapping (Podmiot3)”](#third-party-mapping-podmiot3)
KSeF supports multiple third-party roles via the `Podmiot3` element. A `Podmiot3` is generated when delivery information is present **or** when the invoice has a `third_party` object (available from v2026-04-20).
### Role Codes
[Section titled “Role Codes”](#role-codes)
| Role Code | B2Brouter `third_party.role` | Description |
| --------- | --------------------------------- | ---------------------------------------- |
| 2 | `delivery_recipient` | Delivery recipient |
| 3 | `original_supplier` | Original entity (taken over/transformed) |
| 4 | `additional_buyer` | Additional purchaser |
| 5 | `invoice_issuer` | Invoice issuer on behalf of taxpayer |
| 6 | `payer` | Payer on behalf of purchaser |
| 7 | `local_government_unit_issuer` | Local government unit — issuer |
| 8 | `local_government_unit_recipient` | Local government unit — recipient (JST) |
| 9 | `vat_group_member_issuer` | VAT group member — issuer |
| 10 | `vat_group_member_recipient` | VAT group member — recipient (GV) |
| 11 | `employee` | Employee |
### Delivery (Role 2)
[Section titled “Delivery (Role 2)”](#delivery-role-2)
Delivery information is mapped to `Podmiot3` with role code `2` when `invoice.delivery_address` is present. The delivery address is also duplicated under `Fa/WarunkiTransakcji/Transport/WysylkaDo`.
| **BT ID** | **Business Term** | **API Field (B2Brouter)** | **KSeF FA(3) XPath** |
| --------- | --------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------- |
| BT-70 | Delivery Party Name | `invoice.delivery_party_name` | `Podmiot3/DaneIdentyfikacyjne/Nazwa` |
| BT-71 | Delivery Location ID (GLN) | `invoice.delivery_location_id` (if type is GLN/088) | `Podmiot3/Adres/GLN` |
| BT-75 | Delivery Address Street | `invoice.delivery_address` | `Podmiot3/Adres/AdresL1` and `Fa/WarunkiTransakcji/Transport/WysylkaDo/AdresL1` |
| BT-77,78 | Delivery Postal Code + City | `invoice.delivery_postalcode` + `invoice.delivery_city` | `Podmiot3/Adres/AdresL2` and `Fa/WarunkiTransakcji/Transport/WysylkaDo/AdresL2` |
| BT-80 | Delivery Country | `invoice.delivery_country` | `Podmiot3/Adres/KodKraju` and `Fa/WarunkiTransakcji/Transport/WysylkaDo/KodKraju` |
| — | Third Party Role | Fixed: `2` (Recipient) | `Podmiot3/Rola` |
> **Note:** KSeF does not support delivery date (BT-72) or delivery period fields.
### JST / GV / Other Roles (Roles 3–11) — v2026-04-20+
[Section titled “JST / GV / Other Roles (Roles 3–11) — v2026-04-20+”](#jst--gv--other-roles-roles-311--v2026-04-20)
Changed in version 2026-04-20
Third-party support beyond delivery recipient.
| **Business Term** | **API Field (B2Brouter)** | **KSeF FA(3) XPath** |
| ------------------------------ | ------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Third Party Name | `invoice.third_party.name` | `Podmiot3/DaneIdentyfikacyjne/Nazwa` |
| Third Party Tax ID | `invoice.third_party.tin_value` | `Podmiot3/DaneIdentyfikacyjne/NIP` (PL), `NrVatUE` (EU), or `NrID` (non-EU) |
| Third Party Address | `invoice.third_party.address` | `Podmiot3/Adres/AdresL1` |
| Third Party Postal Code + City | `invoice.third_party.postalcode` + `invoice.third_party.city` | `Podmiot3/Adres/AdresL2` |
| Third Party Country | `invoice.third_party.country` | `Podmiot3/Adres/KodKraju` |
| Third Party Role | `invoice.third_party.role` → role code | `Podmiot3/Rola` |
| JST flag | `invoice.apply_to_local_government_unit` | `Podmiot2/JST` (`1` when true, `2` when false) |
| GV flag | `invoice.apply_to_vat_group_member` | `Podmiot2/GV` (`1` when true, `2` when false) |
## Invoice Line-Level Mapping (FaWiersz)
[Section titled “Invoice Line-Level Mapping (FaWiersz)”](#invoice-line-level-mapping-fawiersz)
All attributes listed in the following table are properties of objects contained in the array: `invoice.invoice_lines_attributes[]`. The `API Field` column refers to keys within each invoice line object.
For regular invoices (VAT, KOR, UPR, ROZ), lines are rendered as `FaWiersz` elements. For advance payment invoices (ZAL, KOR\_ZAL), lines are rendered as `ZamowienieWiersz` elements.
| **BT ID** | **Business Term** | **API Field (invoice line object)** | **KSeF FA(3) XPath** |
| --------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------- |
| BT-126 | Invoice Line ID | `position` | `Fa/FaWiersz/NrWierszaFa` |
| BT-153 | Item Name | `description` | `Fa/FaWiersz/P_7` |
| BT-130 | Billed Quantity (Unit Code) | `unit` (check API Reference for code list) | `Fa/FaWiersz/P_8A` |
| BT-129 | Billed Quantity | `quantity` | `Fa/FaWiersz/P_8B` |
| BT-155 | Item Seller Identifier | `article_code` | `Fa/FaWiersz/Indeks` |
| BT-157 | Item Standard Identifier (GTIN) | `article_code2` | `Fa/FaWiersz/GTIN` |
| BT-146 | Price Amount | `price` | `Fa/FaWiersz/P_9A` |
| - | Discount Unit Price | Derived from line `allowance_charges_attributes[]{allowance_charge_indicator: "allowance"}` amount / `quantity` | `Fa/FaWiersz/P_10` |
| BT-131 | Line Net Amount | `extension_amount` (if not provided, it will be calculated) | `Fa/FaWiersz/P_11` |
| — | Line Tax Amount | Computed from taxes | `Fa/FaWiersz/P_11Vat` |
| — | Tax Code | Derived from tax characteristics | `Fa/FaWiersz/P_12` |
### Additional Item Properties Mapping (DodatkowyOpis per Line)
[Section titled “Additional Item Properties Mapping (DodatkowyOpis per Line)”](#additional-item-properties-mapping-dodatkowyopis-per-line)
Each invoice line can carry additional key-value properties that map to line-level `DodatkowyOpis` elements in the KSeF XML.
All attributes listed in the following table are properties of objects contained in the array: `invoice.invoice_lines_attributes[].additional_item_properties_attributes[]`.
| **BT ID** | **Business Term** | **API Field (additional item property object)** | **KSeF FA(3) XPath** |
| --------- | --------------------- | ----------------------------------------------- | ---------------------------- |
| — | Line Number Reference | Parent line’s `position` | `Fa/DodatkowyOpis/NrWiersza` |
| BT-160 | Item Attribute Name | `name` (max 255 chars) | `Fa/DodatkowyOpis/Klucz` |
| BT-161 | Item Attribute Value | `value` (max 255 chars) | `Fa/DodatkowyOpis/Wartosc` |
**Example JSON:**
```json
{
"invoice": {
"invoice_lines_attributes": [
{
"position": 1,
"description": "Product A",
"additional_item_properties_attributes": [
{ "name": "Color", "value": "Red" },
{ "name": "Size", "value": "XL" }
]
}
]
}
}
```
**Resulting KSeF XML:**
```xml
1
Color
Red
1
Size
XL
```
#### Special Property: `unit_of_measure`
[Section titled “Special Property: unit\_of\_measure”](#special-property-unit_of_measure)
Setting an additional item property with name `unit_of_measure` allows you to specify a free-text unit code for the line’s `P_8A` field in KSeF, overriding the standard unit codelist.
```json
{
"additional_item_properties_attributes": [
{ "name": "unit_of_measure", "value": "szt." }
]
}
```
This property is consumed directly as the line’s unit code (`Fa/FaWiersz/P_8A`) and is **not** emitted as a `DodatkowyOpis` element in the XML output.
### Taxes Mapping
[Section titled “Taxes Mapping”](#taxes-mapping)
All attributes listed in the following table are properties of objects contained in the array: `invoice.invoice_lines_attributes[].taxes_attributes[]`.
| **BT ID** | **Business Term** | **API Field (tax object)** | **KSeF FA(3) Mapping** |
| --------- | --------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| BT-118 | Tax Category Code | `category` (check our code list) | Used to derive `P_12` tax code |
| BT-119 | Tax Rate Applicable Percent | `percent` | Used to derive `P_12` tax code and `P_13_x`/`P_14_x` aggregations |
| BT-120 | Tax Exemption Reason | `comment` | `Fa/Adnotacje/Zwolnienie/P_19A`. Also accepts a KSeF P\_12 code (`zw`, `np I`, `np II`, `0 KR`, `0 WDT`, `0 EX`, `oo`) to override the derived value. |
## Tax Breakdown Aggregation (P\_13\_x / P\_14\_x)
[Section titled “Tax Breakdown Aggregation (P\_13\_x / P\_14\_x)”](#tax-breakdown-aggregation-p_13_x--p_14_x)
KSeF requires tax amounts to be aggregated by rate at the header level. These fields are automatically computed from the invoice’s tax breakdowns:
| **KSeF Field** | **Description** | **Tax Rate / Category** |
| -------------- | ---------------------------------------------------- | ------------------------------------------- |
| `P_13_1` | Taxable base at basic rate | 23% or 22% |
| `P_14_1` | Tax amount at basic rate | 23% or 22% |
| `P_14_1W` | Tax amount at basic rate converted to PLN | 23% or 22% (foreign currency invoices only) |
| `P_13_2` | Taxable base at first reduced rate | 8% or 7% |
| `P_14_2` | Tax amount at first reduced rate | 8% or 7% |
| `P_14_2W` | Tax amount at first reduced rate converted to PLN | 8% or 7% (foreign currency invoices only) |
| `P_13_3` | Taxable base at second reduced rate | 5% |
| `P_14_3` | Tax amount at second reduced rate | 5% |
| `P_14_3W` | Tax amount at second reduced rate converted to PLN | 5% (foreign currency invoices only) |
| `P_13_6_1` | Total value of domestic zero-rated sales | 0% domestic (excluding intra-EU and export) |
| `P_13_6_2` | Total value of intra-Community supply of goods at 0% | 0% intra-EU goods |
| `P_13_6_3` | Total value of exports at 0% | 0% export |
| `P_13_7` | Total value of exempt sales | Exempt (category E, domestic scope) |
| `P_13_8` | Total value of cross-border reverse charge (np I) | Category AE, non-domestic scope |
| `P_13_10` | Total value of domestic reverse charge | Category AE, domestic scope |
| `P_15` | Total amount due (inclusive of all taxes) | Sum of all categories |
Note
Fields `P_14_xW` (tax amounts converted to PLN) are only included for invoices in foreign currencies. The conversion uses the `exchange_rate` provided in the invoice.
# JSON Invoice Fields to UBL XML (EN 16931 / Peppol BIS 3.0)
> Mapping of B2Brouter JSON invoice fields to UBL 2.1 XML paths (EN 16931 / Peppol BIS 3.0).
This guide details the mapping between the **B2Brouter Invoice API fields**, the corresponding **Business Terms (BT)** as defined by the European e-Invoicing standard (EN 16931), and the **XML paths** in the **UBL 2.1** model (including Peppol BIS Billing 3.0).
## Header-Level Mapping
[Section titled “Header-Level Mapping”](#header-level-mapping)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------ | ------------------ | ------------------------------------ | --------------------------------------------------- |
| BT-1 | Invoice number | `invoice.number` | `cbc:ID` |
| BT-2 | Invoice Issue Date | `invoice.date` | `cbc:IssueDate` |
| BT-3 | Invoice Type Code | `invoice.type_code` (api 2026-03-02) | `cbc:InvoiceTypeCode` (or `cbc:CreditNoteTypeCode`) |
| BT-5 | Document Currency | `invoice.currency` | `cbc:DocumentCurrencyCode` |
| BT-6 | Tax Currency Code | `invoice.contact.currency` | `cbc:TaxCurrencyCode` |
| BT-7 | Tax Point Date | `invoice.tax_point_date` | `cbc:TaxPointDate` |
| BT-9 | Payment Due Date | `invoice.due_date` | `cbc:DueDate` |
| BT-10 | Buyer Reference | `invoice.buyer_reference` | `cbc:BuyerReference` |
| BT-19 | Accounting Cost | `invoice.buyer_accounting_reference` | `cbc:AccountingCost` |
| BT-22 | Note (Header) | `invoice.extra_info` | `cbc:Note` |
Notes:
* BT-6: Emitted only when the buyer contact’s currency differs from the invoice currency (BT-5) and tax amounts in that currency are present. When set, a second `cac:TaxTotal` block is rendered with amounts in this currency.
## References & Periods
[Section titled “References & Periods”](#references--periods)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------ | -------------------------- | ------------------------------------ | ----------------------------------------------------------------- |
| BT-13 | Purchase Order Reference | `invoice.ponumber` | `cac:OrderReference/cbc:ID` |
| BT-14 | Sales Order Reference | `invoice.sales_order_reference` | `cac:OrderReference/cbc:SalesOrderID` |
| BT-12 | Contract Reference | `invoice.contract_number` | `cac:ContractDocumentReference/cbc:ID` |
| BT-15 | Receiving Advice Reference | `invoice.receiving_advice_reference` | `cac:ReceiptDocumentReference/cbc:ID` |
| BT-16 | Despatch Advice Reference | `invoice.delivery_note_number` | `cac:DespatchDocumentReference/cbc:ID` |
| BT-25 | Preceding Invoice Ref | `invoice.amended_number` | `cac:BillingReference/cac:InvoiceDocumentReference/cbc:ID` |
| BT-26 | Preceding Invoice Date | `invoice.amended_date` | `cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate` |
| BT-73 | Invoicing Period Start | `invoice.invoicing_period_start` | `cac:InvoicePeriod/cbc:StartDate` |
| BT-74 | Invoicing Period End | `invoice.invoicing_period_end` | `cac:InvoicePeriod/cbc:EndDate` |
| BT-11 | Project Reference | `invoice.file_reference` | `cac:ProjectReference/cbc:ID` |
| — | Originator Document Ref | `invoice.lot_reference` | `cac:OriginatorDocumentReference/cbc:ID` |
Notes:
* For credit notes, `file_reference` (BT-11) is emitted as `cac:AdditionalDocumentReference/cbc:ID` instead of `cac:ProjectReference/cbc:ID`.
## Parties Mapping
[Section titled “Parties Mapping”](#parties-mapping)
This mapping is described for an issued invoice; if you want to map received invoice swap the AccountingSupplierParty and AccountingCustomerParty.
> **Self-billing (`IssuedSelfInvoice` / `ReceivedSelfInvoice`)**: in self-billing the buyer issues the invoice on behalf of the supplier, so the party roles in the XML are inverted relative to the API fields. For an `IssuedSelfInvoice`, `account` data appears in `AccountingCustomerParty` (your company is the buyer) and `invoice.contact` data appears in `AccountingSupplierParty` (the contact is the supplier). The same applies symmetrically to `ReceivedSelfInvoice`.
### Seller Party
[Section titled “Seller Party”](#seller-party)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------ | -------------------------- | ------------------------- | -------------------------------------------------------------------------------------------- |
| BT-34 | Seller Endpoint ID | `account.pin_value` | `cac:AccountingSupplierParty/cac:Party/cbc:EndpointID @schemeID` |
| BT-27 | Seller Legal Name | `account.name` | `cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName` |
| BT-31 | Seller VAT Identifier | `account.tin_value` | `cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID` |
| BT-35 | Seller Address Street | `account.address` | `cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName` |
| BT-36 | Seller Address Street 2 | `account.address2` | `cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:AdditionalStreetName` |
| BT-37 | Seller Address City | `account.city` | `cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:CityName` |
| BT-38 | Seller Address Postal Code | `account.postalcode` | `cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:PostalZone` |
| BT-39 | Seller Province | `account.province` | `cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:CountrySubentity` |
| BT-40 | Seller Country | `account.country` | `cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cac:Country/cbc:IdentificationCode` |
| BT-41 | Seller Contact Name | `account.contact_person` | `cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Name` |
| BT-42 | Seller Contact Phone | `account.phone` | `cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Telephone` |
| BT-43 | Seller Contact Email | `account.email` | `cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail` |
## Buyer Party
[Section titled “Buyer Party”](#buyer-party)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------ | ------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------- |
| BT-49 | Buyer Endpoint ID | `invoice.contact.pin_value` | `cac:AccountingCustomerParty/cac:Party/cbc:EndpointID @schemeID` |
| BT-44 | Buyer Legal Name | `invoice.contact.name` | `cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName` |
| BT-45 | Buyer Trade Name | `invoice.contact.name` | `cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name` |
| BT-48 | Buyer VAT Identifier | `invoice.contact.tin_value` | `cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID` |
| BT-50 | Buyer Address Street | `invoice.contact.address` | `cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName` |
| BT-51 | Buyer Address Street 2 | `invoice.contact.address2` | `cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:AdditionalStreetName` |
| BT-52 | Buyer Address City | `invoice.contact.city` | `cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:CityName` |
| BT-53 | Buyer Address Postal Code | `invoice.contact.postalcode` | `cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:PostalZone` |
| BT-54 | Buyer Province | `invoice.contact.province` | `cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:CountrySubentity` |
| BT-55 | Buyer Country | `invoice.contact.country` | `cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cac:Country/cbc:IdentificationCode` |
| BT-56 | Buyer Contact Name | `invoice.contact.contact_person` | `cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:Name` |
| BT-57 | Buyer Contact Phone | `invoice.contact.phone` | `cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:Telephone` |
| BT-58 | Buyer Contact Email | `invoice.contact.email` | `cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail` |
## Delivery
[Section titled “Delivery”](#delivery)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------ | ------------------------- | ------------------------------ | ---------------------------------------------------------------------------------- |
| BT-72 | Delivery Date | `invoice.delivery_date` | `cac:Delivery/cbc:ActualDeliveryDate` |
| BT-71 | Delivery Location ID | `invoice.delivery_location_id` | `cac:Delivery/cac:DeliveryLocation/cbc:ID` |
| BT-75 | Delivery Address Street | `invoice.delivery_address` | `cac:Delivery/cac:DeliveryLocation/cac:Address/cbc:StreetName` |
| BT-76 | Delivery Address Street 2 | `invoice.delivery_address2` | `cac:Delivery/cac:DeliveryLocation/cac:Address/cbc:AdditionalStreetName` |
| BT-77 | Delivery City | `invoice.delivery_city` | `cac:Delivery/cac:DeliveryLocation/cac:Address/cbc:CityName` |
| BT-78 | Delivery Postal Code | `invoice.delivery_postalcode` | `cac:Delivery/cac:DeliveryLocation/cac:Address/cbc:PostalZone` |
| BT-79 | Delivery Province | `invoice.delivery_province` | `cac:Delivery/cac:DeliveryLocation/cac:Address/cbc:CountrySubentity` |
| BT-80 | Delivery Country | `invoice.delivery_country` | `cac:Delivery/cac:DeliveryLocation/cac:Address/cac:Country/cbc:IdentificationCode` |
| BT-70 | Delivery Party Name | `invoice.delivery_party_name` | `cac:Delivery/cac:DeliveryParty/cac:PartyName/cbc:Name` |
## Payment
[Section titled “Payment”](#payment)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------- | -------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------- |
| BT-81 | Payment Means Code | `invoice.payment_method` | `cac:PaymentMeans/cbc:PaymentMeansCode @listID='UNCL4461'` |
| BT-82 | Payment Means Text | `invoice.payment_method_text` | `cac:PaymentMeans/cbc:PaymentMeansCode @name` |
| BT-83 | Remittance Information | `invoice.remittance_information` | `cac:PaymentMeans/cbc:PaymentID` |
| BT-84 | Payee Bank Account IBAN | `invoice.bank_account.iban` | `cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID` |
| BT-84-0 | Payee Bank Account Number | `invoice.bank_account.number` | `cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID` (fallback when no IBAN) |
| BT-85 | Payee Bank Account Name | `invoice.bank_account.name` | `cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:Name` (currently not rendered) |
| BT-86 | Payee Bank Account BIC | `invoice.bank_account.bic` | `cac:PaymentMeans/cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID` |
| BT-87 | Card Account Number | `invoice.card_account_attributes.account_number` | `cac:PaymentMeans/cac:CardAccount/cbc:PrimaryAccountNumberID` |
| BT-87-0 | Card Account Holder Name | `invoice.card_account_attributes.holder_name` | `cac:PaymentMeans/cac:CardAccount/cbc:HolderName` |
| BT-88 | Card Account Network ID | `invoice.card_account_attributes.network` | `cac:PaymentMeans/cac:CardAccount/cbc:NetworkID` |
| BT-20 | Payment Terms | `invoice.payment_terms` | `cac:PaymentTerms/cbc:Note` |
| BT-89 | Mandate Reference (SEPA DD only) | `invoice.mandate_reference_identifier` | `cac:PaymentMeans/cac:PaymentMandate/cbc:ID` |
| BT-90 | Creditor Reference (SEPA DD) | `invoice.bank_assigned_creditor_reference` | `cac:AccountingSupplierParty/cac:Party/cac:PartyIdentification/cbc:ID @schemeID='SEPA'` |
| BT-91 | Debtor Account IBAN (SEPA DD) | `invoice.contact_iban` | `cac:PaymentMeans/cac:PaymentMandate/cac:PayerFinancialAccount/cbc:ID` |
## Totals & Taxes
[Section titled “Totals & Taxes”](#totals--taxes)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------ | --------------------------- | --------------------------------- | -------------------------------------------------- |
| BT-106 | Line Net Amount Total | *(calculated)* | `cac:LegalMonetaryTotal/cbc:LineExtensionAmount` |
| BT-109 | Tax Exclusive Amount | *(calculated)* | `cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount` |
| BT-107 | Allowance Total Amount | *(calculated)* | `cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount` |
| BT-108 | Charge Total Amount | *(calculated)* | `cac:LegalMonetaryTotal/cbc:ChargeTotalAmount` |
| BT-112 | Tax Inclusive Amount | *(calculated)* | `cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount` |
| BT-113 | Total Prepaid Amount | `invoice.payments_on_account` | `cac:LegalMonetaryTotal/cbc:PrepaidAmount` |
| BT-114 | Rounding Amount | *(from invoice adjustment)* | `cac:LegalMonetaryTotal/cbc:PayableRoundingAmount` |
| BT-115 | Amount Due for Payment | `invoice.payable_amount` | `cac:LegalMonetaryTotal/cbc:PayableAmount` |
| BT-110 | Invoice Total VAT Amount | *(calculated)* | `cac:TaxTotal/cbc:TaxAmount` |
| BT-116 | VAT Category Taxable Amount | *(calculated from lines)* | `cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount` |
| BT-117 | VAT Category Tax Amount | *(calculated from lines)* | `cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount` |
| — | Withheld Amount | `invoice.withheld_percent` | `cac:AllowanceCharge/cbc:Amount` |
| — | Withheld Reason | `invoice.amounts_withheld_reason` | `cac:AllowanceCharge/cbc:AllowanceChargeReason` |
## Header Allowances & Charges
[Section titled “Header Allowances & Charges”](#header-allowances--charges)
| **BT** | **Business Term** | **API Field (B2Brouter)** | **UBL XPath** |
| ------ | ----------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| BT-92 | Allowance Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", amount: X}` | `cac:AllowanceCharge[cbc:ChargeIndicator=false]/cbc:Amount` |
| BT-93 | Allowance Base Amount | (calculated when percent-based) | `cac:AllowanceCharge[cbc:ChargeIndicator=false]/cbc:BaseAmount` |
| BT-94 | Allowance Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", percentage: X}` | `cac:AllowanceCharge[cbc:ChargeIndicator=false]/cbc:MultiplierFactorNumeric` |
| BT-97 | Allowance Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", description: "..."}` | `cac:AllowanceCharge[cbc:ChargeIndicator=false]/cbc:AllowanceChargeReason` |
| BT-99 | Charge Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", amount: X}` | `cac:AllowanceCharge[cbc:ChargeIndicator=true]/cbc:Amount` |
| BT-100 | Charge Base Amount | (calculated when percent-based) | `cac:AllowanceCharge[cbc:ChargeIndicator=true]/cbc:BaseAmount` |
| BT-101 | Charge Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", percentage: X}` | `cac:AllowanceCharge[cbc:ChargeIndicator=true]/cbc:MultiplierFactorNumeric` |
| BT-104 | Charge Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", description: "..."}` | `cac:AllowanceCharge[cbc:ChargeIndicator=true]/cbc:AllowanceChargeReason` |
| BT-102 | Apply Tax To Allowance/Charge | `allowance_charges_attributes[]{apply_taxes: true}` | `cac:AllowanceCharge/cac:TaxCategory` |
## Invoice Line-Level Mapping
[Section titled “Invoice Line-Level Mapping”](#invoice-line-level-mapping)
All attributes below are properties of objects in: `invoice.invoice_lines_attributes[]`.
| **BT** | **Business Term** | **API Field (invoice line)** | **UBL XPath** |
| -------- | ------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| BT-126 | Invoice Line ID | `position` | `cac:InvoiceLine/cbc:ID` |
| BT-129 | Invoiced Quantity | `quantity` | `cac:InvoiceLine/cbc:InvoicedQuantity` |
| BT-130 | Invoiced Quantity (Unit) | `unit` | `cac:InvoiceLine/cbc:InvoicedQuantity @unitCode` |
| BT-131 | Line Extension Amount | `extension_amount` (or calculated) | `cac:InvoiceLine/cbc:LineExtensionAmount` |
| BT-146 | Price Amount | `price` | `cac:InvoiceLine/cac:Price/cbc:PriceAmount` |
| BT-153 | Item Name | `description` | `cac:InvoiceLine/cac:Item/cbc:Name` |
| BT-154 | Item Description | `notes` | `cac:InvoiceLine/cac:Item/cbc:Description` |
| BT-155 | Seller Item ID | `article_code` | `cac:InvoiceLine/cac:Item/cac:SellersItemIdentification/cbc:ID` |
| BT-156 | Buyer Item ID | `article_code_buyer` | `cac:InvoiceLine/cac:Item/cac:BuyersItemIdentification/cbc:ID` |
| BT-157 | Standard Item ID | `article_code2` | `cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID @schemeID` |
| BT-157-1 | Standard Item ID Scheme | `article_code2_scheme` | `cac:InvoiceLine/cac:Item/cac:StandardItemIdentification/cbc:ID @schemeID` (defaults to `0160`) |
| BT-158 | Classification Code | `classification_code` | `cac:InvoiceLine/cac:Item/cac:CommodityClassification/cbc:ItemClassificationCode @listID` |
| BT-158-1 | Classification Code Scheme | `classification_code_scheme` | `cac:InvoiceLine/cac:Item/cac:CommodityClassification/cbc:ItemClassificationCode @listID` |
| BT-151 | VAT Category (per line) | `taxes_attributes[].category` | `cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:ID` |
| BT-152 | VAT Percent (per line) | `taxes_attributes[].percent` | `cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:Percent` |
| BT-120 | Tax Exemption Reason | `taxes_attributes[].comment` | `cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:TaxExemptionReason` |
| BT-160 | Additional Item Property Name | `additional_item_properties_attributes[].name` | `cac:InvoiceLine/cac:Item/cac:AdditionalItemProperty/cbc:Name` |
| BT-161 | Additional Item Property Value | `additional_item_properties_attributes[].value` | `cac:InvoiceLine/cac:Item/cac:AdditionalItemProperty/cbc:Value` |
| BT-133 | Buyer Accounting Reference | `contact_reference` | `cac:InvoiceLine/cbc:AccountingCost` |
| BT-134 | Invoicing Line Period Start | `invoicing_period_start` | `cac:InvoiceLine/cac:InvoicePeriod/cbc:StartDate` |
| BT-135 | Invoicing Line Period End | `invoicing_period_end` | `cac:InvoiceLine/cac:InvoicePeriod/cbc:EndDate` |
| BT-136 | Line Allowance Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", amount: X}` | `cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator=false]/cbc:Amount` |
| BT-138 | Line Allowance Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", percentage: X}` | `cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator=false]/cbc:MultiplierFactorNumeric` |
| BT-139 | Line Allowance Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "allowance", description: "..."}` | `cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator=false]/cbc:AllowanceChargeReason` |
| BT-141 | Line Charge Amount | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", amount: X}` | `cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator=true]/cbc:Amount` |
| BT-143 | Line Charge Percent | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", percentage: X}` | `cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator=true]/cbc:MultiplierFactorNumeric` |
| BT-144 | Line Charge Reason | `allowance_charges_attributes[]{allowance_charge_indicator: "charge", description: "..."}` | `cac:InvoiceLine/cac:AllowanceCharge[cbc:ChargeIndicator=true]/cbc:AllowanceChargeReason` |
| BT-128 | Purchase Order Reference (line) | `invoice_lines_attributes[].ponumber` | `cac:InvoiceLine/cac:DocumentReference/cbc:ID` |
| BT-132 | PO Line Reference | `invoice_lines_attributes[].sequence_number` | `cac:InvoiceLine/cac:OrderLineReference/cbc:LineID` |
# Peppol and CII Document Type Codes
> Reference of document type codes used in Peppol BIS 3.0 and CII/Factur-X formats.
## Overview
[Section titled “Overview”](#overview)
When sending invoices through PEPPOL networks or generating CII/Factur-X format documents, you can specify the document type using the `type_document` field. This field corresponds to the Invoice Type Code as defined in the UN/CEFACT code list 1001 (UNCL1001).
The codes are used in:
* **PEPPOL BIS Billing 3.0/Chorus** (UBL format): `InvoiceTypeCode` element
* **Cross Industry Invoice (CII)** (Factur-X, ZUGFeRD, EN16931): `TypeCode` element
## Usage
[Section titled “Usage”](#usage)
The `type_document` field is optional and acts as a hint for the document type code. B2Brouter will use this value when it’s valid for the export format, otherwise it falls back to automatic assignment based on business logic:
* **380**: Standard commercial invoice (default)
* **381**: Credit note
* **389**: Self-billed invoice
* **480**: Not-Taxable / Reverse charge invoice
**Important**: When exporting documents:
* If `type_document` is valid for the target format (PEPPOL, CII, or FatturaPA), it will be used in the exported XML
* If `type_document` is not valid for the target format, the system automatically uses an appropriate default code based on invoice type
* This allows you to import documents in one format (e.g., FatturaPA) and export them in another (e.g., PEPPOL) seamlessly
### Setting type\_document via API
[Section titled “Setting type\_document via API”](#setting-type_document-via-api)
```json
{
"invoice": {
"type_document": "380",
"number": "INV-2025-001",
"date": "2025-01-26",
"contact_id": 12345,
"invoice_lines_attributes": [...]
}
}
```
## Valid PEPPOL/UBL Document Type Codes
[Section titled “Valid PEPPOL/UBL Document Type Codes”](#valid-peppolubl-document-type-codes)
The following UN/CEFACT codes will be used when exporting to PEPPOL and UBL format invoices:
### Standard Invoice Codes
[Section titled “Standard Invoice Codes”](#standard-invoice-codes)
| Code | Description |
| ------- | ------------------------------------------------- |
| **71** | Request for payment |
| **80** | Debit note related to goods or services |
| **82** | Metered services invoice |
| **84** | Debit note related to financial adjustments |
| **102** | Tax notification |
| **218** | Final payment request based on completion of work |
| **219** | Payment request for completed units |
| **326** | Partial invoice |
| **331** | Commercial invoice which includes a packing list |
| **380** | Commercial invoice |
| **382** | Commission note |
| **383** | Debit note |
| **384** | Corrected invoice |
| **386** | Prepayment invoice |
| **388** | Tax invoice |
| **393** | Factored invoice |
| **395** | Consignment invoice |
| **553** | Forwarder’s invoice discrepancy report |
| **575** | Insurer’s invoice |
| **623** | Forwarder’s invoice |
| **780** | Freight invoice |
| **817** | Claim notification |
| **870** | Consular invoice |
| **875** | Partial construction invoice |
| **876** | Partial final construction invoice |
| **877** | Final construction invoice |
**Note**: Codes 326 and 384 can only be used when both parties (supplier and customer) have country ‘DE’.
### Credit Note Codes (for PEPPOL)
[Section titled “Credit Note Codes (for PEPPOL)”](#credit-note-codes-for-peppol)
These codes are valid only when the invoice is a credit note (`is_credit_note: true`):
| Code | Description |
| ------- | -------------------------------------------- |
| **81** | Credit note related to goods or services |
| **83** | Credit note related to financial adjustments |
| **381** | Credit note |
| **396** | Factored credit note |
| **532** | Forwarder’s credit note |
### Self-Billing Invoice Codes (for PEPPOL)
[Section titled “Self-Billing Invoice Codes (for PEPPOL)”](#self-billing-invoice-codes-for-peppol)
| Code | Description |
| ------- | ---------------------- |
| **389** | Self-billed invoice |
| **527** | Self-billed debit note |
### Self-Billing Credit Note Code (for PEPPOL)
[Section titled “Self-Billing Credit Note Code (for PEPPOL)”](#self-billing-credit-note-code-for-peppol)
| Code | Description |
| ------- | ----------------------- |
| **261** | Self-billed credit note |
**Note**: This code can only be used when `type: IssuedSelfInvoice` and `is_credit_note: true`
## Valid CII/Factur-X Document Type Codes
[Section titled “Valid CII/Factur-X Document Type Codes”](#valid-ciifactur-x-document-type-codes)
The following codes will be used when exporting to Cross Industry Invoice (CII) format documents:
### Standard Invoice Codes
[Section titled “Standard Invoice Codes”](#standard-invoice-codes-1)
| Code | Description |
| ------- | ---------------------------------- |
| **326** | Partial invoice |
| **380** | Commercial invoice |
| **384** | Corrected invoice |
| **875** | Partial construction invoice |
| **876** | Partial final construction invoice |
| **877** | Final construction invoice |
### Credit Note Code (for CII)
[Section titled “Credit Note Code (for CII)”](#credit-note-code-for-cii)
| Code | Description |
| ------- | ----------- |
| **381** | Credit note |
### Self-Billing Code (for CII)
[Section titled “Self-Billing Code (for CII)”](#self-billing-code-for-cii)
| Code | Description |
| ------- | ------------------- |
| **389** | Self-billed invoice |
**Note**: Codes 326 and 384 can only be used when both parties (supplier and customer) have country ‘DE’.
## Examples
[Section titled “Examples”](#examples)
### Example 1: Standard PEPPOL Invoice
[Section titled “Example 1: Standard PEPPOL Invoice”](#example-1-standard-peppol-invoice)
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_document": "380",
"number": "INV-2025-001",
"date": "2025-01-26",
"due_date": "2025-02-26",
"contact_id": 12345,
"invoice_lines_attributes": [
{
"quantity": 10,
"price": 50.0,
"description": "Professional services",
"taxes_attributes": [
{
"name": "VAT",
"percent": 21
}
]
}
]
}
}
```
### Example 2: PEPPOL Credit Note
[Section titled “Example 2: PEPPOL Credit Note”](#example-2-peppol-credit-note)
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_document": "381",
"is_credit_note": true,
"number": "CN-2025-001",
"date": "2025-01-26",
"amended_number": "INV-2025-001",
"contact_id": 12345,
"invoice_lines_attributes": [...]
}
}
```
### Example 3: Prepayment Invoice
[Section titled “Example 3: Prepayment Invoice”](#example-3-prepayment-invoice)
```json
{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"type_document": "386",
"number": "PREP-2025-001",
"date": "2025-01-26",
"contact_id": 12345,
"invoice_lines_attributes": [...]
}
}
```
### Example 4: Self-Billed Invoice
[Section titled “Example 4: Self-Billed Invoice”](#example-4-self-billed-invoice)
```json
{
"send_after_import": true,
"invoice": {
"type": "ReceivedInvoice",
"type_document": "389",
"number": "SELF-2025-001",
"date": "2025-01-26",
"contact_id": 12345,
"invoice_lines_attributes": [...]
}
}
```
## XML Output
[Section titled “XML Output”](#xml-output)
### PEPPOL BIS 3.0 (UBL)
[Section titled “PEPPOL BIS 3.0 (UBL)”](#peppol-bis-30-ubl)
```xml
380
...
```
### CII (Factur-X/EN16931)
[Section titled “CII (Factur-X/EN16931)”](#cii-factur-xen16931)
```xml
380
...
```
## Standards References
[Section titled “Standards References”](#standards-references)
* [PEPPOL BIS Billing 3.0](https://docs.peppol.eu/poacc/billing/3.0/) - Invoice Type Code
* [EN 16931](https://ec.europa.eu/digital-building-blocks/wikis/display/DIGITAL/Compliance+with+eInvoicing+standard) - European e-invoicing standard
# Get started
> Steps to configure accounts and start sending and receiving invoices with the B2Brouter API.
## Configure the tax data for the accounts \[REQUIRED]
[Section titled “Configure the tax data for the accounts \[REQUIRED\]”](#configure-the-tax-data-for-the-accounts-required)
From the app (gear icon) or via API through the `/accounts` endpoint. Create as many accounts as you need in sandbox for testing.
* **eDocExchange**: create accounts for your companies in the **App** (manual linking in the “Developers” section). **If you are a reseller, you can automate eDocExchange provisioning** to offer a more complete service to your end customers.
* **eDocSync**: create user accounts **via API** (**POST /accounts**).
```bash
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"account": {
"country": "{COUNTRY_CODE}",
"tin_value": "{YOUR_TAX_IDENTIFICATION_NUMBER}",
"tin_scheme": "{YOUR_TAX_IDENTIFICATION_SCHEME}",
"name": "{ENTERPRISE_NAME}",
"address": "{YOUR_STREET}",
"city": "{YOUR_CITY}",
"postalcode": "00000",
"email": "{MAIN_EMAIL}"
}
}'
```
> When you create an account via API, **read the `id` from the response** and persist it in your environment for future requests for that company.
* **Accounts Guide**:
* **Setting Up Your Company**:
* **Schemes Guide**:
* **API Reference**:
## Activate transports \[Recommended: b2brouter, mail & peppol] \[Optional] \[REQUIRED Peppol BE cases]
[Section titled “Activate transports \[Recommended: b2brouter, mail & peppol\] \[Optional\] \[REQUIRED Peppol BE cases\]”](#activate-transports-recommended-b2brouter-mail--peppol-optional-required-peppol-be-cases)
```bash
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/transports \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"transport": {
"code": "peppol",
"enabled": true,
"reception": true,
"standard_documents": true,
"invoice": true,
"credit_note": true,
"application_response": true,
"pin_scheme": "{YOUR_IDENTIFICATION_SCHEME}",
"pin_value": "{YOUR_IDENTIFICATION_NUMBER}"
}
}'
```
* **Transports Guide**:
* **API Reference**:
## Activate Tax Report \[REQUIRED ES & IT cases]
[Section titled “Activate Tax Report \[REQUIRED ES & IT cases\]”](#activate-tax-report-required-es--it-cases)
```bash
curl --request POST \
--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 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"tax_report_setting": {
"code": "verifactu",
"auto_generate": true,
"auto_send": true,
"enabled": true,
"reason_vat_exempt": "E1",
"special_regime_key": "01",
"special_regime_key_igic": "01",
"reason_no_subject": "N1",
"credit_note_code": "R1"
}
}'
```
* **Tax Reports Guide**:
* **API Reference**:
## List accounts to retrieve the `ACCOUNT_ID` \[Optional]
[Section titled “List accounts to retrieve the ACCOUNT\_ID \[Optional\]”](#list-accounts-to-retrieve-the-account_id-optional)
If you don’t already have the IDs for your integration group, this is the fastest way to list them. You can also copy an account’s ID from the **App → Developers** tab.
```bash
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts?offset=0&limit=25' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
* **API Reference**:
***
Next step: follow the end-to-end sending flow guide to create contacts, create/import invoices, send them, and retrieve/download the final documents.
## Lookup Contacts \[Optional]
[Section titled “Lookup Contacts \[Optional\]”](#lookup-contacts-optional)
If found, you can jump straight to step **4)** by retrieving the `id`.
```bash
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts?offset=0&limit=25&name=B2Brouter%20Global%20S.L.' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
* **API Reference**:
## Lookup in the directory \[Optional]
[Section titled “Lookup in the directory \[Optional\]”](#lookup-in-the-directory-optional)
If it exists, you can use the data to create your contact.
```bash
curl --request GET \
--url https://api-staging.b2brouter.net/directory/es/ESB63276174 \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
* **Directory Guide**:
* **API Reference**:
## Create contact \[Optional]
[Section titled “Create contact \[Optional\]”](#create-contact-optional)
```bash
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"contact": {
"language": "en",
"is_client": true,
"is_provider": true,
"public_sector": false,
"name": "B2Brouter Global S.L.",
"tin_value": "ESB63276174",
"tin_scheme": "9920",
"country": "es",
"email": "john.doe@example.com",
"address": "Avda. Diagonal, 433 1º1ª",
"postalcode": "08036",
"province": "Barcelona",
"city": "Barcelona",
"transport_type_code": "b2brouter",
"document_type_code": "xml.facturae.3.2.2",
"payment_method": 1
}
}'
```
If you are unsure which `tin_scheme`, `transport_type_code` or `document_type_code` to use, remember to use the **Code Lists** endpoints.
* **Schemes Guide**:
* **API Reference**:
* **API Reference Transport Types**:
* **API Reference Document Types**:
* **API Reference Schemes**:
## Create and send a simple invoice \[REQUIRED]
[Section titled “Create and send a simple invoice \[REQUIRED\]”](#create-and-send-a-simple-invoice-required)
Remember that if you don’t want to manage contacts in B2Brouter, Instead of using `contact_id`, you can **embed a full `contact` object inside the `invoice`** in the same `POST /invoices` request; in that case, provide the contact details on every invoice. If your system already generates a recognized e-invoice XML, the **simplest** integration is to import it directly. However, if you serve customers in multiple countries, **JSON is usually more portable** because some formats have country-specific fields. Use `GET /document_types` to check available document types.
```bash
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": {
"type": "IssuedInvoice",
"bank_account": {
"type": "iban"
},
"terms": "custom",
"invoice_lines_attributes": [
{
"unit": 1,
"quantity": 1,
"description": "Example Service",
"price": 10,
"taxes_attributes": [
{
"name": "VAT",
"category": "S",
"percent": 21
}
]
}
],
"apply_taxes_to_charge": false,
"charge_is_reimbursable_expense": false,
"number": "123456",
"contact_id": {CONTACT.ID},
"date": "2025-09-24",
"due_date": "2025-10-24"
}
}'
```
* **Send Invoices via JSON (Peppol example)**:
* **Invoice Calculation Guide**:
* **Payment Methods Guide**:
* **API Reference**:
* **API Reference Import XML**:
## Retrieve data of the issued invoice \[OPTIONAL]
[Section titled “Retrieve data of the issued invoice \[OPTIONAL\]”](#retrieve-data-of-the-issued-invoice-optional)
Once sent, you can retrieve the `invoice.id` from the response and perform a `GET` with all the information. If you have **Tax Report** enabled (ES/IT), you can also retrieve the `tax_report_id` to see all the details. Remember that you can configure **Webhooks** for state changes.
### List invoices
[Section titled “List invoices”](#list-invoices)
To fetch the most recent invoices as JSON:
```bash
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices?offset=0&limit=25' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### Get an invoice
[Section titled “Get an invoice”](#get-an-invoice)
To retrieve a specific invoice as JSON (most fields are already returned in the creation response):
```bash
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}?include=lines' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### Get an invoice with a specific document type
[Section titled “Get an invoice with a specific document type”](#get-an-invoice-with-a-specific-document-type)
If you need the rendered electronic invoice in a specific format:
```bash
curl --request GET \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/as/{document_type_code} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
By performing a GET on the “download\_legal\_url” provided in the invoice payload, you can download the legal document delivered to the end user (for re-sending or archiving on your system). Alternatively, call `GET /invoices/{id}/as/legal` directly to get the same archived legal document without first fetching `download_legal_url` from the invoice payload; this does not generate a billable transaction — see [Download invoices](/en/developers/essential-guides/download-invoice/) and [Transaction: View\_as](/en/developers/essential-guides/transaction/#view_as).
### Get a Tax Report \[ES & IT CASES]
[Section titled “Get a Tax Report \[ES & IT CASES\]”](#get-a-tax-report-es--it-cases)
Fetch all the details of a declaration, including the link and QR:
```bash
curl --request GET \
--url https://api-staging.b2brouter.net/tax_reports/{TAX_REPORT_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
***
* **Send invoices (end-to-end)**:
## Move to production
[Section titled “Move to production”](#move-to-production)
1. **Register** (if you haven’t yet) at
2. Once logged in, go to **Subscription** and subscribe to **eDocExchange** monthly according to the approximate volume of transactions.
3. Minimal technical changes to migrate:
* **From sandbox**: swap the `test_` key for a production key. The base URL (`https://api.b2brouter.net`) stays the same.
* **From staging**: change the domain from `api-staging.b2brouter.net` to `api.b2brouter.net` and use the production API key.
* Update the **`{ACCOUNT_ID}`** (if they differ between environments).
* **The payload and functional behavior are the same** across environments.
**You don’t need to have the whole integration finished**: you can go to production with the **basic flow** and you will still have **sandbox** (and staging, if needed) available to continue developing with the support of B2Brouter technicians.
***
## Best practices
[Section titled “Best practices”](#best-practices)
* If you manage **multiple companies** and they need access to B2Brouter, separate them into **groups** for privacy. (User management and ownership from the account settings in the app).
* **Do not send real invoices from sandbox or staging** via email or b2brouter to real recipients: create **test** accounts and contacts (you can do internal sends to review reception with a test email).
* Avoid using **real NIF/Tax IDs** of third parties you won’t integrate.
* **Simplify the process**: aim for a clean, global flow; avoid unnecessary local rules if the transport and format already solve them.
* **Prioritize JSON**. Although B2Brouter can convert to the vast majority of global formats, working with our base format lets you invoice globally in all kinds of formats without legal issues.
***
## Recommended guides to get started
[Section titled “Recommended guides to get started”](#recommended-guides-to-get-started)
* **Accounts (create/list/update)**:
* **Directory (lookup, schemes, public sector routes like DIR3/DIRe)**:
* **Transports (email, Peppol, b2brouter, FACe, SFTP…)**:
* **Tax report settings (TicketBAI, SDI)**:
* **Schemes / Identifiers (TIN, CIN, PIN, EAS, …)**:
* **Payments (standard methods/values, e.g., Bank transfer, Direct debit …)**:
* **Country-specific behaviors** (TicketBAI, SDI, VERI\*FACTU …):
* **Italy SDI (FatturaPA 1.3.2, fields and rules)**:
* **VERI\*FACTU (AEAT, requirements and FAQ)**:
* **TicketBAI (concepts and obligations)**:
## User’s Manual
[Section titled “User’s Manual”](#users-manual)
For users who need to use the Web Application, our platform documentation is available here:
## Let’s start!
[Section titled “Let’s start!”](#lets-start)
Remember that throughout the whole process we will be happy to help you through Support.
Good luck with your integration process!
# Introduction
> Introduction to the B2Brouter API environments, authentication, and getting started.
## Environments
[Section titled “Environments”](#environments)
B2Brouter has three environments — see [Environments](/en/developers/testing/enviroments/) for a full comparison.
| Environment | Base URL | When to use |
| -------------- | ------------------------------------------------ | --------------------------------------------------------------------- |
| **Sandbox** | `https://api.b2brouter.net` (key prefix `test_`) | Daily development, payload validation, onboarding |
| **Staging** | `https://api-staging.b2brouter.net` | Large integrations, broad end-to-end tests, authority QA environments |
| **Production** | `https://api.b2brouter.net` | Live traffic |
> **Start with [Sandbox](/en/developers/testing/sandbox/).** It is the recommended environment for initial development and day-to-day testing. Use staging only for large-scale integrations or broad end-to-end tests that require a full environment setup.
v2025-01-01
Use `app.b2brouter.net` instead of `api.b2brouter.net`.
## First steps
[Section titled “First steps”](#first-steps)
### Register and configure your account
[Section titled “Register and configure your account”](#register-and-configure-your-account)
To start using the B2Brouter API, register at [app.b2brouter.net/en/register](https://app.b2brouter.net/en/register). Detailed setup instructions are available [here](https://www.b2brouter.net/docs/#/en/README?id=sign-up).
Once registered, log in to B2Brouter and set up your account with a Basic plan. You can find detailed instructions [here](https://www.b2brouter.net/docs/#/en/set-up/setting-up-your-company).
### Get a sandbox API key
[Section titled “Get a sandbox API key”](#get-a-sandbox-api-key)
Open the **Developers** section and create a sandbox. Your sandbox API key starts with `test_` and routes all requests to the sandbox environment automatically — no separate URL needed. See [Sandbox](/en/developers/testing/sandbox/) for full setup instructions.
### Staging Environment Activation
[Section titled “Staging Environment Activation”](#staging-environment-activation)
If you need the staging environment (large integrations, broad end-to-end tests), register at [app-staging.b2brouter.net/en/register](https://app-staging.b2brouter.net/en/register). Staging accounts start with limited permissions — **submit a support ticket** to request the necessary access. Our team will grant it.
## Using the REST API
[Section titled “Using the REST API”](#using-the-rest-api)
To access the B2Brouter API, follow these steps:
1. Access your account.
2. Within your account, click on the **Developers tab**.
3. Click on the API key to copy it to the clipboard.
4. To find the accounts IDs, click on **View IDs for each account** or click on the **Edit group icon** next to your group name.
5. Access to the [**API Reference**](https://developer.b2brouter.net/reference).
**Note:** API keys are environment-specific. Sandbox keys (prefix `test_`) only work in sandbox; production keys only work in production; staging keys only work in staging. Obtain each key by logging in to the corresponding environment.
By default, the API Reference is documented with the staging environment. To switch to sandbox or production, open the **Base URL** dropdown.
### Authentication
[Section titled “Authentication”](#authentication)
The credentials must be provided in the request headers, with the name **X-B2B-API-Key**.
### API Version
[Section titled “API Version”](#api-version)
Available from version 2025-10-13
In B2Brouter each group has a default API Version. You can check it in the **Developers tab**, under **API Keys**. You can manually specify the **API Version** in the request headers, with the name **X-B2B-API-Version**. If no version is specified, B2Brouter will use the default version of the group.
### Response Formats
[Section titled “Response Formats”](#response-formats)
B2Brouter only provides the response in **JSON** format.
### Account Identifiers
[Section titled “Account Identifiers”](#account-identifiers)
Use the **[List Accounts endpoint](https://developer.b2brouter.net/reference/get-accounts)**
```bash
curl --request GET \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
In this response, pay attention to the information in the **identifier** field. You should use this ID when you want to interact with the account through the API.
**Note:** You can also find the accounts IDs in the Developers tab by clicking the **Edit group icon** next to your group name. The list of accounts and their IDs will be displayed.
### API Rate Limiting
[Section titled “API Rate Limiting”](#api-rate-limiting)
To ensure optimal performance and fair usage, B2Brouter API enforces rate limits on incoming requests:
**Rate Limits:**
* **Production:** 1,000 requests per minute
* **Sandbox / Staging:** 600 requests per minute
**Rate Limit Exceeded:** When you exceed the rate limit, the API returns:
* HTTP status code `429 Too Many Requests`
**Best Practices:**
* Implement exponential backoff when receiving 429 responses
* Cache responses when possible to reduce API calls
* Use webhooks instead of polling for real-time updates
## SDKs and client libraries
[Section titled “SDKs and client libraries”](#sdks-and-client-libraries)
Available from version 2025-10-13
You can integrate with the API directly over HTTP or by using an official SDK. The first official SDK is the **PHP SDK**:
* GitHub:
It provides methods for complete invoice management, multi-format documents downloads, complete tax reports operations (including Verifactu), pagination utilities, and error handling. Additional SDKs for other programming languages will be added over time.
## Download the OpenAPI Reference
[Section titled “Download the OpenAPI Reference”](#download-the-openapi-reference)
You can download the full REST API reference in OpenAPI (YAML) format from the following links:
* [v2026-06-26](https://app.b2brouter.net/api/v20260626/bundled/openapi.yaml)
* [v2026-04-20](https://app.b2brouter.net/api/v20260420/bundled/openapi.yaml)
* [v2026-03-02](https://app.b2brouter.net/api/v20260302/bundled/openapi.yaml)
* [v2025-10-13](https://app.b2brouter.net/api/v20251013/bundled/openapi.yaml)
* [v2025-01-01](https://app.b2brouter.net/api/v20250101/bundled/openapi.yaml)
# SDKs
> Official PHP SDK to integrate electronic invoicing and tax reporting into your application.
The B2Brouter PHP SDK lets you integrate electronic invoicing and tax reporting into your PHP application without dealing with raw HTTP requests.
* **Zero dependencies** — PHP 7.4+ with standard extensions (cURL, JSON, mbstring)
* **Automatic retries** — network failures handled with exponential backoff
* **Pagination support** — Iterator and Countable for large result sets
* **Full tax report support** — Verifactu, TicketBAI, SDI, KSeF, ZATCA, and more
## Installation
[Section titled “Installation”](#installation)
```bash
composer require b2brouter/b2brouter-php
```
## Quick start
[Section titled “Quick start”](#quick-start)
```php
invoices->create($accountId, [
'invoice' => [
'number' => 'INV-2025-001',
'date' => '2025-01-15',
'due_date' => '2025-02-15',
'currency' => 'EUR',
'contact' => [
'name' => 'Acme Corporation',
'tin_value' => 'ESB12345678',
'country' => 'ES',
'email' => 'billing@acme.com',
],
'invoice_lines_attributes' => [
[
'description' => 'Professional Services',
'quantity' => 10,
'price' => 100.00,
'taxes_attributes' => [
['name' => 'IVA', 'category' => 'S', 'percent' => 21.0]
]
]
]
]
]);
echo "Invoice created: {$invoice['id']}\n";
```
## Configuration
[Section titled “Configuration”](#configuration)
```php
$client = new B2BRouterClient('your-api-key', [
// 'api_base' => 'https://api.b2brouter.net', // Sandbox & Production (default)
// 'api_base' => 'https://api-staging.b2brouter.net', // Staging
'api_version' => '2026-04-20',
'timeout' => 80,
'max_retries' => 3,
]);
```
The SDK defaults to `https://api.b2brouter.net`. Use a `test_` key for sandbox or a production key for production. Set `api_base` to `https://api-staging.b2brouter.net` only if you need the staging environment.
### Identifying your application
[Section titled “Identifying your application”](#identifying-your-application)
Integrators and plugins can append their identity to the `User-Agent` header:
```php
$client = new B2BRouterClient('your-api-key', [
'app_info' => [
'name' => 'MyApp', // required
'version' => '1.0.0', // optional
'url' => 'https://myapp.com', // optional
],
]);
```
## Invoices
[Section titled “Invoices”](#invoices)
### Create
[Section titled “Create”](#create)
```php
$invoice = $client->invoices->create($accountId, [
'invoice' => [
'number' => 'INV-2025-001',
'date' => '2025-01-15',
'currency' => 'EUR',
'contact' => [
'name' => 'Customer Name',
'tin_value' => 'ESB12345678',
'country' => 'ES',
],
'invoice_lines_attributes' => [
[
'description' => 'Service',
'quantity' => 1,
'price' => 1000.00,
'taxes_attributes' => [
['name' => 'IVA', 'category' => 'S', 'percent' => 21.0]
]
]
]
],
'send_after_import' => false,
]);
```
### Retrieve, update, delete
[Section titled “Retrieve, update, delete”](#retrieve-update-delete)
```php
$invoice = $client->invoices->retrieve($invoiceId);
$invoice = $client->invoices->update($invoiceId, [
'invoice' => ['extra_info' => 'Payment terms: 30 days net']
]);
$client->invoices->delete($invoiceId);
```
### List with pagination
[Section titled “List with pagination”](#list-with-pagination)
```php
$invoices = $client->invoices->all($accountId, [
'limit' => 25,
'offset' => 0,
'date_from' => '2025-01-01',
'date_to' => '2025-12-31',
]);
foreach ($invoices as $invoice) {
echo "{$invoice['number']}: €{$invoice['total']}\n";
}
echo "Total: {$invoices->getTotal()}, has more: " . ($invoices->hasMore() ? 'yes' : 'no') . "\n";
```
### Download
[Section titled “Download”](#download)
* PDF
```php
$pdf = $client->invoices->downloadPdf($invoiceId);
file_put_contents('invoice.pdf', $pdf);
```
* Facturae 3.2.2
```php
$xml = $client->invoices->downloadAs($invoiceId, 'xml.facturae.3.2.2');
```
* UBL BIS3
```php
$xml = $client->invoices->downloadAs($invoiceId, 'xml.ubl.invoice.bis3');
```
### Send and acknowledge
[Section titled “Send and acknowledge”](#send-and-acknowledge)
```php
$client->invoices->send($invoiceId);
$client->invoices->acknowledge($invoiceId, ['ack' => true]);
```
## Accounts and contacts
[Section titled “Accounts and contacts”](#accounts-and-contacts)
* Accounts
```php
$accounts = $client->accounts->all(['limit' => 25]);
$account = $client->accounts->retrieve($accountId);
$account = $client->accounts->create(['account' => [/* ... */]]);
$account = $client->accounts->update($accountId, ['account' => [/* ... */]]);
$client->accounts->delete($accountId);
$client->accounts->unarchive($accountId);
```
* Contacts
```php
$contacts = $client->contacts->all($accountId, ['limit' => 25]);
$contact = $client->contacts->create($accountId, ['contact' => [/* ... */]]);
$contact = $client->contacts->update($contactId, ['contact' => [/* ... */]]);
$client->contacts->delete($contactId);
```
## Tax reports
[Section titled “Tax reports”](#tax-reports)
Tax reports are generated automatically when you send invoices, provided you have configured `TaxReportSettings` for the account. See [Tax Report Settings](/en/developers/essential-guides/tax-report-settings/) for setup instructions.
### Configure settings
[Section titled “Configure settings”](#configure-settings)
```php
$client->taxReportSettings->create($accountId, [
'tax_report_setting' => [
'code' => 'verifactu',
'start_date' => '2025-01-01',
'auto_generate' => true,
'auto_send' => true,
]
]);
```
### Retrieve and download
[Section titled “Retrieve and download”](#retrieve-and-download)
```php
$taxReportId = $invoice['tax_report_ids'][0];
$taxReport = $client->taxReports->retrieve($taxReportId);
echo "State: {$taxReport['state']}\n";
if (!empty($taxReport['qr'])) {
file_put_contents('qr.png', base64_decode($taxReport['qr']));
}
$xml = $client->taxReports->download($taxReportId);
```
### Correction and annullation
[Section titled “Correction and annullation”](#correction-and-annullation)
* Correction
```php
$client->taxReports->update($taxReportId, [
'tax_report' => ['tax_inclusive_amount' => 133.1, /* ... */]
]);
```
* Annullation
```php
$client->taxReports->delete($taxReportId);
```
### States
[Section titled “States”](#states)
| State | Meaning |
| ------------------------ | ----------------------------------- |
| `processing` | Chaining and submission in progress |
| `registered` | Accepted by the tax authority |
| `registered_with_errors` | Submitted with warnings |
| `error` | Submission failed |
| `annulled` | Cancelled |
## Error handling
[Section titled “Error handling”](#error-handling)
```php
use B2BRouter\Exception\ApiErrorException;
use B2BRouter\Exception\AuthenticationException;
use B2BRouter\Exception\PermissionException;
use B2BRouter\Exception\ResourceNotFoundException;
use B2BRouter\Exception\InvalidRequestException;
use B2BRouter\Exception\ApiConnectionException;
try {
$invoice = $client->invoices->create($accountId, ['invoice' => [/* ... */]]);
} catch (AuthenticationException $e) {
// 401 — invalid API key
} catch (PermissionException $e) {
// 403 — insufficient permissions
} catch (ResourceNotFoundException $e) {
// 404
} catch (InvalidRequestException $e) {
// 400 / 422 — validation errors
$details = $e->getJsonBody();
} catch (ApiConnectionException $e) {
// network error
} catch (ApiErrorException $e) {
// any other API error
error_log("Request ID: {$e->getRequestId()}");
}
```
Always log the **Request ID** when reporting issues to support — it uniquely identifies the failed request.
# Environments
> Overview of B2Brouter's three environments and when to use each one.
B2Brouter has three environments. Choose based on what you are trying to do.
| Environment | Base URL | API key prefix | When to use |
| -------------- | ----------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------- |
| **Sandbox** | `https://api.b2brouter.net` | `test_…` | Daily development, payload validation, onboarding, small integrations |
| **Staging** | `https://api-staging.b2brouter.net` | `stag_…` | Large integrations, cross-system tests, high-volume simulations, full end-to-end flows |
| **Production** | `https://api.b2brouter.net` | `prod_…` (or no prefix for legacy) | Live traffic |
***
## Sandbox — start here
[Section titled “Sandbox — start here”](#sandbox--start-here)
Sandbox is the **recommended environment for initial development and day-to-day testing**. It is isolated, free to use, and available immediately — no setup ticket required.
Key properties:
* Uses the **same base URL as production** (`https://api.b2brouter.net`). Routing to sandbox is determined entirely by the API key: keys starting with `test_` go to sandbox; all others go to production.
* Outbound sends (email, PEPPOL, tax authority submissions) are **simulated** — documents advance through their lifecycle without contacting any real system.
* All eDocExchange features are available regardless of your real subscription.
* Usage does not count against your production transaction quota.
See [Sandbox](/en/developers/testing/sandbox/) for setup instructions and a full feature reference.
***
## Staging — for large or broad integrations
[Section titled “Staging — for large or broad integrations”](#staging--for-large-or-broad-integrations)
Staging is a **full-stack test environment** with its own domain (`api-staging.b2brouter.net`) and its own database. Use it when:
* You need to test a **large integration** with multiple accounts, high transaction volumes, or complex cross-system flows.
* You need a **persistent shared environment** across a development team.
* You are integrating with a **country-specific authority** whose own test endpoint (DGFiP QAS, KSeF test, ZATCA sandbox, etc.) connects to B2Brouter’s staging environment.
To get started with staging, [register on the staging app](https://app-staging.b2brouter.net/en/register) and then open a support ticket to request the necessary API permissions — staging accounts start with limited access.
> **Note.** For everything else — trying the API, validating payloads, writing integration tests, onboarding — use [Sandbox](/en/developers/testing/sandbox/) instead. Staging requires more setup and is better suited for broader test scenarios.
***
## Production
[Section titled “Production”](#production)
Live environment. All real transactions, billing, and PEPPOL registrations happen here. Do not use for development or testing.
# Sandbox
> Isolated test environment for developing and validating your integration with B2Brouter.
Sandbox is an isolated test environment that lets you try B2Brouter (issue invoices, send documents, test the API) without touching real data, real recipients, or your subscription’s transaction count.
## What is Sandbox
[Section titled “What is Sandbox”](#what-is-sandbox)
* A self-contained workspace with separate data (invoices, contacts, products, templates, API keys).
* Lets you run end-to-end tests before committing to a paid plan.
* Used to validate your API integration before deploying to production.
* Ideal for training, demos, and proof-of-concept work.
> **Simulated network behavior.** By default, sends in sandbox succeed and the document reaches its final state (equivalent to production). In addition, sandbox can **reproduce the full outbound lifecycle**, including rejections and network errors, when you send to one of the documented **test recipients** below. No document ever goes out over the real network: the whole cycle is simulated inside B2Brouter.
## Getting started
[Section titled “Getting started”](#getting-started)
### Create a Sandbox (web UI)
[Section titled “Create a Sandbox (web UI)”](#create-a-sandbox-web-ui)
1. Sign in to B2Brouter.
2. Click the **Developers** button (lightning-bolt icon).
3. Click **Create Sandbox**.
4. Give the sandbox a name.
5. Maximum 5 sandboxes per account / integration group.
### Enter and exit
[Section titled “Enter and exit”](#enter-and-exit)
* Click the open icon (↗) to enter sandbox mode.
* While you’re in it, a **purple banner** appears on every page.
* Click **Exit Sandbox** to return to production.
## Using the API in Sandbox
[Section titled “Using the API in Sandbox”](#using-the-api-in-sandbox)
API routing is **driven entirely by the key**:
* No separate sandbox URL or subdomain is needed.
* Use the standard B2Brouter API base: `https://api.b2brouter.net/`.
* Production keys start with `prod_` (or with no prefix, for compatibility).
* Sandbox keys start with `test_`.
Pass the sandbox key via the `X-B2B-API-Key` header or the `api_key` query parameter.
## Document lifecycle
[Section titled “Document lifecycle”](#document-lifecycle)
Documents follow the same lifecycle as in production. With a normal recipient, the send reaches:
`draft → sent → registered`
With a **test recipient** (see below), sandbox continues the cycle according to the associated scenario, **asynchronously** (transitions arrive within a few seconds), so you can test how your system handles each outcome:
* **Registered successfully:** `sent → registered`
* **Rejected:** `sent → registered → refused`
* **Network error (unregistered recipient):** `sent → error`, with `error_code = PEPPOL_NO_RECEIVER`
Since transitions are asynchronous, if you poll you may see intermediate states in quick succession; your code must tolerate receiving them back to back. Webhooks fire on every transition, same as in production.
## Simulating network responses (test recipients)
[Section titled “Simulating network responses (test recipients)”](#simulating-network-responses-test-recipients)
To test how your system reacts to a rejection or a network error, send to one of the predefined **test recipients**. They work like *test cards*: the outcome is determined by the recipient, not by any special parameter. Your sending code runs exactly as it would in production.
**Every new sandbox already includes these contacts, created automatically** (look for them in your contacts list), along with **an active PEPPOL channel**, so you can send without any prior setup.
| Test contact | Channel | Scheme | Identifier (XIN) | Simulated outcome |
| ------------------------- | ------- | ---------- | ---------------- | ------------------------------------- |
| Test PEPPOL — Registered | PEPPOL | GLN (0088) | `9508397101047` | `sent → registered` |
| Test PEPPOL — Refused | PEPPOL | GLN (0088) | `9506215594996` | `sent → registered → refused` |
| Test PEPPOL — No receiver | PEPPOL | GLN (0088) | `9500047420799` | `sent → error` (`PEPPOL_NO_RECEIVER`) |
You can also create your own contacts with one of these identifiers: sandbox recognizes the outcome by the **identifier**, not by the specific contact.
**Any other recipient** (not listed here) follows the default path and succeeds at `sent`/`registered`.
### Current scope of the simulation
[Section titled “Current scope of the simulation”](#current-scope-of-the-simulation)
The enriched lifecycle (registered/rejected/error) is available for **PEPPOL invoices**. Other document types and channels complete successfully (`sent`), and some cases only distinguish success/error. Not yet simulated: receiving inbound documents, order responses, or response artifacts from tax authorities (CSV/UPO). These will arrive in later phases.
## What is simulated (not real)
[Section titled “What is simulated (not real)”](#what-is-simulated-not-real)
* **No emails are sent** from sandbox.
* **No real PEPPOL traffic**, no document crosses the PEPPOL network, and the real SMP is **never queried**. Recipient registration and the network lifecycle are fully simulated inside B2Brouter.
* **No real submissions to tax authorities**, SII, TicketBAI, Verifactu, Chorus, and ZATCA are routed to the authority’s test endpoints or skipped.
* **No payments are processed.**
## Watermark on documents
[Section titled “Watermark on documents”](#watermark-on-documents)
Invoices and quotes downloaded from sandbox carry a clear watermark: “Not valid — Test invoice generated from the B2Brouter Sandbox”.
## Limitations
[Section titled “Limitations”](#limitations)
**Not available in Sandbox:**
* Real delivery of documents to PEPPOL, email, B2Bconnector, or SFTP.
* Real payment processing.
* Tax authority verification checks.
* Connections (Transport) tab disabled. The test PEPPOL channel already comes configured automatically: you don’t need to manage it to send to test recipients.
* B2Bconnector and SFTP channels.
* Account closure features.
**Other restrictions:**
* Profile changes (name, password, 2FA) must be made in production.
* API keys don’t cross environments (`test_` keys only work in sandbox).
* Production data can’t be cloned into sandbox with a single click.
## Webhooks
[Section titled “Webhooks”](#webhooks)
Webhooks **fire** from sandbox intentionally, since webhook delivery is one of the things integrators need to test. Sandbox and production webhooks are independent and configured separately.
## Key differences from production
[Section titled “Key differences from production”](#key-differences-from-production)
* Identity (user, password, 2FA) is shared between environments.
* All other data is isolated.
* Activity in sandbox does not consume subscription quota.
* All API-level features are available regardless of your contracted plan.
# Code list
> Reference for all B2Brouter API codes — document types, transport types, identifier schemes, tax categories, invoice states, currency codes, and unit codes.
This reference lists all standardized codes used across the B2Brouter API. Use the tabs to browse each category and the search box to quickly find a specific code.
# Invoice field mapper
> Interactive mapper from B2Brouter's JSON invoice API to UBL 2.1, CII, FatturaPA, Facturae and KSeF FA(3) XML paths, aligned with EN 16931 Business Terms (BT-1, BT-2…). Search, compare and copy field paths across formats.
Map any B2Brouter JSON invoice field to its equivalent XML path in international e-invoicing standards. Toggle the format columns you need, search by field name, Business Term code (BT-1, BT-2…) or XML path, and click any cell to copy the path to the clipboard.
Supported formats: **UBL 2.1**, **CII**, **Facturae 3.2.2**, **FatturaPA 1.2.3** and **KSeF FA(3)**. PEPPOL BIS 3.0 and other UBL profiles inherit the UBL paths shown here. The matrix grows as new formats and Business Terms are documented.
# Document & Format Validator
> Validate your electronic invoice files against supported formats before sending them through B2Brouter.
B2Brouter provides two ways to validate an electronic document: an **online tool** for manual checks, and an **API endpoint** for programmatic validation. Both apply the same validation rules.
## Online validator
[Section titled “Online validator”](#online-validator)
The online validator is a free web tool for checking documents manually, without writing any code.
Access it at [app.b2brouter.net/en/validation](https://app.b2brouter.net/en/validation).
### How to use it
[Section titled “How to use it”](#how-to-use-it)
1. Go to [app.b2brouter.net/en/validation](https://app.b2brouter.net/en/validation).
2. Upload your XML file.
3. Click **Validate**.
The tool returns a list of errors and warnings, each with the XPath location of the problematic element and a description of the rule that failed.
## API endpoint
[Section titled “API endpoint”](#api-endpoint)
The `/documents/validate` endpoint lets you validate documents programmatically. Use it to integrate validation into your application, CI pipeline, or automated workflow.
See the full API reference at [developer.b2brouter.net/reference/validate-document](https://developer.b2brouter.net/reference/validate-document).
Authentication is required. The endpoint accepts a document file and returns structured validation results.
## What gets validated
[Section titled “What gets validated”](#what-gets-validated)
Both the online tool and the API endpoint check:
* **Schema compliance** — whether the XML structure matches the format specification (e.g. UBL 2.1, CII D16B).
* **Business rules** — whether mandatory fields are present and values are within allowed ranges.
* **Schematron rules** — format-specific validation rules defined by the standard (e.g. Peppol BIS rules, EN 16931).
* **Model DB** — field-level checks against our database in relation to what the tax authority requires.
* **Tax report** — for invoices that generate a tax report, validation of the invoice values needed to produce a valid tax report.
Neither tool checks whether the receiver exists or whether the routing information is correct — that happens at sending time.
## Validation when creating a document via API
[Section titled “Validation when creating a document via API”](#validation-when-creating-a-document-via-api)
The same validation runs automatically when you submit a document through the API (e.g. `POST /documents`). If the document does not pass, the API returns a validation error and the document is rejected before it enters the system.
This means you do not need to call `/documents/validate` separately before every submission — but it is useful for debugging and pre-flight checks during development.
## Reading the results
[Section titled “Reading the results”](#reading-the-results)
| Result | Meaning |
| ----------- | ------------------------------------------------------------------------------------------------ |
| **Valid** | The document passes all schema and business rules for the selected format. |
| **Warning** | The document is technically valid but contains values that may cause issues with some receivers. |
| **Error** | The document does not conform to the format. It will be rejected if submitted. |
Fix all errors before using the document in a real integration. Warnings are informational but should be reviewed.
## Supported formats
[Section titled “Supported formats”](#supported-formats)
Both the online tool and the API support the main formats accepted by B2Brouter, including:
* Peppol BIS Billing 3.0 (UBL)
* EN 16931 (European standard)
* Factura-e (Spain)
* FatturaPA (Italy)
* UBL 2.1 Invoice / Credit Note
The full list of available formats is shown in the format selector on the validation page.
## Common use cases
[Section titled “Common use cases”](#common-use-cases)
* **Before going live** — validate sample documents from your ERP or billing system to catch integration issues early.
* **Debugging API errors** — if the API returns a format validation error, use the online tool to get a detailed breakdown.
* **Testing new formats** — when adding support for a new country or format, validate your output before running end-to-end tests.
* **Automated pre-flight checks** — call `/documents/validate` from your CI pipeline or integration layer before submitting documents to production.
# Migration guide v2025-01-01 → v2025-10-13
> Step-by-step guide to migrate your integration from API version v2025-01-01 to v2025-10-13.
This guide helps you upgrade from `X-B2B-API-Version: 2025-01-01` to `X-B2B-API-Version: 2025-10-13`, focusing on what you need to change, not just what changed.
[Changelog ](/en/developers/changelog/v2025-10-13/)For the full list of additions and fixes, see the v2025-10-13 changelog.
### Updating Your Integration
[Section titled “Updating Your Integration”](#updating-your-integration)
1. **Update Base URL**: Change your API base URL from `app.b2brouter.net` or `app-staging.b2brouter.net` to `api.b2brouter.net` or `api-staging.b2brouter.net` respectively.
2. **Add Version Header**: Include `X-B2B-API-Version: 2025-10-13` header in all requests, or configure the default version in your group's API Key settings.
3. **Remove Format Extensions**: Remove `.json` or `.xml` extensions from all endpoint paths. All responses are now JSON by default.
4. **Update Path Segments**:
* Replace `/projects/` with `/accounts/` in all paths
* Replace `/clients/` with `/contacts/` in all paths
* Remove `/users/` prefix from code list endpoints
* Remove `/api/v1/` prefix from directory and tax report endpoints
5. **Update Pagination Handling**: Update your code to read pagination data from the `meta` object instead of root-level properties:
* Old (v2025-01-01)
```javascript
const totalCount = response.total_count;
const offset = response.offset;
const limit = response.limit;
```
* New (v2025-10-13)
```javascript
const totalCount = response.meta.total_count;
const offset = response.meta.offset;
const limit = response.meta.limit;
```
6. **Update Property Names**: Replace all references to `client` with `contact` in your request and response handling code:
* Old (v2025-01-01)
```javascript
// Request body
{
"invoice": {
"client_id": 123,
"client": {
"name": "Client Name",
"taxcode": "9920:ESD29766391"
}
}
}
// Response body
{
"invoice": {
"id": 123,
"client": { ... }
}
}
```
* New (v2025-10-13)
```javascript
// Request body
{
"invoice": {
"contact_id": 123,
"contact": {
"name": "Contact Name",
"tin_scheme": "9920",
"tin_value": "ESD29766391"
}
}
}
// Response body
{
"invoice": {
"id": 123,
"contact": { ... }
}
}
```
7. **Consolidate Invoice Listings**: Replace separate invoice listing endpoints with the consolidated `/accounts/{account}/invoices` endpoint using the `type` parameter:
* For received invoices: `GET /accounts/{account}/invoices?type=ReceivedInvoice`
* For self-invoices: `GET /accounts/{account}/invoices?type=IssuedSelfInvoice`
* For simplified invoices: `GET /accounts/{account}/invoices?type=IssuedSimplifiedInvoice`
8. **Update Tax Reports**: Replace old deprecated tax report endpoints with the new consolidated endpoints under `/accounts/{account}/tax_reports` and `/tax_reports/{id}`. Check on [New Tax Reports API](https://developer.b2brouter.net/reference/get-tax-reports) for more details.
9. **Handle Full Responses**: Update your code to handle full resource representations returned by PUT, DELETE, and POST operations instead of empty `204` responses.
10. **Remove XML Support**: If your integration relied on XML responses, convert your code to handle JSON responses only.
11. **Update Deprecated Invoice Attributes**: Replace deprecated invoice attributes with their standardized alternatives:
* Old (v2025-01-01)
```javascript
{
"invoice": {
"contact_person": "John Doe",
"state": "accepted",
"customer_party_identification": "12345",
"accounting_cost": "DEPT-001",
"iban": "ES6000000000000000000000",
"bic": "ABCDESMMXXX",
"num_contracte": "CONTRACT-2024",
"organ_gestor": "ORG-001",
"oficina_comptable": "OFF-001"
}
}
```
* New (v2025-10-13)
```javascript
{
"invoice": {
"customer_contact_person": "John Doe",
// state removed - use POST /invoices/{id}/mark_as instead
"contact": {
"party_identification": "12345"
},
"buyer_accounting_reference": "DEPT-001",
"bank_account": {
"iban": "ES6000000000000000000000",
"bic": "ABCDESMMXXX"
},
"contract_number": "CONTRACT-2024",
"managing_unit": "ORG-001",
"accounting_unit": "OFF-001"
}
}
```
12. **Update Contact/Client Attributes**: Replace deprecated contact attributes with their standardized alternatives:
* Old (v2025-01-01)
```javascript
{
"contact": {
"old_channel": "peppol",
"taxcode": "9920:ESD29766391",
"contact": "John Doe",
"bank_account": "ES6000000000000000000000",
"company_identifier": "A12345678",
"transport_type": "peppol",
"document_type": "xml.ubl.invoice.bis3",
"posta_elettronica_certificata": "pec@example.it",
"codice_destinatario": "ABC1234"
}
}
```
* New (v2025-10-13)
```javascript
{
"contact": {
"transport_type_code": "peppol",
"tin_scheme": "9920",
"tin_value": "ESD29766391",
"contact_person": "John Doe",
"bank_account_number": "ES6000000000000000000000",
"cin_value": "A12345678",
"document_type_code": "xml.ubl.invoice.bis3",
"certified_email": "pec@example.it",
"recipient_code": "ABC1234"
}
}
```
13. **Update Routing Codes**: Routing code fields (`cin1_value`, `cin1_scheme`, ..., `cin5_value`, `cin5_scheme`) are no longer accepted as top-level contact attributes. They must be sent inside a `routing_codes` wrapper:
* Old (v2025-01-01)
```javascript
{
"contact": {
"name": "Ajuntament de Girona",
"cin1_value": "L01170792",
"cin1_scheme": "8014",
"cin2_value": "L01170792",
"cin2_scheme": "8014",
"cin3_value": "L01170792",
"cin3_scheme": "8014"
}
}
```
* New (v2025-10-13)
```javascript
{
"contact": {
"name": "Ajuntament de Girona",
"routing_codes": {
"cin1_value": "L01170792",
"cin1_scheme": "8014",
"cin2_value": "L01170792",
"cin2_scheme": "8014",
"cin3_value": "L01170792",
"cin3_scheme": "8014"
}
}
}
```
In responses, routing codes are also returned inside the `routing_codes` object.
14. **Update Invoice State Changes**: Replace direct `state` attribute updates with the `mark_as` endpoint:
* Old (v2025-01-01)
```javascript
PUT /invoices/{id}.json
{
"invoice": {
"state": "accepted"
}
}
```
* New (v2025-10-13)
```javascript
POST /invoices/{id}/mark_as
{
"state": "accepted"
}
```
### Testing Your Migration
[Section titled “Testing Your Migration”](#testing-your-migration)
We recommend thoroughly testing your integration in the staging environment (`api-staging.b2brouter.net`) before deploying to production. The `X-B2B-API-Version` header allows you to test the new version without affecting your current integration.
# Migration guide v2025-10-13 → v2026-03-02
> Step-by-step guide to migrate your integration from API version v2025-10-13 to v2026-03-02.
This guide helps you upgrade from `X-B2B-API-Version: 2025-10-13` to `X-B2B-API-Version: 2026-03-02`, focusing on what you need to change, not just what changed.
[Changelog ](/en/developers/changelog/v2026-03-02/)For the full list of additions and fixes, see the v2026-03-02 changelog.
### Invoice Calculation — base\_quantity:
[Section titled “Invoice Calculation — base\_quantity:”](#invoice-calculation--base_quantity)
A new `base_quantity` parameter has been added to invoice lines, allowing prices quoted per multiple units (e.g., price per 100 pieces). The formula is now:
```plaintext
Line Base Amount = Quantity * Price / Base Quantity
```
When `base_quantity` is `1` (the default), this is backward-compatible with existing integrations.
In API responses, the `price` field now returns the original price as provided, and `base_quantity` is returned separately. In prior versions, the `price` field in responses contained the effective unit price (price / base\_quantity).
See the [Invoice Calculation Guide](https://developer.b2brouter.net/docs/invoice_calculation) for full details and examples.
***
# Migration guide v2026-03-02 → v2026-04-20
> Step-by-step guide to migrate your integration from API version v2026-03-02 to v2026-04-20.
This guide helps you upgrade from `X-B2B-API-Version: 2026-03-02` to `X-B2B-API-Version: 2026-04-20`, focusing on what you need to change, not just what changed.
[Changelog ](/en/developers/changelog/v2026-04-20/)For the full list of additions and fixes, see the v2026-04-20 changelog.
### At a glance
[Section titled “At a glance”](#at-a-glance)
| What changed | Do you need to act? |
| -------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| Credit notes and amendments use a new `invoice_references` structure | **Yes** — affects all integrations that create or read corrective invoices |
| `payment_method_info` removed from invoice responses | **Yes** — remove any code that reads this field |
| `clasification_code_scheme` typo fixed in invoice line responses | **Yes** — update your field name if you read it |
| New validation when creating child contacts (offices) | **Yes** — only if you set `tin_value` on contacts with a `parent_id` |
| New validation when creating invoices with an inline contact | **Yes** — ensure `tin_value` or `cin_value` is always included |
| New fields in invoice, account, and Peppol transport responses | Only if your code fails on unexpected JSON fields |
| Peppol transport: `pending_peppol_directory_publish` replaced | Only if you read this field |
| TIN verification endpoint (new) | No — opt-in feature |
| New account fields (`routing_codes`, `auto_remittance`, etc.) | No — opt-in |
| KSeF additions | No — only relevant for Polish KSeF integrations |
***
### Changes that require action
[Section titled “Changes that require action”](#changes-that-require-action)
#### 1. Credit notes and amendments: replace flat fields with `invoice_references`
[Section titled “1. Credit notes and amendments: replace flat fields with invoice\_references”](#1-credit-notes-and-amendments-replace-flat-fields-with-invoice_references)
This is the most significant change. The individual amendment fields on invoices have been replaced by a single `invoice_references` array. **If your integration creates or reads corrective invoices (credit notes, amendments), you must update both your requests and your response handling.**
The following fields are no longer sent or returned:
| Removed field | Replaced by `invoice_references[].` |
| -------------------------------- | ---------------------------------------------------- |
| `amended_number` | `number` |
| `amended_date` | `date` |
| `amended_invoicing_period_start` | `invoicing_period_start` *(send only, not returned)* |
| `amended_invoicing_period_end` | `invoicing_period_end` *(send only, not returned)* |
| `amend_reason` | `reason` |
| `amend_code_tax` | `tax_correction_code` |
| `correction_method` | `correction_method` |
Each entry in `invoice_references` requires a `reference_type`. Use `"amend"` for corrective invoices referencing an original invoice.
**Updating your request (create/update):**
* Before (v2026-03-02)
```json
{
"invoice": {
"number": "CN-2026-001",
"is_credit_note": true,
"contact_id": 123,
"amended_number": "INV-2026-042",
"amended_date": "2026-02-10",
"amend_reason": "Price correction"
}
}
```
* After (v2026-04-20)
```json
{
"invoice": {
"number": "CN-2026-001",
"is_credit_note": true,
"contact_id": 123,
"invoice_references": [
{
"reference_type": "amend",
"number": "INV-2026-042",
"date": "2026-02-10",
"reason": "Price correction"
}
]
}
}
```
**Updating your response handling (read):**
* Before (v2026-03-02)
```javascript
const originalNumber = invoice.amended_number;
const reason = invoice.amend_reason;
```
* After (v2026-04-20)
```javascript
const ref = invoice.invoice_references.find(
(r) => r.reference_type === "amend",
);
const originalNumber = ref?.number;
const reason = ref?.reason;
```
**Affected endpoints:** `GET /invoices/{id}`, `POST /accounts/{account}/invoices`, `PATCH /invoices/{id}`
***
#### 2. `payment_method_info` removed from invoice responses
[Section titled “2. payment\_method\_info removed from invoice responses”](#2-payment_method_info-removed-from-invoice-responses)
The field `payment_method_info` is no longer returned. It contained a human-readable, HTML-formatted payment summary — if you were using it, switch to the structured fields already present in the response: `payment_method`, `contact_iban`, `contact_bic`, etc.
**Affected endpoints:** `GET /invoices/{id}`, `POST /accounts/{account}/invoices`, `PATCH /invoices/{id}`
***
#### 3. Typo fix in invoice line field name: `clasification_code_scheme` → `classification_code_scheme`
[Section titled “3. Typo fix in invoice line field name: clasification\_code\_scheme → classification\_code\_scheme”](#3-typo-fix-in-invoice-line-field-name-clasification_code_scheme--classification_code_scheme)
The field was misspelled in all previous versions. The response now returns the correct spelling. If your code reads `clasification_code_scheme` (one 's') from invoice line responses, update it.
Note: sending the correct spelling in requests was already accepted in all versions — only the response key changes.
* Before (v2026-03-02)
```json
{ "clasification_code_scheme": "..." }
```
* After (v2026-04-20)
```json
{ "classification_code_scheme": "..." }
```
**Affected endpoints:** `GET /invoices/{id}`, `POST /accounts/{account}/invoices`, `PATCH /invoices/{id}`
***
#### 4. Child contacts (offices): `tin_value` is now validated against the parent
[Section titled “4. Child contacts (offices): tin\_value is now validated against the parent”](#4-child-contacts-offices-tin_value-is-now-validated-against-the-parent)
When creating or updating a contact that belongs to a parent company (using `parent_id`), the API now rejects the request if the provided `tin_value` does not match the parent contact's. Previously, a mismatched value was silently ignored.
The recommended approach is to omit `tin_value` entirely when creating offices — it is automatically inherited from the parent.
```json
// Recommended (v2026-04-20) — create a branch office
{
"contact": {
"name": "Branch Office Madrid",
"parent_id": 456
}
}
```
**Affected endpoints:** `POST /accounts/{account}/contacts`, `PATCH /contacts/{id}`
***
#### 5. Creating an invoice with an inline contact now requires an identifier
[Section titled “5. Creating an invoice with an inline contact now requires an identifier”](#5-creating-an-invoice-with-an-inline-contact-now-requires-an-identifier)
If you create an invoice and pass the contact data inline (using a `contact` object instead of a `contact_id`), the contact must now include at least one of `tin_value` or `cin_value`. Requests without either will be rejected with `422 Unprocessable Entity`.
This does not apply to simplified invoices (`IssuedSimplifiedInvoice`), which do not require an identified contact.
```json
// v2026-04-20 — tin_value or cin_value is required
{
"invoice": {
"contact": {
"name": "Acme Corp",
"tin_value": "B12345678",
"tin_scheme": "9915"
}
}
}
```
**Affected endpoints:** `POST /accounts/{account}/invoices`
***
### New fields in existing responses
[Section titled “New fields in existing responses”](#new-fields-in-existing-responses)
The following fields have been added to existing responses. They are backwards-compatible — your integration will continue to work without changes. However, if your code fails when it encounters unexpected JSON fields, update your response models to allow them.
**Invoice responses** (`GET /invoices/{id}`, `POST`, `PATCH`):
* `invoice_references` — array of amendment/prepayment references (see section 1 above)
* `exchange_rate` — the exchange rate applied when the invoice currency differs from the local tax authority currency
* `exchange_date` — the date of the exchange rate
* `order_date` — purchase order date (complements the existing `ponumber` field)
* `contact.cin_value`, `contact.cin_scheme` — company identifier of the invoice contact
**Account responses** (`GET /accounts/{account}`, `GET /accounts`):
* `routing_codes` — object containing up to 5 additional company identifiers (`cin1_value`/`cin1_scheme` through `cin5_value`/`cin5_scheme`)
* `auto_remittance` — boolean
* `skip_line_taxable_base_rounding` — boolean
**Peppol transport response** (`GET /accounts/{account}/transports`):
* `sml_status` — SML registration status: `"not_published"`, `"processing"`, or `"published"`
* `peppol_directory_status` — Peppol Directory listing status (replaces the old `pending_peppol_directory_publish` boolean)
* `reception_document_types` — list of document types this participant can receive via Peppol
* `standard_documents` and `pending_peppol_directory_publish` are no longer returned
***
### New features available in this version
[Section titled “New features available in this version”](#new-features-available-in-this-version)
**TIN verification** — verify that a company name and tax number are registered with the tax authority before sending an invoice. Currently supports Spain (AEAT).
* `POST /tin_verifications?country=es` — submit a list of up to 20,000 TIN+name pairs for verification; returns `202 Accepted` while processing runs in the background
* `GET /tin_verifications/{id}` — retrieve the results once processing is complete
* Subscribe to the `tin_verification.finished` webhook event to be notified automatically
**New account configuration fields:**
* `routing_codes` on accounts — store up to 5 additional company identifiers, useful for Peppol routing
* `auto_remittance` — automatically generate structured OGM remittance references on Belgian invoices
* `skip_line_taxable_base_rounding` — useful for invoices with many lines where small rounding differences accumulate; requires `apply_taxes_per_line: true`
**KSeF (Polish e-invoicing) additions** — if your integration handles Polish invoices, this version adds support for advance payment workflows (`ZAL`, `ROZ`, `KOR_ZAL`, `KOR_ROZ`), local government and VAT group invoicing scenarios, and several new tax report fields. See the [changelog](https://developer.b2brouter.net/changelog/v20260420) for the full list.
***
### Migration checklist
[Section titled “Migration checklist”](#migration-checklist)
Use this checklist before switching to v2026-04-20 in production.
**Corrective invoices (credit notes, amendments):**
* [ ] Replace the flat amendment fields (`amended_number`, `amended_date`, `amend_reason`, etc.) with `invoice_references` in all invoice create/update requests
* [ ] Include `reference_type: "amend"` on each entry
* [ ] Update response handling to read from `invoice_references` instead of the flat fields
* [ ] Test: create a credit note, read it back, and confirm the `invoice_references` array is present and correct
**Invoice response fields:**
* [ ] Remove any code that reads `payment_method_info`
* [ ] Update `clasification_code_scheme` → `classification_code_scheme` wherever you read invoice line responses
* [ ] Confirm your response handling accepts new fields: `exchange_rate`, `exchange_date`, `order_date`, `contact.cin_value`, `contact.cin_scheme`
**Contacts:**
* [ ] If you create contacts with `parent_id`, remove or align `tin_value` with the parent contact's value
* [ ] If you create invoices with an inline `contact` object, ensure `tin_value` or `cin_value` is always included
**Peppol transport:**
* [ ] Update transport response handling: replace `pending_peppol_directory_publish` with `peppol_directory_status`, and remove `standard_documents` reads
* [ ] Confirm your response model accepts `sml_status`, `peppol_directory_status`, and `reception_document_types`
**Accounts:**
* [ ] Confirm your account response handling accepts the new `routing_codes`, `auto_remittance`, and `skip_line_taxable_base_rounding` fields
### Testing your integration
[Section titled “Testing your integration”](#testing-your-integration)
Switch to the new version header in your sandbox or staging environment first:
```plaintext
X-B2B-API-Version: 2026-04-20
```
You can set this header per-request, so you can test against sandbox (or staging) without touching your production configuration. Once you have validated the checklist above, update the version in your production API key settings or request headers.
# Migration guide v2026-04-20 → v2026-06-26
> Step-by-step guide to migrate your integration from API version v2026-04-20 to v2026-06-26.
This guide helps you upgrade from `X-B2B-API-Version: 2026-04-20` to `X-B2B-API-Version: 2026-06-26`, focusing on what you need to change, not just what changed.
[Changelog ](/en/developers/changelog/v2026-06-26/)For the full list of additions and fixes, see the v2026-06-26 changelog.
### At a glance
[Section titled “At a glance”](#at-a-glance)
| What changed | Do you need to act? |
| -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Offices now store their own `cin_value` / `cin_scheme` instead of inheriting them | **Yes** — only if your integration creates or reads offices (contacts with `parent_id`) |
| French Annuaire lookup on `/directory/fr` can now return `202 Accepted` | **Yes** — only if you call `/directory/fr` for French identifiers |
| Flat `cinN_value` / `cinN_scheme` no longer accepted on invoice `contact` | **Yes** — move these into `contact.routing_codes` |
| Dedicated routing fields (`dire`, `accounting_unit`, etc.) no longer accepted at invoice top level | **Yes** — move these into the invoice-level `routing_codes` object |
| `POST /invoices/{id}/generate_tax_report` now returns `201 Created` | **Yes** — only if you check for an exact `200` status |
| `number` invoice list filter now matches by prefix instead of exact match | Only if your integration relies on exact-match behavior |
| New received invoice webhook events | No — opt-in |
| Create offices via `POST /accounts` with `parent_id` | No — opt-in |
| `issue_after_import` on invoice creation and import | No — opt-in |
| Account `unit_code_format` preference | No — opt-in |
| Per-invoice `apply_taxes_per_line` | No — opt-in |
| New `taxable_base` / `tax_amount` fields in invoice list | No — additive |
| DGFiP `refuse_reason_code` on mark\_as and invoice responses | No — opt-in |
***
### Changes that require action
[Section titled “Changes that require action”](#changes-that-require-action)
#### 1. Offices now store their own CIN instead of inheriting it
[Section titled “1. Offices now store their own CIN instead of inheriting it”](#1-offices-now-store-their-own-cin-instead-of-inheriting-it)
Previously, an office (a contact created with `parent_id`) always inherited `cin_value` / `cin_scheme` from its parent contact. Now these fields are stored independently per office.
* If you send `cin_value` / `cin_scheme` when creating or updating an office, they are stored on the office and returned in responses.
* If you omit them (or clear with `cin_value: ""`), the office still inherits the parent's CIN in read responses — no action needed if you never set CIN on offices.
* `tin_value` / `tin_scheme` are unaffected — still always inherited from the parent and cannot be set independently.
* CIN uniqueness within the account is now enforced for **parent contacts only**: reusing a `cin_value` already assigned to another parent contact in the same account returns `422 Unprocessable Entity`.
- Before (v2026-04-20)
```json
POST /accounts/{account_id}/contacts
{
"contact": {
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "1234567890",
"cin_scheme": 88
}
}
```
```json
// Response — cin_value is ignored, inherited from the parent contact
{
"contact": {
"id": 67890,
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "",
"cin_scheme": ""
}
}
```
- After (v2026-06-26)
```json
POST /accounts/{account_id}/contacts
{
"contact": {
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "1234567890",
"cin_scheme": 88
}
}
```
```json
// Response — cin_value is now stored on the office itself
{
"contact": {
"id": 67890,
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "1234567890",
"cin_scheme": "88"
}
}
```
**Affected endpoints:** `POST /accounts/{account}/contacts`, `PUT /contacts/{id}`
***
#### 2. French Annuaire lookup on `/directory/fr` is now asynchronous
[Section titled “2. French Annuaire lookup on /directory/fr is now asynchronous”](#2-french-annuaire-lookup-on-directoryfr-is-now-asynchronous)
`GET /directory/fr/{scheme}/{id}` now consults the French PPF Annuaire for SIREN, SIRET, and FRCTC (`0225`) identifiers. If your integration calls this endpoint for French identifiers, handle the new response shapes:
* **`202 Accepted`** with a `polling_url` — the company was not yet known; a background job is querying the Annuaire. Retry after a few seconds.
* **`424 Failed Dependency`** with error code `upstream_unavailable` and a `Retry-After` header — the Annuaire or Peppol is temporarily unavailable. Retry after the indicated delay; do not treat this as a `404`.
* **`422 Unprocessable Entity`** with code `invalid_identifier` — the identifier failed length/checksum validation for its scheme.
* A new `information_flags` array is included in successful responses (`FR_ASSUJETTI_ACTIVE`, `FR_ASSUJETTI_INACTIVE`, `FR_ASSUJETTI_UNKNOWN`).
```shell
curl --request GET \
--url 'https://app-staging.b2brouter.net/api/v1/directory/fr/0002/123456789' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: 2026-06-26'
```
```json
// 202 Accepted — not yet known, retry polling_url
{
"status": "processing",
"message": "Querying French PPF Annuaire. Please retry this request.",
"polling_url": "https://app-staging.b2brouter.net/api/v1/directory/fr/0002/123456789"
}
```
**Affected endpoints:** `GET /directory/fr/{scheme}/{id}` (non-FR countries unaffected)
***
#### 3. Flat `cinN_value` / `cinN_scheme` no longer accepted on invoice `contact`
[Section titled “3. Flat cinN\_value / cinN\_scheme no longer accepted on invoice contact”](#3-flat-cinn_value--cinn_scheme-no-longer-accepted-on-invoice-contact)
The inline `contact` object on `POST /accounts/{account}/invoices` no longer accepts flat `cin1_value`/`cin1_scheme` … `cin5_value`/`cin5_scheme` keys. Move them into a `contact.routing_codes` object — the same shape used by `POST /accounts/{account}/contacts`.
* Before (v2026-04-20)
```json
{
"invoice": {
"contact": {
"tin_value": "ESA13585625",
"name": "Buyer Full Name AS",
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS",
"cin1_scheme": "8017"
}
}
}
```
* After (v2026-06-26)
```json
{
"invoice": {
"contact": {
"tin_value": "ESA13585625",
"name": "Buyer Full Name AS",
"routing_codes": {
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS",
"cin1_scheme": "8017"
}
}
}
}
```
Requests still using the flat keys return `422 Unprocessable Entity`.
**Affected endpoints:** `POST /accounts/{account}/invoices`
***
#### 4. Invoice-level routing fields replaced by a `routing_codes` object
[Section titled “4. Invoice-level routing fields replaced by a routing\_codes object”](#4-invoice-level-routing-fields-replaced-by-a-routing_codes-object)
At the invoice top level, both the flat `cinN_value` / `cinN_scheme` keys and the dedicated routing fields (`dire`, `accounting_unit`, `managing_unit`, `processing_unit`, `proponent_unit`, `nadby`, `naddp`, `nadiv`, `nadpr`, `nadud`) are no longer accepted. Use the invoice-level `routing_codes` object instead — it carries the invoice's own routing codes (e.g. buyer's DIR3 / Chorus Pro service codes), independent of the contact's.
| Slot | DIR3 (scheme `8014`) | DIRe (`8015`) | EDI/GLN (`88`) |
| ---- | -------------------- | ------------- | -------------- |
| cin1 | `accounting_unit` | — | `nadby` |
| cin2 | `managing_unit` | — | `naddp` |
| cin3 | `processing_unit` | — | `nadiv` |
| cin4 | `proponent_unit` | — | `nadpr` |
| cin5 | — | `dire` | `nadud` |
* Before (v2026-04-20)
```json
{
"invoice": {
"number": "F-2026-0001",
"accounting_unit": "ESR_MISSION_FACTURES_DEPLACEMENTS"
}
}
```
* After (v2026-06-26)
```json
{
"invoice": {
"number": "F-2026-0001",
"routing_codes": {
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS",
"cin1_scheme": "8014"
}
}
}
```
The applied codes are echoed back in a `routing_codes` object at the invoice top level of the response. Requests still using the old fields return `422 Unprocessable Entity`.
**Affected endpoints:** `POST /accounts/{account}/invoices`, `PUT /invoices/{id}`
***
#### 5. `generate_tax_report` now returns `201 Created`
[Section titled “5. generate\_tax\_report now returns 201 Created”](#5-generate_tax_report-now-returns-201-created)
`POST /invoices/{id}/generate_tax_report` now returns `201 Created` on success instead of `200 OK`. The response body is unchanged. This only affects clients that check for an exact `200` status — clients checking for any `2xx` success are unaffected.
**Affected endpoints:** `POST /invoices/{id}/generate_tax_report`
***
#### 6. Invoice list `number` filter now matches by prefix
[Section titled “6. Invoice list number filter now matches by prefix”](#6-invoice-list-number-filter-now-matches-by-prefix)
`GET /accounts/{account}/invoices?number=...` now performs a **prefix** match instead of an exact match. If your integration relies on the previous exact-match behavior, add a client-side check to filter out unwanted prefix matches, or switch to a more specific value.
```plaintext
GET /accounts/{account}/invoices?number=INV-2
```
now also returns `INV-20`, `INV-21`, … in addition to an invoice numbered exactly `INV-2`. A new `series_code` filter is available for exact-match filtering by series.
**Affected endpoints:** `GET /accounts/{account}/invoices`
***
### New fields in existing responses
[Section titled “New fields in existing responses”](#new-fields-in-existing-responses)
The following fields have been added. They are backwards-compatible — your integration will continue to work without changes. However, if your code fails when it encounters unexpected JSON fields, update your response models to allow them.
**Contact responses** (`GET /contacts/{id}`, `POST`, `PUT`, `DELETE`):
* `parent_id` — `null` for top-level contacts, the parent contact's ID for offices
**Invoice list** (`GET /accounts/{account}/invoices`):
* `taxable_base` — invoice subtotal before taxes
* `tax_amount` — total tax amount across all tax breakdown lines
**Invoice responses** (`mark_as`, create, update):
* `refuse_reason_code` — DGFiP-defined structured reason code for process 210 refusals (accepted on `POST /invoices/{id}/mark_as` alongside `reason`)
**Account responses** (`GET /accounts/{account}`, `GET /accounts`):
* `unit_code_format` — `internal` (default) or `unece_rec20`; see the opt-in feature below for the effect on invoice line `unit`
***
### New features available in this version
[Section titled “New features available in this version”](#new-features-available-in-this-version)
**Received invoice webhooks** — subscribe to `received_invoice.created` and `received_invoice.state_change` to be notified when received invoices are created or change state.
**Offices via `POST /accounts`** — create an office (subsidiary) directly under a parent account by passing `parent_id`; standard required fields become optional and the office inherits the group's integration.
**`issue_after_import`** — pass this flag on invoice creation or import to synchronously transition the invoice to `state=issued` inline with the request, instead of relying on the send flow.
**`unit_code_format` account preference** — set to `unece_rec20` to exchange invoice line `unit` as a UN/ECE Recommendation 20 string code (e.g. `"HUR"`) instead of B2Brouter's internal integer id. The default `internal` keeps existing integrations unaffected.
**Per-invoice `apply_taxes_per_line`** (premium accounts) — override the account-level VAT rounding mode for a single invoice, useful when a public administration recomputes totals with a different rounding rule. See the [invoice calculation guide](/en/developers/common-use-cases/invoice-calculation/).
***
### Migration checklist
[Section titled “Migration checklist”](#migration-checklist)
Use this checklist before switching to v2026-06-26 in production.
**Contacts and offices:**
* [ ] If you set `cin_value` / `cin_scheme` when creating or updating offices, verify the value is unique within the account (duplicates now return `422`)
* [ ] Update any code reading `parent_id` on contact responses if you need to distinguish offices from top-level contacts
**Invoice creation with inline contact:**
* [ ] Move any `cinN_value` / `cinN_scheme` keys inside `invoice.contact` into `invoice.contact.routing_codes`
* [ ] Move any invoice top-level `cinN_value`/`cinN_scheme` or dedicated fields (`dire`, `accounting_unit`, etc.) into the invoice-level `routing_codes` object
**Tax reports:**
* [ ] Update any status check on `POST /invoices/{id}/generate_tax_report` that expects exactly `200 OK`
**Invoice list filtering:**
* [ ] Review any use of the `number` filter that depends on exact-match behavior; switch to `series_code` if you need exact matching by series
**French directory lookups:**
* [ ] Handle `202 Accepted` with `polling_url` and retry
* [ ] Handle `424 Failed Dependency` with `Retry-After` as a transient error, not a `404`
* [ ] Handle `422 Unprocessable Entity` with code `invalid_identifier`
### Testing your integration
[Section titled “Testing your integration”](#testing-your-integration)
Switch to the new version header in your sandbox or staging environment first:
```plaintext
X-B2B-API-Version: 2026-06-26
```
You can set this header per-request, so you can test against sandbox (or staging) without touching your production configuration. Once you have validated the checklist above, update the version in your production API key settings or request headers.
# Send invoice via SDI
> Quick recipe to issue a FatturaPA invoice to the Italian SDI with the B2Brouter API.
Quick recipe to issue an invoice to the SDI (*Sistema di Interscambio*) with the B2Brouter API: account → contact → invoice → status.
Full guide
This page is only a summary. For all the details (recipient identification, B2B/B2C/B2G, VAT nature codes, regime fiscale, autofattura, credit/debit notes, bollo, ritenuta, reception…), see the [SDI (FatturaPA)](/en/developers/guides-by-country/italy/sdi-fatturapa/) guide.
## 1. Create the Italian company account
[Section titled “1. Create the Italian company account”](#1-create-the-italian-company-account)
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/json' \
--data '{
"account": {
"country": "it",
"tin_value": "20339260422",
"tin_scheme": 211,
"name": "Test Sender Italy",
"address": "Milano street, 1",
"city": "Milano",
"postalcode": "20019",
"province": "MI",
"tax_regime": "RF01"
}
}'
```
## 2. Create the customer contact
[Section titled “2. Create the customer contact”](#2-create-the-customer-contact)
`transport_type_code: it.sdi`, `document_type_code: xml.fatturapa.1.2`, the Partita IVA with `tin_scheme: 9906` (or the Codice Fiscale with `cin_scheme: 9907`), and the Codice Destinatario in `recipient_code` (if there is none, `0000000`).
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/json' \
--data '{
"contact": {
"tin_scheme": "9906",
"tin_value": "IT12345678901",
"name": "Esempio S.r.l.",
"address": "Via Inventata 88",
"postalcode": "00184",
"city": "Roma",
"province": "RM",
"country": "it",
"transport_type_code": "it.sdi",
"document_type_code": "xml.fatturapa.1.2",
"recipient_code": "{CODICE_DESTINATARIO}"
}
}'
```
## 3. Create and send the invoice
[Section titled “3. Create and send the invoice”](#3-create-and-send-the-invoice)
With `send_after_import: true` the invoice is sent to the SDI directly.
```shell
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": {
"type": "IssuedInvoice",
"number": "1",
"date": "2025-01-22",
"contact_id": {YOUR_CONTACT_ID},
"invoice_lines_attributes": [
{
"quantity": 10,
"price": 47,
"description": "Test Item",
"taxes_attributes": [{ "name": "IVA", "percent": 22 }]
}
]
}
}'
```
## 4. Check the status
[Section titled “4. Check the status”](#4-check-the-status)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
The invoice goes through `sending` → `sent`; if the SDI rejects it, it moves to `error`. See [Checking the invoice status](/en/developers/guides-by-country/italy/sdi-fatturapa/#checking-the-invoice-status) for the details and the *acknowledge* step.
# Send invoices to Chorus Pro
> How to send invoices to French public entities via Chorus Pro using the B2Brouter API.
In France, **all** invoices to public-sector clients must be submitted electronically via **Chorus Pro**, the government’s official B2G e-invoicing portal (Decree No. 2016-1478). Since April 2020, this applies to every central, regional and local administration—whether you’re a French company or an international supplier.
This guide shows you how to use the **B2Brouter API** to:
1. **Set up** your company account (staging & production).
2. **Lookup** public entities by SIRET in the B2Brouter Directory.
3. **Create** customer and organizational-unit contacts.
4. **Generate**, **send**, and **track** your invoices via Chorus Pro.
5. **Download** the exact XML file submitted and **acknowledge**.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* **French company** with a valid TVA/SIRET number.
* **Testing environment (staging)**
* Register at [app-staging.b2brouter.net](https://app-staging.b2brouter.net/register) to try out the API.
* Once registered, open a Support Ticket in staging to request your API key and permissions.
* **Production integration & eDocExchange subscription**
* To go live, register at [app.b2brouter.net](https://app.b2brouter.net/register).
* Subscribe to an eDocExchange Product or contact our Sales team at to discuss plans, sign your contract, and receive dedicated integration support.
## Obtaining API Credentials
[Section titled “Obtaining API Credentials”](#obtaining-api-credentials)
1. Log in to your B2Brouter account.
2. Go to the **Developers** tab.
3. Select **API Keys**.
4. Click on the **clipboard** icon to retrieve your API token.
## Required identifier: SIRET, not SIREN
[Section titled “Required identifier: SIRET, not SIREN”](#required-identifier-siret-not-siren)
To invoice via Chorus Pro, **both the issuing company and the client (the public entity) must be identified with a SIRET** (`cin_scheme="0009"`), never with a SIREN:
* **Issuing company**: if the company’s country is `fr`, `cin_value` must hold a non-blank SIRET and `cin_scheme` must equal `9` (`"0009"`). If the SIRET is blank, or `cin_scheme` is anything other than `9`, the invoice is not sent.
* **Client**: the recipient must also be identified with a SIRET. Without it, contact creation fails with the error “Chorus needs the SIRET identifier of the client”.
> **Hierarchical consistency**: even though invoicing is always SIRET-to-SIRET, the account or contact shouldn’t be “flat” at SIRET level. Keep the same hierarchy as in the DGFiP circuit: a **SIREN** parent with the SIRET modeled as an **organizational unit (OU)** (`parent_id` + `cin_scheme="0009"`) under that parent (see [Account structure](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/#account-structure-parent-and-organisational-units)). If your company already has a B2Brouter account with a SIREN parent, create the issuing SIRET as an OU under that parent instead of a separate standalone account.
## Retrieve or Create Your Company Account
[Section titled “Retrieve or Create Your Company Account”](#retrieve-or-create-your-company-account)
### 1. Retrieve your Company ID
[Section titled “1. Retrieve your Company ID”](#1-retrieve-your-company-id)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts?offset=0&limit=25' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### 2. Create a Company Account (if needed)
[Section titled “2. Create a Company Account (if needed)”](#2-create-a-company-account-if-needed)
Provide `cin_scheme: "0009"` and `cin_value` with the issuing establishment’s SIRET (14 digits). This is required for Chorus Pro: without a SIRET, or with a `cin_scheme` other than `9`, invoice submission fails.
> If your company already has a B2Brouter account at SIREN parent level (for example, for DGFiP), don’t create a new standalone account with this SIRET: create it as an **OU** with `parent_id` pointing to the parent, following the same steps as [Create organisational units](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/#add-establishments-as-organisational-units-optional) in the DGFiP guide. The example below is for a company that doesn’t yet have any B2Brouter account.
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"account": {
"country": "fr",
"rounding_method": "half_up",
"tin_value": "FR46458880332",
"tin_scheme": 9957,
"cin_scheme": "0009",
"cin_value": "45888033200017",
"name": "Exemplar SAS",
"address": "10 Rue Imaginaire",
"city": "Paris",
"postalcode": "75001",
"province": "Île-de-France",
"email": "john.doe@example.com"
}
}'
```
## Verify recipient information
[Section titled “Verify recipient information”](#verify-recipient-information)
You can check if the recipient exists in our public directory:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/directory/fr/0009/13001533200013 \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json'
```
> This lookup already returns the entity’s existing organizational units (service codes) from the Chorus Pro directory. You don’t need to create them yourself as a contact: use the `cin1_scheme`/`cin1_value` already returned by the directory.
## Create a Contact
[Section titled “Create a Contact”](#create-a-contact)
Versions prior to 2025-10-13
Use `client` instead of `contact` and `/projects/{id}/clients.json` instead of `/accounts/{id}/contacts`.
When creating a French customer:
* Use `cin_value` for SIRET-CODE number. This is **required**: without a SIRET, Chorus Pro rejects the invoice with the error “Chorus needs the SIRET identifier of the client”.
* Use `cin_scheme` to identify the [Schemes Codelist](https://docs.peppol.eu/poacc/upgrade-3/codelist/eas/). SIRET-CODE is 0009; a SIREN (`0002`) is not accepted.
* `transport_type_code` should be `fr.chorus`.
* `document_type_code` should be `xml.ubl.invoice.chorus`.
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"contact": {
"language": "en",
"is_client": true,
"is_provider": true,
"terms": "custom",
"public_sector": true,
"name": "UNIVERSITE D AIX MARSEILLE",
"address": "58 BD CHARLES LIVON",
"city": "MARSEILLE 7",
"postalcode": "13007",
"country": "fr",
"currency": "EUR",
"transport_type_code": "fr.chorus",
"document_type_code": "xml.ubl.invoice.chorus",
"cin_value": "13001533200013",
"cin_scheme": "0009"
}
}'
```
### Create an Organizational Unit Contact
[Section titled “Create an Organizational Unit Contact”](#create-an-organizational-unit-contact)
To bill a specific department or service, create a sub-contact under the main entity using `parent_id` and include the Chorus Pro “service code” (`cin1_scheme` / `cin1_value`).
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"contact": {
"parent_id": 1313228381,
"name": "Factures marché FCM ROP cadre A2",
"address": "58 BD CHARLES LIVON",
"city": "MARSEILLE 7",
"postalcode": "13007",
"country": "fr",
"cin1_scheme": "8017",
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS"
}
}'
```
## Create and Send an Invoice
[Section titled “Create and Send an Invoice”](#create-and-send-an-invoice)
When invoicing a French customer, ensure you provide all required fields including:
* `number`, `date` and `due_date`
* At least one `invoice_lines_attributes` with `taxes_attributes`
* `contact_id` or a complete `contact` object
* `ponumber` to identify the Order reference
* `buyer_reference` with the `cin1_value` (Code Service) to identify the target department
```shell
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": {
"type": "IssuedInvoice",
"contact_id": 1313228399,
"bank_account": {
"type": "iban",
"iban": "FR7630006000011234567890189"
},
"terms": "custom",
"invoice_lines_attributes": [
{
"unit": 5,
"quantity": 135,
"price": 25,
"description": "Cocktail Dinatoire",
"taxes_attributes": [
{ "name": "TVA", "category": "S", "percent": 10 }
],
"article_code": "14",
"position": 1
}
],
"number": "00002",
"date": "2025-06-18",
"due_date": "2025-07-18",
"currency": "EUR",
"ponumber": "0123456",
"buyer_reference": "ESR_MISSION_FACTURES_DEPLACEMENTS"
}
}'
```
## Check Invoice Status
[Section titled “Check Invoice Status”](#check-invoice-status)
### Check Single Invoice Status
[Section titled “Check Single Invoice Status”](#check-single-invoice-status)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}?include=lines' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### Check Multiple Invoices Status
[Section titled “Check Multiple Invoices Status”](#check-multiple-invoices-status)
```shell
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices?offset=0&limit=25&state_updated_at_from=2025-06-12' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
### Real-Time Status Updates with Webhooks
[Section titled “Real-Time Status Updates with Webhooks”](#real-time-status-updates-with-webhooks)
Instead of polling, subscribe to push notifications via webhooks. Whenever an invoice state changes, B2Brouter will send an HTTP POST to your endpoint.
[Invoice Status WebHooks - API Reference](https://developer.b2brouter.net/reference/getnewinvoicestatechange)
## Download the Original Invoice XML
[Section titled “Download the Original Invoice XML”](#download-the-original-invoice-xml)
After sending, the **GET /invoices/{id}** response includes a `download_legal_url` field. Use it to fetch the exact XML file submitted to Chorus Pro:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net{download_legal_url} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Accept: application/xml'
```
As a one-step alternative, you can call `GET /invoices/{id}/as/legal` directly, without first fetching `download_legal_url` from the invoice payload. It returns the archived legal document as stored and does not generate a billable transaction — see [Download invoices](/en/developers/essential-guides/download-invoice/) and [Transaction: View\_as](/en/developers/essential-guides/transaction/#view_as).
## Mark Invoice as Acknowledged
[Section titled “Mark Invoice as Acknowledged”](#mark-invoice-as-acknowledged)
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/ack \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
```
## Related guides
[Section titled “Related guides”](#related-guides)
* [DGFiP – E-Invoicing and E-Reporting](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/): domestic French B2B circuit, with a different identifier model (SIREN parent + SIRET organizational units).
For further help:
* [API Reference](https://developer.b2brouter.net/reference)
* [Support](https://www.b2brouter.net/docs/#/en/support/open-incident-report)
# Send to FACe with imported file
> How to send invoices to the Spanish FACe portal using the B2Brouter API.
Utilizing the B2Brouter API empowers you to seamlessly send invoices to FACe. The process involves:
1. **Create an invoice**: Import your invoice document or JSON payload into B2Brouter.
2. **Verify recipient information**: Ensure the recipient details are accurate and complete.
3. **Send the invoice**: Proceed to send the invoice.
4. **Track the document**: Monitor the traceability of your sent document.
## Creating an Invoice
[Section titled “Creating an Invoice”](#creating-an-invoice)
To create an invoice using our API, consider these options:
* **Option 1: JSON Payload** — Utilize a JSON payload to create your issued invoice.
* **Option 2: Importing a Supported File Format**
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices/import \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/octet-stream' \
--data '@invoice.xml'
```
Upon importing your invoice document, B2Brouter will generate an invoice object if the document meets standard validation criteria.
## Verify recipient information
[Section titled “Verify recipient information”](#verify-recipient-information)
If it’s the first time sending a document to the recipient, a contact will be automatically generated using the data provided in the invoice. You can also check if the recipient exists in our public directory:
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/directory/es/9920/ESP2800500G \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json'
```
**Sample response:**
```json
{
"name": "Ayuntamiento de Alcalá de Henares",
"tin_scheme": "9920",
"tin_value": "ESP2800500G",
"invoice": {
"transport_type_code": "es.face",
"document_type_code": "xml.facturae.3.2"
},
"routing_codes": {
"cin1_scheme": "8014",
"cin2_scheme": "8014",
"cin3_scheme": "8014",
"cin1_value": "L01280053",
"cin2_value": "L01280053",
"cin3_value": "L01280053"
}
}
```
You can also edit your contact information to add DIR3 codes:
```shell
curl --request PUT \
--url https://api-staging.b2brouter.net/contacts/{CONTACT_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"contact": {
"transport_type_code": "es.face",
"document_type_code": "xml.facturae.3.2.2",
"cin1_scheme": "8014",
"cin1_value": "{DIR3_ACCOUNTING}",
"cin2_scheme": "8014",
"cin2_value": "{DIR3_MANAGING}",
"cin3_scheme": "8014",
"cin3_value": "{DIR3_PROCESSING}"
}
}'
```
For more detailed information, check our code lists:
* [Transport Types](https://developer.b2brouter.net/reference/get-transport-types)
* [Document Types](https://developer.b2brouter.net/reference/get-document-types)
## Send your invoice
[Section titled “Send your invoice”](#send-your-invoice)
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/send_invoice/{INVOICE_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json'
```
If the imported document matches the recipient’s transport requirements (such as being a facturaE) and remains unaltered, we’ll directly send the original file. Otherwise, we’ll generate a document that aligns with the required document type. Prior to transmission to FACe, we’ll also verify if the XML is signed; if not, we’ll sign it.
## Track your invoice
[Section titled “Track your invoice”](#track-your-invoice)
B2Brouter offers two options for monitoring the status of your document:
* **Option 1**: [Retrieve events related to a specific invoice](https://developer.b2brouter.net/reference/get-events).
* **Option 2**: [Check the status of an invoice](https://developer.b2brouter.net/reference/get-invoice).
# Submit a Flux-10 e-reporting file
> How to deposit an F10 e-reporting file you already generate with the PPF, with B2Brouter acting as the conduit to the DGFiP.
This guide explains how to deposit a **Flux 10 (e-reporting)** file with the PPF when your own system already generates it, without the underlying invoices going through B2Brouter’s invoicing. B2Brouter validates the file against the official v3.2 XSD, normalizes it for deposit, and transmits it to the DGFiP as an Accredited Platform (Plateforme Agréée), returning the CDV lifecycle states to you via webhook and API. This is an alternative path to the native pipeline, where you send invoice data as JSON and B2Brouter builds the F10, and both share the same output channel to the PPF.
## Who this guide is for
[Section titled “Who this guide is for”](#who-this-guide-is-for)
For integrators and software vendors who **already produce their own F10 ``** and only need an accredited channel to deposit it. If what you want is for B2Brouter to generate the F10 from your invoices, don’t use this guide: see the e-Reporting (Flux 10) section of the [DGFiP guide](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/).
Division of responsibilities
On this path B2Brouter doesn’t build the declaration: it validates, deposits and traces it. As a result:
* The **correctness of the declared content** is the responsibility of whoever generates the file. B2Brouter guarantees its structural conformance (XSD v3.2), not its tax accuracy.
* The **periodicity** of the declarations (monthly, quarterly, deadlines under the simplifié or franchise regimes) is also the responsibility of whoever generates the file. B2Brouter deposits what it receives when it receives it; it doesn’t compute the period that applies to you.
* The **10-year legal archiving of the underlying invoices** is the responsibility of whoever issues them, since those invoices never enter B2Brouter. B2Brouter keeps the deposited file, the original submission exactly as we received it, and the PPF’s responses.
This path and the native pipeline are **mutually exclusive per account**: you cannot declare the same scope through both. The exclusion is set contractually during onboarding.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* A French account on B2Brouter with the **`dgfip` Tax Report Setting enabled**. See steps 1 and 2 of the [DGFiP guide](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/).
* `X-B2B-API-Version: 2026-03-02` or later.
* API permissions for the ledger endpoints enabled on your plan. If you get a `403`, open a support ticket to have them enabled.
* An F10 file that validates against the official **v3.2** XSD package.
> The examples use `https://api-staging.b2brouter.net`. For production, replace it with `https://api.b2brouter.net`.
## Deposit the file
[Section titled “Deposit the file”](#deposit-the-file)
```plaintext
POST /accounts/{ACCOUNT_ID}/ledgers/import
```
The request body is the **raw XML**, with `Content-Type: application/octet-stream` (any other content type is rejected, so Rails doesn’t try to parse the body as parameters).
```shell
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/ledgers/import \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: 2026-03-02' \
--header 'Content-Type: application/octet-stream' \
--header 'Idempotency-Key: f10-2026-01-transactions-001' \
--data-binary @flux10-transactions.xml
```
**Response — 201 Created:**
```json
{
"ledger": {
"id": 68916,
"type": "dgfip",
"state": "new"
}
}
```
The ledger `id` is the identifier you’ll use to track the whole lifecycle. Save it.
### Minimum file structure
[Section titled “Minimum file structure”](#minimum-file-structure)
The file is a `` with no namespace. B2Brouter reads three things from it to decide whether it can be deposited: the ``, the declared sub-flow, and XSD validity.
```xml
your-identifier
Déclaration Transactions Janvier 2026
202602110400
IN
0426
PDP426
WK
contact@exemple.fr
832987911
METACORTEX
SE
contact@metacortex.fr
```
**One sub-flow per deposit.** The PPF transmits transactions and payments separately (spec v3.2 §3.7.7): a `` must carry `` **or** ``. The declared sub-flow determines the ledger’s mode. A report carrying both, or neither, is rejected.
### Validations, in order
[Section titled “Validations, in order”](#validations-in-order)
| # | Check | On failure |
| - | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | `Content-Type: application/octet-stream` | **406** — `Content-Type must be application/octet-stream` |
| 2 | Body size ≤ 100 MB (PPF’s IRR\_TAILLE limit, v3.2 §3.4.2) | **422** — `The payload exceeds the PPF file limit of 100 Mo` |
| 3 | XSD validation against the official v3.2 package | **422** with schema error details |
| 4 | API version ≥ 2026-03-02 | **422** — `DGFiP F10 import requires API version 2026-03-02 or later` |
| 5 | Account has the `dgfip` setting enabled | **422** — `The account has no enabled DGFiP e-reporting configuration` |
| 6 | `` SIREN matches the account’s company | **422** — `The SIREN (…) does not match the account's company` |
| 7 | Exactly one sub-flow (transactions or payments) | **422** — `The report carries both transactions and payments; split it into two deposits` or `The report carries neither transactions nor payments` |
The root element decides the authority: a `` with no namespace is interpreted as a DGFiP F10; any other root keeps being treated as a Verifactu ledger, so Verifactu import continues to work across all API versions.
Why we check the `` SIREN
So that one company’s declaration can’t be deposited under another company’s account. In a group with several companies, an API key with access to all accounts could otherwise, without this check, file company A’s declaration under company B’s account.
### Idempotency
[Section titled “Idempotency”](#idempotency)
Send an `Idempotency-Key` header with every deposit. If you don’t send one, we use the SHA256 of the payload, so resubmitting an identical file is also idempotent.
| Scenario | Response |
| --------------------------- | ------------------------------------------------------------------ |
| New key | **201** with the created ledger |
| Same key, same payload | **200** with the already-existing ledger — no duplicate is created |
| Same key, different payload | **409** — `Idempotency-Key already used with a different payload` |
The key is unique per account. Keys longer than 64 characters or containing non-ASCII characters are stored as a SHA256 digest; the behavior is the same.
### Deposit normalization
[Section titled “Deposit normalization”](#deposit-normalization)
Before transmitting, B2Brouter rewrites two nodes of your file:
* **``** becomes the ledger id. It’s the key the PPF uses to correlate returning CDVs; if a client-chosen value were kept, a deposit could claim the responses of a ledger belonging to another account.
* **``** becomes B2Brouter’s Accredited Platform identity (matricule with `schemeId="0238"`, `RoleCode` `WK`), since we’re the one depositing with the PPF. The AIFE has confirmed in writing that this deposit normalization is acceptable.
The rest of the file is left untouched, and **the original submission is kept exactly as we received it** as an audit attachment on the ledger.
## Submission to the PPF
[Section titled “Submission to the PPF”](#submission-to-the-ppf)
The imported ledger stays in the `new` state, and the sending cron picks it up on its first run after midnight of the day of deposit: in practice, **it’s deposited with the PPF the next day**. The transport is an SFTP deposit to the PPF (tar.gz with the F10 envelope).
The **TVA regime doesn’t come into play** for imported files: periodization by regime is a function of the native pipeline. Here the cadence is that of the deposit, and the declared period is whatever you write into the file.
## Tracking the lifecycle
[Section titled “Tracking the lifecycle”](#tracking-the-lifecycle)
### Ledger states
[Section titled “Ledger states”](#ledger-states)
| State | PPF CDV | Meaning |
| -------------- | ----------- | -------------------------------------------------------------------------------- |
| `new` | — | Imported and validated, pending deposit |
| `sent` | — | Deposited with the PPF |
| `acknowledged` | 500 — Reçue | The PPF has received the file |
| `deposited` | — | Deposit accepted by the PPF |
| `registered` | 300 | ✅ **Terminal** — declaration registered by the DGFiP |
| `refused` | 301 | ❌ **Terminal** — rejected; `error_code` and `error_description` carry the reason |
| `error` | — | Transmission error; `error_description` carries the detail |
### Webhooks (recommended)
[Section titled “Webhooks (recommended)”](#webhooks-recommended)
Subscribe to the **`ledger.state_change`** event to receive every transition without polling. It fires when entering `sent`, `error`, `acknowledged`, `deposited`, `registered` and `refused`.
```json
{
"web_hook": {
"url": "https://example.com/hooks",
"events": ["ledger.state_change"]
}
}
```
The body you receive carries the account id and the full ledger:
```json
{
"account_id": 42,
"object": {
"object": "ledger",
"id": 68916,
"state": "registered",
"mode": null,
"error_code": null,
"error_description": null,
"document_type_code": "xml.ledger.dgfip.transactions",
"sent_at": "2026-07-25T02:04:11.000Z",
"created_at": "2026-07-24T10:12:03.000Z"
}
}
```
> If two CDVs arrive close together, the already-superseded state isn’t delivered: you always receive the current state, not necessarily a delivery per transition. Check the ledger if you need to confirm the current state. For webhook signature verification, see the [webhooks guide](/en/developers/essential-guides/webhooks/).
### Checking the state
[Section titled “Checking the state”](#checking-the-state)
```shell
curl --request GET \
--url https://api-staging.b2brouter.net/ledgers/{LEDGER_ID} \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: 2026-03-02'
```
```json
{
"ledger": {
"id": 68916,
"type": "dgfip",
"state": "refused",
"error_code": "303",
"error_description": null,
"created_at": "2026-07-24T10:12:03.000Z",
"sent_at": "2026-07-25T02:04:11.000Z"
}
}
```
### Downloading the deposited file
[Section titled “Downloading the deposited file”](#downloading-the-deposited-file)
```plaintext
GET /ledgers/{LEDGER_ID}/download
```
Returns the **normalized** XML we deposited (with `` and `` rewritten), available from the moment of import. It’s the document to keep as proof of what was declared.
### Downloading the PPF response
[Section titled “Downloading the PPF response”](#downloading-the-ppf-response)
```plaintext
GET /ledgers/{LEDGER_ID}/download_response
```
Returns the CDV received from the PPF. While none has arrived, it returns **404**.
Uniform 404
All read actions are scoped to the API key and return the same 404 whether the ledger doesn’t exist or belongs to another account, so the response never reveals the existence of another account’s ledgers.
> In Sandbox mode, `download` and `download_response` return **503**: there’s no real deposit or PPF responses.
## Resources
[Section titled “Resources”](#resources)
* [DGFiP e-Invoicing and e-Reporting guide](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting/)
* [API reference](https://developer.b2brouter.net/reference)
* [Webhooks](/en/developers/essential-guides/webhooks/)
* [Support](https://www.b2brouter.net/docs/#/en/support/open-incident-report)
# Submit TicketBAI
> How to submit TicketBAI tax reports to the Basque tax authorities using the B2Brouter API.
B2Brouter provides a convenient API for submitting TicketBAI tax reports so you can easily comply with the legislation of the three Basque Tax Authorities (Araba, Bizkaia, and Gipuzkoa). Our API abstracts away the differences between them and offers you a unified interface that is fully compliant with all three Tax Authorities. You can check the status of B2Brouter as *“software garante”* in all three Tax Authorities: [Araba](https://web.araba.eus/es/hacienda/ticketbai/listado-de-software), [Bizkaia](https://www.batuz.eus/es/registro-de-software?q=b2brouter), [Gipuzkoa](https://www.gipuzkoa.eus/es/web/ogasuna/ticketbai/listado-software).
With the B2Brouter API, you can manage the submission of TicketBAIs in two ways:
* Delegate the generation of TicketBAI to B2Brouter
* Generate yourself the XML TicketBAI file
## Delegate the generation of TicketBAI to B2Brouter
[Section titled “Delegate the generation of TicketBAI to B2Brouter”](#delegate-the-generation-of-ticketbai-to-b2brouter)
This is the default and recommended way of processing tax reports. You will only have to worry about your issued invoices; we’ll take care of generating and sending the TicketBAIs.
### Configure your B2Brouter account
[Section titled “Configure your B2Brouter account”](#configure-your-b2brouter-account)
First, you need to set up the Tax Authority that has to receive your tax reports in your account configuration at B2Brouter. Please check the documentation on how to [**set up your account**](https://www.b2brouter.net/docs/#/es/set-up/setting-up-your-company), and specify the Tax Authority details in the **Tax declaration** section.
Once you have configured your tax declarations, setting up any of the Basque Tax Authorities as receivers, a TicketBAI will be generated automatically and sent to the Tax Authority for each invoice you issue via B2Brouter.
### Create issued invoices
[Section titled “Create issued invoices”](#create-issued-invoices)
Second, you create an invoice at B2Brouter using our API. You have two options:
* Create an issued invoice using a [JSON payload](https://developer.b2brouter.net/reference/create-invoice)
* Create an issued invoice by importing a [supported file format](https://developer.b2brouter.net/reference/import-invoice)
### Generate the TicketBAI associated with the issued invoice
[Section titled “Generate the TicketBAI associated with the issued invoice”](#generate-the-ticketbai-associated-with-the-issued-invoice)
If you issued automatically the invoice that you just created (by setting the parameter **send\_after\_import** in the API call), the TicketBAI corresponding to the invoice will be generated automatically and sent to the Tax Authority.
Alternatively, if you issue your invoices using other means or want more control over the issuing process, you can explicitly generate the TicketBAI corresponding to any issued invoice imported in B2Brouter by calling the [generate tax report method](https://developer.b2brouter.net/reference/generate-tax-report-from-invoice).
### Check the status of the TicketBAI
[Section titled “Check the status of the TicketBAI”](#check-the-status-of-the-ticketbai)
The process of sending tax reports to the Basque Tax Authorities is asynchronous — we generate, sign, chain, and send the TicketBAI in a background process.
You will have to check the status of the TicketBAI we send on your behalf:
* Check a [specific Tax Report](https://developer.b2brouter.net/reference/get-tax-report) by using its ID.
* Get a list of tax reports associated with an invoice ID by calling the [list tax reports method](https://developer.b2brouter.net/reference/get-tax-reports).
Because of the asynchronous nature of the tax report sending process, there is no guarantee that when you check the status it will already have the response of the Tax Authority. It usually takes a few seconds to complete the transaction. The transaction is complete if the status is **registered** or **error**. We recommend implementing an [exponential backoff strategy](https://en.wikipedia.org/wiki/Exponential_backoff) for retries.
## Generate yourself the XML TicketBAI file
[Section titled “Generate yourself the XML TicketBAI file”](#generate-yourself-the-xml-ticketbai-file)
If you prefer to generate the XML file of the TicketBAI yourself, we can process them for you (sign, chain, and send them to the corresponding Tax Authority). We can also send you back the response of the Tax Authority.
Please contact [sales](mailto:sales@b2brouter.com) if you are interested in this method of processing tax reports.
# ISO/IEC 27001
> B2Brouter holds ISO/IEC 27001 certification, the international standard for Information Security Management Systems (ISMS), covering risk assessment, controls, and data protection.
B2Brouter holds the ISO/IEC 27001 certification, the international reference standard for Information Security Management Systems.
This certification attests that we have a management framework aimed at identifying, assessing and addressing information security risks, applying appropriate controls and procedures to protect the data and services we manage.
[View ISO 27001 certificate ](/trust/iso27001-certificate-en.pdf)
[View security policy ](/en/trust-center/security-policy/)

# National Security Framework — ENS
> B2Brouter is certified under the Spanish Esquema Nacional de Seguridad (ENS), the national framework for securing information systems in electronic administration and digital services.
B2Brouter is also certified under the Esquema Nacional de Seguridad — ENS, the Spanish reference framework for ensuring the security of information systems in the context of electronic administration and digital services.
The ENS certification reinforces our commitment to implementing appropriate security measures, risk management, incident prevention and the protection of information processed in our systems.
[View the National Security Framework certificate ](/trust/27275960-ens-b2brouter-global-sl.pdf)

# Data Processing Agreement
> Data Processing Agreement
**Version:** 2.1\
**Date:** 4 August 2026\
**Entity:** B2Brouter Global, S.L.
## 1. Purpose and contractual incorporation
[Section titled “1. Purpose and contractual incorporation”](#1-purpose-and-contractual-incorporation)
This Data Processing Agreement (the “DPA”) governs the conditions under which B2Brouter Global, S.L. (“B2Brouter”) will process personal data on behalf of the customer, contracting entity, partner, integrator, reseller or third party that contracts or uses B2Brouter services (the “Client”).
This DPA applies in connection with the provision of B2Brouter, B2Bconnector, eDocSync and other associated services for the issue, receipt, transformation, transmission, retention, tracking and management of electronic invoices, electronic documents and tax reports, as well as technical integrations, support, maintenance, monitoring, security and related functionalities.
This DPA forms an integral part of the agreements, terms of service, service orders, schedules, proposals, commercial agreements or any other contractual documents that expressly incorporate it or refer to it, whenever B2Brouter processes personal data on behalf of the Client or third parties in connection with the contracted services.
No separate signature of the DPA will be required where its acceptance results from those contractual documents or from the commencement of services involving the processing of personal data on behalf of the Controller.
## 2. Roles of the Parties
[Section titled “2. Roles of the Parties”](#2-roles-of-the-parties)
As a general rule, the Client will act as controller and B2Brouter will act as processor in accordance with Article 28 of Regulation (EU) 2016/679 (the General Data Protection Regulation, the “GDPR”) and other applicable data protection legislation.
However, where the Client uses B2Brouter services to provide services to its own customers, users, group companies, recipients or third parties, including eDocSync white-label, resale, intermediation, integration, delegation, provision under the Client’s brand or services offered to the Client’s end customers, the Parties acknowledge that the Client may act, as applicable, as controller or processor in relation to those third parties.
In such cases, B2Brouter will act as processor or, where the Client itself acts as processor for a third-party controller, as sub-processor, in accordance with Article 28 of the GDPR.
For the purposes of this DPA, references to the Controller include the Client when it acts as controller and, where applicable, the third-party controller on whose behalf the Client acts. References to the Processor include B2Brouter when it acts as processor or sub-processor.
## 3. eDocSync white-label, resale or integration services
[Section titled “3. eDocSync white-label, resale or integration services”](#3-edocsync-white-label-resale-or-integration-services)
Where the Client uses B2Brouter services to provide services to third parties, including eDocSync white-label, resale, integration, intermediation, delegation or provision under the Client’s brand, the Client is responsible for obtaining the authorisations, instructions, legal bases, contracts, data processing agreements, notices and consents required in relation to its own customers, users, recipients or third parties.
The Client warrants that its agreements, terms of service, privacy policies, data processing agreements or equivalent documentation permit B2Brouter’s involvement in providing the services and, where applicable, its appointment as processor or sub-processor.
The provision of eDocSync white-label services does not release the Client from its obligations regarding transparency, information, authorisation of sub-processors, legal basis, security, handling of data subject rights and any other obligations applicable to it under data protection law.
Where B2Brouter acts as sub-processor, the Client is responsible for passing on to B2Brouter only lawful instructions that are compatible with the instructions received from the relevant controller.
## 4. Nature, purpose and duration of the processing
[Section titled “4. Nature, purpose and duration of the processing”](#4-nature-purpose-and-duration-of-the-processing)
The nature and purpose of the processing, the categories of personal data and data subjects, and the other details of the processing are described in Annex I --- Details of the Processing.
The processing will continue for the duration of the contractual relationship between B2Brouter and the Client, unless certain data must be retained in restricted form for a longer period in order to comply with legal, tax, accounting or regulatory obligations, or for the establishment, exercise or defence of legal claims.
Once the services have ended, B2Brouter will return, delete or make available to the Client the personal data processed on its behalf, in accordance with the main agreement, this DPA and applicable law.
## 5. Instructions of the Controller
[Section titled “5. Instructions of the Controller”](#5-instructions-of-the-controller)
B2Brouter will process personal data only on documented instructions from the Client.
The following will be considered documented instructions from the Client:
* the main agreement entered into or accepted by the Parties;
* this DPA;
* the applicable schedules, service orders, specific terms or contractual documents;
* the settings configured by the Client on the platform;
* instructions provided by the Client through B2Brouter’s official support, integration or communication channels;
* instructions arising from the ordinary use of the contracted services.
B2Brouter will not use personal data for its own purposes or for purposes other than providing the services, unless required to do so by Union or Member State law. In that case, B2Brouter will inform the Controller of that legal requirement before the processing, unless the applicable law prohibits such information on important grounds of public interest.
If B2Brouter considers that an instruction from the Client infringes applicable data protection legislation, it will inform the Client, unless applicable law prohibits such communication.
## 6. Obligations of the Client and the Controller
[Section titled “6. Obligations of the Client and the Controller”](#6-obligations-of-the-client-and-the-controller)
The Client and, where applicable, the Controller are responsible for:
* ensuring that the processing of personal data has a valid legal basis;
* providing information to data subjects in accordance with applicable law;
* ensuring the accuracy, lawfulness, currency, relevance and minimisation of personal data provided or managed through the services;
* obtaining, where applicable, the necessary consents, authorisations, mandates, instructions or legal bases;
* handling data subject rights, except where the reasonable assistance of B2Brouter is required;
* determining the purposes and essential means of the processing;
* ensuring that the instructions given to B2Brouter are lawful and comply with applicable law;
* carrying out, where applicable, data protection impact assessments, risk assessments or prior consultations with the competent supervisory authority;
* ensuring that personal data entered, uploaded, transmitted or managed through the services does not include special categories of personal data unless there is a sufficient legal basis and, where applicable, appropriate additional measures have been agreed;
* ensuring, in eDocSync white-label, resale, integration or intermediation scenarios, that it has sufficient authorisation to appoint B2Brouter as processor or sub-processor.
B2Brouter will not be responsible for any absence, insufficiency or inaccuracy of the legal basis, information, authorisation, consent, mandate, instruction or documentation for which the Client or controller is responsible.
## 7. Obligations of B2Brouter
[Section titled “7. Obligations of B2Brouter”](#7-obligations-of-b2brouter)
B2Brouter undertakes to:
* process personal data only in accordance with the documented instructions of the Client;
* not apply or use personal data for purposes other than those set out in the agreement and this DPA;
* not disclose personal data to third parties except as provided in this DPA or the agreement, on the Client’s instructions or where required by law;
* ensure that persons authorised to process personal data are subject to appropriate confidentiality obligations;
* implement appropriate technical and organisational measures to ensure a level of security appropriate to the risk;
* reasonably assist the Client in complying with its data protection obligations, on the terms set out in this DPA;
* notify the Client of personal data breaches of which it becomes aware, in accordance with this DPA;
* make available to the Client the information reasonably necessary to demonstrate compliance with the obligations under Article 28 of the GDPR;
* delete, return or make available the personal data at the end of the services, in accordance with this DPA;
* impose on its sub-processors the same data protection obligations as those set out in this DPA, in accordance with Article 28(4) of the GDPR.
## 8. Confidentiality
[Section titled “8. Confidentiality”](#8-confidentiality)
B2Brouter will ensure that persons authorised to process personal data are subject to appropriate confidentiality obligations.
The confidentiality obligation will continue after the contractual relationship between the Parties ends.
B2Brouter will take reasonable measures to limit access to personal data to personnel, contractors, providers, group companies or sub-processors that need access to those data in order to provide the services.
The Client must preserve the confidentiality of the documentation, security measures, internal processes, technical information, trade secrets, reports, evidence or compliance documentation provided by B2Brouter under this DPA.
## 9. Technical and organisational measures
[Section titled “9. Technical and organisational measures”](#9-technical-and-organisational-measures)
B2Brouter will implement appropriate technical and organisational measures to protect personal data against accidental or unlawful destruction, loss, alteration, unauthorised disclosure of, or access to, personal data.
Such measures may include, among other things, access controls, authentication mechanisms, encryption of communications, logical segregation, backups, monitoring, incident management, activity logging, internal security policies, service continuity measures and recovery procedures.
The applicable technical and organisational measures are described in [Technical and Organisational Measures / TOMs](/en/trust-center/gdpr/tom).
The Client acknowledges that the security measures may evolve during the term of the agreement, provided that such updates do not materially reduce the agreed level of security.
Specific availability, support, continuity, RTO, RPO or service-level objectives will be governed, where applicable, by the main agreement, the applicable SLA or specific terms agreed with the Client.
## 10. Sub-processors
[Section titled “10. Sub-processors”](#10-sub-processors)
The Controller grants B2Brouter a general authorisation to engage sub-processors where necessary to provide the services, provided that those sub-processors are subject to data protection obligations substantially equivalent to those set out in this DPA.
The list of authorised sub-processors is included in Annex II --- Sub-processors or will be made available to the Client through a website, contractual notice, compliance documentation or any other reasonably accessible means.
B2Brouter will inform the Client of any significant addition or replacement of sub-processors with reasonable prior notice where such notification is required under applicable law.
The Client may reasonably object to the engagement of a new sub-processor where there are substantiated grounds relating to the protection of personal data. The objection must be made in writing within the period specified by B2Brouter or, if no period is specified, within ten (10) business days after the notification.
If the Client’s objection is reasonable and cannot be resolved through alternative measures, the Parties will negotiate a solution in good faith. If no solution can be reached, the Client may terminate the affected services, without prejudice to amounts accrued up to the effective termination date.
B2Brouter will remain responsible to the Client for compliance by its sub-processors with their data protection obligations, on the terms established in Article 28 of the GDPR.
## 11. B2Brouter / team.blue group companies
[Section titled “11. B2Brouter / team.blue group companies”](#11-b2brouter--teamblue-group-companies)
B2Brouter may engage companies belonging to its corporate group, including, where applicable, team.blue group companies or affiliates, to provide internal, technical, administrative, security, support, infrastructure, monitoring, corporate management or ancillary services necessary for the provision, maintenance, security or improvement of the contracted services.
Where those entities access or process personal data on behalf of B2Brouter in connection with the services provided to the Client, they will act as sub-processors and will be subject to data protection obligations substantially equivalent to those set out in this DPA, in accordance with Article 28 of the GDPR.
The specific group companies acting as sub-processors and the services they provide will be identified in the applicable list of sub-processors where required by data protection law.
B2Brouter’s membership of a corporate group does not, in itself, mean that all group companies have access to personal data processed on behalf of the Client.
## 12. International transfers
[Section titled “12. International transfers”](#12-international-transfers)
B2Brouter will not transfer personal data internationally outside the European Economic Area unless:
* an applicable adequacy decision is in place;
* appropriate safeguards have been adopted in accordance with the GDPR, including, where applicable, standard contractual clauses approved by the European Commission;
* a derogation under data protection law applies;
* there is a documented instruction from the Client; or
* there is an applicable legal obligation.
Where necessary, B2Brouter will adopt reasonable supplementary measures to ensure an adequate level of protection in accordance with applicable law.
## 13. Personal data breaches
[Section titled “13. Personal data breaches”](#13-personal-data-breaches)
B2Brouter will notify the Client without undue delay of any personal data breach of which it becomes aware and which affects personal data processed on behalf of the Client.
B2Brouter will endeavour to provide the initial notification within forty-eight (48) hours after becoming aware of the breach.
The notification will include, to the extent that the information is available:
* the nature of the personal data breach;
* the categories and approximate number of affected data subjects, where possible;
* the categories and approximate number of affected personal data records, where possible;
* the likely consequences of the personal data breach;
* the measures taken or proposed to remedy or mitigate its effects;
* the contact point from which further information can be obtained.
B2Brouter may provide this information in phases where all details are not available at the time of the initial notification.
The Client is responsible for determining whether the personal data breach must be notified to the supervisory authority or the data subjects, unless B2Brouter is legally required to make a direct notification.
Communications relating to personal data breaches will be made through the contact channels designated by the Parties. For these purposes, B2Brouter may use the contact email address provided by the Client in the agreement, on the platform or through official support channels.
The Client may contact B2Brouter regarding data protection matters or personal data breaches at or , or at any other address that B2Brouter communicates or publishes for that purpose.
## 14. Assistance to the Client and data subject rights
[Section titled “14. Assistance to the Client and data subject rights”](#14-assistance-to-the-client-and-data-subject-rights)
Taking into account the nature of the processing and the information available, B2Brouter will reasonably assist the Client in complying with its data protection obligations, particularly regarding:
* the handling of rights of access, rectification, erasure, objection, restriction, portability and rights relating to automated individual decision-making;
* security of processing;
* management of personal data breaches;
* data protection impact assessments;
* prior consultations with supervisory authorities, where applicable.
If B2Brouter directly receives a request from a data subject to exercise rights in relation to personal data processed on behalf of the Client, it will inform the Client, unless legally prohibited from doing so.
B2Brouter will not respond directly to the request unless instructed to do so by the Client or required by applicable law.
Where the requested assistance exceeds the ordinary support included in the contracted services, B2Brouter may charge the Client the reasonable costs arising from that assistance, after informing the Client.
## 15. Audits and verification of compliance
[Section titled “15. Audits and verification of compliance”](#15-audits-and-verification-of-compliance)
B2Brouter will make available to the Client the information reasonably necessary to demonstrate compliance with the obligations under Article 28 of the GDPR.
Where possible, verification will be carried out by providing security documentation, certifications, audit reports, compliance questionnaires, internal policies, reasonable evidence or equivalent documentation.
Any on-site, technical or documentary audit must:
* be requested at least thirty (30) days in advance;
* be limited to the scope strictly necessary to verify compliance with this DPA;
* be conducted during B2Brouter’s normal business hours;
* not materially interfere with the provision of the services;
* be subject to strict confidentiality obligations;
* not compromise the security, availability or integrity of B2Brouter’s systems, other clients or third parties.
Unless required by law or by a competent authority, or justified by a security incident, the Client may not conduct more than one audit per calendar year.
Audits may not involve access to source code, trade secrets, confidential information of other clients, third-party data, shared infrastructure, sensitive internal documentation, detailed security architectures or any information whose disclosure could compromise the security of B2Brouter, its clients or its providers.
Penetration tests, vulnerability scans, intrusive technical analyses or any testing of B2Brouter systems require B2Brouter’s prior written authorisation and acceptance of the applicable technical and security conditions.
Where an audit, review or verification request requires dedicated B2Brouter personnel, technical or legal assistance, additional meetings, preparation of ad hoc documentation, support during the audit, involvement of third parties or any other activity beyond making standard compliance documentation available, B2Brouter may charge the Client the reasonable costs arising from such assistance, after prior notice to the Client and, where applicable, acceptance of the corresponding quotation.
## 16. Return, deletion and retention
[Section titled “16. Return, deletion and retention”](#16-return-deletion-and-retention)
At the end of the services, B2Brouter will, at the Controller’s choice and unless retention is required by law, return, delete or make available to the Client the personal data processed on its behalf.
B2Brouter may retain personal data in duly restricted form where necessary to comply with legal, tax, accounting or regulatory obligations, or for the establishment, exercise or defence of legal claims. During that period, the data will not be used for purposes incompatible with the grounds justifying their retention.
Once the applicable statutory periods have expired, B2Brouter will permanently delete the personal data, unless another legal basis justifies their retention.
The return or export of data will be carried out in the formats, within the periods and under the conditions set out in the main agreement, on the platform or in applicable law. Commercial commitments relating to archiving, post-termination access and portability will, where applicable, be governed by a separate addendum to the main agreement.
## 17. Liability
[Section titled “17. Liability”](#17-liability)
Each Party is responsible for complying with the obligations applicable to it under data protection law.
The Client is responsible for the lawfulness of the processing, the accuracy of the data, the information provided to data subjects, the existence of a valid legal basis, the instructions given to B2Brouter and, where applicable, having sufficient authorisation to appoint B2Brouter as processor or sub-processor.
B2Brouter is responsible for complying with its obligations as processor or sub-processor on the terms set out in the GDPR, the main agreement and this DPA.
The Parties’ liability will be governed by the main agreement, without prejudice to any liability that cannot be limited under applicable law.
## 18. Updates, version control and sub-processors
[Section titled “18. Updates, version control and sub-processors”](#18-updates-version-control-and-sub-processors)
B2Brouter may update this DPA, the TOMs and the list of sub-processors where necessary to reflect legal, technical, organisational, security, infrastructure, provider or service-related changes.
B2Brouter will inform the Client of material changes where required by applicable law or where those changes may materially affect the processing of personal data on behalf of the Client.
Updates will not materially reduce the level of protection, security or safeguards applicable to the processing of personal data.
## 19. Order of precedence
[Section titled “19. Order of precedence”](#19-order-of-precedence)
In the event of a conflict between this DPA and the main agreement on matters specifically relating to the processing of personal data on behalf of the Client, this DPA will prevail.
In the event of a conflict concerning the technical and organisational security measures applicable to the processing of personal data, the TOMs will prevail, unless the main agreement expressly establishes a more specific or restrictive obligation.
In all other matters, the main agreement will prevail, unless the Parties expressly agree otherwise in writing.
20\. Term and acceptance
This DPA forms an integral part of the General Terms and Conditions, agreements, service orders, proposals or other contractual documents that expressly incorporate it or refer to it.
The DPA applies from the date on which the Client accepts the General Terms and Conditions or the contractual document incorporating it, or from the time B2Brouter begins processing personal data on behalf of the Client, whichever occurs first. No separate signature of this DPA is required.
B2Brouter will make the current version of the DPA available on its website, clearly identified by its version number and publication date. Updates will be governed by Article 18.
The DPA will remain in force for as long as B2Brouter processes personal data on behalf of the Client, whether acting as processor or sub-processor.
Obligations which, by their nature, are intended to survive the end of the contractual relationship, including confidentiality, security, return, deletion or restriction of data, cooperation on data protection matters and liability, will remain in force for the applicable statutory periods.
## 21. Governing law and jurisdiction
[Section titled “21. Governing law and jurisdiction”](#21-governing-law-and-jurisdiction)
This DPA is governed by Spanish law and applicable European Union data protection legislation.
Any dispute arising from this DPA will be submitted to the jurisdiction specified in the main agreement, unless applicable law establishes another mandatory jurisdiction.
# ANNEX I DETAILS OF THE PROCESSING
[Section titled “ANNEX I DETAILS OF THE PROCESSING”](#annex-i-details-of-the-processing)
## 1. Controller
[Section titled “1. Controller”](#1-controller)
The Client or, in eDocSync white-label, resale, integration or intermediation scenarios, the third-party controller on whose behalf the Client acts.
## 2. Processor
[Section titled “2. Processor”](#2-processor)
B2Brouter Global, S.L., where it processes personal data on behalf of the Client acting as controller.
Where the Client acts as processor for a third-party controller, B2Brouter will act as sub-processor.
## 3. Subject matter of the processing
[Section titled “3. Subject matter of the processing”](#3-subject-matter-of-the-processing)
Provision of technological services for the issue, receipt, transformation, transmission, retention, tracking and management of electronic invoices, electronic documents, tax reports and related communications, as well as integration, support, maintenance, security, monitoring and associated functionalities.
## 4. Duration of the processing
[Section titled “4. Duration of the processing”](#4-duration-of-the-processing)
The duration of the processing will coincide with the term of the main agreement and, where applicable, any additional periods required for the retention, restriction, return or deletion of the data in accordance with applicable law.
## 5. Nature of the processing
[Section titled “5. Nature of the processing”](#5-nature-of-the-processing)
The processing may include, among other operations:
* access;
* recording;
* storage;
* structuring;
* retention;
* consultation;
* transmission;
* disclosure to recipients or authorities, where applicable;
* format conversion;
* technical validation;
* generation of evidence or records;
* support and incident resolution;
* monitoring and security;
* deletion, restriction or return.
## 6. Purpose of the processing
[Section titled “6. Purpose of the processing”](#6-purpose-of-the-processing)
The purpose of the processing is to enable the provision of the services contracted by the Client, including the management of electronic invoices, electronic documents, tax reports, associated communications, technical integrations, support, security, traceability, retention and compliance with legal or regulatory obligations related to the services.
## 7. Categories of data subjects
[Section titled “7. Categories of data subjects”](#7-categories-of-data-subjects)
The categories of data subjects whose personal data may be processed in connection with the services include, depending on the Client’s use of the platform:
* authorised users of the Client;
* employees, representatives, directors, contractors or contact persons of the Client;
* customers, suppliers, recipients or issuers of invoices, electronic documents or communications managed through the services;
* legal representatives, signatories, contact persons or other interlocutors included in invoices, electronic documents, tax reports or communications;
* third parties whose data may appear in documents, schedules, invoices, communications or files uploaded, transmitted or managed by the Client through the services;
* users, employees, representatives, customers, suppliers, recipients, invoice issuers, contact persons or third parties associated with the Client’s end customers, particularly where the services are provided as eDocSync white-label, resale, integration or intermediation services.
## 8. Categories of personal data
[Section titled “8. Categories of personal data”](#8-categories-of-personal-data)
The categories of personal data may include, as applicable:
* identification data, including first name, surname, corporate name, NIF, NIE, VAT number, SIREN and equivalent tax identifiers;
* contact data, including postal address, email address, telephone number and contact person;
* professional data, including position, department, company, function or user role;
* financial, banking, tax, accounting or transactional data included in invoices, electronic documents, tax reports or related communications;
* platform access and usage data, including user identifiers, IP addresses, activity records, logs, timestamps and technical metadata;
* data included in invoices, schedules, documents, files or communications uploaded, generated, transmitted or managed by the Client;
* signature data, certificates, evidence, technical identifiers or metadata where necessary to provide the service.
The Client is not expected to enter special categories of personal data into the services. If the Client enters such categories of data, it is responsible for ensuring that a valid legal basis exists and for informing B2Brouter in advance where additional measures must be adopted.
## 9. Special processing activities
[Section titled “9. Special processing activities”](#9-special-processing-activities)
B2Brouter does not carry out, on behalf of the Controller, automated individual decision-making that produces legal effects or similarly significantly affects data subjects.
# ANNEX II SUB-PROCESSORS
[Section titled “ANNEX II SUB-PROCESSORS”](#annex-ii-sub-processors)
Tools used by B2Brouter for commercial management, CRM, quotation preparation, prospecting, corporate marketing or management of the contractual relationship with its own clients will not be considered sub-processors for the purposes of this DPA where they do not involve processing personal data on behalf of the Client in connection with the contracted services.
In such cases, B2Brouter will act as controller in respect of those data and the providers used for those purposes will, where applicable, act as processors for B2Brouter in accordance with applicable law.
The following list identifies the sub-processors that may be involved in providing the services where they have access to or process personal data on behalf of B2Brouter.
| Corporate name | Country of establishment | Service provided | Location / processing |
| --------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| **Hetzner Online GmbH** | Germany | Infrastructure, hosting, servers, backups or associated technical services. | Germany / European Union. |
| **Scaleway SAS** | France | Contingency infrastructure, recovery, backups, DRP or associated technical services. | France / European Union. |
| **Amazon Web Services EMEA SARL / AWS** | Luxembourg | Storage, archiving, backups, cloud services or associated technical services, where applicable. | Germany and France / European Union |
| **Team.blue NV** | Belgium | Internal, technical, administrative, security, support, monitoring, corporate management or ancillary services necessary for the provision of the services. | Belgium / European Union |
B2Brouter’s membership of a corporate group does not mean that all group companies have access to personal data processed on behalf of the Client. Only entities that actually process or may access personal data on behalf of B2Brouter in connection with the provision of the services will be considered sub-processors.
B2Brouter may update this list of sub-processors in accordance with this DPA.
# Technical and organizational measures - TOM
> Data Processing Agreement
**Version:** 2.1\
**Date:** 4 August 2026\
**Entity:** B2Brouter Global, S.L.
B2Brouter will implement appropriate technical and organisational measures to ensure a level of security appropriate to the risk, taking into account the state of the art, implementation costs, the nature, scope, context and purposes of the processing, and the risks to the rights and freedoms of natural persons.
The measures described below may be updated during the term of the services, provided that they do not materially reduce the applicable level of security.
## 1. Security governance
[Section titled “1. Security governance”](#1-security-governance)
B2Brouter maintains internal policies, procedures and controls designed to protect the confidentiality, integrity, availability and resilience of the systems and services used to process personal data.
Internal responsibilities for security, data protection, incident management and service continuity are assigned to the relevant B2Brouter teams.
## 2. Access control
[Section titled “2. Access control”](#2-access-control)
B2Brouter applies controls designed to limit access to systems, data and environments to authorised persons only and on the basis of legitimate needs related to the provision of the services.
These measures may include:
* user and permission management;
* privilege control;
* authentication using personal credentials;
* access reviews;
* revocation of access when it is no longer required;
* segregation of duties, where applicable.
## 3. Personnel confidentiality
[Section titled “3. Personnel confidentiality”](#3-personnel-confidentiality)
Personnel authorised to access personal data are subject to appropriate confidentiality obligations.
B2Brouter will take reasonable measures to raise awareness and train personnel involved in providing the services regarding their security and data protection obligations.
## 4. Communications security and encryption
[Section titled “4. Communications security and encryption”](#4-communications-security-and-encryption)
B2Brouter uses measures designed to protect communications and data transmissions against unauthorised access, alteration or improper disclosure.
These measures protect communications and data transmissions through secure channels, certificates and protocols appropriate to the nature of the service.
## 5. Infrastructure and hosting security
[Section titled “5. Infrastructure and hosting security”](#5-infrastructure-and-hosting-security)
B2Brouter provides its services through its own infrastructure or that of specialised providers located in Germany and France, within the European Union, unless otherwise notified and the safeguards required by applicable law are adopted.
The infrastructure providers used by B2Brouter must be subject to appropriate contractual obligations regarding security, confidentiality and data protection.
The specific location of relevant infrastructure and providers will be identified in the list of sub-processors or applicable technical documentation.
## 6. Logical segregation
[Section titled “6. Logical segregation”](#6-logical-segregation)
B2Brouter applies measures designed to maintain appropriate logical segregation between clients, users, environments, data and services in order to prevent unauthorised access or unauthorised processing.
## 7. Backups
[Section titled “7. Backups”](#7-backups)
B2Brouter maintains backup procedures designed to preserve the availability and integrity of data and enable recovery following technical or physical incidents.
Backups are managed in accordance with defined internal procedures, with controls designed to protect information against loss, destruction, alteration or unauthorised access.
## 8. Continuity, recovery and resilience
[Section titled “8. Continuity, recovery and resilience”](#8-continuity-recovery-and-resilience)
B2Brouter maintains service continuity and incident recovery procedures designed to preserve the availability, integrity and resilience of systems and services.
These measures may include restoration procedures, monitoring, incident management, internal escalation and recovery following technical or physical incidents.
Specific recovery objectives, including RTO and RPO where applicable, will be established in the main agreement, SLA, specific terms or technical documentation applicable to the contracted service, provided that they have been expressly agreed with the Client.
## 9. Logging, traceability and monitoring
[Section titled “9. Logging, traceability and monitoring”](#9-logging-traceability-and-monitoring)
B2Brouter maintains technical records, logs, timestamps, activity evidence and monitoring mechanisms intended to ensure the security, traceability, operation, support and technical audit of the services.
Access is limited to authorised personnel, and retention is limited to the periods necessary for those purposes, in accordance with the documented retention periods applicable to each category of record.
## 10. Incident management
[Section titled “10. Incident management”](#10-incident-management)
B2Brouter maintains procedures to identify, analyse, escalate, manage and resolve technical or security incidents that may affect the services.
Where an incident constitutes a personal data breach under the GDPR, this DPA will apply.
## 11. Vulnerability management
[Section titled “11. Vulnerability management”](#11-vulnerability-management)
B2Brouter implements measures designed to identify, assess and manage technical vulnerabilities that may affect the security of systems and services.
These measures may include technical reviews, updates, patches, monitoring, risk assessments and internal remediation procedures.
## 12. Secure development
[Section titled “12. Secure development”](#12-secure-development)
B2Brouter will apply reasonable secure development, deployment and maintenance practices in relation to the services, taking into account the nature and complexity of the platform.
These practices may include change reviews, version control, separation of environments, testing, technical validation and deployment procedures.
## 13. Provider management
[Section titled “13. Provider management”](#13-provider-management)
B2Brouter will assess and select providers and sub-processors taking into account reasonable criteria regarding security, confidentiality, availability, regulatory compliance and data protection.
Providers that process personal data on behalf of B2Brouter will be subject to appropriate contractual obligations under Article 28 of the GDPR when acting as sub-processors.
## 14. Testing, technical audits and penetration testing
[Section titled “14. Testing, technical audits and penetration testing”](#14-testing-technical-audits-and-penetration-testing)
Penetration tests, vulnerability scans, intrusive technical analyses or any testing of B2Brouter systems require B2Brouter’s prior written authorisation.
B2Brouter may establish technical conditions, time windows, scope limitations and coordination measures to prevent impacts on the availability, security, integrity or continuity of the services.
## 15. Updates to measures
[Section titled “15. Updates to measures”](#15-updates-to-measures)
B2Brouter may modify, replace or update the technical and organisational measures described in this Annex where necessary to reflect technical, legal, organisational or security changes.
Such updates will not materially reduce the level of protection applicable to the processing of personal data.
# B2Brouter trust center
> Compliance and certifications
At B2Brouter, information security and data protection are an essential part of our service. As an electronic invoicing and document exchange platform, we work to provide our customers with a secure, reliable environment aligned with the main applicable security and compliance standards. Our commitment is based on the protection of information, service continuity, continuous improvement and transparency with our customers and partners.
#### Our commitment to security
[Section titled “Our commitment to security”](#our-commitment-to-security)
We apply technical and organisational measures aimed at ensuring the confidentiality, integrity, availability, traceability and authenticity of the information processed through our platform. This commitment is reflected in our internal processes, risk management, system protection and the ongoing review of our security measures.
#### Continuous improvement
[Section titled “Continuous improvement”](#continuous-improvement)
Security is an ongoing process. We therefore periodically review, improve and audit our policies, controls and procedures to adapt to new risks, regulatory requirements and our customers' needs. Our goal is to provide a secure, transparent and trustworthy service, maintaining the standards required by our customers, partners and regulatory bodies.
#### Certifications and regulatory compliance
[Section titled “Certifications and regulatory compliance”](#certifications-and-regulatory-compliance)
To reinforce our commitment to security and compliance, B2Brouter holds recognised certifications in the field of information security.
[](/trust/iso27001-certificate-en.pdf)[](/trust/27275960-ens-b2brouter-global-sl.pdf)
[ISO/IEC 27001 ](/en/trust-center/certifications/iso-iec-27001/)
[National Security Framework ](/en/trust-center/certifications/national-security-framework/)
If you need additional information about our certifications, security policies or compliance documentation, please contact our team.
# Security and privacy policy
> Política de seguridad ISO27001
## Objectives
[Section titled “Objectives”](#objectives)
The objectives of implementing an information security and privacy management policy are:
* To ensure the business continuity of the B2Brouter services and activities.
* To protect the information assets of the company.
* To prevent data loss for the users of the B2Brouter services.
* To enhance the audit and control of the security incidents.
* To train staff on security issues and how to address them.
* To grow the organization based on its security procedures and policies and their continuous improvement.
* Ensure the privacy of the personal data processed.
## Policy
[Section titled “Policy”](#policy)
* The policy’s goal is to protect the organization’s informational assets against all internal, external, deliberate or accidental threats.
* CEO has approved the Information Security and Privacy Policy.
* This security and privacy policy ensures that:
* Information will be protected against any unauthorized access.
* Confidentiality of information will be assured. Information can be access only those who have the authorization.
* Integrity of information will be maintained.
* Availability to ensure that authorized users have access to the information and applications when needed.
* Legislative and regulatory requirements will be met.
* Continuity plans will be developed, maintained and tested.
* Information Security and Privacy Policy training will be available for all employees and contractors.
* Procedures exist to support the policy, including continuity plans.
* The Chief Information Security Officer is responsible for maintaining the policy and providing support and advice during its implementation.
* Compliance with the Information Security Policy and Privacy Policy is mandatory.
The policy will be reviewed yearly by the Chief Information Security Officer and the Chief Executive Officer
The Security policy was last updated on September 23, 2024.
Signature: Oriol Bausà i Peris Title: Chief Executive Officer (CEO)
# Set up your account
> Learn how to configure your company in B2Brouter, including fiscal data, Peppol, taxes, bank accounts, users and settings.
## General data
[Section titled “General data”](#general-data)
Once you have registered and logged in to your account, you will see a screen where you have to add your company’s tax details:
* In the **Country** dropdown, select your company’s country.
* In the **NIF** box, enter your company’s NIF.
* Finally, use the option **Validate identifier and continue**.
Next, you access step 2 **Fiscal data**, where you can add your company’s fiscal data (name, address, postcode, etc). Once you have filled in the details, use the **Next** option.
Finally, go to step 3 **Contact person**, where you must add the details of a contact person in your company. Finally, use the **Save** option.
As a result, your B2Brouter account is created and the main menu is activated. Go back to your company settings using the gear icon at the top of the screen and select **Account Settings**:
On the left hand side, you find a submenu with the different sections that allow you to configure other aspects of your account.

#### Fiscal data and logotype
[Section titled “Fiscal data and logotype”](#fiscal-data-and-logotype)
From this section, you can add or modify your company’s fiscal data and logo.
#### Identity certification
[Section titled “Identity certification”](#identity-certification)
In order to issue an electronic invoice from B2Brouter you have to verify your identity using your company’s digital certificate.
Check here how you can [Verify your identity](/en/user-guide/account-settings/verifying-identity).
#### Business registry data
[Section titled “Business registry data”](#business-registry-data)
From this section you can add your company’s commercial register by filling in the available boxes.
#### Contact person
[Section titled “Contact person”](#contact-person)
From this section you can manage the details of the contact person for your account.
#### Fiscal year
[Section titled “Fiscal year”](#fiscal-year)
From this section you can select the start date of the fiscal year.
The fiscal year is the period in which taxes are reported. In case you need to change the start month of your fiscal year, you can select a month that does not coincide with the calendar year.
* In the **Start month of tax year** field, enter the starting month of your tax period.
* Use the **Save** button to save your changes.
Invoices dated within a closed fiscal year cannot be sent.
You can access, view, edit and delete them from the **Income** section.
## Connections
[Section titled “Connections”](#connections)
#### Connections
[Section titled “Connections”](#connections-1)
From this section you can configure the different connections you wish to use to send or receive electronic documents.

* **B2Brouter**: Configure the email connection with link inside the application. If you activate this option and your suppliers also have a B2Brouter account, you will receive invoices directly in the portal.
* **Email:** Configure your outgoing email server. As a result, the invoices you send via email are sent from your company’s email server instead of the @b2brouter server. You can also set up to receive invoices to B2Brouter via email.
#### Configure the Email connection:
[Section titled “Configure the Email connection:”](#configure-the-email-connection)
Click on the **New connection** option under the **Email** icon so you can configure these two options:
**Receipt of invoices in B2Brouter via email: Check the Email box.**
* Select the box **Email**.
* In the text below, you find your B2Brouter email address where your suppliers have to send you invoices.
* This email is: EN “YourTAXID”@b2brouter.email.
**Sending invoices from B2Brouter using your company’s mail server:**
* Activate the option **Customise SMTP server**.
* Fill in the form with the requested information of your server data.
Once the configuration is complete, use the **Save** button on the bottom right.
* **General Points of Entry - Spain:** Configure the connection to those Spanish General Points of Entry that require it.
* **Edicom network**: Configure the Edicom connection for the issuance of electronic documents using this network.
* **Ediversa network**: Configures the Ediversa connection for the issuance of electronic documents using this network.
* **SDI (System Switching Interchange) connection - Italy:** Configure the SDI connection if your company is Italian and you need to send or receive electronic documents.
## Peppol
[Section titled “Peppol”](#peppol)
#### Activation of Peppol
[Section titled “Activation of Peppol”](#activation-of-peppol)
The Peppol network is an international network that allows you to exchange electronic documents (invoices, self-invoices, orders and invoice status changes) securely between companies worldwide. Before you can send or receive documents through Peppol from B2Brouter, you need to activate Peppol.

**Important** Peppol activation is an identity verification process designed to ensure network security and prevent illegal activities. This process only needs to be done once.
##### Prerequisites
[Section titled “Prerequisites”](#prerequisites)
Before starting the activation, make sure you have the following at hand:
* An official document proving your company’s identity (for example, articles of incorporation, VAT number, or equivalent document depending on the country).
* Access to your account settings in B2Brouter.
##### Steps to activate Peppol
[Section titled “Steps to activate Peppol”](#steps-to-activate-peppol)
1. Go to **Account Settings** using the gear icon  at the top of the screen.
2. In the left-hand menu, select **Peppol**.
3. **Select the identifier** you wish to use. Here you can check the status of the identity validation associated with this identifier, its status on the Peppol network and its reception and sending capabilities.
4. Click **Validate identity and activate Peppol** to start the process.
5. Attach the document proving your company’s identity using the **Select files** button.
6. **Enable reception** if you wish. You will be able to select which type of documents you want to receive.
7. **Read the Peppol** service agreement and, once reviewed, check the acceptance box.
8. Click **Send validation and activate Peppol** to complete the process.
#### Migrating from another provider to B2Brouter
[Section titled “Migrating from another provider to B2Brouter”](#migrating-from-another-provider-to-b2brouter)
If your identifier is registered with another Peppol provider, after clicking “Send validation and activate Peppol”, you will see the option to migrate or not to B2Brouter.
The migration options are as follows:
1. **Migration code:** Enter the migration code provided by your current provider. This is the recommended option to avoid losing capabilities during the migration period.
2. **Continue without code:** Ask your provider to unpublish your company from Peppol. Once unpublished, when you access the Peppol page, you will see the updated data. Likewise, if you have obtained the migration code, you will have the option to enter it.
**Option not to migrate** You have the option of not migrating to B2Brouter and keeping the current management with your provider, using B2Brouter only to send documents (unless you configure the SMP of your current provider). If you choose this option, you can migrate later if you wish.
#### After sending the validation
[Section titled “After sending the validation”](#after-sending-the-validation)
Once the request has been submitted, the B2Brouter support team will review the documentation. In the meantime, you can start using Peppol to send invoices.
To set up your customers’ Peppol identifier or find out how routing works, see [What is the Peppol identifier](/en/user-guide/routing-codes/peppol-identifier).
**What happens if the validation is rejected** If the validation is rejected, the Peppol transport will no longer be available in your account. You will receive a notification from the support team with instructions on how to complete the validation again.
#### Document reception and formats
[Section titled “Document reception and formats”](#document-reception-and-formats)
When your company is published on the Peppol network with reception capabilities, you will be able to see the formats you accept in the blue box on the side panel. Formats are set by default according to the country of your identifier and the type of document you have selected. To disable reception, uncheck all document type boxes.
#### Other options
[Section titled “Other options”](#other-options)
In the **More options** dropdown you can find detailed information about the status of your identifier on the Peppol network:
* **Provider:** Name and email address of the Peppol access provider assigned to your account. This is the entity responsible for managing your connection to the network.
* **SML status:** The SML (Service Metadata Locator) is the central directory of the Peppol network that allows your company to be located. If it is **published**, your identifier is findable on the network and documents can be correctly routed to you.
* **SMP status:** The SMP (Service Metadata Publisher) contains information about the types of documents you can receive and how. If it is **published**, your reception capabilities are visible to other network participants.
* **Peppol Directory status:** Indicates whether your company is published in the [Peppol Directory](https://directory.peppol.eu), the public catalogue where businesses can find you as a Peppol-connected provider.
##### Peppol Directory
[Section titled “Peppol Directory”](#peppol-directory)
The Peppol Directory is a public catalogue where companies can search for and find providers connected to the Peppol network. Being published makes it easier for your current and potential customers to identify you as a company ready for electronic invoicing.
If needed, you can unpublish your company from the Peppol Directory using the **Unpublish from Peppol Directory** option. Keep in mind that by unpublishing:
* You will remain connected to the Peppol network and will be able to continue sending and receiving invoices.
* Your company will not appear in public directory searches.
* Your customers will need to know your Peppol identifier to send you invoices.
**Tip:** We recommend not unpublishing from the directory unless necessary. If you do, you can republish at any time.
##### Unpublish and migration code
[Section titled “Unpublish and migration code”](#unpublish-and-migration-code)
Next to the main status you will find a three-dot menu ⋮ with additional actions:
* **Unpublish:** Unpublishes your company from the SMP, which is equivalent to disabling document reception through Peppol. Your company will no longer be findable on the network and you will not be able to receive invoices or other documents until you reactivate reception.
* **Request migration code:** Generates a code you can use to migrate your Peppol identifier to another access provider. Use this option only if you plan to change your Peppol provider. Once the migration code has been requested, the service will continue to operate normally until you complete the process by entering the code at the new provider or cancelling the migration from B2Brouter.
### Document reception rules
[Section titled “Document reception rules”](#document-reception-rules)
From this section you can configure the rules that apply to the reception of electronic documents in your account.
* **Automatically reject invalid documents:** Received invoices with validation errors will automatically change to “Rejected” status.
* **Automatically accept valid documents:** Received invoices without validation errors will automatically change to “Accepted” status.
* **Reject documents from unknown senders:** Documents received from senders not listed as one of your suppliers in the Contacts section will be automatically rejected.
* **Require signature on invoices received in FacturaE format:** for Spanish accounts only.
* **Require delivery note number and item code on all invoice lines**
* **Automatically recognise text**: Automatically recognise the text of received invoices.
* **Formats:** You can choose which formats you want to accept. We recommend keeping all formats enabled. If you decide to limit them, please note that any document received in a format that is not selected will be automatically rejected.
## Taxes
[Section titled “Taxes”](#taxes)
### Taxes by default
[Section titled “Taxes by default”](#taxes-by-default)

From this section you can delete or add the taxes that you can later apply to the invoices you generate. **To add taxes:**
* Use the **Add tax** button and access the form at the bottom of the list,
* In the **Name** box, select the tax from the available drop-down options or add it manually.
* From the **Category** drop-down, you can select the category of the tax.
* In the **Percentage** box, add the percentage of the tax.
* You can check the **Enable by default** option and the tax is added by default when you generate the invoice.
Finally, use the **Add tax** option and the tax is created and saved.
**To delete a tax**: Use the three dots icon to the right of each tax and use the **Delete** option to remove the tax.
### Tax reports
[Section titled “Tax reports”](#tax-reports)
In this section you can set up the tax reports available to file with B2Brouter.
## Collection
[Section titled “Collection”](#collection)
From this section you can add the bank accounts of your company that you can select to collect invoices, as well as define the default collection method for new customers.

### Bank accounts
[Section titled “Bank accounts”](#bank-accounts)
**To add bank accounts:**
* Add the information in the form.
* In **Name** you can assign a name to the bank account you are adding.
* In the drop down **Account type** you have to select whether you want to add an IBAN or a Bank Account.
* Depending on the type of account you select, add the IBAN or the Bank Account coding.
* In **BIC** you can add the BIC code.
Finally, to save your changes, use the **Add bank account** option at the bottom of the page.
**To delete bank accounts:** Use the three dots icon to the right of each line and use the **Delete** option to delete the bank account.
### Default payment method
[Section titled “Default payment method”](#default-payment-method)
From this section you can configure the default payment method, and it is used for each new customer you create. If you need to, you can also customise this payment method for each customer or for each invoice:
* In the **Payment method** option, select the collection method you want to use by default on invoices.
* In the **Due date** drop-down, you can select a due date for invoices.
Finally, you can apply all these options that you have configured for all customers. To do this, use the **Apply this payment method to all existing customers** option.
## eAccounting
[Section titled “eAccounting”](#eaccounting)
B2Brouter allows accountants to connect with their clients to automate the reception of their clients’ electronic invoices in their accounting software or to download them directly from the platform.
It is important to note that the client of the agency, i.e. the account that connects to eAccounting, must have a **Premium plan**.
### Establishing the connection
[Section titled “Establishing the connection”](#establishing-the-connection)
The connection between accounts is established by means of an invitation that can only be sent by the agency, and it is the client who has to accept or refuse it in order to connect. There are two options to do this:
#### Option 1. The customer is a B2Brouter user.
[Section titled “Option 1. The customer is a B2Brouter user.”](#option-1-the-customer-is-a-b2brouter-user)
* Go to the **Account Settings** (gear icon at the top).
* Then select the **eAccounting** section located on the left side menu. From here you can manage your connection with your account manager.
#### Option 2. Customer is not yet a B2Brouter user
[Section titled “Option 2. Customer is not yet a B2Brouter user”](#option-2-customer-is-not-yet-a-b2brouter-user)
* Open the email sent to you by your accountant with the invitation.
* Click on the option **View invitation**. You will be redirected to the B2Brouter platform.
* Click **Accept invitation to connect** to activate the connection. If you do so, all your invoices will be automatically sent to your accountant, and he will be able to view the sections: contacts, invoices issued, invoices received, taxes, collections, payments and SEPA files.
The connection can be deactivated, deleted and re-established by both the account manager and the client.
### Integration of client invoices with the accountant
[Section titled “Integration of client invoices with the accountant”](#integration-of-client-invoices-with-the-accountant)
When integration is established, all sent invoices and receipts are automatically sent to the accountant.
* Invoices are integrated when their status is ‘Sent’.
* At the index of the customer invoices there is a label informing about the status of the invoice. These can be:
* Pending: The invoice has not yet been integrated or downloaded.
* Processed: The invoice has already been integrated or downloaded.
* Modified: The invoice has been modified after having been processed.
## Settings
[Section titled “Settings”](#settings)
### Invoices and quotes
[Section titled “Invoices and quotes”](#invoices-and-quotes)
From this section you can customise some aspects of your account.
#### PDF document view
[Section titled “PDF document view”](#pdf-document-view)
In this section you can choose the appearance of the PDFs your customers receive. Select **Default view** or **Classic view**.
#### Legal text and data protection
[Section titled “Legal text and data protection”](#legal-text-and-data-protection)
In this section you can add a legal text that is displayed by default in the footer of all invoices.
#### Invoices and quotes
[Section titled “Invoices and quotes”](#invoices-and-quotes-1)
From this section you can:
* Activate the **Generate invoice number automatically** option if you want the system to provide you with the invoice number automatically, taking into account the numbering of the previous invoice (as long as you follow a correlative numbering).
* Activate the **Apply taxes per line** option if you want the system to calculate the taxes per line for all your invoices by default.
#### Discount and additional charge
[Section titled “Discount and additional charge”](#discount-and-additional-charge)
* Use the drop-down options to indicate whether you want discounts or charges to be shown as a percentage or as an amount.
* You can also activate the **Apply tax** option if you want amounts to be applied to discounts or charges you add to invoices.
#### Note on invoice
[Section titled “Note on invoice”](#note-on-invoice)
Configure this section to create a legal text that will appear by default in the footer of the invoices:
* Select the **language** in which you want to configure the text.
* In **Invoice note** add the text you want to appear on the invoices.
#### Recurring invoices
[Section titled “Recurring invoices”](#recurring-invoices)
Once you have generated a recurring invoice, you can use this option to activate the automatic creation of the following recurring invoices.
#### Imported invoices
[Section titled “Imported invoices”](#imported-invoices)
From this section you can choose the type of rounding you want to give to your imported invoices:
##### Roundings (only for imported invoices).
[Section titled “Roundings (only for imported invoices).”](#roundings-only-for-imported-invoices)
This setting only applies to imported invoices, but does not apply to manually generated invoices:
* Select from the dropdown which type of rounding you want to use: **Round half up**, **Bank rounding**, **Truncate decimals**.
* You can activate the **Round before sum** option if you want the rounding to be done before adding up the invoice total.
#### Invoice sending method for new contacts
[Section titled “Invoice sending method for new contacts”](#invoice-sending-method-for-new-contacts)
Configure a default sending method for all your customers, in cases where you import invoices and you have not created the customer in B2Brouter.
#### What is a B2Brouter transport?
[Section titled “What is a B2Brouter transport?”](#what-is-a-b2brouter-transport)
Transports are the various mechanisms available for sending and receiving documents.
Some of the transports require a specific configuration within the account. These are: B2Brouter, email, Peppol Network, Eusko Jaurlaritza, Junta Castilla-La Mancha (Pecamos), SDI (Sistema diIntercambio) and Ediversa Network, among others.
We can classify them in three different blocks.
| B2Brouter’s own transports | Open Network transports | Private Network transports |
| -------------------------- | ----------------------- | -------------------------- |
| B2Brouter | Peppol Network | AS2 Carrefour |
| Email | FACe | Pagero Network |
| Download | eFACT Catalunya | Basware Network |
#### Attached documents
[Section titled “Attached documents”](#attached-documents)
When attaching documents to an invoice, depending on your preferences, they can go inside or outside the XML. Therefore, there are two options:
* Transport ‘without embeeddedattachments’: The invoice is sent and the attachment is sent separately, it does not go inside the XML.
* Transport ‘detached’: the attachment is embedded inside the XML.
#### What are B2Brouter document types?
[Section titled “What are B2Brouter document types?”](#what-are-b2brouter-document-types)
The document type is the format of the file to be sent or received through a transport.
Examples of formats are PDF Invoice, XML, InvoiceE, Peppol BIS3 Invoice, Peppol PINT JP Invoice, XRechnung UBL Invoice, Chorus Invoice and EDIFACT among others.
#### What is the B2Brouter shipping method?
[Section titled “What is the B2Brouter shipping method?”](#what-is-the-b2brouter-shipping-method)
The sending method indicates the means of transport and the type of document to be used for transmission. It is the combination of the type of transport and the type of document.
### Invoice series
[Section titled “Invoice series”](#invoice-series)
You can define here whether you want to manage the numbering manually or let the platform do it automatically:
* **Automatic numbering**: The platform assigns the numbering automatically and allows you to configure different numbering depending on the type of document. You can define which numbering to use by default in each case.
* **Manual numbering**: The platform suggests the next number taking into account the last one used for the same type of document, but will allow you to edit both the series and the number manually.
To activate the automatic series of your invoices:
1. Activate the button **Use series to invoices**.
2. Define the **Separator**. Select the separator between series and number.
3. Save your changes with the **Save** button.
> **Important:** If your company is registered in Spain, this option will appear enabled by default. This behavior is applied automatically during account creation to ensure the proper functioning of specific processes, such as the generation of corrective invoices, which necessarily require a series. This parameter can be modified later from this same section if needed.
### Electronic signature on invoices
[Section titled “Electronic signature on invoices”](#electronic-signature-on-invoices)
There are two ways to sign your electronic invoices in B2Brouter.
#### Delegated signature
[Section titled “Delegated signature”](#delegated-signature)
The delegated signature authorises B2Brouter to digitally sign documents on your behalf. This is used by default whenever you send an invoice from the platform — no additional configuration is needed.
#### Own signature
[Section titled “Own signature”](#own-signature)
To sign invoices with your own signature, you need an up-to-date digital certificate from your company:
* Use the **Select file** option to search for the file in your system and upload it to B2Brouter.
* You can upload **.pfx** or **.p12** certificates.
Once you have configured the sections you need, save the changes using the **Save** option.
#### Signing according to your plan
[Section titled “Signing according to your plan”](#signing-according-to-your-plan)
Depending on your plan, the available signing options differ:
* **Basic**: All invoices are signed automatically with B2Brouter’s delegated signature. Invoices sent in PDF format are not signed.
* **Professional**: Invoices are signed by default with the delegated signature (except PDF invoices). With your own certificate, you can sign all invoices, including PDF invoices.
* **Business**: Invoices are signed by default with the delegated signature, including PDF invoices. With your own certificate, you can sign all invoices with it.
### Email notifications
[Section titled “Email notifications”](#email-notifications)
Configure notifications of status changes via email:
* Add the email where you want to receive the notification, in the corresponding box indicate the type of document and the related status change.
* You can add several recipients by entering different emails separated by commas.
### Email messages
[Section titled “Email messages”](#email-messages)
Configure and personalise the e-mail message for sending invoices or quotations through this channel:
* Activate the **Customise message for sent invoices** option to personalise the email when sending invoices.
* Activate the **Customise message for sent quotes** option to personalise the email when you send quotes.
Once you activate the option, the drop-down menu where messages can be customised according to language is activated:
* In **Language**, select the language of the message you wish to customise.
* In **Subject**, add the subject of the message.
* In **Body**, add the message you want the recipient to receive.
Once you have configured the sections you need, save your changes using the **Save** option.
## Organisational units
[Section titled “Organisational units”](#organisational-units)
### Organisational units
[Section titled “Organisational units”](#organisational-units-1)
This is an organisation related to your company, which uses the same VAT number but may have a different address or name:
### Add an organisational unit
[Section titled “Add an organisational unit”](#add-an-organisational-unit)
From this section you can create the headquarters of your company if you have one:

* Use the option **Add organisational unit**.
* Add the **Fiscal data** of the head office and use the **Continue** option.
* Add the information of the contact person of the head office.
Finally use the **Continue** option to create the head office. Navigate and access the different locations created via the additional drop down at the top of the page.
## Users
[Section titled “Users”](#users)
### Users
[Section titled “Users”](#users-1)
From this section you can add different users to your account so that they can manage electronic documents, customers and suppliers. You can grant roles to users to limit the access or actions they can perform.

### Add user
[Section titled “Add user”](#add-user)
Use the **Add user** option to add a new user to your account.
* In **Email**, add the email address of the user you want to invite to B2Brouter.
* In **Permissions**, select from the dropdown the permissions you want to grant to the user and check the sections you want them to have access to.
### Delete user
[Section titled “Delete user”](#delete-user)
Click on the three dots next to the email address of the user you want to delete and use the **Delete** button.
#### Role of each permission
[Section titled “Role of each permission”](#role-of-each-permission)
* **Owner**: this is the profile of the user who created the account. There can only be one owner and this owner can manage any parameter of the account.
* **Administrator**: this profile can manage any account parameter, except premium plan subscriptions.
* **View and edit**: this profile can view and edit the different documents that are managed from B2Brouter and customers or suppliers. It cannot configure any parameter of your company settings.
* **View**: this profile can only view the management documents from B2Brouter and customers, but can not modify any data.
Once the users have been configured, use the **Save** option at the bottom right of the screen.
# Verify your identity
> Learn how to verify your identity in B2Brouter with a digital certificate to send e-invoices from a Spanish company.
If your company is from Spain, in order to send an e-invoice from B2Brouter, you must verify your identity with your company’s digital certificate.
Identity verification is a security measure that we have implemented to make B2Brouter a more secure application. This helps to prevent identity theft and fraudulent use of the system and to protect issuers and recipients.
You only need to verify your identity once. Once it has been done, you can use B2Brouter whenever you want.
## How do I verify the identity of my account on B2Brouter?
[Section titled “How do I verify the identity of my account on B2Brouter?”](#how-do-i-verify-the-identity-of-my-account-on-b2brouter)
You can verify the identity of your account in two sections:
**When creating an electronic invoice**
When you create an electronic invoice and click on **Send**, a window will appear where you have to verify your identity. Click on **Continue** to proceed.
**From the Identity certification section**
Access the **Account settings** (gear icon located in the main top menu) and then select **Identity certification**. From here, just follow the steps.

**Verify your identity**
Once you have completed the previous steps, click on Identity verification to verify your identity using your company’s electronic certificate:
* B2Brouter will seek information from the certificate on the system.
* You must have your company’s certificate correctly installed and configured on your system.
If your certificate is valid and B2Brouter receives the correct information from your system, it will automatically verify and notify you. After that, you can send the e-invoice without any problems.
[Consult the certificates accepted by B2Brouter](https://eidas.ec.europa.eu/efda/trust-services/browse/eidas/tls/tl/ES).
[You can also validate your certificate through this link](https://valide.redsara.es/valide/validarCertificado/ejecutar.html). In doing so, you can find out if the certificate you are using is valid and contains the same information as your B2Brouter account.
[Here you can find more information about the validation and configuration of your certificates](https://www.dnielectronico.es/PortalDNIe/PRF1_Cons02.action?pag=REF_320\&id_menu=15).
### I cannot verify my identity
[Section titled “I cannot verify my identity”](#i-cannot-verify-my-identity)
If you are having problems verifying your identity, you need to check:
1. That your certificate is one of the certificates accepted by B2Brouter.
2. That you are using the correct certificate. This should contain the same information as the account you registered on B2Brouter.
3. That you have correctly installed the certificate on your system or browser.
4. Bear in mind that sometimes the antivirus can affect this process and prevent your system from uploading the certificate to B2Brouter.
In addition to these issues, there may be other features on your system that are preventing the certificate from being uploaded to B2Brouter.
# Configure the B2Brouter API connection
> Learn how to configure the B2Brouter API, find your account ID, generate API keys, use staging and make your first request.
B2Brouter API lets you integrate your ERP, ecommerce platform or custom application to create, query and send documents automatically.
This guide covers the basics to get started: where to find the `account_id`, how to generate an API key, the difference between staging and production, and how to make your first request.
For the complete technical detail of each endpoint, also see the [developer documentation](https://developer.b2brouter.net/docs/introduction).
## Before you start
[Section titled “Before you start”](#before-you-start)
* You need a B2Brouter account.
* You must have access to the **Developers** tab.
* For testing, start with the **[sandbox](/en/developers/testing/sandbox/)** — it is the recommended environment for most tests. Use the full staging environment only for large-scale integrations or broad end-to-end tests.
## Where to find the `account_id`
[Section titled “Where to find the account\_id”](#where-to-find-the-account_id)
When you work with the API, many operations are performed against a specific account and require its `account_id`.
You can find it in two ways:
1. In B2Brouter, go to the **Developers** tab.
2. Click **View IDs for each account** or the edit group icon next to your group name.
3. In the account list, you will see the ID for each account.
You can also retrieve it through the API by listing the accounts in your group. In the response, pay special attention to the `id` and `identifier` fields.
## How to generate an API key
[Section titled “How to generate an API key”](#how-to-generate-an-api-key)
1. Log in to your B2Brouter account.
2. Go to the **Developers** tab.
3. Open **API Keys**.
4. Create a new key or copy an existing one.
5. Store it in a safe place.
API keys are different for each environment. A production key does not work in *staging*, and a staging key does not work in production.
## Difference between staging and production
[Section titled “Difference between staging and production”](#difference-between-staging-and-production)
B2Brouter has two main environments:
* **Production**: `https://api.b2brouter.net`
* **Staging**: `https://api-staging.b2brouter.net`
Recommendations:
* Use the **[sandbox](/en/developers/testing/sandbox/)** for most tests and initial integration work.
* Use **staging** only for large-scale or broad end-to-end integration tests.
* Use **production** only after you have fully validated the integration flow.
* Keep credentials and configuration separate for each environment.
If you are working with an older API version (`2025-01-01`), some requests may still use `app.b2brouter.net`, but for newer versions the recommended base URL is `api.b2brouter.net` or `api-staging.b2brouter.net`.
## Authentication
[Section titled “Authentication”](#authentication)
B2Brouter API authenticates requests with the following header:
* `X-B2B-API-Key`
Optionally, you can also send:
* `X-B2B-API-Version`
B2Brouter does not use `Authorization: Bearer ...` as the main authentication mechanism for this API. The correct way to authenticate is with `X-B2B-API-Key`.
## First request with cURL
[Section titled “First request with cURL”](#first-request-with-curl)
A good first test is to list the accounts available in your group:
```bash
curl --request GET \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: 2025-10-13' \
--header 'accept: application/json'
```
If the request is successful, you will get a JSON response with the available accounts. From there you can already use the `account_id` in requests such as:
```bash
curl --request GET \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: 2025-10-13' \
--header 'accept: application/json'
```
## Which `account_id` to use
[Section titled “Which account\_id to use”](#which-account_id-to-use)
When calling endpoints such as `/accounts/{ACCOUNT_ID}/...`, you can use:
* The numeric account ID.
* Or the account identifier, if your group uses it that way.
If you are unsure, always validate it first with `GET /accounts`.
## Handling transient errors
[Section titled “Handling transient errors”](#handling-transient-errors)
In a real integration, it is important to handle temporary errors without treating them as permanent failures.
### Error `429 Too Many Requests`
[Section titled “Error 429 Too Many Requests”](#error-429-too-many-requests)
This error means you exceeded the request limit.
We recommend that you:
* Reduce the frequency of API calls.
* Retry with progressive waiting time using exponential backoff.
* Avoid very aggressive polling loops.
### Error `503 Service Unavailable`
[Section titled “Error 503 Service Unavailable”](#error-503-service-unavailable)
This error indicates a temporary service issue.
We recommend that you:
* Try again after a few seconds.
* Apply a maximum number of retry attempts.
* Log the error so it can be tracked.
### General retry recommendation
[Section titled “General retry recommendation”](#general-retry-recommendation)
For temporary errors such as `429` or `503`:
* Retry a maximum of 3 to 5 times.
* Wait a bit longer between each attempt.
* Do not retry forever.
## Webhooks
[Section titled “Webhooks”](#webhooks)
If you need automatic notifications when the status of an invoice or document changes, the best option is to use **webhooks** instead of making constant API queries.
This guide does not cover webhook setup in detail, but it is the recommended approach to reduce polling and receive near real-time updates.
## Recommended next step
[Section titled “Recommended next step”](#recommended-next-step)
Once you have validated:
1. The API key.
2. Access to the correct environment.
3. The `account_id` you will use.
You can continue with the specific endpoints required by your integration in the [API documentation](https://developer.b2brouter.net/docs/introduction).
# Connect accountants with eAccounting
> Learn how accountants connect with clients in B2Brouter eAccounting to receive, integrate and download customer invoices.
B2Brouter allows accountants to connect with their clients to automate the reception of their clients’ electronic invoices in their accounting software or download them directly from the platform.
It is important to note that the client of the agency, i.e. the account that connects to eAccounting, must have a **Premium plan**.
## Establishing the connection
[Section titled “Establishing the connection”](#establishing-the-connection)
The connection between accounts is established by means of an invitation sent by the account manager, and it is the client who has to accept or reject it in order to connect. To have the invitation sent:
## Log in to your B2Brouter account
[Section titled “Log in to your B2Brouter account”](#log-in-to-your-b2brouter-account)
* Click on the book icon located at the top of the page, next to your company name.
* Once inside the **eAccounting** section, click on the **Add customer** button.
* In the form, fill in the blank boxes with your customer’s information.
* To add your customer, you have two different ways to create the invitation. In both cases the customer will have to accept the invitation to connect.
### Option 1. The customer is a B2Brouter user
[Section titled “Option 1. The customer is a B2Brouter user”](#option-1-the-customer-is-a-b2brouter-user)
You can check if your customer is already registered on the platform to send them the invitation. Add their country and VAT number to send them an invitation from B2Brouter.
### Option 2. Customer is not yet a B2Brouter user
[Section titled “Option 2. Customer is not yet a B2Brouter user”](#option-2-customer-is-not-yet-a-b2brouter-user)
If your customer does not yet have a B2Brouter account, add their email address to send them an invitation.
The connection can be deactivated, deleted and re-established by both the B2Brouter and the customer.
## Integration of customer invoices with the accountant
[Section titled “Integration of customer invoices with the accountant”](#integration-of-customer-invoices-with-the-accountant)
When there is an integration set up, all sent invoices and receipts are sent to the accountant automatically.
* Invoices are integrated when their status is ‘Sent’.
* At the index of the customer invoices there is a label informing about the status of the invoice. These can be:
* Pending: The invoice has not yet been integrated or downloaded.
* Processed: The invoice has already been integrated or downloaded.
* Modified: The invoice has been modified after having been processed.
To download your customers’ invoices, go to the **eAccounting** section via the book icon in the upper band.
# B2Brouter AI Connector (MCP)
> Learn how to connect your AI assistant to B2Brouter to query invoices, tax reports and business figures using the MCP protocol.
The AI Connector lets your usual AI assistant **query your B2Brouter account directly**, along with **the official product documentation**, so it answers your questions with real data instead of whatever the AI might imagine.
Instead of going into the web app to search, filter and cross-reference information, you can ask:
**“Why did the tax authority reject invoice 2026/0142?”** **“What do I have pending to collect? What’s overdue?”** **“What channel does this new customer receive invoices on?”** **“How do I create an invoice via API and send it over Peppol?”**
And the assistant looks up the answer in your account and in B2Brouter’s official guides, and explains it to you.
### Why is this different from asking the AI directly?
[Section titled “Why is this different from asking the AI directly?”](#why-is-this-different-from-asking-the-ai-directly)
An AI assistant without a connector answers from memory: it knows general things about electronic invoicing, but **it doesn’t know your account**, and it can get regulatory details wrong (error codes, mandatory fields, requirements of each tax authority). The connector gives it two things:
1. **Your data:** your invoices, your tax reports, your figures — the same ones you see in the web app, in real time.
2. **Official knowledge:** B2Brouter’s complete documentation: user guide, developer guides and tax authority guides (AEAT/Verifactu/TicketBAI, DGFiP, KSeF, SDI, ZATCA, LHDN, Peppol…).
And a key instruction for the AI: if the answer isn’t in the documentation, the assistant must **admit it doesn’t know** instead of making it up.
**For technical profiles.**
The connector is a remote **MCP** (Model Context Protocol) server, the open standard for AI assistants to access external data and tools. B2Brouter exposes a set of query tools that any MCP client (Claude web/app, Claude Code, compatible IDEs…) can discover and invoke. The assistant autonomously decides which tool to invoke based on your question; you just talk. No B2Brouter installation is needed: it’s a remote server with OAuth authorization.
### What can it do?
[Section titled “What can it do?”](#what-can-it-do)
It can do six things, all **read-only queries** (section 03 develops them with examples):
1. **Search and list your invoices** by status (with errors, rejected, unpaid…), by number, or by period.
2. **Inspect and validate an invoice**: the full content and the exact validation errors, explained.
3. **Track your tax reports**, their status before the authority and, if rejected, the literal message and what it means.
4. **Give you business figures**: revenue, expenses, pending and overdue amounts, monthly evolution, top customers and suppliers.
5. **Tell you how to reach a company with documents**, i.e. which channel it receives them on (Peppol, FACe, Chorus Pro, SDI…) and which codes you need.
6. **Answer questions using the official documentation** for users and developers, including each tax authority’s guides.
### What can’t it do? (and why)
[Section titled “What can’t it do? (and why)”](#what-cant-it-do-and-why)
The connector **cannot create, modify, send or delete anything**. It’s deliberately read-only: the AI helps you *understand and decide*; the *actions* — correcting an invoice, sending it, or chasing a payment — are yours to do, in the B2Brouter web app or via the API, as always.
This isn’t a temporary limitation or a gap: it’s the security design.
**For technical profiles.**
Strictly read-only with no side effects: querying an invoice via the connector doesn’t mark it as read or trigger tax report generation (a deliberate divergence from the REST API’s GET). To write — creating invoices or sending them — the path is the [B2Brouter REST API](https://app.b2brouter.net/developers); the recommended workflow is *create via API, inspect and diagnose via the connector*.
### Who is it useful for
[Section titled “Who is it useful for”](#who-is-it-useful-for)
For anyone working with B2Brouter, whatever their profile:
* **If you handle invoicing**, to unblock rejected invoices and figure out how to invoice a new customer.
* **If you’re an advisor or accounting firm**, to review the tax status of your clients’ accounts and translate the authorities’ rejections.
* **If you’re a developer**, so your coding agent has the canonical API documentation and can diagnose your test invoices without leaving the editor.
* **If you’re a manager or finance lead**, to get business figures from chat, without entering the web app.
This guide doesn’t separate these journeys: everything it describes is available to every profile, within the permissions each user already has.
***
## Connecting your assistant to B2Brouter
[Section titled “Connecting your assistant to B2Brouter”](#connecting-your-assistant-to-b2brouter)
### Requirements
[Section titled “Requirements”](#requirements)
1. **A B2Brouter account with an active paid subscription.** The connector isn’t available on the free plan. Invoice, tax report and financial summary features additionally require the Professional plan or higher on the account being queried.
2. **Your B2Brouter username and password.** The connection is made with your personal user, **no API key is needed**, nor any technical configuration.
3. **An AI assistant compatible with MCP connectors:** Claude (web and app), Claude Code, and other assistants as each platform approves the connector.
### Connecting from a chat assistant
[Section titled “Connecting from a chat assistant”](#connecting-from-a-chat-assistant)
This is the path for most users (Claude web, mobile or desktop app):
1. In your assistant, look for **B2Brouter** in the connector directory, or add it by URL: `https://app.b2brouter.net/mcp`.
2. A B2Brouter window will open asking you to **sign in** (if you’re not already) and **authorize** the assistant’s access.
3. Review what you’re authorizing and accept it. You’ll return to the assistant, now connected.
This is done **once per assistant**. From then on, just ask naturally: the assistant decides on its own when it needs to query B2Brouter, and you see it because it tells you in the conversation.
### Connecting from a coding agent
[Section titled “Connecting from a coding agent”](#connecting-from-a-coding-agent)
If you work with a coding agent (Claude Code, or any MCP-compatible editor/IDE), the connector registers as just another remote MCP server:
* **Server URL:** `https://app.b2brouter.net/mcp`
* When adding it, the authorization flow opens in the **browser**: you sign in to B2Brouter and authorize, as in the case above.
**For technical profiles.**
Authorization is **OAuth 2.1 with Authorization Code + PKCE**. MCP clients can **self-register** (Dynamic Client Registration, RFC 7591), so there’s no need to request client credentials from B2Brouter: any standard-compliant client works directly. The resulting token acts *on behalf of your user*; there are no service tokens or credentials of the assistant’s own on the account. The paid subscription requirement is checked **on every request**: if the subscription expires, access is cut off immediately even if the token is still alive.
### The principle that governs everything: the assistant is you
[Section titled “The principle that governs everything: the assistant is you”](#the-principle-that-governs-everything-the-assistant-is-you)
The assistant acts **with your identity and your permissions**, no more, no less:
* It only sees the accounts where your user is a member.
* Within each account, it only sees the sections you have permission for. If your access to the company account is limited to received invoices, the assistant won’t see issued ones either.
* Each person in the company connects with their **own user**: access and permissions are never shared.
Section [04](#how-it-works-under-the-hood) explains how this model works under the hood.
### Disconnecting it
[Section titled “Disconnecting it”](#disconnecting-it)
From your B2Brouter profile → **Authorized applications**, you can revoke the assistant’s access whenever you want. The effect is **immediate**. You can also remove the connector from the assistant itself.
### Seeing what the assistant has done
[Section titled “Seeing what the assistant has done”](#seeing-what-the-assistant-has-done)
Every query the assistant makes to your account is **logged**: on your B2Brouter profile → **MCP Activity** you’ll see what was queried and when, with which function and from which application. Full transparency over the AI’s activity with your data; also useful, if you’re an accounting firm, as traceability for your clients.
### If the connection doesn’t work
[Section titled “If the connection doesn’t work”](#if-the-connection-doesnt-work)
| Symptom | Likely cause | Solution |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| ”An account with an active subscription is required” | Your user doesn’t have access to any account with an active paid subscription | Activate or renew a subscription, or ask to be added to a paid account |
| Authorization doesn’t finish / doesn’t return to the assistant | Pop-up blocking or session expired mid-flow | Allow the B2Brouter window, sign in and repeat the connection |
| The assistant doesn’t offer B2Brouter | The platform doesn’t yet have the connector approved in its directory | Add it manually by URL (`https://app.b2brouter.net/mcp`) if the assistant allows it |
***
## What you can do
[Section titled “What you can do”](#what-you-can-do)
No need to memorize anything: ask in your own words and in your own language — the assistant translates your question into the queries it needs.
A good first step, especially if you have access to more than one account (group, subsidiaries, or a client portfolio if you’re an accounting firm):
**“What B2Brouter accounts do I have?”**
The assistant lists the accounts where your user is a member, with name and tax IDs. From there, you can refer to them naturally (“the Client SA account”).
***
### 1. Search and list invoices
[Section titled “1. Search and list invoices”](#1-search-and-list-invoices)
This is the starting point for most queries: which invoices meet a condition.
**“What invoices do I have with errors?”** **“What invoices have been rejected this week?”** **“List my overdue invoices, from largest to smallest”** **“Find invoice FC-2026/0089 and tell me what status it’s in”**
The assistant lists the invoices that match, by status (with error, rejected, unpaid…), by number or series, by period, and from the list you can request the detail of any of them.
### 2. Inspect and validate an invoice
[Section titled “2. Inspect and validate an invoice”](#2-inspect-and-validate-an-invoice)
From the summary to the full detail:
**“Show me invoice 2026/0142”**
The assistant returns the full content: parties, lines, amounts, taxes, payment terms. And the key question when something fails:
**“Why did 2026/0142 fail?”** **“Will this invoice pass validation before I send it?”**
The assistant gets the **exact** validation errors and **explains** them using the official documentation: what each error means, which field it affects and what needs to change. You make the correction in the web app (the connector doesn’t touch anything) and then you can verify again: *“is it fine now?”*.
**For technical profiles.**
The invoice body is returned in the **exact format of the REST API response**, and validation is the same battery as the validation endpoint: **XSD + Schematron + model rules**. This means your coding agent can cross-reference the error directly with the JSON your integrator sends and propose the fix to your payload. The recommended workflow for integrations: (1) ask the agent how to do things, consult the official guides (“how do I create an invoice via API and send it over Peppol?”, “how does the sandbox work?”); (2) **create test invoices via API** (the connector doesn’t create anything); (3) diagnose via the connector without leaving the editor (“which invoices from the test batch have ended up in error?”, “invoice 123 doesn’t validate, what do I need to change in my JSON?”); (4) for format mapping, the mapping guides are queryable by the agent: JSON→Facturae, FatturaPA, UBL BIS3, CII, KSeF FA-3.
### 3. Track tax reports
[Section titled “3. Track tax reports”](#3-track-tax-reports)
If your country has electronic invoice declaration or registration (Verifactu, SII or TicketBAI in Spain, DGFiP in France, KSeF in Poland, ZATCA in Saudi Arabia, MyInvois in Malaysia…), each invoice generates a tax report with its own lifecycle before the authority. The connector tracks it:
**“What tax reports do I have in error or rejected?”** **“What did the tax authority reject on the Client SA account this week?”** **“Was the report for invoice FC-0089 registered correctly?”**
And for every rejection, the full diagnosis:
**“Give me the detail of the first one”** → status, amounts and **the authority’s literal rejection message**. **“What does this error mean and how do I fix it?”** → the assistant cross-references the code with B2Brouter’s official guides for each authority and tells you the affected field and the fix.
It works for any of the supported authorities, and you can ask about codes on their own: *“what does this Verifactu error 1210 mean?”*, *“what does this KSeF rejection code mean?”*.
**For technical profiles.**
The report detail includes the identifiers/QR and, if you ask for it, the **XML sent** to the authority (decoded, readable by the agent) — useful for debugging exactly what the authority received.
### 4. Business figures
[Section titled “4. Business figures”](#4-business-figures)
These are the same figures as the B2Brouter dashboard; there aren’t two truths:
**“How have we done this quarter?”** → revenue, expenses and net result for the period. **“What do we have pending to collect? How much is overdue?”** → amounts pending collection and payment, split between *on time* and *overdue*, with invoice count and amount. **“Who are our top customers this year? And suppliers?”** → ranking by invoiced volume. **“How has cash flow evolved month by month?”** → revenue, expenses, difference and cumulative by month.
And free-form composition:
**“Put together a summary for the meeting with the bank: yearly billing, monthly evolution, notable pending and overdue amounts”**
Practical details:
* **Periods:** this month/quarter/year, previous ones, or any specific range (“from January 1st to March 15th”).
* **Taxes:** ask for the figures with or without VAT, as you prefer.
* **Currencies:** each summary is for **a single currency** (by default, the account’s). If you invoice in more than one, ask for them separately (“and now in dollars”).
### 5. How to reach a company with documents
[Section titled “5. How to reach a company with documents”](#5-how-to-reach-a-company-with-documents)
A new client asks you for an electronic invoice and you don’t know where to start. Give the assistant the client’s tax ID (the NIF in Spain, the SIRET in France, the Partita IVA in Italy, the NIP in Poland, the VAT number elsewhere in the EU):
**“Can I send electronic invoices to NIF B51115943? How?”** **“How do I send invoices to SIRET 13002526500013?”** **“Does this German client with VAT DE811907980 receive over Peppol?”**
The assistant queries the **B2Brouter directory** and tells you which channel this company receives documents on (Peppol, FACe, Chorus Pro, SDI…), which document types it accepts and which codes you’ll need: the **DIR3** codes for a Spanish public administration, the **SIRET** and service code for Chorus Pro, the **Codice Destinatario** for the Italian SDI, the **Peppol identifier** for international clients…
* If the company is on the Peppol network, but the data is still syncing, the assistant will tell you and retry.
* If there’s no way to send it documents electronically, it will tell you clearly — and can explain the alternatives.
The directory has a **limit of 50 searches per day and user**. The search is by exact tax ID; searching by approximate name isn’t available (it’s under consideration).
**For technical profiles.**
The directory response includes transport, document type per model, routing codes and mandatory fields published by the recipient — what you need to solve routing in an integration. If you run repeated automated tests, **cache the results**: the 50/day limit also counts your agent’s calls.
### 6. Questions about how B2Brouter works (and electronic invoicing)
[Section titled “6. Questions about how B2Brouter works (and electronic invoicing)”](#6-questions-about-how-b2brouter-works-and-electronic-invoicing)
The assistant has access to B2Brouter’s complete official documentation. **Ask as you would ask support**:
* “How do I create a credit note?”
* “How do I set up TicketBAI?” · “What do I need to do to comply with French electronic invoicing?” · “How does KSeF work?”
* “What is the DIR3 code and where do I put it?” · “What is the Peppol identifier?”
If the answer isn’t in the documentation, the assistant **will tell you** instead of making it up. In that case, the usual support channel remains the way to go.
**For technical profiles.**
The knowledge base includes the **complete developer documentation, versioned with the product**: getting started and SDKs, essential guides (transports, webhooks, error codes, tax report configuration, attachments), end-to-end use cases, country guides for 7 tax authorities, JSON→XML mapping guides, sandbox and environments, and the **API changelog** (“what changed between version 2026-03-02 and 2026-04-20?”). Your coding agent stops hallucinating fields and endpoints: it consults the canonical source.
***
### Chaining it all together
[Section titled “Chaining it all together”](#chaining-it-all-together)
The value of the connector is that these six pieces combine naturally within a conversation. Some complete flows:
* **Unblocking the day’s work:** “what invoices do I have with errors?” → “why did the first one fail?” → “what does this error mean?” → you fix it in the panel → “is it fine now?”.
* **The morning tax review (accounting firms):** “what did the AEAT reject on the Client SA account this week?” → rejection detail → translation using the authority’s guide. To review the whole portfolio: “review all my accounts and tell me which ones have rejected reports” — it works, but with dozens of clients it’s slow (one query per account); for day-to-day work it’s usually faster per specific client.
* **From the overview to the detail:** “what’s overdue?” → “list them by amount” → “show me the biggest one”.
* **Integration (developers):** API guide → create via API in the sandbox → diagnose errors via the connector → fix the payload.
**Remember:** the assistant only reads. Creating, correcting, sending and collecting is done in the panel or via API, as always. The work it saves you is *searching, cross-referencing and understanding*.
***
## How it works under the hood
[Section titled “How it works under the hood”](#how-it-works-under-the-hood)
You don’t need to read this section to use the connector. But if you want to understand, or need to explain to someone, *why* it can be trusted, here’s how it works, first in plain language and then with technical detail.
### The three pieces
[Section titled “The three pieces”](#the-three-pieces)
When you ask “why was invoice 2026/0142 rejected?”, three pieces are involved:
1. **Your AI assistant** (Claude, or whatever you use): understands the question and decides what information it needs.
2. **The B2Brouter connector**: a set of *query functions* the assistant can invoke, “list invoices with errors”, “give me the detail of this tax report”, “search for this company in the directory”.
3. **Your B2Brouter account**: where the data comes from, with the same permissions you have in the panel.
The assistant translates your question into one or more queries to the connector, receives the data and composes the answer. You don’t see this mechanics — only the conversation — but it’s all logged (see “Audit”, below).
**For technical profiles.**
The connector is a remote **MCP server** at `https://app.b2brouter.net/mcp`. It exposes typed query tools that the MCP client discovers on connecting: list accounts, list/show/validate invoices, list/show tax reports, financial summary, directory lookup, and two knowledge tools (index + document). All are read-only. Listing tools carry pagination and filters (status, search by series/number, period); detail tools return bodies equivalent to the REST API’s.
### Identity: the assistant acts on your behalf
[Section titled “Identity: the assistant acts on your behalf”](#identity-the-assistant-acts-on-your-behalf)
There’s no “robot user” or special key for the AI. When you connect the assistant, you authorize it to act **as your user**, and B2Brouter applies to every query exactly the same rules as when you browse the panel:
* **Member:** only the accounts where you’re a member.
* **Section:** within each account, only the sections you have permission for (issued, received, tax reports…).
* **Plan:** invoice, report and finance features require the account to have the Professional plan or higher; all of them require you to be on some paid account.
The practical consequence: **the connector doesn’t open any new doors**. Everything the assistant can see, you could already see. And if tomorrow you’re removed from an account or a section is restricted, the assistant loses it at the same instant.
**For technical profiles.**
Authorization is OAuth 2.1 (Authorization Code + PKCE) with dynamic client registration; the token represents the user, not the application. Permissions are evaluated **on every call** with the same engine as the web (member + capability + section + product) — including the nuances: a member restricted to “received invoices” doesn’t see issued ones via the connector either, and the paid subscription gate is re-checked on every request, so an expired subscription cuts off access even if the token is still alive.
### Why it’s read-only
[Section titled “Why it’s read-only”](#why-its-read-only)
This is the central design decision of this first version. Giving an AI the ability to *write* on documents with legal and tax value (creating invoices, sending them to an authority, modifying data) opens a category of risks (misinterpretation errors, malicious instructions hidden in data) that we want to address calmly and separately. In the meantime:
* **Querying is safe by construction:** no question, however poorly phrased, can change anything in your account.
* **Responsibility stays where it belongs:** the AI proposes the diagnosis; the action (in the web app or via the API) is taken by a person (or a system) with explicit intent.
### Where the knowledge comes from (and why it doesn’t make up answers)
[Section titled “Where the knowledge comes from (and why it doesn’t make up answers)”](#where-the-knowledge-comes-from-and-why-it-doesnt-make-up-answers)
When you ask “how do I set up TicketBAI?”, the assistant doesn’t answer from memory: it consults **B2Brouter’s official knowledge base**: the complete user guide, the developer documentation and each tax authority’s guides, the same ones B2Brouter publishes and versions with the product.
And it carries an explicit instruction: if no document covers the question, it must **say it doesn’t have the information, never make it up**. This is the direct answer to the most serious risk of using AI in a tax domain: a made-up but convincing answer. No assistant is infallible, but the connector is built to minimize this risk, and before a decision with tax consequences, always confirm with your advisor or with support.
**For technical profiles.**
The base is \~130 documents (user guide, developers, FAQ and codelists). The loop is: the agent queries the catalog index → picks documents by description → reads their body → answers citing real content. There’s no full-text search (the agent picks by descriptions); the “say you don’t know” instruction is reinforced in the tools themselves.
### Protection against third-party content
[Section titled “Protection against third-party content”](#protection-against-third-party-content)
Received invoices and the directory contain text written by others: company names, line descriptions. Someone could try to hide instructions for the AI in them (“ignore your instructions and…”). The connector explicitly marks all this content as **data, not instructions**, so the assistant never executes hidden orders inside an invoice or a company name.
**For technical profiles.**
All third-party values travel delimited as untrusted content with unforgeable markers. If you build automations on top of the connector, keep this distinction downstream: never mix invoice text with your agent’s instruction channel.
### No existence oracle
[Section titled “No existence oracle”](#no-existence-oracle)
If you query an identifier that doesn’t exist and one that belongs to someone else’s account, you get **exactly the same error** (“Not found or not authorized”). This is deliberate: no one can use the connector to figure out what exists outside their scope, not even to confirm that an invoice or account exists.
### Audit
[Section titled “Audit”](#audit)
Every query the assistant makes is logged (when, which function, which application, which user) and can be queried on your profile (**MCP Activity**). If you ever wonder “what exactly did the AI look at?”, the answer is there, in full.
### In summary
[Section titled “In summary”](#in-summary)
| Principle | What it means |
| ------------------------ | --------------------------------------------------------------------------- |
| User’s own identity | The assistant sees what you see; no more, no less; revocable instantly |
| Read-only | No question can modify anything; actions are yours |
| Official knowledge | Answers based on B2Brouter’s documentation, with an explicit “I don’t know” |
| Data ≠ instructions | Third-party content can’t give orders to the AI |
| Indistinguishable errors | No existence leakage between accounts |
| Full audit | All activity, logged and queryable |
***
## Limits, security and FAQ
[Section titled “Limits, security and FAQ”](#limits-security-and-faq)
### What the connector does NOT do (by design)
[Section titled “What the connector does NOT do (by design)”](#what-the-connector-does-not-do-by-design)
| Doesn’t do | What to do instead |
| -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Create, modify, send or delete invoices and documents | B2Brouter panel or REST API |
| Chase payments, send reminders | Panel (the assistant explains how) |
| Actions on the subscription or account configuration | Panel |
| Automatically tell you if your account is “ready” for an authority (Verifactu, DGFiP, KSeF…) | Ask the documentation (“what do I need to activate Verifactu?”) and check the configuration in the panel |
| Search the directory for companies by approximate name | Search by exact tax ID (NIF, SIRET, Partita IVA, NIP, VAT number…); search by name is under consideration |
| An aggregated summary of all your accounts at once | Ask account by account, or let the assistant go through them (slow with large portfolios) |
### Usage limits
[Section titled “Usage limits”](#usage-limits)
| Limit | Detail |
| ----------------- | ----------------------------------------------------------------------------------------- |
| Directory | 50 searches per day and user; other features have no specific limit |
| Financial summary | One currency per summary; multi-currency accounts, separately |
| Plan | Active paid account to connect; Professional or higher for invoices, reports and finances |
### Security and privacy
[Section titled “Security and privacy”](#security-and-privacy)
Section [04](#how-it-works-under-the-hood) explains how it works in detail; here’s the summary of guarantees:
* **The connector acts with your identity.** Only your accounts, only your sections (the same rules as the web), checked on every query. Neither the assistant nor the AI provider has its own credentials on your account.
* **Explicit, revocable consent.** You approve the authorization (OAuth) and can revoke it at any time from **Authorized applications**, with immediate effect. If the subscription expires, access stops on its own.
* **Full audit.** Every query is logged and queryable in **MCP Activity**.
* **No leaks between accounts.** A non-existent identifier and one from another account give the same error.
* **Protection against malicious content.** Text written by third parties (received invoices, directory) travels marked as *data, not instructions*.
* **Where your data goes.** Connector responses are processed by the AI provider you connected (Anthropic, Google…), under their terms and B2Brouter’s DPA. Connecting the account implies accepting this processing: review your assistant’s privacy policy.
### If something isn’t working
[Section titled “If something isn’t working”](#if-something-isnt-working)
| Symptom | Likely cause | Solution |
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| ”An account with an active subscription is required” | Your user doesn’t have access to any account with an active paid subscription | Activate or renew a subscription, or ask to be added to a paid account |
| ”Not found or not authorized” when querying an invoice or report | The identifier doesn’t exist **or** you don’t have permission to see it (the message is intentionally the same) | Check the identifier and that your user has access to that section of the account; if it’s a client account, its plan might not include the connector |
| The assistant says it doesn’t have the information | The question falls outside B2Brouter’s documentation: the connector is designed to **admit it rather than make it up** | Contact B2Brouter support through the usual channel |
| ”Rate limit reached” on the directory | You’ve exceeded the 50 daily searches | Wait until the next day or check the directory from the panel |
### Frequently asked questions
[Section titled “Frequently asked questions”](#frequently-asked-questions)
Do I need an API key?
No. The connection is made with your B2Brouter user, with an authorization you approve in the browser.
Do I need to know how to code?
No. Connecting it is three steps in your chat assistant, and from then on you just talk. The technical detail in this manual is optional.
Can everyone in the company use it?
Each person connects their own user and has their own scope. Access and permissions are never shared.
Is there a limit on accounts?
No: the assistant sees every account where you’re a member.
Are the financial summary figures reliable?
They come from the same engine as the panel’s dashboard — they’re the same figures. For critical tax or accounting decisions, always verify against the primary source.
Can the assistant make up answers about regulations?
The connector is designed to answer **only** using B2Brouter’s official documentation and admit when it doesn’t have the answer. Even so, no AI assistant is infallible: before a decision with tax consequences, confirm with your advisor or with support.
Why can't it fix the invoice if it can already see what's wrong?
A deliberate security decision for this first version: everything is read-only. Write capabilities will be studied later, with their own risk analysis.
Why does it tell me "Not found or not authorized" if the invoice exists?
Because your user doesn’t have access to it (section, account or plan). The message doesn’t distinguish “doesn’t exist” from “not authorized”, for security.
Can I use it from my code editor?
Yes — any MCP-compatible coding agent (Claude Code, compatible IDEs) connects with the same URL and the same authorization flow. See section [02](#connecting-your-assistant-to-b2brouter).
What languages does it work in?
Ask in whatever language you like — the assistant translates. The documentation it consults is B2Brouter’s official one.
# B2Brouter for WooCommerce user guide
> Learn how to install, configure and use the B2Brouter WooCommerce plugin to generate invoices, PDFs, webhooks and tax data.
## Table of Contents
[Section titled “Table of Contents”](#table-of-contents)
1. [Introduction](#introduction)
2. [Requirements and Installation](#requirements-and-installation)
3. [Testing the plugin in a staging environment](#testing-the-plugin-in-a-staging-environment)
4. [Initial Configuration](#initial-configuration)
5. [Invoice Generation](#invoice-generation-mode)
6. [Tax Configuration](#tax-configuration)
7. [NIF/VAT Collection](#nifvat-collection)
8. [Webhook Configuration](#webhook-configuration)
9. [PDF Management](#pdf-management)
10. [Customer Experience](#customer-experience)
11. [Troubleshooting](#troubleshooting)
***
## Introduction
[Section titled “Introduction”](#introduction)
**B2Brouter for WooCommerce** is a plugin that automates the generation and sending of electronic invoices from your WooCommerce store using the B2Brouter platform.
### Main Benefits
[Section titled “Main Benefits”](#main-benefits)
* **Legal Compliance**: Automatic compliance with electronic invoicing regulations in multiple countries.
* **Time Saving**: Automatic generation of electronic invoices without manual intervention.
* **Professionalism**: Invoices structured in standard formats (UBL, CII, XML, etc.)
* **Cross-border**: Support for international B2B and B2C transactions.
* **Full Integration**: Works seamlessly with WooCommerce without affecting your workflow.
***
## Requirements and Installation
[Section titled “Requirements and Installation”](#requirements-and-installation)
### Prerequisites
[Section titled “Prerequisites”](#prerequisites)
Before installing the B2Brouter plugin, make sure you have:
* **WordPress 5.8 or higher**
* **WooCommerce 5.0 or higher**, installed and active
* **PHP 7.4 or higher**
* A B2Brouter account with an active **eDocExchange** subscription
> If you don’t have WooCommerce installed yet, go to **Plugins → Add New**, search for “WooCommerce”, install it and activate it before continuing.
### Installing the B2Brouter Plugin
[Section titled “Installing the B2Brouter Plugin”](#installing-the-b2brouter-plugin)
**Recommended option — from the official WordPress repository**
1. In the WordPress admin, go to **Plugins → Add New**.
2. Search for **“B2Brouter for WooCommerce”**.
3. Click **Install Now** and then **Activate**.
**Alternatives — manual download**
If you prefer to install it manually, you can download the ZIP file from:
* The [official WordPress repository](https://wordpress.org/plugins/b2brouter-for-woocommerce/)
* The [GitHub releases page](https://github.com/B2Brouter/b2brouter-woocommerce/releases)
Once you have the ZIP, go to **Plugins → Add New → Upload Plugin**, select the file, and click **Install Now** and **Activate**.
***
Once activated, the plugin’s welcome page will appear with the initial instructions. In the sidebar menu you will see the new **Invoices** section.
From here you can proceed with the initial configuration.
***
## Testing the plugin in a staging environment
[Section titled “Testing the plugin in a staging environment”](#testing-the-plugin-in-a-staging-environment)
> ⚠️ **Important — do not use the Sandbox on your production store**
>
> The B2BRouter Sandbox generates documents marked as **“Not valid”** and its webhooks are completely independent from the production ones. If you configure the plugin with a sandbox key (`test_…`) on your real WooCommerce store:
>
> * Your real customers’ orders would generate invoices **with no legal validity**.
> * Status changes (invoice issued, registered, etc.) **would not reach** your WooCommerce, because production and sandbox webhooks do not communicate with each other.
> * You would lose numbering traceability and quota.
>
> The Sandbox is intended **exclusively for testing environments**. Before configuring the plugin against your real account, we recommend testing it in a staging environment.
### Why it is worth testing first
[Section titled “Why it is worth testing first”](#why-it-is-worth-testing-first)
Before connecting the plugin to your production store, it is worth verifying in a test environment:
* That invoice **numbering** is applied correctly and does not conflict with any other tool.
* That **taxes** (including intra-community reverse charge) are calculated as expected.
* That the **VAT number** field is collected at checkout and propagated to the invoice.
* That **webhooks** correctly update the order status in WooCommerce.
* That **PDFs** are generated and can be downloaded from “My Account”.
* That **emails** to the customer work with the correct text and attachments.
All of this can be validated end-to-end in the sandbox without consuming quota, without sending anything via PEPPOL, and without generating real accounting entries.
### Step 1: Create a staging environment for your WordPress
[Section titled “Step 1: Create a staging environment for your WordPress”](#step-1-create-a-staging-environment-for-your-wordpress)
You have two options:
**Option A — Hosting staging (recommended if available)**
Most managed WordPress providers include a one-click staging system from their control panel:
* **SiteGround** — *Site Tools → Dev → WordPress Staging*
* **Kinsta** — *Sites → Create Staging Environment*
* **WP Engine** — *Add Staging Environment*
* **Cloudways** — *Application → Clone App / Create Staging*
* **Hostinger** — *Hosting → Manage → Create staging*
Check your hosting panel before installing anything — it is the cleanest way to have a staging environment.
**Option B — WP STAGING plugin**
If your hosting does not offer staging or you do not have access to it, the free **WP STAGING** plugin ([wordpress.org/plugins/wp-staging](https://wordpress.org/plugins/wp-staging/)) creates a clone of your store in a subdirectory (e.g. `yourstore.com/staging`) with a single click. The clone has its own login, its own database, and does not touch any data from the real store.
> 💡 Never mix a B2BRouter sandbox with a production store: always confirm that the URL where you configure the plugin ends in `/staging` or a test domain before entering any key.
### Step 2: Get a sandbox API key
[Section titled “Step 2: Get a sandbox API key”](#step-2-get-a-sandbox-api-key)
1. Log in to B2BRouter with your usual account.
2. Go to **Developers → Sandboxes** and open (or create) a sandbox.
3. Inside the sandbox, go to **API Keys** and generate a new key. It will start with the prefix `test_`.
4. Copy the key — you will need it in the next step.
For more details about what the sandbox includes and does not include, consult the [sandbox technical documentation](https://developer.b2brouter.net/docs/sandbox).
### Step 3: Configure the plugin on staging
[Section titled “Step 3: Configure the plugin on staging”](#step-3-configure-the-plugin-on-staging)
On your **staging** WordPress (not on production):
1. Install and activate the B2BRouter plugin as described in the “Requirements and Installation” section.
2. In the plugin settings, enter the `test_…` key you generated.
3. Configure the rest of the parameters (numbering pattern, taxes, PDFs, etc.) as you would in production — you can return to the “Initial Configuration” section for details.
### Step 4: Configure sandbox webhooks
[Section titled “Step 4: Configure sandbox webhooks”](#step-4-configure-sandbox-webhooks)
🚧 Webhook support in the sandbox environment is coming soon.
### Step 5: Pre-production checklist
[Section titled “Step 5: Pre-production checklist”](#step-5-pre-production-checklist)
Place a test order on staging and confirm:
* [ ] An invoice is generated in the B2BRouter sandbox when the order is completed.
* [ ] The invoice number follows the pattern you configured.
* [ ] Taxes are correct, including reverse charge if you make intra-community B2B sales.
* [ ] If the customer enters a VAT number, it is recorded in the invoice.
* [ ] The webhook reaches staging and the order status is updated in WooCommerce.
* [ ] The PDF downloads correctly from “My Account” (it will carry the **“Not valid”** mark — this is normal in sandbox).
* [ ] Emails fire with the correct attachments.
### Step 6: Go to production
[Section titled “Step 6: Go to production”](#step-6-go-to-production)
When everything works on staging:
1. On your **production** WordPress, install the plugin (if you have not done so yet) and configure it **from scratch** with the `prod_…` key from your real account.
2. Configure the webhooks on the **production account** of B2BRouter pointing to the production WP URL.
3. **Do not migrate data** from staging to production — test invoices (those marked as “Not valid”) must stay in the sandbox only.
4. Place a small real test order to confirm that the entire flow also works in production.
***
## Initial Configuration
[Section titled “Initial Configuration”](#initial-configuration)
### Step 1: Obtain B2Brouter Credentials
[Section titled “Step 1: Obtain B2Brouter Credentials”](#step-1-obtain-b2brouter-credentials)
1. Register at [app.b2brouter.net](https://app.b2brouter.net)
2. Purchase an **eDocExchange** subscription.
3. Go to **Developers → API Keys** in the B2Brouter dashboard.
4. Copy the API key and store it securely.
> 💡 You are configuring the production environment. If you have not tested the plugin yet, we recommend doing so first in a staging environment with a sandbox key. See the “[Testing the plugin in a staging environment](#testing-the-plugin-in-a-staging-environment)” section.
### Step 2: Configure the Plugin
[Section titled “Step 2: Configure the Plugin”](#step-2-configure-the-plugin)
1. **Access the settings**:
* In WordPress, go to **Invoices → Settings**
2. **Enter the API key**:
* Paste your B2Brouter API key.
* Click **Validate Key**.
* If the API key is correct, the plugin will display your account information.
3. **Configure the generation mode**:
* **Automatic**: The plugin automatically generates the invoice when an order moves to the Completed status. This is the recommended option if you want to ensure that all orders have an associated invoice.
* **Manual**: The plugin does not generate the invoice automatically. The invoice can be generated manually from each order’s detail page and, once the order is in completed status, the customer can also generate it from their account page. This is the recommended option if you prefer the user to decide when to generate the invoice.
### Step 3: Configure Invoice Numbering
[Section titled “Step 3: Configure Invoice Numbering”](#step-3-configure-invoice-numbering)
1. **Series Codes**:
* **Invoice series code**: Prefix for regular invoices (e.g., “INV”, “FAC”)
* **Credit note series code**: Prefix for credit notes (e.g., “CN”, “NC”)
2. **Numbering pattern** (choose one option):
**a) Automatic** (Recommended):
* B2Brouter generates sequential numbers automatically.
* No need to manage counters.
**b) WooCommerce order number**:
* Uses the native WooCommerce order number.
* Example: If the order is #1234, the invoice will be INV-1234.
**c) Sequential**:
* The plugin maintains an independent counter
* Generates numbers like: INV-00001, INV-00002, INV-00003
**d) Custom pattern**:
* Define your own format using placeholders:
* `{order_id}`: Order ID
* `{order_number}`: WooCommerce order number
* `{year}`: Current year (YYYY)
* `{month}`: Current month (MM)
* `{day}`: Current day (DD)
Custom pattern examples:
* `INV-{year}-{order_id}` → INV-2025-123
* `{year}/{month}/{order_number}` → 2025/12/1234
* `INV{year}{month}{day}-{order_id}` → INV20251211-123
### Step 4: Configure PDFs
[Section titled “Step 4: Configure PDFs”](#step-4-configure-pdfs)
1. **Automatic PDF cache**:
* Enable this option to download and save PDFs on the server, inside the `wp-content/uploads/b2brouter-Invoices/` folder
* Improves response time for customer downloads.
* Reduces calls to the B2Brouter API.
2. **Automatic cleanup**:
* Enable scheduled cleanup to delete PDFs that exceed the number of days defined in the retention period.
* Configure the retention period (default: 90 days).
* Cleanup runs daily via WordPress cron.
3. **Attach PDFs to emails**:
* **Attach to completed order email**: Includes the PDF when the confirmation email is sent.
* **Attach to customer invoice email**: Includes the PDF in WooCommerce invoice emails.
* **Attach to refund email**: Includes the credit note in refund emails.
***
## Invoice Generation Mode
[Section titled “Invoice Generation Mode”](#invoice-generation-mode)
### Automatic Generation
[Section titled “Automatic Generation”](#automatic-generation)
If you have enabled **Automatic Mode**:
1. The customer completes a purchase.
2. Process the order and mark it as **Completed**.
3. The plugin:
* Automatically generates the invoice in B2Brouter.
* Saves the invoice metadata (ID, number, date).
* Adds a note to the order.
* Generates and saves the PDF (if enabled).
* Sends the email with the PDF attached (if configured).
### Manual Generation - Individual Order
[Section titled “Manual Generation - Individual Order”](#manual-generation---individual-order)
If you have enabled **Manual Mode** and need to generate an invoice:
1. Go to **WooCommerce → Orders**.
2. Open the order you want to invoice.
3. Locate the **B2Brouter Invoice** meta box.
4. Click the **Generate Invoice** button.
5. The metabox will display the status and main details of the invoice.
6. You can download the PDF directly from here.
### Manual Generation - Bulk Actions
[Section titled “Manual Generation - Bulk Actions”](#manual-generation---bulk-actions)
To process multiple orders at once:
1. Go to **WooCommerce → Orders**
2. Select the completed orders you want to invoice
3. In the bulk actions dropdown menu, choose **Generate B2Brouter Invoices**
4. Click **Apply**
5. You will see a message with the result for each order:
* Successfully generated invoices
* Invoices that already existed
* Errors during generation
### Generating Credit Notes
[Section titled “Generating Credit Notes”](#generating-credit-notes)
Credit notes are generated for WooCommerce refunds:
1. Open the order in **WooCommerce → Orders**
2. In the ‘Item’ meta box, click **Refund** (the order must have an invoice).
3. Enter the refund amount, which can be full or partial.
4. Confirm the refund.
5. The credit note is automatically generated in the country-specific format (e.g., Spanish rectifying invoices).
***
## Tax Configuration
[Section titled “Tax Configuration”](#tax-configuration)
The plugin reads the WooCommerce tax configuration and automatically converts it into PEPPOL tax categories.
### PEPPOL Tax Categories
[Section titled “PEPPOL Tax Categories”](#peppol-tax-categories)
* **S (Standard rate)**: Standard rate - Applied when taxes exist.
* **E (Exempt from tax)**: Exempt from tax - Taxable products with a 0% rate.
* **Z (Zero-rated goods)**: Zero-rated goods - Products with an explicit zero tax class.
* **NS (Not subject to tax)**: Not subject to tax - Non-taxable products.
* **AE (VAT Reverse Charge)**: Reverse charge - Automatically detected for intra-EU B2B transactions.
### Tax Configuration in WooCommerce
[Section titled “Tax Configuration in WooCommerce”](#tax-configuration-in-woocommerce)
1. **Enable tax calculation**:
* Go to **WooCommerce → Settings → General**.
* In the **Tax and coupon** section, enable “Enable tax rates and calculations”.
2. **Configure taxes**
* Once taxes are enabled, go to **WooCommerce → Settings → Tax**. From this section you can configure the different tax rates:
* **Configure standard rates**:
* Click the **Standard Rates** tab.
* Add your rates by country:
* Country code
* Tax percentage
* Tax name (VAT, etc.)
* Whether it applies to shipping
* **Configure zero rate**:
* Click the **Zero Rate** tab.
* Add your rates by country:
* Country code
* Tax name (VAT, etc.)
* Whether it applies to shipping
* **Configure reduced rate**:
* Click the **Reduced Rate** tab.
* Add your rates by country:
* Country code
* Tax percentage
* Tax name (VAT, etc.)
* Whether it applies to shipping
### Intra-EU Reverse Charge
[Section titled “Intra-EU Reverse Charge”](#intra-eu-reverse-charge)
The plugin automatically detects when reverse charge applies:
**Conditions**:
* The customer provides a valid VAT number
* The customer’s country is different from the merchant’s country
* Both countries are EU member states (27 states)
**What the plugin does**:
* Marks the transaction as reverse charge (AE category)
* Generates the invoice without taxes applied to the customer
* Indicates that the customer must declare the taxes in their own country
***
## NIF/VAT Collection
[Section titled “NIF/VAT Collection”](#nifvat-collection)
### NIF Field Configuration
[Section titled “NIF Field Configuration”](#nif-field-configuration)
The plugin automatically adds a VAT number field to the WooCommerce checkout. Compatible with:
* Block-based checkout (WooCommerce 8.6+)
* Classic shortcode-based checkout
### How It Works
[Section titled “How It Works”](#how-it-works)
1. **At Checkout**:
* The “NIF / VAT Number” field appears in the contact information section.
* It is optional by default.
* The customer can enter their VAT number for B2B/B2C invoices.
2. **Storage**:
* The VAT number is saved as `_billing_tin` in the order metadata box.
* It is also saved to the customer’s profile for reuse.
3. **Invoicing**:
* If the customer provides a VAT number, a **standard invoice** (IssuedInvoice) is generated
* If there is no VAT number, a **simplified invoice** (IssuedSimplifiedInvoice) is generated
* For intra-EU transactions with a VAT number, reverse charge is applied
### VAT Number Validation
[Section titled “VAT Number Validation”](#vat-number-validation)
Currently the plugin does not validate the VAT number format. Validation can be added via:
* Third-party validation plugins
* Custom validation with WooCommerce hooks
* Validation through the B2Brouter API
***
## Webhook Configuration
[Section titled “Webhook Configuration”](#webhook-configuration)
> **Production** and **sandbox** webhooks are completely independent: each has its own configuration and events do not communicate between environments. If you are testing the plugin on staging with a sandbox key, configure the webhooks inside the sandbox pointing to the staging URL. When you move to production, you will need to configure them again on the production account.
Webhooks allow real-time invoice status updates (< 1 second) instead of waiting for the hourly check.
### Why Use Webhooks?
[Section titled “Why Use Webhooks?”](#why-use-webhooks)
* **Instant updates**: Invoice status appears immediately in the WordPress admin.
* **Less load**: Reduces API calls
* **More reliable**: With hourly fallback check
### Step-by-Step Configuration
[Section titled “Step-by-Step Configuration”](#step-by-step-configuration)
#### Step 1: Get the URL from the WordPress Admin Panel
[Section titled “Step 1: Get the URL from the WordPress Admin Panel”](#step-1-get-the-url-from-the-wordpress-admin-panel)
1. Go to **Invoices → Settings**
2. Scroll down to **“Webhook Configuration”**
3. Enable **“Enable Webhooks”**.
4. **Copy the Webhook URL** from the read-only field
* It looks like: `https://yoursite.com/wp-json/b2brouter/v1/webhook`
5. Save the settings.
#### Step 2: Create the Endpoint in the B2Brouter Dashboard
[Section titled “Step 2: Create the Endpoint in the B2Brouter Dashboard”](#step-2-create-the-endpoint-in-the-b2brouter-dashboard)
1. Log in to [app.b2brouter.net](https://app.b2brouter.net)
2. Go to the **Developers** tab
3. Click on **Webhooks** in the main menu.
4. Click **“Add webhook”**
5. Enter a description, e.g. “WooCommerce Store”.
6. **Paste the Webhook URL** copied from WordPress.
7. Select the events to send. Recommended: select all.
8. Save the endpoint and **copy the signing key of the generated Webhook**.
#### Step 3: Complete the Configuration in the WordPress Admin Panel
[Section titled “Step 3: Complete the Configuration in the WordPress Admin Panel”](#step-3-complete-the-configuration-in-the-wordpress-admin-panel)
1. Return to **Invoices → Settings → Webhook Configuration**.
2. **Paste the signing key** from B2Brouter.
3. Leave the **“Enable Security Check”** option enabled.
4. Click **“Save Settings”**.
### Verification
[Section titled “Verification”](#verification)
To verify that webhooks are working correctly:
1. Generate a test invoice.
2. The status should appear in less than 1 second
3. If there are issues:
* Verify that the Webhook URL matches exactly.
* Make sure the Secret is pasted correctly (no extra spaces).
* Check that your WordPress site is accessible from the Internet.
### Webhook Security
[Section titled “Webhook Security”](#webhook-security)
* All requests are cryptographically signed with HMAC-SHA256.
* Requests with invalid signatures are rejected.
* 5-minute timestamp validation to prevent replay attacks.
* The secret is stored securely in WordPress options.
***
## PDF Management
[Section titled “PDF Management”](#pdf-management)
### Server Storage
[Section titled “Server Storage”](#server-storage)
When you enable “Automatic PDF Cache”:
1. **Location**: `wp-content/uploads/b2brouter-Invoices/`
2. **Structure**:
```plaintext
b2brouter-Invoices/
├── 2025/
│ ├── 01/
│ │ ├── invoice-123.pdf
│ │ └── credit-note-124.pdf
│ └── 02/
│ └── invoice-125.pdf
```
### Automatic Cleanup
[Section titled “Automatic Cleanup”](#automatic-cleanup)
The plugin includes a scheduled task to clean up old PDFs:
1. **Configuration**:
* Enable the “Automatic cleanup” checkbox.
* Set the retention period (default: 90 days)
2. **How it works**:
* Runs daily via WordPress cron.
* Deletes files older than the retention period.
* Maintains the folder structure.
* Logs cleanups to the WordPress log.
3. **Disabling cleanup**:
* If you disable cleanup, PDFs are kept indefinitely.
* You will need to manage disk space manually.
### Manual Invoice Download from the WordPress Admin
[Section titled “Manual Invoice Download from the WordPress Admin”](#manual-invoice-download-from-the-wordpress-admin)
1. Go to **WooCommerce → Orders** and select the order whose invoice you want to download.
2. From the order’s “B2Brouter Invoice” metabox, click **Download PDF**.
3. If the PDF is cached, it downloads immediately
4. If not, the plugin:
* Requests the PDF from B2Brouter.
* Caches it (if enabled).
* Downloads it to your browser.
***
## Customer Experience
[Section titled “Customer Experience”](#customer-experience)
### Invoice Downloads
[Section titled “Invoice Downloads”](#invoice-downloads)
Customers can access their invoices in several ways:
1. **From their account → Orders**:
* The order list is displayed.
* If the invoice has already been generated, the **Download Invoice** button will appear in the **Actions** column of the order list. Click to download the invoice.
* If the invoice has not been generated, the **Generate Invoice** button will appear in the **Actions** column of the order list. Click to generate the invoice. Once generated, the button will change to **Download Invoice**. Click to download the invoice.
2. **From their account → Orders → Order**:
* From the order list, in the **Actions** column of an order, click **View** on the order.
* On the order details page, you will find:
* **Download Invoice** button.
* **Download Credit Note** button (if applicable).
2. **From email**:
* If you have enabled “Attach to emails”
* The PDF is included as an attachment in the confirmation email
3. **Customer-generated invoices** (Manual Mode):
* If the mode is Manual, customers can generate invoices themselves
* On the order details page, they will see a **Generate Invoice** button
* After clicking, the page refreshes and the PDF is available
### Security
[Section titled “Security”](#security)
The plugin implements several security measures:
1. **Ownership verification**: Customers can only access their own invoices.
2. **Status validation**: Invoices can only be generated for **completed orders**.
3. **CSRF protection**: All actions use WordPress nonces.
4. **Mode validation**: Customer-side generation only works in Manual mode.
***
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
### Common Issues
[Section titled “Common Issues”](#common-issues)
#### 1. API Key Validation Error
[Section titled “1. API Key Validation Error”](#1-api-key-validation-error)
**Symptoms**: Message “Error validating the API key”
**Solutions**:
* Verify that the API key is correct.
* Make sure your eDocExchange subscription is active.
* Check that your server can connect to B2Brouter (not blocked by a firewall).
#### 2. Invoices Are Not Generated Automatically
[Section titled “2. Invoices Are Not Generated Automatically”](#2-invoices-are-not-generated-automatically)
**Symptoms**: Orders are completed but no invoices are generated.
**Solutions**:
* Verify that the mode is set to **Automatic** in the settings.
* Check that the order status is **Completed**.
* Review the order notes for any errors.
* Check the WordPress logs for detailed errors.
#### 3. PDFs Are Not Attached to Emails
[Section titled “3. PDFs Are Not Attached to Emails”](#3-pdfs-are-not-attached-to-emails)
**Symptoms**: Emails are sent but without the PDF attachment
**Solutions**:
* Verify that “Auto-save PDFs” is enabled.
* Check that “Attach to emails” is enabled.
* Make sure the invoice has been generated before the email is sent.
* Review the SMTP server logs for any sending errors.
#### 4. Webhooks Are Not Working
[Section titled “4. Webhooks Are Not Working”](#4-webhooks-are-not-working)
**Symptoms**: Invoice status is not updated immediately
**Solutions**:
* Verify that the Webhook URL matches exactly.
* Check that the Webhook Secret is correct.
* Make sure your site is accessible from the Internet (webhooks do not work on localhost without tunneling).
* Review the WordPress logs to see if requests are arriving.
* Check that there are no SSL errors on your site’s certificate.
#### 5. Error “Could not generate the PDF”
[Section titled “5. Error “Could not generate the PDF””](#5-error-could-not-generate-the-pdf)
**Symptoms**: The invoice is created but the PDF cannot be downloaded
**Solutions**:
* Check that the `wp-content/uploads/` directory has write permissions.
* Review the invoice status in B2Brouter (it may not have been processed yet).
* Try regenerating the PDF forcefully (Shift + click).
* Verify that the B2Brouter API is operational.
#### 6. PDFs carry the “Not valid” watermark or webhooks are not arriving
[Section titled “6. PDFs carry the “Not valid” watermark or webhooks are not arriving”](#6-pdfs-carry-the-not-valid-watermark-or-webhooks-are-not-arriving)
**Symptom:** generated invoices download with a watermark that reads **“Not valid”**, and/or the order status in WooCommerce is not updated after payment is processed.
**Most likely cause:** you are using a **sandbox** API key (starts with `test_`) instead of a **production** key (starts with `prod_`), or your webhooks are configured in the sandbox instead of the real account.
**Solution:**
1. Check the API key prefix in the plugin settings:
* `test_…` → you are in sandbox. Documents have no legal validity and production webhooks will not arrive.
* `prod_…` → you are in production. This is what you want on the real store.
2. If you have confirmed the key is `prod_…` but webhooks are still not arriving, go to the **production account** of B2BRouter (not the sandbox) and review the webhook configuration.
3. If you are intentionally using the `test_…` key for testing, make sure you are doing so in a **staging** environment and not on the real store. See the “[Testing the plugin in a staging environment](#testing-the-plugin-in-a-staging-environment)” section.
***
#### 7. Incorrect Tax Errors
[Section titled “7. Incorrect Tax Errors”](#7-incorrect-tax-errors)
**Symptoms**: Tax categories are not correct on the invoices
**Solutions**:
* Review the tax configuration in **WooCommerce → Settings → Tax**
* Make sure the tax classes are configured correctly.
* Verify that products have the correct tax class assigned.
* For non-taxable products, set “Tax status” to “None”.
### Enabling Debug Mode
[Section titled “Enabling Debug Mode”](#enabling-debug-mode)
To get more information about errors:
1. Add these lines to your `wp-config.php`:
```php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
```
2. Errors will be logged to `wp-content/debug.log`
3. Search for entries containing “B2Brouter” to see plugin errors
### Getting Help
[Section titled “Getting Help”](#getting-help)
If you cannot resolve the issue:
1. **B2Brouter Support**: eDocExchange customers have access to premium support with:
* Priority ticket resolution.
* Support seven days a week.
2. **GitHub Issues**: [github.com/B2Brouter/b2brouter-woocommerce/issues](https://github.com/B2Brouter/b2brouter-woocommerce/issues)
***
## Best Practices
[Section titled “Best Practices”](#best-practices)
### Test Environment
[Section titled “Test Environment”](#test-environment)
* Start with the **[sandbox](/en/developers/testing/sandbox/)** — it is the recommended environment for most tests and initial development work. Use the full staging environment only for large-scale or broad integration tests.
* Test the entire invoicing flow before going to production.
* Generate test orders with different configurations (with/without VAT number, various countries, etc.).
### Backups
[Section titled “Backups”](#backups)
* Perform regular backups of your WordPress database
* Include the `wp-content/uploads/b2brouter-Invoices/` directory in your backups
* Test backup restoration periodically
### Monitoring
[Section titled “Monitoring”](#monitoring)
* Regularly review the **B2Brouter → Invoices** page to check the status.
* Set up notifications for invoice generation errors.
* Check the WordPress logs periodically.
### Updates
[Section titled “Updates”](#updates)
* Keep the plugin updated to the latest version.
* Review the changelog before updating.
* Test updates in the staging environment first.
### Performance
[Section titled “Performance”](#performance)
* Enable “Auto-save PDFs” to improve response time.
* Configure automatic cleanup to manage disk space.
* Use webhooks for real-time status updates.
***
## Legal Requirements
[Section titled “Legal Requirements”](#legal-requirements)
### Tax Compliance
[Section titled “Tax Compliance”](#tax-compliance)
The plugin helps with tax compliance, but:
* **Responsibility**: The merchant is responsible for tax compliance.
* **Configuration**: Make sure the tax configuration is correct for your country.
* **Advice**: Consult with a tax advisor for the specific requirements of your country.
### Data Protection (GDPR)
[Section titled “Data Protection (GDPR)”](#data-protection-gdpr)
The plugin processes personal data:
* **Legal basis**: Necessary for compliance with legal obligations.
* **Data processed**: Name, address, VAT number, order information.
* **Storage**: Data is saved in WordPress and sent to B2Brouter.
* **Retention**: Configure the retention period according to your legal requirements.
Update your privacy policy to include:
* The use of B2Brouter for invoice generation.
* The data that is sent to B2Brouter.
* How customers can exercise their rights.
***
## Conclusion
[Section titled “Conclusion”](#conclusion)
This plugin provides a complete integration between WooCommerce and B2Brouter for automated electronic invoice management. By following this guide, you will be able to configure and use all the plugin’s features effectively.
For any questions or issues, do not hesitate to contact B2Brouter support or open an issue in the GitHub repository.
**Enjoy automated electronic invoicing with B2Brouter!** 🎉
# B2Brouter directory
> Learn how the B2Brouter directory fills in verified contact data, routing codes and invoice sending methods automatically.
B2Brouter has a directory of organizations (national and international public institutions and private companies) to offer a more automatic and faster search functionality. It is often difficult to find the details of a contact to whom we want to send an invoice: Which department should I send it to? Which code DIR3, DIRE, Peppol Id, etc. should I add? How do I find the correct code for the organizational unit (head office, department, delegation…)?
The B2Brouter directory consists of a large database that automatically provides you with all the necessary data for the invoice to be sent correctly. This procedure is executed when you select a contact verified by the directory and can vary depending on the contact (Spanish or European public administration or private company), since each one has specific requirements.
Thanks to the directory you will no longer have to worry about asking for specific addresses or codes, as they are permanently available and updated in B2Brouter.
Here are the steps to follow to create an invoice using the data in our directory:
## 1. Create a new contact
[Section titled “1. Create a new contact”](#1-create-a-new-contact)
From the **Contacts** section, [create a new customer or supplier](/en/user-guide/contacts/create-contact). Once you have added the contact’s VAT number and go ahead, the system fills in and relates all the data related to the name, address, routing codes and the method of sending invoices. Finally, all that remains is to save the changes to finish creating the contact.
## 2. Create the invoice, select the contact and add the routing codes
[Section titled “2. Create the invoice, select the contact and add the routing codes”](#2-create-the-invoice-select-the-contact-and-add-the-routing-codes)
[Create a new invoice](/en/user-guide/sending-documents/create-electronic-invoice) and select the customer. If it is a contact that is in the B2Brouter directory and has associated data, the option **Organizational unit** is activated from where you can select the headquarters, department, delegation and related codes (DIR3, DIRE, etc.).
If the contact has only one combination of codes, these are added automatically without you having to select any option in **Organizational Unit**.
Finally, you can proceed to create the invoice.
# Create a contact
> Learn how to create customers and suppliers in B2Brouter, set fiscal data, invoice sending methods and payment details.
To create your customers and/or suppliers, go to the **Contacts** section in the top menu. Once in this section, you can create your customers in two ways:
## 1. Create a contact manually
[Section titled “1. Create a contact manually”](#1-create-a-contact-manually)
To create a new contact manually, once you are in the **Contacts** section, use the **Add Contact** option.

#### Identification
[Section titled “Identification”](#identification)
* Through the **Country** dropdown, select your contact’s country.
* In the next box, add your contact’s Tax Identification Number (in the case of Spain, the NIF or DNI).
If you have selected a country where companies can have different types of identifiers, the option **You do not have (country identifier name)** will be activated at the bottom. Activate it and you will be able to enter another type of identifier:
* Fill in the identifier information in the corresponding box.
* You can only enter one of the above mentioned identifiers to be able to invoice through B2Brouter.
Finally, use the option **Validate identifier and continue**.
Now two things can happen, depending on whether the company you are creating is in the B2Brouter directory (Verified data) or not (Unverified data):
### The contact is in the B2Brouter directory.
[Section titled “The contact is in the B2Brouter directory.”](#the-contact-is-in-the-b2brouter-directory)
The information of the **Fiscal data** and the **Invoice sending method** of your contact are added automatically, using the data in the B2Brouter directory.
You only need to select whether you want to add a **Customer**, a **Supplier** or a **Customer and supplier** at the top, as well as adding the information from the last two sections **Invoice payment method** and **Language** (see the next section).
Finally, use the **Continue** option. The contact is created in B2Brouter and you can [start invoicing](/en/user-guide/sending-documents/create-electronic-invoice).
### The contact is not in the B2Brouter directory.
[Section titled “The contact is not in the B2Brouter directory.”](#the-contact-is-not-in-the-b2brouter-directory)
If the data are not entered automatically, it means that the company does not appear as verified in our directory. In this case, the contact data must be entered manually.
#### Fiscal data
[Section titled “Fiscal data”](#fiscal-data)
First of all, select the type of contact you want to add: **Customer**, **Supplier** or **Customer and supplier**.
Next, add the contact’s fiscal data such as name, address, postcode, etc. Please note that fields marked with an asterisk are mandatory.
#### Invoice sending method
[Section titled “Invoice sending method”](#invoice-sending-method)
This section is very important, as this is where you select the method or network by which the invoices are sent to this contact. Firstly, you have to check one of the options according to the type of customer:
##### E-mail link
[Section titled “E-mail link”](#e-mail-link)
This option allows sending an e-mail to the client, with a link to the invoice. The receiver only has to click on the link and accesses the invoice within B2Brouter, from where he can download it.
##### Attachment by e-mail
[Section titled “Attachment by e-mail”](#attachment-by-e-mail)
This option allows sending an e-mail to the client with the invoice attached. You can also send the invoice in different formats, selecting the one you prefer from the drop-down **Transport**:
##### E-mail attachment
[Section titled “E-mail attachment”](#e-mail-attachment)
This option allows you to send an email to the customer with the invoice attached. You can also send the invoice in different formats, selecting the one you prefer from the drop-down **Method**:
* PDF
* Facturae: Spanish electronic invoice format.
* UBL (Universal Business Language): European electronic invoice format.
If you select the last option, remember to indicate the e-mail address to which you wish to send the invoice in the previous step, where the customer’s general data are indicated.
Several email addresses
The contact’s email field accepts several addresses separated by commas, for example `billing@company.com,accounting@company.com`. Write the list with a comma and no spaces: all the addresses will receive the invoices. The same syntax works in the **Send to** field when you send an invoice.
##### Open network (public administrations and Peppol)
[Section titled “Open network (public administrations and Peppol)”](#open-network-public-administrations-and-peppol)
This option allows you to send electronic invoices to the main open networks to which the public administrations of different countries are adhered, as well as to private companies that, due to the regulations of their country, need to send or receive electronic invoices. Once you have checked this option, you must select where you want to send the invoice through the drop-down menu **Method**:
**Europe**
* *ePrior*: if you select this option, you must also indicate the customer’s ePrior identifier. Select the type of identifier in the **Eprior Scheme ID** dropdown and then add the identifier in **Eprior Endpoint ID**.
* *Peppol*: if you select this option, you must also specify the client’s Peppol identifier. To do this, you must first [configure Peppol connection](https://www.b2brouter.net/es/facturacion-electronica-segura-a-traves-de-la-red-peppol/).
Under **Company Peppol ID** select the type of identifier from the drop-down menu and then add the Peppol identifier.
**Spain**
* *FACe*: it is the General Entry Point of the Ministry where practically all the public administrations of the country are adhered.
* *FACe detached*: it is the same as the previous one but, in the case of sending files attached to the invoice, they are sent separately.
* *FACeB2B*: this is the General Entry Point that allows private Spanish companies to exchange electronic invoices. Currently only government contractors and subcontractors are required to use it.
* *FACeB2B detached*: it is the same as the previous one but, in the case of sending attached files to the invoice, they are sent separately.
* *eFACT Catalonia*: it is the General Entry Point where practically all the public administrations of Catalonia are adhered.
* *Junta de Andalucía*: it is the General Entry Point where the administrations related to the Junta de Andalucía (not visible) are adhered.
* *Junta de Castilla - La Mancha (PeCAM)*: is the General Entry Point where the administrations related to the Junta de Castilla - La Mancha are adhered. [To send to this General Entry Point it is necessary to configure the connection first](/en/user-guide/account-settings/setting-up-your-company?id=junta-castilla-la-mancha-pecam).
* *Junta de Galicia*: this is the General Entry Point where the administrations related to the Junta de Galicia (not visible) are adhered. [To send to this General Entry Point it is necessary to configure the connection first](/en/user-guide/account-settings/setting-up-your-company?id=xunta-de-galicia).
* *Government of the Canary Islands (PeFAC)*: it is the General Entry Point where the administrations related to the Government of the Canary Islands are adhered.
* *Eusko Jauralitza (Basque Government)*: it is the General Entry Point where some of the administrations related to the Basque Government are adhered. [To send to this General Entry Point it is necessary to configure the connection first](/en/user-guide/account-settings/setting-up-your-company?id=eusko-jaurlaritza-govern-basc).
* *Osakidetza (Basque Health Department)*: this is the General Entry Point where the administrations of the Basque Health Department are attached. [To send to this General Entry Point it is necessary to configure the connection first](/en/user-guide/account-settings/setting-up-your-company?id=osakidetza).
* *Diputación de Álava*: this is the General Entry Point where the administrations related to the Diputación de Álava are attached (not visible).
* *Diputación de Vizcaya*: it is the General Entry Point where the administrations related to the Diputación de Vizcaya are adhered (not visible).
* *Water Consortium of Bilbao Vizcaya*: is the General Entry Point of the Water Consortium of Bilbao Vizcaya.
* *eEfaktur*: it is the General Entry Point where some administrations of the autonomous community of the Basque Country are adhered.
**France**
* **Chorus**: it is the platform where you have to send electronic invoices to invoice the French public administration. To be able to do this, you first need to [register on the Chorus Pro portal](https://chorus-pro.gouv.fr/cpp/utilisateur?execution=e1s1).
If you select Chorus Pro as the sending method, at the bottom of the **Method** drop-down, the **Service code** box is activated, where the customer’s service code must be added in order to be able to send the invoice correctly.
**Italy**.
* *SDI*: this is the platform through which electronic invoices are sent for invoicing in public and private companies in Italy.
If SDI is selected as the sending method, several options are activated at the bottom of the **Method** drop-down menu:
* Activate the **Is a Public Administration** option if the customer is a public administration.
* In the **VAT collection** dropdown, select the desired option.
* In the **Addressee Code** option, add the customer’s addressee code.
**Portugal**
* *FE-AP Saphety*
* *FE-AP*
* *efacturaGov*
These are the platforms through which electronic invoices are sent to the Portuguese public administration.
**Cyprus**
* *Ariadne*: is the platform through which electronic invoices are sent to the public administration of Cyprus.
**Andorra**
* *Government of Andorra*: is the platform through which electronic invoices are sent to the Government of Andorra.
In order to send an electronic invoice to the Government of Andorra, you must have processed and registered your digital certificate with the client.
**United Kingdom**
* *Peppol NHS*: is the network that allows the exchange of electronic documents between the English health service and its providers. To do this, you first need to [configure the Peppol connection](https://www.b2brouter.net/es/facturacion-electronica-segura-a-traves-de-la-red-peppol/).
If you select Peppol NHS as sending method, at the bottom of the **Method** drop-down, the **Peppol Company ID** options are activated, where you need to add the customer’s Peppol identifier.
**Germany**.
* *Peppol XRechnung*
* *Peppol ZuGFeRD*
If you select either of the two formats as shipping method, at the bottom of the **Method** drop-down the **Peppol Company ID** options are activated, where the customer’s Peppol identifier has to be added.
**Netherlands**.
* *Peppol NLS CIUS*: this is the e-invoice format used in the Netherlands.
If you select this option as the shipping method, the **Peppol Company ID** options are activated at the bottom of the **Method** dropdown, where the customer’s Peppol ID must be added.
**Australia / New Zealand**
* *Peppol AU-NZ*: this is the e-invoice format used for invoicing in the Australian and New Zealand public administration.
If you select this option as sending method, at the bottom of the **Method** dropdown, the **Peppol Company ID** options are activated, where you have to add the customer’s Peppol identifier.
**Singapore**.
* *Peppol SG*
This is the network through which electronic invoices are sent to Singapore.
#### Invoice payment method
[Section titled “Invoice payment method”](#invoice-payment-method)
From this space you can configure a default payment method for the invoices you send to the customer you are creating:
* In the **Collection method** dropdown, select how you want to receive payment for the invoices.
* In the drop-down **Receive payment on this account**, select the account number or IBAN where you want to receive the payment.
* In the **Expiration** drop-down list, select the due date of the invoices.
**What applies to each invoice** — When you send an invoice to this contact, the payment fields (method, bank account, due date, currency) follow this priority order:
> 1. **Whatever the invoice itself carries** — whether you typed it manually on the web form or it came filled in on an invoice imported via API, SFTP, connectors, or PDF upload.
> 2. **Whatever is configured here on the contact** — when an imported invoice does not carry payment data, B2Brouter fills it in with these values. On the web form, the fields appear pre-filled with these values and you can change them before issuing.
> 3. **Empty** — if nothing is configured on the contact either.
>
> The [global payment configuration](/en/user-guide/account-settings/setting-up-your-company) on the account is the template that applies when you create a new contact; it does not act directly on existing invoices.
#### Language
[Section titled “Language”](#language)
In the **Language** dropdown, select the language in which this contact will receive the invoices: both the PDF and the email that B2Brouter sends use this language.
This setting is independent from the platform language of your own [user profile](/en/user-guide/my-user-profile/user-profile#preferences), which only changes the interface you see. If a client receives the invoices in the wrong language, edit their contact card and change this field.
Once you have filled in all the above data, a **Summary** tab opens with the general data of the contact created. In addition, you can see who created the contact, as well as more specific details in **Activity**. You also have the option to add **Comments** that you consider relevant.
On the other hand, if you go to **Actions** at the top right, you can directly create invoices and quotes from the created contact. You can also delete the contact.
## 2. Create your customers or suppliers automatically
[Section titled “2. Create your customers or suppliers automatically”](#2-create-your-customers-or-suppliers-automatically)
You can upload a CSV file with the data of your customers and, in this way, they are automatically created with the information contained in the imported file. From the **Contacts** section, use the **Import file** option:
* From the **Download CSV file example** link you can download an example of the file you need to import to create your customers automatically.
* Use the example to create the file with your customer data.
* Use the **Add file** option to find the file in your system and upload it to B2Brouter.
* Finally, use the **Import** option.
If the file is correct, your customers will be created automatically in B2Brouter.
## 3. Actions you can perform once you have created the contact
[Section titled “3. Actions you can perform once you have created the contact”](#3-actions-you-can-perform-once-you-have-created-the-contact)
Once you have created the contact, you can perform various actions, such as editing the contact details, adding organisational units or reference persons, and creating invoices and delivery notes from the contact.
### Edit the created contact
[Section titled “Edit the created contact”](#edit-the-created-contact)
Once you have created the contact, access the **Contacts** section from the main menu at the top. Select the contact you wish to edit and you will see the contact summary displayed. To edit any data, go to the left hand menu and select the section you require.
#### Fiscal data
[Section titled “Fiscal data”](#fiscal-data-1)
From this section you can edit your contact’s tax details such as name, address, postcode, etc.
#### Contact data
[Section titled “Contact data”](#contact-data)
In this section you can edit your contact’s details such as first and last name, telephone number, email address, etc.
#### Collection
[Section titled “Collection”](#collection)
In this section you can change your contact’s bank details, such as payment method, account number or payment due date.
#### Integration
[Section titled “Integration”](#integration)
From this section, you can add or modify the internal integration code that your client has in your management system.
#### Invoices
[Section titled “Invoices”](#invoices)
Here you can edit the method of sending invoices, the email address of your contact where notifications are sent, the format of the invoice, etc. The email field also accepts several addresses separated by commas.
#### Organisational units
[Section titled “Organisational units”](#organisational-units)
An Organisational Unit is a department, head office or delegation of a company. From this section you can add one or edit an organisational unit already created.
#### People
[Section titled “People”](#people)
The contact person can be the reference person of your client and/or supplier, or a contact person in a commercial prospecting. From this section you can add one or edit a person already created.
# Create an organizational unit
> Learn how to create branches, departments or offices for a B2Brouter contact and select them when creating invoices.
With B2Brouter you can create the headquarters or delegation of a company using the same NIF but modifying other data such as the name or address. To do this, you must follow the following steps:
## 1. Create the main company
[Section titled “1. Create the main company”](#1-create-the-main-company)
Go to the **Contacts** section in the main menu. Once there, select the **Add contact** option and you can start adding your contact’s data to create it. [Here you can see how to create a contact in B2Brouter](/en/user-guide/contacts/create-contact).
## 2. Create the site
[Section titled “2. Create the site”](#2-create-the-site)
Once you have created the contact (the main company), you can create the headquarters. Do it from the **Contacts** section, by clicking on the name of the company whose head office you want to create. Then, access the box labeled **Add organizational unit** on the menu in the left part of the page.
You then access the form that allows you to add the details of the head office, which is associated with the main company:
* You cannot modify the NIF.
* Other data such as name, address, etc. can be modified.
Once the data has been added, use the **Add organizational unit** option, which you can find at the bottom of the screen.
You can create as many locations as you wish.
## 3. Indicate the Organizational unit on the invoice
[Section titled “3. Indicate the Organizational unit on the invoice”](#3-indicate-the-organizational-unit-on-the-invoice)
Once you have created the location, you can select it when you generate an invoice. To do this, you must create a new invoice in the **Invoices** section and then click on the **New invoice** button located on the left side of the screen.
* In the **Customer** section, add the main company.
* Then, you can select the location you want to be included in this invoice in the drop-down menu **Organizational unit**, where you will find all the locations previously created.
# Import contacts
> Learn how to import customers and suppliers into B2Brouter by uploading a CSV file from the Contacts section.
You can create your contacts within B2Brouter automatically and massively, by uploading a CSV file containing the information. As a result, these are created automatically without having to enter the data manually. To do so, follow these steps:
* Through the top main menu, go to the **Contacts** section.
* Select the **Import file** option.
To import your customers’ information to B2Brouter, you can **Download example CSV file** from the following page. So:
* Click on the link **Download example CSV file**.
* The sample CSV file is downloaded, which you must copy with the data of your customers.
Once you have generated the file from your system with your customer data:
* Use the **Select files** option.
* Look for the file in your system.
* Use the **Import** option.
It is very important that the file is in CSV format and not in XLSX (Excel). If the imported file contains any errors, has not been generated correctly or the information you are trying to import belongs to a company already created, the system displays an informative error at the time of import.
# First steps
> Learn the first steps in B2Brouter: set up your account, create contacts and send your first electronic invoice.
Once you have registered and logged in, you are ready to send your first invoice. You only need to follow three steps.

## 1. Set up your account
[Section titled “1. Set up your account”](#1-set-up-your-account)
Before sending invoices, [set up your company](/en/user-guide/account-settings/setting-up-your-company/) by adding your fiscal data, bank accounts and any other configuration you need.
## 2. Create your first contact
[Section titled “2. Create your first contact”](#2-create-your-first-contact)
You need to have the client created in B2Brouter before you can invoice them. [Create your first contact](/en/user-guide/contacts/create-contact/) and select the delivery method you want to use to send the invoice. If your client is in the [B2Brouter directory](/en/user-guide/contacts/b2brouter-directory/), the sending configuration is done automatically — you only need to enter their tax ID.
## 3. Create and send the invoice
[Section titled “3. Create and send the invoice”](#3-create-and-send-the-invoice)
Once the contact is created, [create the invoice](/en/user-guide/sending-documents/create-electronic-invoice/) and add the data and line items you want to invoice.
When the invoice is ready, use the **Send** option at the top of the invoice. The invoice is sent directly from B2Brouter to your client — no download or upload needed. If the invoice was sent successfully, its status changes to **Sent**.
You can [check the meaning of all invoice statuses](/en/user-guide/sending-documents/status-issued-invoices/) at any time.
***
With B2Brouter you can also send [quotes](/en/user-guide/sending-documents/create-quotes/), [dispatch notes](/en/user-guide/sending-documents/create-dispatch-notes/) and receive [orders](/en/user-guide/receiving-documents/receive-orders/).
# Register as a user
> Learn how to sign up for B2Brouter, activate your account by email and log in for the first time.
B2Brouter is a cloud-based electronic invoicing platform that lets you send and receive invoices, quotes, orders and other business documents digitally — through public networks like Peppol, government portals, or directly by email — without installing any software.
It works for businesses of any size: from freelancers sending their first invoice to companies exchanging thousands of documents a month through automated integrations.
To start working with B2Brouter, you need to register as a user.
To do so, access **[B2Brouter](https://www.b2brouter.net/global/)** and use the option **[Start for free](https://app.b2brouter.net/en/register)**, which you can find in the top right corner of the screen. On the next page:
* Enter your email address in the **Email** box.
* Enter the password you want to use in **Password**.
* Check the box to accept the terms of use.
* Finally, click on **Continue**.

You receive an email to the email address you entered, where you will find an activation link:
* Go to your email inbox.
* Open our email.
* Use the link to activate your account.
Once your account is activated, the system redirects you to the **[Login](https://app.b2brouter.net/en/login/)** page, from where you can access your B2Brouter account for the first time:
* In the **Email** box, enter the email address you used when signing up.
* In **Password**, type the password you used when signing up.

Finally, click on **Login** and you can access your account to start to create your company.
# Document view modes
> Learn how to switch between original, preview and legal views for invoices, quotes, proformas and tax declarations in B2Brouter.
When viewing a document (issued invoices, received invoices, simplified invoices, quotes, proforma invoices, or tax declarations), you can display it in different viewing modes depending on the type of document.
You can switch between these views using the buttons located at the top of the document:
* **Original view:** Displays the document as it was generated from an XML file or other source file.
* **Preview:** Shows a readable preview of the document.
* **Legal view:** Displays the final version of the document, which will be sent, has been sent, or has been received, as applicable.
# Recurring invoices
> Learn how to create recurring invoice templates, set billing frequency and generate periodic invoices from templates in B2Brouter.
Recurring invoice management allows you to generate an invoice that you need to send periodically, from an already created template. This saves you from having to create the invoice each time. To manage recurring invoices, you first need to create the template. You have two ways to create the template:
## Create the template of a recurring invoice from a created invoice.
[Section titled “Create the template of a recurring invoice from a created invoice.”](#create-the-template-of-a-recurring-invoice-from-a-created-invoice)
You can generate the template, taking advantage of information from an invoice you have already created in the past. To do this, access the **Income** section through the main top menu and then **Commercial invoices** or **Tickets** through the options on the top left:
* Click on the number of the invoice you want to convert to template.
* In the top menu of the invoice, use the **More options** option.
* Use the **Save as recurring invoice** option.
On the next page you access the invoice creation form:
* It leverages the data from the invoice you are converting to recurring.
* You can modify some data if you want.
* In the **General data** section, in the **Frequency** dropdown, you must select the billing frequency of the recurring invoice.
Finally go to the bottom of the page, select **Continue** and on the next page, use the **Save and preview** option to finish creating the recurring invoice.
To view all the recurring invoice templates created, access **Revenues** through the top main menu and **Recurring** in the submenu that is activated just below the main menu.
## Create a recurring invoice template from the templates section.
[Section titled “Create a recurring invoice template from the templates section.”](#create-a-recurring-invoice-template-from-the-templates-section)
You can generate the recurring invoice template from the **Templates (recurring)** section. To do this, access the **Income** section via the main menu and then **Templates (recurring)** via the options on the top left:
* Use the **Create recurring invoice** option.
* On the next page use the drop down to select the invoice customer.
* Then use the **Continue** option.
You access the form that allows you to create the invoice. You must follow the steps as you always do. [Here you can see how to create an invoice with B2Brouter](/en/user-guide/sending-documents/create-electronic-invoice).
In the **General data** section you must select the billing frequency of the recurring invoice, using the drop-down **Frequency**.
Finally save the changes using the **Save and preview** option and the template is generated.
## Send a recurring invoice
[Section titled “Send a recurring invoice”](#send-a-recurring-invoice)
Access the **Income** section through the main menu and then **Templates (recurring)**. You have two ways to create recurring invoices from the template:
1. Use the **Create recurring invoice** option.
2. Through the list of the created templates, mark those templates that you want to convert into an invoice (box on the left of the list) and use the **Invoice** option that is activated in the upper right part of the list.
The invoice is created automatically, with the template information, and stored in the **Revenues** and **Templates (recurring)** section.
The option to generate the recurring invoice is only activated for those invoices that are in the period of being sent, in relation to the date of the template and the selected frequency.
# Rounding and VAT calculation
> Learn how B2Brouter handles VAT calculation and rounding adjustments for invoice totals without changing VAT amounts.
When you need to adjust a small cents difference on an invoice, it is important to keep in mind that in B2Brouter **VAT cannot be rounded**. The adjustment can only be applied to the **invoice total**.
## Basic rule
[Section titled “Basic rule”](#basic-rule)
* VAT is calculated according to the lines, discounts and taxes applied.
* Rounding does not modify the VAT amount.
* The adjustment is only used to correct the **final invoice total**.
* **Quotes** do not support a rounding adjustment.
## How to activate the rounding adjustment
[Section titled “How to activate the rounding adjustment”](#how-to-activate-the-rounding-adjustment)
To add the adjustment to an invoice:
1. Open the invoice in edit mode.
2. Go to the **Collection** section.
3. Activate the option **Add references, prepayments, withholdings**.
4. Use the **Rounding adjustment** field.
Once this field is filled in, the system adjusts the invoice total without altering the VAT calculation.
Keep in mind:
* The adjustment is a single signed amount: enter a positive value to increase the total, or a negative value to reduce it.
* The maximum value is **±0.99**.
* If the difference is bigger than 0.99, it is not a rounding difference: review the lines, discounts or taxes of the invoice.
## How it behaves with discounts
[Section titled “How it behaves with discounts”](#how-it-behaves-with-discounts)
Before applying the rounding adjustment, the system calculates the amounts and taxes normally according to the invoice data.
### Line discount
[Section titled “Line discount”](#line-discount)
If you apply a discount directly to a line:
* the discount affects the taxable base of that line
* VAT is recalculated on the resulting line amount
* the rounding adjustment, if used afterwards, only modifies the final total
### Discount on the invoice total
[Section titled “Discount on the invoice total”](#discount-on-the-invoice-total)
If you apply a discount to the invoice total:
* the system recalculates the amounts and taxes based on that overall discount
* VAT is still calculated automatically from the result
* the rounding adjustment should only be used to correct the final cents difference
## Limitation for quotes
[Section titled “Limitation for quotes”](#limitation-for-quotes)
Quotes do not allow a rounding adjustment.
If you need to apply this type of adjustment, you must do it on the invoice, not on the quote.
## When it is useful
[Section titled “When it is useful”](#when-it-is-useful)
The rounding adjustment is useful when:
* you need the final total to match an external amount
* there is a small cents difference due to calculation criteria
* you want to correct the total without changing the lines or taxes
If you need to modify the taxable base or the VAT amount, you should review the lines, discounts or taxes applied to the invoice. You can also check [how to create an invoice](/en/user-guide/sending-documents/create-electronic-invoice).
# Add and manage accounts
> Learn how to add a B2Brouter account, switch between company accounts and delete accounts from the account menu.
With B2Brouter you can create and manage as many accounts as you like.
Once you have signed up, access your account. When you are in your account:
* Open the drop-down menu in the black strip at the top left menu, where your company name appears.
* Click on the option **Add account**.
* On the next page, start to [set up your account](/en/user-guide/account-settings/setting-up-your-company).
Once you have created the different accounts you wish to manage, you can navigate between them by accessing the drop-down menu in the black band at the top of the menu and selecting the account you wish to work from.
To delete an account you have already created:
* Open the dropdown you find in the black strip in the top menu, where your company name appears.
* Use the option **My accounts**.
Here you will find a list of all your accounts.
* Click on the three dots next to each account.
* From the drop-down menu, select the **Delete** button.
It is important to note that, once deleted, you will not be able to recover the invoices that were created for that company.
# Recover access to your account
> Learn how to recover access to a B2Brouter account or request company ownership when the identifier already exists.
If you **cannot remember the email address** to access your account or you are creating a new company but the system informs you that the **identifier already exists on B2Brouter**, you need to follow these steps to retrieve or request ownership:
1. [Sign up to B2Brouter](/en/user-guide/getting-started/register-user) creating the user name you want to use from now on.
2. [Log into B2Brouter](https://app.b2brouter.net/global/login) with the user data you have just created.
3. Once you have accessed the new account, select the country of the company and add the identifier.
4. Finally, click on the option **Continue**.
The system informs you that the identifier is already being used on B2Brouter and it provides you with a fragment of the email associated with the account. Scrolling down, you find various options that you have to follow to be able to retrieve the account:
1. **Yes, it is mine or it belongs to a person known to me**: This option is not always available. If it does not appear, you can only proceed by following step 2.
Select this option if you know the owner of the previously mentioned email address or if it is an email account to which you have access. The request is sent to the email address and the recipient has to accept it by following the steps in the message.
Finally, choose the option **Request transfer of ownership** and wait until the recipient of the email accepts ownership.
2. **No, I do not recognise this email address**: If you do not recognise the email address provided or you recognise it but cannot access it, you must select this option. In doing so, you request the transfer of ownership to our Support Department.
There is a number of instructions that you need to follow carefully to complete the request. In short, they are the following steps:
a. In the **Comment** box, explain the reason for requesting ownership.
b. Use the option **Upload file** to upload the NIF or photocopy of the ID document, in relation to the request you are making.
c. Choose the option **Contact** the support team.
Finally, our support team receives the request. They validate the information and if everything is correct your request is accepted. Once this has been done, you receive an email notification and you have access to the company.
# Reset your password
> Learn how to reset your B2Brouter password, recover account access and sign in again using your email address.
If you have forgotten the password for your account or you are trying to access your account but the message “incorrect password” appears, you can generate a new password. To do so, access the [login page](https://app.b2brouter.net/en/login) of B2Brouter:
* Use the link called **[Forgot your password?](https://app.b2brouter.net/en/lost_password)** which you find under the boxes where you enter the login details.
* On the next page, the system asks you for the email address that is associated with your user name. Type it.
* Finally, click on **Submit**.
We send you an email to the email address indicated, with a link that enables you to change the password for your account:
* Open your email account.
* Open the email from B2Brouter.
* Use the option **Change password**.
The link redirects you to the B2Brouter page from where you can change the password for your account. From this page you need to enter the new password twice and click on **Save**.
Finally, your password is changed and you can [log into your account](https://app.b2brouter.net/en/login) using the same email address and the new password.
# Set up two-factor authentication
> Learn how to enable, disable and recover B2Brouter two-factor authentication using a TOTP app and backup authentication codes.
Two-factor authentication adds an extra layer of security to your B2Brouter account. When it is enabled, in addition to your password you must enter a temporary code generated by a TOTP app on your mobile device.
If you do not have access to the device at some point, you can use a backup authentication code that you generated earlier.
## Before you start
[Section titled “Before you start”](#before-you-start)
* You must be able to access your B2Brouter account.
* You need a TOTP app on your mobile device, such as Google Authenticator, Authy or Duo Mobile.
* When you generate backup authentication codes, keep them in a safe place. Each code can only be used once.
## Enable two-factor authentication
[Section titled “Enable two-factor authentication”](#enable-two-factor-authentication)
1. Go to your [User profile](/en/user-guide/my-user-profile/user-profile/).
2. In the **Password and authentication** section, go to **Two-Factor Authentication** and click **Enable**.
3. Scan the **QR code** with your TOTP app.
4. Enter the **Code** generated by the app.
5. Click **Enable**.
From that moment on, each time you sign in to B2Brouter you will be asked for a verification code.
## Generate backup authentication codes
[Section titled “Generate backup authentication codes”](#generate-backup-authentication-codes)
After enabling two-factor authentication, we recommend generating backup authentication codes:
1. From your [User profile](/en/user-guide/my-user-profile/user-profile/), go to **Password and authentication**.
2. In the **Authentication codes** section, click **Generate backup codes**.
3. Enter your current two-factor authentication code and click **Accept**.
4. B2Brouter will show you 10 one-time codes. Keep them in a safe place.
If you generate new codes, the previous ones become invalid.
## Disable two-factor authentication if you still have access
[Section titled “Disable two-factor authentication if you still have access”](#disable-two-factor-authentication-if-you-still-have-access)
1. Go to your [User profile](/en/user-guide/my-user-profile/user-profile/).
2. In **Password and authentication**, in the **Two-Factor Authentication** section, click **Disable**.
3. Enter your current two-factor authentication code.
4. Click **Disable**.
If you do not have the code from the TOTP app but still have an unused backup authentication code, you can also use it to disable two-factor authentication.
## Recover access if you have lost the TOTP app
[Section titled “Recover access if you have lost the TOTP app”](#recover-access-if-you-have-lost-the-totp-app)
If you do not have access to the TOTP app and you do not have any unused backup authentication codes left, you must ask our support team to disable two-factor authentication manually.
To submit the request, provide the following information:
* The email address associated with the account.
* The company identifier.
* A copy of your ID, tax ID card or identity document.
Our team will review the documentation manually and, if the information is correct, they will disable two-factor authentication. You will receive an email once the process has been completed.
# Manage your user profile
> Learn how to update your B2Brouter user profile, change email and password, enable 2FA, and set language and time zone preferences.
To customise aspects such as your name, password or language, follow these steps:
* Click on the icon located at the top right, next to your username, to access the **User profile**.
* In the drop-down menu that appears, select the first option.
Once inside the user profile settings, you can navigate through the different sections using the menu on the left side.

## Profile data
[Section titled “Profile data”](#profile-data)
In this section, you can change your first and surname associated with your profile.
### Change of Email Address
[Section titled “Change of Email Address”](#change-of-email-address)
To change the email address associated with your B2Brouter account, follow these steps:
* Click the **Change email address** button.
* Enter your new email address.
* A pop-up window will appear, informing you that you’re about to change your email address. **Confirm** the action.
* You will receive an email at the new address. Click the **Confirm change** button to complete the process.
## Password and authentication
[Section titled “Password and authentication”](#password-and-authentication)
To change your password follow the steps below:
* Enter your new password in the **Password** field.
* Re-enter your new password in the **Confirm your password** field.
* Be sure to save your changes by clicking **Save**.
### Two-Factor Authentication
[Section titled “Two-Factor Authentication”](#two-factor-authentication)
Two-factor authentication enables an additional security measure that adds a second layer of protection to the password you use to access B2Brouter.
To activate this option you need to download a TOTP application (e.g. Google Authenticator, Authy or Duo Mobile) to your mobile device.
It is important to bear in mind that the device used to configure the two-factor authentication cannot be modified, it must always be the same.
You should also consider that, once you have configured the two-factor authentication, every time you want to access the platform you must have the device within reach, so that you can enter the code it provides you with.
Once you have downloaded the application:
* In the **Two-Factor Authentication** section of B2Brouter, click on the **Enable** button.
* Scan the QR code provided by B2Brouter with the TOTP application.
* Enter the **Code** generated in the app to enable two-factor authentication.
* Click **Enable**.
From this moment on, every time you log in to the platform, the system will ask you for the code generated by the TOTP application you have downloaded, thus providing greater security to your data.
To access this code you must open the TOTP application every time you want to access your B2Brouter account.
## Preferences
[Section titled “Preferences”](#preferences)
In this section, you can customise the language and time zone of the platform according to your preferences. Don’t forget to click **Save** to apply the changes you have made.
The language you select here only changes the interface for your user: the language of the invoices each client receives is set in the **Language** field of [their contact card](/en/user-guide/contacts/create-contact#language).
# Convert an order into an invoice
> Learn how to convert a received order into an invoice in B2Brouter and reuse order data to avoid creating the invoice manually.
To convert an order to invoice, first you need to receive the order in your B2Brouter account or import it manually. Once it is in your B2Brouter account, you can use the information to generate the invoice without having to enter the data manually.
You have two ways to receive orders. You can import the orders received from your system or you can receive the orders through Peppol or EDI. [Here you can see in detail how to receive orders](/en/user-guide/receiving-documents/receive-orders).
When the received order is in your order list, you can [modify the status](/en/user-guide/receiving-documents/manage-status-received-orders) and in case it is accepted, you can convert it into an invoice by using the data and thus avoiding to create the invoice from scratch:
* Open the order by clicking on the number.
* At the top, you will find the order menu.
* Use the **Invoice** option.
You then access the form that allows you to create the invoice:
* The information contained in the order is automatically filled in.
* As taxes are not included in the order, you have to add them manually.
* Through the top menu of the invoice, access the **Lines** section.
* In the **Taxes** box, select the tax you want to apply.
* Finally, use the **Save and preview** option at the bottom.
This generates the invoice and all that remains is to send it when you need it.
# Manage received invoice statuses
> Learn how to change received invoice statuses in B2Brouter and notify suppliers through supported invoice reception networks.
You can manage status changes of received invoices. Depending on the method of receipt, you can send the status change to your supplier. If not, the change of status allows you to keep an internal control of the received invoices.
## Modify and send the status change
[Section titled “Modify and send the status change”](#modify-and-send-the-status-change)
To modify and send the change of status of the received invoices, you have to access the **Expenses** section through the main menu and then **Invoices received** through the options on the left side.
* Open the invoice you wish to manage by clicking on the number.
* At the top, you will find the invoice menu.
* Use the option **Mark as**.
* Select the status you want to send.
* Then a window is activated where you can add a reason to be sent with the status.
Once you have followed the above steps and entered the reason, use the **mark as accepted and notify by mail** option and the notification and reason is sent to your supplier.
## When status changes can be sent
[Section titled “When status changes can be sent”](#when-status-changes-can-be-sent)
Not all invoice receipt methods allow for the exchange of invoice notifications. As a consequence, you can only send the status change to your supplier when he sends you the invoices through one of these networks:
* **B2Brouter internal channel**: when two B2Brouter users exchange invoices through the portal.
* Peppol Network\*\*: if you receive invoices through this network, you can send status changes to your suppliers even if they have been sent to you from B2Brouter.
* FACeB2B2B\*\*: if you receive the invoices through this network, you can send status changes to your suppliers even if they have not been sent to you from B2Brouter.
If you receive the invoices via e-mail or import them, you cannot send the status notification to your customer, but you can modify the status of the invoices to have an internal control.
# Manage received order statuses
> Learn how to accept, reject, modify or manually update received order statuses in B2Brouter through Peppol, EDI or import.
You can manage changes in the status of received orders. In the case of importing orders, you can change the status manually, which allows you to keep an internal control. In case you [receive the order via one of the enabled networks](/en/user-guide/receiving-documents/receive-orders) (Peppol or EDI), you can send the status change to your supplier.
## Modify and send the change of status
[Section titled “Modify and send the change of status”](#modify-and-send-the-change-of-status)
To modify and send the status change of the received orders, you have to access the **Incoming payments** section through the main menu and then **Orders** through the options at the bottom:
* Open the order you want to manage by clicking on the number.
* At the top, you will find the order menu.
* Just below the order menu, if you have just received the order, you will find the options **Accept**, **Modify order** or **Decline**.
* Click on the option you need, to modify and send the status.
### Accept
[Section titled “Accept”](#accept)
You send your supplier the acceptance of the order. Then [you can create the invoice from the order](/en/user-guide/receiving-documents/convert-order-invoice).
### Reject
[Section titled “Reject”](#reject)
You send to your supplier the rejection of the order.
### Modify order
[Section titled “Modify order”](#modify-order)
You can modify the order received and let your supplier know the modifications made and the products that you will end up invoicing him. To do this, use the option **Modify order**:
* You access the form that allows you to modify the data.
* You can modify data such as the delivery date or the quantities of the products ordered.
* Once you have made your changes, use the **Save** option.
Once you have saved the changes, you can send your supplier the notification with the modifications made using the **Accept** option at the top of the order.
The possibility of making and notifying the changes depends on the method by which you receive the order and on the supplier. The same applies to the data you can modify.
## Modify the status without sending it
[Section titled “Modify the status without sending it”](#modify-the-status-without-sending-it)
In case you are importing the order, you can modify the status to keep an internal control of all orders. In this case, the status change is not sent. To do this:
* Open the order by clicking on the number.
* At the top you will find the order menu.
* Open the **Mark** option.
* Mark the order as **Accepted** or **Declined**.
Follow this process for all orders and you will be able to track it whenever you need to.
# Receive invoices
> Learn how to receive supplier invoices in B2Brouter by import, email, B2Brouter exchange, Peppol or FACeB2B.
With B2Brouter you can manage invoices from all your suppliers thanks to the use of different reception methods. To manage supplier invoices, you have to access the **Expenses** section through the main menu and then **Invoices received** through the options on the top left.
* You will find the list of received invoices.
* The received invoices that have not been managed have the number marked in bold.
* To the right of the number, you can see the status of the invoices.
* By opening the invoice you can [manage status](/en/user-guide/receiving-documents/manage-status-received-invoices).
There are several ways to manage received invoices:
## Importing received invoices.
[Section titled “Importing received invoices.”](#importing-received-invoices)
You can upload to B2Brouter the received invoices that you have in your system. To do this, once you are in the received invoices page:
* Click on the arrow next to **New invoice** and then **Import**.
* On the next page, click on the **Add file** option.
* Find the file in your system and select it.
* Finally, use the **Import** option.
If the file is correct, it is uploaded and the received invoice is created in your list of received invoices, where you can manage it.
You can import the invoice file in different formats:
### PDF
[Section titled “PDF”](#pdf)
This is an unstructured electronic format. This means that the interpretation of data is not 100% accurate. If you import an invoice in PDF format, the system may not interpret all the information in the file, and for this reason B2Brouter allows you to add it manually. When you import an invoice in PDF format:
* On the left side of the screen you will find the PDF of the invoice you have imported.
* On the right, you find the invoice in the B2Brouter format, with the data interpreted by the system.
* In the upper part you will find the invoice menu with the different options.
When there is some data that the system does not interpret, a question mark + the name of the data appears in the display on the right. For example: **? Due date**:
* Click on it to add the data manually.
* The form appears allowing you to add the selected data and to add or modify other data.
* On the left side of the screen, the PDF of the invoice continues to be displayed so that you can consult the information.
* When you have added the data, save the changes used with the **Save** option.
Another way to add the information manually is to use the **Modify** option at the top of the invoice. On the other hand, you can also use the **Recognize Text** option to have the system re-recognize the information contained in the PDF of the imported invoice.
### XML format of type Facturae or UBL
[Section titled “XML format of type Facturae or UBL”](#xml-format-of-type-facturae-or-ubl)
Facturae and UBL formats are structured formats. This means that the system can interpret 100% of the information in the imported file. Once you import the invoice, it is uploaded and stored in the list of received invoices. If you open it, it contains all the information and there is no need to add any other data manually.
The import of incoming invoices is available with the [Basic plan](https://www.b2brouter.net/global/prices/)
## Manual creation of incoming invoices
[Section titled “Manual creation of incoming invoices”](#manual-creation-of-incoming-invoices)
You can also create an incoming invoice manually, [just as you do with outgoing invoices](/en/user-guide/sending-documents/create-electronic-invoice). To do so, from the **Invoices received** space you must use the green colored **New** option and then **Invoice**.
## Invoice exchange between B2Brouter users
[Section titled “Invoice exchange between B2Brouter users”](#invoice-exchange-between-b2brouter-users)
You can receive invoices from other users who also work in B2Brouter. It is very simple: as both companies are in B2Brouter, the connection is made automatically. Your supplier only has to create and send the invoice from B2Brouter and it arrives to your received invoices space. The same happens if you send the invoice.
The exchange of invoices between B2Brouter users is available with the Basic Plan.
## Receiving invoices via e-mail
[Section titled “Receiving invoices via e-mail”](#receiving-invoices-via-e-mail)
You can receive invoices through e-mail. To proceed, your supplier must send you an email with the invoice attached to your B2Brouter email address. As a consequence, the invoice attached to the email is uploaded and stored in your received invoices space.
In the same way as with the import, the invoice formats accepted by B2Brouter via email are PDF, UBL and Facturae. In case you receive invoices in PDF format, you may have to add the data manually.
### Activate the reception of invoices by e-mail.
[Section titled “Activate the reception of invoices by e-mail.”](#activate-the-reception-of-invoices-by-e-mail)
This is a reception method that needs to be activated. To do so, access the **Account settings** via the gear icon:
* Access **Connections** on the left side menu.
* Click on the **Email** option.
* Go to the **Reception** section and activate the **Activate reception** button.
* Save the changes with the **Save** button to start receiving invoices in your B2Brouter account via email.
This action activates a dedicated email address for your suppliers to send you invoices. The address is in a blue box and consists of your company’s VAT number (company’).
This is a feature available in the **Professional Plan** if you wish to receive one invoice via email, or in the **Business Plan** if you wish to receive multiple invoices via email.
## Receiving invoices through open networks.
[Section titled “Receiving invoices through open networks.”](#receiving-invoices-through-open-networks)
You can receive invoices through open networks such as [Peppol](https://www.b2brouter.net/es/blog/PEPPOL-estandar-europeo) or [FACeB2B](https://www.b2brouter.net/es/blog/faceb2b-facturacion-electronica-entre-empresas/):
### Peppol
[Section titled “Peppol”](#peppol)
It is an international electronic document exchange network that allows you to send and receive invoices and other documents such as [orders](/en/user-guide/receiving-documents/receive-orders) or [delivery notes](/en/user-guide/sending-documents/create-dispatch-notes).
To be able to receive invoices through this network you have to have the Business Plan from B2Brouter and enable the connection to Peppol in our portal. You find your Peppol identifier by accessing the gear at the top, then **Company settings**. On the next page access the **Connections** section and finally **Peppol**.
When you are doing the configuration, check the option **Enable receipt of documents via Peppol** and save the changes.
Once you have done that, you can receive invoices in your B2Brouter account via Peppol. You only have to tell your supplier your Peppol identifier so that he can include it in the invoices he sends you.
### FACeB2B
[Section titled “FACeB2B”](#faceb2b)
This is a Spanish network for the exchange of electronic invoices. Right now, only state contractors and subcontractors are required to manage invoices through this General Entry Point.
To be able to receive invoices in B2Brouter through FACeB2B, you must first perform the following steps:
#### DIRe processing
[Section titled “DIRe processing”](#dire-processing)
All the companies that wish to receive invoices through FACeB2B2B must process their [DIRe](/en/user-guide/routing-codes/dire-code), an identifier that allows the reception of the invoice. [From this page, you can register your company’s DIRE](https://dire.gob.es/portal).
#### Registration in FACeB2B2B
[Section titled “Registration in FACeB2B2B”](#registration-in-faceb2b2b)
In order to receive invoices through the FACeB2B2B General Entry Point, you must register your company in its portal.
[Here you can register in FACeB2B2B](https://faceb2b.gob.es/).
Once you have registered in FACeB2B2B, when you are in your profile, you have to select B2Brouter as your e-invoice provider. In addition, you must contract the Business Plan, which is the one that allows you to receive invoices through FACeB2B2B.
# Receive orders
> Learn how to receive or import orders in B2Brouter through UBL, EDI, Peppol or direct connections and create invoices from them.
With B2Brouter you can register orders received in two ways:
* By importing the order from your system into B2Brouter.
* Receiving the order directly in your B2Brouter account.
## Import incoming orders
[Section titled “Import incoming orders”](#import-incoming-orders)
To import the received orders, you have to access the **Incomes** section that you find in the main menu and then **Orders** through the options on the left side:
* Use the **Import order** option.
* On the next page, use the **Select files** option.
* Search for the file in your system and select it.
* Finally, use the **Import** option.
The order is uploaded and will be stored as **New** in your order list. You can import orders in **UBL** and **EDI** format.
## Receive orders
[Section titled “Receive orders”](#receive-orders)
You can also receive orders sent by your customer, directly to your B2Brouter account. Once your customer has sent you an order, it is stored in the **Incomes** section that you find in the main menu and then **Orders** in the options at the bottom. You just need to let your customer know how to send you the orders, and once he has sent them:
* Orders received appear as **New** in the order list.
* You can reply to your customer and [send change order status received](/en/user-guide/receiving-documents/manage-status-received-orders) (Accepted, Modified or Rejected).
* Once the order is accepted, you can [generate the invoice from the order](/en/user-guide/receiving-documents/convert-order-invoice), using the data.
You can receive orders through your enabled networks (Peppol or EDI) or through direct connections, for example with Carrefour.
# What is the service code (Chorus Pro)?
> Learn what the Chorus Pro service code is, where to obtain it and how to add it to invoices for French public administrations.
The service code is a code that has to be included on the electronic invoice when you have to send it to the French public administration through Chorus Pro. It serves to direct the invoice to the correct department of the receiver and is mandatory.
## How to obtain the service code
[Section titled “How to obtain the service code”](#how-to-obtain-the-service-code)
Each public entity has its own service codes. For this reason, your customer has to send you this information when you need to invoice. There is also an alternative, the [B2Brouter directory](/en/user-guide/contacts/b2brouter-directory), where you will find all the service codes related to a public entity.
## How to add the service code
[Section titled “How to add the service code”](#how-to-add-the-service-code)
You have to [create a new invoice](/en/user-guide/sending-documents/create-electronic-invoice) to be able to add the service code. To do this, access the **Invoices** section via the main menu at the top and then select **Invoices issued** from the options activated on the left side of the menu. Next, use the **New Invoice** option. And then:
* From the **Customer** drop-down select the invoice recipient.
* If the selected receiver is in the B2Brouter directory, it is activated in the **Organizational unit** option.
* Select the service code in the drop-down **Organizational unit**.
Finally, continue with the creation of the invoice and the service code will be already included.
### If the Organizational Unit is not activated:
[Section titled “If the Organizational Unit is not activated:”](#if-the-organizational-unit-is-not-activated)
If you have followed the previous steps but the drop-down Organizational Unit is not activated, it means that the customer you have selected or its service code does not appear in the B2Brouter directory. In this case, you have the following option:
#### Create and assign the organizational units to the client.
[Section titled “Create and assign the organizational units to the client.”](#create-and-assign-the-organizational-units-to-the-client)
You can create the organizational units assigned to each receiver. To do this, access the **Contacts** section through the top main menu and open the company by clicking on the name:
* On the next page, select **Add organizational unit** located on the left.
You can then add the data to the Organizational Unit with the corresponding service code:
* In the menu on the left, select **Fiscal data**, put the name and address. They can match those of the main company.
* Fill in the other options you want/need.
* In the same left menu, select **Invoices**.
* Go to **Method of sending invoices**.
* Add the code in **Service code**.
Finally, save the changes using the **Save** option and repeat the process to create as many organizational units as you need for each customer.
# What are DIR3 codes?
> Learn what DIR3 codes are, where to get them and how to add them to electronic invoices for Spanish public administrations.
DIR3 codes are three codes that must be included in the electronic invoice when it is to be sent to the Spanish public administration, and which are used to direct the invoice to the corresponding department so that it can be processed correctly.
These codes are called Managing Body, Processing Unit and Accounting Office, and each recipient may have one or several combinations of different codes. In addition, they are mandatory data to be able to send an electronic invoice to the Spanish Public Administration. These codes are composed of one or two initial letters, plus seven or eight numbers. For example, LA1111111.
## How to obtain DIR3 codes
[Section titled “How to obtain DIR3 codes”](#how-to-obtain-dir3-codes)
Each public entity has its own DIR3, so your customer must provide you with this information when you send them an invoice. There is also an alternative, the [B2Brouter directory](/en/user-guide/contacts/b2brouter-directory), where you will find all the DIR3 related to a public entity.
## How to add DIR3 codes to the electronic invoice
[Section titled “How to add DIR3 codes to the electronic invoice”](#how-to-add-dir3-codes-to-the-electronic-invoice)
You need to [create a new invoice](/en/user-guide/sending-documents/create-electronic-invoice) to be able to add DIR3’s. To do this, access the **Invoices** section through the main top menu and then select **Invoices issued** from the options that are activated in the left menu. Next, use the option at the top right **New Invoice**. Next:
* From the **Customer** drop-down select the invoice recipient.
* If the selected receiver is in the B2Brouter directory, the option **Organizational unit** will be activated.
* Select the DIR3 codes in the **Organizational Unit** dropdown.
Finally, continue with the creation of the invoice and the DIR3 codes will already be present.
### If the Organizational Unit is not activated:
[Section titled “If the Organizational Unit is not activated:”](#if-the-organizational-unit-is-not-activated)
If you have followed the previous steps but the drop-down Organizational Unit is not activated, it means that the client you have selected or its DIR3 are not in the B2Brouter directory. In this case, you have two options:
#### Add DIR3 codes manually
[Section titled “Add DIR3 codes manually”](#add-dir3-codes-manually)
Once you have selected the receiver and you have started to create the invoice, in the **General data** section you find the options **Managing body**, **Processing unit** and **Accounting office**, where you have to add the DIR3 codes provided by the customer.
#### Create and assign the organizational units to the client
[Section titled “Create and assign the organizational units to the client”](#create-and-assign-the-organizational-units-to-the-client)
To avoid having to add DIR3 codes to invoices every time you generate an invoice, you can also create the organizational units assigned to each receiver. To do this, access the **Contacts** section via the main menu at the top and open the company by clicking on the name.
On the next page, select **Add organizational unit**.
Next, you can add the data to the Organizational Unit with the corresponding DIR3 codes:
* In **Fiscal data** put the name and address. They can be the same as those of the main company.
* Fill in the other options you want/need.
* Access at the bottom to **Delivery method**.
* Add the DIR3 codes in the **Accounting Office**, **Managing Body** and **Processing Unit** options.
Finally, save the changes using the **Save** option and repeat the process to create as many organizational units as you need for each client.
# What is the DIRe code?
> Learn what the DIRe code is, how to obtain it and how to add it to electronic invoices sent through FACeB2B.
The DIRe code is a code that must be included in the electronic invoice when you need to send it through the FACeB2B General Point of Entry, and that serves to correctly address the invoice to your client.
Each receiver can have one or several codes, which can be related to departments, delegations or works in which the company participates. Moreover, they are mandatory data to be able to send an electronic invoice through FACeB2B. A DIRe code is composed of the NIF of the owner company plus four digits at the end. For example ESA351487410000.
## How to obtain the DIRe code
[Section titled “How to obtain the DIRe code”](#how-to-obtain-the-dire-code)
Each receiving company has its own DIRe. For this reason, your customer will have to provide you with this information when you have to send them an invoice. There is also an alternative, the [B2Brouter directory](/en/user-guide/contacts/b2brouter-directory), where you will find all the DIRe related to a company.
## How to add the DIRe code to the electronic invoice
[Section titled “How to add the DIRe code to the electronic invoice”](#how-to-add-the-dire-code-to-the-electronic-invoice)
You have to [create an invoice](/en/user-guide/sending-documents/create-electronic-invoice) in order to add the DIRe. To do this, access the **Invoices** section through the main menu above and then select **Invoices issued** from the options that are activated under the menu. Next, use the **Create invoice** option. Next:
* From the **Customer** drop-down, select the invoice recipient.
* If the selected receiver is in the B2Brouter directory, the option **Organizational unit** is activated.
* Select the DIRe code in the **Organizational Unit** dropdown.
Finally, continue creating the invoice and the DIRe code will already be present.
### If the Organizational Unit is not activated
[Section titled “If the Organizational Unit is not activated”](#if-the-organizational-unit-is-not-activated)
If you have followed the previous steps but the drop-down Organizational Unit is not activated, it means that the customer you have selected is not in the B2Brouter directory. In this case, you have the following option:
#### Create and assign the organizational units to the client.
[Section titled “Create and assign the organizational units to the client.”](#create-and-assign-the-organizational-units-to-the-client)
You can create the organizational units assigned to each receiver. To do this, access the **Contacts** section through the top main menu and open the company by clicking on the name:
* On the next page, select **Add organizational unit** located on the left.
You can then add the data to the Organizational Unit with the corresponding DIRe code:
* In the menu on the left, select **Fiscal data**, enter the name and address. They can match those of the main company.
* Fill in the other options you want/need.
* In the same left menu, select **Invoices**.
* Go to **Method of sending invoices**.
* Add the DIRe code in **Service code**.
Finally, save the changes using the **Save** option and repeat the process to create as many organizational units as you need for each customer.
# Assign invoice routing codes to a contact
> Learn how to assign invoice routing codes such as DIR3, DIRE, Peppol ID, SDI code and Chorus Pro service code to a contact.
Routing codes are codes that are used to direct the invoice to the customer’s department, branch or office so that it can be processed correctly. Some examples are:
* Code **DIR3** for invoicing to the Spanish public Administration.
* Code **DIRE** for contractors and subcontractors who need to invoice through FACeB2B.
* **Peppol identifier** for invoicing through the Peppol network.
* **Codice destinatario** for invoicing to SDI Italy.
* **Service code** for billing to France via Chorus Pro.
The routing codes can be different depending on the customer or the invoice. For this reason, before assigning them to the invoice, they must be created and related to each customer. To do this, follow these steps:
## 1. Assigning routing codes to a contact
[Section titled “1. Assigning routing codes to a contact”](#1-assigning-routing-codes-to-a-contact)
First of all, you have to [create an **Organisational Unit**](/en/user-guide/contacts/create-uo) of the customer to assign the related routing codes to it. To do this, access the **Contacts** section:
* Open an already created customer by clicking on the name.
* Select **Add organisational unit**.
You will then access the form to add the organisational unit information for this customer:
* You cannot modify the VAT number.
* You can modify other fiscal data such as the name or the address, if you need to. For example, to add the name of the department.
Now, from the same **Contacts** section, click on the **Invoices** button you find in the left part of the page.
You get to the **Invoice sending method** page, where you can select the **Transport** of your invoices for this specific organisational unit. Here you can also add the needed routing codes.
Finally, save the changes using the **Save** option at the bottom part of the page.
## 2. Assign routing codes to the invoice
[Section titled “2. Assign routing codes to the invoice”](#2-assign-routing-codes-to-the-invoice)
Once you have assigned the routing codes to the customer, you can create the invoice. When generating the invoice, you must first select the customer and then, under **Organisational unit**, select the routing codes, where you find all the organisational units you have created for that customer, with the routing codes you have assigned to them. [Here you can see how to create a new invoice](/en/user-guide/sending-documents/create-electronic-invoice).
# What is the Peppol identifier?
> Learn what a Peppol identifier is, how to obtain it and how to add it to customer data for Peppol electronic documents.
The Peppol identifier is the number that must be included on the electronic document (invoice, order or delivery note) to be sent via the Peppol network. This identifier makes it possible to connect two companies, to direct the electronic documents sent to the correct recipient.
## How to obtain the Peppol identifier
[Section titled “How to obtain the Peppol identifier”](#how-to-obtain-the-peppol-identifier)
Each company has its own Peppol identifier. For this reason, the customer will have to provide you with this information when you need to invoice. There is also an alternative, the [B2Brouter-directory](/en/user-guide/contacts/b2brouter-directory), where you will find the information of companies from all over the world and their Peppol identifier.
## How to add the Peppol identifier to the e-invoice
[Section titled “How to add the Peppol identifier to the e-invoice”](#how-to-add-the-peppol-identifier-to-the-e-invoice)
You must enter the Peppol identifier in the customer data. When you create a new customer and add its VAT number, if it is in the B2Brouter directory, the Peppol identifier is automatically added, so you do not need to do anything else.
In case your customer is not in the B2Brouter directory, you must create it manually and, in **Invoice sending method**, select: **Open network** and in **Transport** choose **Peppol**. Once these options are selected, the **Peppol Company ID** section is activated, where you can enter your customer’s Peppol ID:
* In the **Select Type** drop-down, select the identifier scheme.
* In **Identifier code**, add the numbering of the identifier.
Finally, finish creating the client and save the changes.
[Here you can see the steps to create a client](/en/user-guide/contacts/create-contact).
Once you have created your client and entered its Peppol identifier, you can create the invoice. Once created, the system takes care of adding the Peppol identifier at the time of sending it and, as a consequence, it is correctly routed to the receiver.
# What is the recipient code (SDI)?
> Learn what the SDI recipient code is and how to use it to send or receive Italian electronic invoices through B2Brouter.
The recipient code is a code containing 7 alphanumerical characters used to send electronic invoices to Italian companies using the “Sistema di Interscambio”(SDI) and it is used to send an invoice to the recipient’s system.
## How to manage the recipient code
[Section titled “How to manage the recipient code”](#how-to-manage-the-recipient-code)
If you need to send invoices, you have to ask your client for their recipient code. Once you have it, [create an invoice](/en/user-guide/sending-documents/create-electronic-invoice) and enter it in the **General data** section in the option **Recipient code**.
If you need to receive an invoice on B2Brouter through the SDI, you need to inform your service provider of B2Brouter’s recipient code, which is **EPKDZJB**.
To receive invoices on B2Brouter through the SDI, first you need to register your company with the Agència d’Ingressos, where you have to enter B2Brouter’s recipient code, to be able to register and manage the received invoices from your account.
Once you have done this, all the invoices that you receive from your service providers via the SDI are registered in the section of invoices received by your company on B2Brouter, from where you can manage them.
# Create a credit note
> Learn how to create a credit note from an existing invoice or from scratch, add rectified invoice data and send it in B2Brouter.
There are two ways to create a credit note:
## Create a credit note from an existing invoice.
[Section titled “Create a credit note from an existing invoice.”](#create-a-credit-note-from-an-existing-invoice)
You can create a credit note from an invoice that has already been created and sent, and that you need to amend. To do this, access the **Income** section through the main menu at the top and then **Commercial invoices** through the options at the top left:
* Open the invoice you want to amend by clicking on the number.
* Use the **More options** option, which you find in the top menu of the invoice.
* Select the option **Credit note**.
Then you access the form to create the credit note. This already contains the information of the invoice you are trying to rectify, so you only need to modify the data you need to rectify and save the changes. The credit note is created and finally you only need to [send](/en/user-guide/sending-documents/create-electronic-invoice).
The credit note is related to the amended invoice. You can see the related invoices by opening one of them and accessing the bottom, in the **Transaction tracking** space.
## Create a credit note from zero
[Section titled “Create a credit note from zero”](#create-a-credit-note-from-zero)
You can create a credit note without having to create it from an invoice already generated and sent in B2Brouter. To do so, access the **Income** section through the main menu at the top and then **Commercial invoices** through the options at the top left:
* Select the **Create invoice** option and then **Credit note**.
* On the next page, you must select the customer and you can start [creating the invoice](/en/user-guide/sending-documents/create-electronic-invoice).
The creation of the invoice is like that of a normal invoice, but you can add some additional data:
* In **General data**, in the **Rectified invoice number** box you can add the number of the invoice being rectified.
Finally finish adding all the data you need and save the invoice. It will be created as a new invoice in the list of issued invoices and you will be able to [send the invoice](/en/user-guide/sending-documents/create-electronic-invoice) whenever you want.
# Create a despatch advice
> Learn how to create, edit, send and track a despatch advice from an accepted order in B2Brouter.
To start creating a despatch advice you need to [previously receive the related order](/en/user-guide/receiving-documents/receive-orders). Once you have the order and it has been accepted, you can generate a despatch advice by following the steps below:
* Through the top main menu, access the **Income** section.
* Then, access **Orders** which you can find in the left menu.
* Open the order to which you want to relate the despatch advice by clicking on the number.
* Select the option **Create despatch advice**.
The form for the creation of the despatch advice appears with the data contained in the order:
* At the top left you find the menu with the different sections of the despatch advice: **General data**, **Lines**.
* You can modify the data in the boxes that are active.
* You do not need to modify any data if you do not need to.
To finish creating the despatch advice, use the **Save and preview** option at the bottom of the page.
Once the despatch advice has been generated, on the top right hand side of the document, you find the main menu with the different options that allow you to manage it:
* **More options**: you can use other options such as **Send ASN** to send despatch advice, **Mark as sent**, or **Delete**.
* **Edit**: you can modify again some data of the despatch advice. To save the changes, use the **Save and preview** option at the bottom of the page.
* **Send**: when you consider that the packing slip is correct, just use this option to send it to the customer.
At the top left, there is a space where you can see the **State of the updated despatch advice**.
At the bottom, you find:
* Under **Transaction tracking**, you can track all the documents related to the packing slip you are managing (order and invoice).
* Under **Activity**, you can see all the changes the despatch advice has gone through and the related notifications. Consult the notification by clicking on the + symbol.
* In **Comments**, you can write a private annotation concerning the despatch advice.
You can access all the despatch advice created and stored, by accessing the **Income** section and then **Despatch advices**.
# Create and send an invoice
> Learn how to create, edit, preview and send an invoice in B2Brouter with lines, taxes, attachments and collection details.
To create an invoice go to the **Invoices** section of the main menu above.
* Select the **Create Invoice** option.
* On the next page, you will see a dropdown where you must select the contact you wish to invoice. If you do not have any contact registered, [add a contact](/en/user-guide/contacts/create-contact).
* Once you have selected the client, only if another drop-down is activated with the name **Organisational unit**, you must select the head office, department or delegation you wish to invoice.
Finally, select the option **Continue** to start creating the invoice.

## General information
[Section titled “General information”](#general-information)
Next, you access the **General data** section, where you can add general data such as the number, date, etc. to the invoice:
* Fields marked with an asterisk are mandatory.
* In this section, you can add manually or select other codes such as DIR3, DIRE, etc.
* Activate the **More options** button at the bottom to add other information such as **Contract number**, **Despatch number**, **File reference**, etc.
Access the next step from the **Next** button at the bottom right or from the menu.
## Lines
[Section titled “Lines”](#lines)
From the **Lines** section you can add the information of the lines and select the taxes of the invoice.
Start creating the lines:
* In **Name of the product or service**, add the concept or description of the product or service you want to add to the line.
* In **Quantity**, add the quantity of the product or service you wish to invoice.
* In **Units**, select the most appropriate size for the product or service.
* In **Unit price**, add the unit price of the product or service you are adding to the line. At the end, the system calculates the result.
* Under each line you find the option **Add item from catalogue**, which allows you to create the lines easily using the information from your [catalogue created in B2Brouter](https://www.b2brouter.net/es/agiliza-la-facturacion-gracias-al-catalogo-de-productos/).
* Use the **Add line** option to add as many lines as you need in the invoice.
To add more information about a line, use the three dots icon to the right of each line.
* In the dropdown, select **More options**.
* Here you can add more detailed information about the line in question, such as now the identifier, discount, additional charge, etc.
To delete the line, in the same drop-down select the **Delete** option.
You can also **Add a discount** or **Add an additional charge** to the invoice total:
### Add discount
[Section titled “Add discount”](#add-discount)
* Click on the option **Add discount** and activate the button.
* In **Discount description**, add the concept or description of the discount you wish to apply.
* In **Discount amount**, add the amount you wish to apply.
* This amount can be in currency or in percentage, something you must select through the drop-down menu.
### Add additional charge
[Section titled “Add additional charge”](#add-additional-charge)
* Click on the option **Add additional charge**.
* Under **Reason for charge**, add the name or reason.
* In **Amount of charge**, add the amount you want to apply.
* This amount can be in currency or as a percentage, which you will need to select via the drop-down menu.
* Check the **Apply taxes** box if you want the taxes on the invoice to be applied to the surcharge as well. Otherwise, they are not applied.
### Taxes
[Section titled “Taxes”](#taxes)
In this section, select the taxes to be applied to the invoice. To apply different taxes on each line, use the **Apply per line** button.
Once you have completed these steps, you can access the next section using the **Next** button or from the side menu.
## Other
[Section titled “Other”](#other)
In this section, you can add an **Invoice note** or a **Legal note**. The information you add is reflected in the invoice footer. You can also attach files to your invoices, as well as add delivery information for your goods.
### Additional documents
[Section titled “Additional documents”](#additional-documents)
From the **Additional documents** section, you can add an attachment to the invoice:
* Use the **Select Files** option to browse for the file on your system.
* Select the file on your system and it is uploaded to B2Brouter.
* Use the **Save** button to save the changes to the invoice and upload the file.
* You can also add an optional description of the attached file.
You can attach a link to the invoice that directs to the document you want your customer to receive along with the invoice:
* Use the option **Add link to the file**.
* The option is activated where you can add the link to the file and a description.
Once you have attached the document, you can access the following section.
### Delivery of goods
[Section titled “Delivery of goods”](#delivery-of-goods)
Activate the **Add delivery of goods** button to add the delivery information for the goods on the invoice.
Once you have selected the taxes and created the lines, you can access the next section.
## Collection
[Section titled “Collection”](#collection)
From the **Collection** section, select the payment method, the account where you wish to collect the invoice and the due date, among other things:
* In **Payment method**, select how you wish to collect the invoice.
* Next, the **Bank account** dropdown is activated, where you can select the account where you wish to receive the payment. If you have not previously [added the current account](/en/user-guide/account-settings/setting-up-your-company), you can do so using the **Add bank account** option.
* Under **Due date**, select the due date you wish to apply to the invoice.
You can also set up invoice collection reminders to be sent to your customer:
* Activate the **Send due date reminder to customer** button.
* In the drop-down menu, select the number of days limit to send the reminder once the invoice is overdue.
From this same section, you can add more information to the invoice:
### Add references, payments, withholdings.
[Section titled “Add references, payments, withholdings.”](#add-references-payments-withholdings)
Click on this option and fill in the necessary information to add references, payments and deductions to the invoice.
If you need to adjust a cents difference, see [Rounding and VAT calculation](/en/user-guide/manage-documents/rounding-and-vat-calculation). Remember that the rounding adjustment can only be applied to the invoice total, not to VAT.
### Add factoring assignment
[Section titled “Add factoring assignment”](#add-factoring-assignment)
Click on this option to open the form and be able to add the information of the entity that manages the assignment of the collection of this invoice.
Finally, and once you have finished adding the details of all the sections of the invoice, save the changes and view the invoice using the option **Save and preview**.
Once you have generated the invoice, at the top of the document, you will find the main menu with the different options that allow you to manage it:
* Click the **More options** button and use the options in the drop-down.
* You can **Save as recurring invoice** and **Duplicate invoice**.
* Through the same button, you can also modify the status of the invoice manually by selecting the options **mark as sent**, **mark as accepted**, **mark as registered**, **mark as rejected** or **mark as closed**.
* From this dropdown you can also **Delete**.
Using the other buttons you can also:
* Modify some data of the invoice with the **Edit** option.
* Send the invoice once you consider that it is correct, with the option **Send** to send it to the client. You do not need to do anything else.
* Download the invoice file to your system, using the arrow icon in the top left margin of the invoice.
At the top left you can see the updated invoice status.
In addition, at the bottom of the invoice you will find the sections of the invoice:
* **Activity**, where you can see all the modifications made to the invoice and the related notifications. If the notification allows it, you can consult it by clicking on the + symbol.
* **Comments**, where you can write a comment in reference to the invoice. This comment can be private or public (your customer will also see it if you are sending the invoice to another B2Brouter account), if you tick the box **Public comment (show in the other company)**.
# Create an invoice from a quote
> Learn how to generate an invoice from an accepted B2Brouter quote and reuse the quote data to create the invoice.
In order to create an invoice from an accepted budget, first you need to generate the B2Brouter quote.
To do this, access the **Incomes** section through the top main menu, and then **Quotes** through the options on the left side. Finally [generate and send a quote](/en/user-guide/sending-documents/create-quotes).
Once [sent](/en/user-guide/sending-documents/create-quotes) and [accepted](/en/user-guide/sending-documents/managing-status-quote) the quote:
* Open the quote by clicking on the number.
* Click on the **Edit** option at the top right of the document, if you want to change any data.
* Once all the data is correct, use the **Invoice** option.
The same data as in the quotation are included. Just use the **Save and preview** option at the bottom, and the invoice is created.
# Create a proforma invoice
> Learn how to create, edit, preview and send a proforma invoice in B2Brouter with lines, taxes, attachments and collection details.
To create a proforma invoice, go to the **Income** section of the main top menu and click on **Proforma** in the left-hand sidebar.
Once in this section, select the option **Create proforma invocie**:
* On the next page, there is a dropdown where you must select the contact you wish to invoice. If you do not have any contact registered, [add a contact](/en/user-guide/contacts/create-contact).
Finally, select the option **Continue** to start creating the invoice.
## General data
[Section titled “General data”](#general-data)
Next, you access the **General data** section, where you can add general data such as the number, date, etc. to the invoice:
* Fields marked with an asterisk are mandatory.
* In this section, you can add manually or select other codes such as DIR3, DIRE, etc.
* Activate the **More options** button at the bottom to add other information such as **Contract number**, **Despatch number**, **File reference**, etc.
Finally, use the **Save** option at the bottom left of the screen to save the invoice.
Access the next step from the **Next** button at the bottom right or from the menu.
## Lines
[Section titled “Lines”](#lines)
From the **Lines** section you can add the information of the lines and select the taxes of the invoice.
Start creating the lines:
* In **Name of the product or service**, add the concept or description of the product or service you want to add to the line.
* In **Quantity**, add the quantity of the product or service you wish to invoice.
* In **Units**, select the most appropriate size for the product or service.
* In **Unit price**, add the unit price of the product or service you are adding to the line. At the end, the system calculates the result.
* Under each line you find the option **Add item from catalogue**, which allows you to create the lines easily using the information from your catalogue created in B2Brouter.
* Use the **Add line** option to add as many lines as you need in the invoice.
To add more information about a line, use the three dots icon to the right of each line.
* In the dropdown, select **More options**.
* Here you can add more detailed information about the line in question, such as now the identifier, discount, additional charge, etc.
To delete the line, in the same drop-down select the **Delete** option.
You can also **Add a discount** or **Add an additional charge** to the invoice total:
### Add discount
[Section titled “Add discount”](#add-discount)
* Click on the option **Add discount** and activate the button.
* In **Discount description**, add the concept or description of the discount you wish to apply.
* In **Discount amount**, add the amount you wish to apply.
* This amount can be in currency or in percentage, something you must select through the drop-down menu.
### Add additional charge
[Section titled “Add additional charge”](#add-additional-charge)
* Click on the option **Add additional charge**.
* Under **Reason for charge**, add the name or reason.
* In **Amount of charge**, add the amount you want to apply.
* This amount can be in currency or as a percentage, which you will need to select via the drop-down menu.
* Check the **Apply taxes** box if you want the taxes on the invoice to be applied to the surcharge as well. Otherwise, they are not applied.
### Taxes
[Section titled “Taxes”](#taxes)
In this section, select the taxes to be applied to the invoice. To apply different taxes on each line, use the **Apply per line** button.
Once you have completed these steps, you can access the next section using the **Next** button or from the side menu.
## Other
[Section titled “Other”](#other)
In this section, you can add an **Invoice note** or a **Legal note**. The information you add is reflected in the invoice footer. You can also attach files to your invoices, as well as add delivery information for your goods.
### Additional documents
[Section titled “Additional documents”](#additional-documents)
From the **Additional documents** section, you can add an attachment to the invoice:
* Use the **Select Files** option to browse for the file on your system.
* Select the file on your system and it is uploaded to B2Brouter.
* Use the **Save** button to save the changes to the invoice and upload the file.
* You can also add an optional description of the attached file.
You can attach a link to the invoice that directs to the document you want your customer to receive along with the invoice:
* Use the option **Add link to the file**.
* The option is activated where you can add the link to the file and a description.
Once you have attached the document, you can access the following section.
### Delivery of goods
[Section titled “Delivery of goods”](#delivery-of-goods)
Activate the **Add delivery of goods** button to add the delivery information for the goods on the invoice.
Once you have selected the taxes and created the lines, you can access the next section.
## Collection
[Section titled “Collection”](#collection)
From the **Collection** section, select the payment method, the account where you wish to collect the invoice and the due date, among other things:
* In **Payment method**, select how you wish to collect the invoice.
* Next, the **Bank account** dropdown is activated, where you can select the account where you wish to receive the payment. If you have not previously [added the current account](/en/user-guide/account-settings/setting-up-your-company), you can do so using the **Add bank account** option.
* Under **Due date**, select the due date you wish to apply to the invoice.
You can also set up invoice collection reminders to be sent to your customer:
* Activate the **Send due date reminder to customer** button.
* In the drop-down menu, select the number of days limit to send the reminder once the invoice is overdue.
From this same section, you can add more information to the invoice:
### Add references, payments, withholdings.
[Section titled “Add references, payments, withholdings.”](#add-references-payments-withholdings)
Click on this option and fill in the necessary information to add references, payments and deductions to the invoice.
### Add factoring assignment
[Section titled “Add factoring assignment”](#add-factoring-assignment)
Click on this option to open the form and add the information of the entity that manages the collection of this invoice.
Finally, once you have finished adding the details of all the sections of the proforma, save the changes and view the proforma using the option **Save and preview**.
## Preview the document
[Section titled “Preview the document”](#preview-the-document)
Once you have generated the proforma, at the top of the document, you will see the main menu with the different options that allow you to manage it:
* **Send**: Send the proforma once you consider it is correct. You do not need to do anything else.
* **Edit**: Use this if you need to modify any data on the proforma.
* **More options**:
* **Duplicate proforma invoice**: Duplicate the file.
* **Mark as sent**: Manually change the status of the proforma invoice as sent.
* **Mark as invoiced**: Manually changes the status of the proforma invoice as invoiced.
* **Delete**: Deletes the proforma invoice.
* **Download button**: Located in the upper left-hand margin of the proforma invoice. Download the proforma file to your system.
Once the proforma is sent, you can convert it into an ordinary invoice with the **Invoice** button.
In the top left corner you can see the updated status of the proforma. After sending it, you cannot change this status.
In addition, at the bottom of the proforma you will find the sections of:
* **Activity**, where you can see all the modifications made to the proforma and the related notifications.
* **Comments**, where you can write an annotation in reference to the proforma. This comment can be private or public (your customer will also see it if you are sending the invoice to another B2Brouter account), if you check the box **Public comment (show in the other company)**.
# Create and send a quote
> Learn how to create, edit, preview and send quotes in B2Brouter with customer data, lines, taxes, attachments and collection details.
To start creating a budget, access the **Income** section through the main top menu and then **Quotes** through the options on the top left.
Once you are in this section, select the **Create quote** option:
* On the next page, a drop-down appears where you can select the customer you wish to send the quote to.
* You can go ahead and create a quote without selecting a customer.
Finally, select the **Continue** option to start creating the quote.
## General data
[Section titled “General data”](#general-data)
Next, you access the **General Data** section, where you can add general data of the quotation such as number, date, etc..:
* Fields with an asterisk are mandatory.
* In this section, you can add a **Title** and **Description** of the quote.
Finally, use the **Continue** option at the bottom of the screen to continue creating the quote.
## Lines
[Section titled “Lines”](#lines)
From the **Lines** section you can add the information of the lines and select the taxes of the quote.
Start creating the lines:
* In **Name of the product or service**, add the concept or description of the product or service you want to add to the line.
* In **Quantity**, add the quantity of the product or service.
* In **Units**, select the most appropriate size for the product or service.
* In **Unit price**, add the unit price of the product or service you are adding to the line. At the end, the system calculates the result.
* Under each line you find the option **Add item from catalogue**, which allows you to create the lines easily using the information from your [catalogue created in B2Brouter](https://www.b2brouter.net/es/agiliza-la-facturacion-gracias-al-catalogo-de-productos/).
* Use the **Add line** option to add as many lines as you need in the quote.
To add more information about a line, use the three dots icon to the right of each line.
* In the dropdown, select **More options**.
* Here you can add more detailed information about the line in question, such as now the identifier, discount, additional charge, etc.
To delete the line, in the same drop-down select the **Delete** option.
You can also **Add a discount** or **Add an additional charge** to the quote total:
### Add discount
[Section titled “Add discount”](#add-discount)
* Click on the option **Add discount** and activate the button.
* In **Discount description**, add the concept or description of the discount you wish to apply.
* In **Discount amount**, add the amount you wish to apply.
* This amount can be in currency or in percentage, something you must select through the drop-down menu.
### Add additional charge
[Section titled “Add additional charge”](#add-additional-charge)
* Click on the option **Add additional charge**.
* Under **Reason for charge**, add the name or reason.
* In **Amount of charge**, add the amount you want to apply.
* This amount can be in currency or as a percentage, which you will need to select via the drop-down menu.
* Check the **Apply taxes** box if you want the taxes on the quote to be applied to the surcharge as well. Otherwise, they are not applied.
### Taxes
[Section titled “Taxes”](#taxes)
In this section, select the taxes to be applied to the quote. To apply different taxes on each line, use the **Apply per line** button.
Once you have completed these steps, you can access the next section using the **Next** button or from the side menu.
## Other
[Section titled “Other”](#other)
In this section, you can add a **Note on the quote** or a **Legal note**. The information you add is reflected in the document’s footer. You can also attach files, as well as add delivery information for your goods.
### Additional documents
[Section titled “Additional documents”](#additional-documents)
From the **Additional documents** section, you can add an attachment to the quote:
* Use the **Select Files** option to browse for the file on your system.
* Select the file on your system and it is uploaded to B2Brouter.
* Use the **Save** button to save the changes to the quote and upload the file.
* You can also add an optional description of the attached file.
You can attach a link to the quote that directs to the document you want your customer to receive along with the quote:
* Use the option **Add link to the file**.
* The option is activated where you can add the link to the file and a description.
Once you have attached the document, you can access the following section.
## Collection
[Section titled “Collection”](#collection)
From the **Collection** section you can select the payment method where you want to collect the quote and the due date, among others:
* Under **Collection Method**, select how you wish to collect the quote.
* In **Expiration**, select the expiration date you wish to apply.
Quotes do not allow a rounding adjustment. If you need more context on this rule, see [Rounding and VAT calculation](/en/user-guide/manage-documents/rounding-and-vat-calculation).
Finally, once you have finished adding the data for all the sections of the quotation, you can save the changes and view the document using the **Save and preview** option. On the other hand, while you are editing, you can save the changes and continue modifying the estimate, using the **Save** option. You find both options at the bottom of the budget.
Once the budget has been generated, at the top of the document, you will find the main menu with the different options that allow you to manage it:
* **Edit**: you can go back to modify some data of the budget.
* **Send**: once you consider that the quotation is correct, you only have to use this option to send it to the customer. It is sent by e-mail.
* **Invoice**: with this option you can create the invoice using the budget data. This way you do not have to re-enter the data.
* **Download PDF**: you can download the quotation file to your system, in PDF format using the download button at the left part of the document.
* **More**: You can use other options such as **Duplicate quote**, **Delete quote** or **Open an incident in the Support Department with reference to the quote**.
At the top, there is a colored strip where you can also see the **updated budget status**.
At the bottom, there are two boxes:
* Under **Activity** you can see all the changes made to the budget and the related notifications. You can view the notification by clicking on the + symbol.
* In **Comments** you can write a private annotation concerning the budget.
## Send the quote
[Section titled “Send the quote”](#send-the-quote)
To send a quote correctly, follow these steps:
### 1. Create the client
[Section titled “1. Create the client”](#1-create-the-client)
First you have to [create the client](/en/user-guide/contacts/create-contact) to whom you want to send the quote.
You can also send a quote without a client. In this case, you can move onto the next step.
### 2. Send the quote
[Section titled “2. Send the quote”](#2-send-the-quote)
Once you have created and opened the quote, at the top you will find the option **Send**. Click on this and the quote will be sent directly from B2Brouter to your client, without you needing to do anything else. If the quote has been sent correctly, its status will change to **Sent**.
If you want to send a quote without a client, at this point you have to provide the email where you want to send the document.
Quotes are sent by email.
# Create a simplified invoice
> Learn how to create, send and manage simplified invoices in B2Brouter, including taxes, lines, PDF email delivery and TicketBAI.
To create a simplified invoice, go to the **Income** section of the main menu at the top, and select the **Tickets** option on the left-hand side menu.
* Select the option **Create ticket**.
* On the next page, you do not need to define the customer.
* Click on **Continue**.
Next, you can then access the simplified invoice form.
## General data
[Section titled “General data”](#general-data)
Under **General data** you can add general data such as number or date to the invoice. Fields marked with an asterisk are mandatory.
## Taxes
[Section titled “Taxes”](#taxes)
In this section, select the taxes to be applied to the invoice. To apply different taxes on each line, use the **Apply per line** button.
## Lines
[Section titled “Lines”](#lines)
From the **Lines** section you can add line information:
* In **Product or service name**, add the concept or description of the product or service you want to add to the line.
* In **Quantity**, add the quantity of the product or service you wish to invoice.
* In **Units**, select the most appropriate size for the product or service.
* In **Unit price**, add the unit price of the product or service you are adding to the line. At the end, the system calculates the result.
* Below the lines you find the option **Add item from catalogue**, which allows you to create the lines easily using the information of your catalogue created in B2Brouter.
* Use the **Add line** option to add as many lines as you need in the invoice.
You can also **Add a discount** or **Add an additional charge** to the invoice total:
### Add discount
[Section titled “Add discount”](#add-discount)
* Click on the **Add discount** option and activate the button.
* In **Discount description**, add the concept or description of the discount you wish to apply.
* In **Discount amount**, add the amount you wish to apply.
* This amount can be in currency or in percentage, something you must select through the drop-down menu.
### Add additional charge
[Section titled “Add additional charge”](#add-additional-charge)
* Click on the **Add additional charge** option.
* Under **Reason for charge**, add the name or reason.
* In **Amount of charge**, add the amount you want to apply.
* This amount can be in currency or as a percentage, which you will need to select via the drop-down menu.
* Check the **Apply taxes** box if you want the taxes on the invoice to be applied to the surcharge as well. Otherwise, they are not applied.
## Shipping
[Section titled “Shipping”](#shipping)
Simplified invoices are sent in PDF format by e-mail. From this section you can specify the recipient’s email address:
* In **Send to** add the email address of the invoice recipient.
* In **First name and surname** enter their first name and surname.
* In **Language**, select the language in which you want the invoice to appear.
Use the **Save and preview** option to save your changes and view the simplified invoice. To send an invoice from B2Brouter you only have to press one button. The sending is done directly from B2Brouter.
To do this you must follow the following steps:
* Go to the invoice preview.
* Use the **Send** button at the top right of the screen.
## Manage the statuses of simplified invoices
[Section titled “Manage the statuses of simplified invoices”](#manage-the-statuses-of-simplified-invoices)
B2Brouter always informs you of the current status of your invoices. You have two ways to check the status:
### 1. In the list of invoices
[Section titled “1. In the list of invoices”](#1-in-the-list-of-invoices)
Through the main menu at the top, go to the **Invoices** section and then **Simplified** through the options on the top left. This way you can consult the status of each invoice issued on the right hand side of the list.
### 2. On the invoice
[Section titled “2. On the invoice”](#2-on-the-invoice)
Follow the steps above and open the invoice by clicking on the invoice number. Once you have opened the invoice, at the top, you will find the current status of the invoice with a coloured band.
## Meaning of the statuses of the invoices issued
[Section titled “Meaning of the statuses of the invoices issued”](#meaning-of-the-statuses-of-the-invoices-issued)
The reception of invoice statuses may differ depending on the network through which the invoice was sent. As a general rule, you can find the following statuses depending on the selected sending method:
* **Draft**: the invoice has been created, but not sent. As a consequence, the recipient has not received it.
* **Downloaded**: the invoice has been downloaded.
* **Sent**: the invoice has been sent successfully and, in some cases, this is a final status. For example, if you send an invoice via email.
* **Rejected**: Your customer has rejected the invoice for some reason.
* **Collected**: The invoice has been collected by the receiver.
* **Closed**: the invoice has been paid and is in its final status.
## TicketBAI Declaration
[Section titled “TicketBAI Declaration”](#ticketbai-declaration)
You can also manage the TicketBAI declarations of your simplified invoices. To do so, you must [enable and configure tax declaration from settings](/en/user-guide/tax-report/ticketbai).
Once configured, to generate the declaration:
* Go to the preview of the simplified invoice.
* Use the **More options** button.
* In the dropdown, click on **Generate declaration**.
# Manage invoice statuses
> Learn how to manage issued and received invoice statuses in B2Brouter, including automatic updates and manual status changes.
To manage the status of an invoice, you must have previously created and sent it. Once the invoice has been sent, you have two ways to manage the status:
## Issued invoices
[Section titled “Issued invoices”](#issued-invoices)
You can control the status of the invoices you have sent from B2Brouter in two different ways:
### Receive status change
[Section titled “Receive status change”](#receive-status-change)
If the network through which you send the invoice allows it and your customer sends you the notification, B2Brouter shows you the updated status of each invoice. Here you can see [how to check invoice status](/en/user-guide/sending-documents/status-issued-invoices). In this case you don’t have to do anything, as the porter automatically updates every change that occurs.
### Modify the status manually
[Section titled “Modify the status manually”](#modify-the-status-manually)
You can change the status of an invoice manually. To do this, go to the **Invoices** section and then **Commercial invoices** or **Tickets** via the options on the top left, depending on what type of invoice you want to manage. Then open the invoice by clicking on the invoice number:
* Click on the box indicating the status of the invoice, located at the top left.
* A drop-down menu opens with several options.
* Select the option you want to use. These can be Sent, Accepted, Refused, Collected or Closed.
It is not advisable to modify the status of invoices issued manually, when you are sending them through networks such as the General Entry Points (FACe, eFACT, FACeB2B) or Peppol, because in these cases it is an automatic process.
## Received invoices
[Section titled “Received invoices”](#received-invoices)
If you receive invoices with B2Brouter you can also send status changes of received invoices to your suppliers. [Here you can see how to manage the statuses of the received invoices](/en/user-guide/receiving-documents/manage-status-received-invoices)
# Manage quote statuses
> Learn how to manage quote statuses in B2Brouter, check accepted or rejected quotes and update quote status manually.
To manage the status of a quote, you must first have [created](/en/user-guide/sending-documents/create-quotes) and [sent](/en/user-guide/sending-documents/create-quotes) it. Once the quotation has been sent, you have two ways to manage the status:
## Receive status change.
[Section titled “Receive status change.”](#receive-status-change)
The receiver can change the status of the quotation to **Accepted** or **Rejected** at the time of receiving the document. If he does so, B2Brouter shows you the updated status of each quote. You can check it on the right side of the quotation list or by opening the quotation, at the top.
### Modify the status manually
[Section titled “Modify the status manually”](#modify-the-status-manually)
You can modify the status of a quote manually. To do this, go to the **Revenues** section and then **Quotes** using the options at the top left.
* Select the quote you wish to modify.
* Click on **More options** at the top left.
* A dropdown opens with several options, including **Mark as**. Choose the option you want to use.
# Create and send a self-invoice
> Learn how to create, send and manage self-invoices in B2Brouter, including plan requirements, recipient actions and statuses.
A **self-invoice** is a type of invoice that a **customer** issues on behalf of their **supplier** to document a commercial transaction. This occurs when the **buyer** assumes the responsibility of issuing the tax document instead of the supplier.
Keep in mind that to create self-invoices in B2Brouter, you need a **Business** plan, although the recipient can receive self-invoices with a **Basic** plan.
### **How to create a self-invoice**
[Section titled “How to create a self-invoice”](#how-to-create-a-self-invoice)
1. Go to the **Expenses** section in the top main menu.
2. In the left-side menu, click **Self-invoices**.
3. Select the option **Create self-invoice**.
4. On the next page, a dropdown will appear where you must select the contact you want to issue the self-invoice to.
5. Choose the contact and select **Continue** to start creating the invoice.

In the form, follow the usual [steps to create an invoice](/en/user-guide/sending-documents/create-electronic-invoice).
1. Once you have created the invoice, use the **Save and preview** button to access the invoice preview.
2. A draft of the self-invoice is automatically created.
3. Use the **Send** button to issue the invoice.
The recipient will receive an email with a link to the invoice preview, or they can access it directly in the **Income** section.
### **Requirements and considerations**
[Section titled “Requirements and considerations”](#requirements-and-considerations)
* To **create** self-invoices, you must have a **Business plan**.
* The recipient can **receive** self-invoices with a **Basic plan**.
### **Managing received self-invoices**
[Section titled “Managing received self-invoices”](#managing-received-self-invoices)
Through the **Income > Received self-invoices** section, the recipient can:
* **Accept**: The invoice moves to the “Accepted” status.
* **Reject**: The invoice is marked as “Rejected” and caan be deleted.
* Download the invoice in PDF format.
When the recipient marks the self-invoice as paid, a notification is sent to the issuer.
#### **Self-invoice statuses**
[Section titled “Self-invoice statuses”](#self-invoice-statuses)
* **Sent**: When it is sent to the recipient.
* **Accepted**: When the recipient accepts it; it is then considered a received invoice for accounting purposes.
* **Rejected**: It can be edited or deleted.
* **Paid**: When the recipient confirms payment.
# Check the status of issued invoices
> Learn how to check issued invoice statuses in B2Brouter and understand sent, registered, accepted, rejected and read statuses.
B2Brouter always informs you of the updated status of your invoices. You have two ways to check the status:
### 1. In the list of invoices
[Section titled “1. In the list of invoices”](#1-in-the-list-of-invoices)
Through the top main menu, access the **Incomes** section and then **Invoices issued** through the options on the top left. This way you can check the status of each issued invoice on the right side of the listing.
### 2. On the invoice
[Section titled “2. On the invoice”](#2-on-the-invoice)
Follow the steps above and open the invoice by clicking on the invoice number. Once the invoice is opened, in the upper part, you will find the updated status of the invoice with a colored band.
## Meaning of the statuses of issued invoices
[Section titled “Meaning of the statuses of issued invoices”](#meaning-of-the-statuses-of-issued-invoices)
The reception of invoice statuses can be different depending on the network through which the invoice was sent. As a general rule, you can find the following statuses depending on the selected sending method:
### All shipping methods.
[Section titled “All shipping methods.”](#all-shipping-methods)
In all cases and without differentiating between the selected shipping method, B2Brouter provides you with these statuses:
* **Draft**: the invoice has been created, but has not been sent. As a consequence, the receiver has not received it and it can be edited.
* **Sent**: the invoice has been sent successfully and, in some cases, this is a final status. For example, if you send an invoice via email. This status appears once the button **Issue and send** is used.
* **Error**: for some technical reason, the reason for the invoice could not be fulfilled.
### Open networks (FACe, FACeB2B, eFACT, Peppol, etc.)
[Section titled “Open networks (FACe, FACeB2B, eFACT, Peppol, etc.)”](#open-networks-face-faceb2b-efact-peppol-etc)
These are the statuses you can receive when sending invoices through open networks, although it depends on each case:
* **Registered**: the invoice has been successfully registered and the receiver can already consult it.
* **Accepted**: the invoice has been accepted by the invoice recipient.
* **Reported as paid**: the receiver confirms payment of the invoice.
* **Rejected**: your customer has rejected the invoice for some reason.
### Email
[Section titled “Email”](#email)
Email is a sending method that does not allow status exchange. It is for this reason that, apart from those mentioned at the beginning, you can only receive the status **Read**, which refers to the fact that the receiver has received and opened the invoice.
#### Check the reasons for rejection
[Section titled “Check the reasons for rejection”](#check-the-reasons-for-rejection)
In the case of receiving the rejection of an invoice, you can consult the reason in detail through two spaces:
**1. At the top of the invoice** Open an invoice by clicking on the invoice number and at the top you will find the current status. If it is rejected, you can also consult the reason specified by the receiver.
**2. In the activity** Open an invoice by clicking on the number and, at the bottom, you will find the **Activity** box. In this section, you can check all the steps the invoice has gone through. You will also find the status changes and, if you receive a rejection status, you can consult the reason by clicking on the + symbol.
# Auto-renewal
> Learn how to manage the automatic renewal of your B2Brouter Premium plan subscription and how to activate or deactivate it.
If you pay by credit card or direct debit for one of the Premium plans, automatic renewal is activated and you don’t need to do anything else. This way, you don’t have to worry about renewing every time your plan expires.
However, you can always manage this option according to your priorities. To manage the renewal of your account:
* Click on the gear icon at the top of the page.
* In the drop-down menu, select the option **Account subscription**.
Once you are on your subscription page, you will find the information about your subscription plan.
* Use the option **Renewal and payment**.
* Activate or deactivate the automatic renewal according to your needs.
From B2Brouter we recommend you to activate this feature so you don’t have to worry about making the payment every time the annual period of the contracted service is over.
# Cancel a B2Brouter subscription
> Learn how to cancel a B2Brouter subscription and understand what happens to Professional, Business and eDocExchange plans.
To cancel a subscription, go to the **Account subscription** section by clicking the icon  and click **Renewal and payment**, located in the left-hand menu.
From this section you can:
* Review and update your payment method.
* Check the subscription renewal date.
* Cancel the subscription.
To complete the cancellation, click the **Cancel subscription** button. Once done, you will receive a confirmation email with the cancellation details.
## Effects of the cancellation
[Section titled “Effects of the cancellation”](#effects-of-the-cancellation)
* **Professional or Business subscription**
* The cancellation is processed immediately, but the subscription remains active until the renewal date.
* You will receive a confirmation email with the expiration date and recommendations (e.g., download important data or complete pending projects).
* During this period, you can **reactivate the subscription** at any time by clicking the **Reactivate subscription** button.
* If it is not reactivated, the subscription will expire on the renewal date and your access to the service will be suspended.
* **eDocExchange subscription (monthly)**
* Subscriptions can only be cancelled if they are on a **monthly** basis.
* The cancellation takes effect immediately and the subscription is deactivated on the same day it is processed.
* You will receive a confirmation email indicating that you no longer have access to Premium features and that your account has been converted to **Basic**.
* Any unused transactions will be lost.
* If there is an excess of transactions, it will be invoiced at the time of cancellation.
* If you change your mind, you can re-subscribe directly from the web application.
# Delete an account
> Learn how to permanently delete your B2Brouter account from the User profile and what to check before removing your data.
If you want to permanently delete your B2Brouter account, follow these steps:
1. Go to [B2Brouter](https://app.b2brouter.net/en/login).
2. In the top-right corner of the screen, click the user icon.
3. In the dropdown menu, select **User profile**.
4. In the **Profile** section, click the **Delete user** button.
## Important
[Section titled “Important”](#important)
* Account cancellation is **permanent** and cannot be undone.
* Once deleted, it will not be possible to reactivate the account or recover any associated data.
* Before proceeding, make sure you have downloaded any documents or information you wish to keep.
# Subscribe to Premium
> Learn how to upgrade to a B2Brouter Premium plan, choose Professional, Business or eDocExchange, and complete payment.
To subscribe to a **Premium** plan, you must be [registered in B2Brouter](https://app.b2brouter.net/en/register/) and [have your account properly configured](/en/user-guide/set-up/setting-up-your-company).
Once registration and configuration are complete, go to the **Account subscription** section by clicking the icon  in the header, next to your company name.
On the **Account subscription** page, you will find details of your current subscription and you will be able to subscribe to a **Premium** plan. To do so, follow these steps:
* Click **Upgrade subscription**.
* On the next screen, select the Premium subscription you want to activate: **Professional**, **Business**, or **eDocExchange**.

## Professional and Business
[Section titled “Professional and Business”](#professional-and-business)
If you select a **Professional** or **Business** subscription, you will be taken to the subscription payment screen. The following payment options are available:
* **Credit or debit card**: Enter your card details and confirm the payment.
* **SEPA bank debit**: Enter your IBAN and confirm the payment.
* **Bank transfer**: You will be shown the bank details to complete the transfer. Once completed, upload the proof of payment.
After providing the required information, click **Confirm subscription** to finish the process.
The subscription will be activated immediately if the payment is made by card or SEPA debit. If you choose to pay by bank transfer, the subscription will be activated once our administration team verifies the payment receipt.
Check the [Premium features](https://www.b2brouter.net/global/prices/) that will become available once the subscription is active.
## eDocExchange
[Section titled “eDocExchange”](#edocexchange)
If you select the **eDocExchange** subscription, you will be directed to a page where you can choose the tier you wish to subscribe to.
Each tier includes a specific number of monthly transactions. A transaction is considered any electronic document processed through our services, including:
* Sending
* Receiving
* Generating a document through import and downloading it
Once you have selected the tier, click **Subscribe to eDocExchange**. On the next screen, complete the payment. The available options are:
* **Credit or debit card**: Enter your card details and confirm the payment.
* **SEPA bank debit**: Enter your IBAN and confirm the payment.
The subscription will be activated once the payment is successfully completed.
# Check responses from B2Brouter Support
> Learn how to check B2Brouter Support responses from your ticket or email, reopen tickets and understand request statuses.
If you have [opened an incident in the B2Brouter Support Department](/en/user-guide/support/open-incident-report), you have two ways to check the response from our team:
## Check the response from the incident
[Section titled “Check the response from the incident”](#check-the-response-from-the-incident)
Once our support team has sent you the answer, you can view it by accessing the incident you had previously opened. To do this:
* Click on the question mark icon located at the top right of the screen.
* Then select **Support**.
* Once you access the next page, you will see the list of all the issues you have previously opened.
* On the right side you see the status of the issue.
* Click on **Open** to see your queries and the support response.
If the answer received does not solve your doubts or if you want to comment anything else, you can reopen the issue:
* Select the query you are interested in managing.
* In the text box at the bottom add your answer.
* You can upload a file using the **Add file** option.
* Use the **Accept** option to send us your comment.
* Automatically, your issue is reopened and we will be able to check it again.
## Check the response from the email
[Section titled “Check the response from the email”](#check-the-response-from-the-email)
The answer that our support team writes to the issue you have opened is also sent to the email address of the user with whom you have opened the issue. Thus, you also receive the message in your mailbox.
You can also reopen the ticket from the email message you receive with the reply. Simply reply to this email with the comment you want to make, and the ticket is automatically reopened.
## Statuses of the requests
[Section titled “Statuses of the requests”](#statuses-of-the-requests)
These are the different statuses that a ticket can have and their meanings:
* **New**: the issue has been sent to our Support Department and is pending response.
* **Closed**: we have responded to your comment and we consider that your issue has been resolved. Of course, you can reopen the issue if the answer is not clear to you or you think it has not been resolved.
* **In progress**: We have seen your comment and have responded to you, but it is not yet resolved. We are working to resolve your issue and this may take some time. We will get back to you once it is resolved and close it.
* **Awaiting information**: We have responded to your message, but we have not yet been able to resolve the issue because we are missing information to proceed. If you have an issue in this status, it is very important that you check our response and reply with the information we are waiting for.
* **Reopened**: when you reply to an issue that is in Closed or In Process status, it goes to Reopened status.
# Get help from B2Brouter
> Learn the available ways to get help from B2Brouter, including documentation, support tickets, contact forms and social media.
If you have questions about B2Brouter or how it works, there are several ways to get help:
## B2Brouter Docs
[Section titled “B2Brouter Docs”](#b2brouter-docs)
This is the documentation which shows you how to use all the B2Brouter functionalities step by step and in detail. Use the table of contents to navigate between the different contents, depending on what you need to know.
## B2Brouter Support
[Section titled “B2Brouter Support”](#b2brouter-support)
All B2Brouter users have the option of support within your account. Through this option, you can contact our customer service department to solve any issue related to B2Brouter or billing.
[Here you can see how to open an incident in B2Brouter support](/en/user-guide/support/open-incident-report).
## Contact us
[Section titled “Contact us”](#contact-us)
If you don’t have a B2Brouter account but would like to ask us a question, you can [contact us here](https://www.b2brouter.net/es/contactar/).
## Social media
[Section titled “Social media”](#social-media)
You can also contact us through our social media profiles:
* **YouTube**: we publish tutorial videos where we explain how to use B2Brouter and perform specific actions with the platform. [You can subscribe here](https://www.youtube.com/user/b2brouter)
* **Twitter**: allows you to keep up to date with incidents, news, events, new functionalities and other aspects related to electronic invoicing, [You can find us here.](https://twitter.com/b2brouter?lang=es)
* **LinkedIn**: allows you to meet our team, receive information on electronic invoicing, keep up to date with news and even [access the electronic invoicing group we manage](https://www.linkedin.com/groups/12519109/), where you can participate, ask questions and receive a variety of information on the sector. [You can find us here](https://www.linkedin.com/company/b2brouter/).
We answer all our users’ queries.
# Open a support ticket
> Learn how to open a support ticket in B2Brouter, attach files and provide invoice details so the support team can help you.
If you have any doubts about how B2Brouter works or you have an issue with an invoice, you can use the **Support** option to contact our team, which will provide you with a solution.
To open an incident report in **Support** you have to follow these steps:
* [Log into your account](https://app.b2brouter.net/en/login) in B2Brouter.
* Once you have entered, use the **Support** option at the top right of the screen.
* Then select **Support**.
* On the next page click on the option **Open ticket**.
A form appears that enables you to add information that you want to send to us:
* Indicate a **Subject**.
* Add a **Message**
* With the option **Select files** you have the possibility of attaching a file.
It is important to provide us with as much information and as much detail as possible. This will help us to better understand your doubt and provide you with a smoother and swifter answer. Also remember to indicate the number of the affected invoice, where relevant.
Once we have received your message we will reply as quickly as possible. Bear in mind that we reply to all our users, regardless of the plan they have contracted. Nevertheless, users with the [Premium plans](https://www.b2brouter.net/global/prices/) have priority, so if you are a Basic plan user we ask you for some patience, because there might be a slight delay.
[Here you can see how to consult our responses](/en/user-guide/support/consult-our-responses).
# Get telephone support from B2Brouter
> Learn when B2Brouter telephone support is available and which plans can request phone support through the support channel.
B2Brouter’s main support channel is [available in your account](/en/user-guide/support/open-incident-report).
Communication is via messages, since, because of data protection, our support team can only access information from your account if you open an incident report. Additionally, to guarantee a good support service, in the majority of cases we need to consult your data.
However, there are some cases in which our users can avail of telephone support. This depends on the service they have contracted.
## Users who can avail of telephone support
[Section titled “Users who can avail of telephone support”](#users-who-can-avail-of-telephone-support)
Only users of the [Enterprise plan](https://www.b2brouter.net/global/prices/) can avail of telephone support, because of the complexity of the projects and of more technical processes.
Users of the Enterprise plan have a project manager who they can contact by telehone to solve more technical and complex issues that can arise.
However, we recommend that even users of the Enterprise plan use the support channel for the following reasons:
* The large majority of issues can be solved via this channel, much more efficiently than by phone.
* The creation of the incident report enables us to do an exhaustive follow-up and make improvements to the website to offer our users a much more reliable tool.
## Users with moderate telephone support
[Section titled “Users with moderate telephone support”](#users-with-moderate-telephone-support)
Users of the Business plan are provided with telephone support if they request it through the support contact.
## Users without telephone support
[Section titled “Users without telephone support”](#users-without-telephone-support)
Users of the Professional plan and the Basic plan cannot avail of telephone support. However, our specialised support team is prepared to solve any issue through the support channel. In addition, users of the Professional plan have priority support.
# DGFiP e-Invoicing and e-Reporting
> Learn how to assign B2Brouter as your PA, issue and receive French e-invoices, manage the invoice lifecycle and tax reports in B2Brouter.
This guide covers everything you need to operate with the French e-invoicing reform (RFE) in B2Brouter, from first activation to the day-to-day management of your invoices and tax reports.
[Are you a developer? ](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting)Go directly to the DGFiP developer guide.
[Glossary ](#8-glossary)Look up the meaning of the key terms used in this guide.
## The RFE and the Plateforme Agréée (PA)
[Section titled “The RFE and the Plateforme Agréée (PA)”](#the-rfe-and-the-plateforme-agréée-pa)
The Réforme de la Facturation Électronique (RFE) is the French regulation that progressively requires all companies to issue, send and receive their invoices in electronic format, instead of paper or plain PDF. It also includes e-reporting: sending tax reports to the DGFiP (Direction Générale des Finances Publiques).
To comply with this obligation, companies must use a Plateforme Agréée (PA), an operator registered and authorised by the DGFiP. The PA acts as a trusted intermediary between the company and the tax authority: it receives and transmits invoices in structured format, and sends e-reporting data to the DGFiP.
**B2Brouter is a certified Plateforme Agréée (PA).** When you assign us as your PA, we take care of your registration in the Annuaire (the central directory of the PPF — Portail Public de Facturation), the transmission of your documents, your tax reports, and the management of your invoice status lifecycle.
#### How to comply with the RFE
[Section titled “How to comply with the RFE”](#how-to-comply-with-the-rfe)
The RFE is being rolled out progressively according to company size. From 1 September 2026, all companies are required **to be able to receive electronic invoices**. The obligation to **issue electronic invoices and report to the DGFiP with tax reports** applies later depending on company type (large companies and ETIs from 1 September 2026; SMEs and micro-enterprises from 1 September 2027).
**With B2Brouter you can comply with both obligations right away: simply assign us as your PA to start receiving, and activate the tax report when you need to issue.**
In B2Brouter this translates into two modes, which we will use throughout this guide:
* **Receive only** — the account has B2Brouter assigned as PA, but the tax report is not activated. Covers the obligation to receive.
* **Full e-Reporting** — in addition to the PA, the tax report is activated. Covers both receiving and issuing, with automatic generation of tax reports.
#### Differences between the RFE and Chorus Pro (B2B ≠ B2G)
[Section titled “Differences between the RFE and Chorus Pro (B2B ≠ B2G)”](#differences-between-the-rfe-and-chorus-pro-b2b--b2g)
Chorus Pro is the circuit used to invoice **French public administrations (B2G)**. The RFE, on the other hand, governs B2B invoicing between companies, through your Plateforme Agréée (PA). They are two independent circuits: if your company invoices both other businesses and public administrations, **you can use both circuits in parallel** without overlap.
***
## Your company structure in B2Brouter: main account and organisational units
[Section titled “Your company structure in B2Brouter: main account and organisational units”](#your-company-structure-in-b2brouter-main-account-and-organisational-units)
In France, every company has a **SIREN** number that uniquely identifies it, and every establishment or service has its own **SIRET** code, derived from that SIREN. In B2Brouter we follow the same logic: the **main account** is created with the company's SIREN, and each establishment is represented as an **organisational unit (OU)** identified by its SIRET code.
If your company has several establishments or services under the same SIREN, each one is represented as an independent organisational unit (OU) within the same account. To create an organisational unit with the corresponding SIRET or suffix code, access the account created with the SIREN and follow the steps in the [organisational units](/en/user-guide/account-settings/setting-up-your-company/#organisational-units) setup guide.
PA assignment and tax report for organisational units
Each organisational unit has its own separate billing. PA assignment and the tax report are also independent: you must activate them on the main account and on each organisational unit, as needed. This provides flexibility: for example, you can have the main account in [Receive only](#how-to-comply-with-the-rfe) mode and one or more organisational units in [Full e-Reporting](#how-to-comply-with-the-rfe) mode, or vice versa.
***
## 1. PA assignment and tax report activation
[Section titled “1. PA assignment and tax report activation”](#1-pa-assignment-and-tax-report-activation)
When you assign B2Brouter as your PA, you are registered in the PPF Annuaire with a Peppol transport (the international electronic document exchange network). This allows you to **receive** invoices from your suppliers immediately ([Receive only](#how-to-comply-with-the-rfe) mode).
If you also need to **issue** invoices via DGFiP, after assigning B2Brouter as your PA you must activate the tax report ([Full e-Reporting](#how-to-comply-with-the-rfe) mode). Once activated, B2Brouter takes care of generating and sending the corresponding tax reports to the DGFiP and of sending your invoices via Peppol.
### 1.1 Assign B2Brouter as your PA
[Section titled “1.1 Assign B2Brouter as your PA”](#11-assign-b2brouter-as-your-pa)
Premium subscription required
A Premium subscription is required to assign B2Brouter as your PA. [Upgrade your subscription](/en/user-guide/subscription/subscribe-premium) if you are still on the Basic plan.
Steps to assign B2Brouter as your PA:
1. ⚙ **Account settings** → in the side menu, go to the **e-Reporting France** section.
2. Review the identifier and click **Continue**.
3. **Attach the document** proving the company's identity (for example, the Kbis).
4. Click **Save and complete**.
##### B2Brouter is now your PA and automatically initiates two processes:
[Section titled “B2Brouter is now your PA and automatically initiates two processes:”](#b2brouter-is-now-your-pa-and-automatically-initiates-two-processes)
**1. Registers your company in the PPF Annuaire:**\
Your identifier becomes discoverable by any platform in the French e-invoicing ecosystem.
Publication of the identifier in the PPF Annuaire can take up to **24 hours on first activation**. This is a DGFiP infrastructure rhythm, not a B2Brouter delay. Until it has propagated, you will not be able to issue or receive DGFiP invoices.
Do not confuse with the INSEE directory
The website `annuaire-entreprises.data.gouv.fr` is the public INSEE directory, a different database from the PPF Annuaire. Your company appearing there does not mean it is in the PPF Annuaire: a newly created company can be in the INSEE without having propagated to the PPF yet. The check that counts is at `facturation.chorus-pro.gouv.fr/annuaire`.
**2. Creates your Peppol identifier with scheme 0225:**\
Your company is enabled to receive electronic invoices from any platform connected to Peppol in France. If the account already had a Peppol identifier, it is **replaced** by the identifier with scheme 0225.
> **What is scheme 0225?** In Peppol, each identifier carries a scheme code indicating what type it is. Scheme `0225` is the one that identifies French companies on the Peppol network, based on their SIREN or SIRET. Other schemes identify other types of identifier: for example, `0002` for SIREN or `0009` for SIRET.
Identifier already registered with another PA
If the identifier was already registered with another PA, see the [Change PA](#13-change-pa) section.
##### Common activation errors
[Section titled “Common activation errors”](#common-activation-errors)
| Error | What to do |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **PPF error 422** (e.g. *"Le SIREN en entrée n'existe pas ou la date de début d'effet de ligne annuaire est antérieure à la date de début d'effet de ce SIREN"*) | The SIREN is not yet in the PPF Annuaire, or the start date entered is earlier than the SIREN's effective date. Check your SIREN at facturation.chorus-pro.gouv.fr/annuaire, adjust the date if needed, and try again. |
| **SIREN already in use** | Another active account already has this SIREN. If you think the duplicate account is yours, contact [support](/en/user-guide/support/open-incident-report/) indicating both accounts. |
| **TVA number already in use** | Same case, with the TVA number we automatically derive from your SIREN: it must be unique across the entire system. Retrying will not resolve it — contact [support](/en/user-guide/support/open-incident-report/) to decide which account retains the tax identity. |
| **"Adresse de facturation non active" / "Plateforme agréée rattachée: Non"** | Your record exists in the Annuaire but no platform has claimed it yet. Repeat the assignment in ⚙ Account settings → e-Reporting France. |
##### Verify that the registration is published on the Peppol network
[Section titled “Verify that the registration is published on the Peppol network”](#verify-that-the-registration-is-published-on-the-peppol-network)
Once B2Brouter is assigned as PA, you can check how the Peppol network registration is progressing in **⚙ Account settings → Peppol**. Each indicator can show one of these states: Not published, In progress or Published.
| Indicator | What it means |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **SML** | The global registration on Peppol. Your company is making itself known on the Peppol network. |
| **SMP** | The metadata service (in B2Brouter this follows the SML). The link needed for invoices to reach you correctly is being set up. |
| **Peppol Directory** | Public listing of companies discoverable on Peppol. |
When **SML** and **SMP** are **Published**, you can already receive invoices via Peppol. The Peppol Directory status does not affect this — it only determines whether you appear in the public search listing.
### 1.2 Activate the tax report
[Section titled “1.2 Activate the tax report”](#12-activate-the-tax-report)
If your mode is [Full e-Reporting](#how-to-comply-with-the-rfe), after assigning B2Brouter as your PA you need to activate the tax report.
1. Click the **"Activate e-Reporting France"** switch.
2. **Review the form data**.
* Operation type — Services, Goods or Mixed: indicates the type of company activity for tax reporting purposes.
* TVA exemption reason.
* TVA Regime — The TVA Regime determines how often tax reports are sent to the DGFiP. Make sure you select the one that applies to you.
* Declarant email address — This email is needed to generate tax reports for cross-border and simplified invoices.
3. Click **Save and activate**.
From this moment on, B2Brouter generates and transmits tax reports automatically, with no manual action on your part:
* Every domestic B2B invoice you issue is deposited in the PPF as a tax report.
* B2C invoices and those with a non-French recipient are accumulated and sent to the DGFiP at the frequency you selected in **TVA Regime**.
You can view the transmitted tax reports in the **Tax** section of the application's main menu.
For more information, see the [Tax reports](#7-tax-reports) section of this guide.
### 1.3 Change PA
[Section titled “1.3 Change PA”](#13-change-pa)
Whether you are coming from another Plateforme Agréée or want to leave B2Brouter for another one, the change involves two separate registrations that behave differently:
* The **Annuaire** is updated when you activate the service with B2Brouter.
* The **Peppol registration** is what can block you: an identifier can only be active with one Peppol provider at a time. If it is already active with another PA, assigning B2Brouter is not enough: you need a **migration code** from that PA, or for it to unpublish the identifier.
##### Coming to B2Brouter from another PA
[Section titled “Coming to B2Brouter from another PA”](#coming-to-b2brouter-from-another-pa)
Follow the normal steps to [assign B2Brouter as your PA](#11-assign-b2brouter-as-your-pa). If your identifier is already registered with another Peppol provider, you will see a notice indicating this, with two options:
* **Enter the migration code**, if your previous PA has provided one.
* **Ask your previous PA to unpublish it** from Peppol. Make sure they unpublish from **Peppol** and not just from the **Peppol Directory**: unpublishing the directory only removes you from searches, it does not free the identifier, and activation will stay blocked.
Ask your previous PA whether they offer a migration code: it is the only way to change PA without losing the ability to receive invoices during the transition. If they do not provide one, write to them asking them to contact your new PA to carry out the transfer: **every French PA is required to offer this manual path**, so having no code does not leave you stuck.
##### Leaving B2Brouter for another PA
[Section titled “Leaving B2Brouter for another PA”](#leaving-b2brouter-for-another-pa)
Go to ⚙ **Account settings → Peppol section** in the side menu. In your company's Peppol summary card, click the 3 dots on the line showing the status. You will see three options:
| Option | What it does |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Generate migration code** Recommended | Generates a code you give to your new PA to complete the migration without interrupting the service. |
| **Unpublish from Peppol** | Removes your identifier from the Peppol registry. You will have no service until a platform publishes it again. |
| **Unpublish from the Peppol Directory** | Removes your company from the public catalogue where others can search for you. You stay connected and can send and receive: you simply stop appearing in searches. |
Always use the migration code whenever possible.
***
## 2. Create a French contact
[Section titled “2. Create a French contact”](#2-create-a-french-contact)
This section covers only the DGFiP-specific points; for the general contact creation process, see the [general contact creation guide](/en/user-guide/contacts/create-contact/).
Organisational units in contacts
Just as in your own account, a contact can also have organisational units. If the client invoices from several establishments, do not create a separate contact for each SIRET: create the contact with the company's SIREN and add its establishments as [organisational units](/en/user-guide/contacts/create-uo/), following the same structure as in your account.
##### Invoice delivery method
[Section titled “Invoice delivery method”](#invoice-delivery-method)
When creating the contact, pay attention to the invoice delivery method: in [Receive only](#how-to-comply-with-the-rfe) mode you can select any option, but in [Full e-Reporting](#how-to-comply-with-the-rfe) mode the option to select is **Peppol Network**.
##### Automatic generation of contact identifiers
[Section titled “Automatic generation of contact identifiers”](#automatic-generation-of-contact-identifiers)
B2Brouter auto-completes related identifiers from the one you enter: if you provide the SIREN or SIRET, it automatically generates the TVA number and the Peppol identifier; if you provide the TVA number, it generates the SIREN and the Peppol identifier.
The contact record holds three different codes, each with its own role:
| Identifier | Field | Scheme | Value |
| --------------------------- | ------------ | -------------------------------- | ------------------------------------- |
| Company identifier | `cin_scheme` | `0002` (SIREN) or `0009` (SIRET) | 9 digits (SIREN) or 14 digits (SIRET) |
| Tax identifier / TVA number | `tin_scheme` | `9957` | `FR{kk}{siren}` |
| Peppol identifier | `pin_scheme` | `0225` | SIREN or `SIREN_SIRET` |
Legacy contacts
Contacts created **before activating DGFiP tax reporting**, or **before 31/03/2026**, may not have the TVA number generated automatically. If you issue an invoice against one of these contacts, the error *"El número de TVA del cliente es obligatorio para la declaración DGFiP"* will appear. Edit the contact, add the TVA number and re-issue.
##### Annuaire verification
[Section titled “Annuaire verification”](#annuaire-verification)
When you create a French contact, B2Brouter automatically queries their presence in the PPF Annuaire (Flux 11). You can check this status in the contact record, in the **Summary** section:
| Status | What it means |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Registered** | Invoices are transmitted via the Peppol network and generate their corresponding tax report. |
| **Not registered** | Invoices are created, but no tax report is generated ([Flux 1](#7-tax-reports)), as the declaration would not be valid. The client must register with a PA. |
| **Verification pending** | Creation is not blocked: the invoice is issued normally and its tax report is generated. Annuaire verification completes later. |
##### Non-French contacts
[Section titled “Non-French contacts”](#non-french-contacts)
Any invoice with a contact from another country automatically generates a cross-border tax report (Flux 10). In the invoice delivery method, select **Peppol Network and enter the client's Peppol identifier**: in this case it will not be scheme 0225, but whichever one corresponds to their country.
##### DROM (Guadeloupe, Martinique, Réunion)
[Section titled “DROM (Guadeloupe, Martinique, Réunion)”](#drom-guadeloupe-martinique-réunion)
Contacts with country Guadeloupe, Martinique or Réunion are considered French territory for tax purposes: their invoices generate the domestic tax report (Flux 1), not the cross-border one, and are transmitted with country code `FR`. Other overseas territories (New Caledonia, French Polynesia, Saint Pierre and Miquelon, Wallis and Futuna, TAAF...) are considered exports and generate the cross-border tax report (Flux 10).
***
## 3. Issue an invoice
[Section titled “3. Issue an invoice”](#3-issue-an-invoice)
This section covers invoice issuance in [Full e-Reporting](#how-to-comply-with-the-rfe) mode. If your account is in [Receive only](#how-to-comply-with-the-rfe) mode, you can issue invoices in the usual way in the application, but without the DGFiP-specific features; for more information on creating an invoice, see the [general invoice creation guide](/en/user-guide/sending-documents/create-electronic-invoice/).
### 3.1. Invoice form
[Section titled “3.1. Invoice form”](#31-invoice-form)
#### General data
[Section titled “General data”](#general-data)
Verify that **Transport** shows **Peppol Network** and **Format** shows **France UBL Invoice CIUS**. If so, the invoice will be transmitted via the Peppol network as required by DGFiP; if you have any doubts about these values, see the [Create a French contact](#2-create-a-french-contact) section.
##### Document formats
[Section titled “Document formats”](#document-formats)
| Format | When it is used |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **UBL Peppol France CIUS** | Default, if the recipient is registered in the Annuaire/Peppol. |
| **Factur-X** | For recipients not registered with identifier 0225. It is a **PDF/A-3 with embedded CII XML**; a scanned or printed PDF is not Factur-X. |
| **CII France CIUS** | Alternative XML variant, if the recipient requires it. |
Further down the form you will find the **Operation type** selector, pre-selected by default with the value you configured when [activating the tax report](#12-activate-the-tax-report). You can change it if this particular invoice requires it, or change the default value in ⚙ Account settings → e-Reporting France.
#### Invoice lines
[Section titled “Invoice lines”](#invoice-lines)
Each invoice line includes description, quantity, unit price without TVA and TVA type.
**French TVA types:**
| TVA | Use |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **20 %** | Standard rate. |
| **10 %** | Catering, construction, transport. |
| **5.5 %** | Food, books, energy. |
| **2.1 %** | Press, reimbursable medicines. |
| **0 % Exempt** | CGI exemptions — [code required](#tva-exemption--mandatory-code) `VATEX-FR-CGI…` |
| **0 % Zero-rate** | Franchise en base de TVA (`VATEX-FR-FRANCHISE`). |
| **0 % Reverse charge** | Reverse charge (`VATEX-FR-AE`). |
| **0 % Détaxe** | Transaction outside the scope of TVA (e.g. sales under the détaxe scheme to travellers). No VATEX code needed: the line is declared with category `O` and 0 percentage. |
##### TVA exemption — mandatory code Important
[Section titled “TVA exemption — mandatory code ”](#tva-exemption--mandatory-code-)
If you apply 0 % with exemption, you must indicate the corresponding `VATEX-FR-…` code in the line comment. Without the code, the invoice is created but **is not transmitted to the PPF**: it gets stuck and must be deleted and recreated with the correct code.
If you have already added a VATEX code and the invoice is still not transmitting, check that the code is written exactly as it appears in the list, with no added text — for example `VATEX-EU-IC`, not `VATEX-EU-IC Intra-community exemption`. The field is truncated to 50 characters, so any extra text can cause a mismatch and validation will fail silently, just as if you had not entered any code. If you import the invoice in XML, the code must also be in the exemption reason tag — not in the code tag, which we do not read.
**Most commonly used VATEX codes:**
| Code | Use |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VATEX-FR-FRANCHISE` | Franchise en base de TVA (micro-enterprises). B2Brouter maps it to category `Z` (zero-rate) in the XML. |
| `VATEX-FR-AE` | Reverse charge. |
| `VATEX-EU-IC` | Intra-community sale. |
| `VATEX-FR-CGI261-1` | Medical services. |
| `VATEX-FR-CGI261-3` | Teaching and vocational training. |
| `VATEX-FR-CGI261-5` | Non-profit organisations. |
| `VATEX-FR-CGI261C-1` | Banking and financial transactions. |
| `VATEX-FR-CGI261C-2` | Insurance and reinsurance. |
| `VATEX-FR-CNWVAT` | Credit note without TVA ("avoir net de taxe", e.g. an early-payment discount taken). Only valid on credit notes (types 261, 381 or 396); rejected on invoices (rule G6.21). |
| `VATEX-EU-F` / `VATEX-EU-I` / `VATEX-EU-J` | Margin scheme (second-hand goods, works of art and collectables, travel agencies). The line is declared with category `E`, base = amount including TVA. |
For the full list (more than 20 codes), see the [DGFiP developer guide](/en/developers/guides-by-country/france/dgfip-e-invoicing-and-e-reporting#tva-exempt-lines-category-e).
### Payment information — mandatory fields (B2B)
[Section titled “Payment information — mandatory fields (B2B)”](#payment-information--mandatory-fields-b2b)
Three fields are **mandatory** for French B2B invoices. If any is missing, an explicit error appears on each field.
* **Payment information** — reference and legal notices (legal form, share capital, RCS).
* **Payment method (text)** — method + IBAN/BIC.
* **Payment terms** — deadline, late payment penalties, early payment discount.
> These fields are **not mandatory** for B2C invoices (invoices to individuals).
Examples:
```plaintext
Payment information:
FA-2026-0048 — Exemplar SAS, SAS au capital de 50 000 EUR, RCS Paris 123 456 789
```
```plaintext
Payment method:
Virement bancaire, IBAN FR00 0000 0000 0000 0000 0000 000, BIC XXXXFRPP
```
```plaintext
Payment terms:
Net 30 jours à compter de la date de facture.
Pénalité de retard : 12% annuel (Art. L441-10 C. com.).
Indemnité forfaitaire de recouvrement : 40,00 EUR (Art. D441-5 C. com.).
Pas d'escompte pour paiement anticipé.
```
### What happens when the invoice is sent
[Section titled “What happens when the invoice is sent”](#what-happens-when-the-invoice-is-sent)
When you issue the invoice, B2Brouter transmits it to the recipient via the Peppol network and, in parallel, generates the corresponding tax report and deposits it in the PPF.
```plaintext
Issue invoice - B2Brouter → Invoice → Recipient
↘ Tax report → DGFiP (PPF)
```
To learn more about how and when B2Brouter reports to the DGFiP, see [section 7. Tax reports](#7-tax-reports).
If the invoice ends up in error status, you will find information on how to proceed in the [Invoice with error](#invoice-with-error) section.
***
## 4. Invoice lifecycle
[Section titled “4. Invoice lifecycle”](#4-invoice-lifecycle)
In the invoice view you will see a **main status** and, right next to it, the validation status. The other phases of the lifecycle — sending, tax report, reception and response — can also be consulted by hovering over them. These status changes are communicated via CDAR messages (Flux 6).
| Lifecycle phase | What it indicates |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Validation status** | Whether the structured document and mandatory fields are correct, before attempting to send anything. |
| **Sending status** | Whether B2Brouter has successfully transmitted the documents: the invoice to the recipient and the tax report to the DGFiP. Values: `Sent` or `Error`. |
| **Tax report status** | Whether the DGFiP has accepted the tax report. Value: `Registered`. See details in the [When and how the PPF confirms your tax report (CDV)](#tax-report-lifecycle) section. |
| **Reception status** | Whether the recipient has received the invoice. Value: `Registered` or `Error`. |
| **Response status** | The recipient's action on the invoice: `Accepted`, `Rejected` or `Paid` (CDAR 212, *Encaissée*). Depends solely on the recipient. |
If there is a sending, reception or tax report error, the invoice shows main status `Error` — the cause may be in any of the three.
### Invoice with error
[Section titled “Invoice with error”](#invoice-with-error)
Before being registered, an invoice in `Error` status can be deleted, corrected and recreated. Once registered, it can no longer be deleted: correction is done via a [credit note](#5-credit-notes-rectifying-invoices).
1. Open the invoice to read the error reason. If the error is in the tax report, it will not appear here — you must check it in its own view:
* Access the tax report for this invoice directly from the **Invoice view** → **More options** → **View declaration**.
* Or see the full list of tax reports in the **Tax** section.
2. Delete the invoice.
3. Create a new invoice with the error corrected.
4. Send the invoice.
#### Common errors
[Section titled “Common errors”](#common-errors)
| Error | What to do |
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **"Remittance information is required for DGFiP reporting"** | The Payment information field is missing. |
| **"Payment information is required for DGFiP reporting"** | The Payment method (text) field is missing. |
| **"Payment terms are required for DGFiP reporting"** | The Payment terms field is missing. |
| **"El número de TVA del cliente es obligatorio para la declaración DGFiP"** | Contact has no TVA number. Edit the contact and add the TVA number. |
| **"Invoice number too long / invalid chars"** | Number with >20 characters or invalid characters. Adjust it. |
| `REJ_COH BT-47` | The buyer's SIREN does not exist in the PPF Annuaire. The recipient must register with a PA. |
| **Exempt line without VATEX code** | Delete the invoice and recreate it with the [correct VATEX code](#tva-exemption--mandatory-code). |
| `parameter_invalid` (SIREN) | The SIREN has a digit incorrectly entered and does not pass format validation. |
| `parameter_taken` (SIREN) | The SIREN is already associated with another account. Contact [support](/en/user-guide/support/open-incident-report/). |
***
## 5. Credit notes (rectifying invoices)
[Section titled “5. Credit notes (rectifying invoices)”](#5-credit-notes-rectifying-invoices)
This section covers only the DGFiP-specific points; for the general credit note creation process, see the [general credit note guide](/en/user-guide/sending-documents/create-credit-note/).
* B2Brouter generates a **UBL CreditNote** document with type code **381**.
* The amounts you rectify — whether you enter them as positive or negative — are always treated fiscally as amounts that reduce what was originally declared.
* The same three mandatory payment fields (Payment information, Payment method, Payment terms) are also mandatory.
* When you save and send, the credit note is transmitted to the PPF like any invoice and generates its domestic tax report (Flux 1).
***
## 6. Receive invoices
[Section titled “6. Receive invoices”](#6-receive-invoices)
This section covers only the DGFiP-specific points; for more information on receiving invoices, see the [general invoice reception guide](/en/user-guide/receiving-documents/receive-einvoices/).
With your account activated (in either of the [two modes](#how-to-comply-with-the-rfe)), you are registered in the Annuaire and receive your suppliers' invoices automatically through the Peppol network, with your scheme 0225 identifier, fulfilling the reception obligation.
##### Tax report on received invoices
[Section titled “Tax report on received invoices”](#tax-report-on-received-invoices)
If the supplier is French, you do not need to declare anything: the issuer already does it through their PA ([Flux 1](#domestic-b2b-invoices-flux-1)). If the supplier is from another country, the invoice is automatically included in your purchases Ledger ([Flux 10](#b2c-and-cross-border-flux-10)).
##### What you can do with a received invoice
[Section titled “What you can do with a received invoice”](#what-you-can-do-with-a-received-invoice)
You will find invoices received from your suppliers in the **Expenses** section, where you can open any of them to perform the following actions.
You can approve, reject or mark as paid an invoice; B2Brouter automatically notifies both the supplier and the DGFiP — you do not need to do anything else.
| Action | What it means |
| ----------------------- | -------------------------------------------------------------------------------------- |
| **Approve** `Approuvée` | Notifies the supplier and the DGFiP that you accept the invoice. (CDAR 205) |
| **Reject** `Refusée` | Notifies the supplier and the DGFiP of the rejection. A reason is required. (CDAR 210) |
| **Mark as paid** | Notifies the supplier and the DGFiP that you have paid the invoice. (CDAR 211) |
##### Error in invoice amounts
[Section titled “Error in invoice amounts”](#error-in-invoice-amounts)
If you detect an error in the amounts of a received invoice, you cannot correct it: **the amounts are those declared by the issuer and are copied literally** — we do not recalculate them. Reject it stating the reason so the issuer can reissue it corrected — but first check that it is a real error: a one-cent rounding difference is not.
***
## 7. Tax reports
[Section titled “7. Tax reports”](#7-tax-reports)
In [Full e-Reporting](#how-to-comply-with-the-rfe) mode, B2Brouter generates and transmits tax reports automatically, with no manual action. In [Receive only](#how-to-comply-with-the-rfe) mode, they are not generated.
> **Confidentiality (secret professionnel)** — The tax report never includes the detail of invoice lines (item description, service provided, etc.), only the totals and the TVA breakdown. This preserves professional secrecy in sectors such as healthcare, law and other regulated activities: the DGFiP receives enough information to validate the TVA without knowing the content of the service.
### Domestic B2B invoices (Flux 1)
[Section titled “Domestic B2B invoices (Flux 1)”](#domestic-b2b-invoices-flux-1)
A domestic tax report is generated for each invoice. B2Brouter deposits it in the PPF and, upon receiving the response, updates the status.
**Mixed invoices (goods + services)** — If an invoice combines goods and services lines, the domestic tax report also reflects it as a mixed transaction.
### B2C and cross-border (Flux 10)
[Section titled “B2C and cross-border (Flux 10)”](#b2c-and-cross-border-flux-10)
Unlike Flux 1, which is transmitted invoice by invoice, Flux 10 groups the tax reports of invoices to individuals (B2C) and with non-French recipients into a single submission (Ledger). The frequency at which these Ledgers are sent to the PPF depends on the **TVA Regime** configured when [activating the tax report](#12-activate-the-tax-report), in the e-Reporting France section within Account settings.
Likewise, depending on the type of invoices, several Ledgers can be generated:
| Ledger | What it groups |
| -------------------- | ------------------------------------------------------------------------------------- |
| **Sales Ledger** | Tax reports for invoices you issue to clients outside France or to individuals (B2C). |
| **Purchases Ledger** | Tax reports for invoices you receive from suppliers outside France. |
| **Payments Ledger** | Tax reports for payments you receive for the invoices you have issued. |
### Where to view tax reports
[Section titled “Where to view tax reports”](#where-to-view-tax-reports)
You can view the transmitted tax reports in the **Tax** section of the application's main menu, or access the tax report for a specific invoice directly from its **Invoice view** → **More options** → **View declaration**.
If the total amount when you review that section does not match what you expected, it does not necessarily mean something is missing from the declaration. There are three reasons that add up and almost always explain the difference:
1. **Domestic B2B invoices are not part of any Ledger** — they generate their own individual tax report (Flux 1) per invoice, independent of the e-reporting Ledgers (Flux 10). If you look for their amount inside a Ledger, you will not find it: check their individual Flux 1 report.
2. **Reports are separated by movement type** — sales and purchases are declared in separate Ledgers, and payments in a separate Ledger.
3. **Reports are also separated by period** — according to the frequency of your TVA Regime.
The amount you would add up manually is spread across several different reports, and part of your invoicing — the domestic part — should not even appear here.
### Tax report lifecycle
[Section titled “Tax report lifecycle”](#tax-report-lifecycle)
Each tax report goes through a series of statuses until the DGFiP accepts or rejects it. You can check its status both in the invoice view and in the **Tax** section of the main menu. If there is an error, the exact reason can only be found in the **Tax** section, within the corresponding tax report.
| Status | What it means |
| ------------ | ------------------------------------------------------------------------------------------------------------------ |
| `New` | Created and queued (domestic tax report, Flux 1) or accumulating in the Ledger (cross-border tax report, Flux 10). |
| `Sent` | B2Brouter has deposited it in the PPF. |
| `Received` | The PPF confirms receipt (CDV 500). |
| `Registered` | The DGFiP has accepted it. Final positive status. (CDV 300). |
| `Rejected` | The DGFiP has rejected it (CDV 301). |
| `Error` | Transmission or processing error. |
| `Cancelled` | Invoice cancelled after registration (domestic tax report, Flux 1 only). |
#### When and how the PPF confirms your tax report (CDV)
[Section titled “When and how the PPF confirms your tax report (CDV)”](#when-and-how-the-ppf-confirms-your-tax-report-cdv)
The PPF does not confirm your tax report in one go, but in two separate moments:
* **CDV 500 (*Recevable*)** — arrives **shortly after** submission and confirms that the PPF has technically received and validated the document. Corresponds to status `Received`.
* **CDV 300 / 301 (*Registered* / *Rejected*)** — the DGFiP issues these **in batch, once a day**: this is the moment when it decides whether to accept or reject the declaration. They can take up to 24 hours from submission.
#### Tax report with error
[Section titled “Tax report with error”](#tax-report-with-error)
To read the exact error reason, check the report in the **Tax** section of the main menu, or access it directly from the **Invoice view** → **More options** → **View declaration**.
Most errors originate from the source invoice. [Correct the invoice](#invoice-with-error) and a new tax report is generated automatically. If the message is not clear, contact [support](/en/user-guide/support/open-incident-report/).
***
## 8. Glossary
[Section titled “8. Glossary”](#8-glossary)
| Term | What it is |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **B2B** (Business to Business) | Invoicing between companies. In France, this is the circuit governed by the RFE. |
| **B2Bi** / **Bi2B** (Business to Business international) | B2B invoicing with a client or supplier outside France. Declared as a cross-border tax report (Flux 10), not Flux 1. |
| **B2C** (Business to Consumer) | Invoicing from a company to an individual. Not covered by the RFE, but included in e-reporting (Flux 10). |
| **B2G** (Business to Government) | Invoicing from a company to a public administration. In France managed through Chorus Pro, not the RFE. |
| **RFE** | Réforme de la Facturation Électronique — the French e-invoicing reform. |
| **DGFiP** | Direction Générale des Finances Publiques — the French tax authority. |
| **PA** (Plateforme Agréée) | The operator certified by the DGFiP that manages your e-invoicing and declarations. B2Brouter is a PA. A PA is also known by its former name, PDP. |
| **PDP** (Plateforme de Dématérialisation Partenaire) | The former name of the PA. Since 2025 the official term is Plateforme Agréée (PA); PDP and PA are used interchangeably in the sector. |
| **PPF** (Portail Public de Facturation) | The French State's public portal. It receives tax declarations and maintains the Annuaire, but does not route invoices between companies. |
| **Annuaire** | The PPF's central directory: indicates, for each French company, which PA delivers invoices to it and at which address. |
| **AIFE** | The French State agency that operates Chorus Pro and the PPF. |
| **Chorus Pro** | The circuit for invoicing French public administrations (B2G), independent of the RFE (B2B). |
| **Peppol** | The international network through which electronic invoices travel from one platform to another. |
| **Scheme** | The code that indicates what type of identifier is being used in Peppol (for example, `0225` for the French Peppol identifier, or `0002`/`0009` for SIREN/SIRET). Each country or identifier type has its own scheme. |
| **Routing address** | What providers call the Peppol identifier when they ask "what electronic address should I send the invoice to?". |
| **SML / SMP** | The central Peppol registries that record which platform has each identifier registered. |
| **SIREN** | 9-digit number that uniquely identifies a French company. |
| **TVA number** | The French tax identifier (TVA intracommunautaire): FR + 2 check digits + the SIREN. |
| **SIRET** | 14-digit number (the SIREN plus 5) that identifies a specific establishment of the company. |
| **Code Routage** | Code (scheme 0224) that routes the invoice to a specific department within a SIRET. |
| **Suffixe** | Code (scheme 8040) for a unit without its own establishment; attached directly to the SIREN. |
| **Kbis** | The French commercial register extract, used by companies as proof of existence. |
| **Organisational unit (OU)** | How B2Brouter models each establishment (SIRET) within the main account (SIREN). |
| **CIUS-FR / France UBL Invoice CIUS** | The standard French e-invoice format. |
| **UBL / CII** | The two XML formats in which an electronic invoice can be structured. |
| **Factur-X** | A PDF with the invoice XML embedded inside; a scanned or printed PDF is not a Factur-X. |
| **Ledger** | A batch of tax reports grouped within Flux 10. |
| **VATEX** | The standardised codes that indicate the reason why an invoice does not carry TVA. |
| **CDAR** | The message that communicates a change in invoice status (deposited, received, approved, rejected, paid). |
| **CDV** (Compte-Rendu de Validation) | The PPF's asynchronous response on a tax report. |
| **Flux 1** | The flow of an individual tax report for each domestic B2B invoice (between French companies). |
| **Flux 6** | The flow of invoice status changes. |
| **Flux 10** | The flow of an aggregated tax report (in Ledgers) for B2C and cross-border invoices. |
| **Flux 11** | The flow that queries the Annuaire to verify whether a contact is registered. |
# Frequently asked questions (DGFiP)
> Answers to the most common questions about e-invoicing in France and B2Brouter.
For more information, see the [full guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/) on e-invoicing and tax reporting in France.
## Activation and PA designation
[Section titled “Activation and PA designation”](#activation-and-pa-designation)
How do I designate B2Brouter as my Plateforme Agréée (PA)?
Go to ⚙ Account settings → select **e-Reporting France** in the side menu, review your identifier and attach the document proving your company’s identity. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#11-assign-b2brouter-as-your-pa).
Do I need to register in the Annuaire myself?
No. When you designate us as your PA, B2Brouter automatically registers you in the Annuaire and creates your Peppol identifier with scheme 0225. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#11-assign-b2brouter-as-your-pa).
What is the difference between the Annuaire and the INSEE directory?
They are different databases. `annuaire-entreprises.data.gouv.fr` is the public INSEE directory: your company appearing there does not mean it is already in the PPF Annuaire, since a newly created company can be in the INSEE without having propagated yet. The check that counts is done at `facturation.chorus-pro.gouv.fr/annuaire`. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#11-assign-b2brouter-as-your-pa).
How long does activation take?
Up to 24 hours on first activation. This is not a B2Brouter delay: it is the propagation speed of DGFiP’s infrastructure, and until it completes you will not be able to issue or receive DGFiP invoices. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#11-assign-b2brouter-as-your-pa).
Do I need to attach any document to activate?
Yes, a document proving the company’s identity — for example, the Kbis — is required to complete activation. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#11-assign-b2brouter-as-your-pa).
What happens to my previous PA when I activate B2Brouter?
The Annuaire entry is transferred to B2Brouter automatically, but Peppol registration is stricter: an identifier can only be active with one Peppol provider at a time, so it must be released before you can activate the Peppol network. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#13-change-pa).
What do I do if my identifier is already registered with another Peppol provider?
Ask your previous PA for a Peppol migration code — the recommended approach, with no service interruption — or ask them to unpublish your Peppol identifier directly. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#13-change-pa).
How do I leave B2Brouter to activate another PA?
In ⚙ Account settings → Peppol section, you can generate a migration code — recommended, with no service interruption — or unpublish your Peppol identifier directly. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#13-change-pa).
## Identifiers (SIREN, SIRET, Suffix, Code Routage)
[Section titled “Identifiers (SIREN, SIRET, Suffix, Code Routage)”](#identifiers-siren-siret-suffix-code-routage)
I have several offices — do I need one account per office?
No. Each company (SIREN) has a single main account, and each office (SIRET) is modelled as an organisational unit within that same account, not as an independent account. The same logic applies to your contacts: if a client invoices from several offices, do not create a separate contact for each — use the company’s SIREN and add its offices as organisational units of the contact. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#your-company-structure-in-b2brouter-main-account-and-organisational-units).
What identifiers does a French contact need?
Three: the company identifier (SIREN or SIRET), the TVA number, and the Peppol identifier. B2Brouter derives them automatically from each other based on whichever one you enter. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#2-create-a-french-contact).
What does the error "The customer's TVA number is required for the DGFiP declaration" mean?
This usually happens with contacts created before activating DGFiP tax reporting, which do not have the TVA number generated automatically. Edit the contact, add it and re-issue. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#automatic-generation-of-contact-identifiers).
Why did this invoice between two French companies not generate a tax report?
This is expected if the recipient is not active in the Annuaire: the declaration requires both parties to be registered. Check their status in the contact record. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#annuaire-verification).
## Tax reports
[Section titled “Tax reports”](#tax-reports)
Why can't I find the amount I expected in a tax report?
This does not mean something is missing from the declaration: domestic invoices (Flux 1) have their own individual report and are not part of any batch (Ledger), and reports are also separated by movement type and by period. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#where-to-view-tax-reports).
How often are tax reports sent?
It depends on the type of report: domestic invoices (Flux 1) generate their individual report at the moment the invoice is issued, while B2C and cross-border aggregate reports (Flux 10) are sent according to the **TVA Regime** you configure when activating the tax report. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#7-tax-reports).
## Issuing invoices
[Section titled “Issuing invoices”](#issuing-invoices)
Why is my 0% TVA invoice not reaching the DGFiP and there is no warning?
The VATEX code is missing from the comment on the exempt line. Without it, the invoice is created normally but is never sent to the DGFiP. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#tva-exemption--mandatory-code).
How do I issue a credit note?
It is created from the original invoice, following the general process for creating a credit note. In France, B2Brouter also generates it as a UBL CreditNote document (code 381), and the same three mandatory payment fields as on a regular invoice are required. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#5-credit-notes-rectifying-invoices).
## Invoice lifecycle and DGFiP responses
[Section titled “Invoice lifecycle and DGFiP responses”](#invoice-lifecycle-and-dgfip-responses)
My invoice looked fine and is now in error — is it a bug?
No. DGFiP responds in two phases: a quick technical acknowledgement, and a definitive decision processed in batch once a day that can take up to 24 hours. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#when-and-how-the-ppf-confirms-your-tax-report-cdv).
Can I resend an invoice in error?
Only if it has not yet been registered: delete it, create a new one with the error corrected and send it. If it was already registered, it can no longer be deleted and the correction must be done with a credit note. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#invoice-with-error).
## Receiving invoices
[Section titled “Receiving invoices”](#receiving-invoices)
How do I know I have received an invoice?
B2Brouter notifies you by email as soon as it receives one. You can also check them directly in the **Expenses** section. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#what-you-can-do-with-a-received-invoice).
What can I do with an invoice I have received?
You can approve it, reject it (stating the reason) or mark it as paid; B2Brouter automatically notifies the supplier and the DGFiP, with no further action required on your part. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#what-you-can-do-with-a-received-invoice).
## Public administrations
[Section titled “Public administrations”](#public-administrations)
What is the RFE (Réforme de la Facturation Électronique)?
It is the French regulation that progressively requires all companies to invoice each other in structured electronic format, and includes e-reporting: informing the DGFiP of your sales and receipts. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#the-rfe-and-the-plateforme-agr%C3%A9%C3%A9e-pa).
Is Chorus Pro the same as this?
No. They are two independent circuits: the RFE governs B2B invoicing between companies, while Chorus Pro is exclusively for invoicing public administrations (B2G). You can use both at the same time. [See guide](/en/user-guide/tax-report/dgfip-e-invoicing-france/#differences-between-the-rfe-and-chorus-pro-b2b--b2g).
# Manage KSeF electronic invoicing
> Learn how to activate KSeF, issue and receive Polish e-invoices, manage KSeF numbers, UPO receipts and invoice statuses.
**KSeF** (*Krajowy System e-Faktur*, "National e-Invoice System") is the mandatory platform of the Polish Ministry of Finance for B2B electronic invoicing. Polish VAT-registered companies must submit every invoice in **FA(3)** format to KSeF and receive an official identifier (*Numer KSeF*) before distributing it to the customer.
B2Brouter handles the full cycle: signing with the tax certificate, transforming to FA(3), submitting, receiving the KSeF number, and generating the QR code and the official receipt (UPO).
**Who is it for** — Companies with a Polish NIP (*Numer Identyfikacji Podatkowej*). **Non-Polish companies cannot activate KSeF** nor select it as the transport on a Polish contact — the invoice would not be sent.
**KSeF environments** — The Ministry publishes three: **Production**, **Demo** (real data without fiscal effects) and **Test** (open sandbox for integrations). B2Brouter's *production* instance talks to KSeF Production; the *staging* instance talks to KSeF Test or Demo depending on the certificate configuration.
[Are you a developer? ](/en/developers/guides-by-country/poland/ksef/)Go directly to the KSeF developer guide.
***
## 1 · Activate KSeF
[Section titled “1 · Activate KSeF”](#1--activate-ksef)
### Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* A B2Brouter account with country **Poland** and a correct **NIP** on the company profile.
* A **fiscal authentication certificate** issued in the name of your Polish company (see below).
* The certificate's **password (PIN)**.
### How to obtain the certificate
[Section titled “How to obtain the certificate”](#how-to-obtain-the-certificate)
**Production** — Access the official KSeF web portal of the Polish Ministry of Finance at [ap.ksef.mf.gov.pl/web](https://ap.ksef.mf.gov.pl/web) identifying yourself with your fiscal identity system (Profil Zaufany, qualified signature, or similar). Inside the portal, in the certificates/credentials section, generate an **authentication certificate** linked to your NIP and export it in **PKCS#12 (`.p12` or `.pfx`)** format with a password. The file you get has a name similar to `_Uwierzytelnianie.pfx` (*Uwierzytelnianie* = *Authentication*).
**Test (testing and integrations)** — The equivalent test web portal is [ap-test.ksef.mf.gov.pl/web](https://ap-test.ksef.mf.gov.pl/web). You can generate a **self-signed** certificate with OpenSSL; the subject must include the NIP in the field `serialNumber=TINPL-`. Before using it, register it manually on that same portal: without this step, KSeF Test will reject every signature from that certificate as unknown.
**Only PKCS#12 (`.p12` / `.pfx`) works** — A `.crt` file **is not valid**: it only contains the public part, not the private key, and KSeF needs both to authenticate the signature. If the portal gives you something else, re-export it explicitly choosing **PKCS#12** and ticking *"include the private key"*. Only as a last resort, if you have the `.crt` and the `.key` separately, you can combine them with OpenSSL:
> ```plaintext
> openssl pkcs12 -export \
> -in ksef_certificate.crt \
> -inkey ksef_private_key.key \
> -out ksef_certificate.p12 \
> -name " " \
> -passout pass:
> ```
**Encryption algorithm — 3DES or AES** — If you get the error *"Certificate password is invalid"* even though the password is correct, the file is encrypted with an old algorithm (**RC2-40bit**, typical of exports done from old Windows). KSeF and B2Brouter only accept **3DES** or **AES**. Re-export with OpenSSL:
> ```plaintext
> openssl pkcs12 -in original.p12 -out temp.pem -nodes -passin pass:
> openssl pkcs12 -export -in temp.pem -out compatible.p12 -passout pass:
> ```
### Activation steps
[Section titled “Activation steps”](#activation-steps)
1. ⚙ **Account settings** → **Taxes** → **KSeF**.
2. **Activate KSeF**.
3. Fill in the form:
* **Certificate** — upload the `.p12` or `.pfx` file.
* **PIN** — certificate password.
* **Environment** — *Production* by default. *Demo* or *Test* only if you are testing.
4. **Save**.
When you save, B2Brouter connects to KSeF, validates the certificate and checks that the NIP of the subject matches the account NIP (or that the company is authorised via **ZAW-FA**). If the validation succeeds, the certificate is stored and KSeF is active for your account.
**Once KSeF is active, the tax report is generated on its own** — You don't need to do anything else. Every invoice issued to a Polish contact via KSeF will automatically generate its tax report and will be submitted to KSeF as part of the same issuing flow. If you integrate via API, configure the `tax_report_setting` with `auto_generate: true` and `auto_send: true` so the flow is equivalent to the UI one.
**If the certificate subject does not match the account NIP** — You must submit the **ZAW-FA** form on the official portal to authorise your company to issue invoices with that certificate. Until ZAW-FA is approved, KSeF will reject invoices with error `450` or similar.
***
## 2 · Create a Polish contact
[Section titled “2 · Create a Polish contact”](#2--create-a-polish-contact)
Before issuing an invoice to a customer, the customer must exist in contacts.
1. **Contacts** → **New contact**.
2. Select the **customer's country**.
3. Enter the tax identifier — it depends on the customer's country (see table below).
4. **Validate identifier and continue** — for Polish customers, B2Brouter connects to the Ministry registry to verify the NIP and autocomplete the legal name.
5. Fill in the address (street, postal code, city).
6. Configure the **delivery method** and choose **KSeF** as transport. If KSeF does not appear, switch the network type to the one that shows KSeF in the list (in some accounts it is an open network that bundles several transports).
7. **Save**.
### Three identification structures depending on the customer's country
[Section titled “Three identification structures depending on the customer's country”](#three-identification-structures-depending-on-the-customers-country)
KSeF requires informing the customer using one of three mutually exclusive structures. If you mix formats (NIP with prefix, prefix without country, etc.) in the same account, you will get **intermittent errors** — some invoices pass and others are rejected.
| Customer type | Identifier to enter | How KSeF renders it in XML |
| -------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------- |
| **Polish** | NIP (exactly 10 digits, **without** the `PL` prefix) | `Podmiot2/DaneIdentyfikacyjne/NIP` |
| **Other EU country** | VAT number **without country prefix** (e.g. `123456789` for `DE123456789`) | `KodUE` (code) + `NrVatUE` (number, max. 12 characters) |
| **Outside the EU** | Country's tax identifier | `KodKraju` + `NrID` |
**Do not mix formats** — For the same customer, **always** send the same type of identifier. If you send them as Polish on one invoice (`PL` + NIP) and as EU on the next (`DE` + VAT), KSeF will apply different validation rules and the result will be inconsistent.
**Greece: fiscal code `EL`, not `GR`** — On the EU fiscal field (in KSeF, `KodUE`), Greece is identified with **EL**. For the address, `GR` is used. If the form lets you pick `GR` in the fiscal field, change it to `EL`; otherwise KSeF will reject the invoice with the error `KodUE: 'GR' is not an element of the set {...}`.
***
## 3 · Issue an invoice
[Section titled “3 · Issue an invoice”](#3--issue-an-invoice)
### General information
[Section titled “General information”](#general-information)
1. **Invoices** → **Create invoice**.
2. Choose the contact and **Continue**.
3. Fill in:
* **Invoice number** — unique sequential.
* **Invoice date** and **VAT date** (*Data sprzedaży*).
* **Currency** — `PLN` for domestic invoices. For foreign currencies, fill in the **exchange rate** to PLN on the invoice date: it will be sent to KSeF's `KursWalutyZ` field.
* **Notes** — free text that will appear as the invoice footer (`Stopka`) in the official PDF. Useful for legal clauses, contractual conditions, or any information that does not fit in another field.
### Invoice type (`RodzajFaktury`)
[Section titled “Invoice type (RodzajFaktury)”](#invoice-type-rodzajfaktury)
B2Brouter assigns the type according to the workflow, but you can change it on the KSeF section of the invoice, in the **Operation type** field:
* **VAT** — Ordinary invoice. *(default)*
* **KOR** — Corrective invoice. See [§ 5 · Corrective invoices](#5--corrective-invoices-kor).
* **ZAL** — Advance invoice (*prepayment*, Art. 106f sec. 4).
* **ROZ** — Final settlement invoice (Art. 106f sec. 3) — completes a previous ZAL.
* **UPR** — Simplified invoice (Art. 106e sec. 5 item 3) — direct sale ticket.
* **KOR\_ZAL** / **KOR\_ROZ** — Corrective advance and settlement invoices.
### VAT rate (`StawkaPodatku`)
[Section titled “VAT rate (StawkaPodatku)”](#vat-rate-stawkapodatku)
For each line, choose the VAT rate:
| Rate | Code | Use |
| ----------------------------------------- | ----------------------------------- | -------------------------------------------------------------- |
| 23 % / 22 % / 8 % / 7 % / 5 % / 4 % / 3 % | `23`, `22`, `8`, `7`, `5`, `4`, `3` | Domestic rates |
| 0 % domestic | `0 KR` | Domestic zero-rate (neither export nor intra-community) |
| 0 % WDT | `0 WDT` | Intra-community supply (*Wewnątrzwspólnotowa Dostawa Towarów*) |
| 0 % Export | `0 EX` | Export outside the EU |
| Exempt | `zw` | Exempt operation (use of a VAT Act exemption) |
| Domestic reverse charge | `oo` | *Odwrotne obciążenie* — PL reverse charge |
| Not subject to EU VAT | `np I` | Cross-border services not subject to tax (Art. 28b) |
**Exempt lines (`zw`) — legal basis in the Comment** — For lines with exempt VAT, state the legal basis (e.g. *"art. 43 ust. 1 pkt 37 ustawy VAT"*) in the line's **Comment** field. The PDF will show two paragraphs: a generic legal annotation (automatic) and the comment you wrote.
### Exports (`0 EX`)
[Section titled “Exports (0 EX)”](#exports-0-ex)
For an export outside the EU in foreign currency:
* **VAT rate**: `0 EX`.
* **Customer country**: the non-EU country.
* **Exchange rate**: the rate of the day prior to the VAT date, per the ECB or NBP tables.
**Do not use `zw` (exempt) for exports** — An export is `0 EX` (export zero-rate, P\_13\_6\_3). If you use `zw`, the amount is duplicated between the domestic zero-rate and exempt boxes, and KSeF rejects or distorts the declaration.
### Send
[Section titled “Send”](#send)
1. **Save and preview** — review amounts and the customer NIP.
2. **Issue and send** — B2Brouter signs the document with your certificate, deposits it to KSeF, and it remains pending registration.
***
## 4 · Invoice lifecycle
[Section titled “4 · Invoice lifecycle”](#4--invoice-lifecycle)
On the invoice detail page you will see the current status, the *Numer KSeF* once registered, and the UPO receipt.
```plaintext
Sending → Registered → (QR + Numer KSeF + UPO)
↘ Error
```
* **Sending** — B2Brouter has signed and submitted the document. Effective registration by KSeF can take a few minutes, especially at peak hours or month-end.
* **Registered** ✅ — KSeF has validated and assigned the **Numer KSeF** (`NrKSeF`). The QR code on the printed invoice now shows this number, and the official registration receipt becomes available (see below).
* **Error** ❌ — KSeF has rejected the document. The reason appears on the invoice page (code + description in Polish).
**The QR appears right away, but that does not mean KSeF has accepted the invoice** — B2Brouter generates the QR code **locally** from the cryptographic hash, the NIP and the date: it is visible immediately, even if the invoice will end up in error. **The only reliable indicator of acceptance is the `Registered` status**. If the status is `Error`, the invoice is **not** registered in KSeF, regardless of the QR or link generated. While the invoice is `Sending`, the QR carries the `OFFLINE` label.
### Official registration receipt (UPO)
[Section titled “Official registration receipt (UPO)”](#official-registration-receipt-upo)
**What it is.** The **UPO** (*Urzędowe Poświadczenie Odbioru*, "Official Confirmation of Receipt") is the receipt that KSeF issues when it accepts and registers the invoice. It works as an administrative acknowledgement: it is the legal proof, with fiscal value, that you have fulfilled the obligation of registering the invoice in KSeF at a specific moment.
Technically, it is an **XML document digitally signed by the Polish Ministry of Finance**. It contains the *Numer KSeF* (`NrKSeF`) assigned to the invoice, the exact **timestamp** of registration, and the Ministry signature. It is not a visual PDF — it is a machine-readable legal proof.
**Where it comes from.** KSeF generates it the moment it validates and accepts the invoice. It is not generated by B2Brouter or by you.
**How to confirm the registration from B2Brouter.** When the invoice moves to `Registered`, open the associated **KSeF tax report** (linked from the invoice page). On the **Activity** tab, you will see the successful submission event with the assigned *Numer KSeF* — confirmation that KSeF has received, validated and registered the document.
**How to obtain the UPO XML file for your accounting archive.** The original file is available through two channels:
* **From the official KSeF portal** — [ap.ksef.mf.gov.pl/web](https://ap.ksef.mf.gov.pl/web) (or [ap-test.ksef.mf.gov.pl/web](https://ap-test.ksef.mf.gov.pl/web) for the test environment). By logging in with your fiscal identity system, you can browse and download the UPOs of your account directly from the Ministry — they are the official original.
* **Via the B2Brouter API** — if you integrate with the API, the UPO is available as an attachment of the tax report. See the [KSeF Developer Guide](https://developer.b2brouter.net/docs/ksef).
**Keep the UPOs with the invoice** — They have legal value before a tax inspection. Download them from KSeF or via API and archive them together with the original invoice.
### Invoice with error
[Section titled “Invoice with error”](#invoice-with-error)
1. Open the invoice and read the error code and description.
2. Correct the problematic data (contact, NIP, amounts…).
3. **Resend** the invoice. B2Brouter retries the registration with KSeF using the corrected data.
**Exception — invoice number already seen by KSeF** — If the rejection comes from the fact that the invoice number has already been used in a previous submission, you cannot resubmit the same document: delete it, correct the data, and create a new invoice with a **different number**.
***
## 5 · Corrective invoices (KOR)
[Section titled “5 · Corrective invoices (KOR)”](#5--corrective-invoices-kor)
A corrective invoice (*faktura korygująca*) modifies a previous KSeF invoice. The document links the original (`DaneFaKorygowanej`) and restates the affected lines with an indication of the previous and new state.
### Steps
[Section titled “Steps”](#steps)
1. Open the original invoice already **registered** in KSeF.
2. **Create corrective invoice** — B2Brouter automatically fills in the KSeF number, the invoice number, and the date of the original.
3. Indicate the **Effect type** (`TypKorekty`):
* **1** — Effect on the date of the **original invoice**. Use: corrections of errors that existed when issued (wrong amount, wrong VAT rate).
* **2** — Effect on the date of the **corrective invoice**. Use: subsequent events (returns, post-sale discounts).
* **3** — Effect on a **different date** — state it explicitly.
4. Update the lines to the new state (quantities, prices, VAT rates).
5. If you want to add a textual correction reason (e.g. *"Korekta ilości — dostawa niepełna"*) or any associated legal text, write it in the invoice's **Notes** field: it is sent to the document's official footer (`Stopka/Informacje/StopkaFaktury`) and is visible on the KSeF PDF.
6. **Issue and send**.
**"Before / after" state** — The corrective invoice you issue from the form carries the lines with the new values directly; this is the typical case and is enough for most corrections. If you need the formal *before/after* representation (original cancelled lines plus new lines on the same document, with a `Stan przed` marker per line), this option is only available **via API**: see the [Developer Guide](https://developer.b2brouter.net/docs/ksef).
### Correcting an advance or settlement
[Section titled “Correcting an advance or settlement”](#correcting-an-advance-or-settlement)
To correct a **ZAL** (advance), use a **KOR\_ZAL**; to correct a **ROZ** (settlement), use a **KOR\_ROZ**. The procedure is identical to a normal KOR.
***
## 6 · Advances, settlements and tickets
[Section titled “6 · Advances, settlements and tickets”](#6--advances-settlements-and-tickets)
### Advance invoice (ZAL)
[Section titled “Advance invoice (ZAL)”](#advance-invoice-zal)
When you receive a prepayment before the delivery:
1. Create an invoice of type **ZAL**.
2. State the amount received and the **collection date** in the **Zapłacono** (*Paid*) field.
3. **Issue and send**.
### Final settlement invoice (ROZ)
[Section titled “Final settlement invoice (ROZ)”](#final-settlement-invoice-roz)
When you perform the delivery after one or more advances:
1. Create an invoice of type **ROZ**.
2. Link the advance(s) previously registered in KSeF.
3. B2Brouter calculates the difference between the total value and the advances and generates the final amount.
### Simplified ticket (UPR)
[Section titled “Simplified ticket (UPR)”](#simplified-ticket-upr)
For direct sales to individuals on the same day of issue and collection:
1. Create an invoice of type **UPR**.
2. The **customer NIP** is mandatory (it can be a generic NIP if the customer does not have one).
3. **Issue and send**.
***
## 7 · Receive invoices
[Section titled “7 · Receive invoices”](#7--receive-invoices)
Once KSeF is active on the account, your NIP is registered as a recipient and Polish suppliers can send invoices directly to you.
* **Expenses** → you will find all received KSeF invoices here.
* Open an invoice to see its detail.
**Where to find the Numer KSeF of a received invoice** — The official KSeF number (`NrKSeF`) does not appear on the PDF preview, but in the **Activity** section of the received invoice (format `XXXXXXXXXX-YYYYMMDD-XXXXXXXXXXXX-XX`). The QR code is only visible on the PDF if the supplier included it on their image.
**An invoice the supplier claims to have sent but does not appear in Expenses** — First verify that the PDF sent by the supplier carries a **KSeF QR code**. Without a QR, the invoice was not sent via KSeF — the supplier may not have completed the registration. If there is a QR, scan it and copy its URL: with that URL we can check whether the document actually exists in KSeF.
### Received invoice statuses
[Section titled “Received invoice statuses”](#received-invoice-statuses)
* **New** — received in B2Brouter, pending your review.
* **Read** — you have opened it at least once.
* **Approved** / **Refused** — the status you have assigned (no message is sent to the supplier; KSeF does not have an acceptance/rejection channel equivalent to Peppol's).
***
## 8 · Common errors
[Section titled “8 · Common errors”](#8--common-errors)
### Certificate activation
[Section titled “Certificate activation”](#certificate-activation)
* Certificate rejected despite the correct password — the PKCS#12 is encrypted with an old algorithm (RC2-40bit), typical of exports from old Windows. Re-export with 3DES or AES (see [§ 1](#1--activate-ksef)).
### Invoice submission
[Section titled “Invoice submission”](#invoice-submission)
* `450 - Nip nabywcy: '…' jest nieprawidłowy` — customer NIP invalid. Check 10 digits + check digit. For EU contacts, use the EU VAT number **without prefix** (see [§ 2](#2--create-a-polish-contact)).
* `KodUE: 'GR' is not an element of the set` — Greece on the EU fiscal field must be **EL**, not `GR`. Edit the contact and fix it.
* *"Contact Email can't be blank"* — the contact's transport is **email** but there is no address. Add an email or change the transport.
### Statuses
[Section titled “Statuses”](#statuses)
* Status `Error` but QR and URL were generated — normal. The QR is computed locally and does not depend on KSeF's acceptance. The `Error` status is authoritative: the invoice is **not** registered.
* An invoice the supplier claims to have sent but does not appear in **Expenses** — check that the supplier's PDF carries a KSeF QR code. Without QR, it was not actually registered.
### Transport
[Section titled “Transport”](#transport)
* Non-Polish account trying to use KSeF as transport — KSeF is only available for accounts with a Polish NIP. Use **Peppol** or **email** on that contact.
### PDF rendering
[Section titled “PDF rendering”](#pdf-rendering)
* VAT summary shows "23% lub 22%" — official Ministry template label, combining the historical (22 %) and current (23 %) rates in a single field. It is not an error.
* "Nazwa banku" (bank name) field empty — KSeF does not require the bank name and B2Brouter does not auto-populate it. If you need it, add it to the **Notes** field.
***
## 9 · API integration (for developers)
[Section titled “9 · API integration (for developers)”](#9--api-integration-for-developers)
This manual describes the UI. If you integrate with B2Brouter via API, the official reference is the **[KSeF Developer Guide](https://developer.b2brouter.net/docs/ksef)** — it covers the *Tax Report Settings*, *Tax Reports*, *Invoices* and *Contacts* endpoints, the full JSON-to-FA(3) mapping, the status tracking webhooks, and the UPO download.
Authentication via static **API key** (header `X-B2B-API-Key`) + API version (`X-B2B-API-Version`). KSeF-specific functionalities require version **`2025-10-13`** or later.
# Manage Immediate Supply of Information (SII)
> Learn how to import, send, consult and download SII records from B2Brouter to the Spanish Tax Agency (AEAT), manually or automatically.
[Are you a developer? ](/en/developers/guides-by-country/spain/sii/)Go directly to the SII developer guide.
With B2Brouter, you can send SII records to the Spanish Tax Agency (AEAT), complying with the required format and technical specifications.
You can upload information to B2Brouter using two types of files: in XML format, following the specifications of the AEAT, or in CSV format, following the specifications of B2Brouter.
Once you have uploaded the files with the data from the records, the document is sent from B2Brouter, as it is connected with the AEAT. The sending can be configured so that it occurs automatically once the document of records has been imported to B2Brouter, or it can be done manually, sending all the recently imported records en masse, by just pressing a button.
## Importing SII records to B2Brouter
[Section titled “Importing SII records to B2Brouter”](#importing-sii-records-to-b2brouter)
Once you have generated one of the two abovementioned files with the information from the records you want to send to the AEAT, you have to import it into your B2Brouter account. To do this you have two options:
### Automatic importing of the records
[Section titled “Automatic importing of the records”](#automatic-importing-of-the-records)
B2Brouter can be integrated with your management system. As a consequence, you can automatise the importing of the documents to your B2Brouter account to avoid having to do manual procedures. [Here you can see the different integration options](/en/developers/integration-without-code/b2bconnector).
### Manual importing of the records
[Section titled “Manual importing of the records”](#manual-importing-of-the-records)
You can also import the records manually from your B2Brouter account:
* [Login to your B2Brouter account](https://app.b2brouter.net/es/login/).
* Go to the **Taxes** section in the main top menu and then click on the **SII** option on the left side.
Once you are in the **SII** section, just below the main menu, the different log books that you have available to submit (**Invoices issued**, **Invoices received**, **Collections**, etc.) are activated:
* Access the log book from which you want to import the data.
* Use the option **New** and then **Import**.
* On the next page, use the **Select File** option to search your system for the document you want to import.
* Once selected, click on **Import**.
If the file has been generated correctly, the records will be uploaded and will appear with the status **New** on the list of records.
## Send the records to AEAT
[Section titled “Send the records to AEAT”](#send-the-records-to-aeat)
Once you have imported the document with the records, you have two ways to send it:
### Automatic sending of the records
[Section titled “Automatic sending of the records”](#automatic-sending-of-the-records)
If you have configured the automatic sending of records, they will be sent to the AEAT just after being imported, without you having to do anything else.
To configure automatic sending:
* Use the gear option in the top of the screen and access to your **Account settings**.
* Access the section **SII records**.
* Click on the option **Present imported records automatically**.
Finally, save the changes by clicking on **Save**.
### Manual sending of the records
[Section titled “Manual sending of the records”](#manual-sending-of-the-records)
If you prefer to consult the records imported to B2Brouter before sending them, this is also possible. This is the default sending method, so you do not need to configure anything. You simply have to send the document once the records have been imported:
* Once you have imported the records to B2Brouter, they will appear with the status **New**.
* At the top right of the list the option **Send new records** will be activated.
* Click on the option **Send new records** and all the imported records that appear with the status **New** will be sent.
## Consult SII documents
[Section titled “Consult SII documents”](#consult-sii-documents)
To consult and manage the records filed with the Tax Agency, access the **Taxes** section in the main menu and click on **SII**. Then, a submenu is activated at the bottom, where you must access the log book you wish to consult.
### Filter the documents
[Section titled “Filter the documents”](#filter-the-documents)
You can filter the documents using various parameters to obtain only the data you wish to consult:
* Open the **Filters** option at the top left of the record list.
* Use the different parameters to filter: date, status, etc.
* Finally, use the **Apply** option.
The filter is applied and only the records that meet the filtered parameters appear in the list.
### Consult the documents
[Section titled “Consult the documents”](#consult-the-documents)
Once you have filtered the documents, you can open and consult each document separately by clicking on the number on the left side of the list.
If the list is very long, the records are spread over different pages. You can navigate between them via the bottom right-hand side.
## Download SII record reports
[Section titled “Download SII record reports”](#download-sii-record-reports)
Once you have imported the records to B2Brouter, you can download different types of reports that allow you to keep track of the imported documents — presented or rejected — and compare them with the information in your system or in the AEAT.
First, use the **Filter** option to select only the records from which you wish to download the report (see [Filter the documents](#filter-the-documents) above).
Once you have filtered the records, at the top of the record list you will find the **Export** option, where you can select the report you wish to generate:
### CSV file
[Section titled “CSV file”](#csv-file)
A CSV file is downloaded with data from the filtered records. This report includes general information such as period, sender, receiver, etc., as well as the status of each record, verification code, etc.
### Report
[Section titled “Report”](#report)
A file in CSV format is downloaded with all the information contained in each of the filtered records. In this case, there is no data on the current status or verification code of the record.
### Grouped report
[Section titled “Grouped report”](#grouped-report)
A file is downloaded in CSV format with grouped information of all filtered records, including the total of all amounts and taxes of the selected records.
### Detailed report
[Section titled “Detailed report”](#detailed-report)
A file in CSV format is downloaded with all the information contained in each of the filtered records. The difference with the normal report is that this one also includes the updated status of each record.
Select the report you want to generate and the document with the data is downloaded to your system.
# Manage TicketBAI tax reports
> Learn how to activate TicketBAI, submit invoice declarations, manage statuses and download QR codes in B2Brouter.
TicketBAI is a joint project by the Basque Government and the Provincial Tax Authorities that aims to establish legal and technical obligations for the invoicing software used by taxpayers. This system allows the tax administration to more effectively monitor income derived from economic activities, especially in sectors where goods or services are sold directly to consumers and payment is often made in cash.
[Are you a developer? ](/en/developers/guides-by-country/spain/ticketbai/)Go directly to the TicketBAI developer guide.
## How does TicketBAI work?
[Section titled “How does TicketBAI work?”](#how-does-ticketbai-work)
TicketBAI involves the immediate and real-time submission of invoice data to the tax administration as soon as the invoice is generated. This ensures that no changes can be made after the invoice is issued, guaranteeing its security. Additionally, the invoice includes a QR code that allows the recipient to verify its fiscal compliance through the tax administration's platform.
## How to activate TicketBAI in B2Brouter
[Section titled “How to activate TicketBAI in B2Brouter”](#how-to-activate-ticketbai-in-b2brouter)
To activate TicketBAI, you must have an active Premium Plan (Professional, Business or eDocExchange).
* Click on the gear icon at the top of the screen and select **Account settings.**
* Go to the **Taxes** section in the side menu and select **TicketBAI**.
* Carefully read the section **Important information before activating TicketBAI**.
* Click the **Activate TicketBAI** button.
* A pop-up window will appear where you can review the default TicketBAI configuration and modify it if necessary. If needed, you can adjust the settings when creating an invoice, but those changes will only apply to that specific invoice and will not affect the default configuration.
* Once the default configuration is updated, click the "next" button. A confirmation message will appear.
* Click **Activate TicketBAI** to complete the activation.
* Once activated, a box will display the date and time of TicketBAI activation.
## Important information before activating TicketBAI
[Section titled “Important information before activating TicketBAI”](#important-information-before-activating-ticketbai)
### TicketBAI cannot be deactivated
[Section titled “TicketBAI cannot be deactivated”](#ticketbai-cannot-be-deactivated)
Once you activate TicketBAI, you commit to the corresponding Provincial Tax Authority to submit a declaration for every invoice you generate.
### Start date
[Section titled “Start date”](#start-date)
The date you activate TicketBAI will be the official start date of your commitment to the corresponding Provincial Tax Authority to declare all invoices issued from that moment onward. This date cannot be changed.
### Stopping the use of B2Brouter
[Section titled “Stopping the use of B2Brouter”](#stopping-the-use-of-b2brouter)
If you decide to stop using B2Brouter after activating TicketBAI, you will need to have another TicketBAI-compliant invoicing system. Once the commitment with the Tax Authority has been made, it is no longer possible to generate invoices manually, as all invoices must be submitted using a TicketBAI-compliant invoicing system.
## Create the invoice and submit the declaration
[Section titled “Create the invoice and submit the declaration”](#create-the-invoice-and-submit-the-declaration)
Once TicketBAI is configured, you can start [creating invoices](/en/user-guide/sending-documents/create-electronic-invoice) and then [sending them](/en/user-guide/sending-documents/create-electronic-invoice) along with their corresponding declarations.
The TicketBAI declaration is automatically generated with the information from the invoice and is sent to the corresponding Provincial Tax Authority when you send the invoice.
Invoices are sent to your clients with the integrated QR code. You can see it at the bottom of the open invoice, where you'll also find the link to the record generated on the Provincial Tax Authority's portal. Using the link, you can view all the information registered on the portal.
### Access the declarations
[Section titled “Access the declarations”](#access-the-declarations)
To view a submitted declaration, open the sent invoice. At the top, you'll find the "View declaration" option. Click it to access the declaration information.
You can also access all submitted declarations:
* From the top menu, go to the **Taxes** section.
* On the following page, you'll see the submitted declarations. You can open a document by clicking on its declaration number.
## Manage TicketBAI through integration with your system and B2Brouter
[Section titled “Manage TicketBAI through integration with your system and B2Brouter”](#manage-ticketbai-through-integration-with-your-system-and-b2brouter)
To manage TicketBAI through integration, keep the following in mind:
* You must have an active [eDocExchange Plan](https://www.b2brouter.net/global/prices/).
* Your management system must be able to generate the XML file with the required registration information.
If you meet these requirements, you can connect your management system with B2Brouter to import the required information. B2Brouter will handle invoice chaining, add the digital fingerprint (if needed), digitally sign the document (if needed), and send it directly to the corresponding Provincial Tax Authority.
Follow these steps to manage TicketBAI:
### Configure integration with B2Brouter
[Section titled “Configure integration with B2Brouter”](#configure-integration-with-b2brouter)
[Log in to your B2Brouter account](https://app.b2brouter.net/login) and click the lightning bolt icon (integration).
* To use the TicketBAI service, you can integrate your system with B2Brouter using one of three connection types: B2Bconnector, SFTP, or API.
[See how to configure the B2Bconnector connection](/en/developers/integration-without-code/b2bconnector). [See how to configure the SFTP connection](/en/developers/integration-without-code/sftp). [See how to configure the API connection](/en/user-guide/connect/configuring-api).
### Activate tax declaration
[Section titled “Activate tax declaration”](#activate-tax-declaration)
Follow the steps as explained [here]()
### Import the file
[Section titled “Import the file”](#import-the-file)
Once you generate the file in your system, import it into B2Brouter using the configured connection type.
In all cases, you must upload the TicketBAI file in XML format to the TicketBAI folder:
* The file will be automatically imported into B2Brouter.
* You can import multiple files at once.
### Submit the declaration
[Section titled “Submit the declaration”](#submit-the-declaration)
Once the file is imported, you’ll find it in the **Invoices** section of the main menu and in **Declarations** in the submenu at the bottom of the main menu:
* This section lists all imported documents.
* You can open a document by clicking its number.
* The submission to the tax authority is done right after the import.
### Manage statuses
[Section titled “Manage statuses”](#manage-statuses)
You can check the status of records on the right side of the list or at the top of an open record.
* If the imported file is correct and successfully sent, it will appear as **Sent**.
* If the submission fails, it will appear as **Error**.
### Download the QR code
[Section titled “Download the QR code”](#download-the-qr-code)
If the record was created successfully, you only need to download the registration QR code to insert it into the invoice you need to send.
* Open the record.
* At the top, you'll find the QR code.
* Right-click on the QR code and save it to your system.
* You can also use the link above the QR code to verify the registration directly on the official records website.
# Activate and manage VeriFactu
> Learn how to activate VeriFactu, report invoices to the AEAT, manage credit notes and understand key tax compliance rules.
VeriFactu is a compliance system designed to simplify the adaptation process to the invoicing requirements established by the Anti-Fraud Law. It ensures that all invoices generated by companies are securely transmitted to the Tax Authority (AEAT) to be verified for integrity and authenticity by both the AEAT and the customer.
[Are you a developer? ](/en/developers/guides-by-country/spain/verifactu/)Go directly to the VeriFactu developer guide.
## How does VeriFactu work?
[Section titled “How does VeriFactu work?”](#how-does-verifactu-work)
VeriFactu involves the immediate and real-time submission of invoice data to the AEAT at the moment it is generated. This process ensures that no modifications can be made once the invoice has been issued, thus guaranteeing its integrity and security. Additionally, the invoice will include a QR code that allows the recipient to verify its fiscal validity through the AEAT platform.
## How to activate VeriFactu in B2Brouter
[Section titled “How to activate VeriFactu in B2Brouter”](#how-to-activate-verifactu-in-b2brouter)
To activate the VeriFactu service, you need an active subscription to a Premium Plan (Professional, Business or eDocExchange). However, with a Basic Plan you can also activate it, although with a limited number of tax submissions.

* Click the gear icon located at the top of the screen and select **Account settings**.
* Go to the **Taxes** section in the side menu and open **VeriFactu**.
* Select the option **I want to activate VeriFactu**.
* Carefully read the section **Important information before activating VeriFactu**.
* Check the box **I authorize B2Brouter as an AEAT Social Collaborator**. Once checked, the **Activate VeriFactu** button will be enabled.
* Click the **Activate VeriFactu** button.
* Once activated, a box will appear showing the date and time when VeriFactu activation started.
## When can and can't I activate it?
[Section titled “When can and can't I activate it?”](#when-can-and-cant-i-activate-it)
You can activate VeriFactu when your company is ready to report all issued invoices to the AEAT from the activation date onward. Before confirming activation, review these points carefully:
### Subscription plan
[Section titled “Subscription plan”](#subscription-plan)
With a Premium Plan, you can use VeriFactu as part of your regular subscribed service. With a Basic Plan, you can also activate it, but with a limited number of tax submissions. If you expect a recurring submission volume, it is best to confirm first whether your current plan is sufficient.
### Company postal code
[Section titled “Company postal code”](#company-postal-code)
If your company postal code is invalid, the interface blocks VeriFactu activation. Before trying again, review your company tax details and correct the postal code if needed.
### When it is better not to activate it yet
[Section titled “When it is better not to activate it yet”](#when-it-is-better-not-to-activate-it-yet)
Do not activate it if you are still testing with real invoices or if you are not yet sure from which date you will start reporting all invoices through this system. Once activated, the commitment with the AEAT is irreversible and affects all invoices issued from the start date.
## Important information before activating VeriFactu
[Section titled “Important information before activating VeriFactu”](#important-information-before-activating-verifactu)
### VeriFactu cannot be deactivated
[Section titled “VeriFactu cannot be deactivated”](#verifactu-cannot-be-deactivated)
Once you activate VeriFactu, you commit to the AEAT to submit the declaration for every invoice you generate. This commitment is irreversible.
### Start date
[Section titled “Start date”](#start-date)
The date you activate VeriFactu will be the official start date of your commitment to the AEAT to declare all invoices issued from that moment onward. This date cannot be changed.
### Stopping the use of B2Brouter
[Section titled “Stopping the use of B2Brouter”](#stopping-the-use-of-b2brouter)
If you decide to stop using B2Brouter after activating VeriFactu, you will need to have another VeriFactu-compliant invoicing system. Once you have made the commitment with the AEAT, it is no longer possible to generate invoices manually, as all invoices must be submitted through a VeriFactu-compliant invoicing system.
## Credit notes with VeriFactu enabled
[Section titled “Credit notes with VeriFactu enabled”](#credit-notes-with-verifactu-enabled)
When VeriFactu is enabled, an issued invoice must not be edited. If you need to correct any data, you must always create a credit note, even if the recipient rejected the original invoice.
This is the key rule: with VeriFactu enabled, always rectify and never edit the original invoice after issuance.
To do it correctly:
* Open the original invoice and create a [credit note](/en/user-guide/sending-documents/create-credit-note).
* Use the credit note to correct the necessary data.
* Send the new invoice so the correction is properly recorded within the VeriFactu flow.
If the recipient rejects an invoice because of an administrative or commercial error, that rejection does not make the original invoice editable again. A credit note is still required to preserve fiscal traceability.
## Comparison with TicketBAI, SII and FACe
[Section titled “Comparison with TicketBAI, SII and FACe”](#comparison-with-ticketbai-sii-and-face)
| System | What it is | When it is used | Relationship with the invoice |
| --------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| VeriFactu | A tax reporting system linked to the Anti-Fraud Law and the AEAT | When you must report invoice information to the AEAT at the time of issuance | The invoice becomes part of a verifiable flow and should not be edited after issuance |
| TicketBAI | A tax system managed by the Basque Provincial Tax Authorities | When the taxpayer's tax obligation falls under TicketBAI | It follows a similar traceability and tax reporting logic, but with different authorities and requirements |
| SII | The Immediate Supply of Information system for VAT records sent to the AEAT | When you must submit VAT registry records through SII | It is not the same flow as VeriFactu: it works with tax records, not with the irreversible activation of a verifiable invoicing system |
| FACe | A general entry point for electronic invoices sent to public administrations | When you send an invoice to a public administration that receives through FACe | It is a delivery channel, not a tax regime like VeriFactu |
In some cases, a company may work with more than one of these environments, but each one addresses a different need: tax compliance, record submission, or invoice delivery to the correct recipient.
## Common errors and related help
[Section titled “Common errors and related help”](#common-errors-and-related-help)
If you need help with error messages or activation and submission blocks, also see the [VeriFactu error codes guide](https://www.agenciatributaria.es/static_files/AEAT_Desarrolladores/EEDD/IVA/VERI-FACTU/Validaciones_Errores_Veri-Factu.pdf). That guide gathers the most common errors and how to interpret them.
> #### VeriFactu Compliance
>
> [Section titled “VeriFactu Compliance”](#verifactu-compliance)
>
> B2Brouter Global S.L. has submitted the Responsible Declaration to the Tax Agency, in accordance with the VeriFactu system, in compliance with Royal Decree 1007/2023.
>
> Our invoicing software is adapted to the technical and legal requirements established by Spanish regulations regarding the issuance of verifiable invoices.
# Create SEPA direct debit files
> Learn how B2Brouter creates SEPA direct debit files, manages file statuses and stores SEPA mandates for invoice collection.
B2Brouter automatically creates SEPA files for the invoices you have issued with the **Debit payment** as the payment method.
The SEPA file is a payment system that allows direct debit of invoices. As a result, the bank can manage the invoice collection transaction from your customer’s account to yours.
## When is the SEPA file generated?
[Section titled “When is the SEPA file generated?”](#when-is-the-sepa-file-generated)
B2Brouter generates the SEPA file with the issued invoices that meet the following requirements:
* The invoices in question have the same due date.
* The payment method selected on the invoices is **Debit payment**.
* The status of the invoices is **Sent**, **Read**, **Accepted**, **Registered** or **Reported as paid**.
If the invoices do not meet these requirements, the SEPA file does not include them.
## Consult your SEPA files
[Section titled “Consult your SEPA files”](#consult-your-sepa-files)
Go to the **Banks** tab on the top menu, and use the **SEPA files** button on the left-hand side menu. Here you can consult all the files that have been generated.
## File statuses
[Section titled “File statuses”](#file-statuses)
Files can have different statuses:
* **New**: When the SEPA file is still open, invoices with the same due date are added.
* **Downloaded**: When the SEPA file is downloaded, this status appears automatically.
* **Collected**: When the invoices included in the SEPA file have been collected.
* **Partially collected**: When an invoice included in the SEPA file has been rejected, this status appears automatically.
## What happens when you mark a SEPA file as collected?
[Section titled “What happens when you mark a SEPA file as collected?”](#what-happens-when-you-mark-a-sepa-file-as-collected)
All invoices included in the SEPA file are automatically marked as **Collected**. At the same time, collections are automatically generated in the **Banks** section.
## How to add invoice information to an already downloaded SEPA file?
[Section titled “How to add invoice information to an already downloaded SEPA file?”](#how-to-add-invoice-information-to-an-already-downloaded-sepa-file)
When you download the SEPA file, it no longer includes invoices issued after the download.
In case you want to add invoice data to a downloaded SEPA file you must:
* **Delete** the SEPA file via the trash icon on the right-hand side.
* The SEPA file is automatically recreated, including all invoices with the same due date.
## SEPA mandates
[Section titled “SEPA mandates”](#sepa-mandates)
With B2Brouter you can manage SEPA mandates for collection management, uploading and storing the mandates signed by your customers. To do this:
* Go to the **Banks** tab in the top menu.
* From the menu on the left-hand side, use the **Mandates** button.
From this section you can add the mandates manually.
* Use the **Add mandate** button.
* In the form, add the necessary information, such as now the customer, the type of mandate, the type of collection and the date.
* Use the **Select file** option to upload the signed document.
* Click **Save** to generate the mandate.
Once you have added the mandate, you can preview it from the **Mandates** section. You can view, edit, delete and download it whenever you need to.
# Payments and collections
> Learn how payments and collections help you track collected and paid invoices in B2Brouter, and how to create and assign them to invoices.
With the **Collections and payments** functionality you can keep an exhaustive control of the invoices collected and paid. To do this, you have to access the **Banks** section in the main menu and then **Collections** or **Payments** through the options on the left side. From this section, you can assign payments or collections, partial or total, to the invoices you have issued or received.
From this same section, you can also use other related options such as [generate SEPA direct debits](/en/user-guide/tools-features/banks/create-sepa-direct-debit).
Note that the management of collections and payments is not automatic. As B2Brouter cannot know if you have actually received a collection in your account or have made a payment, this management will have to be manual.
## Create payments and collections
[Section titled “Create payments and collections”](#create-payments-and-collections)
B2Bouter allows you to manage the collections and payments of the invoices issued or received in your account.
To manage the collections or payments, go to the **Banks** section that you find in the top main menu:
Then, a submenu is activated on the left side, where you have to select the **Collections** or **Payments** option, depending on whether you want to assign a collection to an outgoing invoice or a payment to an incoming invoice. The procedure is the same in both cases:
* Select the **Add Collection** or **Add Payment** option (depending on the space you are in).
* In **Date**: select the date of the invoice to which you want to assign the collection or payment.
* In **Customer** or **Supplier**: select the customer or supplier of the invoice to which you want to assign the collection or payment.
* In **Invoice**: select the invoice to which you want to assign the collection or payment.
* In **Amount collected** or **Amount paid**: indicate the amount you have collected or paid. This can be total or partial.
Finally, use the **Save** option to generate the collection or payment. If you have allocated the total amount of the invoice, the system marks the invoice as closed and considers it as paid or collected.
## Create a collection or payment from the invoice
[Section titled “Create a collection or payment from the invoice”](#create-a-collection-or-payment-from-the-invoice)
You can also create a collection or payment directly from the invoice. To do this, access the **Invoices** or **Expenses** section through the main menu at the top. Then, a submenu is activated on the left side, where you have to select the option **Invoices issued** or **Invoices received**:
* Open the invoice by clicking on the number.
* Use the **More options** option at the top of the invoice.
* Select **Collection** or **Payment**, depending on whether it is an invoice issued or received.
Finally, follow the first steps above.
# Create a product and service catalogue
> Learn how to create or import a product and service catalogue in B2Brouter and use catalogue items in invoice and quote lines.
You can create a catalogue with a list of all the products or services your company offers. Once created, you can add the different items in the invoice lines, quickly, easily and without errors.
## Create the product catalogue
[Section titled “Create the product catalogue”](#create-the-product-catalogue)
To create the catalogue go to the **Income** section in the top menu. Then, select the **Catalogue** option on the left menu. You can create your catalogue in two ways:
### Create the catalogue manually
[Section titled “Create the catalogue manually”](#create-the-catalogue-manually)
To create your catalogue manually, select the **Add catalogue item** option. On the next page, fill in the form with the requested information:
* **ID**: Assign an identifier to the product or service.
* **Language**: Use the drop-down menu to select the language in which you want to enter the content. Generate the item in the different languages and the information of each product or service will be displayed in the language assigned to each Contact.
* **Name**: Indicates a description of the product or service. This information is used as a line description when you create the invoice or quotation lines using the catalogue items.
* **Description**: Enter descriptive information about the product. This description is only shown on the invoice if you check the **Show description** option.
#### Default price
[Section titled “Default price”](#default-price)
You can define the unit type and default price of the product or service in this section.
* **Units**: Use the dropdown to assign a unit to the product or service (Hours, Meters, Liters, etc).
* **Unit price**: Write the unit price of the product and choose the currency in this section.
#### Price per country
[Section titled “Price per country”](#price-per-country)
Once the default price is defined, you can add a specific price per country. Please note that you can only define one price per country.
#### Price per client
[Section titled “Price per client”](#price-per-client)
Once the default price is defined, you can add a specific price per client. Please note that you can only define one price per customer.
Finally, use the **Save** option to create the item in your catalogue. Repeat this action to add as many items as you want in your catalogue. Once the catalogue has been created, you can easily \[add the items to the invoice lines] (en/sending-documents/create-electronic-invoice).
### Upload your catalogue to B2Brouter
[Section titled “Upload your catalogue to B2Brouter”](#upload-your-catalogue-to-b2brouter)
You can upload in bulk all the items in your catalogue in a .CSV file. To do this, use the **Import file** option.
On the next page, use the **Select file** option to search for the .CSV file in your system, with all the items you want to add to your B2Brouter catalogue. Once the file is selected, use the **Import** option. In this way all the items contained in the .csv file are uploaded to B2Brouter and added to your catalogue.
* Check the option **Overwrite duplicate items** if you want to upload in your catalogue modified items that have been previously imported.
* Use the link **Download CSV file example** to download an example of the CSV file that you have to generate, in order to import the items to your B2Brouter catalogue.
# Generate reports
> Learn how to generate on-screen reports in B2Brouter, including payment and collection reports and the Model 303 VAT report.
B2Brouter allows you to generate different reports from the **Reports** section in the main menu. These reports are shown on screen and cannot be downloaded.
## Payment and collection reports
[Section titled “Payment and collection reports”](#payment-and-collection-reports)
B2Brouter allows you to generate a report of the payments and collections you have generated. To do this, access the **Reports** option through the main menu and then:
* Select the type of report you are interested in (invoices issued, invoices received, payments, etc).
* Use the dates to select the period for which you want to generate the report.
* Use the **Apply** option.
The report generated during the selected period will be displayed on the screen.
## Model 303 report
[Section titled “Model 303 report”](#model-303-report)
If you manage the Immediate Supply of Information records, you also have the option of generating a Form 303 report with the data from the SII records, to compare it with the information you obtain from your system or that you have registered directly with the Tax Agency.
This report is a test version (BETA) and may contain errors. The information is compiled only with data from invoices sent and received via B2Brouter.
To generate the report it is important that you manage all the SII records from B2Brouter. If this is the case, access the **Reports** section through the top main menu and then **Model 303** through the options on the left side. Next:
* Select the year and period.
* Select the option **Apply**.
You get a report of the Model 303 of the selected period. This report is not downloadable and can only be consulted on screen.
# Overview
> Every API version, what changed, and how to migrate — all in one place.
B2Brouter uses date-based versioning. Pin a version in every request with `X-B2B-API-Version` and upgrade at your own pace — changes never apply automatically.
## Key changes by version
[Section titled “Key changes by version”](#key-changes-by-version)
***
#### v2026-06-26 *(Latest)*
[Section titled “v2026-06-26 (Latest)”](#v2026-06-26-latest)
[Changelog ](/en/developers/changelog/v2026-06-26/)
[Migration guide ](/en/developers/upgrade-api/from-v2026-04-20-to-v2026-06-26/)
| Change | Area | Type | |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------- | -------- |
| [Independent CIN for contact offices.](/en/developers/changelog/v2026-06-26/#independent-cin-for-contact-offices) | Contacts | Changed | Breaking |
| [French Annuaire lookup on `/directory/fr`.](/en/developers/changelog/v2026-06-26/#french-annuaire-lookup-on-directoryfr) | Directory | Changed | Breaking |
| [Invoice and contact `routing_codes` no longer accept flat `cinN_value` keys.](/en/developers/changelog/v2026-06-26/#invoice-level-routing_codes-on-invoice-creation) | Invoices | Changed | Breaking |
| [New received invoice webhook events.](/en/developers/changelog/v2026-06-26/#received-invoice-webhook-events) | Webhooks | Added | |
| [Create offices via `POST /accounts` with `parent_id`.](/en/developers/changelog/v2026-06-26/#create-offices-via-post-accounts-with-parent_id) | Accounts | Added | |
***
#### v2026-04-20
[Section titled “v2026-04-20”](#v2026-04-20)
[Changelog ](/en/developers/changelog/v2026-04-20/)
[Migration guide ](/en/developers/upgrade-api/from-v2026-03-02-to-v2026-04-20/)
| Change | Area | Type | |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ------- | -------- |
| [Peppol transport: new `sml_status` and `peppol_directory_status` fields.](/en/developers/changelog/v2026-04-20/#peppol-transport-changes) | Peppol transport | Changed | Breaking |
| [Validate `tin_value` and `tin_scheme` against parent contact.](/en/developers/changelog/v2026-04-20/#validate-tin_value-and-tin_scheme-against-parent-contact) | Contacts | Changed | Breaking |
| [Credit notes now use `invoice_references` instead of amend fields.](/en/developers/changelog/v2026-04-20/#replace-amend-fields-with-invoice_references) | Invoices | Changed | Breaking |
| [`payment_method_info` removed from invoice responses.](/en/developers/changelog/v2026-04-20/#remove-payment_method_info-from-invoice-response) | Invoices | Removed | Breaking |
| [New TIN verification endpoint — verify tax IDs against AEAT asynchronously.](/en/developers/changelog/v2026-04-20/#tin-verification-endpoint) | Verification | Added | |
| [New `routing_codes` field on accounts for additional identifiers.](/en/developers/changelog/v2026-04-20/#add-additional-identifiers-to-accounts-routing_codes) | Accounts | Added | |
***
#### v2026-03-02
[Section titled “v2026-03-02”](#v2026-03-02)
[Changelog ](/en/developers/changelog/v2026-03-02/)
[Migration guide ](/en/developers/upgrade-api/from-v2025-10-13-to-v2026-03-02/)
| Change | Area | Type | |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ------- | -------- |
| [Scheme fields (`tin_scheme`, `cin_scheme`) changed from number to zero-padded string — e.g. `7` → `"0007"`.](/en/developers/changelog/v2026-03-02/#changed) | Contacts, Invoices | Changed | Breaking |
| [`type_document` renamed to `type_code` across all formats.](/en/developers/changelog/v2026-03-02/#changed) | Invoices | Changed | Breaking |
| [`POST` endpoints now return `201 Created` instead of `200 OK`.](/en/developers/changelog/v2026-03-02/#changed) | API | Changed | Breaking |
| [`correction_method` now validates against an enum of allowed values.](/en/developers/changelog/v2026-03-02/#changed) | Invoices | Changed | Breaking |
| [Deprecated `discount_amount` and `charge_amount`fields removed.](/en/developers/changelog/v2026-03-02/#removed) | Invoices | Removed | Breaking |
| [New `X-B2B-API-Request-Id` response header for end-to-end request tracing.](/en/developers/changelog/v2026-03-02/#added) | API | Added | |
***
#### v2025-10-13
[Section titled “v2025-10-13”](#v2025-10-13)
[Changelog ](/en/developers/changelog/v2025-10-13/)
[Migration guide ](/en/developers/upgrade-api/from-v2025-01-01-to-v2025-10-13/)
| Change | Area | Type | |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ------- | -------- |
| [New domain `api.b2brouter.net` — old `app.b2brouter.net` no longer valid.](/en/developers/changelog/v2025-10-13/#changed) | Infrastructure | Changed | Breaking |
| [`/projects` renamed to `/accounts`, `/clients` renamed to `/contacts`.](/en/developers/changelog/v2025-10-13/#changed) | Resources | Changed | Breaking |
| [Tax codes split into `tin_scheme` and `tin_value` structured fields.](/en/developers/changelog/v2025-10-13/#changed) | Contacts | Changed | Breaking |
| [All API responses now default to English.](/en/developers/changelog/v2025-10-13/#changed) | API | Changed | Breaking |
| [PUT, DELETE, and POST operations now return complete resource representations instead of `204 No Content`](/en/developers/changelog/v2025-10-13/#response-improvements) | API | Changed | Breaking |
| [`cin*_value` nested under routing\_codes object](/en/developers/changelog/v2025-10-13/#changed) | API | Changed | Breaking |
| [XML support removed — all responses are now JSON only.](/en/developers/changelog/v2025-10-13/#removed) | API | Removed | Breaking |
| [Deprecated Tax Reports API removed — new RESTful Tax Reports API is now the only option.](/en/developers/changelog/v2025-10-13/#removed) | Tax Reports | Removed | Breaking |
# v2025-10-13
> First major B2Brouter API version with significant improvements to structure and REST consistency.
This is the first major release of B2Brouter API. It includes significant improvements and changes to make the API consistent, RESTful, and easier to work with.
Now there's an option to select the API Version to use in each request. The `X-B2B-API-Version` header parameter allows specifying the desired version for the request. If the **API Version** is not specified in the request, it will be defined by the group configuration related to the **API Key** used in the request.
> This version implements breaking changes. Each breaking change is marked with the tag Breaking.
## Key Changes Summary
[Section titled “Key Changes Summary”](#key-changes-summary)
### API Infrastructure
[Section titled “API Infrastructure”](#api-infrastructure)
* **New Domain**: API access moved from `app.b2brouter.net` to `api.b2brouter.net`
* **Version Header**: New `X-B2B-API-Version` header for explicit version control
* **JSON Only**: XML format support removed; all responses now JSON by default
* **Response Language**: All API responses now default to English regardless of account configuration
### Path Structure Modernization
[Section titled “Path Structure Modernization”](#path-structure-modernization)
* **Resource Naming**: `/projects` renamed to `/accounts`, `/clients` renamed to `/contacts`
* **Clean URLs**: Removed `.{format}` extensions from all endpoints
* **Simplified Hierarchy**: Removed `/users` prefix from code lists, `/api/v1` prefix from directory and tax report endpoints
* **Standardized Pagination**: All list responses now use consistent `meta` object for pagination data
### Schema Standardization
[Section titled “Schema Standardization”](#schema-standardization)
* **Terminology Consistency**: Unified `client` → `contact` throughout the API
* **Structured Identifiers**: Tax codes split into `tin_scheme` and `tin_value` for better structure
* **English Naming**: Spanish, Catalan, and Italian field names standardized to English equivalents
* **Nested Structures**: Bank account fields moved to nested objects for better organization
* **Deprecated Fields Removed**: Cleaned up legacy attributes in favor of standardized alternatives
### Response Improvements
[Section titled “Response Improvements”](#response-improvements)
* **Full Representations**: PUT, DELETE, and POST operations now return complete resource representations instead of `204 No Content`
* **Consistent Errors**: Standardized error responses with `409 Conflict` for business rule violations
* **Simplified References**: Removed nested `project` objects from invoice responses
### Tax Reports API Overhaul
[Section titled “Tax Reports API Overhaul”](#tax-reports-api-overhaul)
The deprecated Tax Reports API has been removed in order to promote the use of a new, more generic and RESTful Tax Reports API suitable for most use cases.
All endpoints that were marked as *\[Deprecated]* in version **2025-01-01** (Legacy) have been removed. The endpoints that were marked as *\[New Tax Report API]* in version **2025-01-01** now become the only way to manage Tax Reporting via API. The `/api/v1` prefix has been removed from the path of those endpoints.
***
## Added
[Section titled “Added”](#added)
**New API Version Header**
Added `X-B2B-API-Version` header parameter to specify the API version per request. If not specified, defaults to the version configured in the group's API Key settings.
**New Tax Reports Endpoints**
* `POST /accounts/{account}/tax_reports` - Create a Tax Report
* `POST /accounts/{account}/tax_reports/import` - Import a Tax Report from XML file
* `GET /tax_reports/{id}` - Get a tax report
* `PATCH /tax_reports/{id}` - Correct or Modify a Tax Report
* `DELETE /tax_reports/{id}` - Annulate a tax report
* `GET /tax_reports/{id}/download` - Download a tax report in Tax Authority format
**New Ledgers Endpoints**
* `GET /ledgers/{id}/download` - Download a Ledger XML document
* `GET /ledgers/{id}/download_response` - Download the Tax Authority response for a Ledger
* `POST /accounts/{account}/ledgers/import` - Import a ledger from XML payload
**New Validation Endpoint**
* `POST /documents/validate` - Validates electronic documents (UBL, CII, Peppol BIS, PINT, ZUGFeRD, XRechnung, Factur-X, FacturaE, Svefaktura, Fattura PA, eSPap, and any EN16931 CIUS-compliant format)
**New Transport Endpoint**
* `DELETE /accounts/{account}/transports/{code}` - Delete a transport configuration
**Standardized Pagination**
All listing endpoints now return pagination metadata in a consistent `meta` object containing `total_count`, `offset`, and `limit`.
**Consolidated Invoice Listing**
Added `type` query parameter to `/accounts/{account}/invoices` endpoint to filter by invoice type (IssuedInvoice, ReceivedInvoice, IssuedSelfInvoice, IssuedSimplifiedInvoice).
**Full Resource Responses**
Changed PUT, POST, and DELETE operations to return complete resource representations:
* `PUT /accounts/{account}` - Now returns full account representation
* `DELETE /accounts/{account}` - Now returns full account representation with deletion status
* `POST /accounts/{account}/unarchive` - Now returns full account representation
* `POST /accounts/{account}/logo` - Now returns full account representation
* `DELETE /accounts/{account}/logo` - Now returns full account representation
* `PUT /bank_accounts/{id}` - Now returns full bank account representation
* `DELETE /bank_accounts/{id}` - Now returns full bank account representation
* `PUT /contacts/{id}` - Now returns full contact representation
* `DELETE /contacts/{id}` - Now returns full contact representation
* `PUT /invoices/{id}` - Now returns full invoice representation
* `DELETE /invoices/{id}` - Now returns full invoice representation
* `POST /invoices/{id}/mark_as` - Now returns full invoice representation
* `POST /invoices/{id}/ack` - Now returns full invoice representation
***
## Changed
[Section titled “Changed”](#changed)
🚨 Breaking
**Response Language**
All API responses now default to English regardless of account configuration.
**Previous behavior:**
* API responses inherited the language from the account's default language setting
**New behavior:**
* All API responses default to English
**API Domain Change**
API base URL changed from `app.b2brouter.net` to `api.b2brouter.net`
* Old: `https://app.b2brouter.net/api/...`
* New: `https://api.b2brouter.net/...`
**Resource Path Renaming**
All endpoints that contained `/projects/{account}` have been renamed to `/accounts/{account}`:
* `GET /projects/{account}/contacts` → `GET /accounts/{account}/contacts`
* `POST /projects/{account}/contacts` → `POST /accounts/{account}/contacts`
* `GET /projects/{account}/events` → `GET /accounts/{account}/events`
* `GET /projects/{account}/invoices` → `GET /accounts/{account}/invoices`
* `POST /projects/{account}/invoices` → `POST /accounts/{account}/invoices`
* `GET /projects/{account}/orders` → `GET /accounts/{account}/orders`
* `POST /projects/{account}/orders` → `POST /accounts/{account}/orders`
* `POST /projects/{account}/despatch_advices` → `POST /accounts/{account}/despatch_advices`
* And all other project-scoped endpoints
All endpoints referencing `/clients` have been renamed to `/contacts`:
* `GET /clients/{id}` → `GET /contacts/{id}`
* `PUT /clients/{id}` → `PUT /contacts/{id}`
* `DELETE /clients/{id}` → `DELETE /contacts/{id}`
**Format Extension Removal**
Removed `.{format}` path extensions from all endpoints. JSON is now the only supported format and is set as default:
* `GET /invoices/{id}.{format}` → `GET /invoices/{id}`
* `PUT /invoices/{id}.{format}` → `PUT /invoices/{id}`
* `DELETE /invoices/{id}.{format}` → `DELETE /invoices/{id}`
* `POST /invoices/{id}/mark_as.{format}` → `POST /invoices/{id}/mark_as`
* `POST /invoices/{id}/add_attachment.{format}` → `POST /invoices/{id}/add_attachment`
* `POST /invoices/{id}/add_attachments.{format}` → `POST /invoices/{id}/add_attachments`
* `POST /invoices/{id}/generate_tax_report.{format}` → `POST /invoices/{id}/generate_tax_report`
* `POST /invoices/{id}/ack.{format}` → `POST /invoices/{id}/ack`
* `GET /orders/{id}.{format}` → `GET /orders/{id}`
* `POST /orders/{id}/mark_as.{format}` → `POST /orders/{id}/mark_as`
* `GET /clients/{id}.{format}` → `GET /contacts/{id}`
* `PUT /clients/{id}.{format}` → `PUT /contacts/{id}`
* `DELETE /clients/{id}.{format}` → `DELETE /contacts/{id}`
* And all code list endpoints
**Code Lists Path Simplification**
Removed `/users` prefix from all code list endpoints:
* `GET /users/countries` → `GET /countries`
* `GET /users/currencies` → `GET /currencies`
* `GET /users/languages` → `GET /languages`
* `GET /users/schemes` → `GET /schemes`
* `GET /users/invoice_states` → `GET /invoice_states`
* And all other code list endpoints
**Contact Schema Standardization**
Contact (formerly Client) resource now uses standardized field names:
**Tax Identifiers:**
* `taxcode` → split into `tin_scheme` (e.g., "VAT", "TAX") and `tin_value` (the actual number)
* `company_identifier` → `cin_value` with accompanying `cin_scheme`
**Routing Codes:**
* `cin1_value`, `cin1_scheme`, `cin2_value`, `cin2_scheme`, ... `cin5_value`, `cin5_scheme` → nested under `routing_codes` object
* In responses, routing codes are always returned inside a `routing_codes` object
* In requests, routing codes must be sent inside a `routing_codes` wrapper
**Transport:**
* `channel` → `transport_type_code`
**Italian Fields:**
* `posta_elettronica_certificata` → `certified_email`
* `codice_destinatario` → `recipient_code`
**Bank Details:**
* `iban`, `bic`, `bank_name` → nested under `bank_account` object
**Contact Person:**
* `contact` → `contact_person`
**Invoice Schema Improvements**
**Reference Fields:**
* `project` nested object removed; use flat `account_id` field
* `accounting_cost` → `buyer_accounting_reference`
* `contact_person` → `customer_contact_person`
**Bank Details:**
* `iban` and `bic` → nested under `bank_account.iban` and `bank_account.bic`
**Public Administration Fields (English names):**
* `num_contracte` → `contract_number`
* `organ_gestor` → `managing_unit`
* `oficina_comptable` → `accounting_unit`
* `organ_proponent` → `proponent_unit`
* `unidad_contratacion` → `contract_unit`
* `unitat_tramitadora` → `processing_unit`
**Event Resource Updates**
* `ClientEvent` renamed to `ContactEvent` for consistency
***
## Deprecated
[Section titled “Deprecated”](#deprecated)
*No features were marked as deprecated in this release.*
***
## Removed
[Section titled “Removed”](#removed)
🚨 Breaking
**XML Format Support**
Support for XML format response has been removed. JSON format only is now supported and has been set as default.
**Deprecated Company/Project Endpoints**
* `GET /projects.{format}` - Removed. Use `GET /accounts` instead.
* `GET /projects/{account}/my_company.{format}` - Removed. Use `GET /accounts/{account}` instead.
**Deprecated Tax Reports Endpoints**
* `POST /tax_reports/send_tax_report/{id}.{format}` - Removed. Use `POST /accounts/{account}/tax_reports` instead.
* `GET /tax_reports/{id}/as/{document_type_code}` - Removed. Use `GET /tax_reports/{id}/download` instead.
**Channels Endpoint**
* `GET /users/channels.{format}` - Removed. Use `GET /transport_types` instead.
**Event Field**
Removed `description` field from event responses in `GET /accounts/{account}/events` endpoint.
**Schema Attributes Removed**
**Account Schema:**
* `prefer_xslt_pdf` - Removed
**Invoice Schema:**
* `contact_person` - Removed. Use `customer_contact_person` instead
* `state` (write-only) - Removed. Use `POST /invoices/{id}/mark_as` endpoint instead
* `customer_party_identification` - Removed. Use `contact.party_identification` instead
* `accounting_cost` - Removed. Use `buyer_accounting_reference` instead
* `iban` - Removed. Use nested `bank_account.iban` structure instead
* `bic` - Removed. Use nested `bank_account.bic` structure instead
* `num_contracte` - Removed. Use `contract_number` instead
* `organ_gestor` - Removed. Use `managing_unit` instead
* `oficina_comptable` - Removed. Use `accounting_unit` instead
* `organ_proponent` - Removed. Use `proponent_unit` instead
* `unidad_contratacion` - Removed. Use `contract_unit` instead
* `unitat_tramitadora` - Removed. Use `processing_unit` instead
* `unitat_tramitadora_name` - Removed. Use `processing_unit` instead
**Contact Schema:**
* `old_channel` - Removed. Use `transport_type_code` instead
* `contact` - Removed. Use `contact_person` instead
* `bank_account` - Removed. Use `bank_account_number` instead
* `company_identifier` - Removed. Use `cin_value` instead
* `transport_type` - Removed. Use `transport_type_code` instead
* `document_type` - Removed. Use `document_type_code` instead
* `sepa_type` - Removed
* `posta_elettronica_certificata` - Removed. Use `certified_email` instead
* `codice_destinatario` - Removed. Use `recipient_code` instead
**Parameter Schemas:**
Deprecated status filter parameters:
* `discarded` - Marked as deprecated and will be removed
* `received_invoice_discarded` - Marked as deprecated
* `received_invoice_new` - Removed
* `received_invoice_error` - Removed
* `received_invoice_refused` - Removed
* `received_invoice_annotated` - Removed
* `received_invoice_invalid` - Removed
***
## Fixed
[Section titled “Fixed”](#fixed)
*No bug fixes were specifically documented in this release.*
***
## Security
[Section titled “Security”](#security)
*No security-specific changes were documented in this release.*
# v2026-03-02
> API changelog for v2026-03-02, including breaking changes.
> This version implements breaking changes. Each breaking change is marked with the tag Breaking.
## Added
[Section titled “Added”](#added)
* **New field `invoice_lines[].base_quantity`** (decimal, defaults to 1) to support Price Base Quantity (BT-149), where `price` represents the price per `base_quantity` units.
* **New fields `tax_currency_code` and `tax_amount_in_tax_currency`**: `tax_currency_code` (ISO 4217 string) and `tax_amount_in_tax_currency` (decimal) for Tax Accounting Currency (BT-6 & BT-111) dual currency support.
* **New field `bank_accounts[].is_default`** (boolean) to mark one bank account per company as the default.
* **New response header `X-B2B-API-Request-Id`** returned on every logged API request for end-to-end tracing.
* **New read-only fields on tax reports**: `annulled_by_id` and `corrected_by_id` (integer, nullable) to reference the tax report that annulled or corrected the current one.
* **New KSeF-specific fields on tax reports**: `payment_account_name` (string), `purchase_order_reference` (string), `sales_order_reference` (string), and `tax_inclusive_amount_before_allowances_and_charges` (decimal).
* **New fields on tax report lines**: `tax_report_lines[].item_seller_identifier` and `tax_report_lines[].item_standard_identifier` (string) on TicketBAI and KSeF tax report lines for seller-assigned and standard item identifiers.
* **New endpoint `DELETE /attachments/:id`** to delete an attachment.
* **New unit codes for `invoice_lines[].unit`**:
* `37` — Tonnes (UN/ECE: TNE)
* `38` — Megawatt-hour (UN/ECE: MWH)
* `39` — Months (UN/ECE: MON)
* `40` — Years (UN/ECE: ANN)
* `41` — Unit/piece (UN/ECE: C62)
* **Tax report settings**: endpoints `PUT` and `DELETE /accounts/{account}/tax_report_settings/{code}` now support `DgfipTaxReportSetting` as an additional response variant for French e-reporting configuration.
* **New endpoint `POST /accounts/{account}/ledgers/import`** to deposit a self-generated DGFiP Flux 10 F10 file directly with the PPF, and **`GET /ledgers/{id}`** to track its lifecycle. See [Submit a Flux-10 e-reporting file](/en/developers/use-cases-by-country/submit-flux10-ereporting-file/).
***
## Changed
[Section titled “Changed”](#changed)
🚨 Breaking
* **Scheme fields type change (number → string)**: `tin_scheme`, `cin_scheme`, `pin_scheme` changed from **number to zero-padded 4-character string** (e.g., `7` → `"0007"`) across all endpoints (accounts, contacts, invoices, transports). When the scheme value is `null`, the field is also returned as `null`.
* **`type_document` renamed to `type_code`** for document type codes across formats (Peppol, CII, KSeF, FatturaPA).
* **`correction_method` restricted to enum**: previously accepted any string, now validates against allowed values: `01` (full replacement), `02` (differences), `03` (bulk deal in period), `04` (authorized by Tax Agency). Invoices with invalid values will receive a validation error.
* **`POST` endpoints return `201 Created`**: `POST /accounts/{account}/invoices`, `POST /accounts/{account}/invoices/import`, `POST /accounts/{account}/tax_reports`, `POST /accounts/{account}/tax_reports/import`, and `POST /accounts/{account}/contacts` now return `201 Created` instead of `200 OK`.
* **Directory lookup async response**: now returns `202 Accepted` with a polling URL when a participant is found in Peppol SML but not yet in the local directory.
* **Contact `is_provider`** now defaults to `true` when creating contacts via API.
* **`contact_id` ignored for `IssuedSimplifiedInvoice`**: simplified invoices always use inline contact fields.
* **Field `payments_on_account`** is now visible in `GET /invoices/{invoice}`.
***
## Deprecated
[Section titled “Deprecated”](#deprecated)
*No features were marked as deprecated in this release.*
***
## Removed
[Section titled “Removed”](#removed)
🚨 Breaking
* **`taxcode` query parameter**: removed from `GET /accounts/{account}/invoices`. Use the `query` parameter with `tin_value=` instead.
* **Deprecated invoice discount/charge fields removed**: the following fields were deprecated in v2025-10-13 in favor of `allowance_charges_attributes` and have been removed. The API accepts these fields without error but silently discards them.
| Removed field | Migration to `allowance_charges_attributes` |
| -------------------------------- | ------------------------------------------------------------------------- |
| `discount_amount` | `[{allowance_charge_indicator: "allowance", amount: X}]` |
| `discount_percent` | `[{allowance_charge_indicator: "allowance", percentage: X}]` |
| `discount_text` | `[{allowance_charge_indicator: "allowance", description: "..."}]` |
| `charge_amount` | `[{allowance_charge_indicator: "charge", amount: X}]` |
| `charge_percent` | `[{allowance_charge_indicator: "charge", percentage: X}]` |
| `charge_reason` | `[{allowance_charge_indicator: "charge", description: "..."}]` |
| `charge_is_reimbursable_expense` | `[{allowance_charge_indicator: "charge", is_reimbursable_expense: true}]` |
| `apply_taxes_to_charge` | Use `apply_taxes` within `allowance_charges_attributes` |
***
## Fixed
[Section titled “Fixed”](#fixed)
* Received Facturae invoices with `correction_method: "01"` now return the original value instead of incorrectly returning `"02"`.
* Polish invoices in EUR with BT-6/BT-111 tax currency data now correctly derive the exchange rate for KSeF reporting.
***
## Security
[Section titled “Security”](#security)
*No security-specific changes were documented in this release.*
# v2026-04-20
> API changelog for v2026-04-20 with new features and improvements.
Select this version by sending `X-B2B-API-Version: 2026-04-20` in your request headers.
> This version implements breaking changes. Each breaking change is marked with the tag Breaking.
## Changes overview
[Section titled “Changes overview”](#changes-overview)
| Change | Area |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| [Add additional identifiers to accounts (`routing_codes`)](#add-additional-identifiers-to-accounts-routing_codes) | Accounts |
| [Add `auto_remittance` to accounts](#add-auto_remittance-to-accounts) | Accounts |
| [Add `skip_line_taxable_base_rounding` to accounts](#add-skip_line_taxable_base_rounding-to-accounts) | Accounts |
| [Expose `cin_value` and `cin_scheme` in invoice contact fields](#expose-cin_value-and-cin_scheme-in-invoice-contact-fields) | Invoices |
| [Replace amend fields with `invoice_references`](#replace-amend-fields-with-invoice_references) Breaking | Invoices |
| [Peppol transport changes](#peppol-transport-changes) (remove `standard_documents`, `pending_peppol_directory_publish`; add `sml_status`, `peppol_directory_status`, `reception_document_types`) Breaking | Transports |
| [Validate `tin_value` and `tin_scheme` against parent contact](#validate-tin_value-and-tin_scheme-against-parent-contact) Breaking | Contacts |
| [Require `tin_value` or `cin_value` when creating a contact inline on invoices](#require-tin_value-or-cin_value-when-creating-a-contact-inline-on-invoices) Breaking | Invoices |
| [New TIN verification endpoint](#tin-verification-endpoint) | TIN Verifications |
| [New `tin_verification.finished` webhook event](#tin-verification-webhook) | Webhooks |
| [Add `exchange_rate` and `exchange_date` to invoice response](#add-exchange_rate-and-exchange_date-to-invoice-response) | Invoices |
| [Remove `payment_method_info` from invoice response](#remove-payment_method_info-from-invoice-response) Breaking | Invoices |
| [Fix `clasification_code_scheme` typo in invoice line responses](#fix-clasification_code_scheme-typo-in-invoice-line-responses) Breaking | Invoices |
| [Add `order_date` to invoice response](#add-order_date-to-invoice-response) | Invoices |
| [Add `total_amount_due` to invoice request](#add-total_amount_due-to-invoice-request) | Invoices *(KSeF)* |
| [Add `third_party` support to invoices](#add-third-party-support-to-invoices) | Invoices *(KSeF)* |
| [Add `customer_party_jst` and `customer_party_gv` to tax reports](#add-customer_party_jst-and-customer_party_gv-to-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Add `purchase_order_date` and `despatch_advice_reference` to tax reports](#add-purchase_order_date-and-despatch_advice_reference-to-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Add `supplier_party_krs`, `supplier_party_regon` and `supplier_party_bdo` to tax reports](#add-supplier_party_krs-supplier_party_regon-and-supplier_party_bdo-to-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Add `supplier_contact_phone` and `supplier_contact_email` to tax reports](#add-supplier_contact_phone-and-supplier_contact_email-to-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Add `previous_advance_total` to tax reports](#add-previous_advance_total-to-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Add `order_total_amount` to tax reports](#add-order_total_amount-to-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Add `prepayment_references` to tax reports](#add-prepayment_references-to-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Make `third_party` fields writable on tax reports](#make-third_party-fields-writable-on-ksef-tax-reports) | Tax Reports *(KSeF)* |
| [Add KSeF invoice type codes](#add-ksef-invoice-type-codes) | Invoices *(KSeF)* |
| [Report Spanish SII VAT books via the JSON Tax Report API](#report-spanish-sii-vat-books-via-the-json-tax-report-api) | Tax Reports *(SII)* |
## Added
[Section titled “Added”](#added)
### TIN verification endpoint
[Section titled “TIN verification endpoint”](#tin-verification-endpoint)
New endpoint to verify TIN (Tax Identification Number) + name pairs against a tax authority census. Currently supports Spain (AEAT) only.
Processing is asynchronous: `POST` returns `202 Accepted` immediately, and results are fetched via `GET` or received through the `tin_verification.finished` webhook event. Results from the last 24 hours are cached to avoid redundant calls to the tax authority.
**New endpoints:**
* `POST /tin_verifications?country=es` — Submit a batch of up to 20,000 TIN+name pairs for verification.
* `GET /tin_verifications/{id}` — Retrieve the status and results of a verification request.
**Example: Submit verification**
```shell
curl --request POST \
--url 'https://app-staging.b2brouter.net/tin_verifications?country=es' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/json' \
--data '[{"tin": "B12345678", "name": "Ejemplo S.L."}]'
```
```json
{
"id": 42,
"status": "pending",
"results": []
}
```
**Example: Fetch results**
```json
{
"id": 42,
"status": "success",
"results": [
{
"tin": "B12345678",
"normalized_tin": "ESB12345678",
"name": "Ejemplo S.L.",
"tin_format_valid": true,
"census_match": true
}
]
}
```
When `census_match` is `true`, the `name` field in results returns the canonical name from the tax authority census; otherwise it echoes the submitted name.
***
### TIN verification webhook
[Section titled “TIN verification webhook”](#tin-verification-webhook)
New webhook event `tin_verification.finished` is fired when a TIN verification request reaches a terminal state (`success` or `failed`). The payload contains only the request ID and status — use `GET /tin_verifications/{id}` to fetch the full results.
Subscribe to this event when configuring a webhook:
```json
{
"web_hook": {
"url": "https://example.com/hooks",
"events": ["tin_verification.finished"]
}
}
```
***
### Add additional identifiers to accounts (`routing_codes`)
[Section titled “Add additional identifiers to accounts (routing\_codes)”](#add-additional-identifiers-to-accounts-routing_codes)
New object `routing_codes` on accounts to support up to 5 additional Company Identifier Numbers beyond the primary `cin`. Contains fields `cin1_value`, `cin1_scheme` through `cin5_value`, `cin5_scheme` (string, nullable). Scheme codes are returned as zero-padded 4-character strings (e.g. `"0088"`), or `null` when the scheme is unknown.
**GET response:**
```json
"routing_codes": {
"cin1_value": "7080000950171",
"cin1_scheme": "0088",
"cin2_value": null,
"cin2_scheme": null,
...
"cin5_value": "1234567890",
"cin5_scheme": "0001",
}
```
**POST/PUT input:** use the same nested `routing_codes` object. If `cinN_value` is provided, `cinN_scheme` is required (and vice versa).
```json
"account": {
"routing_codes": {
"cin1_value": "7080000950171",
"cin1_scheme": "0088"
}
}
```
**Affected endpoints:**
* `POST /accounts` — accepts `routing_codes` in the request body.
* `PUT /accounts/{account}` — accepts `routing_codes` in the request body.
* `GET /accounts/{account}` — returns `routing_codes` in the response.
* `GET /accounts` — returns `routing_codes` in each account entry.
***
### Add `auto_remittance` to accounts
[Section titled “Add auto\_remittance to accounts”](#add-auto_remittance-to-accounts)
New boolean field `auto_remittance` (default `false`) on accounts enables automatic generation of structured remittance references (OGM codes) for Belgian invoices, without requiring an integration group that enforces them.
When `auto_remittance` is `true` and both the account and the invoice contact are located in Belgium, the invoice will automatically receive a structured remittance reference — the same behavior previously available only through integration groups that require OGM codes.
**Affected endpoints:**
* `POST /accounts` — accepts `auto_remittance` in the request body.
* `PUT /accounts/{account}` — accepts `auto_remittance` in the request body.
* `GET /accounts/{account}` — returns `auto_remittance` in the response.
***
### Add `skip_line_taxable_base_rounding` to accounts
[Section titled “Add skip\_line\_taxable\_base\_rounding to accounts”](#add-skip_line_taxable_base_rounding-to-accounts)
New boolean field `skip_line_taxable_base_rounding` (default `false`) on accounts. When enabled, the invoice tax calculation skips rounding of individual line taxable bases and per-line tax amounts, rounding only the final accumulated totals. This reduces rounding drift on invoices with many lines.
Requires `apply_taxes_per_line` to be `true` — the API returns `422 Unprocessable Entity` if `skip_line_taxable_base_rounding` is set to `true` while `apply_taxes_per_line` is `false`.
**Affected endpoints:**
* `POST /accounts` — accepts `skip_line_taxable_base_rounding` in the request body.
* `PUT /accounts/{account}` — accepts `skip_line_taxable_base_rounding` in the request body.
* `GET /accounts/{account}` — returns `skip_line_taxable_base_rounding` in the response.
* `GET /accounts` — returns `skip_line_taxable_base_rounding` in each account entry.
***
### Expose `cin_value` and `cin_scheme` in invoice contact fields
[Section titled “Expose cin\_value and cin\_scheme in invoice contact fields”](#expose-cin_value-and-cin_scheme-in-invoice-contact-fields)
The `contact` object in invoice responses now includes `cin_value` and `cin_scheme` fields alongside the existing `tin_value` and `tin_scheme`.
**Affected endpoints:**
* `GET /invoices/{id}` — `contact` object now includes `cin_value` and `cin_scheme`.
* `GET /accounts/{account}/invoices` — `contact` object in index entries now includes `cin_value` and `cin_scheme`.
***
## Changed
[Section titled “Changed”](#changed)
### Validate `tin_value` and `tin_scheme` against parent contact
[Section titled “Validate tin\_value and tin\_scheme against parent contact”](#validate-tin_value-and-tin_scheme-against-parent-contact)
🚨 Breaking
The requests `POST /accounts/{account}/contacts` and `PATCH /contacts/{id}` with `parent_id` now validate `tin_value` and `tin_scheme` against the parent contact. Offices (organizational units) inherit TIN from their parent; if the provided `tin_value` does not match the parent's, the API returns `422 Unprocessable Entity` with code `parameter_invalid`. Previously, mismatched values were silently ignored.
`cin_value` and `cin_scheme` are **inherited** from the parent contact and not validated against it.
To migrate, either omit `tin_value` when creating offices (recommended), or ensure it matches the parent's value exactly.
**Affected endpoints:**
* `POST /accounts/{account}/contacts` — rejects mismatched `tin_value` / `tin_scheme` when `parent_id` is set.
* `PATCH /contacts/{id}` — rejects mismatched `tin_value` / `tin_scheme` when the contact has a parent.
***
### Require `tin_value` or `cin_value` when creating a contact inline on invoices
[Section titled “Require tin\_value or cin\_value when creating a contact inline on invoices”](#require-tin_value-or-cin_value-when-creating-a-contact-inline-on-invoices)
🚨 Breaking
`POST /accounts/{account}/invoices` now validates that when a `contact` object is provided inline, at least one of `tin_value` or `cin_value` is present. If both are missing, the API returns `422 Unprocessable Entity` with code `parameter_blank` and `param: "contact"`.
`IssuedSimplifiedInvoice` is exempt from this validation — simplified invoices (tickets) do not require an identified contact.
To migrate, always include `tin_value` or `cin_value` when sending an inline `contact` on invoice create. Alternatively, use `contact_id` to reference an existing contact instead.
**Affected endpoints:**
* `POST /accounts/{account}/invoices` — rejects inline `contact` objects without a `tin_value` or `cin_value` (except for `IssuedSimplifiedInvoice`).
***
### Replace amend fields with `invoice_references`
[Section titled “Replace amend fields with invoice\_references”](#replace-amend-fields-with-invoice_references)
🚨 Breaking
A new `invoice_references` array replaces the flat amendment fields (`amended_number`, `amended_date`, `amended_invoicing_period_start`, `amended_invoicing_period_end`, `amend_reason`, `amend_code_tax`, `correction_method`) on the Invoice resource.
Each reference has a `reference_type`:
* `amend` — corrective invoice referencing the original. Max 1 per invoice.
* `prepayment` — settlement invoice (ROZ) referencing advance payment invoices (ZAL). Multiple allowed.
**Breaking change — response:** the flat fields `amended_number`, `amended_date`, `amended_invoicing_period_start`, `amended_invoicing_period_end`, `amend_reason`, and `correction_method` are no longer returned in the GET response. Use `invoice_references` instead.
**Breaking change — request:** the flat write parameters `amended_number`, `amended_date`, `amended_invoicing_period_start`, `amended_invoicing_period_end`, `amend_reason`, `amend_code_tax`, and `correction_method` are no longer accepted. Use `invoice_references` in the request body instead.
**Migration guide — field mapping:**
| v2026-03-02 (flat field) | v2026-04-20 (`invoice_references`) | Notes |
| -------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `amended_number` | `invoice_references[].number` | Read + Write |
| `amended_date` | `invoice_references[].date` | Read + Write |
| `amended_invoicing_period_start` | `invoice_references[].invoicing_period_start` | Write-only in v2026-04-20 (not returned in response) |
| `amended_invoicing_period_end` | `invoice_references[].invoicing_period_end` | Write-only in v2026-04-20 (not returned in response) |
| `amend_reason` | `invoice_references[].reason` | Read + Write |
| `amend_code_tax` | `invoice_references[].tax_correction_code` | Already write-only in v2026-03-02 |
| `correction_method` | `invoice_references[].correction_method` | Read + Write |
| *(new)* | `invoice_references[].reference_type` (`amend` or `prepayment`) | Required |
| *(new)* | `invoice_references[].referenced_invoice_id` | Read-only; B2BRouter id of the referenced invoice (`null` if external) |
| *(new)* | `invoice_references[].tax_registration_code` | Read + Write |
| *(new)* | `invoice_references[].total_amount` | Read + Write |
**Affected endpoints:**
* `GET /invoices/{id}` — returns `invoice_references` array. Flat amend fields removed from response.
* `POST /accounts/{account}/invoices` — accepts `invoice_references` array.
* `PATCH /invoices/{id}` — accepts `invoice_references` array. Use `{ "id": 123, "_destroy": true }` to delete a reference.
**InvoiceReference fields:**
| Field | Type | Description |
| ------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer | Read-only identifier |
| `reference_type` | string | `amend` or `prepayment` (required) |
| `referenced_invoice_id` | integer | Read-only B2BRouter id of the referenced invoice. `null` when the reference points to an invoice not in the system (e.g. an amend by external number) |
| `number` | string | Invoice number of the referenced invoice |
| `series_code` | string | Series code of the referenced invoice |
| `date` | date | Issue date of the referenced invoice |
| `invoicing_period_start` | date | Start of the invoicing period (write-only; not returned in response) |
| `invoicing_period_end` | date | End of the invoicing period (write-only; not returned in response) |
| `reason` | string | Correction reason |
| `correction_method` | string | Correction method (e.g. Facturae 01-04) |
| `tax_registration_code` | string | Tax authority registration code (e.g. KSeF NrKSeF number) |
| `tax_correction_code` | string | Tax correction type code (e.g. KSeF TypKorekty) |
| `total_amount` | float | Total of the referenced invoice |
| `currency` | string | Currency of total\_amount (ISO 4217) |
**Example: Create a credit note referencing the original invoice**
```shell
curl --request POST \
--url 'https://app-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 '{
"invoice": {
"number": "CN-2026-001",
"date": "2026-03-15",
"is_credit_note": true,
"contact_id": {CONTACT_ID},
"invoice_references": [
{
"reference_type": "amend",
"number": "INV-2026-042",
"date": "2026-02-10"
}
],
"invoice_lines_attributes": [
{ "quantity": 2, "price": 50, "description": "Product returned",
"taxes_attributes": [{ "name": "VAT", "percent": 21 }] }
]
}
}'
```
**Example: Create a ROZ (settlement) invoice with prepayment references**
```shell
curl --request POST \
--url 'https://app-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 '{
"invoice": {
"number": "ROZ/2026/001",
"date": "2026-08-17",
"type_code": "ROZ",
"contact_id": {CONTACT_ID},
"invoice_references": [
{
"reference_type": "prepayment",
"tax_registration_code": "9999999999-20260215-8BEF280C8D35-4D"
},
{
"reference_type": "prepayment",
"number": "ZAL/2026/100"
}
],
"invoice_lines_attributes": [
{ "quantity": 1, "price": 8000, "description": "Service — final settlement",
"taxes_attributes": [{ "name": "VAT", "percent": 8 }] }
]
}
}'
```
**Example: Create a KOR\_ZAL (advance correction) with amend reference**
```shell
curl --request POST \
--url 'https://app-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 '{
"invoice": {
"number": "KOR_ZAL/2026/001",
"date": "2026-03-17",
"type_code": "KOR_ZAL",
"contact_id": {CONTACT_ID},
"invoice_references": [
{
"reference_type": "amend",
"number": "ZAL/2026/050",
"date": "2026-02-15",
"tax_registration_code": "9999999999-20260215-8BEF280C8D35-4D",
"tax_correction_code": "1",
"total_amount": 20000.0,
"currency": "PLN"
}
],
"invoice_lines_attributes": [
{ "quantity": 1, "price": 5000, "description": "Advance correction",
"taxes_attributes": [{ "name": "VAT", "percent": 8 }] }
]
}
}'
```
***
### Peppol transport changes
[Section titled “Peppol transport changes”](#peppol-transport-changes)
🚨 Breaking
The Peppol transport response has been updated with new fields that replace the previous publication status indicators. Publish and unpublish operations now run asynchronously — poll these fields to track progress.
**Removed fields:**
* `standard_documents` — no longer returned in responses. If sent in a request, it is silently ignored. The system now automatically configures standard documents for all Peppol transports.
* `pending_peppol_directory_publish` — replaced by `peppol_directory_status`.
**New fields:**
* `sml_status` — string. SML (Service Metadata Locator) registration status.
* `peppol_directory_status` — string. Peppol Directory listing status.
* `reception_document_types` — array of document type objects (`code`, `name`) listing the document types this participant can receive via Peppol. Empty when `reception` is `false` or the company has no reception services configured.
Possible values for the two status fields: `"not_published"`, `"processing"` (async operation pending), and `"published"`. These fields reflect the publication status of this participant on the Peppol network via B2Brouter as Access Point.
**Example response (Peppol transport, published participant):**
```json
{
"code": "peppol",
"enabled": true,
"reception": true,
"invoice": true,
"credit_note": true,
"self_billing": false,
"order": true,
"application_response": true,
"pin_scheme": "0208",
"pin_value": "0123456789",
"sml_status": "published",
"peppol_directory_status": "published",
"reception_document_types": [
{ "code": "xml.ubl.invoice.be", "name": "Belgium UBL.BE Invoice 3.0" },
{ "code": "xml.ubl.credit_note.bis3", "name": "Peppol BIS3 CreditNote" }
]
}
```
**Affected endpoints:**
* `GET /accounts/{account}/transports` — response includes `sml_status`, `peppol_directory_status`, and `reception_document_types`; no longer returns `standard_documents` or `pending_peppol_directory_publish`.
* `PATCH /transports/{code}` — `standard_documents` parameter is silently ignored.
* `PUT /transports/{code}` — `standard_documents` parameter is silently ignored.
***
### Add `exchange_rate` and `exchange_date` to invoice response
[Section titled “Add exchange\_rate and exchange\_date to invoice response”](#add-exchange_rate-and-exchange_date-to-invoice-response)
New fields on the invoice response:
* `exchange_rate` (decimal, nullable) — the exchange rate used for currency conversion when the invoice currency differs from the tax authority's local currency.
* `exchange_date` (date, nullable) — the date of the exchange rate.
These fields were previously accepted on input (write-only) but not returned in API responses. Starting with v2026-04-20, they are included in the response.
**Affected endpoints:**
* `GET /invoices/{id}` — returns `exchange_rate` and `exchange_date`.
* `POST /accounts/{account}/invoices` — returns both fields in the response.
* `PATCH /invoices/{id}` — returns both fields in the response.
***
### Add `order_date` to invoice response
[Section titled “Add order\_date to invoice response”](#add-order_date-to-invoice-response)
New field `order_date` (date, nullable) on the invoice response. This is the purchase order date, complementing the existing `ponumber` (purchase order reference) field. Previously accepted on input but not returned in API responses.
**Affected endpoints:**
* `GET /invoices/{id}` — returns `order_date`.
* `POST /accounts/{account}/invoices` — returns `order_date` in the response.
* `PATCH /invoices/{id}` — returns `order_date` in the response.
***
### Fix `clasification_code_scheme` typo in invoice line responses
[Section titled “Fix clasification\_code\_scheme typo in invoice line responses”](#fix-clasification_code_scheme-typo-in-invoice-line-responses)
🚨 Breaking
The invoice line response field `clasification_code_scheme` was misspelled (missing an 's') in all previous API versions. Starting with v2026-04-20, the response returns the correct spelling: `classification_code_scheme`.
* **Request (input):** `classification_code_scheme` — unchanged, all versions already accepted the correct spelling.
* **Response (output):** `classification_code_scheme` — corrected in v2026-04-20. Older versions (v2025-01-01, v2025-10-13, v2026-03-02) continue to return `clasification_code_scheme` to avoid breaking existing integrations.
If your code reads `clasification_code_scheme` from invoice line responses, update it to `classification_code_scheme` when migrating to v2026-04-20.
**Affected endpoints:**
* `GET /invoices/{id}` — `lines[].classification_code_scheme` now uses correct spelling in response.
* `POST /accounts/{account}/invoices` — response body uses correct spelling.
* `PATCH /invoices/{id}` — response body uses correct spelling.
***
### Add `total_amount_due` to invoice request
[Section titled “Add total\_amount\_due to invoice request”](#add-total_amount_due-to-invoice-request)
New write-only field `total_amount_due` on the Invoice resource. Accepts the invoice total amount directly, bypassing line-based calculation.
Primarily intended for KSeF `ZAL` (advance payment) and `KOR_ZAL` (advance correction) invoices, where the invoice total cannot be derived from invoice lines in the usual way. Maps to the `P_15` field in the KSeF FA(3) XML.
`total_amount_due` is not returned in the response (write-only). The resolved total is returned via the standard `total` and `payable_amount` fields.
**Affected endpoints:**
* `POST /accounts/{account}/invoices` — accepts `total_amount_due` in the request body.
* `PATCH /invoices/{id}` — accepts `total_amount_due` in the request body.
***
## KSeF Changes
[Section titled “KSeF Changes”](#ksef-changes)
### Add `third_party` support to invoices
[Section titled “Add third\_party support to invoices”](#add-third_party-support-to-invoices)
Three new fields on the Invoice resource support Polish KSeF invoicing scenarios where a third party (Podmiot3) is involved:
* `apply_to_local_government_unit` — boolean, marks the invoice as addressed to a JST (Jednostka Samorządu Terytorialnego), a subordinate local government unit.
* `apply_to_vat_group_member` — boolean, marks the invoice as addressed to a GV (Grupa VAT) member.
* `third_party` — nested object with the third party's name, NIP (tin\_value), and address. Required when either flag is `true`.
These fields are mutually exclusive: only one of `apply_to_local_government_unit` or `apply_to_vat_group_member` can be `true` on the same invoice.
These fields use an **expandable response** pattern: they are not included in API responses by default. Request them via the `include` query parameter.
When generating a KSeF tax report from an invoice, B2Brouter uses these flags to automatically set the buyer/third-party roles. The invoice contact becomes the buyer (Podmiot2) and the `third_party` becomes Podmiot3 (role 8 for JST, role 10 for GV).
**Affected endpoints:**
* `GET /invoices/{id}` — returns the new fields when requested via `?include=apply_to_local_government_unit,apply_to_vat_group_member,third_party`
* `POST /accounts/{account}/invoices` — accepts the new fields in the request body
* `PATCH /invoices/{id}` — accepts the new fields in the request body
**Example: Create an invoice for a government unit (JST)**
```shell
curl --request POST \
--url 'https://app-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 '{
"invoice": {
"number": "FV/2026/001",
"date": "2026-03-23",
"contact_id": {CONTACT_ID},
"apply_to_local_government_unit": true,
"third_party": {
"role": "local_government_unit_recipient",
"name": "Przykład Jednostka Podrzędna",
"tin_value": "PL1234567890",
"tin_scheme": "9945",
"address": "ul. Przykładowa 12",
"city": "Warszawa",
"postalcode": "00-001",
"country": "pl"
},
"invoice_lines_attributes": [
{ "quantity": 1, "price": 100, "description": "Service" }
]
}
}'
```
**Example: Read invoice with expanded fields**
```shell
curl --request GET \
--url 'https://app-staging.b2brouter.net/invoices/{INVOICE_ID}?include=apply_to_local_government_unit,apply_to_vat_group_member,third_party' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}'
```
```json
{
"invoice": {
"id": 67890,
"apply_to_local_government_unit": true,
"apply_to_vat_group_member": false,
"third_party": {
"role": "local_government_unit_recipient",
"name": "Przykład Jednostka Podrzędna",
"tin_value": "PL1234567890",
"tin_scheme": "9945",
"cin_value": null,
"cin_scheme": null,
"address": "ul. Przykładowa 12",
"address2": null,
"city": "Warszawa",
"postalcode": "00-001",
"province": null,
"country": "pl"
},
...
}
}
```
***
### Add `customer_party_jst` and `customer_party_gv` to KSeF tax reports
[Section titled “Add customer\_party\_jst and customer\_party\_gv to KSeF tax reports”](#add-customer_party_jst-and-customer_party_gv-to-ksef-tax-reports)
Two new boolean fields on KSeF tax reports indicate whether the buyer is a government unit or a VAT group member:
* `customer_party_jst` — `true` if the buyer is a subordinate local government unit. Defaults to `false`.
* `customer_party_gv` — `true` if the buyer is a VAT group member. Defaults to `false`.
When creating tax reports from invoices, these fields are set automatically based on the invoice's `apply_to_local_government_unit` and `apply_to_vat_group_member` flags. When creating tax reports directly via the API, set them explicitly.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts and returns `customer_party_jst` and `customer_party_gv` when the tax report type is `KSeF`.
* `POST /accounts/{account}/tax_reports/import` — accepts the new fields when importing KSeF tax reports.
**Example: Create a KSeF tax report for a government unit**
```json
{
"tax_report": {
"type": "KSeF",
"customer_party_jst": true,
"customer_party_gv": false,
...
}
}
```
***
### Add `purchase_order_date` and `despatch_advice_reference` to KSeF tax reports
[Section titled “Add purchase\_order\_date and despatch\_advice\_reference to KSeF tax reports”](#add-purchase_order_date-and-despatch_advice_reference-to-ksef-tax-reports)
Two new fields on KSeF tax reports to support order and despatch traceability:
* `purchase_order_date` — date (`YYYY-MM-DD`), nullable. The date of the purchase order (BT-92). Mapped automatically from the invoice's order date.
* `despatch_advice_reference` — string, nullable. The despatch advice reference number (BT-16). Mapped automatically from the invoice's delivery note number.
Both fields are readable and writable via the API.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts and returns both fields when the tax report type is `KSeF`.
* `GET /tax_reports/{id}` — returns both fields for KSeF tax reports.
***
### Add `supplier_party_krs`, `supplier_party_regon` and `supplier_party_bdo` to KSeF tax reports
[Section titled “Add supplier\_party\_krs, supplier\_party\_regon and supplier\_party\_bdo to KSeF tax reports”](#add-supplier_party_krs-supplier_party_regon-and-supplier_party_bdo-to-ksef-tax-reports)
Three new fields on KSeF tax reports for Polish company registry identifiers:
* `supplier_party_krs` — string, nullable. KRS (Krajowy Rejestr Sądowy) number of the supplier. Must be exactly 10 digits when provided.
* `supplier_party_regon` — string, nullable. REGON (statistical identification number) of the supplier. Must be exactly 9 or 14 digits when provided.
* `supplier_party_bdo` — string, nullable. BDO (waste database register) number of the supplier.
These are mapped automatically from the invoice company's CIN values when the scheme codes match (8036 for REGON, 8037 for KRS, 8038 for BDO). If provided directly via the API, `supplier_party_krs` and `supplier_party_regon` are validated and return `422 Unprocessable Entity` if they do not meet the digit requirements.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts and returns the fields when the tax report type is `KSeF`.
* `GET /tax_reports/{id}` — returns the fields for KSeF tax reports.
***
### Add `supplier_contact_phone` and `supplier_contact_email` to KSeF tax reports
[Section titled “Add supplier\_contact\_phone and supplier\_contact\_email to KSeF tax reports”](#add-supplier_contact_phone-and-supplier_contact_email-to-ksef-tax-reports)
Two new fields on KSeF tax reports for supplier contact details (EN16931 BT-41, BT-42, BT-43):
* `supplier_contact_phone` — string, nullable. Supplier contact telephone number (BT-42). Mapped from the invoice company's phone. Exported in the FA(3) XML as `DaneKontaktowe/Telefon`.
* `supplier_contact_email` — string, nullable. Supplier contact email address (BT-43). Mapped from the invoice company's email. Exported in the FA(3) XML as `DaneKontaktowe/Email`.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts and returns the fields when the tax report type is `KSeF`.
* `GET /tax_reports/{id}` — returns the fields for KSeF tax reports.
***
### Add `previous_advance_total` to KSeF tax reports
[Section titled “Add previous\_advance\_total to KSeF tax reports”](#add-previous_advance_total-to-ksef-tax-reports)
New field on KSeF tax reports for corrective advance-payment invoices (`KOR_ZAL`):
* `previous_advance_total` — decimal, nullable. The gross total of the original ZAL (advance payment) invoice being corrected. Maps to the `P_15ZK` field in the KSeF FA(3) XML. Required when `invoice_type_code` is `KOR_ZAL`.
When generating a tax report from an invoice, this value is mapped automatically from the invoice's amend reference `total_amount`. When creating a tax report directly via the API, it must be set explicitly.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts and returns the field when the tax report type is `KSeF`.
* `GET /tax_reports/{id}` — returns the field for KSeF tax reports.
**Example: Create a KOR\_ZAL tax report**
```json
{
"tax_report": {
"type": "KSeF",
"invoice_type_code": "KOR_ZAL",
"previous_advance_total": 1230.00,
...
}
}
```
***
### Add `order_total_amount` to KSeF tax reports
[Section titled “Add order\_total\_amount to KSeF tax reports”](#add-order_total_amount-to-ksef-tax-reports)
New field on KSeF tax reports for advance payment and advance correction invoices:
* `order_total_amount` — decimal, nullable. The total value of the order (`WartoscZamowienia`). Required when `invoice_type_code` is `ZAL` or `KOR_ZAL`.
When generating a tax report from an invoice, this value is mapped automatically from the invoice. When creating a tax report directly via the API, it must be set explicitly for ZAL and KOR\_ZAL invoices.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts and returns the field when the tax report type is `KSeF`.
* `GET /tax_reports/{id}` — returns the field for KSeF tax reports.
***
### Add `prepayment_references` to KSeF tax reports
[Section titled “Add prepayment\_references to KSeF tax reports”](#add-prepayment_references-to-ksef-tax-reports)
New array field on KSeF tax reports for settlement invoices (`ROZ`/`KOR_ROZ`):
* `prepayment_references` — array of objects, each referencing an advance payment invoice that this ROZ invoice settles. Each entry maps to a `FakturaZaliczkowa` element in the KSeF FA(3) XML. Required (non-empty) when `invoice_type_code` is `ROZ` or `KOR_ROZ`.
Each object in the array has:
| Field | Type | Description |
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
| `id` | integer | Read-only internal identifier (returned in response) |
| `number` | string | Invoice number of the advance payment invoice (`NrFaZaliczkowej`). Required when `registration_code` is absent. |
| `registration_code` | string | KSeF registration number of the advance payment invoice (`NrKSeFFaZaliczkowej`). Required when `number` is absent. |
| `_destroy` | boolean | Set to `true` (with `id`) to remove the reference |
When generating a tax report from an invoice, the references are mapped automatically from the invoice's `prepayment` invoice references. When creating directly via the API, set them explicitly.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts and returns the array when the tax report type is `KSeF`.
* `GET /tax_reports/{id}` — returns the array for KSeF tax reports.
**Example: Create a ROZ tax report**
```json
{
"tax_report": {
"type": "KSeF",
"invoice_type_code": "ROZ",
"prepayment_references": [
{ "registration_code": "9999999999-20260215-8BEF280C8D35-4D" },
{ "number": "ZAL/2026/001" }
],
...
}
}
```
***
### Make `third_party` fields writable on KSeF tax reports
[Section titled “Make third\_party fields writable on KSeF tax reports”](#make-third_party-fields-writable-on-ksef-tax-reports)
The `third_party_*` identifier fields on KSeF tax reports were previously read-only — populated only when generating a tax report from an invoice. Starting with v2026-04-20, they are writable so API consumers can set the Podmiot3 data when creating tax reports directly.
Newly writable fields:
* `third_party_name` — string, nullable.
* `third_party_id` — string, nullable. Identifier value (e.g. GLN).
* `third_party_scheme` — string, nullable. Identifier scheme (e.g. `"0088"` for GLN).
* `third_party_tax_id` — string, nullable. NIP / VAT number of the third party.
* `third_party_tax_scheme` — string, nullable. Tax scheme code.
* `third_party_country` — string, nullable. ISO 3166-1 alpha-2 country code.
* `third_party_endpoint_id` — string, nullable. Peppol endpoint identifier for routing purposes.
Already-writable fields in previous versions (unchanged): `third_party_role` (KSeF code `2`–`11`, e.g. `2` = delivery recipient, `8` = local government unit, `10` = VAT group member), `third_party_address`, `third_party_postalcode`, `third_party_city`.
**Validation:** when `third_party_role` is set, `third_party_country` is now required. The API returns `422 Unprocessable Entity` with `param: "third_party_country"` if missing. (This validation is new in v2026-04-20 — possible because `third_party_country` is now writable.)
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — accepts the third-party fields in the request body.
* `PATCH /tax_reports/{id}` — accepts the third-party fields in the request body.
***
### Add KSeF invoice type codes
[Section titled “Add KSeF invoice type codes”](#add-ksef-invoice-type-codes)
New `type_code` values for Polish KSeF advance-payment and correction invoice workflows:
| `type_code` | Description |
| ----------- | ------------------------------------------------------------------------------- |
| `ZAL` | Advance payment invoice (*Faktura zaliczkowa*) |
| `ROZ` | Settlement invoice (*Faktura rozliczeniowa*) — settles one or more ZAL invoices |
| `KOR_ZAL` | Correction of an advance payment invoice |
| `KOR_ROZ` | Correction of a settlement invoice |
| `KOR` | Generic correction invoice (KSeF FA(3) *Korekta*) |
| `VAT` | VAT-only invoice (*Faktura VAT*) |
| `UPR` | Simplified invoice (*Faktura uproszczona*) |
These values are accepted on create (`POST /accounts/{account}/invoices`) and update (`PUT /invoices/{id}`), and returned in invoice responses. Existing type codes (`IssuedInvoice`, `ReceivedInvoice`, etc.) remain unchanged.
**Affected endpoints:**
* `POST /accounts/{account}/invoices` — accepts the new `type_code` values.
* `PUT /invoices/{id}` — accepts the new `type_code` values.
* `GET /invoices/{id}` — may return the new values in the `type_code` field.
***
## SII Changes
[Section titled “SII Changes”](#sii-changes)
### Report Spanish SII VAT books via the JSON Tax Report API
[Section titled “Report Spanish SII VAT books via the JSON Tax Report API”](#report-spanish-sii-vat-books-via-the-json-tax-report-api)
This version exposes the Spanish SII (*Suministro Inmediato de Información*) on the standard JSON Tax Report API, the same surface other CTC authorities (Verifactu, TicketBai, KSeF) already use — no SOAP, AEAT certificates, or per-book XML envelopes. Records cover the nine SII books (Expedida, Recibida, Inversion, Intracomunitaria, Metalico, Seguro, Viajesagencia, Cobro, Pago), with A0/A1/B lifecycle (create / `correct` / `annullate`). A bulk endpoint ingests many records in one request for high-volume reporters.
SII is only reachable on this API version: send `X-B2B-API-Version: 2026-04-20`. An older or absent version header returns `422` and never reaches the SII path.
**Affected endpoints:**
* `POST /accounts/{account}/tax_reports` — create an SII record (`type: "SiiDocument"`, `book_type: …`).
* `PATCH /tax_reports/{id}` — submit an A1 modificación.
* `DELETE /tax_reports/{id}` — submit a B baja / annullation.
* `POST /accounts/{account}/tax_report_batches` — bulk-create SII records in one request.
* `GET /tax_reports/{id}`, `GET /tax_reports/{id}/download` — read the record and its rendered AEAT XML.
See the [SII guide](/en/developers/guides-by-country/spain/sii/) for the per-book field mapping.
***
## Removed
[Section titled “Removed”](#removed)
### Remove `payment_method_info` from invoice response
[Section titled “Remove payment\_method\_info from invoice response”](#remove-payment_method_info-from-invoice-response)
🚨 Breaking
The `payment_method_info` field has been removed from the invoice response. This field contained an HTML-formatted summary of payment details (e.g. `"Bank transfer to
IBAN ES13 210..."`), which is redundant with the structured fields already present in the response (`payment_method`, `contact_iban`, `contact_bic`, etc.) and rendered poorly in JSON consumers.
**Affected endpoints:**
* `GET /invoices/{id}` — no longer returns `payment_method_info`.
* `POST /accounts/{account}/invoices` — no longer returns `payment_method_info` in the response.
* `PATCH /invoices/{id}` — no longer returns `payment_method_info` in the response.
# v2026-06-26
> API changelog for v2026-06-26 with new features, improvements and breaking changes.
Select this version by sending `X-B2B-API-Version: 2026-06-26` in your request headers.
> This version implements breaking changes. Each breaking change is marked with the tag Breaking.
## Changes overview
[Section titled “Changes overview”](#changes-overview)
| Change | Area |
| -------------------------------------------------------------------------------------------------------------------------- | --------- |
| [Received invoice webhook events](#received-invoice-webhook-events) | Webhooks |
| [Create offices via `POST /accounts` with `parent_id`](#create-offices-via-post-accounts-with-parent_id) | Accounts |
| [Independent CIN for contact offices](#independent-cin-for-contact-offices) 🚨 Breaking | Contacts |
| [`parent_id` in contact responses](#parent_id-in-contact-responses) | Contacts |
| [DGFiP `refuse_reason_code` on mark\_as and invoice responses](#dgfip-refuse_reason_code-on-mark_as-and-invoice-responses) | Invoices |
| [`issue_after_import` on invoice creation and import](#issue_after_import-on-invoice-creation-and-import) | Invoices |
| [French Annuaire lookup on `/directory/fr`](#french-annuaire-lookup-on-directoryfr) 🚨 Breaking | Directory |
| [Inline contact `routing_codes` on invoice creation](#inline-contact-routing_codes-on-invoice-creation) 🚨 Breaking | Invoices |
| [Invoice-level `routing_codes` on invoice creation](#invoice-level-routing_codes-on-invoice-creation) 🚨 Breaking | Invoices |
| [Taxable base and tax amount in invoice list](#taxable-base-and-tax-amount-in-invoice-list) | Invoices |
| [Invoice list filters: `number` now matches by prefix and `series_code`](#invoice-list-filters) | Invoices |
| [Generate tax report returns 201 Created](#generate-tax-report-returns-201-created) 🚨 Breaking | Invoices |
| [Account `unit_code_format` preference](#account-unit_code_format-preference) | Accounts |
| [Per-invoice `apply_taxes_per_line`](#per-invoice-apply_taxes_per_line) | Invoices |
## Added
[Section titled “Added”](#added)
### Received invoice webhook events
[Section titled “Received invoice webhook events”](#received-invoice-webhook-events)
Two new webhook events for received invoices:
* `received_invoice.created` — fired when a new received invoice is created in the platform (e.g. imported or upload).
* `received_invoice.state_change` — fired when a received invoice transitions to a new state.
Subscribe to these events when configuring a webhook:
```json
{
"web_hook": {
"url": "https://example.com/hooks",
"events": ["received_invoice.created", "received_invoice.state_change"]
}
}
```
**Payload for `received_invoice.created`:**
```json
{
"invoice_id": 12345,
"account_id": 42,
"state": "received"
}
```
**Payload for `received_invoice.state_change`:**
```json
{
"invoice_id": 12345,
"event_id": 67890,
"state": "accepted",
"notes": null
}
```
***
### Create offices via `POST /accounts` with `parent_id`
[Section titled “Create offices via POST /accounts with parent\_id”](#create-offices-via-post-accounts-with-parent_id)
`POST /accounts` now accepts an optional `parent_id` field. When provided, the account is created as an office (subsidiary) under the specified parent account.
When `parent_id` is set:
* The parent must be a top-level account within the same group (not itself an office).
* The standard required fields (`name`, `postalcode`, `email`, `address`, `city`, `province`, `phone`, `tin_value`) become optional.
* The created account inherits the group's integration and is returned with `parent_id` in the response.
Returns `422 Unprocessable Entity` with code `parameter_invalid` (param `parent_id`) if:
* The parent account is not found or belongs to a different group.
* The parent is itself an office.
**Example request:**
```json
POST /accounts
{
"account": {
"parent_id": 12345,
"name": "Paris Establishment",
"address": "1 Rue de la Paix",
"city": "Paris",
"postalcode": "75001",
"province": "Ile-de-France",
"email": "paris@example.com"
}
}
```
**Example response:**
```json
{
"account": {
"id": 67890,
"parent_id": 12345,
"name": "Paris Establishment",
...
}
}
```
**Notes:**
* `tin_value` / `tin_scheme` cannot be set on an office — they are always inherited from the parent account. Sending them returns `422 parameter_cannot_change` (param `tin_value`).
* `cin_value` / `cin_scheme` are stored independently on the office. If not set, the office inherits the parent's CIN in read responses.
***
### `parent_id` in contact responses
[Section titled “parent\_id in contact responses”](#parent_id-in-contact-responses)
All single-contact responses now include `parent_id`: `GET /contacts/{id}`, `POST /accounts/{account}/contacts`, `PUT /contacts/{id}`, and `DELETE /contacts/{id}`.
* For top-level contacts, `parent_id` is `null`.
* For offices, `parent_id` contains the ID of the parent contact.
***
### DGFiP `refuse_reason_code` on mark\_as and invoice responses
[Section titled “DGFiP refuse\_reason\_code on mark\_as and invoice responses”](#dgfip-refuse_reason_code-on-mark_as-and-invoice-responses)
`POST /invoices/{id}/mark_as` now accepts an optional `reason_code` parameter when transitioning to the `refused` state. This field carries a DGFiP-defined structured reason code for process 210 refusals.
**Accepted values:**
`ADR_ERR`, `REF_CT_ABSENT`, `CMD_ERR`, `TX_TVA_ERR`, `MONTANTTOTAL_ERR`, `CALCUL_ERR`, `DOUBLON`, `NON_CONFORME`, `DEST_ERR`, `TRANSAC_INC`, `EMMET_INC`, `CONTRAT_TERM`, `DOUBLE_FACT`, `AUTRE`
**Example request:**
```json
POST /invoices/{id}/mark_as
{
"state": "refused",
"reason": "Incorrect total amount",
"reason_code": "MONTANTTOTAL_ERR"
}
```
**Example response (excerpt):**
```json
{
"invoice": {
"id": 12345,
"state": "refused",
"refuse_reason": "Incorrect total amount",
"refuse_reason_code": "MONTANTTOTAL_ERR",
"..."
}
}
```
***
### `issue_after_import` on invoice creation and import
[Section titled “issue\_after\_import on invoice creation and import”](#issue_after_import-on-invoice-creation-and-import)
`POST /accounts/{account}/invoices` and `POST /accounts/{account}/invoices/import` now accept an optional `issue_after_import` flag. When `true` and the invoice passes validations:
* The invoice transitions to `state=issued` (c1 corner) synchronously, inline with the request.
* If the account has a `before_send` tax report setting enabled (Verifactu, Ksef, Zatca) with `auto_generate` on and the invoice qualifies, the tax report is generated inline and exposed via `tax_report_ids` in the response. When `auto_generate` is off the invoice is still issued but no report is created — consistent with the send flow, those reports are managed through the Tax Reports API.
* Authorities that report after sending (e.g. SII, SDI) are not triggered by issuing; their reports are created when the invoice is sent or marked as sent.
* No background send job is enqueued for the invoice; the transport-level state (c2) remains untouched. Tax reports that auto-send (e.g. Ksef) still enqueue their own send job.
While the invoice remains in `state=issued` it is read-only: it cannot be updated, deleted, or reverted to `new` until it is sent or downloaded.
If both `send_after_import` and `issue_after_import` are `true`, `send_after_import` wins — sending already issues the invoice. Ignored for non-invoice documents.
The bulk import endpoint `POST /accounts/{account}/invoices/imports` accepts the same `issue_after_import` flag (as a multipart form field). It is applied per file: each imported invoice that qualifies is issued; a file that cannot be issued, or whose issuing fails, is reported on that item without aborting the batch.
**Example request:**
```json
POST /accounts/{account}/invoices
{
"issue_after_import": true,
"invoice": {
"number": "F-2026-0001",
"..."
}
}
```
***
### Taxable base and tax amount in invoice list
[Section titled “Taxable base and tax amount in invoice list”](#taxable-base-and-tax-amount-in-invoice-list)
`GET /accounts/{account}/invoices` now returns two additional fields per invoice:
* `taxable_base` — the invoice subtotal before taxes (float, same currency as `currency`).
* `tax_amount` — the total tax amount, calculated as the sum of all tax breakdown amounts (float).
**Example response (partial):**
```json
{
"invoices": [
{
"id": 12345,
"number": "INV-2025-001",
"total": 121.0,
"taxable_base": 100.0,
"tax_amount": 21.0,
"currency": "EUR"
}
]
}
```
***
### Account `unit_code_format` preference
[Section titled “Account unit\_code\_format preference”](#account-unit_code_format-preference)
Accounts now expose a `unit_code_format` field that controls the code list used for invoice line unit codes when reading and writing invoices through the API.
* `internal` (default) — B2BRouter's own integer unit code list.
* `unece_rec20` — UN/ECE Recommendation 20 codes (e.g. `HUR`, `KGM`, `H87`).
Read it on `GET /accounts/{account}` and set it on `PUT /accounts/{account}`:
```json
{
"account": {
"unit_code_format": "unece_rec20"
}
}
```
When set to `unece_rec20`, the invoice line `unit` field changes type: instead of the default integer id it is exchanged as a UNECERec20 **string** code (e.g. `"HUR"`). It is returned this way on invoice reads — `GET /invoices/{id}` and the `create`/`update` responses — and accepted as a code on writes (invoice create and import); unknown codes fall back to `other`. Integrations that parse `unit` as an integer must handle the string form for these accounts.
The default `internal` keeps the integer id, so existing integrations are unaffected unless they opt in.
***
### Per-invoice `apply_taxes_per_line`
[Section titled “Per-invoice apply\_taxes\_per\_line”](#per-invoice-apply_taxes_per_line)
New field `apply_taxes_per_line` (boolean, nullable) on invoices, both writable and returned in the response. It overrides, **per invoice**, the account-level setting of the same name that controls VAT rounding:
* `true` — taxes are computed per line (each line rounded, then summed).
* `false` — taxes are computed per invoice (the taxable base is summed per rate, then rounded once).
* omitted / `null` — inherit the account's `apply_taxes_per_line` setting (the previous behaviour).
The override is honoured only for premium accounts. This lets you align the rounding mode with public administrations that recompute totals and reject mismatches, without changing the account-wide default. See the [invoice calculation guide](/en/developers/common-use-cases/invoice-calculation/) for worked examples.
**Affected endpoints:**
* `GET /invoices/{id}` — returns the effective `apply_taxes_per_line`.
* `POST /accounts/{account}/invoices` — accepts and returns `apply_taxes_per_line`.
* `PATCH /invoices/{id}` — accepts and returns `apply_taxes_per_line`.
***
## Changed
[Section titled “Changed”](#changed)
### Independent CIN for contact offices
[Section titled “Independent CIN for contact offices”](#independent-cin-for-contact-offices)
🚨 Breaking
`POST /accounts/{account}/contacts` and `PUT /contacts/{id}` now store `cin_value` and `cin_scheme` independently on offices. Previously, CIN was always inherited from the parent contact.
**Behavior:**
* If `cin_value` / `cin_scheme` are provided, they are stored on the office and returned in responses.
* If omitted (or cleared with `cin_value: ""`), the office inherits `cin_value` from the parent contact.
* If the provided `cin_value` and `cin_scheme` are exactly the parent's, they are not stored: the office keeps inheriting the parent's CIN.
* `tin_value` / `tin_scheme` are still inherited from the parent and cannot be set independently.
* CIN uniqueness within the account is enforced for **parent contacts only**: sending a `cin_value` already assigned to another parent contact in the same account returns `422 Unprocessable Entity`.
**Affected endpoints:**
* `POST /accounts/{account}/contacts` — `cin_value` / `cin_scheme` are now stored when `parent_id` is set.
* `PUT /contacts/{id}` — `cin_value` / `cin_scheme` are now stored on offices.
**Example: Create an office with its own CIN**
```json
POST /accounts/{account_id}/contacts
{
"contact": {
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "1234567890",
"cin_scheme": 88
}
}
```
**Response:**
```json
{
"contact": {
"id": 67890,
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "1234567890",
"cin_scheme": "88",
"..."
}
}
```
***
### French Annuaire lookup on `/directory/fr`
[Section titled “French Annuaire lookup on /directory/fr”](#french-annuaire-lookup-on-directoryfr)
🚨 Breaking
`GET /directory/fr/{scheme}/{id}` now consults the French PPF Annuaire (DGFiP REST API) when the identifier is a SIREN (9 digits, scheme `0002`), a SIRET (14 digits, scheme `0009`), or an FRCTC electronic address (scheme `0225`, format `SIREN`, `SIREN_SIRET`, `SIREN_SIRET_CodeRoutage` or `SIREN_Suffix`).
The identifier must be valid for its scheme — length and checksum are validated for every scheme, not just French ones. An identifier the scheme's validator rejects returns `422 Unprocessable Entity` with code `invalid_identifier` (this includes a SIREN/SIRET of the wrong length, a failed checksum, or an FRCTC address whose SIREN/SIRET is invalid). Schemes with no validator are accepted and looked up as before.
For scheme `0225` the value is used as the Annuaire addressing identifier as-is, and the resolved company is keyed by its underlying legal identifier: a bare SIREN resolves to `cin_scheme` `0002`, a `SIREN_SIRET` to `0009` (SIRET), with the full FRCTC address kept as `pin_scheme` `0225`; a code routage is exposed as a `cin1` routing code (scheme `0224`). This means a `0225` lookup and a later `0009`/`0002` lookup of the same company return the same directory entry.
The lookup is asynchronous to keep response times in milliseconds:
* If the company is already known in B2Brouter's directory, the response is `200 OK` with the company data.
* If it is not yet known, the response is `202 Accepted` with a `polling_url`. A background job queries the Annuaire and persists the result. Retry the request after a few seconds — once the data is available the request returns `200 OK`.
* When a known directory entry has not been checked in the last 24 hours (or has never been enriched from the Annuaire), the response is still `200 OK` immediately and a refresh is triggered in the background (stale-while-revalidate).
* After a `404 Not Found` on a SIREN/SIRET, subsequent requests for the same identifier return `404` for 15 minutes without re-querying the Annuaire. Retry after the window if you believe the identifier has since been registered.
* If the lookup fails because an upstream service (Annuaire, Peppol) is unavailable, the response is `424 Failed Dependency` with error code `upstream_unavailable` and a `Retry-After` header — retry after the indicated delay. A transient upstream failure is never converted into a `404`.
The response includes a new field `information_flags` (array of strings) carrying the Annuaire registration state, aligned with the Peppol CTC specification:
| Flag | Meaning |
| ----------------------- | ---------------------------------------------------------- |
| `FR_ASSUJETTI_ACTIVE` | Identifier is present and active in the Annuaire |
| `FR_ASSUJETTI_INACTIVE` | Identifier is present in the Annuaire but inactive |
| `FR_ASSUJETTI_UNKNOWN` | Company is known to B2Brouter but absent from the Annuaire |
Exactly one of these flags is set per company. When a company already known to B2Brouter (e.g. from Peppol) is absent from the Annuaire, it is returned with `FR_ASSUJETTI_UNKNOWN` so the absence is visible. An identifier not known to B2Brouter at all and absent from the Annuaire resolves to `404 Not Found` (see the negation-cache note above).
Subordinate units (SIRET offices below a SIREN) are not yet exposed in the response.
**Example: company not yet known — first request**
```shell
curl --request GET \
--url 'https://app-staging.b2brouter.net/api/v1/directory/fr/0002/123456789' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: 2026-06-26'
```
```json
{
"status": "processing",
"message": "Querying French PPF Annuaire. Please retry this request.",
"polling_url": "https://app-staging.b2brouter.net/api/v1/directory/fr/0002/123456789"
}
```
HTTP status: `202 Accepted`.
**Example: same request a few seconds later**
```json
{
"name": "SIREN 123456789",
"country": "fr",
"information_flags": ["FR_ASSUJETTI_ACTIVE"],
...
}
```
HTTP status: `200 OK`.
**Affected endpoints:**
* `GET /directory/fr/{scheme}/{id}` — extended behavior described above. Non-FR countries are unaffected.
***
### Inline contact `routing_codes` on invoice creation
[Section titled “Inline contact routing\_codes on invoice creation”](#inline-contact-routing_codes-on-invoice-creation)
🚨 Breaking
The inline `contact` on `POST /accounts/{account}/invoices` now accepts a `routing_codes` object — the same shape used by `POST /accounts/{account}/contacts` and returned in contact responses. It carries up to five additional Company Identifier Numbers (`cin1_value`/`cin1_scheme` … `cin5_value`/`cin5_scheme`) beyond the primary `cin`. If `cinN_value` is provided, `cinN_scheme` is required (and vice versa). These routing codes are also returned inside `routing_codes` in the embedded `invoice.contact` of the response.
```json
POST /accounts/{account_id}/invoices
{
"invoice": {
"contact": {
"tin_value": "ESA13585625",
"name": "Buyer Full Name AS",
"routing_codes": {
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS",
"cin1_scheme": "8017"
}
}
}
}
```
**Breaking change:** flat `cinN_value` / `cinN_scheme` keys inside `contact` are no longer accepted. Such requests return `422 Unprocessable Entity`. Send the contact's routing codes through `contact.routing_codes` instead.
***
### Invoice-level `routing_codes` on invoice creation
[Section titled “Invoice-level routing\_codes on invoice creation”](#invoice-level-routing_codes-on-invoice-creation)
🚨 Breaking
`POST /accounts/{account}/invoices` and `PUT /invoices/{id}` now accept a `routing_codes` object at the invoice top level — the same `cin1_value`/`cin1_scheme` … `cin5_value`/`cin5_scheme` shape used by `contact.routing_codes`. It carries the invoice's **own** routing codes (e.g. the buyer's DIR3 / Chorus Pro service codes), independent of the contact's.
The applied codes are echoed back in a `routing_codes` object at the invoice top level of the response, so an integrator can verify what was stored.
```json
POST /accounts/{account_id}/invoices
{
"invoice": {
"number": "F-2026-0001",
"routing_codes": {
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS",
"cin1_scheme": "8017"
}
}
}
```
The dedicated routing-code fields that earlier versions accepted at the invoice top level are no longer accepted. Set the equivalent `routing_codes` slot instead, depending on the scheme:
| Slot | DIR3 (scheme `8014`) | DIRe (`8015`) | EDI/GLN (`88`) |
| ---- | -------------------- | ------------- | -------------- |
| cin1 | `accounting_unit` | — | `nadby` |
| cin2 | `managing_unit` | — | `naddp` |
| cin3 | `processing_unit` | — | `nadiv` |
| cin4 | `proponent_unit` | — | `nadpr` |
| cin5 | — | `dire` | `nadud` |
**Breaking change:** at the invoice top level, both the flat `cinN_value` / `cinN_scheme` keys and the dedicated routing-code fields (`dire`, `accounting_unit`, `managing_unit`, `processing_unit`, `proponent_unit`, `nadby`, `naddp`, `nadiv`, `nadpr`, `nadud`) are no longer accepted, on both `POST /accounts/{account}/invoices` and `PUT /invoices/{id}`. Such requests return `422 Unprocessable Entity`. Send the invoice's routing codes through the invoice `routing_codes` object instead. Earlier API versions still accept these fields.
***
### Invoice list filters
[Section titled “Invoice list filters”](#invoice-list-filters)
Filtering changes on `GET /accounts/{account}/invoices`:
**`number` now matches by prefix**
The `number` query parameter now performs a **prefix** match instead of an exact match. Invoices whose number *starts with* the supplied value are returned.
```plaintext
GET /accounts/{account}/invoices?number=INV-2
```
returns `INV-20`, `INV-21`, … as well as an invoice numbered exactly `INV-2`.
> In previous versions the `number` filter required the full invoice number and returned only an exact match.
**`series_code`**
The `series_code` query parameter filters invoices by series code using an exact match.
***
### Generate tax report returns 201 Created
[Section titled “Generate tax report returns 201 Created”](#generate-tax-report-returns-201-created)
🚨 Breaking
`POST /invoices/{id}/generate_tax_report` now returns `201 Created` on success instead of `200 OK`. The response body is unchanged.
**Before (≤ 2026-04-20):** `200 OK`
**After (≥ 2026-06-26):** `201 Created`
> This is a minor breaking change for clients that check for an exact `200` status. Clients checking for any `2xx` success are unaffected.
# Change the account tax identification number
> Learn how to request a change to your B2Brouter account tax ID and submit documentation through the support channel.
Are you trying to transfer account ownership? See the guide [Transfer account ownership](/en/user-guide/troubleshooting/transfer-account-ownership).
For security reasons, once you have added a VAT number to an account, you cannot change it freely. You have to request the modification through the Support channel.
## Request a change of your account ID number
[Section titled “Request a change of your account ID number”](#request-a-change-of-your-account-id-number)
To request a change of your account’s VAT number, click on the gear icon at the top of the page and click on **Account settings**. Once you are in the page of your fiscal data:
* Click on the **Start request for identifier** option that you find on the right side of the screen.
* In the next step, select **Start request**, make the request and indicate the reason for the change.
* Use the **Select files** option to attach the VAT card or the photocopy of the ID card you wish to enter in your account.
* To send the request, use the **Send** option.
Finally, our support team receives your request. We will check the data and modify the NIF if everything is correct. Once the change has been made, we will notify you via the submitted request, which you can access through the [Support](/en/user-guide/support/open-incident-report) channel.
# Error 500 or unresponsive page after an update
> What to do when a B2Brouter page shows error 500 or stops responding after an update: force a full reload, clear the browser cache or test in an incognito window.
After a B2Brouter update, a specific screen may fail while the rest of the application works normally. Typical symptoms:
* The page shows the error **500 — There has been a server error**.
* The screen stays loading and does not respond.
In most cases the problem is not in B2Brouter: the browser has kept files from the previous version of the application. Follow these steps in order until the page works again.
## 1. Force a full reload
[Section titled “1. Force a full reload”](#1-force-a-full-reload)
On the failing page, reload ignoring the browser cache:
* **Windows / Linux**: `Ctrl` + `F5` (or `Ctrl` + `Shift` + `R`)
* **Mac**: `Cmd` + `Shift` + `R`
This solves most cases.
## 2. Clear the browser cache
[Section titled “2. Clear the browser cache”](#2-clear-the-browser-cache)
If the error continues:
1. Open your browser’s option to clear browsing data.
2. Select **only** the cached images and files. Do not select cookies or history: you would lose your open sessions on other websites.
3. Set the time range to the last hour or the last 24 hours.
4. Clear the data, close the tab and open B2Brouter again.
## 3. Try an incognito window
[Section titled “3. Try an incognito window”](#3-try-an-incognito-window)
If the error continues, open B2Brouter in an incognito (private) window.
* If the page works there, some old data remains in your normal window: repeat step 2 with the time range **All time**.
* If the page also fails in the incognito window, the problem is not the browser cache. Go to the next step.
## 4. Contact support
[Section titled “4. Contact support”](#4-contact-support)
If none of the above solves it, [open an incident report](/en/user-guide/support/open-incident-report) and include:
* the exact URL of the page that shows the error
* the number of the affected document
* the browser you use and its version
* a screenshot of the error
# Sent invoices has not been received
> Learn what to check when an invoice appears as sent but the receiver says they have not received it.
If an invoice appears as **Sent** but the receiver says they have not received it, this does not necessarily mean that the invoice has been delivered correctly. Follow this checklist to identify the likely issue before opening an incident report.
## Check the actual invoice status
[Section titled “Check the actual invoice status”](#check-the-actual-invoice-status)
The first step is to review the status of the issued invoice. The status **Sent** only means that B2Brouter has performed the sending action, but it does not always mean that the receiver has received or accepted the document.
* Review [the status of issued invoices](/en/user-guide/sending-documents/status-issued-invoices).
* Keep in mind that **Sent** is not the same as **Read**, **Delivered**, **Accepted** or another final status depending on the network.
* If you send by email, there may be no further statuses beyond **Sent** or **Read**.
## Review the sending method configured on the contact
[Section titled “Review the sending method configured on the contact”](#review-the-sending-method-configured-on-the-contact)
Open the contact and check which **Invoice sending method** is configured.
* Verify whether the invoice was meant to be sent by email, open network, Peppol or B2Brouter.
* Confirm that the email address, transport or receiver identifier is correct.
* If needed, review [how to create and configure a contact](/en/user-guide/contacts/create-contact).
An incorrect sending method is one of the most common reasons why a document appears as sent but does not reach the expected recipient.
## Check whether the sending channel is the correct one for the receiver
[Section titled “Check whether the sending channel is the correct one for the receiver”](#check-whether-the-sending-channel-is-the-correct-one-for-the-receiver)
If the receiver is a public administration or a company that requires a specific network, you should validate that the invoice was sent through the right channel.
* If it is a Spanish public administration, check whether it was sent through **[FACe](https://proveedores.face.gob.es/proveedores/consultar-facturas)**, **FACeB2B** or another general entry point.
* If the contact is configured with **B2Brouter** transport but the receiver expects FACe or FACeB2B, the invoice will not arrive through the correct channel.
* If you use FACeB2B, also review the routing codes and the [DIRE code](/en/user-guide/routing-codes/dire-code).
## If the invoice was sent by email, check for a bounce or mailbox issue
[Section titled “If the invoice was sent by email, check for a bounce or mailbox issue”](#if-the-invoice-was-sent-by-email-check-for-a-bounce-or-mailbox-issue)
When the invoice is sent by email, the issue may be related to the recipient address or to a bounced message.
* Check that the contact email address is correct and up to date.
* Confirm with the receiver that they have checked spam, junk mail or internal filters.
* If your account uses email notifications or email sending, review the relevant configuration in [Company settings](/en/user-guide/account-settings/setting-up-your-company).
## If the invoice was sent through Peppol, validate the identifier and reception settings
[Section titled “If the invoice was sent through Peppol, validate the identifier and reception settings”](#if-the-invoice-was-sent-through-peppol-validate-the-identifier-and-reception-settings)
If the invoice was sent through Peppol, check that the receiver can be found on the network and that the identifier used is correct.
* Review [what the Peppol identifier is](/en/user-guide/routing-codes/peppol-identifier).
* Verify that the customer record contains the correct identifier.
* Also check that your Peppol configuration is active in [Company settings](/en/user-guide/account-settings/setting-up-your-company).
## When to open an incident report
[Section titled “When to open an incident report”](#when-to-open-an-incident-report)
If after these checks the invoice still appears as **Sent** but receipt cannot be confirmed, open an incident report and include at least the following information:
* Invoice number.
* Receiver contact.
* Sending method used.
* Current invoice status.
* Screenshot or detail of what the receiver reports.
You can do this from [How to open an incident report](/en/user-guide/support/open-incident-report).
# Transfer B2Brouter account ownership
> Learn how to request ownership transfer for a B2Brouter company already linked to another user or email address.
If you need to access an account or company that already exists in B2Brouter, you should not request a change to the account tax ID. In this case you must request an **ownership transfer**.
## Difference from changing the tax ID
[Section titled “Difference from changing the tax ID”](#difference-from-changing-the-tax-id)
An **ownership transfer** is used to recover or request access to a company that is already linked to another user.
If what you need is to modify the tax identifier of your current account, see the guide [How to change the account ID number](/en/user-guide/support/change-tax-identification-number).
## When you need to request an ownership transfer
[Section titled “When you need to request an ownership transfer”](#when-you-need-to-request-an-ownership-transfer)
You should follow this process when:
* You have registered with a new email address.
* You are creating your company in B2Brouter.
* The system detects that the tax ID already exists and tells you that the identifier is already associated with another account.
## How to request an ownership transfer
[Section titled “How to request an ownership transfer”](#how-to-request-an-ownership-transfer)
1. [Sign up to B2Brouter](/en/user-guide/getting-started/register-user) with the new email address you want to use from now on.
2. Access the new account.
3. Create the company and enter the corresponding country and tax ID.
4. Click **Continue**.
If the tax ID already exists in B2Brouter, the system will tell you and show you the available options to recover or request ownership of the account.
### If you recognise the email shown
[Section titled “If you recognise the email shown”](#if-you-recognise-the-email-shown)
If the screen shows part of the email address associated with the account and it is an address you know or can access:
1. Select **Yes, it is mine or it belongs to a person known to me**.
2. Click **Request transfer of ownership**.
3. Wait for the person who receives the notification to accept the transfer.
### If you do not recognise the email or cannot access it
[Section titled “If you do not recognise the email or cannot access it”](#if-you-do-not-recognise-the-email-or-cannot-access-it)
If you do not recognise the email address shown or you know it but cannot access it:
1. Select **No, I do not recognise this email address**.
2. In the **Comment** box, explain the reason for your request.
3. Use **Upload file** to attach the required documentation.
4. Click **Contact** to send the request to the support team.
## Required documentation
[Section titled “Required documentation”](#required-documentation)
For the support team to validate the request, you must provide:
* The reason for the ownership transfer request.
* An image of the correct company tax ID.
* A copy of the applicant’s ID or identity document.
## What happens next
[Section titled “What happens next”](#what-happens-next)
Our support team reviews the information and documents provided. If everything is correct, they accept the request and you receive an email notification confirming that you now have access to the company.