DEV Community

Ashish Kushwaha
Ashish Kushwaha

Posted on • Originally published at cellystial.com

Generate PDFs in your n8n workflows with Cellystial

A practical walkthrough: install the Cellystial community node, add your API key, generate a PDF from a template using data from a previous step, and save the file.

I build Cellystial, a PDF generation tool, and one of the things people kept asking for was an n8n node. So we made one. This post walks through using it: installing the node, adding your key, and generating a real PDF from data that comes out of an earlier step in your workflow.

The PDF is returned as a binary file, so you can drop it straight into Send Email, Write Binary File, Google Drive, S3, or whatever you already use.

A finished n8n workflow: data comes in, the Cellystial node makes the PDF, the next node sends it.

One thing up front so nobody is surprised: this is an official Cellystial community node. You can install it on self-hosted n8n today. It is not yet a verified node and it is not on n8n Cloud yet (verification is in progress). If you run self-hosted n8n, you are good to go.

What you need

  • A self-hosted n8n instance.
  • A Cellystial account. Registration is open and free, no card required.
  • A template in your account that you want to fill with data.

Step 1: Install the node

In n8n, go to Settings -> Community nodes -> Install.

Type the package name:

n8n-nodes-cellystial
Enter fullscreen mode Exit fullscreen mode

Accept the community-node prompt and click Install. After a moment you'll see two new nodes in the panel:

  • Cellystial is the action node (generate PDFs, check batch status).
  • Cellystial Trigger fires your workflow when something happens in your account.

Open the Community nodes tab.

Click Install a community node.

Enter n8n-nodes-cellystial and accept the prompt.

Installed: the Cellystial and Cellystial Trigger nodes are ready.

Step 2: Get an API key and add the credential

Log in to your dashboard at app.cellystial.com and go to Settings -> Developer & API. Create a key. Keys start with sk_prod_ for real renders, or sk_test_ for free, watermarked renders that are good for trying things out.

Back in n8n, create a new Cellystial API credential and paste the key in. n8n validates the key the moment you save it, so if you typo'd it or grabbed the wrong one, you'll know right away instead of getting a confusing error later.

My advice: start with an sk_test_ key. Test renders are free and watermarked, so you can wire the whole thing up and prove the flow works without paying for it. Swap to sk_prod_ when you're happy.

Get your key in the dashboard under Settings, Developer and API.

Paste it into a new Cellystial API credential in n8n.

Step 3: Generate a PDF

Add a Cellystial node and set the operation to Generate PDF. This generates one PDF per incoming item.

You'll see these fields:

  • Template Name or ID is a dropdown that loads the templates from your account automatically. Pick the one you want.
  • JSON Payload is the data that goes into the template.
  • File Name defaults to document.pdf.
  • Put Output File in Field is the field name the binary lands on. Defaults to data.

Add the Cellystial node.

Choose the Generate PDF operation.

Pick your template; it loads from your account.

Mapping data into the JSON Payload

This is the part worth slowing down on.

The JSON Payload field holds the data your template expects. The keys have to match the variable names in your template. You can type a literal JSON object, or you can map values from earlier nodes.

To map from a previous step, click the field and switch it to Expression mode (the toggle near the field). Then you can write an n8n expression.

If the incoming item already has the exact shape your template wants, the simplest thing is to pass the whole item through:

{{ $json }}
Enter fullscreen mode Exit fullscreen mode

Or build the object yourself and pull individual fields:

{
  "customer": "{{ $json.name }}",
  "total": {{ $json.amount }}
}
Enter fullscreen mode Exit fullscreen mode

Here's a fuller example payload. Say you have a coffee shop receipt template:

{
  "customer": {
    "name": "Mara Ellis",
    "email": "mara@example.com"
  },
  "receipt": {
    "number": "RCPT-5821",
    "date": "Jun 17, 2026",
    "method": "Visa ····4242"
  },
  "items": [
    { "qty": "2", "name": "Cappuccino (12 oz)", "price": "$4.50" },
    { "qty": "1", "name": "Almond croissant", "price": "$3.90" },
    { "qty": "1", "name": "House blend beans (250g)", "price": "$10.50" }
  ],
  "totals": { "total": "$23.40", "balanceDue": "$0.00" },
  "status": "PAID"
}
Enter fullscreen mode Exit fullscreen mode

In a real workflow those values would be expressions pointing at whatever came before, like a webhook payload or a row from your database. The literal version above is handy for a first test run.

Map data from a previous step into the JSON Payload, in expression mode.

Execute the step to generate the PDF.

Step 4: Do something with the PDF

When the node runs, the PDF comes out as binary on the field you chose (data by default). The incoming JSON is carried through unchanged, so you keep any fields you need later, like the order id or the customer's email.

From there it's normal n8n. A few common next steps:

  • Write Binary File saves it to disk.
  • Send Email attaches it.
  • Google Drive / S3 uploads it.

Point the binary property of those nodes at your output field (data) and you're done.

The generated PDF comes back as binary on the data field.

The finished PDF.

Doing a lot of them: the Batch operation

If you're generating many PDFs at once, switch the operation to Generate PDFs (Batch). It queues an async batch where each incoming item becomes one row.

The data field here is named Row Data instead of JSON Payload. It already ships in expression mode and defaults to {{ $json }}, the whole incoming item, used once per row. (For single Generate PDF you flip JSON Payload to expression mode yourself; Row Data is already set up that way.)

A few optional fields you might want:

  • Document ID is your own id for each PDF, echoed back so you can match each output to its source. It's also used as the filename.
  • Output Filename defaults to the Document ID.
  • Completion Webhook URL gets called when the batch finishes.

The batch operation returns a single item with the batch id and its status (queued while it gets going). To check on it later, use the Get Batch Status operation with that Batch ID. While it's still working you get a status summary. Once it's done it emits one item per row with the download links, including a zipUrl for the whole batch.

The Trigger node, briefly

The Cellystial Trigger starts a workflow when something happens in your account. On activation it registers an n8n webhook as a Cellystial subscription, and it cleans that up when you deactivate the workflow. The events are:

  • pdf.generated, a single PDF finished
  • batch.completed, a bulk batch finished
  • template.created
  • template.updated
  • template.deleted

Deliveries are HMAC-signed (via the X-Cellystial-Signature header), so you can confirm they actually came from us.

A common pattern: have something generate a PDF, then use the pdf.generated trigger in a second workflow to post a Slack message or update the order record once the file is ready.

That's it

Install the node, paste a test key, pick a template, map your data into the JSON Payload, and send the binary wherever it needs to go. Start with an sk_test_ key so the first run is free.

The full guide with step-by-step screenshots lives here: https://cellystial.com/integrations/n8n

If you try it and hit something rough, I'd genuinely like to hear about it.

Top comments (0)