Skip to content
Log in

DGFiP e-Invoicing and e-Reporting

From September 2026, all French companies must route their invoices and VAT 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 youDetails
PPF registrationPublishes your SIREN/SIRET to the Annuaire automatically on activation
Flux 1 — B2B e-invoicingGenerates UBL/CII/Factur-X, transmits to PPF, routes to buyer’s platform
Flux 6 — Invoice lifecycleManages CDAR status messages (Déposée, Reçue, Approuvée, Refusée, Encaissée)
Flux 10 — e-ReportingAggregates B2C and cross-border B2B transactions (intra-EU and extra-EU) into daily Ledgers sent to PPF
Peppol 0225 receptionReceives invoices from any French or Peppol-connected platform
Document generationYou 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 formatsJSON REST API, Factur-X PDF/A-3 (with embedded CII XML), UBL 2.1 XML, CII XML
Legal archivingAll 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

To integrate, you need 4 steps: create your accountactivate DGFiPcreate a contactsend your first invoice.


Regulatory context: The French e-invoicing reform 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 VAT 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.

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 (Step 2) transfers the Annuaire entry to B2Brouter automatically. 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 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 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.

EnvironmentB2Brouter appB2Brouter APIChorus Pro portal
Productionapp.b2brouter.nethttps://api.b2brouter.netchorus-pro.gouv.fr
Staging (test)app-staging.b2brouter.nethttps://api-staging.b2brouter.netqualif.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.

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 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.

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 (activating B2Brouter in production would transfer the entry)
  • 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 VAT 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:

  1. Go to qualif.chorus-pro.gouv.frEntreprise 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.
  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 DomainesMatelas 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 and activate your account.
  2. Create your company account using a SIREN from the CSV (see Step 1).
  3. Subscribe to an eDocExchange plan (staging subscriptions are simulated — no charge).
  4. Enable DGFiP Tax Report Settings (see Step 2).
    • ⚠️ 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 Step 3).
  6. Send your first invoice (see Issuing Invoices).

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 with https://api.b2brouter.net.


Account structure: Parent and organisational units

Section titled “Account structure: Parent and organisational units”

A French company with several establishments or services is modelled in B2Brouter as a parent account (the main entity) and, optionally, one or more organisational units (UOs) hanging from the parent. Invoices are always issued from a parent account or from a specific UO, but the integration group is shared.

The parent account is always at SIREN level (cin_scheme="0002", the legal entity). A SIRET is never a parent: creating a French parent with cin_scheme="0009" returns 422 parameter_matriu_must_be_siren. Establishments (SIRET) and services are always modelled as UOs under that SIREN parent.

What changes depending on your situation is not the parent’s level but where you activate the DGFiP Tax Report Setting:

  • If your SIREN is not yet registered with any other Plateforme Agréée (PA): activate the Tax Report Setting on the SIREN parent. UOs without their own Tax Report Setting report through the parent automatically. This is the standard flow (Scenario 1).
  • If your SIREN is already registered with another PA, and you only want to bring one specific establishment into B2Brouter: keep the parent at SIREN but do not activate the Tax Report Setting on it (that would transfer the existing Annuaire entry). Instead, create the establishment as a SIRET UO and activate the Tax Report Setting on that UO, so the SIREN’s existing registration is left untouched (Scenario 2).

This decision drives where reporting is activated, not the identifier of the parent (which is always SIREN). If unsure whether your SIREN is already registered, check the Annuaire first with the Directory lookup.

Legacy accounts (normalisation): until June 2026, the platform allowed creating a parent account with a SIRET. If you have accounts created that way, they 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 SIREN parent.

In B2Brouter a French account or contact is defined by a principal identifier (cin_scheme + cin_value) and, optionally, a sub-identifier (routing_codes.cin1_scheme + routing_codes.cin1_value) when you need to address a specific service inside an establishment.

Account / UO principal identifier (cin_scheme + cin_value):

