Skip to main content

Supporting documents

A purchase order, a delivery note, or a contract often accompanies the invoice it justifies: it lets the recipient reconcile the document and approve it without back-and-forth. The Scribee API attaches these files in two places: on an invoice, where they can be embedded in the transmitted e-invoice, and on a customer or supplier record, where they serve as reference documents. The EN 16931 standard provides for this transport (BG-24 block); Scribee applies the format constraints for you.

What Scribee does for you​

  • Embeds documents marked attachable_to_invoice in every format of the invoice generated after the marking: files embedded in the Factur-X, BG-24 block base64-encoded in the UBL and CII XML, pages appended to the end of the PDF rendering for pieces in PDF format. The Factur-X targets PDF/A-3, but conformance is an observed result - read facturx_conformance before treating the file as one (Formats and downloads).
  • Verifies at activation time that the file complies with the closed EN 16931 format list: an invoice never leaves with a non-compliant embedded document.
  • Names each embedded document, following rules that differ from one format to the next. In UBL and CII, the document identifier is your label - failing that, the file name - and its type is carried separately as a code from the closed BR-FR-17 list (BON_COMMANDE, DOCUMENT_ANNEXE, ...), derived from the kind (see the kinds table). In Factur-X, the embedded file always carries the name of the file you sent, never the label; that one becomes the description instead, replacing the plain-text type rather than adding to it. If you rely on these labels, send explicit file names in addition to the label.

Invoice documents, third-party documents​

  • POST /api/v1/invoices/{invoice_id}/supporting_documents: supporting documents specific to an invoice, sales or purchase alike. Only these documents are eligible for embedding in the transmitted invoice.
  • POST /api/v1/parties/{party_id}/supporting_documents: reference documents on a customer or supplier record (banking details, contract, Kbis extract). attachable_to_invoice is always false at this scope: these documents are never embedded in an invoice. Creating records is described in Customers and suppliers.

Document kinds​

kindLabelBT-123 code (UBL, CII)
banking_coordinatesBanking detailsRIB
purchase_orderPurchase orderBON_COMMANDE
delivery_noteDelivery noteBON_LIVRAISON
contractContractDOCUMENT_ANNEXE
kbisKBIS extractDOCUMENT_ANNEXE
identity_documentIdentity documentDOCUMENT_ANNEXE
general_termsGeneral terms and conditionsDOCUMENT_ANNEXE
stylesheetStylesheetFEUILLE_DE_STYLE
tracking_slipTracking slipBORDEREAU_SUIVI
tracking_validation_slipTracking and validation slipBORDEREAU_SUIVI_VALIDATION
prepayment_statementDown payment statementETAT_ACOMPTE
direct_payment_invoiceDirect payment invoice (subcontractor)FACTURE_PAIEMENT_DIRECT
co_contracting_summaryCo-contracting summaryRECAPITULATIF_COTRAITANCE
otherOtherPJA

The last column gives the code Scribee writes into the BT-123 of each embedded piece: cbc:DocumentDescription in UBL, ram:Name in CII. The XP Z12-012 standard (rule BR-FR-17) restricts this field to a closed list of codes, and Scribee derives it from the kind alone: contract, kbis, identity_document and general_terms share the generic code DOCUMENT_ANNEXE, and other yields PJA. Choose the most precise kind so that the recipient receives the matching code.

This enum is distinct from the mandate KYC documents enum (kbis_extract, identity_paper, signing_capacity_proof, additional): kbis is not kbis_extract, identity_document is not identity_paper. KYC documents are deposited on a mandate - see Electronic invoicing mandates - never through the endpoints on this page.

The general_terms kind only accepts PDF files, and the file must be a readable PDF: a corrupted PDF is refused at deposit.

Step 1: attach a document to an invoice​

This call stores the file in your workspace and attaches it to the invoice. It transmits nothing externally and is undone by the DELETE in step 5.

curl -X POST https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/supporting_documents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "supporting_document[kind]=purchase_order" \
-F "supporting_document[label]=Bon de commande BC-2025-0042" \
-F "supporting_document[file]=@bon_de_commande.pdf"

201 response, abridged to the fields useful here:

