How to generate a compliant e-invoice with n8n and SealDoc
Generating a compliant e-invoice involves more moving parts than it looks. You need a valid XML invoice, the right EN 16931 profile, a PDF/A-3 container with that XML embedded, and ideally an RFC 3161 timestamp before it goes into your archive. Building all of that into a custom integration is weeks of work.
This tutorial wires the same result together in n8n with the n8n-nodes-sealdoc community node. No custom code. The workflow generates the invoice, fetches the XML and the PDF/A-3, and stores both.
Prerequisites
- A self-hosted n8n instance with community nodes enabled.
- A SealDoc workspace with API access: the free Developer plan or any paid plan. See pricing.
- A SealDoc API key. Create one in the SealDoc app under Integration → API Keys. Copy it straight away: it is shown only once.
- Your company details. The seller on the invoice is your own company, and its VAT number has to match the VAT number of your SealDoc workspace.
Install the node in n8n under Settings → Community nodes → Install, with the package name n8n-nodes-sealdoc. If you install nodes by hand instead, run this in your n8n nodes folder (usually ~/.n8n/nodes) and restart n8n:
npm i n8n-nodes-sealdoc
Then create the credential: Credentials → New → SealDoc API, and paste your key into the API Key field. Leave Base URL on https://api.sealdoc.eu.
What the workflow does
- A trigger provides the invoice data as JSON: a webhook, a database row, a form submission, or any other n8n source.
- SealDoc · Invoice · Generate builds the e-invoice, validates it and seals it into a PDF/A-3. This happens within the call: the node returns a finished job.
- SealDoc · Invoice · Get XML returns the e-invoice XML as a file.
- An HTTP Request node downloads the PDF/A-3.
- A storage node puts both files in your archive.
Step 1: set up the trigger
Create a new workflow and add a Webhook trigger with HTTP method POST. That gives you a URL any system can call with invoice data. For testing, a Manual Trigger with fixed data works just as well.
A minimal example of what the trigger can provide:
{
"invoiceNumber": "INV-2026-00042",
"invoiceDate": "2026-05-06",
"dueDate": "2026-06-05",
"buyer": {
"name": "Widget BV",
"vatNumber": "NL987654321B01",
"address": "Keizersgracht 10",
"postalCode": "1015 CN",
"city": "Amsterdam",
"country": "NL"
},
"line": {
"description": "Consulting services May 2026",
"quantity": 10,
"unitPriceExclVat": 150.00
}
}
The seller is not in this payload. It is your own company, so you set it once in the node.
Step 2: add Invoice · Generate
Add a SealDoc node, rename it to Generate invoice, and set:
- Resource: Invoice. Operation: Generate.
- Invoice Number Source: Own Number. Invoice Number:
{{ $json.invoiceNumber }}. Prefer SealDoc to number your invoices? Choose SealDoc Numbering Series instead. - Invoice Date:
{{ $json.invoiceDate }}. Due Date:{{ $json.dueDate }}. - Currency: EUR.
- VAT Rate:
0.21. The rate is a fraction, so 21% is 0.21. Leave VAT Category on “derive from the rate” and VAT Label empty: for a standard rate, SealDoc writes the VAT caption itself. - Seller: your company, with Name, VAT Number, Address, Postal Code, City and Country (two-letter code).
- Buyer Source: Buyer Details. Buyer: map each field from the trigger, for example
{{ $json.buyer.name }}and{{ $json.buyer.postalCode }}. Customers you saved in SealDoc can be used directly with Buyer Profile. - Lines: add a line with Description
{{ $json.line.description }}, Quantity{{ $json.line.quantity }}, Unit Hour and Unit Price (Excl. VAT){{ $json.line.unitPriceExclVat }}. - Profile: Basic (Factur-X 1.0). The node also offers XRechnung DE, which needs a Buyer Reference (the Leitweg-ID) and a seller contact, and Peppol BIS 3.0 (UBL 2.1), which needs the Peppol endpoint IDs of both parties.
- RFC 3161 Timestamp: on, which is the default.
Select your SealDoc API credential and run the node. The response looks like this:
{
"jobId": "3f6c2a9e-8d41-4b7a-9c0e-5a2b1d7e4f10",
"status": "completed",
"statusUrl": "/api/jobs/3f6c2a9e-8d41-4b7a-9c0e-5a2b1d7e4f10"
}
completed means the invoice is built, validated against EN 16931 and sealed. There is nothing to poll. If a response ever says queued or processing instead, a SealDoc · Job · Get node with the same jobId tells you when it is done.
Step 3: get the XML
Add a second SealDoc node: Resource Invoice, Operation Get XML, Job ID {{ $('Generate invoice').item.json.jobId }}. The node returns the e-invoice XML as a binary file in the field data: factur-x.xml for the Basic profile, xrechnung.xml for XRechnung and peppol-ubl.xml for Peppol BIS.
Step 4: download the PDF/A-3
Add an HTTP Request node:
- Method: GET
- URL:
https://api.sealdoc.eu/api/download/{{ $('Generate invoice').item.json.jobId }} - Authentication: Generic Credential Type, then Header Auth, with name
X-Api-Keyand your API key as value. - Response Format: File.
The result is the sealed PDF/A-3 with the XML embedded.
Step 5: store the result
Add whichever storage node fits your stack: Google Drive, Dropbox, an S3-compatible bucket, SFTP to your own server, or Write Binary File on the n8n host itself.
From the Starter plan, every job also has a Legal Evidence Pack: GET /api/jobs/{id}/evidence-pack, with the same X-Api-Key header. SealDoc assembles it right after the job; if you ask in the first moments, the API answers 409 with a Retry-After header, so a short Wait node before the request is enough.
When something goes wrong
Errors come back immediately, as a response with a code:
- 400 for invalid input, for example
seller_required,currency_requiredorinvalid_vat_rate. - 402
plan_feature_requiredwhen your plan does not include the feature. - 403
seller_vat_mismatchwhen the seller VAT number differs from your workspace. - 409
quota_reachedwhen your monthly limit is used up. - 422
einvoice_invalidwhen the invoice does not pass validation.
By default the SealDoc node stops the workflow on an error. Set the node’s On Error setting to continue with an error output and route that output to Slack or email, so a person sees what needs fixing.
What you get
A PDF/A-3 that any PDF viewer opens as a readable invoice, with the XML embedded for accounting software that imports Factur-X or ZUGFeRD. The RFC 3161 timestamp token is stored with the job and can be downloaded separately (GET /api/jobs/{id}/timestamp). Drop the PDF into our free Validator to see the verdict and every finding for yourself.
Next steps
- Validate existing invoices: SealDoc · Validator · Check validates a Factur-X or ZUGFeRD file you already have, without generating anything.
- Render other documents: SealDoc · Document · Generate turns Markdown or HTML into a sealed PDF/A-3 in one call.
- Read back what was issued: SealDoc · Invoice · Get Summary returns the invoice number, profile, language and VAT totals of a generated invoice.
The full API reference, with an OpenAPI spec and curl examples, is linked from our developers page.