Document Generation

n8n to PDF: Generating PDFs from n8n workflows using Papermill

David White

Near-Earth Object Weekly Briefing

n8n is a popular tool for creating AI workflows and agents. It’s basically Zapier for the AI age, and has come to dominate the AI workflow space. It has tons of integrations (Papermill node coming soon!) and a very flexible approach.

Often you’ll want to generate PDFs from n8n. Most solutions use the old HTML->PDF hack, and have the usual consistency, quality and reliability problems. So why not use Papermill? It’s easy to integrate via a HTTP request node and you’ll be turning JSON into PDFs within n8n in no time.

Example - GitHub Project Reports

As an example, let’s create a workflow that emails our CTO a PDF report on our GitHub repo activity. I’ll use the n8n repo itself as an example.

Step 1: Sign up for Papermill

If you haven’t registered for a Papermill account, you can sign up for the free tier by going here: https://app.papermill.io/signup and then confirming your email.

Step 2: Set-up the template and get the template ID

Add this report template to your Papermill account. It was designed by Claude based on the Papermill Modern Report and using our MCP server. I told Claude that the AI would provide a commentary on three aspects of the repo, and that I wanted a template with a visualisation of each dataset using Papermill’s built-in visualisations, followed by that commentary.

For more info on creating templates using Claude, checkout the docs or this blog post.

Create a new template in Papermill (File-New in the template editor) and paste in the template from GitHub, replacing the example. You can generate the PDF in Papermill, but for now it will just show placeholders:

https://github.com/papermillio/templates/blob/main/blog/n8n/repo-report.press

Once you've added the template to Papermill, copy the template ID from the top right of the editor for later use.

This template draws from the three flows (the narratives) into page layout. We’ll ask our AI to generate three markdown strings, one for each flow, and send them to Papermill. We’ll also send the GitHub data to Papermill for use in the template.

Step 3: Create the n8n workflow

Sign up for n8n if you haven’t, and create a new “PDF demo” workflow.

Here’s the workflow - we’ll go through creating it step-by-step, but here’s a quick breakdown:

  1. A manual trigger to start the workflow.

  2. A node to set the repo owner and name.

  3. Three GitHub API calls to get general info, commit activity, and language stats for the repo, and a wait node to synchronise.

  4. A JavaScript code node to combine the data for the AI.

  5. An LLM Chain node to generate commentary on the data.

  6. Another code node to format the data for Papermill.

  7. A call to Papermill to get the PDF

  8. A gmail node to send the PDF via email.

Step 4: Set Repo Owner and Name

Create an “Edit Fields” node.

Set two fields: “owner” and “repo”. Both are strings. For our example, owner is “n8n-io” and repo is “n8n”, but you can put your own values in to inspect any GitHub repo.

This nicely separates the particular repo we want as inputs to the rest of the workflow.

Step 5: Get Data from the GitHub API

For the GitHub nodes, you’ll need to get a personal access token:

https://github.com/settings/personal-access-tokens

Create a “fine-grained token” that gives read-only access to public repositories if you’re happy analysing n8n and others now for now, otherwise give read-only access to the private repository you’d like to report on.

Create three HTTP nodes for the GitHub data. Set the URLs as following, replacing the repo name if you’re not using the n8n example:

For authentication on each, use “Generic Credential Type”, “Header Auth”, then create a new Credential, name it “Authorisation” and paste the GitHub key you created into the “Value” field.

Run the GitHub nodes to make sure they work!

Add a merge node afterwards to ensure n8n waits for the three nodes to complete running.

Step 6: Combine the GitHub Data

After the third GitHub node, add a “Code” node (in JavaScript) and add this code:

return {
    repo: $('GitHub: Repo Info').first().json,
    languages: $('GitHub: Languages').first().json,
    commitActivity: $('GitHub: Commit Activity').all().map(i => i.json)
  };

This formats the data nicely for the AI Agent, combining all the commits into one list.

Make sure you have “Run Once for All Items Set”.

Give it a whirl!

Step 7: Add an LLM

Add a “Basic LLM Chain” (not an agent).

You’ll need an add an AI model. In this example, we used Anthropic.

You can generate an API key for your Claude account from: https://platform.claude.com/dashboard

Add this prompt to get the AI to output three markdown strings: one for each of our datasets.

You write the three body sections of an automated GitHub repository report.
You are given the repo's JSON, its language byte counts, and its weekly commit counts.

Produce three short markdown commentaries:
- project_md    — what the project is, its scale, and its maturity
- commits_md    — how commit activity has trended over the past year
- languages_md  — what the codebase is written in and what that implies

Rules:
- Markdown only: paragraphs, `##` subheadings, **bold**, *italic*

Add a “Structured Output Parser” to make sure the model produces valid data. For the schema type choose “Generate from Markdown Example” and use this example:

{
  "project_md": "markdown text",
  "commits_md": "markdown text",
  "languages_md": "markdown text"
}

Step 8: Format the data for Papermill

We need to clean the GitHub data up a bit, and place the AI’s narratives into XML, so create a “Prepare Payload” code node and add the following:

const data = {
  reportDate: new Date().toLocaleDateString(),
  repo: $('Combine GitHub Data').first().json.repo,
  languages: [['Language', 'Bytes'], ...Object.entries($('Combine GitHub Data').first().json.languages)],
  commits:   [['Week', 'Commits'], ...$('Combine GitHub Data').first().json.commitActivity.map(w =>
    [new Date(w.week * 1000).toLocaleDateString(), w.total]
  )]
};

const payload = `<press>
  <flows>
    <body type="markdown">
<project-section />

${$json.output.project_md}

<commits-section />

${$json.output.commits_md}

<languages-section />

${$json.output.languages_md}
    </body>
  </flows>
  <data type="json"><![CDATA[${JSON.stringify(data)}]]></data>
</press>`;

return [{ json: { payload } }];

Step 9: send to Papermill

Now we pass our data and the AI’s analysis to Papermill for PDF generation.

Create a HTTP node to call Papermill:

  1. Method should be “POST”

  2. URL is: https://api.papermill.io/v2/pdf?template_id=your template ID copied from Papermill

  3. Authentication is “Generic Credential Type”, “Bearer Auth”, and then create a credential.

  4. Create a Papermill API key at: https://app.papermill.io/settings

  5. Paste the key into the credential.

  6. Select “Send Body”, set the body content type to “Raw” and “text/xml”

  7. Paste in the payload, which will incorporate the LLM’s analysis:

{{ $json.payload }}

Step 10: Send PDF by Email

Add a gmail node, ensure you’re authenticated, then set up a recipient address and subject. Under “Options” add an “Attachment” and set the field name to “data”.

Run it!
Now try running from the manual start and watch it run. If there are any problems, a node will go red and you can dive in and fix it - being able to re-run individual nodes is super helpful.

When it completes, you’ll receive an email with an attached PDF containing the report on your chosen GitHub repo.

n8n is a popular tool for creating AI workflows and agents. It’s basically Zapier for the AI age, and has come to dominate the AI workflow space. It has tons of integrations (Papermill node coming soon!) and a very flexible approach.

Often you’ll want to generate PDFs from n8n. Most solutions use the old HTML->PDF hack, and have the usual consistency, quality and reliability problems. So why not use Papermill? It’s easy to integrate via a HTTP request node and you’ll be turning JSON into PDFs within n8n in no time.

Example - GitHub Project Reports

As an example, let’s create a workflow that emails our CTO a PDF report on our GitHub repo activity. I’ll use the n8n repo itself as an example.

Step 1: Sign up for Papermill

If you haven’t registered for a Papermill account, you can sign up for the free tier by going here: https://app.papermill.io/signup and then confirming your email.

Step 2: Set-up the template and get the template ID

Add this report template to your Papermill account. It was designed by Claude based on the Papermill Modern Report and using our MCP server. I told Claude that the AI would provide a commentary on three aspects of the repo, and that I wanted a template with a visualisation of each dataset using Papermill’s built-in visualisations, followed by that commentary.

For more info on creating templates using Claude, checkout the docs or this blog post.

Create a new template in Papermill (File-New in the template editor) and paste in the template from GitHub, replacing the example. You can generate the PDF in Papermill, but for now it will just show placeholders:

https://github.com/papermillio/templates/blob/main/blog/n8n/repo-report.press

Once you've added the template to Papermill, copy the template ID from the top right of the editor for later use.

This template draws from the three flows (the narratives) into page layout. We’ll ask our AI to generate three markdown strings, one for each flow, and send them to Papermill. We’ll also send the GitHub data to Papermill for use in the template.

Step 3: Create the n8n workflow

Sign up for n8n if you haven’t, and create a new “PDF demo” workflow.

Here’s the workflow - we’ll go through creating it step-by-step, but here’s a quick breakdown:

  1. A manual trigger to start the workflow.

  2. A node to set the repo owner and name.

  3. Three GitHub API calls to get general info, commit activity, and language stats for the repo, and a wait node to synchronise.

  4. A JavaScript code node to combine the data for the AI.

  5. An LLM Chain node to generate commentary on the data.

  6. Another code node to format the data for Papermill.

  7. A call to Papermill to get the PDF

  8. A gmail node to send the PDF via email.

Step 4: Set Repo Owner and Name

Create an “Edit Fields” node.

Set two fields: “owner” and “repo”. Both are strings. For our example, owner is “n8n-io” and repo is “n8n”, but you can put your own values in to inspect any GitHub repo.

This nicely separates the particular repo we want as inputs to the rest of the workflow.

Step 5: Get Data from the GitHub API

For the GitHub nodes, you’ll need to get a personal access token:

https://github.com/settings/personal-access-tokens

Create a “fine-grained token” that gives read-only access to public repositories if you’re happy analysing n8n and others now for now, otherwise give read-only access to the private repository you’d like to report on.

Create three HTTP nodes for the GitHub data. Set the URLs as following, replacing the repo name if you’re not using the n8n example:

For authentication on each, use “Generic Credential Type”, “Header Auth”, then create a new Credential, name it “Authorisation” and paste the GitHub key you created into the “Value” field.

Run the GitHub nodes to make sure they work!

Add a merge node afterwards to ensure n8n waits for the three nodes to complete running.

Step 6: Combine the GitHub Data

After the third GitHub node, add a “Code” node (in JavaScript) and add this code:

return {
    repo: $('GitHub: Repo Info').first().json,
    languages: $('GitHub: Languages').first().json,
    commitActivity: $('GitHub: Commit Activity').all().map(i => i.json)
  };

This formats the data nicely for the AI Agent, combining all the commits into one list.

Make sure you have “Run Once for All Items Set”.

Give it a whirl!

Step 7: Add an LLM

Add a “Basic LLM Chain” (not an agent).

You’ll need an add an AI model. In this example, we used Anthropic.

You can generate an API key for your Claude account from: https://platform.claude.com/dashboard

Add this prompt to get the AI to output three markdown strings: one for each of our datasets.

You write the three body sections of an automated GitHub repository report.
You are given the repo's JSON, its language byte counts, and its weekly commit counts.

Produce three short markdown commentaries:
- project_md    — what the project is, its scale, and its maturity
- commits_md    — how commit activity has trended over the past year
- languages_md  — what the codebase is written in and what that implies

Rules:
- Markdown only: paragraphs, `##` subheadings, **bold**, *italic*

Add a “Structured Output Parser” to make sure the model produces valid data. For the schema type choose “Generate from Markdown Example” and use this example:

{
  "project_md": "markdown text",
  "commits_md": "markdown text",
  "languages_md": "markdown text"
}

Step 8: Format the data for Papermill

We need to clean the GitHub data up a bit, and place the AI’s narratives into XML, so create a “Prepare Payload” code node and add the following:

const data = {
  reportDate: new Date().toLocaleDateString(),
  repo: $('Combine GitHub Data').first().json.repo,
  languages: [['Language', 'Bytes'], ...Object.entries($('Combine GitHub Data').first().json.languages)],
  commits:   [['Week', 'Commits'], ...$('Combine GitHub Data').first().json.commitActivity.map(w =>
    [new Date(w.week * 1000).toLocaleDateString(), w.total]
  )]
};