{
"data": {
"id": 87,
"kind": "purchase_order",
"label": "Bon de commande BC-2025-0042",
"attachable_to_invoice": false,
"filename": "bon_de_commande.pdf",
"content_type": "application/pdf",
"byte_size": 15234,
"download_url": "https://app.scribee.tech/storage/signed_blobs/bon_de_commande.pdf?token=..."
}
}

The request is multipart/form-data; kind and file are required, label is optional. Formats accepted at deposit: PDF, PNG, JPEG, GIF, CSV, DOCX, XLSX, ODT, ODS; strictly less than 10 MiB (10,485,760 bytes) per file. attachable_to_invoice is false on creation, with one exception: on a sales invoice, an explicit attachable_to_invoice=true together with an EN 16931 file format (PDF, PNG, JPEG, CSV, XLSX, ODS) is accepted as sent. The flag is then set, but nothing is embedded at that moment: creation records the document, it does not regenerate the invoice's files. Embedding happens at the next generation (see below). Everywhere else - a purchase invoice, a non-EN 16931 format, the flag omitted - the value silently falls back to false and step 2 is required.

Step 2: embed the document in the invoice​

PATCH .../toggle_attachable flips the attachable_to_invoice state. This call transmits nothing: it toggles a flag, and the actual embedding happens when Scribee generates the invoice's files. Reversible: call the same endpoint again to disable.

curl -X PATCH https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/supporting_documents/87/toggle_attachable \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": {
"id": 87,
"kind": "purchase_order",
"attachable_to_invoice": true
}
}

Activation is accepted under two conditions: the document belongs to a sales invoice, and its file is in the EN 16931 list (PDF, PNG, JPEG, CSV, XLSX, ODS). GIF, DOCX, and ODT can therefore be deposited in step 1 but do not get embedded. Deactivation is always accepted.

Step 3: list an invoice's supporting documents​

When importing an invoice, the files it carries are added to this list when they meet the accepted formats. They all have attachable_to_invoice=false, and the imported file stays unchanged. They may exceed the manual upload limit. A refused file or a reached limit produces an import warning.

  • UBL and CII, including the CII XML of a Factur-X: each BG-24 piece that contains a base64-encoded object (BT-125) becomes a document. Its kind is read from the BT-123 code, regardless of case, following the kinds table: RIB gives banking_coordinates, BON_LIVRAISON gives delivery_note, and so on. DOCUMENT_ANNEXE, PJA, or a missing or off-list code give other. Its label takes the BT-122 identifier when that differs from the file name, and stays empty otherwise. Extraction accepts at most 100 pieces and at most 50 MiB (52,428,800 bytes) of decoded data in total; beyond that, none of these pieces is imported.
  • Readable representation: the BG-24 piece that carries the invoice's readable representation - a PDF (BT-125-1 application/pdf) whose BT-123 is LISIBLE, or failing that a PDF named exactly lisible.pdf, regardless of case - is never added to this list: it is the one Scribee keeps as the invoice PDF, under the conditions described in Import existing invoices. A second LISIBLE piece, or a LISIBLE piece that is not a PDF, is kept as an other document.
  • Factur-X: files embedded in the PDF are added with kind=other. The invoice XML is excluded. Extraction accepts at most 100 files and less than 50 MiB (52,428,800 bytes) of decoded data in total, including the XML.

GET /api/v1/invoices/{invoice_id}/supporting_documents returns all of the invoice's documents, most recent first, without pagination: the response contains data with no meta object.

curl https://app.scribee.tech/api/v1/invoices/YOUR_INVOICE_ID/supporting_documents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": [
{
"id": 87,
"kind": "purchase_order",
"attachable_to_invoice": true,
"filename": "bon_de_commande.pdf"
}
]
}

GET /api/v1/invoices/{invoice_id}/supporting_documents/{id} returns a single document; its download_url serves the file's content. This link is temporary: it stays valid for one hour after the response that carries it, then answers 404. It is enough on its own to download the file, with no Bearer token: anyone holding it can use it until it expires. Deleting the document (step 5) ends it at once: the link then answers 404. It is the only way to revoke it before its hour is up. Do not store it and do not share it. The file is always served as an attachment (Content-Disposition: attachment). To get a fresh link, fetch the document or the list again: every response carries a new one.

Step 4: a customer's or supplier's documents​

Same deposit mechanism, on the third party's record. This call stores the file in your workspace, transmits nothing externally, and is undone by a DELETE on the same path.

