Skip to main content

Declare payments

For sales whose VAT is due on collection - the default regime for services - it is the payment, not the invoice's issuance, that triggers the tax point: e-reporting therefore requires declaring the amounts collected for each period. In Scribee, this declaration is a byproduct of your payment tracking: every payment recorded on a relevant sales invoice - an international B2B sale or a B2C sale - feeds the declaration for its period, with no additional call. The endpoints on this page are for viewing these declarations and declaring collections that your system does not track as invoice payments.

What Scribee does for you​

  • Declaration derived from invoice payments: recording, correcting, or deleting an invoice payment (Record payments) creates, updates, or deletes the corresponding declaration line - provided e-reporting covers the sale and the issuing company is subject to the payment reporting obligation on the payment date.
  • Period report created as needed: the line joins the payments report whose period covers the collection date; if it does not exist, Scribee creates it in draft Draft status, with the periodicity derived from the company's VAT regime.
  • Breakdown by VAT rate: the collected amount is split across the invoice's VAT rates, in proportion to the tax-inclusive amount for each rate. The share that falls to reverse-charged rates (VAT category AE) is not declared, nor carried over to the other rates, and neither is that of a breakdown in category G (export outside the European Union) or O (outside the scope of VAT): it is not subject to VAT in France. On a double invoice, which mixes goods and services, only the services share is declared. The octroi de mer is never declared (details below).
  • Amounts in euros: the declared share is converted to euros when the line is written; a derived line always carries currency_code EUR.
  • Exclusions applied automatically: purchase invoices generate no payment declaration, nor do sales invoices carrying any tax-point code other than collection, nor sales whose VAT breakdowns are all in category G or O, nor those their buyer, their seller, their reverse charge or their nature as a delivery of goods rules out (details below).
  • Read-only declarations: the report's status is observed read-only, no endpoint modifies it.

Automatic tracking from invoice payments​

Automatic tracking covers sales invoices whose VAT is due on collection, issued by a company subject to the payment reporting obligation on the payment date. Without that obligation no line is created, silently: the API does not signal the exclusion. An invoice that specifies no tax-point code (tax_due_date_code absent) is treated as due on collection - the default regime for services. Any other tax-point code excludes the invoice: only the absence of a code, or the collection code itself, produces a payment declaration. A purchase invoice is excluded too, as is any invoice issued by a company with no payment reporting obligation. A sale whose VAT breakdowns are all in category G (export outside the European Union) or O (outside the scope of VAT) is excluded too: it is not subject to VAT in France, whoever its buyer. An octroi de mer breakdown (category O, tax_exemption_reason equal to OCTROI_MER) does not count towards this. A disbursement sale - every line carrying disbursement set to true, every VAT breakdown in category O, or E under VATEX-EU-79-C, with no VAT amount - is excluded likewise (Issue a sales invoice). Only the disbursement marker makes a line a disbursement: the VATEX-EU-79-C code without that marker does not exclude the invoice on that ground. For those invoices, recording a payment only serves to track settlement.

Automatic tracking furthermore covers only the sales that e-reporting declares, according to how the buyer is identified on the invoice. A sale whose buyer is identified without a SIREN - by a foreign VAT number, for instance -, or whose buyer carries a SIREN but is established outside the French VAT territory, is an international B2B sale: each payment on it becomes a line attached to the invoice. A sale whose buyer carries no identifier is a B2C sale: each payment on it becomes an aggregated collection.

Four sales are excluded on top of that, whatever their tax-point code and the company's regime, and the API does not signal the exclusion either:

  • a sale to a buyer identified by a SIREN or a SIRET, and established in the French VAT territory or stating no country - the one whose invoice is deposited with the PPF (Declare transactions): it is a domestic B2B sale, whose collection is declared by the 212 Collected status of the invoice itself (Record payments), not by a payment declaration line. A buyer carrying a SIREN but established outside that territory, in a foreign country as in the overseas territories, is not excluded on that ground: the sale is an international B2B sale;
  • a sale by a seller established outside the French VAT territory: a seller established in French Guiana, Mayotte, an overseas collectivity or the French Southern and Antarctic Lands, according to the seller address the invoice carries - including an FR address whose postal code places it in one of those territories (Overseas territories outside the VAT territory) - owes no sales e-reporting at all, payments included;
  • a wholly reverse-charged sale: when every VAT breakdown of the invoice, other than those in category G or O, carries tax_category_id equal to AE, the VAT is due by the buyer and the sale does not enter the payment declaration;
  • a delivery of goods: an invoice whose billing framework names goods - a code starting with B, B1 or B7 for instance - is excluded, unless it is a down-payment invoice (retainer_invoice or self_billed_retainer_invoice). Without invoicing_process_id, that framework is the one Scribee derives from product_type on the lines. A mixed framework (M1, M2, M4), a services framework - S7 included - or no framework at all does not exclude the invoice.

