France E-Reporting
E-reporting is the DGFiP duty to send transaction data that is not covered by a French B2B e-invoice: sales to consumers and other non-taxable customers (reported as a daily aggregate), sales to businesses established outside France, and purchases from suppliers established outside France (both reported invoice by invoice). It is separate from the B2B e-invoicing mandate.
France e-reporting isn't a separate API. It uses the same Real-Time Reporting solution and the same submission endpoint as everything else: POST /v1/send. Every transaction you submit is resolved to the correct compliance mandate; a French e-reporting transaction is added to a reporting period instead of being turned into a per-document invoice. When the period ends, you review it and confirm it, and only then is it filed, unless you have chosen automatic sending (see below).
POST /v1/reporting-batches/{id}/confirm. If the deadline passes before you confirm, nothing is filed: Clearvo keeps reminding you but does not submit on your behalf. Automatic sending is an explicit opt-in (submissionMode: "batch_auto"): a period you have not confirmed is then filed the day before its deadline, and you can still confirm it earlier. You remain the party liable to the DGFiP either way. See Period lifecycle.csk_live_* / csk_test_* API key system as every Clearvo product. Send each transaction in the currency it was issued in.When the obligation starts
The start date depends on the size of the legal entity (assessed per SIREN), not on the type of sale:
| Who | E-reporting starts |
|---|---|
| Large companies and mid-sized companies (ETI) | 1 September 2026 |
| Micro, small and medium enterprises (SME) | 1 September 2027 |
| Taxable persons not established in France, for purchases where they are the acquirer liable for the VAT (reverse-charge purchases, intra-EU acquisitions) | 1 September 2027, whatever their size |
Clearvo never works out your start date for you. You set it yourself with effectiveFrom when you enable the obligation. Enabling with a date earlier than the legal date for your size is accepted with a warning, not blocked.
What gets reported
What decides the treatment is who the counterparty is and where it is established, not the delivery address and not the billing country on its own. Clearvo uses the counterparty's tax registration country together with its address country, never the VAT number prefix alone.
| Operation | Treatment |
|---|---|
| Sale to a consumer or other non-taxable customer, in France or abroad (including distance sales and exports) | Reported as a daily aggregate (flux 10.3), grouped by day, VAT rate and category. No personal data is sent. Credit notes net into the same aggregate. |
| Sale to a business established outside France (intra-EU supply, export, services) | Reported invoice by invoice (flux 10.1), with the buyer's VAT number. |
| Sale to a business established in France | Not e-reporting. It is a B2B e-invoice (fr_einvoicing). |
| Purchase from a supplier not established in France: intra-EU acquisition, reverse-charge goods or services, triangular transaction, purchase from Monaco | Reported invoice by invoice, by you as the receiving party. Send it with transactionDirection: "purchase"; it appears in its own Received batch. |
| Transfer of your own goods to or from your own VAT registration in another EU country | Reported, one entry per movement, invoice by invoice, using your own foreign VAT number. Send a goods leaving France (shipFrom in France, shipTo in the other country) as a sale-direction entry, and goods arriving in France (shipFrom in the other country) as a purchase-direction entry. Do not send the other country's side of the same movement: it is not a French operation, and an entry whose direction does not match its route is held as needs-info. |
| Purchase from a supplier established in France | Not e-reporting. It arrives as a B2B e-invoice. |
| Imports of goods | Not reported (customs data already feeds your VAT return). |
| Activities exempt under arts. 261 to 261 E CGI, and operations outside the scope of VAT | Not reported. |
| Supply between two French-established taxable persons, located in another EU country | Neither e-invoicing nor e-reporting. |
| OSS or IOSS distance sale | Reported if you are established in France. Not reported if you are not established in France. |
| Customer in Monaco | Monaco is treated as French VAT territory. The stored country stays MC. |
When a fact that changes the answer is missing (for example the customer's country), the transaction is not guessed at: it comes back WAITING_FOR_YOU with reason: NEEDS_INFO (see Needs-info handling).
needsAttention.code = TRANSPORT_HELD and the rest of the period (including the consumer aggregate) is filed normally. A period that contains only held rows is not filed at all and cannot be confirmed yet. Once the platform accepts a kind of entry, Clearvo releases the held entries on its own and sends them as a corrective filing for their period; you do not need to resubmit anything. Operations located outside France are outside what Clearvo applies French e-reporting to.Prerequisites
POST /v1/fr/credentials with your entity's numéro de TVA intracommunautaire. This runs a SIREN check-digit + INSEE key validation immediately — a mistyped number is rejected as 400 INVALID_FORMAT rather than surfacing later as an unexplained pending status. GET /v1/fr/credentials reads back capabilities.ereporting.status (active / pending_activation / sandbox) any time you want to check where your entity's own onboarding stands — poll it rather than re-posting; it only changes on a platform-side update.
PATCH /v1/tax/reporting-obligations with { "obligations": { "fr_ereporting": { "enabled": true, "effectiveFrom": "2026-09-01", "vatRegime": "<your VAT regime>" } } }. Use the start date from the calendar above. Requires the VAT number from step 1 to already be registered — otherwise rejected with 400 REGISTRATION_REQUIRED. submissionMode is batch_review (the default: you confirm each period) or batch_auto (a period you have not confirmed is filed automatically the day before its deadline; you can still confirm it earlier). immediate is rejected with SUBMISSION_MODE_NOT_ALLOWED. vatRegime is one of:
| Value | Meaning |
|---|---|
reel_normal_mensuel | Régime réel normal, monthly VAT returns. |
reel_normal_trimestriel | Régime réel normal, quarterly VAT returns. |
reel_simplifie | Régime réel simplifié. |
franchise_en_base | VAT exemption threshold scheme (franchise en base de TVA). |
You can set this the same way in the dashboard instead of the API — entity's Jurisdictions page, France row, VAT filing regime.
POST /v1/send and every /v1/fr/* write endpoint require an entity-scoped key (csk_live_* or csk_test_*). Account-scoped keys are accepted on read endpoints only.
Submit transactions
There is no separate France e-reporting submission endpoint. Send every transaction through the standard invoice endpoint and mandate resolution routes it correctly on its own. The request body is exactly the one documented in E-Invoicing → Request body (and in openapi.json); there are no France-only fields except the optional shipFrom / shipTo below. A French consumer sale looks like this:
curl https://api.clearvo.io/v1/send \
-H "x-api-key: csk_live_..." \
-H "Content-Type: application/json" \
-d '{
"documentType": "invoice",
"invoiceNumber": "WEB-78921",
"issueDate": "2026-09-14",
"currency": "EUR",
"country": "FR",
"customerType": "B2C",
"customer": {
"name": "Camille Martin",
"address": { "street": "12 rue de la Paix", "city": "Paris", "postalCode": "75002", "country": "FR" }
},
"lines": [
{ "description": "Order 78921", "quantity": 1, "unitPrice": 82.64, "taxRate": 20 }
]
}'
{
"outcome": "ACCUMULATED",
"mandate": "FR_EREPORTING",
"id": "txn_01H8X9K2M4QRT7VWYZ3ABCDEF",
"period": { "key": "2026-09-D2", "start": "2026-09-11", "end": "2026-09-20" },
"deadline": "2026-09-30",
"reviewRequired": true,
"message": "Included in your France e-reporting for period 2026-09-D2 — filing due 2026-09-30."
}
200 ACCUMULATED means the transaction has joined this reporting period — nothing is sent anywhere for it individually, and reviewRequired: true says a human must confirm the period before anything is filed. The transaction is visible straight away: track it with GET /v1/mandate-transactions/{id}, or list the period with GET /v1/mandate-transactions?mandate=FR_EREPORTING&period=2026-09-D2. Never resubmit the same transaction to try to update it (see credit notes below).
Idempotency
The x-idempotency-key header is optional and is only your retry hint. Clearvo identifies every document itself, from its documentType, country and invoiceNumber (for a purchase also the supplier tax ID and issue date), and returns that key in the X-Idempotency-Key response header. Re-sending the same invoice returns the stored result. Re-sending the same invoice number with different content is refused 409 IDEMPOTENT_BODY_MISMATCH instead of silently returning the old result; an earlier NEEDS_INFO or REJECTED attempt is re-run in place. A document missing one of those fields is stored under an identity Clearvo mints, with the warning NO_IDENTITY_NO_DEDUP in the response. See Idempotency.
Fields that matter for France
| Field | Required | Description |
|---|---|---|
| issueDate / taxPointDate | Yes / No | The transaction date. It decides which period the transaction joins (tax point date when supplied, otherwise issue date). Purchases use the same date, as supplied on the record. |
| customer.address.country, customer.taxId | Yes / No | The counterparty's country decides the treatment in What gets reported. Missing it parks the transaction as NEEDS_INFO. |
| documentType | Yes | invoice, credit_note or debit_note. |
| originalInvoiceRef | For credit and debit notes | The invoice being corrected, as an object: { "invoiceNumber": "INV-2026-0001", "issueDate": "2026-06-19" }. Send the original invoice's own number and its issue date as YYYY-MM-DD (the original's date, not the credit note's); the credit or debit note keeps its own new invoiceNumber. If the original was issued before you started sending to Clearvo, the credit note is processed on its own, with its own lines and tax, and the response carries the warning CREDIT_NOTE_ORIGINAL_NOT_FOUND. If an invoice with that number exists but with another issue date, it is treated as a different invoice and the warning is CREDIT_NOTE_ORIGINAL_DATE_MISMATCH. A credit note is reported in the period of its own date. See Credit and debit notes for a worked example. |
| transactionDirection | No | sale (default) or purchase for a purchase you declare as the receiving party. |
| shipFrom / shipTo | Own-goods transfers | Where the goods leave and arrive, as { "country": "XX" }. Required for an own-goods transfer (shipTo only for an outbound transfer from France). For other transactions both are optional: a shipFrom in one of your registered countries decides the reporting country when you send none, and the movement for sales of goods. |
| clientTaxCode | Recommended | Your own ERP tax code. The code's configured movement (domestic, intra-EU, export, own-goods) drives classification. |
Credit notes and debit notes
Amounts on every line are non-negative; the sign is carried by the document type. A credit_note nets against the consumer aggregate in the period of its own date (not the original's date), and a debit_note adds to it. For invoice-level rows a credit note stays a separate document that references the original. Send a corrective document as a new transaction with its own number; never resubmit the original expecting an in-place update.
POST /v1/send/bulk's CSV format is one row per single-line transaction, deduplicated on source_reference — repeating an invoice number across multiple line rows collides as SKIPPED_DUPLICATE. A genuinely multi-line invoice should go through POST /v1/send's JSON lines[] array instead.Needs-info handling
A transaction missing a field the mandate requires comes back as status WAITING_FOR_YOU (reason: NEEDS_INFO, errorCode: MANDATE_INCOMPLETE_DATA) instead of being accumulated. It's parked, not lost — resolve it before the affected period's filing deadline.
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/fr/transactions/needs-info | List every transaction currently parked in NEEDS_INFO, with per-row missing-field detail and a countdown to the affected period's deadline. |
| PATCH | /v1/fr/transactions/needs-info/{id} | Resupply the missing field(s) for one parked row: customerCountry (a sale), supplierCountry / supplierTaxId (a purchase), or shipFrom / shipTo as { "country": "XX" } (an own-goods transfer). At least one per call. |
Alternatively, resubmit the same invoice with the complete data and the waiting attempt is re-run in place, because the invoice's identity is the same. The key you send does not change this, and the PATCH above works either way.
Period lifecycle
Accumulated transactions are grouped into a reporting period (keyed like 2026-09-D2), one batch per direction: sales appear as Issued and purchases as Received, each with its own status and deadline. List them with GET /v1/reporting-batches?obligation=fr_ereporting.
| Status | What it means | What you do |
|---|---|---|
open | The period has not ended. Transactions keep joining and totals are live. | Nothing. GET /v1/reporting-batches/{id} works at any time. Confirming now is refused 409 BATCH_STILL_OPEN. |
closed / ready_for_review | The period ended and is frozen for your review. The reporting_batch.ready_for_review webhook fires once. | Review the batch, exclude anything that does not belong (POST .../exclude moves a record to the next batch), then confirm. |
submitted | You confirmed it and it has been handed to the partner platform for transmission to the DGFiP. | Wait for the outcome. |
filed | The platform has answered. A rejected period shows needsAttention and must be corrected and resubmitted as a whole. | None, or correct and resubmit. |
# 1. Read the batch and note its snapshotVersion
curl https://api.clearvo.io/v1/reporting-batches/{id} -H "x-api-key: csk_live_..."
# 2. Confirm exactly that version
curl -X POST https://api.clearvo.io/v1/reporting-batches/{id}/confirm \
-H "x-api-key: csk_live_..." \
-H "Content-Type: application/json" \
-d '{ "snapshotVersion": 3 }'
| Response | Meaning |
|---|---|
409 BATCH_STILL_OPEN | The period has not ended yet. Confirm once it has closed. |
409 BATCH_STALE | A transaction joined, was excluded or another confirm landed since you read the batch. The body carries currentSnapshotVersion; re-read the batch and confirm again. |
409 ALREADY_SUBMITTED | The batch is already being submitted. |
Webhooks: reporting_batch.ready_for_review, reporting_batch.deadline_approaching, reporting_batch.deadline_passed and reporting_batch.overdue_reminder (see Webhooks). They remind you; they never file. Each row's period.status on GET /v1/mandate-transactions follows the same lifecycle. Pass state= to filter across transaction, period and clearance state in one query: NEEDS_INFO, HELD, OPEN, CLOSED, SUBMITTED or FILED.
Periods and deadlines
The reporting period and the filing deadline depend on your VAT regime:
| VAT regime | Period | Filing deadline |
|---|---|---|
reel_normal_mensuel | Three décades a month: days 1 to 10 (D1), 11 to 20 (D2), 21 to month end (D3) | D1: the 20th of the same month. D2: the 30th of the same month (the last day of February). D3: 10 days after month end. |
reel_normal_trimestriel | Calendar month | The 9th of the following month |
reel_simplifie | Calendar month | The 25th of the following month. The simplified regime is abolished from 1 January 2027. |
franchise_en_base | Not scheduled by Clearvo | No deadline is computed. The DGFiP fiche describes a bimonthly cycle. |
Clearvo shows the deadline on every ACCUMULATED response and every batch, and reminds you as it approaches. It never files after the deadline on your behalf; with batch_auto it files the day before the deadline.
Export
GET /v1/fr/transactions/export streams every canonical France mandate transaction for the entity as text/csv or application/x-ndjson — built for pulling a full décade, month, or quarter's worth of rows for audit-defense purposes, rather than paging a UI. Bound the range with period (in the entity's own cadence) or from/to — mutually exclusive; omit both to export everything.