Send invoices to Chorus Pro
In France, all invoices to public-sector clients must be submitted electronically via Chorus Pro, the government’s official B2G e-invoicing portal (Decree No. 2016-1478). Since April 2020, this applies to every central, regional and local administration—whether you’re a French company or an international supplier.
This guide shows you how to use the B2Brouter API to:
- Set up your company account (staging & production).
- Lookup public entities by SIRET in the B2Brouter Directory.
- Create customer and organizational-unit contacts.
- Generate, send, and track your invoices via Chorus Pro.
- Download the exact XML file submitted and acknowledge.
Prerequisites
Section titled “Prerequisites”- French company with a valid TVA/SIRET number.
- Testing environment (staging)
- Register at app-staging.b2brouter.net to try out the API.
- Once registered, open a Support Ticket in staging to request your API key and permissions.
- Production integration & eDocExchange subscription
- To go live, register at app.b2brouter.net.
- Subscribe to an eDocExchange Product or contact our Sales team at https://www.b2brouter.net/global/contact/ to discuss plans, sign your contract, and receive dedicated integration support.
Obtaining API Credentials
Section titled “Obtaining API Credentials”- Log in to your B2Brouter account.
- Go to the Developers tab.
- Select API Keys.
- Click on the clipboard icon to retrieve your API token.
Required identifier: SIRET, not SIREN
Section titled “Required identifier: SIRET, not SIREN”To invoice via Chorus Pro, both the issuing company and the client (the public entity) must be identified with a SIRET (cin_scheme="0009"), never with a SIREN:
- Issuing company: if the company’s country is
fr,cin_valuemust hold a non-blank SIRET andcin_schememust equal9("0009"). If the SIRET is blank, orcin_schemeis anything other than9, the invoice is not sent. - Client: the recipient must also be identified with a SIRET. Without it, contact creation fails with the error “Chorus needs the SIRET identifier of the client”.
Hierarchical consistency: even though invoicing is always SIRET-to-SIRET, the account or contact shouldn’t be “flat” at SIRET level. Keep the same hierarchy as in the DGFiP circuit: a SIREN parent with the SIRET modeled as an organizational unit (OU) (
parent_id+cin_scheme="0009") under that parent (see Account structure). If your company already has a B2Brouter account with a SIREN parent, create the issuing SIRET as an OU under that parent instead of a separate standalone account.
Retrieve or Create Your Company Account
Section titled “Retrieve or Create Your Company Account”1. Retrieve your Company ID
Section titled “1. Retrieve your Company ID”curl --request GET \ --url 'https://api-staging.b2brouter.net/accounts?offset=0&limit=25' \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'accept: application/json'2. Create a Company Account (if needed)
Section titled “2. Create a Company Account (if needed)”Provide cin_scheme: "0009" and cin_value with the issuing establishment’s SIRET (14 digits). This is required for Chorus Pro: without a SIRET, or with a cin_scheme other than 9, invoice submission fails.
If your company already has a B2Brouter account at SIREN parent level (for example, for DGFiP), don’t create a new standalone account with this SIRET: create it as an OU with
parent_idpointing to the parent, following the same steps as Create organisational units in the DGFiP guide. The example below is for a company that doesn’t yet have any B2Brouter account.
curl --request POST \ --url https://api-staging.b2brouter.net/accounts \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data '{ "account": { "country": "fr", "rounding_method": "half_up", "tin_value": "FR46458880332", "tin_scheme": 9957, "cin_scheme": "0009", "cin_value": "45888033200017", "name": "Exemplar SAS", "address": "10 Rue Imaginaire", "city": "Paris", "postalcode": "75001", "province": "Île-de-France", "email": "john.doe@example.com" } }'Verify recipient information
Section titled “Verify recipient information”You can check if the recipient exists in our public directory:
curl --request GET \ --url https://api-staging.b2brouter.net/directory/fr/0009/13001533200013 \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'Content-Type: application/json'This lookup already returns the entity’s existing organizational units (service codes) from the Chorus Pro directory. You don’t need to create them yourself as a contact: use the
cin1_scheme/cin1_valuealready returned by the directory.
Create a Contact
Section titled “Create a Contact”When creating a French customer:
- Use
cin_valuefor SIRET-CODE number. This is required: without a SIRET, Chorus Pro rejects the invoice with the error “Chorus needs the SIRET identifier of the client”. - Use
cin_schemeto identify the Schemes Codelist. SIRET-CODE is 0009; a SIREN (0002) is not accepted. transport_type_codeshould befr.chorus.document_type_codeshould bexml.ubl.invoice.chorus.
curl --request POST \ --url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data '{ "contact": { "language": "en", "is_client": true, "is_provider": true, "terms": "custom", "public_sector": true, "name": "UNIVERSITE D AIX MARSEILLE", "address": "58 BD CHARLES LIVON", "city": "MARSEILLE 7", "postalcode": "13007", "country": "fr", "currency": "EUR", "transport_type_code": "fr.chorus", "document_type_code": "xml.ubl.invoice.chorus", "cin_value": "13001533200013", "cin_scheme": "0009" } }'Create an Organizational Unit Contact
Section titled “Create an Organizational Unit Contact”To bill a specific department or service, create a sub-contact under the main entity using parent_id and include the Chorus Pro “service code” (cin1_scheme / cin1_value).
curl --request POST \ --url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'Content-Type: application/json' \ --data '{ "contact": { "parent_id": 1313228381, "name": "Factures marché FCM ROP cadre A2", "address": "58 BD CHARLES LIVON", "city": "MARSEILLE 7", "postalcode": "13007", "country": "fr", "cin1_scheme": "8017", "cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS" } }'Create and Send an Invoice
Section titled “Create and Send an Invoice”When invoicing a French customer, ensure you provide all required fields including:
number,dateanddue_date- At least one
invoice_lines_attributeswithtaxes_attributes contact_idor a completecontactobjectponumberto identify the Order referencebuyer_referencewith thecin1_value(Code Service) to identify the target department
curl --request POST \ --url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'content-type: application/json' \ --data '{ "send_after_import": true, "invoice": { "type": "IssuedInvoice", "contact_id": 1313228399, "bank_account": { "type": "iban", "iban": "FR7630006000011234567890189" }, "terms": "custom", "invoice_lines_attributes": [ { "unit": 5, "quantity": 135, "price": 25, "description": "Cocktail Dinatoire", "taxes_attributes": [ { "name": "TVA", "category": "S", "percent": 10 } ], "article_code": "14", "position": 1 } ], "number": "00002", "date": "2025-06-18", "due_date": "2025-07-18", "currency": "EUR", "ponumber": "0123456", "buyer_reference": "ESR_MISSION_FACTURES_DEPLACEMENTS" } }'Check Invoice Status
Section titled “Check Invoice Status”Check Single Invoice Status
Section titled “Check Single Invoice Status”curl --request GET \ --url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}?include=lines' \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'accept: application/json'Check Multiple Invoices Status
Section titled “Check Multiple Invoices Status”curl --request GET \ --url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices?offset=0&limit=25&state_updated_at_from=2025-06-12' \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'accept: application/json'Real-Time Status Updates with Webhooks
Section titled “Real-Time Status Updates with Webhooks”Instead of polling, subscribe to push notifications via webhooks. Whenever an invoice state changes, B2Brouter will send an HTTP POST to your endpoint.
Invoice Status WebHooks - API Reference
Download the Original Invoice XML
Section titled “Download the Original Invoice XML”After sending, the GET /invoices/{id} response includes a download_legal_url field. Use it to fetch the exact XML file submitted to Chorus Pro:
curl --request GET \ --url https://api-staging.b2brouter.net{download_legal_url} \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'Accept: application/xml'As a one-step alternative, you can call GET /invoices/{id}/as/legal directly, without first fetching download_legal_url from the invoice payload. It returns the archived legal document as stored and does not generate a billable transaction — see Download invoices and Transaction: View_as.
Mark Invoice as Acknowledged
Section titled “Mark Invoice as Acknowledged”curl --request POST \ --url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/ack \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: {YOUR_API_VERSION}' \ --header 'accept: application/json'Related guides
Section titled “Related guides”- DGFiP – E-Invoicing and E-Reporting: domestic French B2B circuit, with a different identifier model (SIREN parent + SIRET organizational units).
For further help: