Skip to main content

Issue a sales invoice

Your software produces sales invoices. This page follows the API-side journey: create the invoice as a draft from your system, adjust it, then deposit it - the moment Scribee finalizes the document, assigns its definitive number, and records status 200 Deposited. You are working in production: the draft phase is your rehearsal space, the deposit is the point of no return.

What Scribee does for you​

  • Completes your payload with directory data (party_id) and the issuing company's default values: seller party, legal notices, banking details.
  • Assigns the definitive number at deposit, from the numbering template configured in the Scribee interface's invoicing settings. That replacement only happens when the number carried by the invoice starts with DRAFT-.
  • Generates the four deliverable formats: pdf, ubl (UBL 2.1), cii (CII EN16931), facturx (PDF/A-3 with embedded XML). Generation is asynchronous and restarts at creation, on every PATCH, and at deposit. The pdf is the exception when you supplied your own (provided_pdf set to true) or when the invoice came from a PDF or image import: the original file is kept and never overwritten.
  • Notifies your webhooks: invoice.created at creation, invoice.lifecycle_event.created on every transition (Webhooks).

What the deposit does not do​

The deposit sends the invoice neither to the PPF (Portail Public de Facturation, the French public invoicing portal), nor to the Peppol network, nor by email to your client. It records status 200 on the invoice, regenerates the formats (a PDF you provided is kept as is), optionally hands the document to a connected accounting software, and notifies your webhooks.

As a consequence, the deposit call itself produces no recipient status. Once the invoice does travel the regulatory channel, however, the statuses issued by the recipient's platform (202 Received, 205 Approved, 210 Refused, 213 Rejected) are applied by inbound-flow processing and written into lifecycle_events. That history is therefore not limited to your own transitions plus the automatic move to 212 Collected (Record payments): expect statuses you did not trigger.

To address the document to your client, use Send by email and track delivery.

The journey​

Prerequisites​

The chosen company determines everything else: the seller, the legal notices, the banking details, the numbering sequence, and the directory in which party_id is resolved. A party_id belonging to another company of the same workspace is rejected.

Step 1: create the draft invoice​

This call creates an invoice in the draft state (000 Draft) in your workspace. Nothing is transmitted. The draft stays editable until deposit, and deletable as long as its number starts with DRAFT-.

For Scribee to assign the definitive number at deposit, send a provisional number prefixed DRAFT- (unique per company - suffix it with your internal reference). If you send a definitive number right away, it is kept as-is at deposit, but the draft is then no longer deletable via the API.

warning

article_name, tax_category_id, article_unit_price_excluding_taxes, quantity, and quantity_unit_code are required on every line. This call writes no placeholder here: if any of the five is missing on a line, the entire creation fails with 422 and nothing is saved - neither the line nor the invoice. This is different from the four invoice-level fields below, whose absence lets the call through with a 201 status.

curl -X POST https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/invoices \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_id": 317,
"invoice": {
"direction": "sales",
"invoice_number": "DRAFT-CRM-2026-0042",
"issue_date": "2026-07-31",
"due_date": "2026-08-30",
"type_code": "invoice",
"currency_code": "EUR",
"parties": [
{ "role": "buyer", "party_id": 42 }
],
"lines": [
{
"line_id": "1",
"article_name": "Prestation de conseil",
"quantity": 2.0,
"quantity_unit_code": "day",
"article_unit_price_excluding_taxes": 500.0,
"line_extension_amount": 1000.0,
"tax_category_id": "S",
"vat_rate": 20.0
}
],
"tax_subtotals": [
{ "tax_category_id": "S", "vat_rate": 20.0, "vat_amount": 200.0, "amount_without_taxes": 1000.0, "currency_code": "EUR" }
],
"total_amount_excluding_taxes": 1000.0,
"total_tax_amount": 200.0,
"tax_inclusive_amount": 1200.0,
"payable_amount": 1200.0
}
}'

201 response, abridged to the fields useful here:

{
"data": {
"id": 12345,
"invoice_number": "DRAFT-CRM-2026-0042",
"type_code": "invoice",
"lifecycle_state": "draft",
"lifecycle_status_code": "000",
"lifecycle_available_transitions": ["deposit"],
"tax_inclusive_amount": 1200.0,
"customization_id": null,
"upload_source": "manual",
"delivery_status": "not_delivered"
}
}

A missing required field does not fail the call​

This is the most important behaviour on this page. Four fields condition the deposit: invoice_number, issue_date, type_code, currency_code. If one is missing, the call still returns 201: Scribee writes a placeholder value and records a blocking import error.

Missing fieldValue written instead
invoice_numberTEMP- followed by 8 hexadecimal characters
issue_datetoday's date
type_codeinvoice
currency_codeEUR

That import error is exposed by no field of the response. Three signals give it away:

  • lifecycle_available_transitions is []: deposit is removed from it as long as the error is present;
  • none of the four formats is generated, so GET /api/v1/invoices/{id}/download returns nothing;
  • if invoice_number was missing, the returned number starts with TEMP-.

Check lifecycle_available_transitions on every 201 response. If it is [], the invoice exists but cannot be deposited.

To unblock it, send a PATCH: it clears import errors without re-checking them. So the PATCH must itself carry the four fields. In particular, send back an invoice_number prefixed DRAFT-. A TEMP- number survives the PATCH, is never replaced at deposit (only a DRAFT- number is), and makes the draft undeletable: you would freeze a legal original numbered TEMP-A1B2C3D4.

The VAT breakdown has to reconcile with the totals​

On a sales invoice, tax_subtotals is compared with the same document's totals before anything is stored: the sum of the amount_without_taxes values must equal total_amount_excluding_taxes, and the sum of the vat_amount values must equal total_tax_amount, within 0.01. A wider gap fails the call with a 422, on both creation and modification, and nothing is stored (see Errors and edge cases).

This is the PPF's G1.53 rule, applied here rather than at deposit: it already refused these invoices, but much later and far from the call that created them. The check does not apply when the payload carries no tax_subtotals, when the total for the half under consideration is absent from the payload, when no breakdown entry states the amount for that half, or when the document is a purchase invoice: this platform never deposits a purchase, so the rule never judges it.

An invoice already paid at issuance​

A sales invoice already settled when it is issued is declared explicitly, through the billing framework (BT-23): send invoicing_process_id set to B2 (goods), S2 (services) or M2 (goods and services), or the matching key paid_goods_invoice, paid_services_invoice or paid_goods_and_services_invoice. The response always returns the key. Scribee never infers this framework from your amounts: an invoice sent as B1, S1 or M1 stays in that framework, even when its prepaid_amount equals its total.

The invoice's amounts are taken as they are, so they must state the settlement themselves:

  • prepaid_amount (BT-113) exactly equal to tax_inclusive_amount (BT-112), with no tolerance;
  • payable_amount (BT-115) at 0;
  • payable_rounding_amount (BT-114) absent or at 0;
  • due_date (BT-9) present. On an already-paid invoice it carries the payment date, so it cannot be later than issue_date. At creation, an absent issue_date is compared as today's date.

An incoherent set fails the call with a 422, on both creation and modification, and nothing is stored: Scribee does not fill in the amounts for you (see Errors and edge cases). On a PATCH, a field absent from the payload is read from the stored invoice: moving an existing invoice to S2 without resending its amounts is accepted only if the amounts already stored state the settlement. The check also applies when the code arrives through profile_id: a profile_id set to B2, S2 or M2 takes precedence over invoicing_process_id in the BT-23 of the generated files, and it is judged the same way.

The check does not look at type_code: a credit note declared already paid is accepted on the same terms. It does not apply to purchase invoices. An already-paid invoice may however name no down-payment invoice by document_id in invoice_references: it would become a final invoice after down payment, whose B4, S4 or M4 framework has no already-paid variant, and the call returns 422 without saving anything.

This framework describes the invoice, not its collection: it records no payment and does not move the invoice to 212 Collected. As on any invoice, an absent payment_means key is filled with the configured bank details (see Default values): send your own payment_means to describe the payment means actually used, or [] to declare none.

For a card payment (type_code 48), send in the payment_means entry the card number card_primary_account_number (BT-87, in practice its last digits), the holder card_holder_name (BT-88) and the card network card_network_id (BT-87-1), for example VISA, MASTERCARD or CB. The network is free text, with no code list: it is stored as sent, only leading and trailing blanks are removed, and the response returns it in payment_card.network_id; payment_card is null when no card number is stored. The UBL file writes it in cbc:NetworkID, which the UBL syntax makes mandatory beside the card number. Without card_network_id, the draft is accepted and stays editable, but the UBL file keeps the payment means under code 48 without writing any card information in it (no number, no holder, no network). On a sale that is deposited as an invoice, the deposit is also refused with a 422 while the network is missing: set card_network_id with a PATCH, then deposit again. Scribee never infers it from the card number. The CII file has no equivalent element and does not carry it.

What is not filled in by default​

  • customization_id (BT-24, specification identifier) is not stored on your behalf: it is null in the response as long as you do not send it. The generated UBL, CII, and Factur-X files, however, carry the EXTENDED-CTC-FR profile, against which the invoice is also checked at deposit (Formats and downloads). Send the value your recipient expects if they target another profile. A payer party, a buyer_agent, seller_agent, invoicee, or invoicer party, and a role_code carried by a payee party, all require an EXTENDED-CTC-FR customization_id: without it, creation and modification alike are refused with a 422 (see below), rather than accepted and then failing at download. On a PATCH, the customization_id already stored counts when the payload does not carry the key; a key that is present wins, including an empty string, which clears the profile and therefore makes the call fail.
  • upload_source is manual on an invoice created with POST /api/v1/workspaces/{workspace_id}/invoices. Only POST /api/v1/workspaces/{workspace_id}/invoices/upload writes api (Import existing invoices). Do not use this field to find the invoices your integration created.

Invoicing in a currency other than the euro​

currency_code (BT-5) carries the invoice currency. It is a three-letter code, and the API applies no list of allowed values: USD, GBP or CHF are accepted just like EUR. It is also the only currency field you have to send.

As soon as an invoice is not denominated in EUR, the reform requires two more: the VAT accounting currency (BT-6), which must be EUR, and the VAT total expressed in that currency (BT-111). Scribee establishes both itself at deposit time:

  • tax_currency_code is written as EUR. If you had sent a different one at creation or by PATCH, the deposit replaces it: this value is set by the platform, never taken from your payload.
  • The invoice's VAT total is converted into euros at the reference rate recorded for its issue date (issue_date), rounded to the cent, then frozen together with the rate applied. The amount does not move afterwards: it is the one the deposited file carries.
  • The generated UBL and CII files then carry BT-6 and a second VAT total in euros, beside the total expressed in the invoice currency.

Of those three values, only tax_currency_code is returned by the API. The rate applied and the converted VAT total are carried by no response field; you read them in the UBL, CII and Factur-X files (Formats and downloads).

On an invoice in EUR, none of this is emitted, and that is deliberate: the EN 16931 and Peppol checks refuse an invoice whose VAT currency repeats the invoice currency. A tax_currency_code sent on an invoice in euros is therefore cleared at deposit.

The rate used is the one published for the issue date. When no rate was published that day - a weekend, a public holiday - the last rate published before that date is carried forward, up to ten days for most currencies, and up to thirty-five days for a few whose reference rate appears with a longer delay. Past that, the deposit is refused and the invoice stays a draft (see Errors and edge cases).

Parties and the directory​

For a sale to a buyer established outside the French VAT territory and not subject to flux 1, Scribee can fill electronic address BT-49 with an email (scheme EM) when neither an electronic endpoint nor a directory address is stated. The invoice contact email takes priority; when absent on a draft, the linked customer's default contact is used. Deposit freezes this email on the invoice before validating the generated document. An existing address is never replaced, and this fallback does not trigger email delivery. BT-49 is not included in the flux 1 tax extract.

A parties entry with a party_id is filled in from the company directory: legal name, legal identifiers (SIREN, SIRET), VAT number, billing address, default contact. This data is copied onto the invoice at creation time. Later directory changes do not replace copied values. For a draft without a contact email, the BT-49 fallback above can use the current default contact until deposit freezes it. Any attribute sent on the same entry overrides the directory value; billing_address_id selects a specific billing address, otherwise the default billing address is used. Six entries accept a role_code (a UNCL 3035 code, for example PR): payer (EXT-FR-FE-44), payee (EXT-FR-FE-26), buyer_agent (EXT-FR-FE-04), seller_agent (EXT-FR-FE-67), invoicee (EXT-FR-FE-90, always IV), and invoicer (EXT-FR-FE-113, always II). Carried by any other role, it fails the call with a 422; carried by one of those entries with a value absent from the UN/CEFACT PartyRoleCode D22A list, it fails it too; and on a payee party it additionally requires an EXTENDED-CTC-FR customization_id (see Errors and edge cases). The comparison is case-sensitive - pr is refused, never upper-cased on your behalf - and an empty string counts as absence, so it triggers no refusal.

A SIRET declared as such is stored as its legal unit's SIREN. The directory keeps the establishment's identity; the document has to carry the legal unit's. As soon as the value retained for a parties entry states a legal_registration_scheme_id of siret (or its code 0009) and a legal_registration_id of exactly 14 digits, the created party carries its first 9 digits and the siren scheme: French rule BR-FR-11 requires a 9-digit SIREN on the buyer, and a SIRET copied as-is does not satisfy it.

The conversion also applies to the legal_registration_id you send yourself, with or without a party_id, on POST as on PATCH, and whatever role the entry carries - sales and purchases alike. Your explicit value still overrides the record's: the entity you name is the one retained, only the establishment suffix is dropped. So read the party back from the response rather than treating your payload as the stored state. The company-side party Scribee adds itself when your payload carries none escapes this path: its scheme is resolved from the company record and can therefore be siret (see below).

The check is narrow, and three values pass through unchanged: a registration declared under a foreign scheme, whether it is 14 digits or not - shape alone proves nothing, a foreign number of the same length is not a SIRET; a registration declared under any other scheme; and a registration for which no scheme is declared, neither by you nor by the record. The customer or supplier record itself is never rewritten: three establishments of one legal unit remain three distinct records carrying their three SIRETs, and only the invoices converge on the SIREN.

In the exported CII and UBL, the role_code is only emitted on the payer, payee, buyer_agent, seller_agent, invoicee, and invoicer blocks. A document received or imported through a path other than this endpoint is subject neither to the D22A list check nor to the profile check: its payee party role_code is emitted exactly as received, whatever the declared profile. On a purchase invoice, a self-billed sales invoice, or a document imported from a UBL, CII, or Factur-X file, a role_code stored on a role that has none (seller, buyer, tax_representative, delivery) stays stored and keeps being returned in the JSON, but never reaches the XML. The same goes for a code other than IV on an invoicee or other than II on an invoicer, and for the identifier or legal_registration_id of one of these four actors received without a scheme: no code or identifier is then emitted in the block.

The roles are seller (issues the invoice), buyer (receives it), payee (collects payment if different from the seller), payer (a third party that pays on the buyer's behalf - not to be confused with payee: payee is BG-10, the party being paid; payer is EXT-FR-FE-BG-02, the party that pays, and is only meaningful under the EXTENDED-CTC-FR profile), tax_representative (the seller's tax representative), and delivery (delivery party). On a sales invoice, buyer resolves against the company's customers; payee, payer, and tax_representative against the company's whole directory. The company-side role - seller on a sales invoice - is always derived from the company record: do not send it a party_id, it would be rejected with 422. If your payload contains no seller entry, Scribee adds it from the company record and its head office establishment.

The legal_registration_scheme_id on that derived party is not a copy: Scribee resolves it from the company record, registering country first, identifier shape second. A company's legal_identifier is deliberately multinational - a French SIREN, an EIN, a TIN, whatever the registering country uses - so its shape alone proves nothing: a foreign 14-digit number is not a SIRET, and declaring it as one would put a false French legal identity on a document Scribee transmits as a Plateforme Agréée.

Record's legal_registering_countryRecord's legal_identifierParty's legal_registration_scheme_id
FR, or blankexactly 14 digitssiret
FR, or blankexactly 9 digitssiren
FR, or blankany other shapenull
any other countryany shapenull

A blank legal_registering_country counts as France: the field is optional, on a platform that is French by construction. An explicitly foreign country is never coerced to France; the comparison ignores case and surrounding whitespace. The party's legal_registration_id is the record's legal_identifier in every case, including when no scheme is declared.

Scribee refuses on write any French company legal_identifier that is not a 9-digit SIREN (Companies and establishments). The table's siret row therefore only concerns a record saved before this check, whose legal_identifier and legal_registering_country have both been left unchanged since; the "any other shape" row, such a record or a record without a legal_identifier.

Do not assume the company-side party carries siren. A French company recorded before the SIREN check may still hold a 14-digit SIRET, and so carry siret, and a company registered outside France carries no scheme at all. An integration that recognises your companies by legal_registration_scheme_id being siren alone therefore misses some of them: match on legal_registration_id instead, or accept both schemes and the absence of a scheme.

The response also carries, each under its own key, four actors specific to the EXTENDED-CTC-FR profile: buyer_agent (the buyer's agent, EXT-FR-FE-BG-01), seller_agent (the seller's agent, EXT-FR-FE-BG-03), invoicee (EXT-FR-FE-BG-04), and invoicer (third-party invoicer, EXT-FR-FE-BG-05). They are filled in on received or imported invoices whose UBL or CII XML carries them. You can also send them in parties, on creation and modification alike, under one of the two EXTENDED-CTC-FR customization_id values; under any other profile the entry is refused with 422. The generated ubl, cii, and facturx files then carry them: in UBL in cac:AgentParty (both agents) and cac:ServiceProviderParty (invoicee on the buyer side, invoicer on the seller side), in CII in ram:BuyerAgentTradeParty, ram:SalesAgentTradeParty, ram:InvoiceeTradeParty, and ram:InvoicerTradeParty. The regulatory data extract (flux 1) sent to the PPF never carries them: its schema does not declare them. A sales invoice naming an invoicer is only deposited when the company holds, on the issue date, an active third-party invoicer billing mandate for that party, attested by an administrator of the company (XP Z12-014, case 19a). These mandates are created and attested in the company settings of the Scribee interface: the API can neither create nor attest them. See Errors and edge cases.

When a sales invoice or credit note names an invoicer and the company holds, on the issue date (BT-2), that active third-party invoicer billing mandate, Scribee adds to the ubl, cii, and facturx files, to the PDF, and to the flux 1 extract an invoice-level note with subject code DCL (BT-21). The deposit records on the invoice the mandate it is issued under; on an invoice deposited this way, the note follows that mandate, and a termination or end of the mandate after the deposit does not remove it from files generated again afterwards. Its text (BT-22) states that the invoice was drawn up by the third-party invoicer in the name and on behalf of the seller, for example Facture établie par Compta Services (SIREN 732829320) au nom et pour le compte de Acme SAS (SIREN 552100554). Each party is named by its name, followed by its SIREN only when its legal_registration_scheme_id is siren (0002) and its legal_registration_id is filled in; under any other scheme, the name stands alone. The text is truncated to 1024 characters. This note is not stored on the invoice: it does not appear in the item_notes read back with GET /api/v1/invoices/{id}?include=item_notes, nor in the edit form of the Scribee interface. If the invoice already carries an invoice-level note whose code is declaration (UNCL4451 code DCL), Scribee adds no other. The note is added neither on a self-billed sales invoice nor on a received purchase invoice. Its absence never blocks a deposit: XP Z12-014 (case 19a) recommends it without requiring it.

Two limits to know about:

  • name is required on seller and buyer. A buyer entry sent without a party_id and without name returns 422, with details keyed by field. country_code is not: Scribee fabricates no country, neither on the payload nor when resolving a customer record whose address carries none, and a party carrying no country reads back as null in seller.address.country_code and buyer.address.country_code. Send it anyway: the deposit asks for it on sales that are deposited as an invoice, and refuses them when it is missing (see The invoice lifecycle).
  • The response only returns seller, buyer, payee, payer, tax_representative, buyer_agent, seller_agent, invoicee, and invoicer, each under its own key, null when the invoice carries none. A delivery party is stored and exported, but does not appear in the returned JSON.

:::info The delivery location: a name on one side, an identifier on the other delivery_location_name carries the location's name (BT-70) - "Entrepôt Paris Nord". delivery_location_id carries a coded identifier (BT-71) - a GLN, a SIRET - and is exported only when you also send its scheme in delivery_location_scheme_id (BT-71-1). The reform's BR-FR-CO-10 rule requires it: an identifier without its scheme makes the deposit invalid, so Scribee would rather omit it than produce a file that gets refused.

Send the name in delivery_location_name. A name placed in delivery_location_id cannot be qualified by any scheme: it will not appear in the ubl, cii and facturx files. :::

Default values​

When the key is absent from the payload, Scribee fills in:

  • item_notes (sales only): the legal notices configured in the Scribee interface.
  • payment_means: the banking details configured in the Scribee interface. If the buyer entry is sent by name only, without a resolvable party_id, no banking details are inserted: the client's factoring status cannot be established.
  • tax_due_date_code (BT-8, sales only): the code 5 when the issuing company has opted for VAT on debits. It is set at creation and then frozen: a value you send always wins and stays untouched, and an invoice already created does not change code if the option is taken or revoked afterwards.

An explicitly empty array ([]) empties the collection and is never refilled: this is how a PATCH removes existing notices or banking details. Only the note of a company that belongs to a single taxable entity is still added to it (A company that belongs to a single taxable entity).

An invoice carrying the code 5 drops out of the automatic payment-declaration tracking, which only covers sales invoices with no tax point code or with 72 - or 432, which names the same tax point on the other UNTDID list (Record payments). And if the invoice also carries tax_point_date (BT-7), the ubl, cii, and facturx files emit the date only: the two fields are mutually exclusive in the standard formats, whereas the API response does return both.

A company that belongs to a single taxable entity​

A sale issued by a member of a single taxable entity (assujetti unique) states it. Scribee writes that statement for you, from the company's single taxable entity settings: they are entered in the Scribee interface, and the API exposes them neither for reading nor for writing.

When those settings carry the single taxable entity's SIREN and its complete details - name, VAT number, address -, every sales invoice created by POST /api/v1/workspaces/{workspace_id}/invoices receives, in addition to what you send:

  • a tax_representative party (BG-11) carrying the single taxable entity's name, VAT number and address, with country_code set to FR;
  • a document-level note whose code is tax_declaration and whose content is MEMBRE_ASSUJETTI_UNIQUE. It is added to the notes you send, including when your item_notes array is empty;
  • the single taxable entity's SIREN on the seller party, under scheme 0231. The JSON does not return it: it only appears in the ubl, cii and facturx files, as an additional seller identifier (BT-29).

The creation response therefore carries a filled tax_representative; the note is read back with include=item_notes. An invoice created by converting a quote (POST /api/v1/quotes/{id}/convert) receives the same three items, provided it carries a seller party.

{
"data": {
"tax_representative": {
"name": "Groupe TVA ACME",
"vat_identifier": "FR96552100554",
"address": { "line_1": "1 rue de la Paix", "city": "Paris", "postal_code": "75002", "country_code": "FR" }
}
}
}

These values are frozen at creation. A PATCH on the draft carries them over unchanged, even if the company's settings have changed since or it has left the single taxable entity. A draft that does not carry them receives them on PATCH only if the company still declares, with its complete details, the single taxable entity SIREN it declared when the invoice was created.