cin_schemeIdentifierFormatExample
0002 (2)SIREN — legal entity9 digits + Luhn123456789
0009 (9)SIRET — establishment14 digits (SIREN + 5 digits) + Luhn12345678900012
8040Suffix — UO without its own SIRETAlphanumeric [A-Za-z0-9_\-\/]+SUF01, ADMIN

Sub-identifier (routing code) — only for SIRET UOs with several internal services:

routing_codes.cin1_schemeIdentifierFormatExample
224Code Routage — service inside a SIRETAlphanumeric [A-Za-z0-9_\-\/]+ (min. 1 alphanumeric char)COMPTA, SERV01, A123

EAS Peppol of the FR participant: the electronic identifier on the Peppol network is always 0225 (FRCTC Electronic Address). Schemes 224 (Code Routage) and 8040 (Suffix) are internal directory sub-identifiers and are not published as standalone Peppol EAS.

Suffix vs Code Routage — The Suffix (8040) is the principal cin_scheme of a UO without its own SIRET. The Code Routage (224), in contrast, always goes in routing_codes.cin1_scheme="224" on a UO with SIRET — never as a principal cin_scheme.

Important: a SIRET on its own (without a SIREN in front) is not a valid published identifier. A SIRET always lives on a UO under the SIREN parent; the published composition is SIREN_SIRET, where the SIREN comes from the parent account.

See the 4 final identifier formats at the Annuaire
Annuaire compositionMeaning
SIRENWhole entity (Parent)
SIREN_SIRETEstablishment (Case 1 / Case 4)
SIREN_SIRET_<CR>Service inside an establishment (Case 2 / Case 5)
SIREN_<SUFFIX>UO without its own SIRET (Case 3 / Case 6)

None of these compositions are issue-only by themselves — including the bare SIREN row. Whether an address also receives invoices (a 0225 transport is published for it) depends solely on the issue_only flag on that Tax Report Setting, not on which composition it uses. See the note on issue_only under Scenario 3 below.

To see the exact API fields that produce each format, see the Cases and topologies table below.

Your structure depends on two factors: whether your SIREN is free of other PAs, and whether you want to issue or also receive. Three scenarios cover all standard configurations.

Section titled “Scenario 1 — SIREN as parent (recommended flow)”

If your SIREN is not yet registered with any other PA, the parent is always at SIREN level. Everything else is modelled as UOs.

#CaseParent accountUO (office)Annuaire address
1A — Établissementcin_scheme="0002" (SIREN)cin_scheme="0009" (SIRET)SIREN_SIRET
2A — Servicecin_scheme="0002" (SIREN)cin_scheme="0009" (SIRET) + routing_codes.cin1_scheme="224"SIREN_SIRET_<CR>
6Suffix UOcin_scheme="0002" (SIREN)cin_scheme="8040" (Suffix)SIREN_<SUFFIX>
  • Case 1 (A Établissement) — Model specific establishments separately (one SIREN parent + N SIRET UOs).
  • Case 2 (A Service) — Like Case 1, plus internal services within an establishment identified by a Code Routage.
  • Case 6 (Suffix UO) — Internal UOs (departments, administrative units) that don’t correspond to legal establishments with their own SIRET.

Scenario 2: SIREN parent, reporting on the establishment UO (SIREN already with another PA, bringing one specific establishment)

Section titled “Scenario 2: SIREN parent, reporting on the establishment UO (SIREN already with another PA, bringing one specific establishment)”

If your SIREN is already registered with another PA and you want to bring one specific establishment into B2Brouter without disturbing that existing registration, the parent is still at SIREN level (a SIRET is never a parent). The difference from Scenario 1 is only where the Tax Report Setting is activated: not on the SIREN parent (which would transfer the existing Annuaire entry), but on the SIRET UO. The UO publishes independently (see organisational units with their own Tax Report Setting), so the SIREN’s registration with the other PA is left untouched.

The topology is the same as Cases 1 and 2 (SIREN parent + SIRET UO, optionally with a Code Routage service):