const payload = `<press>
  <flows>
    <body type="markdown">
<project-section />

${$json.output.project_md}

<commits-section />

${$json.output.commits_md}

<languages-section />

${$json.output.languages_md}
    </body>
  </flows>
  <data type="json"><![CDATA[${JSON.stringify(data)}]]></data>
</press>`;

return [{ json: { payload } }];

Step 9: send to Papermill

Now we pass our data and the AI’s analysis to Papermill for PDF generation.

Create a HTTP node to call Papermill:

  1. Method should be “POST”

  2. URL is: https://api.papermill.io/v2/pdf?template_id=your template ID copied from Papermill

  3. Authentication is “Generic Credential Type”, “Bearer Auth”, and then create a credential.

  4. Create a Papermill API key at: https://app.papermill.io/settings

  5. Paste the key into the credential.

  6. Select “Send Body”, set the body content type to “Raw” and “text/xml”

  7. Paste in the payload, which will incorporate the LLM’s analysis:

{{ $json.payload }}

Step 10: Send PDF by Email

Add a gmail node, ensure you’re authenticated, then set up a recipient address and subject. Under “Options” add an “Attachment” and set the field name to “data”.

Run it!
Now try running from the manual start and watch it run. If there are any problems, a node will go red and you can dive in and fix it - being able to re-run individual nodes is super helpful.

When it completes, you’ll receive an email with an attached PDF containing the report on your chosen GitHub repo.

n8n is a popular tool for creating AI workflows and agents. It’s basically Zapier for the AI age, and has come to dominate the AI workflow space. It has tons of integrations (Papermill node coming soon!) and a very flexible approach.

Often you’ll want to generate PDFs from n8n. Most solutions use the old HTML->PDF hack, and have the usual consistency, quality and reliability problems. So why not use Papermill? It’s easy to integrate via a HTTP request node and you’ll be turning JSON into PDFs within n8n in no time.

Example - GitHub Project Reports

As an example, let’s create a workflow that emails our CTO a PDF report on our GitHub repo activity. I’ll use the n8n repo itself as an example.

Step 1: Sign up for Papermill

If you haven’t registered for a Papermill account, you can sign up for the free tier by going here: https://app.papermill.io/signup and then confirming your email.

Step 2: Set-up the template and get the template ID

Add this report template to your Papermill account. It was designed by Claude based on the Papermill Modern Report and using our MCP server. I told Claude that the AI would provide a commentary on three aspects of the repo, and that I wanted a template with a visualisation of each dataset using Papermill’s built-in visualisations, followed by that commentary.

For more info on creating templates using Claude, checkout the docs or this blog post.

Create a new template in Papermill (File-New in the template editor) and paste in the template from GitHub, replacing the example. You can generate the PDF in Papermill, but for now it will just show placeholders:

https://github.com/papermillio/templates/blob/main/blog/n8n/repo-report.press

Once you've added the template to Papermill, copy the template ID from the top right of the editor for later use.

This template draws from the three flows (the narratives) into page layout. We’ll ask our AI to generate three markdown strings, one for each flow, and send them to Papermill. We’ll also send the GitHub data to Papermill for use in the template.

Step 3: Create the n8n workflow

Sign up for n8n if you haven’t, and create a new “PDF demo” workflow.

Here’s the workflow - we’ll go through creating it step-by-step, but here’s a quick breakdown:

  1. A manual trigger to start the workflow.

  2. A node to set the repo owner and name.

  3. Three GitHub API calls to get general info, commit activity, and language stats for the repo, and a wait node to synchronise.

  4. A JavaScript code node to combine the data for the AI.

  5. An LLM Chain node to generate commentary on the data.

  6. Another code node to format the data for Papermill.

  7. A call to Papermill to get the PDF

  8. A gmail node to send the PDF via email.

Step 4: Set Repo Owner and Name

Create an “Edit Fields” node.

Set two fields: “owner” and “repo”. Both are strings. For our example, owner is “n8n-io” and repo is “n8n”, but you can put your own values in to inspect any GitHub repo.

This nicely separates the particular repo we want as inputs to the rest of the workflow.

Step 5: Get Data from the GitHub API

For the GitHub nodes, you’ll need to get a personal access token:

https://github.com/settings/personal-access-tokens

Create a “fine-grained token” that gives read-only access to public repositories if you’re happy analysing n8n and others now for now, otherwise give read-only access to the private repository you’d like to report on.

Create three HTTP nodes for the GitHub data. Set the URLs as following, replacing the repo name if you’re not using the n8n example:

For authentication on each, use “Generic Credential Type”, “Header Auth”, then create a new Credential, name it “Authorisation” and paste the GitHub key you created into the “Value” field.

Run the GitHub nodes to make sure they work!

Add a merge node afterwards to ensure n8n waits for the three nodes to complete running.

Step 6: Combine the GitHub Data

After the third GitHub node, add a “Code” node (in JavaScript) and add this code:

return {
    repo: $('GitHub: Repo Info').first().json,
    languages: $('GitHub: Languages').first().json,
    commitActivity: $('GitHub: Commit Activity').all().map(i => i.json)
  };

This formats the data nicely for the AI Agent, combining all the commits into one list.

Make sure you have “Run Once for All Items Set”.

Give it a whirl!

Step 7: Add an LLM

Add a “Basic LLM Chain” (not an agent).

You’ll need an add an AI model. In this example, we used Anthropic.

You can generate an API key for your Claude account from: https://platform.claude.com/dashboard

Add this prompt to get the AI to output three markdown strings: one for each of our datasets.

You write the three body sections of an automated GitHub repository report.
You are given the repo's JSON, its language byte counts, and its weekly commit counts.

Produce three short markdown commentaries:
- project_md    — what the project is, its scale, and its maturity
- commits_md    — how commit activity has trended over the past year
- languages_md  — what the codebase is written in and what that implies

Rules:
- Markdown only: paragraphs, `##` subheadings, **bold**, *italic*

Add a “Structured Output Parser” to make sure the model produces valid data. For the schema type choose “Generate from Markdown Example” and use this example:

{
  "project_md": "markdown text",
  "commits_md": "markdown text",
  "languages_md": "markdown text"
}

Step 8: Format the data for Papermill

We need to clean the GitHub data up a bit, and place the AI’s narratives into XML, so create a “Prepare Payload” code node and add the following:

const data = {
  reportDate: new Date().toLocaleDateString(),
  repo: $('Combine GitHub Data').first().json.repo,
  languages: [['Language', 'Bytes'], ...Object.entries($('Combine GitHub Data').first().json.languages)],
  commits:   [['Week', 'Commits'], ...$('Combine GitHub Data').first().json.commitActivity.map(w =>
    [new Date(w.week * 1000).toLocaleDateString(), w.total]
  )]
};

const payload = `<press>
  <flows>
    <body type="markdown">
<project-section />

${$json.output.project_md}

<commits-section />

${$json.output.commits_md}

<languages-section />

${$json.output.languages_md}
    </body>
  </flows>
  <data type="json"><![CDATA[${JSON.stringify(data)}]]></data>
</press>`;

return [{ json: { payload } }];

Step 9: send to Papermill

Now we pass our data and the AI’s analysis to Papermill for PDF generation.

Create a HTTP node to call Papermill:

  1. Method should be “POST”

  2. URL is: https://api.papermill.io/v2/pdf?template_id=your template ID copied from Papermill

  3. Authentication is “Generic Credential Type”, “Bearer Auth”, and then create a credential.

  4. Create a Papermill API key at: https://app.papermill.io/settings

  5. Paste the key into the credential.

  6. Select “Send Body”, set the body content type to “Raw” and “text/xml”

  7. Paste in the payload, which will incorporate the LLM’s analysis:

{{ $json.payload }}

Step 10: Send PDF by Email

Add a gmail node, ensure you’re authenticated, then set up a recipient address and subject. Under “Options” add an “Attachment” and set the field name to “data”.

Run it!
Now try running from the manual start and watch it run. If there are any problems, a node will go red and you can dive in and fix it - being able to re-run individual nodes is super helpful.

When it completes, you’ll receive an email with an attached PDF containing the report on your chosen GitHub repo.

Like this article? Share it.

Start generating documents today

Get your API key and generate your first PDF in under five minutes.