Do not send them yourself. For a member company, a tax_representative party, or a document-level note with code tax_declaration (or its UNCL4451 code TXD), fails the call with 422, even when identical to the one Scribee writes. A PATCH that sends back the invoice as a GET returns it is therefore refused: remove tax_representative and the tax_declaration note before sending it back. Line notes are not affected. At creation, membership is read from the company's current settings; on a PATCH, it is the one the invoice froze at creation. A company that belongs to no single taxable entity may send its own tax_representative party and its own tax_declaration notes, except a note whose text is MEMBRE_ASSUJETTI_UNIQUE, refused with 422. See Errors and edge cases.

If the company's settings carry the single taxable entity's SIREN without its complete details, the invoice receives none of the three items, but the company remains a member: the refusals above apply, and the invoice's deposit is refused until the statement is complete (see Errors and edge cases).

Purchase invoices are not affected, nor is an invoice imported through POST /api/v1/workspaces/{workspace_id}/invoices/upload: they are stored as they were issued.

The lines' quantity unit​

quantity_unit_code, required on every line, must be a code accepted by the EN 16931 check for BT-130. A code that check rejects returns 422, in the same shape as an incomplete line (see Errors and edge cases). A few valid codes as examples: C62 (unit), KGM (kilogram), XPP (piece).

The line's nature: goods or service​

product_type on a line is service, good, or both. It is optional: if you do not send it, Scribee derives the line's nature from its quantity_unit_code - a time-based unit (hour, day, week, month, year, half_year, quarter) is treated as a service, everything else as goods. This classification derives the B, S, or M letter of BT-23 (invoicing framework) when you send no invoicing_process_id. The mandatory "operation category" mention on the PDF reads BT-23: an invoicing_process_id you send (or a profile_id that is a BT-23 code) therefore decides the mention, whatever the lines say.

The margin scheme note (BT-21)​

A sale under the margin scheme carries its mention in an invoice-level note: an item_notes entry whose code is value_added_tax_margin_scheme and whose content carries the text of the mention. code also accepts the raw UNCL4451 code AVE; the response always returns the key.

{
"item_notes": [
{ "code": "value_added_tax_margin_scheme", "content": "Régime particulier - Biens d'occasion" }
]
}

The text is yours. Scribee never adds this note by itself and never writes its content, even when the VAT breakdown designates a sale under the margin scheme. Like any item_notes array you send, this one replaces the legal notices Scribee would have filled in (Default values): put them in the same array.

On output, the note becomes ram:IncludedNote with ram:SubjectCode AVE in CII and in Factur-X, and in UBL cbc:Note, whose text is prefixed with #AVE#. A received or imported invoice carrying a note under the code AVE, in UBL, CII or Factur-X, keeps it with its text as is; it reads back under the key value_added_tax_margin_scheme with include=item_notes.

The line note (BT-127)​

Each line accepts its own item_notes array, distinct from the invoice-level item_notes (BT-21 / BT-22). content carries BT-127, the note text; code carries EXT-FR-FE-183, that note's subject code - a French extension, since EN 16931 defines no line-level subject code. code accepts the Scribee key or the raw UNCL4451 code; the response always returns the key.

{
"lines": [
{
"line_id": "1",
"article_name": "Lave-linge",
"item_notes": [
{ "code": "general_information", "content": "Eco-contribution DEEE" }
]
}
]
}

Read the note back with GET /api/v1/invoices/{id}?include=lines: it comes back in item_notes on the line. In Factur-X (CII) output the pair becomes ram:Content and ram:SubjectCode; in UBL, where a line carries a single cbc:Note, the code is prefixed to the text as #AAI#Eco-contribution DEEE.

Invoiced object identifiers (BT-128)​

object_identifier carries the line's invoiced object identifier (BT-128), for example a meter number, and object_identifier_scheme_id its scheme (BT-128-1). The EXTENDED-CTC-FR profile admits several identifiers on one line: the following ones are declared in the additional_object_identifiers array, each entry carrying identifier and, optionally, scheme_id. The object_identifier / object_identifier_scheme_id pair always remains the line's first identifier; the array carries the following ones, in the order you send them.

{
"lines": [
{
"line_id": "1",
"article_name": "Acheminement",
"object_identifier": "51214922223746",
"object_identifier_scheme_id": "AVE",
"additional_object_identifiers": [
{ "identifier": "C5", "scheme_id": "AWA" },
{ "identifier": "30001234567890", "scheme_id": "ACD" }
]
}
]
}

Each array entry is checked, and the call fails with 422 if any of them is refused:

  • identifier is required, 255 characters at most;
  • scheme_id must be a code from the UNTDID 1153 list retained by the EN 16931 rule BR-CL-07; the comparison is case-sensitive, and an empty string counts as absence;
  • the line must carry an object_identifier: an additional identifier without a first identifier is refused.

At creation, this refusal takes the form of an incomplete detail line (see Errors and edge cases) and no invoice is created; on a PATCH, the draft stays unchanged.

Read the identifiers back with GET /api/v1/invoices/{id}?include=lines: each line returns additional_object_identifiers, an empty array when it carries at most one identifier. An invoice received as UBL or CII likewise keeps every identifier of each line, in the file's order.

In the produced files, each identifier becomes a separate element, in the same order: a cac:DocumentReference with cbc:DocumentTypeCode 130 in UBL, a ram:AdditionalReferencedDocument with ram:TypeCode 130 in CII, where the scheme is written in ram:ReferenceTypeCode. The PDF lists them all. Outside the EXTENDED-CTC-FR profile, the EN 16931 standard admits only one identifier per line: an invoice whose customization_id designates another profile is accepted at write time, but the ubl or cii download fails with 422 as soon as a line carries several (Formats and downloads).

Order, despatch advice and delivery at line level​

Each line can name its own order, its own despatch advice and its own delivery location, distinct from the header's. Apart from order_line_reference (BT-132), these fields are French extensions specific to the EXTENDED-CTC-FR profile: the EN 16931 standard defines none of them at line level.

Line fieldTermContent
order_line_referenceBT-132Referenced purchase order line
purchase_order_referenceEXT-FR-FE-135Purchase order number
despatch_advice_referenceEXT-FR-FE-140Despatch advice number
despatch_advice_line_referenceEXT-FR-FE-141Despatch advice line
despatch_advice_dateEXT-FR-FE-201Despatch advice date
delivery_location_idEXT-FR-FE-146Delivery location identifier
delivery_location_scheme_idEXT-FR-FE-148Scheme of that identifier, a four-digit ISO 6523 ICD code (0088, for example)
delivery_location_nameEXT-FR-FE-149Delivery location name
delivery_address_line_1, delivery_address_line_2, delivery_address_line_3, delivery_address_postal_code, delivery_address_city, delivery_address_region_codeEXT-FR-FE-151 to EXT-FR-FE-156Delivery address (EXT-FR-FE-150)
delivery_countryEXT-FR-FE-157Country code of the delivery address
{
"lines": [
{
"line_id": "1",
"article_name": "Palette",
"order_line_reference": "OL-7",
"purchase_order_reference": "PO-2024-001",
"despatch_advice_reference": "DESADV-42",
"despatch_advice_line_reference": "3",
"despatch_advice_date": "2024-01-10",
"delivery_location_id": "3012345678901",
"delivery_location_scheme_id": "0088",
"delivery_location_name": "Entrepot Lyon Sud",
"delivery_address_city": "Lyon",
"delivery_country": "FR"
}
]
}

Three rules tie these fields together:

  • despatch_advice_reference is required as soon as the line carries despatch_advice_line_reference or despatch_advice_date;
  • delivery_location_id and delivery_location_scheme_id go together: one without the other is refused. This is the difference from the header's delivery location identifier, which is accepted without a scheme but then never exported;
  • delivery_country is required as soon as the line carries any of the delivery_address_* fields.

At creation, a line that breaks one of these rules fails the call with 422, in the same shape as an incomplete detail line (see Errors and edge cases).

Read these fields back with GET /api/v1/invoices/{id}?include=lines. Each line returns order_line_reference and purchase_order_reference as stored, then two objects:

  • despatch_advice groups reference, line_reference and issue_date. It is null when the line has no despatch_advice_reference.
  • delivery groups location_name, location_id, location_scheme_id and address. It is null when the line carries no identifier, no name, and no address or country field. address (line_1, line_2, line_3, city, postal_code, country_subdivision, country_code) is null when the line has neither an address field nor a country.

In the produced files, the line's order becomes ram:BuyerOrderReferencedDocument/ram:IssuerAssignedID in CII and Factur-X, cac:OrderLineReference/cac:OrderReference/cbc:ID in UBL. The despatch advice becomes ram:DespatchAdviceReferencedDocument in CII, cac:DespatchLineReference in UBL. The delivery becomes a ram:ShipToTradeParty under the line's ram:SpecifiedLineTradeDelivery in CII, a line-level cac:Delivery in UBL, which carries the identifier and the address in cac:DeliveryLocation and the name in cac:DeliveryParty. The address is only emitted with its country.

:::warning These fields are only carried under the EXTENDED-CTC-FR profile, and UBL requires two line numbers Apart from order_line_reference, these fields are only emitted in ubl, cii and facturx under the EXTENDED-CTC-FR profile. Under any other profile - an EN 16931 or Peppol customization_id, for example - they are left out of the generated files, without an error: the download succeeds, and the invoice keeps them and returns them with GET /api/v1/invoices/{id}?include=lines. A sales invoice created through the API without a customization_id is produced under EXTENDED-CTC-FR and is not affected.

Under that profile, UBL also requires a line number where CII does not: purchase_order_reference is only carried together with order_line_reference, and despatch_advice_reference only together with despatch_advice_line_reference. If the line carries the first without the second, the ubl download fails with 422 naming the lines, while cii and facturx carry the value as stored. Scribee never invents the missing number. :::

Disbursements (débours)​

A sales line is declared a disbursement with the disbursement boolean: true marks it, false or an absent key leaves it unmarked. Scribee never infers a disbursement from the VAT category: a line in category O (outside the scope of VAT) without disbursement stays an ordinary line, as does a line in category E under VATEX-EU-79-C. On read, GET /api/v1/invoices/{id}?include=lines returns disbursement on every line, false included.

{
"lines": [
{
"line_id": "2",
"article_name": "Frais de greffe",
"quantity": 1,
"quantity_unit_code": "one",
"article_unit_price_excluding_taxes": 300.0,
"line_extension_amount": 300.0,
"tax_category_id": "O",
"disbursement": true
}
]
}

A disbursement can also be stated in category E (exempt), with the exemption reason VATEX-EU-79-C carried by the line itself, in tax_exemption_reason_code, and its label in tax_exemption_reason. The tax_subtotals breakdown, which you send as for any invoice, then carries a row of the same category and the same code, with no VAT amount:

{
"lines": [
{
"line_id": "2",
"article_name": "Frais de greffe",
"quantity": 1,
"quantity_unit_code": "one",
"article_unit_price_excluding_taxes": 300.0,
"line_extension_amount": 300.0,
"tax_category_id": "E",
"vat_rate": 0,
"tax_exemption_reason_code": "VATEX-EU-79-C",
"tax_exemption_reason": "Débours",
"disbursement": true
}
],
"tax_subtotals": [
{ "tax_category_id": "E", "vat_rate": 0, "vat_amount": 0, "amount_without_taxes": 300.0, "currency_code": "EUR", "tax_exemption_reason_code": "VATEX-EU-79-C", "tax_exemption_reason": "Débours" }
]
}

On read, the tax object of each line returns tax_exemption_reason_code and tax_exemption_reason next to category_code and percent, null when the line carries none.

A document-level allowance or charge (allowance_charges) also accepts tax_exemption_reason_code and tax_exemption_reason, and GET /api/v1/invoices/{id}?include=allowance_charges returns them. When the breakdown carries, next to the E row under VATEX-EU-79-C, another E row under another exemption code, that code designates the breakdown row the allowance or charge belongs to: send VATEX-EU-79-C on the one that applies to the disbursements. When two E rows at the same rate share the same code and differ only by their tax_exemption_reason, that label tells them apart: send on the allowance or charge the label of the breakdown row it targets.