#CaseParent accountUO carrying the Tax Report SettingAnnuaire address
4C — Établissement-onlycin_scheme="0002" (SIREN), no TRScin_scheme="0009" (SIRET) + own Tax Report SettingSIREN_SIRET
5C — + Servicecin_scheme="0002" (SIREN), no TRScin_scheme="0009" (SIRET) + routing_codes.cin1_scheme="224" + own Tax Report SettingSIREN_SIRET_<CR>
  • Case 4 (C Établissement-only) — A single SIRET UO carries the Tax Report Setting; the SIREN parent has none.
  • Case 5 (C + Service) — Like Case 4, plus internal services on that establishment addressed by a Code Routage (routing_codes.cin1_scheme="224" on the SIRET UO).

Scenario 3 — Issue-only (issue only, without publishing transport 0225)

Section titled “Scenario 3 — Issue-only (issue only, without publishing transport 0225)”

If your SIREN is already with another PA and you only want to issue from B2Brouter (without publishing a 0225 transport that would conflict with the other PA’s), use the issue_only=true flag on the parent account. B2Brouter will not register any 0225 transport with the PPF.

Pure case — only issue_only=true (no suffix):

  • cin_scheme="0002" (SIREN) + issue_only=true
  • No UOs, no suffix
  • Annuaire address: SIREN (the rest handled by the other PA)

Sub-case with suffix: when you also need internal addressing SIREN_<SUFFIX>, create the suffix as a Suffix UO (Case 6), not on the parent:

  • parent_id = SIREN parent, cin_scheme="8040", cin_value="<SUFFIX>", with issue_only=true on that UO
  • A suffix (like any SIRET or Code Routage) is always a UO, never placed on the parent itself
  • Annuaire address: SIREN_<SUFFIX>

issue_only is independent of the suffix: you can set issue_only=true on the SIREN parent with no suffix at all (pure issue-only). A suffix, when you need one, is a separate Suffix UO (Case 6).

A suffix does not mean issue-only. The suffix (SIREN_<SUFFIX>) is only an addressing detail — whether it also receives invoices depends exclusively on issue_only, not on the presence of the suffix itself:

  • issue_only=true + suffix → the SIREN_<SUFFIX> Ligne is created in the Annuaire, but no 0225 transport is published for it → issue-only, no reception. Use this when reception for that address is already handled by another PA.
  • No issue_only (default) + suffix → the 0225 transport is published for SIREN_<SUFFIX> → issue and receive at that same suffixed address, exactly like any other address.

If you want to both issue and receive at a suffixed address, do not set issue_only on that Tax Report Setting.

Contacts: same model, stricter golden rule

Section titled “Contacts: same model, stricter golden rule”

Contacts in B2Brouter (French clients and suppliers) follow exactly the same hierarchy (parent + UOs) and the same cin_scheme values (SIREN / SIRET / Code Routage / Suffix). Manage contacts exactly like your own accounts: the contact parent must be the SIREN, and any SIRET, Code Routage or Suffix must be a UO. Although the system technically allows a contact parent at another level, doing so risks breakages when you later add UOs of the same client, so always keep the SIREN as the parent.

Golden rule for contacts: the parent is always the SIREN; model any SIRET, Code Routage or Suffix as UOs, exactly as for accounts.

With the parent at SIREN, the cases from the 3 scenarios apply to contacts (UOs can be SIRET, Code Routage or Suffix depending on the client’s structure).

To create a contact UO, use the same endpoint as for a regular contact — POST /accounts/{ACCOUNT_ID}/contacts — adding parent_id pointing to the parent contact. tin_value and tin_scheme are inherited from the parent and cannot diverge.

Legacy contacts (normalisation): as with accounts, until June 2026 a contact could be created with a SIRET as its parent. If you have contacts created that way, normalise them: replace the parent contact’s identifier with the SIREN (cin_scheme="0002") and create the corresponding SIRET as a UO under that SIREN contact.


Step 1: Retrieve or Create your Company Account

