Skip to content

France

Send and receive domestic French B2B invoices under the 2026 e-invoicing reform.

Availability. The regulated French flow is not yet open in production. In the sandbox, a send with the france declaration takes both legs: our provider produces the tax record (flow 1) and marks it cleared, then delivers the invoice over Peppol in the France CIUS format. An accredited French platform rejected our documents on 2026-09-20; after a correction by our provider, the same platform has received them since 2026-09-25. Received is not accepted, and the sandbox cleared is not a verdict from the tax administration: on 2026-09-27 it also cleared a tax record that breaks a published DGFiP rule. Treat a sandbox France send as an integration test, never as evidence your document will be accepted or cleared.

How France works on getpeppr

France's B2B e-invoicing reform routes every domestic invoice through government-accredited platforms (plateformes agréées, PA) that clear documents with the tax authority (DGFiP). getpeppr is not a plateforme agréée: it is the developer layer — API, SDK, dashboard — built on Storecove, which is an accredited platform. You can verify accredited platforms on the official DGFiP list at impots.gouv.fr. Your integration stays the same JSON-in, webhooks-out contract you use for every other country.

DateObligationWho
1 Sept 2026Receive electronic invoicesAll VAT-subject companies in France
1 Sept 2026Issue electronic invoicesLarge companies and mid-caps (ETI)
1 Sept 2027Issue electronic invoicesSMEs and micro-enterprises
These dates are set by law and may evolve. getpeppr helps you prepare the technical integration; your company's own compliance obligations are yours to confirm with a tax adviser.

Set up a French company

A Legal Entity's country is fixed at creation — it cannot be changed later. If your existing organization is registered in another country, create a new organization for your French entity and complete onboarding with country: FR.

French companies are addressed by their SIREN or SIRET. Three identifier schemes are available when you add a Peppol Identifier:

SchemeValueUse it for
0002 SIRENE9-digit SIREN or 14-digit SIRETYour company identity — the recommended default
0009 SIRET14-digit SIRETEstablishment-level addressing
0225 FR:CTC9-digit SIRENYour electronic address for the French reform (directory registration)

In production, French identifiers are verified against official registries (VIES and INSEE SIRENE) before sending is enabled, and getpeppr derives your French VAT identifier from your SIREN automatically once it can be proven correct. You never submit a VAT number by hand.

Register in the French directory

Receiving domestic French B2B invoices requires a directory entry: the 0225 FR:CTC identifier is your company's electronic address for the reform, registered with your bare 9-digit SIREN. Adding it from the console (or the Legal Entities API) registers it on the network and advertises it for France-specific documents. In production, registration in the DGFiP directory (annuaire) itself is not open yet; in the sandbox, a platform customer's suffixed address is also registered on our provider's sandbox directory (Legal Entities). If you will also send under the regulated flow, register your SIREN (0002) first and the directory identifier second — see why the order matters.

Propagation takes 20–60 minutes. A freshly registered identifier is not routable immediately — senders get a delivery failure until directory propagation settles. And route with the scheme the recipient actually registered: sending to 0002:… when the recipient is registered under 0225:… does not resolve, even after propagation.

Send an invoice to France

Sending to a French buyer is a standard send() call. Use 0002 (SIRENE) or 0009 (SIRET) as the routing scheme — these are the recommended receiver identifiers for French B2B. French VAT rates: standard 20%, reduced 10% / 5.5% / 2.1%.

Not sure which scheme a buyer is registered under? Check with the free Peppol participant lookup or the directory search API.

Declaring the regulated French flow

A French buyer does not, on its own, put an invoice into the regulated flow. The declaration is yours to make: add a france block carrying the Cadre de Facturation — the use case the document falls under, defined by AFNOR XP Z12-012.

await peppol.invoices.send({
  number: "INV-2026-001",
  to: { name: "Tricatel", peppolId: "0225:123456782", country: "FR", /* … */ },
  france: { cadreDeFacturation: "S1" },
  lines: [/* … */],
});

Omit the block and the invoice travels as an ordinary Peppol document, whatever the buyer's country — we never infer the regime for you. The Peppol France Solution Architecture puts that choice on the sender, and guessing it would file your document under the wrong set of rules.

Declaring the regime is checked, not just recorded. The identity issuing the invoice needs two things: registration in the French directory (scheme 0225, settled registry check, published to the network), and a SIREN under scheme 0002. The SIREN is not about addressing — French invoicing rule BR-FR-10 puts the seller SIREN on the document itself. Order matters: the network operator writes the first non-tax identifier registered on your legal entity as the seller's legal registration number (measured on 22 September 2026). Register the SIREN (0002) before the directory identifier (0225); registered the other way round, the directory identifier is written instead and the rule fails. Before submitting a declared invoice we read that order, and refuse the send if the SIREN is not first — to fix it, remove the identifiers registered ahead of the SIREN and add them back. Any of these conditions unmet and the send is refused with 422 france_regime_not_eligible — before anything is submitted, and the message names which one. Register the identifiers first, then declare. A cadre outside the published list is a 400 france_cadre_invalid.