The exemption codes tax_exemption_reason_code of the lines, of the tax_subtotals breakdown and of allowances and charges are saved stripped of leading and trailing spaces, and in upper case: vatex-eu-79-c is saved and returned as VATEX-EU-79-C.

Five rules govern the field:

  • it is accepted only on a line in category O, or in category E with a tax_exemption_reason_code of VATEX-EU-79-C: on any other line - an E line with no exemption code or under another code included -, true fails the call with 422;
  • a marked line carries no VAT: sent with a non-zero vat_rate, it fails the call with 422. Scribee does not bring its rate down to 0, as it does for an unmarked line in category O or E. Sent without vat_rate, a marked line in category E is stored at rate 0; in category O, it stays without a rate;
  • the VATEX-EU-79-C code is accepted only on a line in category E, marked or not: on a line of any other category, it fails the call with 422;
  • it is accepted only on a sales invoice: on a purchase invoice, a line set to true fails both creation and modification with 422;
  • on a PATCH, the lines are rebuilt (see step 2): a marked line sent back without the key becomes unmarked. Send disbursement again on every line that must stay marked.

The refusals are detailed in Errors and edge cases. The marker appears in no generated file: no term of the standard carries it, and the ubl, cii and facturx files do not change whether a line is marked or not.

It does change what is transmitted and declared. A sales invoice whose every line is marked, 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, leaves the reform: it is not deposited with the PPF, no copy of it goes out over Peppol and no lifecycle status is transmitted for it; it is neither declared by an e-reporting invoice nor counted into the B2C aggregate, and its payments are not declared (Declare transactions). It still moves to 200 on deposit like any invoice: only the regulatory transmissions do not take place. A single unmarked line, or a breakdown carrying VAT, is enough to keep it in the ordinary circuit. Only the marker decides: the VATEX-EU-79-C code alone takes no invoice out of the reform, and an invoice whose lines are in category E under that code without carrying disbursement follows the circuit of any invoice. An invoice mixing taxable lines and disbursement lines therefore follows the circuit of any invoice, disbursements included: for a buyer carrying a SIREN and established in the French VAT territory, it is deposited with the PPF together with its disbursement lines.

Conversely, an invoice whose whole VAT breakdown is in category O but with at least one unmarked line stays in the ordinary circuit. For such a buyer, and for a company declared in the emission wave, its deposit is refused with 422, because the PPF rejects a flux 1 whose whole VAT breakdown is in category O (The invoice lifecycle). If these are disbursements, mark every line with disbursement; otherwise, correct the VAT categories; then deposit the invoice again.

Such a mixed invoice whose disbursements are in category O requires a customization_id of the EXTENDED-CTC-FR profile. Under an EN 16931 profile, the generated files are checked against rule BR-O-12, which forbids an invoice carrying a VAT breakdown in category O from containing a line of any other category; the EXTENDED-CTC-FR rules do not contain it.

Invoice type​

The type is chosen at creation via type_code. The API accepts the Scribee key or the equivalent UNTDID 1001 code, and always returns the key. A value outside this list returns 422.

type_codeUNTDID 1001Label
invoice380Invoice
self_billed_invoice389Self-billed invoice
factored_invoice393Factored invoice
self_billed_factored_invoice501Self-billed factored invoice
retainer_invoice386Retainer invoice
self_billed_retainer_invoice500Self-billed retainer invoice
corrected_invoice384Corrected invoice
self_billed_corrected_invoice471Self-billed corrected invoice
corrected_factored_invoice472Corrected factored invoice
self_billed_corrected_factored_invoice473Self-billed corrected factored invoice
credit_note381Credit note
self_billed_credit_note261Self-billed credit note
factored_credit_note396Factored credit note
self_billed_factored_credit_note502Self-billed factored credit note
retainer_credit_note503Retainer credit note
consolidated_credit_note262Consolidated credit note

Self-billing (self_billed_*) means an invoice drawn up by the buyer on the seller's behalf. It has nothing to do with the VAT reverse charge, which is declared through tax_category_id set to AE on lines and VAT breakdowns.

Retainer invoice and final invoice after a down payment​

A retainer (down-payment) invoice is created like any invoice, with type_code set to retainer_invoice (386) or self_billed_retainer_invoice (500). On these two types, as on retainer_credit_note (503), invoicing_process_id (BT-23) refuses the final-invoice-after-down-payment frameworks B4, S4, and M4: the call returns 422.

The final invoice that settles down payments lists them in invoice_references (BG-3), one entry per retainer invoice. Each entry takes one of two forms:

  • the values themselves: invoice_number (BT-25), invoice_date (BT-26), and type_code (EXT-FR-FE-02), the type of the referenced invoice. type_code accepts the same list as the invoice type, Scribee key or UNTDID 1001 code, and a value outside this list returns 422;
  • document_id, the Scribee identifier of one of your retainer invoices: Scribee copies its number, issue date, and type, and the entry's other values are ignored. The designated invoice must be a sales invoice of the same company, of type retainer_invoice or self_billed_retainer_invoice, whose number is no longer provisional (neither DRAFT- nor TEMP-), issued in the same currency_code as the final invoice, and addressed to the same buyer: the buyer party of both invoices carries the same party_id. A buyer sent without a party_id cannot designate any retainer invoice. An invoice of type retainer_invoice, self_billed_retainer_invoice, or retainer_credit_note settles no down payment: it cannot designate any retainer invoice by document_id, and its prepaid_amount is never computed. Otherwise the call returns 422 (see Errors and edge cases).

Read the references back with GET /api/v1/invoices/{id}?include=invoice_references: each entry returns type_code and document_id, the latter null for an entry sent by its values.

When a final invoice settles down-payment invoices already counted in the B2C aggregate, those down payments are taken back out of it (Declare transactions), in two cases: the final invoice is deposited at the PPF or declared in the transaction data (10.1), or it is itself counted in the B2C aggregate, where only the balance then remains declared. The amount of the down payments goes in prepaid_amount (BT-113): do not also deduct them through a negative line.

For a company declared in the emission wave, the deposit of the final invoice is refused with 422, before any counting or reversal, when one of those down-payment invoices:

  • is cited by number only, without document_id. Send that invoice_references entry again in a PATCH with the document_id of the down-payment invoice, billed to the same customer record as the final invoice, then deposit the invoice again;
  • is designated by document_id but is not billed to the same customer record as the final invoice: the buyer parties of both invoices do not carry the same party_id, or one of them is typed directly, without party_id. A final invoice whose buyer has no party_id therefore links no down-payment invoice with certainty, and its deposit is refused. The API does not lift this refusal and has no field to do so: only a user allowed to edit the draft can confirm, from the invoice page in the Scribee interface, that the down-payment invoice does belong to this operation. The message names the down-payment invoices concerned and points to that confirmation. It is recorded in the invoice's traceability and holds only for the two buyers present when it is given: each one's customer record or, for a buyer typed directly without party_id, its name, its legal_registration_id and its legal_registration_scheme_id, ignoring leading, trailing or repeated spaces and letter case. If the buyer of either invoice changes customer record, gains one or loses it, or if one of those three fields of a buyer typed directly is edited, on the final invoice or on the down-payment invoice alike, the match becomes uncertain again: the deposit, including through the API, is refused again until it is confirmed again. Once the match is confirmed, deposit the invoice again;
  • has already been taken back out of the B2C aggregate for another final invoice: settling it a second time would leave it counted in full or taken back twice. Remove it from this invoice, or correct the final invoice that settled it;
  • has been taken back out of the B2C aggregate for another final invoice, cancelled or rejected since, so that the reversal was compensated and the down payment declared again: a down payment is taken back once only. Issue a new retainer invoice, or remove it from this invoice;
  • was counted in the B2C aggregate of another company before being moved to this one: it can only be taken back out of that company's declaration. Settle it from the company that declared it, or remove it from this invoice;
  • is linked by document_id but no longer belongs to the same company as the final invoice, one of the two having been moved since: an invoice settles only its own company's down payments, and they can only be taken back out of the declaration of the company that declared them. Unlike the previous case, the down payment did not join the final invoice's company after being counted elsewhere: the two invoices now sit in different companies. Move them back into the same company, or remove the down-payment invoice from this invoice;
  • is also deducted through a negative line citing its number in invoice_reference_number: it would be deducted twice;
  • has payments already declared as payment data, or awaiting declaration. This includes a payment-data record you declared yourself, through the API or by CSV import, in a seller-role payment report of any company of the workspace, and that designates the down-payment invoice: its invoice_number is exactly the down-payment invoice's number and, when it carries an invoice_date, that date is the down-payment invoice's issue date; or its invoice_number is exactly the number kept by the final invoice's invoice_references entry whose document_id designates the down-payment invoice and, when the payment-data record and that entry each carry a date, the two dates are equal. That entry keeps the number and date copied when it was written, even if the down-payment invoice has been renumbered or redated since. A payment-data record without an invoice number designates no down-payment invoice. This case only applies to a final invoice deposited at the PPF or declared in the transaction data (10.1); they prevent neither the deposit nor the reversal of a final invoice itself counted in the B2C aggregate.
  • could have as its payment a payment-data record the company - or another company of the same workspace - holds without knowing which invoice it relates to: a payment-data record Scribee declared from an invoice payment - or declared before Scribee recorded who declared it - that no longer designates its invoice or its payment, whatever its date - a collected down payment may precede the retainer invoice -, and carrying no invoice number, the down-payment invoice's number, or the number kept by the invoice_references entry whose document_id designates the down-payment invoice, with the same date when that record and that entry each carry one. Taking the down payment back would leave that payment declared beside it. Like the previous case, this one only applies to a final invoice deposited at the PPF or declared in the transaction data (10.1). Payment data you declare yourself, through the API or by CSV import, never triggers this case: a record that designates the down-payment invoice falls under the previous case. The message asks you to contact support, who attribute each such payment to its invoice; then deposit the invoice again.

A corrected invoice (corrected_invoice, corrected_factored_invoice or their self-billed variant) whose invoice_references cites a final invoice whose reversal is written and not compensated does not also deduct the down payments taken back through a negative line citing their number in invoice_reference_number: the reversal stays in place as long as the corrected invoice replaces the final invoice (The reversal compensated after the final invoice is cancelled or rejected), and those down payments would be declared nowhere. For a company declared in the emission wave, the deposit of such a corrected invoice, bound for the PPF, the transaction data (10.1) or the B2C aggregate, is refused with 422, with the code operation_failed. The error message names the down-payment invoices concerned, and the invoice stays a draft. Remove those negative lines - the down payments are carried in prepaid_amount (BT-113) - then deposit the invoice again.

A final invoice counted in the B2C aggregate that carries no buyer party likewise links no down-payment invoice with certainty: its deposit is refused, unless a match by document_id has been confirmed by a user as above. The error message names the down-payment invoices concerned, and the invoice stays a draft.

A final invoice entered into Scribee from a file, whose reversal is written, no longer returns to draft: revert_to_draft answers 422 with the message Cette facture ne peut pas repasser en brouillon : son dépôt a repris de la déclaration des données de transaction B2C journalières les mensualités qu'elle solde, et cette correction est déclarée. La repasser en brouillon laisserait ces mensualités reprises sans facture qui les solde. Émettez plutôt un avoir pour la corriger., and no longer appears in lifecycle_available_transitions. Correct it with a credit note.