curl -X POST https://app.scribee.tech/api/v1/parties/YOUR_PARTY_ID/supporting_documents \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "supporting_document[kind]=banking_coordinates" \
-F "supporting_document[label]=RIB 2025" \
-F "supporting_document[file]=@rib.pdf"
{
"data": {
"id": 91,
"documentable_type": "Party",
"documentable_id": 12,
"kind": "banking_coordinates",
"attachable_to_invoice": false
}
}

documentable_type is Party, never Customer or Supplier: both are subclasses of one model and the polymorphic column stores the base class. Filter on documentable_id, not on the type.

Reading follows the same paths as on the invoice side: GET /api/v1/parties/{party_id}/supporting_documents (list, not paginated) and GET /api/v1/parties/{party_id}/supporting_documents/{id} (single document). There is no toggle_attachable at this scope.

Step 5: delete a document​

This call deletes the record and the stored file - deleting the file is permanent, only a new deposit restores it. Invoices already transmitted are not modified.

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

204 response with no body. Same path on the third-party side: DELETE /api/v1/parties/{party_id}/supporting_documents/{id}. The write scope is required.

What happens next​

  • Embedding applies at the moment Scribee generates the invoice's files. In the UBL and CII XML, each activated document becomes a BG-24 block with its content base64-encoded, its name taken from label (or the file name), and the BT-123 code of its kind (see the kinds table). In the Factur-X, it is embedded in the PDF as an attached file, and the CII XML embedded in that PDF carries the same BG-24 block with the same code. The Factur-X's PDF/A-3 conformance remains an observed result and not a guarantee (facturx_conformance). In the PDF rendering, pages of pieces in PDF format are appended after the invoice; pieces in another format (PNG, CSV, ...) travel only in the XML and the Factur-X. An attached PDF is not checked for readability at deposit: a corrupt file is accepted and marked attachable, then simply skipped during concatenation, with no error and no signal in the API. Check your pieces before sending them.
  • Activate the flag before issuing the invoice (Issue a sales invoice): the export files are rebuilt on every draft modification, but a document added or activated after a copy has left the platform does not reach that copy, and a deletion recalls nothing.
  • General terms and conditions deposited at the company level from the Scribee interface are appended to Scribee-rendered sales PDFs only, and only when the deposited language matches the buyer's (or specifies none). They are added neither to a PDF you provide, nor to the attachments of the UBL, CII, and Factur-X formats. They do not go through the endpoints on this page.

Errors and edge cases​

422: missing field​

kind and file are required at deposit:

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Un fichier est requis."]
}
}

A missing kind produces "Un type de document est requis." in the same format. Complete the request and replay it.

422: file refused at deposit​

A file outside the accepted list (PDF, PNG, JPEG, GIF, CSV, DOCX, XLSX, ODT, ODS), a file of 10 MB or more, or a general_terms that is not a readable PDF, return 422 with the cause in details. Convert or shrink the file before replaying the call.

422: activation refused​

toggle_attachable refuses activation in two cases, each with its own message:

{
"error": "unprocessable_entity",
"code": "validation_failed",
"message": "La validation a échoué",
"details": {
"base": ["Le format du fichier n'est pas conforme à EN 16931."]
}
}
  • "Ce document ne peut pas être joint au flux de facturation.": the document belongs to a purchase invoice; only sales invoices embed documents.
  • "Le format du fichier n'est pas conforme à EN 16931.": the file is a GIF, DOCX, or ODT. Redeposit the piece in an EN 16931 list format (PDF, PNG, JPEG, CSV, XLSX, ODS), then activate it.

404 Not Found​

Four causes. The invoice, the third party, or the document does not exist; it belongs to a workspace outside your application's scope; it belongs to a workspace whose IPv4 allowlist denies your request's address - on these endpoints a denied address produces this 404, not the 403 described in the API conventions; or the company's offer does not cover sales invoicing, which hides both its sales invoices and its customer records (suppliers stay visible). That last cause returns 404 on a resource that exists and is inside your scope.

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

Check the identifier and the workspace attachment (Your first call).

403 Forbidden: insufficient scope​

Deposit and toggle_attachable require the write scope; deletion requires write:

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

Request a new token with the required scopes (Authentication).