Declare transactions
Sales to individuals and operations invoiced with parties abroad do not go through e-invoicing: they are declared through e-reporting. For a sales invoice you deposit on Scribee, that declaration is established on its own: Scribee reads the buyer's identifiers and derives which of the two obligations the sale carries. The endpoints on this page serve what no deposited invoice carries, what the automatic declaration refuses, and the correction of what has been declared: aggregated for B2C, invoice by invoice for invoices issued outside the e-invoicing channel. The records you create attach to the declaration whose identifier you supply, and you read them back through the list endpoints.
What Scribee declares on its own from your invoices
Depositing a sales invoice is enough. The two obligations are exclusive: a sale is never both the subject of an invoice deposit and of an operation declaration.
| Buyer on the deposited invoice | What Scribee does |
|---|---|
| Carries a SIREN, and is established in the French VAT territory or states no country | The invoice is deposited with the PPF, and nothing is declared in e-reporting (see The invoice lifecycle). One exception, for a seller established in the overseas territories outside the VAT territory: see Overseas territories outside the VAT territory. |
| No SIREN, but carries another identifier - intracommunity VAT number, foreign registration -, or carries a SIREN but is established outside the French VAT territory, in a foreign country as in the overseas territories outside it | An e-reporting invoice is created in the period's declaration, without you having to submit it there. |
| No identifier at all | The sale is counted into the day's B2C aggregate, in the period's declaration. |
The buyer's territory is read from its country_code, and from its postal_code for an FR address (see Overseas territories outside the VAT territory). A SIREN is therefore not enough for the invoice deposit: the buyer must also be established in the French VAT territory. A buyer carrying a SIREN whose country_code is not filled in stays on the first row, and it is the deposit that asks for that country (see The invoice lifecycle).
The VAT category of the lines changes nothing in this table. An invoice whose lines and VAT breakdowns are all in category O (outside the scope of VAT) follows it like any other: Scribee does not infer that it is a disbursement. It is declared by an e-reporting invoice for a buyer on the second row, and counted as TNT1 in the B2C aggregate for a buyer on the third (see The category and the amounts counted). For a buyer on the first row, however, it cannot be deposited with the PPF, which rejects a flux 1 whose VAT breakdown is entirely in category O (rule G2.32): for a company declared in the emission wave, the deposit is refused with 422 and the invoice stays in draft (see The invoice lifecycle). If these are disbursements, mark its lines as described below; otherwise, correct its VAT categories. This refusal does not apply when the buyer is a public entity, which rule G2.32 excludes: Scribee recognizes one when the buyer's SIREN designates, in the national directory, a legal unit of public type, decides it at deposit and keeps that answer for the whole transmission of the invoice. Such an invoice is deposited; nor does the flux 1 check apply G2.32 to it when its whole VAT breakdown is in category E with an exemption reason from article 261 of the CGI (VATEX-FR-CGI261-1, VATEX-FR-CGI261A...), or when it mixes O and E breakdowns.
Only the explicit marking of lines as disbursements changes what is declared (Issue a sales invoice): a sales invoice whose every line carries disbursement set to true, and whose every VAT breakdown is in category O - or E with a tax_exemption_reason_code of VATEX-EU-79-C - with no VAT amount, is neither deposited with the PPF, nor declared by an e-reporting invoice, nor counted into the B2C aggregate, whichever row of the table its buyer is on. A single unmarked line puts it back into this table, and the VATEX-EU-79-C code is not enough without the marker: an invoice whose lines are in category E under that code without carrying disbursement follows this table like any other.
The period retained is the one covering the fait générateur - the delivery date, falling back to the issue date. Nothing is transmitted to the tax authority at deposit time: the declaration stays in draft Draft state.
What Scribee reads as a SIREN
The first row of the table decides everything else, and the buyer carries a SIREN there as soon as its legal_registration_id / legal_registration_scheme_id pair yields one - including in a shape that is not that of a bare SIREN:
| What the buyer carries | The SIREN retained |
|---|---|
Nine digits under the siren (0002) scheme | the value itself |
Fourteen digits - a SIRET - under the siren (0002) scheme | its first nine digits |
Fourteen digits under the siret (0009) scheme | its first nine digits |
Nine digits with no declared scheme, for a buyer whose country_code is within the French VAT territory | the value itself |
| Nine digits with no declared scheme, for a buyer established elsewhere | none: the sale follows the second row of the table |
The first nine digits of a SIRET are the SIREN of the same legal unit (Annexe 7, rule G1.80): the reduction loses nothing of the buyer's identity, which is the only question asked here.
The territory restriction on the fourth row is deliberate. Nine digits is equally the shape of a Norwegian organisation number, a Lithuanian legal-entity code or a Portuguese NIF. Reading those as SIRENs would classify foreign sales as franco-French ones: they would go out as invoice deposits instead of the operation declaration they owe, and that declaration would never be made. So declare the scheme of your foreign counterparties' registrations - that is what makes their identifier readable rather than guessed.
These readings do not rescue an unreadable registration, and that refusal is unchanged: on a buyer established within the French VAT territory, a value that is present and that none of the rows above can exploit - 987/654/321 - has the deposit refused with a 422, details keyed on buyer.legal_registration_id (see The invoice lifecycle). A buyer that genuinely carries no registration at all stays on the third row of the table.
:::danger Do not resubmit what Scribee has already declared Creating a record here for a sale already deposited as an invoice declares it twice. Over-declaration is a regulatory breach exactly as under-declaration is, and the API does not detect it for you: it does not reconcile a record you create with a deposited invoice. Before each creation, check that no sales invoice deposited on Scribee carries the operation, and read the declaration back through its list endpoints. :::
Overseas territories outside the VAT territory
The French VAT territory is limited to metropolitan France, Guadeloupe, Martinique and La Réunion. Guyane (GF), Mayotte (YT), the overseas collectivities - Saint-Pierre-et-Miquelon (PM), Saint-Barthélemy (BL), Saint-Martin (MF), Wallis-et-Futuna (WF), French Polynesia (PF), New Caledonia (NC) - and the French Southern and Antarctic Lands (TF) are excluded from it. Two cases follow, and they take precedence over the table above:
| Party established in one of these territories | What Scribee does |
|---|---|
| The seller | Nothing is declared: no deposit of the invoice with the PPF, no e-reporting invoice, no sale counted into the B2C aggregate, and no directory address is looked up for the buyer. |
| The buyer, carrying a SIREN | The invoice is not deposited with the PPF: an e-reporting invoice is created in the period's declaration, as for a buyer with no SIREN, and no directory address is looked up. |
The first case only concerns these French territories: a seller established in a foreign country follows the table at the top of the page. The second also holds for a foreign country: a buyer established in a foreign country who carries a SIREN also gets an e-reporting invoice, its invoice is not deposited with the PPF, and no directory address is looked up for it.
:::warning Behaviour change Until now, a buyer established in a foreign country who carried a SIREN kept the invoice deposit with the PPF. Its sale is now declared by an e-reporting invoice, and its collection falls under the payments declaration, on the conditions described in Declare payments. :::
The territory is read from the party's country_code. An address whose country_code is FR is attached to its territory by the start of its postal_code:
| Start of the postcode | Territory |
|---|---|
973 | Guyane |
976 | Mayotte |
975 | Saint-Pierre-et-Miquelon |
97133 | Saint-Barthélemy |
97150 | Saint-Martin |
984 | French Southern and Antarctic Lands |
986 | Wallis-et-Futuna |
987 | French Polynesia |
988 | New Caledonia |
Any other postcode - including another 971 code, which stays in Guadeloupe - leaves the address in the French VAT territory.
Triangular operation
A sales invoice whose triangular_position is intermediary - your company is the intermediary of an intra-EU triangular operation - is the subject of no operation declaration: no e-reporting invoice, no sale counted into the B2C aggregate, whichever row of the table its buyer would follow. The field changes nothing in the deposit with the PPF: a buyer on the first row receives the invoice like any other.
Scribee never infers this position from the invoice. As long as triangular_position is null, the sale follows the table at the top of the page like an ordinary sale; a first_supplier sales invoice follows it too. The field is filled in at creation or when modifying the draft, before the deposit (see Issue a sales invoice).
What remains yours to do
- Purchases. A purchase invoice is never declared automatically: the buyer-side (
BY) obligation depends on your own VAT regime and not on the issuer's. Declare it through the endpoints on this page. - Companies outside the emission wave. As long as the issuing company is not declared in the wave in force, no declaration is established automatically for its invoices. This is the same company setting described in The invoice lifecycle.
- Companies whose regime does not carry the obligation. Nothing is declared for them, and no declaration is prepared.
- Operations no invoice carries. A cash-register total, a receipt: nothing will submit them in your place.
The sales the B2C aggregate refuses
On the B2C path, nine cases are refused rather than counted, and they are refused at the invoice deposit, before anything is declared. Like the path itself, this check only targets companies declared in the emission wave. The deposit fails with 422, the invoice stays in draft and no final number is consumed; the message names the cause (The invoice lifecycle). So there is nothing to catch up on here: correct the invoice, then deposit it again.
| Case | What the invoice carries |
|---|---|
Mixed invoicing framework (M1, M2, M4) | The framework announces both deliveries of goods and supplies of services. The split is read on the invoice lines, which an aggregate does not carry: Scribee does not guess it. |
| Invoicing framework absent, or outside the list of frameworks | The operation's category is derived from the invoicing framework (see The category and the amounts counted). |
| Margin-scheme sale mixed with other breakdowns | A VAT breakdown of category E whose exemption reason is VATEX-EU-F, VATEX-EU-I, VATEX-EU-J, or VATEX-EU-D, alongside another VAT breakdown - taxable, G or O. The aggregate counts a margin-scheme sale in a category of its own: issue it on a separate invoice. |
| Margin-scheme sale without a margin base | A VAT breakdown of category E whose exemption reason is VATEX-EU-F, VATEX-EU-I, VATEX-EU-J, or VATEX-EU-D. The aggregate declares such a sale under TMA1, on the margin excluding VAT and the VAT due on that margin - two amounts the invoice does not carry, and that you supply with it in margin_bases (see Sales under the margin scheme). With no base at all, the sale is refused at the deposit. |
| Margin-scheme sale with an inconsistent base | A margin_vat_amount differs by more than one cent from its margin_base_amount taxed at its vat_rate, or the margins including VAT exceed the selling price the invoice carries (see Sales under the margin scheme). |
| Totals absent | The pre-tax total or the VAT total is missing. |
| Foreign currency with no euro counter-value | An aggregate carries its amounts in EUR: the invoice must carry its VAT total in euros. A margin-scheme sale does not need that total: it is the VAT on the margin that is converted to euros, at the exchange rate frozen at deposit (see Invoicing in a currency other than the euro). |
| VAT breakdown absent | An aggregate always carries a breakdown by VAT rate, including when the amounts are nil. |
| Breakdown and totals disagree | The invoice's pre-tax total and VAT total do not equal, to the centime, the sum of its VAT breakdown. |
Three refusals, on the other hand, only happen after a successful deposit, when the declaration is established. In those three cases, the invoice deposit succeeds normally; the declaration, however, is not established. Read the period's declaration back to check that an expected sale does appear in it.
- A currency code that ISO 4217 does not recognise, on the invoice itself or on one of its VAT breakdown lines.
- A sale mixing taxable VAT breakdowns with
GorObreakdowns that Scribee cannot split between two aggregates (see The category and the amounts counted): a breakdown whosetax_category_idis not a known VAT category, aGorObreakdown carrying a VAT amount, or breakdowns whose amounts excluding tax or VAT amounts do not match the invoice totals. Such a sale is never declared whole underTLB1orTPS1. - An invoicing framework
B7orS7(VAT already collected). This framework states that the operation is already in a B2C aggregate: counting it again would declare it twice. An invoice issued under this framework is addressed to an identified business buyer; identify the buyer, or use the framework of the sale if it was never declared.
In the first two cases, the sale remains to be declared here. In the last one, create nothing for it: the operation is already declared.
A VAT breakdown in category O (outside the scope of VAT) may carry no rate. It is not refused: on both paths, the aggregate and the e-reporting invoice alike declare it with a vat_rate of 0.
The category and the amounts counted
The category (category_code) of a sale counted into the aggregate is derived from its invoicing framework: TLB1 for a delivery-of-goods framework (B1, B2...), TPS1 for a supply-of-services framework (S1, S2...). One exception takes precedence: an invoice whose VAT breakdown lines are all in category G (export outside the European Union) or O (outside the scope of VAT) is counted as TNT1, non-taxable operations, whether its framework is goods or services. This is the case of a sale of goods to a consumer established in Guyane, Mayotte or an overseas collectivity. An invoice that mixes such lines with taxable lines is split between two aggregates: its G and O breakdown lines are counted as TNT1, for their amount excluding tax and zero VAT, and the rest of the sale under its framework's category, TLB1 or TPS1, for the invoice's total excluding tax less the TNT1 share and for the whole of its VAT. The sale then counts as one operation in each of the two aggregates. It is never declared whole under TLB1 or TPS1: when its lines cannot be split that way, it is refused (see The sales the B2C aggregate refuses).
The octroi de mer does not enter the aggregate. A VAT breakdown line in category O whose tax_exemption_reason is OCTROI_MER is carried neither in the transaction's tax_subtotals nor in its total_amount_excluding_taxes, which receives the invoice's pre-tax total less that line's amount_without_taxes. Nor is it taken into account when deciding the TNT1 category.
Sales under the margin scheme
A sale under the margin scheme carries its VAT breakdown in category E, with the exemption reason VATEX-EU-F, VATEX-EU-I, VATEX-EU-J, or VATEX-EU-D: the invoice states the selling price, never the margin. The aggregate, for its part, declares this sale under TMA1, on the margin excluding VAT and the VAT due on that margin, rate by rate. You supply these amounts with the invoice, in the margin_bases array of POST /api/v1/workspaces/{workspace_id}/invoices or PATCH /api/v1/invoices/{id} (API reference).
Each element of the array describes the margin at one VAT rate, a single row per rate:
vat_rate: the VAT rate the margin is taxed at, one of the positive French rates (20.0,10.0,5.5...);margin_base_amount: the margin excluding VAT;margin_vat_amount: the VAT due on that margin;source:actualfor the actual margin,estimatedfor a margin derived from an estimated average margin rate, when the actual margin is not known at the time of the sale. Both are accepted and declared the same way.
Both amounts are zero or positive, with at most two decimals, and expressed in the invoice currency (currency_code). Scribee does not match the sale against your purchase invoices: no margin is computed for you, the base declared is the one you supply.
Excerpt of a creation payload:
{
"invoice": {
"invoicing_process_id": "B1",
"tax_subtotals": [
{ "tax_category_id": "E", "vat_rate": 0, "amount_without_taxes": 1500.0, "vat_amount": 0, "currency_code": "EUR", "tax_exemption_reason_code": "VATEX-EU-F" }
],
"margin_bases": [
{ "vat_rate": 20.0, "margin_base_amount": 300.0, "margin_vat_amount": 60.0, "source": "actual" },
{ "vat_rate": 10.0, "margin_base_amount": 45.45, "margin_vat_amount": 4.55, "source": "estimated" }
]
}
}
Read the bases back with GET /api/v1/invoices/{id}?include=margin_bases: they come back sorted by ascending rate, each with its id. Without include=margin_bases, the key is absent from the response.
On update, margin_bases follows its own rule: an absent key keeps the stored bases, an array replaces them all, [] clears them. Only a draft can be modified: once the invoice is deposited, a PATCH is refused with 403 and its bases no longer change. So supply them before the deposit.
To change only the bases, send margin_bases alone. A PATCH /api/v1/invoices/{id} whose only key it is replaces the bases, or clears them with [], and answers 200 with the invoice; it does not rebuild the rest of the invoice from the payload. As soon as another key comes with it, the call is a full update again, subject to the rules of step 2 of Issue a sales invoice: a collection such as lines sent without the invoice fields is refused with 422.
{
"invoice": {
"margin_bases": [
{ "vat_rate": 20.0, "margin_base_amount": 300.0, "margin_vat_amount": 60.0, "source": "actual" }
]
}
}
A refused row - a rate outside the list of positive French rates, a negative amount or one with more than two decimals, an unknown source, two rows at the same rate - fails the whole call with 422, with the code validation_failed and a details keyed on the offending attribute (vat_rate, margin_base_amount...). Nothing is stored; on update, the previous bases stay in place.
When the declaration is established, the sale counts as one operation under TMA1, with one breakdown line per base, at its rate: its total excluding tax is the sum of the margin_base_amount, its VAT total the sum of the margin_vat_amount. Outside the euro, the VAT on the margin is converted to euros at the rate frozen at deposit (see Invoicing in a currency other than the euro). The sale in the example above thus adds 345.45 excluding tax and 64.55 of VAT to the TMA1 aggregate.
The bases are transmitted as you supply them. Scribee therefore checks their plausibility at the invoice deposit: for a company declared in the emission wave, the deposit is refused with 422, and the invoice stays in draft, in three cases:
- no base is supplied;
- a
margin_vat_amountdiffers by more than one cent from itsmargin_base_amounttaxed at itsvat_rateand rounded to the cent; - the margins including VAT (
margin_base_amountplusmargin_vat_amount, across all rates) exceed the selling price carried by the invoice's VAT breakdown lines.
A single inconsistent base is enough to refuse the deposit. Correct the bases with a PATCH carrying only margin_bases, then deposit the invoice again. These are the refusals described in The sales the B2C aggregate refuses.
Down-payment invoices taken back out of the B2C aggregate
A down-payment invoice whose buyer carries no identifier is counted in the B2C aggregate, like any sale on the third row of the table. The final invoice settling it has it taken back out, for a company declared in the emission wave, in two cases:
- the final invoice leaves the B2C aggregate: its buyer, for its part, is identified, and it is deposited at the PPF or declared in the transaction data (10.1). The down payment then turns out to be declared in the wrong aggregate. The reversal follows the deposit of the final invoice.
- the final invoice is itself counted in the B2C aggregate: it is counted there for its whole amount, so the down payment would appear there twice; only the balance must remain declared. The reversal follows the counting of the final invoice.
The down payments taken back are those the final invoice designates by document_id in invoice_references (Retainer invoice and final invoice after a down payment). Do not also deduct them through a negative line citing their number in invoice_reference_number: they would be deducted twice, and the deposit of the final invoice is refused in that case.
Each counting is taken back by its opposite: what the down payment contributed - amount excluding taxes, tax total and the rate-by-rate breakdown - is written as a negative, under the same category (category_code) and in the same currency as at counting. The day of the reversal depends on the case. When the final invoice leaves the B2C aggregate, it is the day of its issue date (issue_date, BT-2), not its delivery date. When it is itself counted in the B2C aggregate, it is the day it is counted there: its delivery date (delivery_date), failing that its issue date. The reversal is written in the declaration covering that day. If that declaration has already been deposited at the PPF, the reversal moves to the first day of the next declaration, and so on up to the first declaration that has not been, never beyond today's date. A declaration in flight is not skipped: the reversal waits for the PPF's response and is retried automatically, a limited number of times. A reversal is not a sale: it does not change transaction_count, the number of sales (TT-85). GET /api/v1/e_reportings/{e_reporting_id}/transactions can therefore return a transaction with negative amounts, or, when a reversal is its only operation, a transaction whose transaction_count is 0 and whose totals are negative; the 10.3 flux then omits the transactions count. The transactions you write always declare at least one operation: a transaction_count at 0 is refused there (see below). For a final invoice counted in the B2C aggregate, unless a declaration is already deposited, the reversal therefore lands on the day the invoice was counted: when the down payment and the final invoice share the same category and the same currency, that day's transaction carries the balance.
The reversal is not written:
- when that day is after today's date;
- when the declaration covering that day and every later one up to today's date are deposited at the PPF;
- when no declaration covers that day for the company, its VAT regime not carrying the obligation on that day;
- when what the down payment contributed can no longer be recomputed from the down-payment invoice as it is recorded;
- when one of the refusals described in Retainer invoice and final invoice after a down payment, which apply to the deposit of the final invoice, arises between that deposit and the reversal.
The down payment then stays counted in the B2C aggregate alongside the final invoice, which carries the whole operation: the transaction in which it was counted keeps its amounts, and no negative amount is written. Scribee opens a case on its side to handle the reversal; that case is not exposed by the API.
A final invoice cancelled (220), or rejected (213) with no resend possible under the same number (see below), before the reversal is written takes nothing back: the down payment stays counted in the B2C aggregate, with no negative amount. A rejection after which the invoice can be resent under the same number does not stop the reversal, and resending the invoice starts it again.
The reversal compensated after the final invoice is cancelled or rejected
When a final invoice whose reversal is written is then cancelled (220) or rejected (213), the down payments it settled are no longer settled by any invoice: Scribee compensates the reversal. What each down payment contributed - total excluding tax, total VAT and the rate-by-rate breakdown - is carried again, as a positive amount, under the same category (category_code) and in the same currency as when it was counted, without changing transaction_count. When the final invoice was itself counted in the B2C aggregate, it is taken out of it in the same operation, or the down payments would appear there twice: what it contributed is carried as a negative amount, under its category and in its currency, without changing transaction_count either: when the compensation is its only operation, a transaction has a transaction_count at 0. The compensation is written on the day the cancellation or rejection took effect, in the declaration covering that day; if that declaration is already deposited at the PPF, it moves to the first day of the next declaration, as the reversal does. Only a rejection after which the invoice cannot be resent under the same number counts: a rejection pronounced at emission, or a rejection by the recipient's platform for duplication (reason REJ_UNI). Any other rejection by the recipient's platform leaves the reversal in place; the later cancellation of the invoice compensates it. Each reversal is compensated once only. A final invoice that is no longer cancelled or rejected when the compensation runs triggers none. A final invoice cancelled or rejected and replaced by a corrected invoice triggers none either: a corrected invoice (corrected_invoice, corrected_factored_invoice or their self-billed variant) of the same company, of the same nature, self-billed or not, whose sole invoice_references entry names the final invoice, which names no other buyer - when the buyer parties of both invoices each carry a legal_registration_id, it is the same -, and which is deposited. The corrected invoice then carries the operation: the down payments stay taken back, and nothing is carried again for them. When the final invoice was itself counted in the B2C aggregate, only its own counting is taken out of it, as above, the corrected invoice being counted there in its turn. A corrected invoice rejected (213) that can be resent under the same number still replaces it, and a corrected invoice in turn replaced by another leaves the operation to the latter. The final invoice is no longer replaced, and its reversal is compensated, when the corrected invoice carrying the operation is cancelled (220) without being replaced in its turn, or rejected (213) with no resend possible under the same number. Nothing is written, and Scribee opens a case on its side, not exposed by the API, when the corrected invoice carrying the operation is refused by the buyer (210), when it took effect before the reversal was written, when it replaces a final invoice whose reversal was already compensated, when the final invoice, counted in the B2C aggregate, was replaced before any reversal was written, the down payment it settles still counted there, or when the final invoice, which leaves the B2C aggregate, was replaced with no reversal ever written for it, a down payment it settles still counted in the B2C aggregate; in that last case, the case stays open as long as that down payment is counted there. A corrected invoice citing a final invoice whose reversal stands does not also deduct its down payments through a negative line citing their number in invoice_reference_number: they would be declared nowhere, and its deposit is refused in that case. A credit note issued on the final invoice triggers no compensation: the final invoice stays in force, and the down payments it settles are not counted again. When the compensation cannot be written - no open declaration up to today, or a counting, of the down payment or of the final invoice, whose contribution was not recorded -, nothing is written and Scribee opens a case on its side, not exposed by the API.
A down payment whose reversal was compensated is not taken back a second time: another final invoice settling it is refused at deposit (Retainer invoice and final invoice after a down payment).
Once the reversal is written, neither the transaction in which the down payment was counted, nor the one carrying the negative amount or its compensation, nor the one in which a final invoice counted in the B2C aggregate was counted can be changed or deleted any longer (see 422: transaction of a down-payment reversal below).
Once the reversal is written for a final invoice leaving the B2C aggregate, and as long as it is not compensated, the down-payment invoice's payments are no longer declared as payment data: Scribee no longer declares them from invoice payments, and refuses a payment declaration you write yourself naming the down-payment invoice (422: payment of a taken-back down-payment invoice).
What Scribee does for you
- Declarations prepared in advance: each company subject to the transactions reporting obligation receives its upcoming declarations, in
draftDraft state, before any data. A company whose current VAT regime does not carry that obligation is explicitly skipped by the sweep: no new declaration is prepared for it - do not treat an empty list as an anomaly. The list is not guaranteed empty for all that: declarations already populated are kept and stay visible, including after a change of regime. Preparation covers - with the period (start_date,end_date), the periodicity derived from its VAT regime (ten-day period, month, or two-month period), and the declaration deadline (due_date). - Separation by declarant role: data is aggregated by company, by period, and by role - seller (
SE) or buyer (BY); a sales operation and a purchase operation for the same period never share a declaration. - Read-only declarations: the declaration's
statefield is observed read-only, no endpoint modifies it, and its value is alwaysdraft. Each declaration also carries anavailable_transitionsarray, deprecated and always[]: do not build anything on it (E-reporting).
Transactions or invoices: which record to choose
Two record types feed a transactions-kind declaration:
- A transaction aggregates several B2C operations: a date, a category (
category_code), a count of operations (transaction_count), cumulative amounts, and a breakdown by VAT rate. No customer data appears in it. A transaction attaches only to a seller-role (SE) declaration. - An e-reporting invoice individually describes an invoice issued or received outside the e-invoicing channel - typically a cross-border operation: number, dates, seller and buyer identification, lines, and VAT breakdown. It attaches to the declaration whose role matches the direction of the operation:
SEwhen the declarant company sells,BYwhen it buys.
Choose the target declaration
The declaration's identifier appears in the path of every creation call: you choose the period by choosing the declaration. List the workspace's declarations, then pick the right one on three criteria, not two: the kind (kind is transactions), the period covering the date to declare, and the declarant role (role).
:::caution Kind plus period does not identify a declaration
Every period opens two transactions declarations per company - one where it declares as seller (role is SE) and one as buyer (role is BY). Add role and company_id to your selection: picking the first declaration whose kind and period match amounts to choosing at random between the two, and a declaration with an incompatible role is refused with a 422.
:::
The creation call then checks consistency with the chosen declaration's declarant role and refuses any mismatch (see 422: incompatible declarant role below). It does not check the period: targeting the declaration whose bounds cover the operation's date is up to you.
The list is paginated (page, per_page - 20 by default, 100 at most) and sortable (sort_by: start_date, end_date, due_date, kind, state, created_at, updated_at; sort_order: asc or desc; default sort: start_date descending); it exposes no filter.
curl https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/e_reportings \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response abridged to the fields useful here:
{
"data": [
{
"id": 512,
"kind": "transactions",
"role": "SE",
"state": "draft",
"start_date": "2026-07-01",
"end_date": "2026-07-31",
"due_date": "2026-08-10",
"company_id": 7
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total_pages": 1,
"total_count": 3
}
}
Declare B2C transactions
This call creates a transaction record in the declaration, in your production account. Nothing is transmitted to the administration at the time of the call; the record can be corrected (PATCH) and deleted (DELETE). The write scope is required and the response is a 201.
POST /api/v1/e_reportings/{e_reporting_id}/transactions requires date, category_code, transaction_count, total_amount_excluding_taxes, and total_tax_amount. Two fields are optional: currency_code, omitted, is stored as EUR; tax_due_date_code, omitted, comes back null in the response - an empty string is accepted too. Also accepted is tax_subtotals, the breakdown by VAT rate, where each element requires amount_without_taxes, vat_rate, and currency_code (a code from the ISO 4217 list, see the box below) and accepts vat_amount, tax_category_id, tax_exemption_reason, and tax_exemption_reason_code.
category_code states the nature of the operation: TLB1 taxable supply of goods, TPS1 taxable supply of services, TNT1 non-taxable operations, TMA1 operations taxable on the margin.
tax_due_date_code carries the VAT due-date code, among 5, 29, and 72 (UNTDID 2475) and 3, 35, and 432 (UNTDID 2005). It is optional: the regulation expects it only from operations under VAT on debits, and the API does not check that condition - if your regime does not require it, omit the field rather than putting a default value in it.
:::caution What the API validates, and what it does not
Five fields are validated, at creation as well as at modification, and answer 422 (see 422: missing or invalid fields below): a category_code outside its four values - TLB2, for instance - is refused; a transaction_count at zero or negative is refused; a date later than the day of the call is refused; a tax_due_date_code outside the six codes above is refused - its absence is not; a currency_code missing from the ISO 4217 list Scribee ships is refused, on the transaction as on each tax_subtotals subtotal.
The currency check covers the code's existence, not only its shape, and the comparison is case-sensitive: EUR passes, eur and USd are refused, and a well-shaped token that names no currency - XYZ, for instance - is refused too. This check is Scribee's own: the Flux 10 schematron checks the shape of the code and does not check its existence in the referential, so a value the PPF would accept is refused here. Upper-case your codes before the call: nothing is normalised on write.
The totals must reconcile with the VAT breakdown, as on the e-reporting invoice (see below). total_amount_excluding_taxes must equal the sum of the tax_subtotals amount_without_taxes, whatever the transaction currency; total_tax_amount must equal the sum of the vat_amount, but only when currency_code is EUR - which it is when you omit it. The accepted gap is 0.01 per amount summed; each amount is first rounded to two decimals, and a subtotal without vat_amount counts as 0.00. Do not rely on this tolerance: the PPF compares the amounts as binary floating-point numbers, and Scribee reproduces its computation, so a gap equal to the tolerance can exceed it - 1.01 declared over a single 1.00 subtotal is refused, while 1000.01 over 1000.00 passes. Send totals equal to the sum of their breakdown. Beyond that, the request is refused with a 422 before anything is stored (see 422: unreconciled totals below): the PPF would otherwise reject the whole declaration over this single transaction. A transaction without tax_subtotals is not checked. On modification, the check covers the transaction that results from the request, but only when the request sends total_amount_excluding_taxes, total_tax_amount, tax_subtotals, or currency_code: a PATCH on other fields is not reconciled, and a refused tax_subtotals replacement is rolled back and leaves the existing subtotals intact.
The rest is not. A date outside the start_date / end_date bounds of the target declaration is accepted, and the record stays attached to that declaration. Amounts at zero or negative also pass: a negative aggregate is legitimate, it carries credit notes. This data then feeds a declaration destined for the DGFiP: for everything the API does not check, validate it before the call.
:::
curl -X POST https://app.scribee.tech/api/v1/e_reportings/YOUR_E_REPORTING_ID/transactions \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"transaction": {
"date": "2026-07-15",
"category_code": "TPS1",
"currency_code": "EUR",
"tax_due_date_code": "35",
"transaction_count": 42,
"total_amount_excluding_taxes": 3500.0,
"total_tax_amount": 700.0,
"tax_subtotals": [
{
"amount_without_taxes": 3500.0,
"vat_amount": 700.0,
"vat_rate": 20.0,
"currency_code": "EUR",
"tax_category_id": "S"
}
]
}
}'
Response abridged to the fields useful here; it also carries created_at and updated_at:
{
"data": {
"id": 3101,
"date": "2026-07-15",
"category_code": "TPS1",
"currency_code": "EUR",
"tax_due_date_code": "35",
"transaction_count": 42,
"total_amount_excluding_taxes": 3500.0,
"total_tax_amount": 700.0,
"report_id": 512
}
}
Amounts are stored with four decimals but returned rounded to two: 3500.1234 is stored as sent and read back as 3500.12. Do not reconcile your ledger against the value read back.
The granularity of the aggregates is up to you: one record per day and per operation category is the most readable pattern, but the API imposes no granularity.
Correct or delete a transaction
A record is read, corrected, and deleted by its identifier, without e_reporting_id in the path. Correction and deletion operate on your production account and transmit nothing to the administration.
PATCH /api/v1/e_reportings/transactions/{id} changes only the fields sent, with the same checks - and the same missing checks - as creation; the totals reconciliation, for its part, applies only under the conditions described above. Providing tax_subtotals replaces the entire existing set: the subtotals in place are deleted then recreated, so their id values change on every replacement; sending "tax_subtotals": [] empties the breakdown, which a later PATCH can rebuild. A transaction that is part of a down-payment reversal cannot be changed at all: the PATCH is refused with a 422, with a body that has no details (see 422: transaction of a down-payment reversal below). The same holds for a transaction into which Scribee itself counted invoices of the B2C aggregate (see 422: transaction into which Scribee counted invoices below).
curl -X PATCH https://app.scribee.tech/api/v1/e_reportings/transactions/YOUR_TRANSACTION_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"transaction": {
"transaction_count": 45,
"total_amount_excluding_taxes": 3750.0,
"total_tax_amount": 750.0
}
}'
Response abridged to the fields useful here; as on creation, it carries every field of the record, including those the PATCH did not touch:
{
"data": {
"id": 3101,
"date": "2026-07-15",
"category_code": "TPS1",
"currency_code": "EUR",
"tax_due_date_code": "35",
"transaction_count": 45,
"total_amount_excluding_taxes": 3750.0,
"total_tax_amount": 750.0,
"report_id": 512
}
}
DELETE /api/v1/e_reportings/transactions/{id} responds 204 with no body and also deletes the associated VAT subtotals; the deletion is permanent. It can also be refused with a 422 when the declaration has been deposited at the PPF or is in flight to it (see 422: declaration deposited or in flight below), and when the transaction is part of a down-payment reversal - it carries the counting of a down-payment invoice that Scribee took back out of the B2C aggregate, or the negative amount of that reversal (see 422: transaction of a down-payment reversal below) -, and when Scribee itself counted invoices of the B2C aggregate into it (see 422: transaction into which Scribee counted invoices below). The endpoint accepts destroy or write: the write scope of the common read write combination is enough (Authentication).
curl -X DELETE https://app.scribee.tech/api/v1/e_reportings/transactions/YOUR_TRANSACTION_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Declare an invoice outside e-invoicing
This call creates an e-reporting invoice in the declaration, in your production account. Nothing is transmitted to the administration or to the final customer at the time of the call; the record can be corrected (PATCH /api/v1/e_reportings/invoices/{id}, which also replaces every nested set supplied) and deleted (DELETE /api/v1/e_reportings/invoices/{id}, response 204, or 422 if the declaration has been deposited or is in flight). The write scope is required and creation responds 201.
POST /api/v1/e_reportings/{e_reporting_id}/invoices requires invoice_number, issue_date, due_date, type_code, currency_code (a code from the ISO 4217 list, validated as on the transaction), seller_scheme_id, and buyer_scheme_id. type_code accepts the Scribee key (invoice, credit_note, corrected_invoice, ...) or the equivalent UNTDID 1001 code (380, 381, ...); responses always return the key. Also included is the rest of the parties' identification (seller_id, seller_vat_number, seller_country, buyer_id, buyer_vat_number, buyer_country), the amounts (total_amount_excluding_taxes, total_tax_amount), the tax_subtotals breakdown, the invoice-level item_notes, discounts, and expenses sets, the invoice_lines lines (with their own item_notes, discounts, and expenses), and the invoice_references references.
tax_due_date_code carries the TT-24 due-date code here, the Flux 10 TaxDueDateTypeCode element - the same business fact as BT-8 on a Flux 1 invoice, under the identifier e-reporting gives it. It stays optional, with a default on sales: omitted - or sent empty - on an SE declaration whose declaring company has opted for VAT on debits, it is stored as 5. A value you send always wins, and a BY declaration is never filled in: the invoice there is the supplier's, and stating their own tax point is theirs to do. The default applies at creation only; a PATCH does not revisit it.
issue_date cannot be later than the day of transmission. The write accepts a future date, but POST /api/v1/workspaces/{workspace_id}/e_reportings/{id}/submit then refuses the whole declaration with a 422, carrying code: "validation_failed" and a reason that names the invoice, its date, and the deposit day (E-reporting). A date after the end of the period, but not after the day of transmission, remains admitted.
Quantity, unit price, discount amount, and expense amount are mandatory. Every invoice_lines entry carries quantity and article_unit_price_excluding_taxes; every discounts entry, like every expenses entry, carries amount_excluding_taxes, both at the invoice level and nested inside a line. An absent key and an explicit null are refused the same way, with a 422, before anything is stored (see 422: missing or invalid fields below); 0 remains valid, a genuinely zero quantity, unit price, discount, or expense can be declared. Flux 10 reconciles none of these four values against any other amount: an unknown figure would go out as 0.00 with nothing to flag it. On modification, the check covers only what your request sends: a PATCH that does not mention invoice_lines demands nothing of the lines already stored, whereas a PATCH that sends them must state a figure on every entry it puts in place - a refused replacement is rolled back and leaves the existing lines intact.
The totals must reconcile with the VAT breakdown. total_amount_excluding_taxes must equal the sum of the tax_subtotals amount_without_taxes, whatever the invoice currency; total_tax_amount must equal the sum of the vat_amount, but only when currency_code is EUR. The accepted gap is 0.01 per amount summed - 0.03 for a breakdown of three subtotals; each amount is first rounded to two decimals, and a subtotal without vat_amount counts as 0.00. Do not rely on this tolerance: the PPF compares the amounts as binary floating-point numbers, and Scribee reproduces its computation, so a gap equal to the tolerance can exceed it - 1.01 declared over a single 1.00 subtotal is refused, while 1000.01 over 1000.00 passes. Send totals equal to the sum of their breakdown. Beyond that, the request is refused with a 422 before anything is stored (see 422: unreconciled totals below): the PPF would otherwise reject the whole declaration over this single invoice. An omitted total is not reconciled, and an invoice without tax_subtotals is not checked. On modification, the check covers the invoice that results from the request, but only when the request sends total_amount_excluding_taxes, total_tax_amount, tax_subtotals, or currency_code: a PATCH on other fields is not reconciled, so an invoice stored before this check stays editable even if its totals depart from its breakdown, and a refused tax_subtotals replacement is rolled back and leaves the existing subtotals intact.
Both identifier schemes are mandatory at creation. seller_scheme_id and buyer_scheme_id must carry a value: a payload that omits one, or sends it empty, is refused with a 422 before anything is stored (see 422: missing party scheme below). Flux 10 requires the scheme on both parties, so an invoice that states none cannot be declared; the refusal now lands on the call that produced it rather than on a later transmission. On modification, the check covers only what your request sends: a PATCH that does not mention buyer_scheme_id does not demand it, but a PATCH that sends "buyer_scheme_id": "" is refused just as at creation.
The identifier must then match the format implied by the scheme stated beside it: siren (0002) and tahiti (0229) require exactly nine digits, ridet (0228) nine or ten, european_union (0223) and international (0227) at most eighteen characters. A seller_id or a buyer_id outside that format is refused with a 422 (see 422: identifier outside its scheme's format below). The check only targets an identifier that is supplied: a party described by its VAT number alone, with no seller_id or buyer_id, has no format to verify.
Consistency with the declarant role is checked at creation: Scribee places the declarant company on the invoice, then refuses the write when the side it finds does not match the declaration's role. A side is recognised as the declarant company's own only if its seller_id - or its buyer_id - is exactly the company's nine-digit SIREN, as recorded in Scribee, and it carries no stated scheme other than siren. In other words, seller_scheme_id - or buyer_scheme_id - is siren (the code 0002 is accepted too). An absent scheme does not rule out the side that carries it, but now that both schemes are mandatory at creation, that case only concerns invoices stored before the obligation, which a PATCH covering other fields leaves as they are. The comparison ignores case and surrounding whitespace, and requires exactly one of the two parties to match, except for the stock transfer described below.
A stated european_union, international, ridet, or tahiti scheme describes a counterparty, never the declarant, and an identifier longer than nine digits does not match either: a SIRET sent on the declarant side does not designate it. siret no longer belongs to that list: Flux 10 admits it on neither party, and both seller_scheme_id and buyer_scheme_id reject it with a 422. In practice, send the side the declaration's role points at - seller_id on an SE declaration, buyer_id on a BY declaration - with the nine-digit SIREN and the siren scheme: that is the unambiguous form, and it remains recommended. Describe the counterparty under one of the four other schemes.
A transfer of your own goods between France and another member state carries your SIREN on both sides. When seller_id and buyer_id both designate the declarant company, Scribee tells the sides apart by their VAT numbers: the declared side is the one whose seller_vat_number - or buyer_vat_number - is a French VAT number attached to the company's SIREN, provided the other side carries the VAT number of another European Union member state. A French number on the seller side makes the invoice a movement out of France, to be declared on an SE declaration; on the buyer side, a movement into France, to be declared on a BY declaration. Consistency with the declaration's role is then checked as for any other invoice. Greece is written here under the EL prefix, and Northern Ireland's XI prefix is admitted. Any other pair - no VAT number, two French numbers, a Swiss, British, or Norwegian number on the other side - leaves the side undetermined and the request is refused (see 422: incompatible declarant role below).
On a BY declaration, the breakdown declares the VAT you self-assess. An invoice there is an acquisition: each tax_subtotals entry carries category AE at the rate booked in your accounts, not the supplier's zero-rated breakdown. A subtotal in category K or G, or in AE without a positive rate - vat_rate absent or 0 -, is refused with a 422 (see 422: acquisition not self-assessed below); at creation, the refusal takes everything with it: neither the invoice nor its subtotals are stored. The check covers every subtotal the request stores, on creation as on modification. An SE declaration is not affected.
:::caution VATEX-FR-CNWVAT is admitted on three invoice types only
A tax_subtotals entry whose tax_exemption_reason_code is VATEX-FR-CNWVAT is accepted only when the invoice carries one of these three types: credit_note (381), self_billed_credit_note (261), or factored_credit_note (396). On any other type_code - invoice (380) included - the request is refused with a 422, and the refusal takes everything with it: neither the invoice nor its subtotals are stored.
That set is narrower than the set of credit notes. self_billed_factored_credit_note (502) and retainer_credit_note (503) are credit notes and are treated as such everywhere else, but they do not carry this exemption reason.
The check runs again on modification, including when the request does not resend the breakdown: a PATCH that changes the type_code of an invoice whose stored subtotals carry VATEX-FR-CNWVAT is refused when the new type is not one of the three.
:::
curl -X POST https://app.scribee.tech/api/v1/e_reportings/YOUR_E_REPORTING_ID/invoices \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"invoice": {
"invoice_number": "FAC-2026-0192",
"issue_date": "2026-07-12",
"due_date": "2026-08-12",
"type_code": "invoice",
"currency_code": "EUR",
"seller_id": "843811544",
"seller_scheme_id": "siren",
"buyer_vat_number": "DE811907980",
"buyer_country": "DE",
"buyer_scheme_id": "european_union",
"total_amount_excluding_taxes": 12000.0,
"total_tax_amount": 0.0,
"tax_subtotals": [
{
"amount_without_taxes": 12000.0,
"vat_amount": 0.0,
"vat_rate": 0.0,
"currency_code": "EUR",
"tax_category_id": "K"
}
]
}
}'
Response abridged to the fields useful here:
{
"data": {
"id": 2087,
"invoice_number": "FAC-2026-0192",
"issue_date": "2026-07-12",
"due_date": "2026-08-12",
"type_code": "invoice",
"currency_code": "EUR",
"seller_id": "843811544",
"buyer_vat_number": "DE811907980",
"buyer_country": "DE",
"total_amount_excluding_taxes": 12000.0,
"total_tax_amount": 0.0,
"report_id": 498
}
}
View a declaration's records
GET /api/v1/e_reportings/{e_reporting_id}/transactions and GET /api/v1/e_reportings/{e_reporting_id}/invoices return the declaration's records, paginated (page, per_page - 20 by default, 100 at most). Transactions sort by date (default), category_code, transaction_count, total_amount_excluding_taxes, created_at, or updated_at; invoices by issue_date (default), due_date, invoice_number, type_code, created_at, or updated_at. sort_order is asc or desc, descending by default. The include parameter loads associations into the response: report and tax_subtotals for transactions; report, invoice_lines, tax_subtotals, item_notes, discounts, expenses, and invoice_references for invoices. A single record is read at GET /api/v1/e_reportings/transactions/{id} or GET /api/v1/e_reportings/invoices/{id}; the read scope is enough everywhere.
On the subtotals returned by include=tax_subtotals, tax_exemption_reason and tax_exemption_reason_code are not echoed back as sent: the code is upcased, and when either one is missing Scribee fills it from its VAT-regime catalog - including from tax_category_id alone when that category maps to a single regime. The K in the example above therefore comes back with tax_exemption_reason_code set to VATEX-EU-IC and the matching French wording, although the request carried neither; the S in the transaction example leaves both fields null.
What happens next
- The record joins the declaration whose identifier you supplied, in your production account; you can read it back immediately through the list endpoints. Nothing is transmitted to the administration at the time of the call.
- The API exposes no declaration total. The declaration list returns the period bounds, the deadline, and the state, never a total;
include=transactionsonly adds a summary of each attached record. Compute your totals yourself from the list endpoints. - The declaration's
statefield is your observation, read-only: no call on your side modifies it (E-reporting). - The period's declaration deadline is carried by
due_dateon the declaration.
Errors and edge cases
401 Unauthorized
Missing, expired, or invalid token. Empty body; request a new token at /oauth/token (Authentication).
403 Forbidden: insufficient scope
Creating, modifying, or deleting with a token limited to read:
{
"error": "forbidden",
"message": "Vous n'êtes pas autorisé à effectuer cette action"
}
Request a new token carrying read write.
403 Forbidden: workspace access
On the only call on this page that carries a workspace_id - the declaration list - a workspace that exists but that your application is not attached to returns a distinct message:
{
"error": "forbidden",
"message": "L'application n'a pas accès à cet espace de travail"
}
404 Not Found
The declaration or the record does not exist, or belongs to a workspace outside your application's scope:
{
"error": "not_found",
"message": "La ressource demandée est introuvable"
}
The /api/v1/e_reportings/... endpoints on this page resolve the declaration and the record among the workspaces your application can reach from your IP address: an address refused by the workspace's IPv4 allowlist therefore yields a 404 here, not the 403 the declaration list returns (Your first call). Check the identifier against the workspace's list of declarations, then check your calling address.
400 Bad Request: page past the last one
On the lists, requesting a page past the last page of a non-empty collection returns:
{
"error": "bad_request",
"message": "Le numéro de page dépasse le nombre de pages disponibles"
}
Bound your requests with meta.total_pages.
422: wrong declaration type
A transaction or an invoice created on a payments-kind declaration is refused:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Ce rapport ne peut pas contenir de transactions - son type doit être \"transactions\""]
}
}
For an invoice, the message is Ce rapport ne peut pas contenir de factures - son type doit être "transactions". Target a transactions-kind declaration; payments have their own declaration (Declare payments).
422: incompatible declarant role
A transaction on a BY-role declaration is refused with the message Les transactions ne peuvent être rattachées qu'à un rapport de rôle vendeur (SE). For an invoice whose parties do not match the declaration's role:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Le vendeur/acheteur de cette facture ne correspond pas au rôle déclarant du rapport"]
}
}
When neither seller_id nor buyer_id places the declarant company - or both do without their VAT numbers telling the sides apart - the message is Impossible de déterminer si la société est le vendeur ou l'acheteur pour cette facture - vérifiez les valeurs seller_id et buyer_id. This is the answer to expect from a payload that identifies the declarant under a scheme other than siren, or by an identifier longer than its SIREN. The same message covers a third case, where your payload is not at fault: the declarant company's record carries no usable legal identifier in Scribee - none at all, or fewer than nine digits - so no comparison is possible. If both of your identifiers are correct, fill in the company's legal identifier before retrying. The check runs again on modification as soon as seller_id, buyer_id, either scheme, or either VAT number is included in the request.
422: missing or invalid fields
A missing required field returns the validation envelope, with the messages always grouped under details.base - never under the offending field's name, even when a single field is at fault. Each message pairs the column name, in English, with the validation text, in French:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Category code doit être rempli(e)"]
}
}
The transaction's five validated fields take the same envelope and the same grouping under details.base. They accumulate: a payload at fault on all five receives all five messages.
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": [
"Devise ne fait pas partie de la liste des devises ISO 4217",
"Category code doit être TLB1 (livraisons de biens taxables), TPS1 (prestations de services taxables), TNT1 (opérations non taxables) ou TMA1 (opérations taxables sur la marge)",
"Tax due date code doit être 5, 29 ou 72 (UNTDID 2475) ou 3, 35 ou 432 (UNTDID 2005), ou rester vide",
"Transaction count doit être supérieur à 0",
"Date ne peut pas être postérieure à la date du jour"
]
}
}
The currency takes the same message on the e-reporting invoice and on each tax_subtotals subtotal. That message names the Devise attribute, never currency_code, and does not say which subtotal is at fault. A refused subtotal carries away the whole write: neither the record nor the other subtotals are created.
A discounts or an expenses entry with no amount_excluding_taxes takes the same envelope and the same grouping under details.base. The message is Amount excluding taxes doit être renseigné : le flux 10.1 déclarerait sinon une remise de 0,00 (AllowanceCharge/Amount) for a discount, and Amount excluding taxes doit être renseigné : le flux 10.1 déclarerait sinon un frais de 0,00 (AllowanceCharge/Amount) for an expense. Both share the same Amount excluding taxes prefix: only the word remise or frais tells them apart, and neither says which entry of the array is at fault.
The invoice's constrained-value fields - type_code, seller_scheme_id, buyer_scheme_id, seller_tax_proxy_scheme_id, invoicing_process_id - take a different path when the value sent is not recognised: the response is still a 422 with the same envelope, but the message placed in details.base is raw and entirely in English, of the form 'BAD' is not a valid type_code. Match on the field name it cites rather than on its wording. siret is the exception on seller_scheme_id and buyer_scheme_id: the value is recognised, but Flux 10 does not admit it, and the message comes back translated, still grouped under details.base, in the form Seller scheme doit être siren (0002), european_union (0223), international (0227), ridet (0228) ou tahiti (0229) ; siret (0009) n'est pas admis en flux 10.
422: missing party scheme
seller_scheme_id and buyer_scheme_id are checked before anything else, and their message does not follow the shape of the other validation messages: it cites the field under its API name, in lowercase, instead of the anglicised column name. The grouping under details.base is the same. The two sides accumulate - a payload that sends neither scheme receives both messages:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": [
"seller_scheme_id : le schéma d'identifiant de la partie est obligatoire ; sans lui la facture ne peut pas être déclarée en flux 10",
"buyer_scheme_id : le schéma d'identifiant de la partie est obligatoire ; sans lui la facture ne peut pas être déclarée en flux 10"
]
}
}
422: identifier outside its scheme's format
A seller_id or a buyer_id that does not match its scheme's format takes the usual shape: column name in English, text in French, under details.base. The two parties accumulate.
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Buyer ne respecte pas le format du schéma siren (siren et tahiti : 9 chiffres ; ridet : 9 ou 10 chiffres ; european_union et international : 18 caractères au plus)"]
}
}
This check runs after the declarant-role check, not before: a payload whose declarant side is itself misidentified first receives Impossible de déterminer si la société est le vendeur ou l'acheteur pour cette facture - vérifiez les valeurs seller_id et buyer_id. The format message is therefore the one you will see when the party at fault is the counterparty, or when the declarant is correctly identified elsewhere in the payload.
422: exemption reason reserved for credit notes
A tax_exemption_reason_code of VATEX-FR-CNWVAT on an invoice whose type_code is not credit_note (381), self_billed_credit_note (261), or factored_credit_note (396):
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Tax exemption reason code est réservé aux avoirs (types 261, 381, 396) ; cette facture porte le type 380"]
}
}
The message names the invoice type by its UNTDID 1001 code, even when your request sent the Scribee key. It does not say which subtotal is at fault: the breakdown is not indexed in the message, so it is on you to find the line carrying that code.
422: acquisition not self-assessed
A subtotal in category K or G, or in AE without a positive rate, on an invoice of a BY declaration. The reason, in details.base, names the refused category and recalls that an acquisition is declared with the self-assessed VAT, in category AE at the rate booked in your accounts. As with the exemption reason above, it does not say which subtotal is at fault.
422: unreconciled totals
A transaction or an invoice whose total_amount_excluding_taxes departs from the sum of the tax_subtotals amount_without_taxes, or - in EUR only - whose total_tax_amount departs from the sum of the vat_amount, beyond the tolerance of 0.01 per amount summed, evaluated the way the PPF does (see above: a gap equal to the tolerance can be refused). Each total out of line gets its own message, and the two add up. On an invoice:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": [
"[G1.53] total_amount_excluding_taxes : le total déclaré 1000.00 ne correspond pas à la somme des amount_without_taxes de tax_subtotals (1200.00), au-delà de la tolérance de 0,01 par montant additionné. Le PPF rejetterait tout le rapport sur cette facture.",
"[G1.53] total_tax_amount : le total déclaré 200.00 ne correspond pas à la somme des vat_amount de tax_subtotals (150.00), au-delà de la tolérance de 0,01 par montant additionné. Le PPF rejetterait tout le rapport sur cette facture en EUR."
]
}
}
The message quotes the declared total, then the computed sum in parentheses, both rounded to two decimals and written with a decimal point (1000.03), as in your request. On a transaction, the two messages are identical except for their ending: sur cette transaction. and sur cette transaction en EUR. instead of sur cette facture. and sur cette facture en EUR.. Correct the total or the breakdown so that they agree; a refusal on creation stores nothing, a refusal on modification leaves the record as it was.
422: declaration deposited or in flight
A declaration whose deposit the PPF has accepted (deposit_outcome of "300", E-reporting) no longer changes, and a transmitted declaration the PPF has not answered about does not change either until the answer arrives. All three verbs are affected the same way - creating, modifying, and deleting a record - because a line that appears and a line that disappears move Scribee's record equally far from what was transmitted. A rejected declaration ("301") stays correctable: that is exactly what the reform expects.
On creation and modification, the refusal takes the validation envelope described above:
{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Cette déclaration a été déposée auprès du PPF (300) : son contenu ne peut plus être modifié."]
}
}
On deletion the envelope differs: the refusal is about the whole declaration rather than about a field, so the body carries no details and the reason sits in message. Branch on code, which is never translated or reworded, rather than on message.
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette déclaration a été déposée auprès du PPF (300) : son contenu ne peut plus être modifié."
}
When the declaration is in flight, code is the same and message says the PPF has not answered yet and invites you to retry once the answer has arrived. That refusal is temporary: it lifts as soon as the outcome lands.
422: transaction of a down-payment reversal
A transaction that is part of a down-payment reversal (see Down-payment invoices taken back out of the B2C aggregate) can no longer be changed or deleted. Three transactions are concerned: the one in which Scribee counted the down-payment invoice, whose counting taken back must be kept; the one carrying the negative amount of the reversal, or its compensation, which must keep matching what was counted; and the one in which Scribee counted a final invoice whose reversal took down payments back, a sale that must stay declared beside the reversal. The refusal concerns the whole record: on change as on deletion, the body carries code: "operation_failed", the reason in message, and no details. A transaction PATCH can therefore answer 422 in two shapes: validation_failed with details when the payload is refused, operation_failed without details in this case. Branch on code.
On deletion, the message depends on the transaction's role in the reversal. For the one in which the down payment was counted:
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Ce cumul journalier de transactions ne peut pas être supprimé : une mensualité qui y est comptée a été reprise des données de transaction B2C par la facture finale qui la solde, et ce comptage doit être conservé."
}
For the one carrying the negative amount, the message is Ce cumul journalier de transactions ne peut pas être supprimé : il porte le montant négatif qui reprend une mensualité des données de transaction B2C, enregistré pour la facture finale qui la solde, et cette reprise doit être conservée., including for the one carrying a compensation. For the one in which the final invoice was counted, the message is Ce cumul journalier de transactions ne peut pas être supprimé : il compte une facture de solde dont la reprise a retiré des données de transaction B2C les mensualités qu'elle solde, et cette vente doit rester enregistrée à côté de la reprise.
On change, the envelope is the same, and the message is Ce cumul journalier de transactions ne peut pas être modifié : une mensualité qui y est comptée a été reprise des données de transaction B2C par la facture finale qui la solde, et le modifier ne correspondrait plus à ce qui a été repris. for the transaction in which the down payment was counted, and Ce cumul journalier de transactions ne peut pas être modifié : il porte le montant négatif qui reprend une mensualité des données de transaction B2C, enregistré pour la facture finale qui la solde, et le modifier ne correspondrait plus à ce qui a été compté. for the one carrying the negative amount or its compensation, and Ce cumul journalier de transactions ne peut pas être modifié : il compte une facture de solde dont la reprise a retiré des données de transaction B2C les mensualités qu'elle solde, et le modifier ne correspondrait plus à cette reprise. for the one in which the final invoice was counted.
422: transaction into which Scribee counted invoices
A transaction into which Scribee itself counted invoices of the B2C aggregate (see What Scribee declares on its own from your invoices) can no longer be changed or deleted, whether or not it is part of a down-payment reversal: its totals must keep matching what was counted into it. Scribee counts a sale into the declaration's transaction that already carries the same date, the same category_code and the same currency_code, and creates one only when none exists: a transaction you declared yourself therefore falls under this refusal as soon as Scribee counts an invoice into it. A transaction into which Scribee counted no invoice stays changeable and deletable. The refusal concerns the whole record, with the same envelope as for a down-payment reversal: code: "operation_failed", the reason in message, and no details. When the transaction is also part of a down-payment reversal, the reversal's message is the one returned (see 422: transaction of a down-payment reversal above).
On deletion:
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Ce cumul journalier de transactions ne peut pas être supprimé : la plateforme y a compté elle-même des factures déclarées dans les données de transaction B2C, et ces déclarations doivent être conservées."
}
On change, the envelope is the same, and the message is Ce cumul journalier de transactions ne peut pas être modifié : la plateforme y a compté elle-même des factures déclarées dans les données de transaction B2C, et le modifier ne correspondrait plus à ce qui a été compté.
Related pages
- E-reporting - declarations, their periods, and their states
- Declare payments - declaring collections
- API reference: list e-reporting invoices
- API reference: create an e-reporting invoice
- API reference: list e-reporting transactions
- API reference: create an e-reporting transaction
- API reference: create an invoice
- API reference: update an invoice