Likewise, a down-payment invoice entered into Scribee from a file, whose counting in the B2C aggregate was taken back by the final invoice settling it, no longer returns to draft: revert_to_draft answers 422 with the message Cette facture de mensualité ne peut pas repasser en brouillon : la facture finale qui la solde l'a reprise de la déclaration des données de transaction B2C journalières, et cette correction est déclarée. Modifiée puis déposée à nouveau, elle ne pourrait pas être déclarée de nouveau à sa place, et les déclarations ne lui correspondraient plus. Émettez plutôt un avoir pour la corriger., and no longer appears in lifecycle_available_transitions. Correct it with a credit note.

These checks do not apply to a final invoice that is declared nowhere, and for which no down payment is therefore taken back: a sale to another member of the same single taxable entity, or an invoice outside the reform because it carries only disbursements.

The invoicing framework of a final invoice is declared in invoicing_process_id: B4 for goods, S4 for services, M4 for both. Send it with the final invoice: invoicing_process_id stays null in the response if you do not send it.

The amount of the down payments already paid goes in prepaid_amount (BT-113). When every invoice_references entry of type retainer_invoice or self_billed_retainer_invoice is designated by document_id, Scribee computes this amount itself and replaces the values sent: prepaid_amount becomes the sum of the tax_inclusive_amount (BT-112) of the designated retainer invoices, and payable_amount (BT-115) is tax_inclusive_amount minus that sum, plus payable_rounding_amount. This computation happens at creation, and on a PATCH that sends invoice_references. As soon as one of these entries is sent by its values, Scribee keeps prepaid_amount and payable_amount as you send them.

A line can also reference an earlier invoice, for example the line that takes back a down payment (EXT-FR-FE-BG-06): invoice_reference_number (EXT-FR-FE-136), invoice_reference_date (EXT-FR-FE-138), invoice_reference_type_code (EXT-FR-FE-137, same list as type_code), and invoice_reference_line_id (EXT-FR-FE-139). These values come back on the line in the invoice_reference object, under the keys invoice_number, invoice_date, type_code, and line_id; the object is null as long as invoice_reference_number is empty.

In the ubl, cii, and facturx files, the number and date of the invoice_references entries are always emitted. Their type_code, and the reference carried by a line as a whole, are only emitted under one of the two EXTENDED-CTC-FR customization_id values (the values the payer error below lists). The API response returns these values whatever the profile.

Triangular operation​

triangular_position states your company's position in an intra-EU triangular operation: first_supplier (first supplier), intermediary (intermediary) or final_customer (final customer). It is not an EN16931 field. It is null on every invoice outside such an operation, and that is its value until you fill it in: Scribee never infers it from the invoice. It is returned on every read of the invoice.

It is sent inside the invoice object, at creation (POST /api/v1/workspaces/{workspace_id}/invoices) as well as when modifying the draft (PATCH /api/v1/invoices/{id}). The accepted values depend on direction:

directionAccepted values
salesfirst_supplier, intermediary
purchasesintermediary, final_customer

Any other value - a value outside this list, or a value the direction does not accept, such as final_customer on a sales invoice - is refused with 422 and nothing is saved (see Errors and edge cases). null and the empty string mean no position.

On modification, this field does not quite follow the scalar rule described in step 2: omitted, it keeps the stored position; sent as null or "", it clears it.

File import (POST /api/v1/workspaces/{workspace_id}/invoices/upload) does not accept it: an imported invoice carries null, and the position is then filled in through PATCH /api/v1/invoices/{id} while it is a draft.

On a sales invoice, intermediary changes what Scribee declares in e-reporting: see Declare transactions.

Supplying your own PDF​

The pdf_base64 field, sent inside the invoice object of this same call, replaces the readable layer generated by Scribee with your own PDF. Its constraints, its synchronous checks, and its five 422 causes are detailed in Provide your own PDF. It is only accepted at creation: PATCH ignores it.

Create and deposit in a single call​

The lifecycle_state field, sent inside the invoice object, merges this step and step 4: the invoice is created and immediately deposited in the same call. On a sales invoice, the only value that advances the lifecycle is deposited; draft is accepted but has no effect - it is the ordinary creation described above.

warning

A deposit requested through lifecycle_state is all or nothing. If it fails - missing numbering template, any of the causes in Errors and edge cases - the entire creation is rolled back: neither the invoice nor its lines exist. This is different from the four invoice-level fields in step 1, whose absence does not prevent creation.

curl -X POST https://app.scribee.tech/api/v1/workspaces/YOUR_WORKSPACE_ID/invoices \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"company_id": 317,
"invoice": {
"direction": "sales",
"invoice_number": "DRAFT-CRM-2026-0043",
"issue_date": "2026-07-31",
"due_date": "2026-08-30",
"type_code": "invoice",
"currency_code": "EUR",
"lifecycle_state": "deposited",
"parties": [
{ "role": "buyer", "party_id": 42 }
],
"lines": [
{
"line_id": "1",
"article_name": "Prestation de conseil",
"quantity": 2.0,
"quantity_unit_code": "day",
"article_unit_price_excluding_taxes": 500.0,
"line_extension_amount": 1000.0,
"tax_category_id": "S",
"vat_rate": 20.0
}
],
"tax_subtotals": [
{ "tax_category_id": "S", "vat_rate": 20.0, "vat_amount": 200.0, "amount_without_taxes": 1000.0, "currency_code": "EUR" }
],
"total_amount_excluding_taxes": 1000.0,
"total_tax_amount": 200.0,
"tax_inclusive_amount": 1200.0,
"payable_amount": 1200.0
}
}'

201 response, the invoice is already in the deposited state (200 Deposited) with its definitive number:

{
"data": {
"id": 12346,
"invoice_number": "FAC-2026-0199",
"lifecycle_state": "deposited",
"lifecycle_status_code": "200",
"lifecycle_available_transitions": ["collect"]
}
}

Step 2: modify the draft​

PATCH /api/v1/invoices/{id} only works on a draft and transmits nothing. Two rules apply, different depending on the kind of field:

  • Collections are destroyed then rebuilt on every call: parties, lines, tax_subtotals, payment_means, item_notes, allowance_charges, invoice_references. A collection absent from the payload is emptied, not preserved. A PATCH that omits parties leaves the invoice without a buyer; the defaults (seller, legal notices, banking details) are then reapplied as at creation.
  • Absent scalar fields are kept as they are. Omitting due_date does not clear it. So you cannot empty a scalar by omitting it.

One collection is an exception to the first rule: margin_bases, the margin bases of a sale under the margin scheme. Absent from the payload, it keeps the stored bases; an array replaces them, [] clears them. A PATCH whose only key is margin_bases changes those bases alone: the rest of the invoice stays as stored, with no rebuild of the collections and none of the three side effects below (see Sales under the margin scheme).

Send back the entire invoice: it is the only shape whose result is predictable.

For a company that belongs to a single taxable entity, remove from it the tax_representative party and the tax_declaration note that Scribee wrote: sending them back fails the call with 422, and Scribee rewrites them itself (A company that belongs to a single taxable entity).

Three side effects to know about:

  • direction and pdf_base64 are accepted by the endpoint then ignored. Sending them produces neither a change nor an error.
  • The PATCH purges the four already-generated formats as well as the PDF you supplied, resets provided_pdf to false and facturx_conformance to null, then restarts generation in the background. A download issued right after a PATCH may return nothing.
  • The PATCH clears import errors without re-checking them (see step 1).

One field, on the other hand, is refused outright rather than ignored: lifecycle_state. Sent with a value, it fails the whole call with 422, the code invalid_argument, and the message lifecycle_state ne peut pas être modifié sur ce point d'entrée. Utilisez PATCH /api/v1/invoices/{id}/transition pour faire évoluer la facture dans son cycle de vie. No change is applied, not even to the other fields of the payload. The refusal is about the presence of a value: lifecycle_state: null or an empty string passes through the endpoint with no error and no effect. To move the invoice forward, go through step 4 below (The invoice lifecycle).

curl -X PATCH https://app.scribee.tech/api/v1/invoices/12345 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"invoice": {
"invoice_number": "DRAFT-CRM-2026-0042",
"issue_date": "2026-07-31",
"due_date": "2026-09-15",
"type_code": "invoice",
"currency_code": "EUR",
"parties": [
{ "role": "buyer", "party_id": 42 }
],
"lines": [
{
"line_id": "1",
"article_name": "Prestation de conseil",
"quantity": 2.0,
"quantity_unit_code": "day",
"article_unit_price_excluding_taxes": 500.0,
"line_extension_amount": 1000.0,
"tax_category_id": "S",
"vat_rate": 20.0
}
],
"tax_subtotals": [
{ "tax_category_id": "S", "vat_rate": 20.0, "vat_amount": 200.0, "amount_without_taxes": 1000.0, "currency_code": "EUR" }
],
"total_amount_excluding_taxes": 1000.0,
"total_tax_amount": 200.0,
"tax_inclusive_amount": 1200.0,
"payable_amount": 1200.0
}
}'
{
"data": {
"id": 12345,
"due_date": "2026-09-15",
"lifecycle_state": "draft"
}
}

Step 3: delete the draft​

DELETE /api/v1/invoices/{id} permanently deletes the invoice. The endpoint accepts destroy or write: the write scope carried by the common read write combination is therefore enough, and destroy stays available for a client that deletes without writing (Authentication).

The call only succeeds on a draft whose number is absent or starts with DRAFT-. Any other number - yours, or the TEMP- one Scribee wrote when invoice_number was missing at creation - makes the invoice undeletable and the call returns 403.

curl -X DELETE https://app.scribee.tech/api/v1/invoices/12345 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

The response is a 204 status with no body.

Step 4: deposit the invoice​

Deposit is the point of no return. This call:

  • replaces a DRAFT- number with the definitive number from the company's numbering template, and increments its numbering sequence;
  • moves the invoice to the deposited state (200 Deposited) and records the event in lifecycle_events;
  • regenerates the formats, keeping a PDF you provided;
  • triggers delivery to your accounting software if a connected accounting integration is set to auto-deliver on deposit.

An invoice created by the API is a legal original: once deposited, it never reverts to draft (revert_to_draft is only offered on invoices deposited into Scribee from a file issued elsewhere). Any correction goes through a credit note (type_code: credit_note) referencing the original invoice via invoice_references.

curl -X PATCH https://app.scribee.tech/api/v1/invoices/12345/transition \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "invoice": { "event": "deposit" } }'
{
"data": {
"id": 12345,
"invoice_number": "FAC-2026-0198",
"lifecycle_state": "deposited",
"lifecycle_status_code": "200",
"lifecycle_available_transitions": ["collect"],
"delivery_status": "not_delivered"
}
}

On a sales invoice, your system itself triggers only two events: deposit (200 Deposited) then, once payment is received, collect (212 Collected). Recording a payment that settles the invoice triggers collect on its own, with no transition call; undoing that payment can move the invoice back to 211 (Record payments). The other lifecycle codes are written by no call of this API, but inbound regulatory-flow processing can apply them: see The invoice lifecycle.

Duplicate a sales invoice​

POST /api/v1/invoices/{id}/duplicate creates a new draft from an existing sales invoice, whatever its lifecycle state. The call takes no body and requires the write scope. The original invoice is not modified, and nothing is transmitted.

curl -X POST https://app.scribee.tech/api/v1/invoices/12345/duplicate \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": {
"id": 12347,
"invoice_number": "DRAFT-3f9a1c0b7e2d4a65",
"issue_date": "2026-09-29",
"due_date": "2026-11-14",
"type_code": "invoice",
"lifecycle_state": "draft"
}
}

The 201 response returns the new draft in the same shape as GET /api/v1/invoices/{id}, with its own id. It is an ordinary draft: it is modified by PATCH (step 2), deleted (step 3) and deposited (step 4).

The copy starts afresh on what dates the invoice:

  • invoice_number is a provisional DRAFT- number, replaced by the definitive number at deposit;
  • issue_date is today's date;
  • due_date keeps the original's payment term: it falls as many days after today as the original due date fell after its issue date, and never before today. If the original had no due date, the copy falls due 30 days after today. On an invoice already paid at issuance, due_date carries the payment date, not a term: the copy, which is not declared already paid, then falls due 30 days after today. A term that would put the copy's due date past 9999-12-31 gets the duplication refused (see 422 on duplication: due date out of range).

