DGFiP e-Invoicing and e-Reporting
Guide to integrating the B2Brouter API and complying with the French RFE from your own system.
Since September 2026, all French companies must receive their invoices through a certified platform, and large companies and ETIs must also issue invoices and report TVA data through it. SMEs (PME) and micro-enterprises must do so from September 2027. 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 receiver’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 → activate DGFiP → create a contact → send your first invoice.
Regulatory context: The French e-invoicing reform mandates that all French companies use a certified Plateforme Agréée (PA) to transmit invoices and report TVA data to the DGFiP, starting September 2026 (September 2027 for issuing and e-reporting by SMEs and micro-enterprises). Since 21 February 2026, art. 289 bis CGI only provides for plateformes agréées: the government’s PPF (Portail Public de Facturation) is no longer an alternative. 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?”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 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). To bring in only one establishment while keeping that existing registration, see Scenario 2 in Account structure. 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 receiver 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 for initial API testing and payload validation. In sandbox, DGFiP Annuaire writes are simulated: activating a Tax Report Setting registers nothing at the PPF, so you need no PPF-registered identifiers. The SIREN and SIRET must still pass their checksum, however, because sandbox runs in the production environment. Only staging accepts any 9- or 14-digit number. For full end-to-end testing with the DGFiP QAS (qualification) environment, use B2Brouter’s staging environment as described below.
New to the API? Start with the API introduction: environments, API key and first request.
Before you start: choose your environment
Section titled “Before you start: choose your environment”| Environment | B2Brouter app | B2Brouter API | Chorus Pro portal |
|---|---|---|---|
| Production | app.b2brouter.net | https://api.b2brouter.net | chorus-pro.gouv.fr |
| Staging (test) | app-staging.b2brouter.net | https://api-staging.b2brouter.net | 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”Register at app.b2brouter.net 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 for real B2B invoices. During the pilot, before September 2026, the DGFiP was to clear QAS-period records before the reform went live, so invoices sent during the pilot did 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)”Register at app-staging.b2brouter.net 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 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 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”The Chorus Pro QAS portal 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:
- Go to qualif.chorus-pro.gouv.fr → Entreprise tab → Créer mon compte.
- You can use a temporary email address (e.g. temp-mail.io).
- Use any name; this account is purely for obtaining test identifiers.
- Check your inbox for the “Initialisation de mot de passe Chorus Pro” email and set your password (link valid 60 minutes).
- Log in → go to Domaines → Matelas de données → click Générer un matelas de données and confirm.
- 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.
- 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:
- Register at app-staging.b2brouter.net and activate your account.
- Create your company account using a SIREN from the CSV (see Create your company account).
- Subscribe to an eDocExchange plan (staging subscriptions are simulated — no charge).
- Enable DGFiP Tax Report Settings (see Activate the Tax Report Setting).
- ⚠️ Your company’s Annuaire registration is not immediate: publication goes out with the nightly batch and is live the next morning, both in staging and in production. This is how the DGFiP infrastructure works, not a B2Brouter delay. Until then you will not be able to send invoices.
- The following day: create a test contact using a second SIREN from the CSV (see Create a contact).
- Send your first invoice (see Issuing Invoices).
API credentials
Section titled “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.
Base URL: All examples below use
https://api-staging.b2brouter.net. For production, replace withhttps://api.b2brouter.net.
Account structure: Parent and organisational units
Section titled “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.
Which countries this guide covers. The account rules in this guide (parent and UOs, identifiers, Tax Report Setting) are for companies established in the French TVA territory: France (fr) and the DROM Guadeloupe (gp), Martinique (mq) and La Réunion (re). For an issuer established in any other overseas territory (Guyane, Mayotte, Polynésie française, Nouvelle-Calédonie, Saint-Pierre-et-Miquelon…) this guide does not establish the account configuration: contact support before creating the account.
The golden rule
Section titled “The golden rule”The parent account is always a SIREN. Any SIRET, Code Routage or Suffix (see 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 to pick your configuration.
Contacts
Section titled “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.
Identifiers and schemes
Section titled “Identifiers and schemes”Identifiers in B2Brouter
Section titled “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 | 842917361 |
0009 (9) | SIRET, 14 digits (SIREN + 5 digits), with two Luhn checks: one on the 14 digits and one on the leading SIREN | Establishment | 84291736100011 |
8040 | Suffix, alphanumeric [A-Za-z0-9_.-]{1,100} | UO without its own SIRET | SUF01, ADMIN |
SIREN and SIRET validation is arithmetic only: B2Brouter does not query the Sirene register or any other. An invented number that passes the check is accepted, and it can match a real company’s number. To build a test pair:
- Pick 8 digits and append the Luhn check digit: this is the SIREN.
- Append 4 establishment digits and the Luhn check digit computed over those 13 digits: this is the SIRET.
Every SIREN and SIRET on this page passes both checks, for example 842917361 and 84291736100011.
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”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_<CR> | Service inside an establishment |
0225 | SIREN_<SUFFIX> | UO without its own SIRET |
Each composition corresponds to one or two of the cases in the Case table. To see the API fields that produce it, see Add establishments as organisational units.
Scenarios and cases
Section titled “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.
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). |
| 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. |
Case table
Section titled “Case table”| Case | Organisational unit (UO) | Tax Report Setting | Annuaire address (pin_value) | When you need it |
|---|---|---|---|---|
| 1. Établissement Scenario 1 | SIRET | Parent:ONUO: ON1 | SIREN_SIRET | You have establishments and they all report from the company. |
| 2. Service Scenario 1 | SIRET + Code Routage | Parent:ONUO: ON1 | SIREN_SIRET_<CR> | You want to address a specific department inside an establishment. |
| 3. Suffix Scenario 1 | Suffix | Parent:ONUO: ON1 | SIREN_<SUFFIX> | You have a unit without its own SIRET that must receive and report at its own address. |
| 4. Établissement-only Scenario 2 | SIRET | Parent:OFFUO: 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:OFFUO: ON | SIREN_SIRET_<CR> | Like Case 4, but addressing a department inside that establishment. |
| 6. Issue-only Scenario 3 | Suffix with issue_only2 | Parent:OFFUO: ON | SIREN_<SUFFIX> | 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.
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).
Create your company account
Section titled “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.
Create the parent account with the SIREN
Section titled “Create the parent account with the SIREN”Create the account with POST /accounts. If it already exists, retrieve its id with GET /accounts and skip the creation.
Fields specific to a French account:
Section titled “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 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 |
Example request:
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": "FR22842917361", "tin_scheme": 9957, "cin_scheme": "0002", "cin_value": "842917361", "rounding_method": "half_up" } }'Example response:
{ "account": { "id": 83428, "name": "Exemplar SAS", "tin_value": "FR22842917361", "tin_scheme": 9957, "cin_scheme": "0002", "cin_value": "842917361", "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)”If your company structure requires UOs (see the case table), create each UO with POST /accounts 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).
Restrictions:
Section titled “Restrictions:”- A UO cannot carry
tin_valueortin_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:”| 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. A SIRET always starts with the 9 digits of the parent SIREN, by INSEE definition: if the parent is 842917361, valid SIRETs take the form 842917361XXXXX |
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”| UO type | Fields to send to POST /accounts | Annuaire result |
|---|---|---|
| Établissement (Cases 1 and 4) | parent_id (SIREN parent) + cin_scheme="0009" + cin_value=<SIRET> | SIREN_SIRET |
| Service (Cases 2 and 5) | parent_id (SIREN parent) + cin_scheme="0009" + cin_value=<SIRET> + routing_codes.cin1_scheme="224" + routing_codes.cin1_value=<CR> | SIREN_SIRET_<CR> |
| Suffix (Cases 3 and 6) | parent_id (SIREN parent) + cin_scheme="8040" + cin_value=<SUFFIX> | SIREN_<SUFFIX> |
Payload examples
Section titled “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.
Cases 1 and 2: Établissement UO with optional Service
Section titled “Cases 1 and 2: Établissement UO with optional Service”For an establishment without a Code Routage (Case 1), omit the
routing_codesblock.
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": "84291736100011", "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”The payload for Case 6 (Issue-only) is identical to Case 3: what separates them is not the
POST /accountsbut the Tax Report Setting you activate afterwards, withissue_only: true. That flag is only accepted on a UO withcin_scheme="8040". See Tax Report Setting modes.
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”This is the key onboarding step. When you create a Tax Report Setting with code: "dgfip", B2Brouter automatically:
- Registers your company in the PPF’s Annuaire: your identifier becomes discoverable by any platform in the French e-invoicing ecosystem.
- 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): 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:
- 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.- Tax Report Setting on the SIREN and on one or more SIRET UOs: the company is published both as
SIRENand as eachSIREN_SIRET(one Annuaire address per activated account). All active DGFiP Tax Report Settings under the same SIREN must share the samevat_regime: activating one with a different value returns422 dgfip_vat_regime_taken_per_siren.- 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 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_datetomorrow), 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:
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-10-01", "type_operation": "services", "vat_regime": "reel_normal_mensuel", "naf_code": "62", "enterprise_size": "eti", "email": "jane.doe@example.com" } }'Sample response:
{ "tax_report_setting": { "code": "dgfip", "start_date": "2026-10-01", "auto_generate": true, "auto_send": true, "enabled": true, "type_operation": "services", "vat_regime": "reel_normal_mensuel", "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”| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Must be "dgfip". |
start_date | date | No | When tax reporting begins. Must be tomorrow or later: today’s date is refused with 422. 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 carries an M-series process code (M1/M2/M4) and reports all the breakdowns (see Process codes). This setting can be updated after activation. |
naf_code | string | No | Optional; validated only when provided. The company’s NAF/APE code (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. |
enterprise_size | string | No | Optional; validated only when provided. The company’s size category as defined by INSEE. 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. |
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 and Franchise en base de TVA 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, and vat_regime is not required. Defaults to false. See Tax Report Setting modes. |
issue_only | boolean | No | When true, B2Brouter issues your invoices and tax reports while reception stays with your current PA. Only accepted on a Suffix UO (cin_scheme="8040"): on any other account the request returns 422 dgfip_issue_only_suffix_uo_only. Defaults to false. See issue_only. |
peppol_email_fallback | boolean | No | Sends the invoice by email in Factur-X format when the French recipient is registered in the Annuaire but confirmed absent from Peppol. Defaults to false: without it, the send is blocked instead. See Issue invoices. |
TVA regimes and transmission frequency
Section titled “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. |
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
Tax Report Setting modes
Section titled “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 | Parent or UO | yes | yes | no | none sent |
issue_only | 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”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. vat_regime is not required in this mode.
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-10-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”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.
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.
On activation, B2Brouter publishes your line at the PPF Annuaire (the French administration’s directory that routes Flux 1) with the SIREN_<SUFFIX> address, but creates no Peppol transport. The line the other PA publishes for your SIREN stays intact: whatever you publish, you publish under SIREN_<SUFFIX>, which is a different identifier.
Enabling Peppol issuing
Section titled “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: truepublishes 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_valuemust be the{parent SIREN}_{suffix}composition withpin_scheme: 225, the same address B2Brouter has already published on the Annuaire line.
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": "842917361_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.
If you later want to receive through B2Brouter as well, disable issue_only.
Change PA
Section titled “Change PA”Changing PA involves two separate registrations that behave differently:
- The Annuaire is always updated by the incoming PA, not the outgoing one.
- The Peppol registration is what can block the change: an identifier can only be active with one provider at a time. Until the registration is moved or deregistered, the incoming PA’s activation fails with
422.
There are three ways to unblock it:
- With a migration code, the automatic and recommended path, because it does not interrupt service. It is always issued by the PA that currently publishes the identifier, and used by the incoming PA on activation. At B2Brouter you can request one when leaving (see Leaving B2Brouter) and supply one when arriving (see Coming to B2Brouter).
- With Peppol deregistration: the outgoing PA unpublishes the identifier from Peppol. This frees it and the incoming PA can activate, but it leaves you without service until they publish it again. See Unpublishing, deregistering and migrating.
- Manually, when the outgoing PA offers neither: contact the incoming PA, and it is the one that claims the change from the outgoing one. Every French PA is required to offer this path, so having no code does not leave you stuck (see Coming to B2Brouter).
Unpublishing, deregistering and migrating
Section titled “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”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_settingswithpeppol_migration_code. This is the main route for France: it moves the identifier as part of the same activation.POST /transportsorPUT /transports/{code}withmigration_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.
If the other PA offers you neither a code nor a deregistration, open a support ticket and we will start the manual procedure to claim your portability.
Leaving B2Brouter
Section titled “Leaving B2Brouter”POST /accounts/{ACCOUNT_ID}/transports/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 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 GETs tells you whether a code has been issued and not yet cancelled. The code itself is never exposed in any GET.
Create a contact
Section titled “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. "FR22842917361") | 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_<CR> or SIREN_<SUFFIX> — see Identifier levels) |
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"anddocument_type_code: "xml.ubl.invoice.frcius.v1". Withoutpin_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 or the official Peppol Directory.
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,"0204"for the German Leitweg-ID of public administrations,"0106"for Dutch KVK) and the appropriatecountrycode. 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:
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": "987654324", "tin_scheme": 9957, "tin_value": "FR14987654324", "pin_scheme": "0225", "pin_value": "987654324", "transport_type_code": "peppol", "document_type_code": "xml.ubl.invoice.frcius.v1" } }'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 setpin_valueto theSIREN_SIRETcomposition (e.g.987654324_98765432400019) 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 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)”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:
{ "invoice": { "type": "IssuedInvoice", "contact": { "name": "Client Exemple SARL", "country": "fr", "currency": "EUR", "language": "fr", "cin_scheme": "0009", "cin_value": "98765432400019", "tin_scheme": 9957, "tin_value": "FR14987654324", "pin_scheme": "0225", "pin_value": "987654324_98765432400019", "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). From API version2026-04-20, the system rejects (422) a non-simplified inline contact that provides neithertin_valuenorcin_value.currencyandlanguageare mandatory on any contact; without them the creation fails regardless. The inline contact also acceptsrouting_codes.cin1_scheme/routing_codes.cin1_valueto 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)”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):
curl --request GET \ --url https://api-staging.b2brouter.net/directory/fr/0009/98765432400019 \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}'Resolved response: example:
{ "external_company": { "country": "fr", "cin_scheme": "0009", "cin_value": "98765432400019", "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). Use it to inspect before sending or to display the recipient’s status in your UI. It does not guarantee the route actually used: if the recipient later proves unreachable on Peppol, by default the send is blocked, or falls back to email if the account has opted into the Peppol → email fallback. See the note below.
GET Lookup Directory - API Reference
Annuaire verification and tax report generation
Section titled “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: trueor 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
- Staging (QAS) → qas.facturation.chorus-pro.gouv.fr/annuaire
⚠️ The
annuaire-entreprises.data.gouv.frwebsite is for general fiscal data; it does not guarantee DGFiP Annuaire registration — they are different databases.Since September 2026 this permissive behaviour no longer applies: tax declarations fail if the identifier is not listed as DGFiP-activable, even when the SIREN/SIRET is valid.
Issue invoices
Section titled “Issue invoices”Once your company is onboarded and the DGFiP Tax Report Setting is enabled, creating invoices works through the standard Invoice API, POST /accounts/{ACCOUNT_ID}/invoices. 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, series included, can have up to 35 characters (rule BR-FR-01). B2Brouter validates it when you create the invoice: a longer number or one with characters that are not allowed returns HTTP 422. The 20-character limit applies only to B2G invoices for Chorus Pro.
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”The most common case: a domestic French B2B invoice with standard TVA (20%).
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", "payment_method_text": "Virement bancaire", "payment_terms": "Net 30 jours à compter de la date de facture.", "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).
France-specific invoice fields
Section titled “France-specific invoice fields”Payment information
Section titled “Payment information”The payment fields carry payment data only:
| Field | EN 16931 | Description | Required |
|---|---|---|---|
remittance_information | BT-83 | Payment reference (for example, the invoice number) | No |
payment_method_text | BT-82 | Textual description of the payment method | No |
payment_terms | BT-20 | Payment terms (due date, early payment discount you grant, and so on) | No |
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 |
These fields are never used as a legal mention. Don’t write the late payment penalties, the 40 € compensation or the discount mention in them: send them as legal payment mentions.
An invoice with a positive amount due needs a due_date or payment_terms (rule BR-CO-25). Without either, the request returns HTTP 422 (“Due date can’t be blank”).
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 only place for them: don’t write them inpayment_method_textor in a legal mention.
Legal payment mentions (PMD, PMT, AAB)
Section titled “Legal payment mentions (PMD, PMT, AAB)”French invoices carry three legal payment mentions as invoice notes with a subject code (BT-21), one note per code (rules BR-FR-05 and BR-FR-06):
| Code | Mention | Default text |
|---|---|---|
PMD | Late payment penalties (pénalités de retard) | Tout retard de paiement engendre une pénalité exigible à compter de la date d’échéance, calculée sur la base de trois fois le taux d’intérêt légal. |
PMT | Fixed compensation of 40 € for collection costs (indemnité forfaitaire) | Indemnité forfaitaire pour frais de recouvrement en cas de retard de paiement : 40 €. |
AAB | Early payment discount, or that there is none (escompte) | Les règlements reçus avant la date d’échéance ne donneront pas lieu à escompte. |
The mentions are optional in the request. If you don’t send one, B2Brouter writes its default text in the invoice. Send your own text when the default doesn’t fit your conditions, for example when you apply a specific penalty rate or when you grant an early payment discount: without your AAB mention, the invoice states that there is no discount, even if your payment_terms say otherwise.
How to send them depends on the API version:
-
API version
2026-06-26or later: use thecoded_notesattribute, one entry per mention."coded_notes": [{ "subject_code": "PMD", "text": "Pénalités de retard : 12 % annuel, exigibles dès le lendemain de la date d'échéance." },{ "subject_code": "PMT", "text": "Indemnité forfaitaire pour frais de recouvrement : 40 €." },{ "subject_code": "AAB", "text": "Escompte de 2 % pour paiement sous 10 jours." }]On update, only the codes you send change: each code you send replaces the invoice’s notes with that code, and the other notes are kept. To remove a mention, send the code with
_destroy:{ "subject_code": "AAB", "_destroy": true }. An empty array, or omitting the attribute, leaves the notes as they are.coded_notesalso accepts other UNTDID 4451 codes (for exampleREG,AAI,TXD, orABLfor the company’s legal mentions such as RCS and share capital, rule BR-FR-07), which are emitted as their own notes.ACCisn’t accepted: usefa_clauses. -
Earlier versions: write the mentions as tags in
extra_info:#PMD#Pénalités de retard : 12 % annuel, exigibles dès le lendemain de la date d'échéance. #PMT#Indemnité forfaitaire pour frais de recouvrement : 40 €. #AAB#Escompte de 2 % pour paiement sous 10 jours.
For each code, B2Brouter takes the text from the coded_notes entry; if there isn’t one, from the tag in extra_info; and if there isn’t one either, it uses the default text.
On an invoice sent in a French format, the request returns HTTP 422 when:
- the same code (
PMD,PMT,AABorTXD) appears more than once (BR-FR-06); - the
PMTorAABtext is longer than 255 characters; - a
coded_notesentry has nosubject_codeor notext.
Importing from other formats (UBL, CII, Factur-X): you don’t have to do anything. Each
PMD,PMTorAABnote in the file is kept as its own note with the same text, andPaymentID(BT-83) andPaymentTerms(BT-20) keep their own values.
TVA-exempt lines (category E)
Section titled “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
commentis omitted on a category E line, the invoice is created (HTTP 200) but never transmitted to the PPF. The response will contain a non-emptyerrorsarray — 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 | E (exempt) | Franchise en base de TVA (sent to the PPF as Z) |
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 | — | AE (reverse charge) | 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 theVATEX-EU-*codelist (UNCL5305 / EN16931 extensions). The régime de la marge (margin scheme) usesVATEX-EU-F,VATEX-EU-IorVATEX-EU-Jon a categoryEline. Provide the applicable code in the linecomment.
Franchise en base de TVA (VATEX-FR-FRANCHISE)
Section titled “Franchise en base de TVA (VATEX-FR-FRANCHISE)”Invoices under the franchise en base de TVA regime carry tax category E with the exemption code VATEX-FR-FRANCHISE (rule BR-FR-CO-16). For franchise invoices, set percent: 0.0 and category: "E" with comment: "VATEX-FR-FRANCHISE" on each tax line. The invoice keeps category E: B2Brouter transcodes it to Z only in the data it sends to the PPF (rule BR-FR-MAP-08):
{ "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)”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.
{ "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”From API version 2026-04-20, reference the original invoice with invoice_references to issue a credit note:
"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.
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": "Pénalités de retard : trois fois le taux d'intérêt légal.", "payment_method_text": "Indemnité forfaitaire pour frais de recouvrement en cas de retard de paiement : 40 €.", "payment_terms": "Pas d'escompte pour paiement anticipé.", "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 withis_amend: true+amended_number+amended_dateinstead ofinvoice_references. These fields remain accepted in those versions for backward compatibility, but are now legacy: useinvoice_referencesfor new integrations.
Process codes
Section titled “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. To set a specific code (for example S7 or B7), send transaction_type_code on the invoice.
| 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 | TVA already collected (services): sale already declared through B2C e-reporting (or in the TVA return) and invoiced later to a business. See case 30 |
| B7 | Goods | TVA already collected (goods): sale already declared through B2C e-reporting (or in the TVA return) and invoiced later to a business. See case 30 |
Mixed invoices in Flux 1: with
type_operation: "mixed", the Flux 1 tax report carriesM1,M2orM4and reports all the invoice breakdowns. There is noM7code: a mixed invoice whose TVA was already reported via B2C e-reporting carriesS7. The services-only rule applies to payment data (212), not to Flux 1.
Document formats
Section titled “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”If your system already generates Factur-X or UBL XML invoices, submit them directly using the document import endpoint:
POST /accounts/{id}/invoices/importSet 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”France uses a Y-/5-corner model, not clearance. The invoice is transmitted to the receiver regardless of whether a Flux 1 tax report is registered; the PPF does not pre-clear invoices before they reach the receiver. 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 receiver.
States ↔ CDV for issued invoices
Section titled “States ↔ CDV for issued invoices”| State | CDV | Origin | Description |
|---|---|---|---|
sending | — | B2Brouter (C1) | The invoice has been created and is queued for PPF transmission |
sent | 200:Déposée | B2Brouter (C2) | The invoice has been sent to the receiver and the PPF successfully |
registered | 202:Reçue | Receiver (C3) | The receiver’s system has received the invoice |
read | 204:Prise en charge | Receiver (C4) | The receiver has viewed the invoice |
accepted | 205:Approuvée | Receiver (C4) | The receiver has approved the invoice |
accepted1 | 206:Approuvée partiellement | Receiver (C4) | The receiver has partially approved the invoice |
annotated | 207:En litige | Receiver (C4) | The receiver has disputed the invoice |
refused | 210:Refusée | Receiver (C4) | The receiver has rejected the invoice2 |
paid | 212:Encaissée | Issuer (C1) | You have recorded the invoice payment. B2Brouter sends 212 for services only. See Encaissée state: payment confirmation. |
error | — | Various | Pre-send validation or transmission failed. The errors field contains the reason. See How to handle errored invoices. |
1 CDV 206 sets the same state as 205: accepted. The invoice state does not distinguish a full approval from a partial one. The amount the receiver approved is recorded as metadata on the corresponding event, not as an invoice field.
2 The invoice payload carries refuse_reason_code with the structured rejection reason.
How to handle errored invoices
Section titled “How to handle errored invoices”Before sending:
For an invoice to be sent, it has to pass schematron validation. If it does not, the send returns HTTP 422, the invoice is left in error state and no tax report is created. The message cites the rule code that failed, for example G1.24 for an invalid TVA rate. The errors field holds the rejection reason: edit the invoice with a PATCH and send it again, or delete it and create a new one. There is no retry endpoint: the path is correct-and-resend.
After sending:
DELETE and PATCH return 422: the invoice is inside the receiver’s lifecycle and the correction is made with a credit note. The exception is a failed deposit: as long as all of its tax reports are in error or refused state and the invoice has not reached the receiver, you can still correct it with a PATCH and send it again. DELETE still returns 422: an invoice with a tax report, in any state, cannot be deleted.
Tracking invoice states
Section titled “Tracking invoice states”Webhooks (recommended)
Section titled “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
Polling
Section titled “Polling”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'Download the original Invoice document
Section titled “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:
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 and Transaction: View_as.
Encaissée state: payment confirmation
Section titled “Encaissée state: payment confirmation”Encaissée is the last state of the CDV cycle of a French B2B invoice. The issuer sends it, not the receiver: B2Brouter sends it on your behalf when you record a payment of the invoice (see the full CDV mapping in the Invoice states table). Under art. 41 septies G of annex IV to the CGI, it only applies to services: B2Brouter does not send it for supplies of goods or for reverse-charge breakdowns (category AE), and on a mixed invoice it reports only the service breakdowns. On a received invoice, marking it as paid sends 211 to the supplier (see How to respond).
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) |
Mark an invoice as paid via API
Section titled “Mark an invoice as paid via API”When payment is confirmed, mark the invoice at the PPF with a mark_as call:
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 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.
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-02or later.
For a single full payment, you can also call mark_as directly with state: "paid" (see 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: trueinstead ofamountto 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 explicitamountinstead, 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
paidstate.- If the payment generated a Flux 10 tax report (cross-border or B2C invoice), a
PUT/DELETEon that payment returns HTTP 422 because the already-submitted declaration is locked. The domestic FR-FR case (CDV212, no additional tax report) has no such lock.
Receiving Invoices
Section titled “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.
Responding to a received invoice
Section titled “Responding to a received invoice”As the receiver, you respond with POST /invoices/{id}/mark_as. The general mark_as mechanics, common to every country, are in Switch invoice state.
Each state transition emits the corresponding CDV to the issuer. Automatic rejections (213) are the exception: they require no call, because B2Brouter derives the DGFiP reason code from the validation errors (REJ_COH when there are none to classify).
How to respond: call, state and CDV
Section titled “How to respond: call, state and CDV”| State | CDV | Call | Description |
|---|---|---|---|
read | 204:Prise en charge | state: "read" | You take the invoice into your system |
accepted | 205:Approuvée | state: "accepted" | You approve the invoice |
accepted | 206:Approuvée partiellement | state: "accepted" + amount + amount_code1 + reason | You approve the invoice partially |
annotated | 207:En litige | state: "annotated" + dispute: true2 + reason | You dispute the invoice |
refused | 210:Refusée | state: "refused" + reason_code3 | You reject the invoice |
paid | 211:Paiement transmis | state: "paid" | You tell the supplier you have paid the invoice. Not sent to the DGFiP |
The reason is mandatory for 206 and 207: without it, the call returns 422.
1 The amount_code values are MAP, MAPTTC, MNA and MNATTC.
2 Without dispute: true, the annotated state only records internally and sends no CDV.
3 reason_code is optional in the call, but a 210 always carries a reason: if you send none, B2Brouter sets NON_CONFORME. See Rejection codes.
Example partial approval request:
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", "amount": 250.00, "amount_code": "MAP", "reason": "One line does not match the purchase order" }'Rejection codes
Section titled “Rejection codes”When you reject an invoice you can add a reason_code with the DGFiP’s structured reason: 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 and DOUBLE_FACT.
The API also accepts AUTRE, but the DGFiP does not allow it in a rejection.
The DGFiP refuses a rejection without a comment. If you send no reason, B2Brouter writes the comment from the official label of the reason_code, or of the default code NON_CONFORME when you send none, and the invoice history shows “Refusal comment generated from the reason code”. In the web app the reason is mandatory.
Example request:
POST /invoices/{id}/mark_as{ "state": "refused", "reason": "Incorrect total amount", "reason_code": "MONTANTTOTAL_ERR"}Example response (extract):
{ "invoice": { "id": 12345, "state": "refused", "refuse_reason": "Incorrect total amount", "refuse_reason_code": "MONTANTTOTAL_ERR" }}Check the state of a Tax Report
Section titled “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: Recevable) |
registered | ✅ Terminal: Invoice accepted and registered by the DGFiP (CDV condition 250) |
refused | ❌ Terminal: Rejected by the DGFiP (CDV condition 251) |
error | ❌ Terminal: Transmission or PPF processing error (e.g. CDV condition 501: Irrecevable) |
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: Recevable) |
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 (e.g. CDV condition 501: Irrecevable) |
Polling
Section titled “Polling”Query the state using the tax report ID from tax_report_ids in the invoice response:
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
e-Reporting (Flux 10)
Section titled “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
<Report>and only need an accredited channel to deposit it, see Flux 10: deposit a file your own system generates instead.
B2C transactions
Section titled “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)”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 contact on the invoice, not your account): 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 carryIdentificationCode FR, exactly like mainland France. The other overseas territories (Guyanegf, Mayotteyt, Polynésie françaisepf, Nouvelle-Calédonienc, TAAFtf, Saint-Pierre-et-Miquelonpm) fall outside the French TVA territory and follow the Flux 10 e-Reporting path. This paragraph is about the counterparty of the invoice; for the account that issues, see Account structure.
Flux 10 Ledgers
Section titled “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), 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”Each ledger is identified by two internal dimensions:
- DGFiP role (
SEorBY):SE(Seller / émission): for sales you issue.BY(Buyer / réception): for intracommunity purchases you declare as the buyer.
- Mode (
transactionsorpayments):transactions: invoice ledgers (process codes S1 / B1 / M1), document typexml.ledger.dgfip.transactions.payments: payment ledgers (process codes S2 / B2 / M2, TVA on services accrues at collection), document typexml.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.
Further resources
Section titled “Further resources”Guides:
Section titled “Guides:”- Supported Use Cases: the 44 RFE use cases (XP Z12-014) and which ones B2Brouter covers.
- Tax Report Settings Guide: the Tax Report Settings endpoint in depth, for every country, not just France.
- Receive, integrate and manage received invoices: how to respond to a received invoice with
mark_as, on any network.
Use cases:
Section titled “Use cases:”- Send invoices to Chorus Pro (B2G): circuit for invoices addressed to the French public sector.
- Flux 10: deposit a file your own system generates: deposit at the PPF an e-Reporting file you generated in your own system.