Section titled “Step 1: Retrieve or Create your Company Account”

Structure reminder: the parent account is always cin_scheme="0002" (SIREN) — see Account structure. What differs between scenarios is not the parent identifier but where you activate the Tax Report Setting (on the SIREN parent in Scenario 1, on the SIRET UO in Scenario 2). Establishments are always created as UOs in Step 1b.

If you already have a B2Brouter account for your company, retrieve its id with the List Accounts endpoint and skip the creation step.

If you haven’t yet created one, use the Create Account endpoint (eDocSync/API path) or the web UI onboarding wizard (eDocExchange path). Both produce the same result.

When creating a French company account:

  • cin_scheme: always "0002" (SIREN, 9 digits) for the parent account. A SIRET is not accepted as a parent identifier: creating a French parent account with cin_scheme "0009" returns 422 parameter_matriu_must_be_siren. Establishments (SIRET) are created as organisational units under the SIREN parent (see Step 1b).
  • cin_value: The SIREN of your company (9 digits).
  • tin_value: French VAT number in the format FR{kk}{siren}, where kk is the two-digit checksum (e.g. FR32123456789). It is mandatory for DGFiP invoicing; you do not need to compute the checksum yourself. If you provide only the SIREN or only the VAT number, B2Brouter derives the other automatically when you activate the DGFiP Tax Report Setting. This derivation happens only on activation: if you neither provide the VAT number nor activate the Tax Report Setting, it stays empty and invoicing will fail, so activate the setting to avoid problems.
  • country: "fr".

Example request:

Terminal window
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"
}
}'

Sample response:

{
"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"
}
}

POST Account - API Reference

Step 1b — Create organisational units (optional)

Section titled “Step 1b — Create organisational units (optional)”

If your company structure requires UOs (see Cases 1, 2, 4, 5 and 6 in Topologies), create each UO with POST /accounts adding parent_id (the parent account id created in Step 1a).

This feature requires an API version that supports UOs with parent_id. Check the API changelog for the minimum version.

Restrictions:

  • parent_id is mandatory to create a UO.
  • A UO cannot include tin_value or tin_scheme (they are inherited from the parent).
  • A UO cannot be parent of another UO (only one level of nesting).

Mapping UO ↔ Annuaire result:

UO typeFields to send to POST /accountsAnnuaire result
Établissement (Case 1)parent_id (SIREN parent) + cin_scheme="0009" + cin_value=<SIRET>SIREN_SIRET
Service (Case 2)parent_id (SIREN parent) + cin_scheme="0009" + cin_value=<SIRET> + routing_codes.cin1_scheme="224" + routing_codes.cin1_value=<CR>SIREN_SIRET_<CR>
Establishment UO carrying its own TRS (Scenario 2, Cases 4/5)Same fields as Case 1 (or Case 2 for a service): parent_id (SIREN parent) + cin_scheme="0009" + cin_value=<SIRET> [+ routing_codes.cin1_scheme="224" + routing_codes.cin1_value=<CR> for a service]. The difference is that this UO gets its own Tax Report Setting while the SIREN parent has none.SIREN_SIRET / SIREN_SIRET_<CR>
Suffix (Case 6)parent_id (SIREN parent) + cin_scheme="8040" + cin_value=<SUFFIX>SIREN_<SUFFIX>

Mandatory fields on every UO payload: besides parent_id and cin_*, you must include country, email, address, city, postalcode and province. Without these, the creation returns HTTP 422 (“Region/Province/Country can’t be blank”). tin_value and tin_scheme are inherited from the parent and must not be repeated.

Example — Établissement UO with optional Service (Cases 1 and 2):

Terminal window
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"
}
}'

For an establishment without Code Routage (pure Case 1), omit the routing_codes block.

Example — Establishment UO that carries its own Tax Report Setting (Scenario 2, Cases 4/5):