Carried over from the original invoice: the customer (buyer.party_id), type_code (a credit note stays a credit note), currency_code, category_id (except a category archived since: it is then null on the copy; an invoice without a category gives a copy without a category, without taking the customer's default category), the lines with the first note and the discount of each, their product_type and their VAT category (tax.category_code, which for instance tells an exempt E line from a Z line at the same 0% rate), the invoice-level discounts, and margin_bases. A catalog line whose product is no longer sellable, or whose name, description or unit no longer match the product's, is carried over as a free line, with its own name, description, price and VAT. An invoice the copy cannot reproduce identically - charges, a price base quantity, discounts of another shape than the form's, several notes on a line, an exempt disbursement under another reason than the form's, or any other carried-over detail the copy does not give back identically - is not duplicated (see Errors and edge cases). The same goes for an invoice carrying payment_terms, buyer_reference, project_reference, contract_reference, tender_reference, accounting_cost or payable_rounding_amount, or a party other than seller and buyer (payee, payer, buyer_agent, seller_agent, invoicee, invoicer): the copy does not carry them over.

Rebuilt from the company's and the customer's current data, not copied: seller, the buyer details, payment_means, tax_due_date_code (BT-8: code 5 if the company has opted for VAT on debits at the time of the duplication, no code otherwise, whatever the original's), and the legal notices (item_notes). The original's legal notices must still appear identically on the copy: a notice the company has edited or removed since makes the duplication fail (see 422 on duplication: copy not identical to the original).

Not carried over: invoicing_period, the delivery date, invoice_references, purchase_order_reference, sales_order_reference, despatch_advice_reference, receiving_advice_reference, the payment reference (payment_id), payments, and attachments.

warning

A credit note or a corrected invoice must cite the invoice it corrects. Since invoice_references is not carried over, set it on the copy with a PATCH before depositing it, sending the whole invoice back (step 2).

What happens next​

  • The definitive number is consumed on the company's numbering sequence; it is never reused, even if the invoice is later cancelled.
  • The delivery_status field does not describe delivery to the recipient. It tracks delivery of the invoice to a connected accounting software. The values actually written are sent, confirmed, and failed; not_delivered is the default value; pending is declared but no code writes it. Without a connected accounting integration, the field stays not_delivered forever.
  • The transition history is read via GET /api/v1/invoices/{id} with include=lifecycle_events. To be notified instead of polling the API, see Webhooks.
  • Each format is downloaded from GET /api/v1/invoices/{id}/download: see Formats and downloads.
  • Deposit does not send an email to your client. To address the document to them, use Send by email and track delivery; recording received payments is covered by Record payments.

Errors and edge cases​

Errors common to all endpoints (401, 404, error envelope) are described in API conventions. Every 422 on this page also carries a machine code, stable and never translated: branch your handling on it rather than on the message text.

404 at creation: unknown company_id​

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

The company_id you sent does not exist in the targeted workspace, or is neither a number nor a string.

422 at creation: the offer does not include sales invoicing​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Votre offre n'inclut pas la facturation de vente. Contactez votre cabinet comptable pour mettre à niveau votre offre."
}

A company under an accounting firm only issues sales invoices if its offer allows it. Otherwise direction: "sales" returns 422: the targeted company exists and stays visible in the workspace, it is the operation that is refused. The body carries no details. POST /api/v1/workspaces/{workspace_id}/invoices/upload answers the same block with the same status and the same text, except that it places it in details.file rather than in message.

422 at creation: invalid direction​

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": { "direction": ["doit être 'sales' ou 'purchases'"] }
}

direction is required and is either sales or purchases. For an invoice you issue, send sales.

422 at creation: directory resolution​

Three possible failures on a parties entry, each with error: "unprocessable_entity", the code operation_failed, and a dedicated message, with no details:

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Aucun client ou fournisseur trouvé pour party_id 42 dans cette entreprise"
}
  • Aucun client ou fournisseur trouvé pour party_id 42 dans cette entreprise: the party_id does not exist in the directory of the selected company, or does not match the role (a sales buyer resolves against customers, not suppliers). Check the company_id you sent first, then the identifier in Customers and suppliers.
  • party_id n'est pas autorisé pour le rôle 'seller' : la partie de l'entreprise est dérivée de la fiche entreprise, pas de l'annuaire: remove the party_id from the seller entry; this role is filled in automatically.
  • 'abc' n'est pas un identifiant de référence valide : party_id et billing_address_id doivent être des entiers positifs: fix the reference format before replaying the call.

422 at creation or modification: payer party outside the EXTENDED-CTC-FR profile​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une partie 'payer' (tiers payeur, EXT-FR-FE-BG-02) n'est autorisée qu'en profil EXTENDED-CTC-FR : renseignez customization_id avec l'une des valeurs urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended, urn:cen.eu:en16931:2017#conformant#urn.cpro.gouv.fr:1p0:extended-ctc-fr, ou retirez la partie payer"
}

A parties entry with the payer role is only accepted under one of the two EXTENDED-CTC-FR customization_id values the message lists. The customization_id taken into account is the payload's as soon as the key is present, including an empty string; otherwise, on a PATCH, the one already stored on the invoice. The response carries no details key. Send the expected customization_id, or remove the payer entry.

422 at creation or modification: document_id does not designate a retainer invoice to settle​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "document_id 42 n'est pas une facture d'acompte que cette facture peut solder : elle doit être une facture d'acompte émise (386 ou 500) de la même entreprise, adressée au même acheteur party_id"
}

An invoice_references entry carrying document_id must designate a retainer invoice the invoice can settle, under the conditions described in Retainer invoice and final invoice after a down payment. The response carries no details key. Check the identifier, the retainer invoice's number, the currency_code, and the buyer party_id of both invoices, or send the reference by its values.

422 at creation or modification: role_code outside its roles, outside the list, or outside the profile​

Three distinct causes, each with error: "unprocessable_entity", the operation_failed code, and no details key. The first: a role_code carried by a role that has none - seller, buyer, tax_representative, or delivery. The message lists the six roles that carry one.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "role_code n'est pas accepté sur l'entrée 'seller' : seuls payer (EXT-FR-FE-44), payee (EXT-FR-FE-26), buyer_agent (EXT-FR-FE-04), seller_agent (EXT-FR-FE-67), invoicee (EXT-FR-FE-90) et invoicer (EXT-FR-FE-113) en portent un. Retirez-le de l'entrée 'seller'"
}

The second: a role_code whose value does not belong to the UN/CEFACT PartyRoleCode D22A list, on one of those six entries.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "'PRR' n'est pas un role_code valide : un code rôle de partie doit provenir de la liste UN/CEFACT PartyRoleCode D22A (UNCL 3035), sensible à la casse"
}

The third: a role_code carried by a payee party while the customization_id taken into account is not an EXTENDED-CTC-FR profile, the only profile that defines EXT-FR-FE-26.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "role_code sur une partie 'payee' (EXT-FR-FE-26) n'est défini qu'en profil EXTENDED-CTC-FR : renseignez customization_id avec l'une des valeurs urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended, urn:cen.eu:en16931:2017#conformant#urn.cpro.gouv.fr:1p0:extended-ctc-fr, ou retirez role_code de la partie payee"
}

This third check is evaluated after the second: a doubly wrong payload - an unlisted value on a payee party outside the profile - is told first about the value at fault. The customization_id taken into account is determined exactly as for the payer party: the payload's as soon as the key is present, otherwise, on a PATCH, the one already stored.

This check bears on the attribute alone: a payee party without a role_code stays accepted under every profile. That is the difference with the payer party, whose whole block requires the EXTENDED-CTC-FR profile and is therefore refused earlier, whether or not it carries a role_code (see the previous section).

Remove the role_code from entries that carry none, fix the value - it is taken as sent, with no case normalization (PR is accepted, pr is not) - or set the expected EXTENDED-CTC-FR customization_id. An empty value is never refused: it is treated as an absence.

422 at creation or modification: EXTENDED-CTC-FR actor​

A parties entry with the buyer_agent, seller_agent, invoicee, or invoicer role is only accepted under one of the two EXTENDED-CTC-FR profiles the message lists. Under any other customization_id, it is refused with error: "unprocessable_entity", the operation_failed code, and no details key, and nothing is recorded. The message names the first of these roles found in parties. The customization_id taken into account is determined exactly as for the payer party: the payload's as soon as the key is present, otherwise, on a PATCH, the one already stored. A payload that also carries a payer party is told the payer message first.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une partie 'buyer_agent' est un bloc EXTENDED-CTC-FR (XP Z12-012 EXT-FR-FE-BG-01 / -03 / -04 / -05) sans équivalent EN 16931 : renseignez customization_id avec l'une des valeurs urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended, urn:cen.eu:en16931:2017#conformant#urn.cpro.gouv.fr:1p0:extended-ctc-fr, ou retirez la partie buyer_agent"
}

Under those profiles, the role_code checks of the previous section apply to these actors: an unlisted D22A role_code is told the invalid value message, and on invoicee or invoicer a listed value other than the role's fixed code - IV for invoicee, II for invoicer - is told this one.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "role_code de l'entrée 'invoicee' doit valoir IV : ce bloc porte un code rôle fixe, et une autre valeur désignerait un autre acteur"
}