The buyer's SIREN travels too: the French tax platform records it as the buyer's legal registration number (scheme 0002, nine digits). You rarely need to send it: when to.peppolId is a French address we read it there — the directory address 0225:<SIREN> with or without a suffix such as 0225:123456782_SALES, a SIREN under 0002, or a SIRET under 0009, whose first nine digits are the SIREN. To give it explicitly, send to.companyId with to.companyIdScheme: "0002" (SIREN) or "0009" (SIRET); what you send is always the one used. When none of them gives a valid SIREN (for example a buyer addressed under a non-French scheme), the invoice is refused with 400 france_buyer_siren_missing before anything is submitted.

A declared invoice also constrains its number. French invoicing rule BR-FR-01 allows at most 35 characters, made only of letters, digits and + - _ / with no spaces, and the French tax platform currently accepts at most 20 characters until 1 December 2026. A number outside either limit, or sent as anything but a string, is refused with 400 france_invoice_number_invalid before anything is submitted, and the message names the rule.

A declared credit note must also name the invoice it credits with its issue date: set invoiceReference and invoiceReferenceDate (YYYY-MM-DD). French rule BR-FR-CO-05 ignores a reference without its date, so a credit note missing either is refused with 400 france_credit_note_reference_invalid before anything is submitted.

import { Peppol } from "@getpeppr/sdk";

const peppol = new Peppol({ apiKey: "sk_live_..." });

// Route with the scheme the buyer is registered under —
// SIRENE (0002) or SIRET (0009) are the recommended routing schemes.
const invoice = await peppol.invoices.send({
  number: "INV-2026-042",
  to: {
    name: "Fournitures Dupont SARL",
    peppolId: "0009:73282932000074", // 0009 = SIRET (14 digits)
    street: "12 Rue de la Paix",
    city: "Paris",
    postalCode: "75002",
    country: "FR",
  },
  lines: [
    { description: "Consulting — June", quantity: 1, unitPrice: 1200, vatRate: 20 },
  ],
});

console.log(invoice.status); // "submitted"

Track French invoice statuses

Live today. getpeppr sends French invoices on the regulated profile and reports transmission statuses — accepted, delivered, failed — through the same canonical status vocabulary as every other country, with the native label preserved in detail. Your legal entity is registered to receive lifecycle statuses from the network — every production entity, and every entity created from now on — so when the recipient's accredited platform rejects an invoice it has received (Rejetée, 213), you see status: "failed" with the code in detail.platformFiscal.code set to "213".

Landing next. The statuses issued by your buyer and the administration — Reçue, Refusée, Encaissée — map to the fields below. They travel on the same network capability, but we have not yet observed each of them end to end; until we have, an invoice that your buyer refused may stay delivered on your side. Integrate now: the shape below is final and will not move.

French statusMeaningWhere it lands
Reported to the DGFiPThe invoice data (flow 1) reached the administration — code: "F1", and the clearing proof with include=evidence. Not delivery.detail.platformFiscal
DeliveredThe buyer's platform acknowledged the invoice on Peppoldetail.delivery
RejetéeTechnical rejection (format, directory, authority checks)detail.platformFiscal
RefuséeBusiness refusal — the buyer declined the invoicedetail.businessDisposition
Prise en charge, Approuvée, Approuvée partiellement, En litigeThe buyer's response — in hand (204), approved (205), partially approved (206) or disputed (207); 206 and 207 carry the buyer's reason in detail.businessDisposition.reasondetail.businessDisposition
Paiement transmisThe buyer says it has sent the payment (211). Not a collection: the invoice is not marked paiddetail.settlement
EncaisséePayment received — reported for VAT on collection. Not yet observed coming back after a report of yours: read your report's outcome on paymentReportdetail.settlement
Rejection is not refusal. Rejetée is a technical failure you fix and resend; Refusée is a business decision by your buyer. The two arrive on different axes so your code never has to guess. In the sandbox, statuses emitted by an accredited buyer platform were observed reaching getpeppr through our provider: 204, 205, 206, 207 and 211 on 2026-09-27, 210 on 2026-09-25. None has yet come from a production buyer.
const invoice = await peppol.invoices.getStatus(invoiceId);

// Canonical status — the same vocabulary for every country.
// A platform rejection (Rejetée, 213) is "failed": fix and resend.
// A buyer refusal (Refusée, 210) is "rejected": a business decision.
console.log(invoice.status); // e.g. "failed"

// The raw pre-coercion status string is always preserved
console.log(invoice.rawStatus);

// Structured national detail, when the gateway exposes it.
// Native labels are preserved, never over-translated.
const fiscal = invoice.detail?.platformFiscal;
if (fiscal) {
  console.log(fiscal.code);         // e.g. "213"
  console.log(fiscal.label);        // e.g. "Rejetée"
  console.log(fiscal.jurisdiction); // "FR"
}

Report a payment (encaissement)

For services, French VAT is due when the payment is collected, not when the invoice is issued — so the reform requires sellers to report payments received. When a French CTC invoice gets paid, report it with markAs(id, "paid"): getpeppr files the payment report through the accredited platform, and calling again returns the report already filed instead of filing another.

The report declares the whole invoice. There is no amount to pass: the accredited platform takes the VAT breakdown of the invoice as it was sent. A partial payment cannot be reported yet. A credit note is not reported as paid: it is refused with 422 and Getpeppr-Result-Code: invoices.payment_reporting_credit_note, and nothing is filed.

Where the report stands. Read the invoice (invoices.getStatus(id), GET /v1/invoices/{id}): once a report exists it carries paymentReport.status — submitted when the provider accepted it, processed once the provider has processed it, failed when it was refused (call markAs again to retry). If an attempt got no answer, the report may already be filed and reads interrupted: call again within 30 minutes of the first attempt and it is resolved without filing twice; after that it turns into an unconfirmed report. unconfirmed and provider_failed need support, and calling again files nothing. The report is not a status of the invoice: the invoice's own status does not move when you report a payment.

Not yet proven end to end. Payment reporting applies to invoices sent with the france declaration, the only ones that carry the French mandate profile. For such an invoice, paid files the report with the accredited platform; its acceptance by the tax administration is not yet proven end to end — processed does not say the administration received it. Any other invoice is refused with 422 and Getpeppr-Result-Code: invoices.payment_reporting_not_applicable, and nothing is filed. That is the last gate on this route, and an earlier one can refuse first under its own result code — in production, an unverified identity does — so branch on the header rather than assuming this refusal.

Platforms. The identity checked is the one that issued the invoice. For an invoice you sent on behalf of a sub-tenant, that is the sub-tenant, not your platform: the call needs your master key, and it is refused with send_as.sub_tenant_not_sendable while that sub-tenant cannot send (in the sandbox, send_as.sub_tenant_test_identity_required when the remedy is a test identity). A standard key receives 404 for a sub-tenant's invoice, as it does when reading one.
// French VAT on services is due on collection (encaissement):
// when the buyer pays, the seller reports the payment.
// One call files the payment report through the accredited platform;
// calling again returns the report already filed.
const result = await peppol.invoices.markAs(invoiceId, "paid");

// Where the report stands is read on the invoice itself:
// "submitted", then "processed" once the provider has processed it.
// "interrupted": an attempt got no answer — call again within 30 minutes
// of the first one; after that it turns "unconfirmed" (contact support).
const { paymentReport } = await peppol.invoices.getStatus(invoiceId);
console.log(paymentReport?.status);

E-reporting FAQ

Do I need to e-report my domestic B2B transactions separately?

No. For domestic B2B invoices sent through the regulated flow, the transaction data reaches the DGFiP through the electronic invoice itself — that is the point of the reform. Do not submit a separate transaction e-report for these invoices.

So e-reporting is fully automatic?

Not entirely — payment reporting stays your responsibility. When a French CTC invoice for services is paid, you report the encaissement with markAs(id, "paid"). Transactions: automatic. Payments: one API call from you.

What about B2C or cross-border transactions?

B2C and international transactions fall under separate e-reporting obligations that getpeppr does not cover today. The current French scope is domestic B2B: send, receive, and payment reporting.

Is getpeppr a plateforme agréée (PA)?

No. getpeppr is a developer layer (API/SDK) built on Storecove, an accredited platform — you can verify accredited platforms on the DGFiP's official list. Storecove carries the regulated role; getpeppr gives you the integration.

Can I test the French flow in sandbox?

Partially, and less than you might expect. Sandbox exercises directory registration, sending, receiving and webhooks over the Peppol network — a real cross-platform exchange was observed on 2026-09-20, in both directions. But on a send to a French recipient our provider applies the France CIUS document type rather than the standard Peppol BIS profile. An accredited French platform rejected it on 2026-09-20 and, after a correction by our provider, has received it since 2026-09-25. So sandbox proves transport and receipt, not acceptance. Since 2026-09-27 the sandbox also exercises the regulated leg: the tax record is produced and marked cleared, and the buyer's French statuses come back. That is still not a verdict from the tax administration, and production is not yet open.