The UO is created exactly like Case 1/2 (below, a service variant with a Code Routage). The difference in Scenario 2 is that you then activate a Tax Report Setting on this UO (not on the SIREN parent), so the SIREN’s existing Annuaire registration with another PA is preserved (see Step 2).

Terminal window
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 — Accounting Service",
"parent_id": {PARENT_ACCOUNT_ID},
"cin_scheme": "0009",
"cin_value": "12345678900012",
"routing_codes": { "cin1_scheme": "224", "cin1_value": "COMPTA" },
"country": "fr",
"email": "accounting@example.com",
"address": "1 Rue de la Paix",
"city": "Paris",
"postalcode": "75001",
"province": "Île-de-France"
}
}'

The UO carries the SIRET as principal cin_scheme="0009" and, for a service, identifies it via routing_codes.cin1_value. The Code Routage never goes as principal cin_scheme. For an establishment-only UO (Case 4), omit the routing_codes block.

Example — Suffix UO (Case 6):

Terminal window
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"
}
}'

The Issue-only cases (Scenario 3) are not created as UOs — they are the SIREN parent itself, defined in Step 1a with cin_scheme="0002" (and issue_only=true, plus an optional 8040 suffix). Cases 4 and 5 (Scenario 2), by contrast, are UOs: a SIREN parent plus a SIRET UO that carries its own Tax Report Setting.


Step 2: Enable Tax Report Settings for DGFiP

Section titled “Step 2: Enable Tax Report Settings for DGFiP”

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 SIREN/SIRET becomes discoverable by any platform in the French e-invoicing ecosystem.
  2. Creates a Peppol 0225 transport — enabling your company to receive electronic invoices from any Peppol-connected platform in France.

⚠️ Transport replacement: If the account you activate on already has a Peppol transport, it will be replaced by the new 0225 (FRCTC Electronic Address) transport during activation. 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; a UO without one reports through the parent. Three configurations:

  1. TRS on the SIREN only: the company is published as SIREN, and establishments report through it (standard case, Scenario 1).
  2. TRS 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), so the whole entity and specific establishments are each addressable.
  3. TRS 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.

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:

Terminal window
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:

{
"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"
}
}
FieldTypeRequiredDescription
codestringYesMust be "dgfip".
start_datedateYesWhen tax reporting begins. Must be today or a future date. Defaults to tomorrow if omitted.
type_operationstringYesDefault 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). This setting can be updated after activation.
naf_codestringYesThe 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_sizestringYesThe 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€).
reason_vat_exemptstringNoDefault VAT 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 VAT exemption regime. See VAT-exempt lines and Franchise en base de TVA for the full list of accepted codes.
emailstringNoContact email for tax-related notifications.
auto_generatebooleanNoAlways true for DGFiP (legal obligation). Cannot be changed.
auto_sendbooleanNoAutomatically transmit tax reports to the PPF. Defaults to true.
enabledbooleanNoWhether the setting is active. Defaults to true. Annuaire registration only occurs when true.
annuaire_onlybooleanNoWhen 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 Reception-only mode.

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

If your account only needs to receive electronic invoices (not issue them) — for example, a company not yet required to issue but that wants to be registered with the Annuaire so its suppliers can send invoices via Peppol — you can activate the DGFiP Tax Report Setting with annuaire_only: true.

What it activates:

  • Registers your company with the PPF Annuaire (visible to issuers).
  • Creates the Peppol 0225 transport for reception.

What it skips compared to a full activation:

  • No Flux 1 tax reports are generated when issuing invoices.
  • No CDAR messages (Déposée → Reçue → Approuvée… cycle) are sent.
  • enterprise_size and naf_code are not required.

This mode is useful for receiving companies in evaluation, or for entities that have a different invoicing provider but want to receive via B2Brouter. When you want to start issuing, update the Tax Report Setting to full mode with a PATCH (see Tax Report Settings Guide).

Example — reception-only activation:

Terminal window
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"
}
}'

A French B2B contact needs routing identifiers (how the invoice reaches the recipient) and tax identification (how the recipient appears in the UBL XML).