Each of these roles admits a single entry per invoice: a second entry of the same role is refused with the message Une seule partie 'invoicee' est admise [...], on POST and PATCH alike. Each actor also needs the party UBL nests it inside: a buyer_agent or invoicee without a buyer, or a seller_agent or invoicer without a seller, is refused with the message Une partie 'invoicee' exige une partie 'buyer' [...], rather than the actor vanishing from the UBL file while CII sends it. The company-side party (the seller of a sale, the buyer of a purchase), which the API adds when omitted, counts; on PATCH, which replaces every party, the set sent is what is judged. Each actor must also carry a name, its registered name (EXT-FR-FE-03 / -66 / -89 / -112, mandatory in the block), whether sent or filled from party_id; a name that is not a JSON string (for example false or a number) is refused like a missing name. Likewise, an identifier must come with its identifier_scheme_id (EXT-FR-FE-07 / -70 / -92-1 / -116): without it the call is refused with the message L'identifiant d'une partie 'invoicee' doit être accompagné de son identifier_scheme_id [...]. An endpoint_id must come with its endpoint_scheme_id (EXT-FR-FE-13 / -76 / -99 / -122): without it the call is refused with the message L'adresse électronique d'une partie 'invoicee' doit être accompagnée de son endpoint_scheme_id [...]. A legal_registration_id must come with its legal_registration_scheme_id (EXT-FR-FE-09 / -72 / -95 / -118): without it the call is refused with the message L'identifiant légal d'une partie 'invoicee' doit être accompagné de son legal_registration_scheme_id [...]. An actor address that states any field (address_line_1 to address_line_3, postal_code, city, country_subdivision) must carry its country_code: XP Z12-012 requires the country in the invoicee and invoicer addresses (EXT-FR-FE-107 / -130), and the Factur-X EXTENDED schema requires it in all four actor addresses. Without it the call is refused with the message L'adresse postale d'une partie 'invoicee' doit porter son country_code [...], rather than the address being dropped from the exported files. Conversely, an identifier_scheme_id, endpoint_scheme_id or legal_registration_scheme_id sent without its value is refused with the message The identifier_scheme_id of a 'invoicee' party is stated without its identifier [...]: a scheme is emitted only as the attribute of the value it qualifies. A directory_routing_identifier is refused on these four actors with the message A 'invoicee' party cannot carry a directory_routing_identifier [...]: it routes the seller and the buyer only, an actor's electronic address is its endpoint_id, and the one on the sheet named by party_id is not copied onto the actor. A stated country_code must be an ISO 3166-1 alpha-2 code: FRA or any other code Scribee does not recognise is refused with the message Le country_code 'FRA' d'une partie 'invoicee' n'est pas un code ISO 3166-1 alpha-2 [...], rather than stored empty and the whole address lost on export; a country_code that is not a JSON string is refused the same way. Each scheme must also belong to the list the EXTENDED-CTC-FR profile binds to its field: endpoint_scheme_id to the CEF EAS list (BR-CL-25), identifier_scheme_id and legal_registration_scheme_id to the ISO 6523 ICD list (BR-CL-10 / BR-CL-11). So ridet (0228) is refused as an endpoint_scheme_id, and fr_vat (9957) as an identifier_scheme_id or legal_registration_scheme_id, with the message Le endpoint_scheme_id 'ridet' d'une partie 'invoicee' n'est pas un schéma que la plateforme peut transmettre pour endpoint_id [...]. Only the codes of that list that Scribee's scheme table carries are accepted, less 0231, reserved for the single taxable entity: a listed code the table lacks is refused with the same message. An endpoint_id longer than 125 characters is refused with the message L'endpoint_id d'une partie 'invoicee' dépasse 125 caractères [...]: BR-FR-25 (EXT-FR-FE-12 / -75 / -98 / -121) rejects a longer electronic address at export. Under endpoint_scheme_id aife (0225), the endpoint_id may hold only letters, digits and + - _ .: ABC DEF is refused with the message L'endpoint_id 'ABC DEF' d'une partie 'invoicee' contient un caractère que le schéma 0225 n'admet pas [...] (BR-FR-23). An identifier or legal_registration_id under the siren (0002) scheme must be exactly 9 digits, otherwise the call is refused with the message Le legal_registration_id 'ABC' d'une partie 'invoicee' n'est pas un SIREN [...] (BR-FR-32). An identifier under the siret (0009) scheme must be exactly 14 digits, otherwise the call is refused with the message L'identifier '123' d'une partie 'invoicee' n'est pas un SIRET [...], and, when the actor's legal_registration_id is a SIREN, start with it, otherwise the call is refused with the message Le SIRET '55210055400013' d'une partie 'invoicee' ne commence pas par son SIREN '732829320' [...] (BR-FR-09). A scheme that is not a JSON string counts as missing. An identifier, endpoint_id or legal_registration_id that is not a JSON string (for example false or a number) is refused, even beside its scheme, with the message Le legal_registration_id d'une partie 'invoicee' doit être une chaîne JSON [...], rather than stored as its text and sent as an identifier nobody issued. These checks run after the role_code ones.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une partie 'invoicee' doit porter un nom : la raison sociale d'un bloc acteur EXTENDED-CTC-FR est obligatoire (XP Z12-012 EXT-FR-FE-03 / -66 / -89 / -112). Renseignez name sur l'entrée invoicee"
}

Set the expected EXTENDED-CTC-FR customization_id, or remove the entry; on invoicee and invoicer, send the role's fixed code or omit role_code; send one entry per role; set the actor's name as a string, the scheme of its identifier, of its electronic address and of its legal registration id, or remove them; and send the country_code of an actor address, or remove the address.

422 at creation or modification: VAT breakdown not reconciled with the totals​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "La ventilation de TVA ne se raccorde pas aux totaux du document : total_amount_excluding_taxes indique 1000.00 alors que la somme des montants correspondants des tax_subtotals vaut 1200.00. La règle PPF G1.53 tolère un écart de 0,01 et refuse la facture au dépôt au-delà : la ventilation est donc refusée ici plutôt qu'au moment du dépôt."
}

The message names the total at fault - total_amount_excluding_taxes for the sum of the amount_without_taxes values, total_tax_amount for the sum of the vat_amount values - then the two amounts it compared, to two decimals. Here the breakdown was filled with tax-inclusive amounts while the field carries the taxable base (BT-116): 1200.00 broken down against 1000.00 declared.

The two halves are checked one after the other and the first one to fail stops the call: correct the one the message names, then replay. The response carries no details key. No invoice is created, and on a PATCH no modification is applied.

422 at creation or modification: incoherent already-paid invoice​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une facture déjà payée (invoicing_process_id B2, S2 ou M2) doit indiquer un prepaid_amount égal au tax_inclusive_amount (BR-FR-CO-09) : prepaid_amount vaut '100.00', tax_inclusive_amount vaut '120.00'."
}

The invoice declares a B2, S2 or M2 framework, but its amounts or its date do not describe a complete settlement (see An invoice already paid at issuance). The checks run in this order and the first one to fail stops the call: prepaid_amount equal to tax_inclusive_amount, payable_amount at 0, payable_rounding_amount absent or at 0, due_date present, due_date no later than issue_date, no invoice_references entry carrying document_id. The message names the field at fault and, for amounts, the value it read, to two decimals; an absent value shows as empty (''). Dates are quoted in YYYY-MM-DD format.

The response carries no details key. No invoice is created, and on a PATCH no modification is applied. Correct the amounts, or go back to the B1, S1 or M1 framework if the invoice is not paid yet.

422 at creation or modification: triangular_position refused​

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": { "triangular_position": ["n'est pas inclus(e) dans la liste"] }
}

The value sent is not one of the three positions, or the invoice's direction does not accept it (see Triangular operation). The message is the same in both cases. No invoice is created, and on a PATCH no modification is applied: the invoice keeps its previous position.

422 at creation: number already in use​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une facture avec le numéro 'DRAFT-CRM-2026-0042' existe déjà pour cette entreprise."
}

Numbers are unique per company, including provisional numbers. Suffix your DRAFT- numbers with a unique reference from your system.

This constraint only applies to sales invoices: the same number can appear on a purchases invoice of the same company without triggering this conflict, since that number comes from your supplier and is outside your control.

The same conflict on a PATCH takes the generic validation envelope, with a code of its own for the duplicate:

{
"error": "unprocessable_entity",
"code": "duplicate_record",
"message": "La validation a échoué",
"details": { "base": ["Un enregistrement avec cet identifiant existe déjà"] }
}

422 at creation: incomplete detail line​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "La ligne '1' de la facture est incomplète : Article name doit être rempli(e). Ces informations sont obligatoires ; corrigez la ligne puis soumettez à nouveau la facture."
}

article_name, tax_category_id, article_unit_price_excluding_taxes, quantity, and quantity_unit_code are required on every line. Here article_name is missing; the attribute name shows up in English even in this French message, for lack of a localized label for invoice lines. Unlike the usual validation errors, this response carries no details key: the message names the offending fields directly. No invoice is created; fix the line and replay the call.

422 at creation or modification: disbursement refused​

At creation, a line marked disbursement outside category O and category E under VATEX-EU-79-C is refused in the shape of an incomplete detail line:

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "La ligne '1' de la facture est incomplète : Disbursement ne peut marquer qu'une ligne de catégorie O, ou E sous VATEX-EU-79-C (XP Z12-014 cas 16). Ces informations sont obligatoires ; corrigez la ligne puis soumettez à nouveau la facture."
}

A marked line sent with a non-zero vat_rate is refused the same way:

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "La ligne '1' de la facture est incomplète : Disbursement ne peut marquer une ligne portant de la TVA : un débours est remboursé pour son montant exact (CGI 267 II 2°). Ces informations sont obligatoires ; corrigez la ligne puis soumettez à nouveau la facture."
}

As is a line, marked or not, carrying VATEX-EU-79-C outside category E:

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "La ligne '1' de la facture est incomplète : Tax exemption reason code VATEX-EU-79-C n'est admis que sur une ligne de catégorie E. Ces informations sont obligatoires ; corrigez la ligne puis soumettez à nouveau la facture."
}

On a PATCH, these refusals take the generic validation envelope, under the disbursement key for the first two and tax_exemption_reason_code for the last:

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": { "disbursement": ["ne peut marquer qu'une ligne de catégorie O, ou E sous VATEX-EU-79-C (XP Z12-014 cas 16)"] }
}

On a purchase invoice, a marked line is refused at creation and at modification alike:

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "disbursement ne peut être porté que par une ligne de facture de vente : les débours (XP Z12-014 cas 16) sont déclarés par le vendeur sur sa propre vente. Retirez disbursement des lignes de la facture d'achat.",
"details": { "disbursement": ["disbursement ne peut être porté que par une ligne de facture de vente : les débours (XP Z12-014 cas 16) sont déclarés par le vendeur sur sa propre vente. Retirez disbursement des lignes de la facture d'achat."] }
}

A purchase line carrying disbursement set to false, as a GET returns it, is accepted. In every case, no invoice is created and, on a PATCH, the stored lines stay unchanged. Remove disbursement from the line, move it to category O or to category E under VATEX-EU-79-C, bring its vat_rate down to 0, or remove VATEX-EU-79-C from a line that is not in category E.

422 at creation or modification: single taxable entity statement sent​

On a sales invoice of a company that belongs to a single taxable entity, a tax_representative party or a document-level note with code tax_declaration (or TXD) is refused, whatever its content:

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Un membre d'assujetti unique ne peut pas envoyer de partie tax_representative ni de note TXD au niveau du document : Scribee reprend le BG-11 et la note MEMBRE_ASSUJETTI_UNIQUE des paramètres d'assujetti unique de la société et les fige sur la facture. Retirez-les de la requête"
}

On a sales invoice of a company that belongs to no single taxable entity, it is the tax_declaration note whose text is MEMBRE_ASSUJETTI_UNIQUE that is refused:

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "'MEMBRE_ASSUJETTI_UNIQUE' est réservé à un membre d'assujetti unique : cette société n'en déclare aucun. Déclarez l'assujetti unique dans les paramètres de la société, ou retirez la note"
}

In both cases, no invoice is created and, on a PATCH, the draft stays unchanged. Remove the party or the note from the payload and replay the call (A company that belongs to a single taxable entity).

422 at creation: invalid lifecycle_state value​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Valeur lifecycle_state invalide à la création : 'collected'. Valeurs autorisées : draft, deposited, available"
}

On a sales invoice, only deposited advances the lifecycle; available is reserved for purchases invoices. No invoice is created.

403: the invoice is no longer a draft​

PATCH /api/v1/invoices/{id} and DELETE /api/v1/invoices/{id} respond 403 with error: "forbidden" as soon as the invoice has left the draft state, or for a DELETE on a draft whose number does not start with DRAFT-. No change is applied. The message field here carries a technical English label produced by the authorization layer: do not parse it, rely on the HTTP status and on error.

If the invoice is deposited by another request while your PATCH is being processed, the call fails with 422, with the code operation_failed and the message Seules les factures en brouillon peuvent être mises à jour, whether the PATCH is a full update or carries only margin_bases. Here too, no change is applied: the invoice stays as the deposit froze it.

After deposit, issue a correction via a credit note.

422 at deposit: the refusing check names its cause​

Deposit evaluates several business checks before moving the invoice forward. The first one to fail supplies the message, and each has its own text: on a draft, a deposit refusal never appears under the generic Impossible de passer de ... sentence any more.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "La facture comporte des erreurs d'import bloquantes. Corrigez-les avant de changer son statut.",
"details": {}
}

This check covers two situations. Either one of the four fields invoice_number, issue_date, type_code, currency_code was missing at creation and Scribee recorded a blocking error (see step 1). Or the PDF you supplied could not be processed: that failure too is recorded on the invoice as a blocking import error, with no conformance verdict - see Provide your own PDF. In both cases, fix it with a PATCH carrying the entire invoice, with an invoice_number prefixed DRAFT-, then deposit again.

A supplied PDF whose conformance was evaluated as non-compliant falls under a distinct check, evaluated before the import-error one: deposit is then refused with Le PDF Factur-X joint n'est pas conforme. Remplacez-le par un fichier conforme avant de déposer la facture. That draft cannot receive a new PDF - see Provide your own PDF.