A sale where only some of the breakdowns carry AE, G or O stays declared, but without those breakdowns: the collected amount is first split across all the invoice's rates, then the AE, G and O shares are set aside. The sum of the line's amount_collected values is then lower than the payment amount.

A double invoice - invoicing framework M1, M2, or M4 - declares only the services share: the declared collected amount is the payment's, multiplied by the service lines' total including tax over the invoice's total including tax, and the breakdown carries only the service lines' amounts. The nature of each line is read from its product_type, failing that from its quantity_unit_code (Issue a sales invoice). A double invoice whose services share is zero - it carries only goods, its services are wholly reverse-charged, or their total nets to zero - produces no line. When it also carries G or O breakdowns, the services share is computed on its taxable lines and breakdowns alone: the G and O lines weigh on neither side, and their breakdowns are not declared.

The octroi de mer is not VAT: a breakdown line in category O whose tax_exemption_reason is OCTROI_MER never appears as a rate line in the declaration. On a sale carrying octroi de mer, the declared collected amount is the payment's, multiplied by the VAT breakdowns' total including tax over the invoice's total including tax, octroi de mer included. A payment of 1,210 on an invoice of 1,000 excluding tax, 200 of VAT at 20 % in category S and 10 of octroi de mer thus declares a single rate line, 20 %, with an amount_collected of 1,200. A sale left with nothing to declare once the octroi de mer is set aside - no other breakdown, breakdowns that are all reverse-charged (AE) or in category G or O, or a total that nets to zero - produces no line.

Amounts are declared in euros. Since VAT on collection is chargeable on payment, a share collected in another currency is converted at the euro reference rate published for the date of that payment - the ECB's or, for some currencies, that of the central bank quoting them. Each payment on the same invoice is therefore converted at its own rate, and that is the only rate used: the rate recorded on the invoice when it was deposited never converts a collection. The converted amount is frozen on the line: only a correction of the payment recomputes it.

A payment whose date has no rate yet is put on hold, not lost. Reference rates are published daily, often after a same-day payment is recorded. While the rate for its date is missing, the payment is recorded but its declaration line is neither created nor updated; Scribee retries automatically, every hour, and writes the line at the payment-date rate as soon as it is published. A transmission made in the meantime goes without that payment; if that declaration is accepted, the period then owes a rectificative transmission, which will carry it. The wait is bounded to 38 days, counted from the payment date or from the day the payment was first held, whichever is earlier. It applies to every currency, including one invoiced for the first time. Beyond that, the payment is no longer retried (see below).

Eight situations nonetheless leave a payment that automatic tracking covers without a declaration line, silently: the payment stays recorded on the invoice, but no line is created - and an existing line is not updated, it stays as it was. The API signals nothing.

  • A currency outside the ISO 4217 list on a declared VAT breakdown of the invoice, or on the invoice itself when the breakdown states none. The breakdowns the declaration sets aside - reverse charge, categories G and O, octroi de mer - are not checked.
  • A negative amount due for payment (payable_amount, BT-115) on an invoice that is not a credit note: the payment is then a refund to the buyer, which a payment declaration cannot carry with a negative sign.
  • A sale under the margin scheme: a VAT breakdown line of category E whose exemption reason is VATEX-EU-F, VATEX-EU-I, VATEX-EU-J, or VATEX-EU-D, whether or not its margin bases (margin_bases) are supplied.
  • A double invoice whose lines do not split between goods and services: in particular no detail line, a line whose product_type is both, no VAT breakdown, or line amounts that net to zero.
  • A double invoice whose services share falls outside the payment: negative lines - a discount entered as a services line, for instance - put the services' total including tax below zero or above the invoice's total including tax.
  • A sale carrying octroi de mer whose VAT share falls outside the payment: negative breakdown lines - a negative octroi de mer, for instance - put the VAT breakdowns' total including tax below zero or above the invoice's total including tax.
  • A down-payment invoice taken back out of the B2C aggregate by a final invoice leaving it: the final invoice settling it is deposited at the PPF or declared in the transaction data (10.1), and had its counting taken back (Down-payment invoices taken back out of the B2C aggregate). A payment of that down-payment invoice is no longer declared as payment data, whatever its date, as long as the reversal is not compensated. No correction of the invoice is expected. A reversal made for a final invoice itself counted in the B2C aggregate is not concerned: the down-payment invoice's payments stay declared.
  • A euro rate that can no longer arrive: the 38-day wait described above ran out without a rate being published for the payment date.

The declaration remains owed: once the cause is lifted, the next correction of the payment produces the line. A down-payment invoice taken back out of the B2C aggregate by a final invoice leaving it is the exception: that cause is not lifted by a correction, and its payment is simply not declared. A payment declaration you write yourself naming that down-payment invoice is refused likewise (see 422: payment of a taken-back down-payment invoice below). The cause is lifted only if the final invoice is then cancelled (220), or rejected (213) with no resend possible under the same number, and not replaced by a corrected invoice: the reversal is then compensated (The reversal compensated after the final invoice is cancelled or rejected), and Scribee declares on its own, with no correction on your part, the down-payment invoice's payments that have no line. They follow the rules of any payment: a closed declaration sets the line aside.

Each derived line carries the collection date and the breakdown of the collected amount by VAT rate. A line from an international B2B sale also carries the number and issue date of the original invoice. A sale to a buyer the invoice does not identify - no SIREN, no legal_registration_id, no vat_identifier, or no buyer party at all - is the exception: its line is an aggregated collection, and both invoice_number and invoice_date are null on it. The API returns one line per payment; in the transmitted declaration, the B2C collections of a single day are summed per VAT rate and per currency. If you correct a payment's date to another period still subject to the obligation, the line moves to that period's report, created if necessary; if you delete the payment, the line is removed. If the invoice no longer meets the conditions of this section when you correct a payment - buyer now identified by a SIREN and established in the French VAT territory, delivery of goods, VAT switched to reverse charge or to the debit option -, the correction removes the existing line instead of updating it; the company's obligation, for its part, follows the callout below. While the declaration holding the line is closed - a deposit awaits its verdict, or the declaration was accepted -, the line stays in place: the change is set aside, then applied if the declaration becomes editable again after a 301 rejection, or carried by the rectificative transmission the period owes if it was accepted. You do not call any of the endpoints below for this: recording the payment on the invoice is enough (Record payments).

:::caution Correction and deletion do not behave the same way outside the obligation The three operations do not share one condition. Deletion removes the existing line whatever the eligibility at the time of the call. Correction can only land on a period actually subject to the obligation: if you move an already-declared payment to a date whose regime does not carry the obligation, no report can receive it and the existing line stays attached to its previous report, neither updated nor removed. The original declaration therefore keeps carrying an amount at a date that is no longer its own, and the API signals nothing. After a move like that, re-read the original period's report, and delete then recreate the payment rather than correcting its date when you need the line gone. :::

View a report's declarations​

GET /api/v1/e_reportings/{e_reporting_id}/payments returns the report's declaration lines; the read scope is enough and the call changes nothing. A period's payments report is found in the workspace's report list, GET /api/v1/workspaces/{workspace_id}/e_reportings, by its kind (payments, labeled Payments), start_date, and end_date fields (API reference: list reports).

curl https://app.scribee.tech/api/v1/e_reportings/YOUR_REPORT_ID/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{
"id": 305,
"payment_date": "2025-01-10",
"invoice_date": "2024-12-15",
"invoice_number": "F2024-0198",
"report_id": 57
},
{
"id": 304,
"payment_date": "2025-01-06",
"invoice_date": "2024-12-02",
"invoice_number": "F2024-0185",
"report_id": 57
}
],
"meta": {
"current_page": 1,
"per_page": 20,
"total_pages": 1,
"total_count": 2
}
}

The list is paginated (page, per_page - 20 by default, 100 maximum) and sortable (sort_by: payment_date, invoice_date, invoice_number, created_at, or updated_at; sort_order: asc or desc; default sort: payment_date descending). The include parameter loads the report and tax_subtotals associations on demand. A single line can be read at GET /api/v1/e_reportings/payments/{id}.

Declare a collection manually​

Manual CRUD serves collections that Scribee does not see happen - typically a sale whose invoice is not tracked in Scribee. This call creates a declaration line in the targeted report, in your production account. Nothing is sent to the DGFiP or to any third party at the time of the call; the line can be corrected (PATCH) and deleted (DELETE). The write scope is required.

:::danger Period membership is not checked - and cannot be repaired Target the payments report whose period covers the collection date yourself: nothing verifies it server-side. A payment_date outside the targeted report's bounds is accepted without an error, and the entry stays attached to that report. PATCH does not fix it: it changes the date, never the report. No endpoint moves an entry from one report to another - the only repair is to delete the entry and recreate it in the right report. Check the report's start_date and end_date before calling: this is regulatory data, not metadata. :::

POST /api/v1/e_reportings/{e_reporting_id}/payments accepts payment_date (required), invoice_date, and invoice_number (optional, and inseparable: both, or neither), plus tax_subtotals, the breakdown by VAT rate described below.

:::info invoice_date and invoice_number go together A collection is declared in two forms, and two only: attached to an invoice, where it carries that invoice's number and issue date; or aggregated, where it carries neither. There is no intermediate form, so a request that fills in only one of the two fields receives a 422. Send the complete pair, or omit it entirely. :::

curl -X POST https://app.scribee.tech/api/v1/e_reportings/YOUR_REPORT_ID/payments \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payment": {
"payment_date": "2025-01-10",
"invoice_date": "2024-12-15",
"invoice_number": "F2024-0198"
}
}'
{
"data": {
"id": 305,
"payment_date": "2025-01-10",
"invoice_date": "2024-12-15",
"invoice_number": "F2024-0198",
"report_id": 57
}
}

A line created via the API carries these three fields; the response is a 201.

The breakdown by VAT rate is filled in two ways: automatic tracking from invoice payments builds it for you, and both the POST and the PATCH accept a nested tax_subtotals array. Each entry accepts amount_collected, vat_rate, currency_code, tax_category_id, tax_exemption_reason and tax_exemption_reason_code; amount_without_taxes and vat_amount are not accepted on write - a collection carries the collected amount alone. It is also the only place a payment states a currency. On a line from automatic tracking, it is always EUR: the amount there is already converted. amount_collected cannot be negative (see Errors and edge cases).

On read, include=tax_subtotals returns amount_collected, the only amount a collection subtotal carries, beside id, vat_rate, currency_code, tax_category_id, the exemption fields, created_at and updated_at. amount_without_taxes and vat_amount are present too but always null: do not build an accounting reconciliation on those two fields.

Correct or delete a declaration​

These calls modify or delete the line in your production account; nothing is sent to a third party at the time of the call.

:::caution These endpoints also reach derived lines They are not limited to lines you created: they accept the identifier of any line in your scope, including those produced by automatic tracking. A correction applied to a derived line is overwritten as soon as the originating invoice payment changes - the line is then rebuilt from the invoice, subtotals included. To correct a derived line durably, correct the payment on the invoice (Record payments), not the declaration. :::

PATCH /api/v1/e_reportings/payments/{id} accepts the same fields as creation; only the fields sent change. tax_subtotals is the exception: supplying the key replaces the entire existing set - the subtotals in place are deleted then recreated, so their id changes on every replacement, and "tax_subtotals": [] empties the breakdown. A request that does not mention the key leaves the breakdown untouched.

curl -X PATCH https://app.scribee.tech/api/v1/e_reportings/payments/YOUR_PAYMENT_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "payment": { "payment_date": "2025-01-12" } }'
{
"data": {
"id": 305,
"payment_date": "2025-01-12",
"invoice_date": "2024-12-15",
"invoice_number": "F2024-0198",
"report_id": 57
}
}

DELETE /api/v1/e_reportings/payments/{id} responds 204 with no body. The write scope is required.

curl -X DELETE https://app.scribee.tech/api/v1/e_reportings/payments/YOUR_PAYMENT_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

What happens next​

  • The collection joins the declaration for its period in your production account; you can read it back immediately through the list endpoints.
  • The declaration's state field is read-only: no API endpoint modifies it (E-reporting).

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​

Writing or deleting with a token limited to read:

{
"error": "forbidden",
"message": "Vous n'êtes pas autorisé à effectuer cette action"
}

404 Not Found​

The report or the declaration line does not exist, or belongs to a workspace outside your application's scope:

{
"error": "not_found",
"message": "La ressource demandée est introuvable"
}

400 Bad Request: page beyond the last​

On the list, requesting a page beyond 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"
}

422: wrong report type​

A POST on a report of type transactions is refused - payment declarations only live in a report of type payments:

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Ce rapport ne peut pas contenir de paiements - son type doit être \"payments\""]
}
}

Validation refusals - the ones carrying a details.base - all announce code: "validation_failed". That is the key to branch on: it is neither translated nor reworded, while message and the details.base texts arrive in French. A refused DELETE takes a different code, operation_failed, and carries no details: the reason is in message instead.

422: report with buyer role​

Payment declarations can only attach to reports where the company declares as seller; this is the role of the reports Scribee creates for your sales collections:

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Les paiements ne peuvent être rattachés qu'à un rapport de rôle vendeur (SE)"]
}
}

422: missing collection date​

payment_date is required at creation. The response carries the envelope error: "unprocessable_entity" with code: "validation_failed" and message: "La validation a échoué", and the validation message arrives in details.base.

422: incomplete invoice reference​

invoice_number and invoice_date are filled in together or not at all. A request carrying only one of them is refused, on creation as on correction, with the same envelope as above - code included - and the validation message in details.base. To attach a collection to an invoice, send both fields; to declare it as an aggregated collection, send neither.

422: currency outside the ISO 4217 list​

A subtotal's currency_code must appear in the ISO 4217 list Scribee ships, at creation as at correction. The check covers the code's existence and not only its shape, and the comparison is case-sensitive: EUR passes, eur and USd are refused, as is a well-shaped token that names no currency - XYZ, for instance.

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

The message names the Devise attribute, never currency_code, and does not say which subtotal is at fault. The refusal carries away the whole call: on a POST the line is not created, and on a PATCH the previous breakdown stays in place, untouched. Upper-case your codes before the call - nothing is normalised on write.

422: negative collected amount​

A negative amount_collected is refused, at creation as at correction: rule G1.16 of Annexe 7 forbids a sign on a collected amount, so an already-declared collection is not cancelled by a negative one. Zero remains accepted.

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Amount collected ne peut pas être négatif (annexe 7, règle G1.16) : le PPF refuse le signe sur un montant encaissé, un encaissement provisoire ne peut donc pas être annulé par un encaissement négatif"]
}
}

The message names the Amount collected attribute, never amount_collected, and does not say which subtotal is at fault. As with the currency, the refusal carries away the whole call: on a POST the line is not created, and on a PATCH the previous breakdown stays in place, untouched.

422: payment of a taken-back down-payment invoice​

A payment naming a down-payment invoice taken back out of the B2C aggregate by a final invoice leaving it - deposited at the PPF or declared in the transaction data (10.1) (Down-payment invoices taken back out of the B2C aggregate) - is refused, at creation as at correction: that down-payment invoice's payments are no longer declared as payment data. The payment names the down-payment invoice when its invoice_number is exactly that invoice's number, case and accents included, and, when invoice_date is filled in, that date is its issue date. The down-payment invoice is searched in every company of the workspace, not only in that of the targeted report. On correction, it is the line as the call would leave it that is examined: a PATCH changing only payment_date is refused too when the line names such a down-payment invoice.

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Ce paiement porte sur la facture d'acompte A2024-0042, dont la déclaration dans les données de transaction B2C a été reprise par la facture finale qui la solde : l'opération ne relève plus du circuit B2C, ses paiements ne se déclarent donc plus en e-reporting. Retirez ce paiement ou rattachez-le à une autre facture."]
}
}

The message names the down-payment invoice by its number. The refusal carries away the whole call: on a POST the line is not created, and on a PATCH the line stays as it was. DELETE is not concerned. A reversal made for a final invoice itself counted in the B2C aggregate does not trigger this refusal, and the refusal ends as soon as the reversal is compensated - the final invoice cancelled (220), or rejected (213) with no resend possible under the same number, and not replaced by a corrected invoice (The reversal compensated after the final invoice is cancelled or rejected).

A CSV import of payment data applies the same refusal: each line at fault is reported with the code instalment_taken_back and this message, and no line of the file is imported.