FieldValuePurpose
cin_scheme"0009" (SIRET) or "0002" (SIREN)Organisation ID — used to look up the recipient in the Annuaire
cin_valueSIRET or SIRENOrganisation ID — the identifier registered in the Annuaire
tin_scheme9957Tax ID — ISO 6523 code for French fiscal identifier
tin_valueFR{kk}{siren} (e.g. "FR78225214234")Tax ID — French VAT number in the UBL XML
pin_scheme"0225"EAS Peppol of the FR participant (FRCTC Electronic Address) — required for French contacts
pin_valueRecipient’s Annuaire compositionContact’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" 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 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, "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:

Terminal window
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",
"is_client": true,
"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"
}
}'

POST Contact - API Reference

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 topologies.

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):

Terminal window
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}'

Resolved response — example:

{
"external_company": {
"country": "fr",
"cin_scheme": "0009",
"cin_value": "98765432100011",
"information_flags": ["FR_ASSUJETTI_ACTIVE"],
"source": ["peppol", "annuaire"]
}
}

France-specific fields:

FieldValuesMeaning
information_flagsFR_ASSUJETTI_ACTIVERecipient is registered and active at the PPF Annuaire
information_flagsFR_ASSUJETTI_INACTIVERecipient has an out-of-date registration or no assigned PA
information_flagsFR_ASSUJETTI_UNKNOWNThe PPF has no conclusive information (e.g. 404)
sourcearray 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.

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: 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:

⚠️ 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.


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.

The most common case: a domestic French B2B invoice with standard TVA (20%).

Terminal window
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).

POST Invoice - API Reference

Payment information (required native fields)

Section titled “Payment information (required native fields)”

Three payment fields are mandatory for French B2B e-invoices (IssuedInvoice). Provide them as direct API fields:

FieldDGFiP fieldDescriptionRequired
remittance_informationPMDPayment reference and legal mentions (company registration, share capital, RCS)Yes — B2B domestic and cross-border
payment_method_textPMTTextual description of the payment methodYes — B2B domestic and cross-border
payment_termsAABDue date, late payment penalties, discount conditionsYes — B2B domestic and cross-border
bank_account_idId 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:

#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.

When a line has category: "E" (VAT-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 VAT exemption codes (BT-121)

CodeLegal referenceTax categoryDescription
VATEX-FR-FRANCHISEArt. 293 B CGIZ (zero-rate)Franchise en base de TVA
VATEX-FR-CNWVATE (exempt)Non-established in France
VATEX-FR-AEE (exempt)Autoliquidation (reverse charge)
VATEX-FR-CGI261-1Art. 261-1° CGIE (exempt)Soins et services médicaux
VATEX-FR-CGI261-2Art. 261-2° CGIE (exempt)Services paramédicaux
VATEX-FR-CGI261-3Art. 261-3° CGIE (exempt)Enseignement scolaire, universitaire et formation professionnelle
VATEX-EU-F / -I / -JE (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)”

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:

{
"taxes_attributes": [
{
"name": "TVA",
"percent": 0.0,
"category": "E",
"comment": "VATEX-FR-FRANCHISE"
}
]
}

Some operations fall outside the scope of VAT 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 VAT field): the line carries no VAT.
  • Débours (reimbursable expenses advanced on behalf of the client): passed through without VAT.
{
"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.

To issue a credit note, set is_amend: true and reference the original invoice using amended_number and amended_date. B2Brouter generates a UBL CreditNote with type code 381 and includes the billing reference.

Terminal window
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,
"is_amend": true,
"amended_number": "FA-2026-0048",
"amended_date": "2026-09-15",
"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" }
]
}
]
}
}'

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 CodeType of OperationDescription
S1ServicesStandard invoice for services
B1GoodsStandard invoice for goods
M1MixedStandard invoice for mixed operations
S2ServicesPaid invoice for services
B2GoodsPaid invoice for goods
M2MixedPaid invoice for mixed operations
S4ServicesInvoice with payments on account (services)
B4GoodsInvoice with payments on account (goods)
M4MixedInvoice with payments on account (mixed)
S7ServicesCorrection of a registered invoice (services)
B7GoodsCorrection 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 Type CodeFormatDescription
xml.ubl.invoice.frcius.v1UBL XMLFrance CIUS Peppol invoice (Flux 1 / Annuaire)
xml.cii.cross_industry_invoice.frcius.v1CII XMLFrance CIUS CII invoice
xml.cii.cross_industry_invoice.facturx.fr.all_profiles.v1Factur-X (CII XML)France CIUS Factur-X invoice (all profiles)

If your system already generates Factur-X or UBL XML invoices, submit them directly using the document import endpoint:

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

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.

StateDGFiP CDVDescription
sendingThe invoice has been created and is queued for PPF transmission.
sent200 — DéposéeThe invoice has been successfully deposited at the PPF.
registered202 — ReçueThe PPF has validated and forwarded the invoice to the buyer.
accepted205 — ApprouvéeThe buyer has approved the invoice.
refused210 — RefuséeThe buyer has rejected the invoice.
paid212 — EncaisséeThe invoice payment has been confirmed.
errorAn 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.

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

Terminal window
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

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:

Terminal window
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 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 table).

B2Brouter propagates the payment confirmation to the PPF via one of two paths, depending on the invoice nature:

CasePathMechanism
Domestic FR → FR (B2Brouter detects company.country == "fr" and contact.country == "fr")CDARSends 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 fileGenerates a tax report in Ledger C — payments (see Three ledgers model)

When payment is confirmed, mark the invoice at the PPF with a mark_as call:

Terminal window
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"}'

API support coming soon. For now, the API only accepts marking the invoice when it is fully paid (state: "paid").

If you manage payments from an ERP, keep partial-payment bookkeeping internally and only mark the invoice’s PPF state once the total has been covered. Alternatively, handle partial payments in the B2Brouter web app: open the invoice → Create payment → enter the paid amount.


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:

StateDescription
newTax report created, queued for transmission
sentDocument deposited to PPF via SFTP
acknowledgedPPF has received and validated the file (CDV condition 500 — Reçue)
registeredTerminal — Invoice accepted and registered by the DGFiP (CDV condition 300)
refusedTerminal — Rejected by the DGFiP (CDV condition 301)
errorTerminal — Transmission or PPF processing error
annulledInvoice has been annulled after registration

Flux 10 — Cross-border B2B and B2C invoices (e-Reporting):

StateDescription
newTax report created, accumulated for the daily ledger batch
sentLedger deposited to PPF via SFTP
acknowledgedPPF has received the ledger (CDV condition 500 — Reçue)
registeredTerminal — Ledger accepted and registered by the DGFiP (CDV condition 300)
refusedTerminal — Rejected by the DGFiP (CDV condition 301)
errorTerminal — Transmission or PPF processing error

Query the state using the tax report ID from tax_report_ids in the invoice response:

Terminal window
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


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 Submit a Flux-10 e-reporting file instead.

Sales to non-VAT-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 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 VAT territory and follow the Flux 10 e-Reporting path.

B2Brouter groups all Flux 10 tax reports from a calendar day into Ledgers sent to the PPF once per day (scheduled at 02:00 AM server time). You can identify Flux 10 tax reports by a non-null ledger_id in the tax report response.

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, VAT on services accrues at collection) — document type xml.ledger.dgfip.payments.

The combination of roles and modes yields up to 3 daily ledgers per account:

LedgerRoleModeContent
A — ÉmissionSEtransactionsIssued invoices (B2C and cross-border B2B)
B — RéceptionBYtransactionsIntracommunity / extra-EU purchases declared as the buyer
C — PaymentsSEpaymentsConfirmed 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 + day + (role, mode) under a lock to avoid duplicates.


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.

When you receive an invoice, report its status to the PPF by updating the invoice state:

Terminal window
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