The mandatory-legal-mentions check, for its part, is evaluated after the import-error one. It returns its own message, Des mentions légales obligatoires sont absentes de la facture (...). Renseignez-les dans les paramètres de facturation de l'entreprise, puis réessayez., together with a details object naming, key by key, the attributes to fill in. It is detailed in The invoice lifecycle.

The single taxable entity statement check is evaluated right after the legal mentions one. It only targets sales invoices Scribee issues - created by POST /api/v1/workspaces/{workspace_id}/invoices or by converting a quote, never imported through POST /api/v1/workspaces/{workspace_id}/invoices/upload - and created while the company declared a single taxable entity. It refuses the deposit when the invoice does not carry the three items described in A company that belongs to a single taxable entity: Cette facture a été créée alors que la société était membre d'un assujetti unique, mais elle ne le déclare pas en entier (SIREN du vendeur sous le schéma 0231, raison sociale, numéro de TVA et adresse de l'assujetti unique, note MEMBRE_ASSUJETTI_UNIQUE). Complétez l'assujetti unique dans les paramètres de la société, puis réenregistrez le brouillon avant de le déposer. Complete the settings in the Scribee interface, then send a PATCH on the draft, which writes the three items onto it, and deposit again. If the company no longer declares, with its complete details, the single taxable entity SIREN it declared when the invoice was created, the PATCH does not write them and the deposit stays refused.

The billing mandate check only covers sales invoices naming an invoicer (EXT-FR-FE-BG-05). It refuses the deposit as long as the company does not hold, on the issue date (BT-2), an active third-party invoicer billing mandate for that party, matched on its legal_registration_id: Tiers facturant doit disposer d'un mandat de facturation (tiers facturant) de la société actif à la date d'émission. The invoice stays a draft. Have the mandate recorded, then deposit again. A PATCH of a draft that changes the issue date (or the type, direction or company) likewise judges the invoicer the payload installs, never the one it replaces: an invoicer without an active mandate on the new date is refused with 422 and the same message, and nothing is changed; removing the invoicer is accepted.

An active mandate is not enough for the deposit: it must have been attested by an administrator of the company. When none of the active mandates covering the third-party invoicer on the issue date is attested, the deposit is refused with 422, with the code operation_failed and a message naming the third-party invoicer as the mandate records it: Tiers facturant doit disposer d'un mandat de facturation (tiers facturant) attesté : le mandat de Compta Services couvrant la date d'émission n'a pas été attesté par un administrateur de la société. Attestez-le dans les mandats de facturation de la société avant de déposer la facture. The invoice stays a draft. This check only covers the deposit: a PATCH of the draft does not require the attestation. Have the mandate attested in the company settings of the Scribee interface, then deposit again.

The generic sentence Impossible de passer de deposited à deposit. L'état actuel ne permet pas cette transition. only survives for a call genuinely made from the wrong state - a deposit on an invoice that is no longer a draft, for example. It no longer reports any of these checks.

A deposit requested in a single call - POST /api/v1/workspaces/{workspace_id}/invoices with lifecycle_state: "deposited" - goes through the same checks and returns the same messages; no invoice is created then.

422 at deposit: no numbering template​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Impossible de déposer la facture : aucun modèle de numérotation configuré pour cette entreprise",
"details": {}
}

The provisional DRAFT- number cannot be replaced: the company's numbering template must be configured in the Scribee interface's invoicing settings before the first deposit.

422 at deposit: no exchange rate available​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Impossible de déposer cette facture en USD : aucun taux de change n'est disponible pour le 12/03/2026. Une facture en devise étrangère doit porter son total de TVA en EUR, ce qui exige un taux publié. Attendez la synchronisation des taux, puis relancez le dépôt.",
"details": {}
}

This refusal only concerns invoices denominated in a currency other than EUR: the deposit has to convert their VAT total into euros, and no published rate was found for the issue date. It is evaluated after the checks described above and before the EN 16931 check.

The invoice is not modified and stays a draft: replay the deposit unchanged once the rate is available. A wrong issue_date produces the same refusal - earlier than the rate history, or too far ahead of the most recent publication day: check it before waiting. See Invoicing in a currency other than the euro.

422: unknown event or reserved for the platform​

{
"error": "unprocessable_entity",
"code": "invalid_argument",
"message": "Évènement de cycle de vie inconnu ou manquant. Fournissez un nom d'évènement valide parmi les transitions disponibles de la facture.",
"details": {}
}

The event field must be one of the values listed in lifecycle_available_transitions. The code invalid_argument is specific to this case: an event reserved for the platform (for example receive) is recognized, and its refusal carries the code operation_failed with the message La transition receive ne peut pas être déclenchée manuellement.

403 on duplication: invoice cannot be duplicated​

POST /api/v1/invoices/{id}/duplicate responds 403 with error: "forbidden" and the message Vous n'êtes pas autorisé à effectuer cette action when the token does not carry the write scope, or when the designated invoice is a purchase invoice, a self-billed invoice (self_billed_*), a factored invoice (factored_invoice, corrected_factored_invoice), a retainer credit note (retainer_credit_note, 503), or a consolidated credit note (consolidated_credit_note, 262). No draft is created.

404 on duplication: invoice outside your scope​

Duplication responds 404 with the ordinary not_found envelope when the identifier does not exist, designates an invoice of another workspace, or a sales invoice of a company whose offer does not include sales invoicing.

422 on duplication: buyer not linked to a customer​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette facture n'est rattachée à aucun client et ne peut donc pas être dupliquée. Rattachez d'abord son acheteur à un client."
}

The original invoice is duplicated for the same customer, so its buyer must be linked to a customer record, which reads as a non-null buyer.party_id. No draft is created. Other creation refusals may respond the same way, with the code operation_failed and their cause in message.

422 on duplication: charges​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette facture comporte des frais que le formulaire de facture ne sait pas encore reprendre : elle ne peut donc pas être dupliquée."
}

An invoice carrying charges, at invoice level (allowance_charges with charge_indicator: true) or on one of its lines, is refused with this message: the copy rebuilds no charge. The duplication refusals in this section and the following ones are checked in the order they appear, and only the first one met is returned: an invoice carrying both charges and discounts that cannot be reproduced receives this message. No draft is created.

422 on duplication: price base quantity​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une ligne de cette facture exprime son prix pour une quantité de base différente de 1, que le formulaire de facture ne sait pas reprendre : elle ne peut donc pas être dupliquée."
}

The copy computes each line as quantity x unit price. A line whose price.base_quantity (BT-149) is set and other than 1 - 100 units at 10 EUR per 100, for instance - would come back at another amount: it is refused with this message. No draft is created.

422 on duplication: discounts that cannot be reproduced​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette facture comporte des remises de pied de facture que le formulaire de facture ne peut pas reproduire : elle ne peut donc pas être dupliquée."
}

The copy rebuilds a single invoice-level discount, spread over the groups of lines sharing a VAT rate and a VAT category: either the same percentage on every group, or an amount split in proportion to each group's net total. Invoice-level discounts (allowance_charges with charge_indicator: false) of any other shape - differing percentages, amounts that do not follow that proportion, or a discount that covers only some groups - are refused with this message. No draft is created.

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une ligne de cette facture comporte une remise que le formulaire de facture ne peut pas reproduire (il en permet une par ligne, en pourcentage ou en montant, calculée sur quantité x prix) : elle ne peut donc pas être dupliquée."
}

The copy rebuilds a single discount per line, from its percentage if it has one, otherwise from its amount, recomputed on quantity x unit price and rounded to two decimals. A line carrying more than one discount, or whose discount, recomputed that way, does not give back exactly the stored amount and percentage - for example an amount discount stated beyond two decimals -, is refused with this message. No draft is created.

422 on duplication: several notes on a line​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une ligne de cette facture comporte plusieurs notes, alors que le formulaire de facture n'en reprend qu'une par ligne : elle ne peut donc pas être dupliquée."
}

The copy carries over only the first note of each line. An invoice with a line carrying more than one note is refused with this message. No draft is created.

422 on duplication: exempt disbursement​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une ligne de débours exonérée de cette facture (catégorie E) indique un autre motif d'exonération que celui que le formulaire de facture saisit pour elle (VATEX-EU-79-C, \"REMBOURSEMENT\") : elle ne peut donc pas être dupliquée."
}

The copy carries over a line marked disbursement in category O, and in category E when it states exactly the reason the invoice form enters for an exempt disbursement: tax_exemption_reason_code set to VATEX-EU-79-C and tax_exemption_reason set to REMBOURSEMENT. A disbursement line in category E without a reason text, or under another text - Débours in the example of Disbursements (débours) included -, is refused with this message, because the copy would rewrite it. No draft is created.

422 on duplication: product changed or withdrawn from sale​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Une ligne de cette facture porte sur un produit modifié ou retiré de la vente depuis, et le formulaire de facture ne peut pas la reproduire en ligne libre : elle ne peut donc pas être dupliquée."
}

A catalog line whose product is no longer sellable, or whose name, description or unit no longer match the product's, is carried over as a free line, and a free line carries the default unit one (C62). Such a line stated in another unit is refused with this message. No draft is created.

422 on duplication: VAT breakdown of an invoice without lines​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette facture sans lignes comporte une ventilation de TVA dont le formulaire de facture ne peut pas reproduire les bases imposables : elle ne peut donc pas être dupliquée."
}

An invoice without lines is carried over through the rates and VAT amounts of its breakdown: each amount_without_taxes at a positive rate is recomputed from vat_amount and the rate, and a single 0% entry stating its VAT category receives the rest of total_amount_excluding_taxes. When that recomputation does not give back the stored tax_subtotals identically, the duplication is refused with this message. No draft is created.

422 on duplication: due date out of range​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette facture arrive à échéance trop longtemps après sa date d'émission pour que la copie reprenne le même délai de paiement : elle ne peut donc pas être dupliquée."
}

The copy carries the original's payment term over from today. When that term would put its due date past 9999-12-31, the duplication is refused with this message. No draft is created.

422 on duplication: copy not identical to the original​

{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Cette facture comporte des éléments que le formulaire de facture ne peut pas reproduire à l'identique : elle ne peut donc pas être dupliquée."
}

This refusal comes after all the previous ones and guarantees the result: the copy is identical to the original invoice in every business field it carries over, otherwise the call fails with 422 and nothing is created. Once the copy is built, Scribee compares it with the original field by field: the document's fields and its parties other than seller and buyer, every line, in order and with its grouping, with its content, its quantities and amounts, its VAT rate, category and exemption reason, its notes, its object identifiers and its product type; every discount, at line or invoice level, with its reason and codes; the VAT breakdown (tax_subtotals); the document-level notes (item_notes), legal notices included; the document totals and its amount due before any settlement. Not compared are the details the copy restates on purpose, described above: number, dates, invoicing period, delivery, invoice_references and the order and advice references, seller and buyer, payment means, tax_due_date_code, a category archived since, the tax_declaration note and the tax_representative party Scribee writes for a single taxable entity, and the original's payments. For legal notices, the only tolerated difference is a notice the company has added since the original was issued, under a subject (code) the original carries none under. An invoice carrying a custom document-level note, or a legal notice whose text the company has since edited or which it has removed, is therefore refused with this message. At the slightest difference, the copy is cancelled and the duplication refused with this message. No draft is created.

IP outside the allowlist: 403 or 404 depending on the endpoint​

If the workspace restricts IP addresses, the returned status depends on the shape of the route:

{
"error": "forbidden",
"message": "Cette adresse IP n'est pas autorisée pour cet espace de travail"
}

This response only concerns POST /api/v1/workspaces/{workspace_id}/invoices, which names the workspace in its path. Routes built on the invoice identifier - PATCH, DELETE, PATCH .../transition, GET .../download, POST .../duplicate - return 404 with the ordinary not_found envelope, indistinguishable from a non-existent invoice.