Provide your own PDF
Your product already generates PDF invoices in your visual identity, and your customers are used to them. This page shows how to keep that rendering as the visible layer of the Factur-X: you send your PDF when the invoice is created, Scribee embeds the CII XML generated from the data into it, and produces a Factur-X (PDF/A-3 with embedded XML). Without pdf_base64, Scribee generates the visible layer itself - this is the default path, no additional field is required.
What Scribee does for you
- XML generation: the
factur-x.xmlfile (CII) is produced from the invoice's JSON data. You never write XML. - PDF/A-3 assembly: Scribee embeds this XML in your PDF and writes the required structural layers into it - Factur-X XMP metadata, OutputIntent, associated files tree. An OutputIntent already present in your PDF is kept as is, without its colour profile being revalidated: supplying a PDF whose profile is conformant is on you.
- Replacement of existing XML: if your PDF is already a Factur-X, its
factur-x.xmlis replaced by the one generated from the invoice data. The data sent to the API is authoritative. - Supporting documents: the invoice's attachments are embedded in the same file (Supporting documents).
- Conformance validation: the assembled file is checked against PDF/A-3b and the result is exposed on the invoice (
facturx_conformance).
What Scribee does not do: fix the content of your pages. Fonts, colors, and transparency are your PDF generator's responsibility; non-compliant content is detected by validation, never repaired.
Your PDF, on the generator side
Points to check on the file your generator produces, before the first call:
- a PDF document - the API looks for the
%PDF-header within the first kilobyte of the file, not necessarily at the very first byte; - not encrypted, no password;
- under 50 MB once decoded;
- encoded in base64 - line breaks and a
data:application/pdf;base64,prefix are accepted; - fonts embedded in the file, PDF/A-3b-compliant transparency and color space (device-independent colors), no embedded JavaScript - these points are not checked at call time but by the conformance validation that follows, and Scribee does not fix them.
The first four points are checked synchronously: a non-compliant file is rejected with 422 before any creation. The last one is settled in your generator's configuration, once and for all.
Step 1: create the invoice with your PDF
This call creates a draft invoice in your production workspace: as long as it is not deposited, it transmits nothing to the recipient, nor to the PPF (Portail Public de Facturation), nor to the Peppol network. Validate your generator's PDF/A-3b conformance before this call: once the invoice is created, no endpoint lets you replace the provided PDF or, in most cases, delete the draft (see Errors and edge cases, section "The PDF cannot be replaced").
The pdf_base64 field sits in the invoice object, at the same level as the invoice's other fields. The payload below is reduced to the fields specific to this page; parties, lines, and totals are built as described in Issue a sales invoice.
PDF_BASE64=$(base64 < facture-INV-2026-042.pdf | tr -d '\n')
cat > payload.json <<EOF
{
"invoice": {
"direction": "sales",
"invoice_number": "INV-2026-042",
"issue_date": "2026-07-31",
"type_code": "invoice",
"currency_code": "EUR",
"pdf_base64": "$PDF_BASE64"
}
}
EOF
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 @payload.json
201 response, abridged to the fields relevant to this page:
{
"data": {
"id": 12345,
"invoice_number": "INV-2026-042",
"lifecycle_state": "draft",
"provided_pdf": true,
"facturx_conformance": "pending"
}
}
The four fields invoice_number, issue_date, type_code, and currency_code govern whether the PDF is taken into account: if one is missing, the invoice is created with a placeholder value and a blocking import error, and your PDF is ignored - provided_pdf then reads false in the response. Check provided_pdf: true before moving to step 2.
Step 2: read the conformance result
Assembly and validation run after the response, in the background. Re-fetch the invoice until facturx_conformance leaves pending.
curl https://app.scribee.tech/api/v1/invoices/12345 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"data": {
"id": 12345,
"invoice_number": "INV-2026-042",
"lifecycle_state": "draft",
"provided_pdf": true,
"facturx_conformance": "compliant"
}
}
facturx_conformance | Meaning | Consequence |
|---|---|---|
pending | Validation has not yet rendered its verdict, or its processing failed. | Deposit remains possible in the general case - it relaunches generation and validation - unless the PDF's processing has definitively failed (see "pending that persists" below). Transmission is blocked. |
compliant | The assembled file is PDF/A-3b compliant. | Deposit and transmission authorized. |
non_compliant | At least one PDF/A-3b rule is violated by the file. | Deposit and transmission blocked. |
facturx_conformance is null when you have not provided a PDF: the visible layer generated by Scribee is compliant by construction and is not subject to this validation. A sales invoice imported as a Factur-X is the exception: its file is judged at deposit, and the field carries that verdict (Import existing invoices).
Deposit is blocked by non_compliant, and by a pending that comes from the PDF being refused at processing, rather than from a validation still awaiting its verdict or a processing run that could not complete on Scribee's side. The deposit itself sends nothing to the PPF or over the Peppol network.
This table describes a sales invoice, the one you issue. On a purchase invoice the PDF is your supplier's and Scribee issues nothing: facturx_conformance takes the same value and the consequences above still stand, but the non_compliant verdict is recorded there as a warning rather than a blocking import error. It therefore does not stand in the way of processing the received invoice. Only the party that issued the PDF can clear the non-conformance.
Step 3: retrieve the assembled Factur-X
Once generation is complete, download the final file: your rendering, completed with the factur-x.xml, the XMP metadata, and the embedded supporting documents.
curl -o INV-2026-042-facturx.pdf \
"https://app.scribee.tech/api/v1/invoices/12345/download?format=facturx" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
The other export formats (pdf, ubl, cii) are described in Formats and downloads.
What happens next
- The invoice's creation triggers a notification to your configured endpoints (Webhooks).
- Export file generation runs in the background; it is what assembles the Factur-X, runs the conformance validation, and updates
facturx_conformance. - Every deposit (
deposit) relaunches generation and validation. Apendingmost often resolves on its own, but not always: when the validator is unavailable or errors out, the field is written back topending. Apendingthat persists across several reads is one to report to support, not one to wait out. - No document is transmitted to a recipient or to the regulatory network while the invoice is a draft. Your own webhook endpoints do receive
invoice.createdas soon as it is created (Webhooks). Deposit and status tracking are covered by Issue a sales invoice.
Errors and edge cases
422 at creation: PDF rejected
Four synchronous checks reject the call before any creation. The body follows the API error envelope (API conventions), with the code operation_failed and no details key:
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Le PDF fourni est chiffré et ne peut pas être traité."
}
message | Cause | What you do |
|---|---|---|
Le PDF fourni n'est pas encodé en base64 valide. | The string does not decode as base64. | Re-encode the file; line breaks and the data-URI prefix are accepted, characters outside the base64 alphabet are not. |
Le fichier fourni n'est pas un document PDF valide. | The %PDF- header is missing from the first kilobyte of the file, or the file cannot be parsed. | Check that you are encoding the final PDF, not another format or an archive. |
Le PDF fourni dépasse la taille maximale autorisée de 50 Mo. | The decoded file reaches 50 MB. | Reduce the file's size, generally by compressing images. |
Le PDF fourni est chiffré et ne peut pas être traité. | The PDF is password-protected or encrypted. | Generate the file without protection. |
provided_pdf set to false in the 201 response
Two causes, indistinguishable in the response. First: one of the four fields invoice_number, issue_date, type_code, currency_code was missing, and the payload never reached the attachment step. Second: the attachment itself failed (unavailable storage, for one); the error is then swallowed into an import warning and creation still answers 201. The two cases do not behave the same way. Missing fields: the payload never reaches the attachment step and nothing is kept. Failed attachment: the file may have been stored before the error, and export generation is launched anyway - the invoice then exists without provided_pdf, with Scribee-rendered exports. In both cases your PDF is not the one that will be served. A PATCH of the missing fields does not attach the PDF afterward (see "The PDF cannot be replaced" below): check provided_pdf in the response and recreate the invoice if needed.
Deposit refused on non_compliant
The deposit transition is refused as long as the file is non-compliant. The non-compliance verdict is recorded on the invoice as a blocking import error, but it is the conformance check - the first one evaluated at deposit - that names the refusal:
{
"error": "unprocessable_entity",
"code": "operation_failed",
"message": "Le PDF Factur-X joint n'est pas conforme. Remplacez-le par un fichier conforme avant de déposer la facture.",
"details": {}
}
The message therefore names the PDF, but not the failing rule. The detail of the failing rules is visible on the invoice from the Scribee interface. Fix your PDF generator's configuration (fonts, colors, transparency) for your next submissions: this draft can neither receive a new PDF nor, in most cases, be deleted (see "The PDF cannot be replaced" below).
pending that persists
- With deposit refused: processing refused the file (a malformed PDF, or one too costly to process: its assembly exceeded the calculation budget allotted to it); deposit stays blocked even though
facturx_conformancestill showspending. The processing failure is recorded as a blocking import error, and absent a non-compliance verdict, the refusal this time carries the generic messageLa facture comporte des erreurs d'import bloquantes. Corrigez-les avant de changer son statut.(Issue a sales invoice). No endpoint lets you fix the PDF on this draft (see "The PDF cannot be replaced"). - With deposit accepted: validation has not concluded yet; deposit relaunches it. The same applies when processing exceeds its overall execution deadline on Scribee's side, which is distinct from a PDF that exhausts its calculation budget: that is an incident on Scribee's side, not a verdict on your PDF, and it is recorded as a warning, which does not block deposit.
facturx_conformanceis then set back topending. Transmission waits forcompliant.
The PDF cannot be replaced
PATCH /api/v1/invoices/{id} ignores pdf_base64 and purges the already-provided PDF as soon as a draft is modified, even on another field: provided_pdf reverts to false and facturx_conformance to null, and the invoice falls back to the visible layer generated by Scribee. DELETE /api/v1/invoices/{id} resolves the situation in one case only: a draft is deletable when its number is empty or starts with DRAFT-. That covers the temporary number Scribee generates itself, and any number you supply carrying that prefix. Any other invoice_number you supply consumes a final number and deletion fails with 403. Validate your generator's PDF/A-3b conformance before the first call: once the invoice is created, no endpoint lets you fix or replace the provided PDF.
Related pages
- Issue a sales invoice - build the payload (parties, lines, totals), deposit, and track statuses
- Formats and downloads - retrieve the invoice as
pdf,ubl,cii, orfacturx - API reference: create an invoice