n8n Integration: Automate Document Generation

Last updated September 20, 2026

The PDFMonkey n8n node lets you generate, retrieve, download, and delete documents directly from your n8n workflows. If you are setting up PDFMonkey with n8n for the first time, start with the Getting Started section below.

Node version

This guide reflects n8n-nodes-pdfmonkey 1.0.0+. If your workflows were built on an older version, see the upgrade notes before updating — the Trigger’s webhook setup and the Generate Document error behavior both changed.

Getting Started #

n8n is a workflow automation platform that connects different apps and services together. It is an open-source alternative to tools like Zapier and Make, offering both cloud and self-hosted options.

Before you begin, make sure you have:

For the full account creation and template setup walkthrough, see From Zero to First Document.

Setting Up Credentials #

Before using the PDFMonkey node, you need to configure your API credentials:

  1. In your n8n workflow, add a PDFMonkey node
  2. Click on Create New Credentials
  3. Enter your PDFMonkey API key (found in your PDFMonkey dashboard)
  4. Click Save

Where to find your API key

Your API key is available in your PDFMonkey dashboard under Account then API. See Authentication for more details.

Your First Workflow #

Let’s create a simple workflow that generates a document when triggered manually. Use the template you created in From Zero to First Document, or create a simple template with a greeting variable.

Step 1: Add a Manual Trigger #

  1. Create a new workflow in n8n
  2. The Manual Trigger node should already be there
  3. This allows you to test your workflow manually

Step 2: Add the PDFMonkey Node #

  1. Click the + button to add a new node
  2. Search for PDFMonkey
  3. Select the PDFMonkey node
  4. Choose Generate Document as the operation

Step 3: Configure the Generate Document Operation #

In the PDFMonkey node configuration:

  1. Select your PDFMonkey credentials (or create them if you haven’t)
  2. Set your Template ID from your PDFMonkey template
  3. Set the Payload Input Method to Key-Value Pairs
  4. Add the following fields:
    • Key: name → Value: Peter Parker
    • Key: favoriteNumber → Value: 8

Payload methods

You can choose between Key-Value Pairs (simple) or JSON (for complex data structures). The JSON method is covered in the Generate a Document section below.

Variable naming

Variable names in your template must follow specific formatting rules. See the naming variables reference for correct and incorrect formats.

Step 4: Test Your Workflow #

  1. Click on Execute Workflow (the play button at the bottom)
  2. Wait a few seconds for the document to generate
  3. You should see the document details in the output
  4. The file is now available at the download_url provided

Step 5: Get the Downloaded File #

When Wait For Completion is enabled (the default), PDFMonkey automatically downloads the generated file once it is ready — no extra option to enable. The file is available as binary data (property data) in the next nodes.

Example: Webhook to Document to Email #

Let’s create a more practical workflow that:

  1. Receives data from a webhook
  2. Generates a document with PDFMonkey
  3. Sends the document by email

The Workflow Structure #

  1. Webhook node: receives form data
  2. PDFMonkey node: generates the document
  3. Send Email node: sends the file as attachment

Configuration #

Webhook node:

  • Set to POST method
  • Path: /generate-document

PDFMonkey node:

  • Operation: Generate Document
  • Select your template
  • Map webhook data to template variables:
    • name{{ $json.name }}
    • favoriteNumber{{ $json.favoriteNumber }}
  • Leave Wait For Completion enabled so the file downloads automatically

Send Email node:

  • Use Gmail, SendGrid, or any SMTP service
  • In attachments, reference the PDFMonkey binary data:
    • Property Name: data
    • File Name: document.pdf

Testing webhooks

Use the Test URL provided by n8n to send POST requests with JSON data like:

{
  "name": "John Doe",
  "favoriteNumber": 42
}

You’ve successfully set up your first PDFMonkey workflow in n8n.

Generate a Document #

The Generate Document operation creates a document from one of your PDFMonkey templates. You provide a template and a dynamic data payload, and PDFMonkey renders the file.

Payload Input Methods #

The PDFMonkey node offers two ways to send data to your template.

Key-Value Pairs (Simple Mode) is the easiest way to send simple data structures. You add fields one by one, each with a key (the variable name in your template) and a value (static or dynamic from previous nodes).

JSON Format (Advanced Mode) handles complex data structures, nested objects, or arrays. Use JSON when your template expects structured data:

{
  "customer": {
    "name": "Acme Inc.",
    "email": "contact@acme.com",
    "address": {
      "street": "123 Main St",
      "city": "New York",
      "zip": "10001"
    }
  },
  "items": [
    {
      "product": "Widget",
      "quantity": 5,
      "price": 10.99
    },
    {
      "product": "Gadget",
      "quantity": 2,
      "price": 24.99
    }
  ],
  "total": 104.93
}

In your template, access nested data with dot notation:

<h2>Invoice for {{customer.name}}</h2>
<p>{{customer.address.street}}, {{customer.address.city}}</p>

{% for item in items %}
  <p>{{item.product}} x{{item.quantity}} = {{item.price}}</p>
{% endfor %}

For more on structuring your dynamic data, see Defining Dynamic Data.

Using Expressions #

n8n expressions let you reference data from previous nodes dynamically:

{
  {
    $json.customerName;
  }
} // Simple field
{
  {
    $node["Webhook"].json["email"];
  }
} // From specific node
{
  {
    $json.items.length;
  }
} // Array length
{
  {
    new Date().toISOString();
  }
} // Current date

You can use expressions in Key-Value pair values, JSON payloads (as string values), the custom filename field, and metadata fields.

Custom Filename #

You can specify a custom name for the generated file. This is useful when storing files in Google Drive, Dropbox, or other storage services.

In the PDFMonkey node options:

  1. Expand Additional Options
  2. Set Custom Filename
  3. Use a static name or n8n expressions:
Invoice-{{ $json.invoiceNumber }}.pdf
Report-{{ $now.format('YYYY-MM-DD') }}.pdf
{{ $json.customerName }}-Contract.pdf

Filename restrictions

  • Do not use slashes / or backslashes \
  • Avoid special characters that might cause issues on different operating systems
  • Non-latin characters may be escaped or replaced

For more details on filename options, see Custom Filename.

Metadata #

Metadata is additional information attached to a document that does not appear in the generated file itself. It is useful for tracking document origin, storing reference IDs, and filtering in webhooks.

To add metadata:

  1. Expand Additional Options in the PDFMonkey node
  2. Set Metadata to Key-Value Pairs or JSON
  3. Add your metadata fields:
{
  "orderId": "ORD-12345",
  "customerEmail": "john@example.com",
  "environment": "production"
}

You can retrieve metadata later using the Get Document operation or in webhook triggers.

Metadata vs Payload

  • Payload (dynamic data): Data used to render the document content
  • Metadata: Information attached to the document but not visible in the output; included in webhook notifications

Wait for Document #

By default, the Generate Document operation waits for the document to be fully generated before continuing.

Document generation time

Most documents generate in a few seconds. Complex documents with many pages, images, or JavaScript may take longer. See Document Statuses for the full generation lifecycle.

5-minute timeout

If a document is still generating after 5 minutes, the node fails the item instead of waiting indefinitely. The document keeps generating on PDFMonkey’s side, so you can still retrieve it afterwards with Get Document or Download File.

If you need to disable waiting and handle completion asynchronously, you can use the PDFMonkey Trigger or poll for completion instead.

Arrays and Complex Data #

n8n makes it easy to work with arrays from previous nodes. Here is an example using Google Sheets data.

Google Sheets node output:

[
  { "product": "Widget", "qty": 5, "price": 10 },
  { "product": "Gadget", "qty": 2, "price": 25 }
]

In PDFMonkey node (JSON mode):

{
  "customerName": "{{ $json.customerName }}",
  "items": {{ $node["Google Sheets"].json }}
}

The array is passed directly to your template where you can loop over it:

{% for item in items %}
  <tr>
    <td>{{item.product}}</td>
    <td>{{item.qty}}</td>
    <td>{{item.price}}</td>
  </tr>
{% endfor %}

For more on loops and conditional rendering, see Conditions and Loops.

Handling Multiple Items #

If your workflow processes multiple items (like multiple customers or orders), use n8n’s Split In Batches node:

1. Get Data (returns 100 items)
2. Split In Batches (batch size: 1)
3. PDFMonkey - Generate Document (runs 100 times)
4. Store files

Or use the Loop Over Items node for more control.

Rate limits

Be mindful of your PDFMonkey plan limits when generating multiple documents. Consider adding Wait nodes between batches if generating many documents. See Rate Limiting for details, and Our Plans for quota information.

Error Handling #

If PDFMonkey reports a generation failure, the node fails that item instead of returning it — the workflow stops (or takes the node’s error branch, if you have one) rather than continuing with a file-less item.

Check status before assuming success

Checking {{ $json.status }} with an IF node after Generate Document no longer works for failures — the node throws before returning anything. To branch on status yourself, disable Wait For Completion and use Get Document or the PDFMonkey Trigger instead.

To keep the workflow running past a failed generation, enable Continue On Fail:

  1. Click on the PDFMonkey node
  2. Go to the Settings tab
  3. Toggle Continue On Fail on

With Continue On Fail enabled, a failed item continues with an error field instead of stopping the workflow:

{
  "error": "PDFMonkey could not generate document <id>: <failure_cause>"
}

PDFMonkey Trigger #

The PDFMonkey Trigger is a webhook-based trigger that listens for document generation events. When a document finishes generating, PDFMonkey sends a notification to your n8n workflow. This is useful for automating post-generation actions like storing files, sending notifications, or updating databases.

For background on how PDFMonkey webhooks work (including event types, payload format, and signature verification), see Webhooks.

Fires on failures too

The trigger fires whether the document succeeds or fails. Check the status field before assuming a file is attached — failed documents arrive with failure_cause set and no binary data (see Trigger Data).

Setup #

  1. In your n8n workflow, click + to add a new node
  2. Search for PDFMonkey Trigger and select it as your workflow’s starting point
  3. Select your PDFMonkey API credentials
  4. Choose a Workspace — the trigger listens for documents generated in that workspace
  5. Optionally, choose one or more Templates to narrow it down; leave empty to listen to every template in the workspace
  6. Click the Activate toggle to enable your workflow

That’s it — the node registers its own webhook with PDFMonkey when the workflow is activated (and while you’re listening for a test event), and removes it when deactivated. There is nothing to configure in the PDFMonkey dashboard.

Upgrading from an older node version

Older versions of this node required creating the webhook by hand in the PDFMonkey dashboard’s Webhooks section. If you have one configured for pdfmonkey/webhook, delete it — the trigger now registers its own, and leaving the old one active means every document is delivered twice.

Trigger Data #

When a document finishes generating, the trigger receives a DocumentCard object. This includes document metadata, status, and download URL, but does not include the original dynamic data you sent:

{
  "document": {
    "id": "a5e86d72-f5b7-43d4-a04e-8b7e08e6741c",
    "app_id": "d6b4e8f2-7a3c-4d1e-9f5b-2c8a1d3e6f90",
    "created_at": "2050-03-13T12:34:56.181+02:00",
    "document_template_id": "2903f5b4-623b-4e10-b2e3-dc7e2e67ea39",
    "document_template_identifier": "My Invoice Template",
    "download_url": "https://pdfmonkey.s3.eu-west-1.amazonaws.com/...",
    "failure_cause": null,
    "filename": "2050-03-14 Peter Parker.pdf",
    "meta": {
      "_filename": "2050-03-14 Peter Parker.pdf",
      "clientRef": "spidey-616"
    },
    "output_type": "pdf",
    "preview_url": "https://preview.pdfmonkey.io/...",
    "public_share_link": null,
    "status": "success",
    "updated_at": "2050-03-13T12:34:59.412+02:00"
  }
}

Dynamic data is not included

The webhook payload does not include the original dynamic data (payload) you sent to generate the document. If you need this information in your workflow, store it in the metadata field when generating the document. Metadata is included in every webhook notification.

Auto-Download #

When a document generates successfully, the PDFMonkey Trigger automatically downloads it as binary data — no option to enable, no separate Download File operation needed. It is available immediately in the next nodes under the binary key data.

On failure, no binary data is attached; check status and failure_cause in the JSON output instead (see Trigger Data).

Use Cases #

Store files in Google Drive:

1. PDFMonkey Trigger (Download File: enabled)
2. Google Drive - Upload File
   - File: {{ $binary.data }}
   - Folder: /Invoices/
   - Filename: {{ $json.filename }}

Send notification to Slack:

1. PDFMonkey Trigger
2. Slack - Send Message
   - Channel: #notifications
   - Message: "New document generated: {{ $json.filename }}"
   - Attachment: {{ $json.download_url }}

Update database record:

1. PDFMonkey Trigger
2. Postgres - Update
   - Table: orders
   - Where: id = {{ $json.metadata.orderId }}
   - Set: pdf_url = {{ $json.download_url }}, status = 'completed'

Email document to customer:

1. PDFMonkey Trigger (Download File: enabled)
2. Gmail - Send Email
   - To: {{ $json.metadata.customerEmail }}
   - Subject: Your invoice is ready
   - Attachments: {{ $binary.data }}

Metadata for Routing #

Metadata enables conditional workflows. When generating a document, attach routing information:

{
  "metadata": {
    "documentType": "invoice",
    "customerId": "12345",
    "sendEmail": true
  }
}

Then in your triggered workflow, use an IF node or Switch node to route based on metadata:

1. PDFMonkey Trigger
2. Switch node
   - Case 1: {{ $json.metadata.documentType }} = "invoice" -> Store in accounting
   - Case 2: {{ $json.metadata.documentType }} = "report" -> Share on Slack
   - Case 3: {{ $json.metadata.documentType }} = "contract" -> Store in DocuSign

Testing #

Method 1: Generate a test document. Use another workflow or the API to generate a document, wait for the webhook to fire, and check the trigger node execution in n8n.

Method 2: Use the test webhook.

  1. Click Listen for event in the trigger node
  2. Generate a document from your PDFMonkey template
  3. The trigger catches the event and displays the data
  4. Click Use test event to use this data while building your workflow

Trigger from the dashboard

You can manually trigger a test generation from your PDFMonkey template editor by clicking the Generate button. This fires the webhook immediately.

You can also have multiple n8n workflows listening to the same template. Each webhook receives the notification independently, which is useful for separating environments or having different workflows for different document types.

Delivery Guarantees #

PDFMonkey uses Svix to deliver webhooks. Deliveries follow these guarantees:

  • At-least-once delivery: You might receive the same event multiple times
  • Best-effort ordering: Events are usually in order but not guaranteed
  • Automatic retries: Failed deliveries are retried with exponential backoff
  • Signature verification: Every webhook is signed so you can verify authenticity

Make your workflow idempotent

Since webhooks might be delivered multiple times, design your workflow to handle duplicate events gracefully. Use document IDs to check if you have already processed an event.

For full details on webhook configuration (including channel routing and signature verification), see Webhooks.

Other Operations #

Beyond generating documents and reacting to triggers, the PDFMonkey node supports several operations for managing documents.

Get Document #

The Get Document operation retrieves information about a previously generated document. Use it to check a document’s status, retrieve its download URL, access attached metadata, or verify a document exists before processing.

Required field: Document ID

Document ID: {{ $json.documentId }}

The operation returns complete document information including status, download URL, preview URL, metadata, and timestamps.

Document status values:

  • draft — document is created but not yet queued for generation
  • pending — document is queued for generation
  • generating — document is currently being rendered
  • success — document generated successfully
  • failure — generation failed (check failure_cause for details)

For the full status lifecycle, see Document Statuses.

Download File #

The Download File operation downloads a generated file as binary data. Use it to download a file that was generated earlier, get binary data separately from generation, or re-download a document for processing.

Required field: Document ID

Optional field: Binary Property Name (default: data)

Document ID: {{ $json.documentId }}
Binary Property Name: pdfFile

The file is stored as binary data and can be used in email attachments, file storage nodes (Google Drive, Dropbox, etc.), FTP uploads, or further processing nodes.

Generate Document has built-in download

If you are generating and immediately downloading, use the Download File option in the Generate Document operation instead of a separate Download File node.

Delete Document #

The Delete Document operation permanently removes a document from PDFMonkey. Use it to clean up after sending a document, remove documents containing sensitive data, manage storage quotas, or implement document retention policies.

Required field: Document ID

Document ID: {{ $json.documentId }}

Deletion is permanent

Once deleted, documents cannot be recovered. The download URL stops working. Make sure you have saved the file elsewhere if you need it.

TTL as alternative

Consider using automatic deletion (TTL) instead of manually deleting documents. You can set documents to auto-delete after a configurable period when generating them.

Polling for Completion #

If you generated a document with Wait for Document disabled, you can poll for its completion instead of using a webhook trigger:

1. PDFMonkey - Generate Document (Wait: disabled)
2. Wait (10 seconds)
3. PDFMonkey - Get Document
   - Document ID: {{ $node["PDFMonkey"].json.id }}
4. IF node
   - {{ $json.status }} = "success"
   - TRUE: Continue with the file
   - FALSE: Loop back to step 2

Better approach: Use the trigger

Instead of polling, consider using the PDFMonkey Trigger to be notified when documents are ready. Triggers are more efficient and respond faster.

Combining Operations #

Generate, store, and clean up:

1. Manual Trigger
2. PDFMonkey - Generate Document (Download: enabled)
3. Google Drive - Upload File
   - File: {{ $binary.data }}
   - Folder: /Invoices/
4. Postgres - Insert
   - Table: documents
   - Data: {
       drive_url: {{ $node["Google Drive"].json.webViewLink }},
       pdfmonkey_id: {{ $node["PDFMonkey"].json.id }}
     }
5. PDFMonkey - Delete Document
   - Document ID: {{ $node["PDFMonkey"].json.id }}

Conditional download based on status:

1. Webhook (receives document ID)
2. PDFMonkey - Get Document
   - Document ID: {{ $json.documentId }}
3. IF node
   - Condition: {{ $json.status }} = "success"
   - TRUE:
     4a. PDFMonkey - Download File
     5a. Send to customer
   - FALSE:
     4b. Send error notification
     5b. PDFMonkey - Delete Document (cleanup failed attempt)

Re-generate and replace:

1. Webhook (receives order update)
2. PDFMonkey - Delete Document
   - Document ID: {{ $json.oldDocumentId }}
3. PDFMonkey - Generate Document
   - Template: Invoice
   - Payload: {{ $json.updatedData }}
4. Update database with new document ID

Troubleshooting #

Authentication Errors #

Invalid API key:

401 Unauthorized - Invalid API key
  1. Go to your PDFMonkey dashboard
  2. Navigate to Account then API
  3. Copy your API key
  4. In n8n, update your PDFMonkey credentials with the correct key
  5. Test the connection

For more on API authentication, see Authentication.

Multiple workspaces

If you have multiple PDFMonkey workspaces, make sure you are using the API key from the correct workspace.

If your credentials suddenly stop working, delete the old credentials in n8n, create new ones with a fresh API key, and update all nodes using those credentials.

Template Not Found #

404 Not Found - Template does not exist

Causes: the template was deleted, the wrong workspace or API key is in use, or the template ID is incorrect.

Solution: Verify the template exists in your PDFMonkey dashboard, check you are using the correct API key, and re-select the template from the dropdown in n8n.

Template not published:

422 Unprocessable Entity - Template is not published

Go to your template in PDFMonkey and make sure it is published by clicking the Publish button, then try generating again.

Common mistake

This is one of the most frequent errors. Always remember to publish your template after making changes.

Invalid Payload #

422 Unprocessable Entity - Invalid payload

Causes: invalid JSON syntax (when using JSON mode), wrong data types, or missing required fields.

Common JSON syntax issues:

Missing quotes:

// Wrong
{ name: "John" }

// Correct
{ "name": "John" }

Trailing commas:

// Wrong
{
  "name": "John",
  "age": 30,
}

// Correct
{
  "name": "John",
  "age": 30
}

Unescaped double quotes in values:

// Wrong - nested quotes break the JSON
{ "name": "Peter "Spider-Man" Parker" }

// Correct - escape inner quotes with backslash
{ "name": "Peter \"Spider-Man\" Parker" }

Using n8n expressions in JSON:

When using expressions, make sure to stringify objects:

// Wrong - Will create invalid JSON
{
  "items": {{ $json.items }}
}

// Correct - Properly stringify
{
  "items": {{ JSON.stringify($json.items) }}
}

Use Key-Value Pairs mode

If you are struggling with JSON syntax, switch to Key-Value Pairs mode. n8n handles all the escaping and formatting automatically.

Download Errors #

Download URL expired:

403 Forbidden - Download URL has expired

Download URLs expire after 1 hour for security reasons.

Option 1: Use Get Document to get a fresh URL, then download from the new URL with an HTTP Request node.

Option 2: Enable the Download File option in the Generate Document node to download immediately after generation.

Option 3: Store the binary data in a file storage service (Google Drive, S3, etc.) instead of relying on PDFMonkey URLs.

For more details, see Download URL Returns 403.

Binary data not available:

If no binary data is available for attachment or storage, make sure Download File is enabled:

  • In Generate Document operation: Additional Options then Download File
  • In PDFMonkey Trigger: Additional Options then Download File
  • Or use a Download File operation explicitly

Webhook Issues #

Webhook not receiving events checklist:

  1. Workflow is activated (not just saved)
  2. Webhook URL is correct in PDFMonkey
  3. Using Production URL not Test URL
  4. Template is published
  5. Firewall is not blocking requests

To debug: check the Executions tab in n8n for errors, go to template settings in the PDFMonkey dashboard and check webhook delivery logs, and try generating a test document manually. See Webhooks for webhook configuration details.

Receiving duplicate webhook events is expected behavior. PDFMonkey webhooks use at-least-once delivery. Make your workflow idempotent by checking if you have already processed a document:

1. PDFMonkey Trigger
2. Postgres - Check if document ID exists
3. IF node
   - Already processed: Stop
   - New document: Continue processing

Wrong webhook URL: After activating your workflow, the webhook URL changes from Test to Production. Copy the Production URL from the trigger node and update the webhook URL in your PDFMonkey settings.

Expression Errors #

Cannot read property of undefined:

Cannot read property 'field' of undefined

This happens when trying to access data that does not exist from a previous node.

Check the node name:

// Wrong node name
{
  {
    $node["PDFmonkey"].json.id;
  }
}

// Correct node name (capital M)
{
  {
    $node["PDFMonkey"].json.id;
  }
}

Check the data exists:

// Assumes field exists
{
  {
    $json.customer.email;
  }
}

// Use optional chaining (n8n 1.0+)
{
  {
    $json.customer?.email;
  }
}

// Or provide default value
{
  {
    $json.customer?.email || "no-email@example.com";
  }
}

Use the expression editor in n8n (click the Expression button) and use the autocomplete to ensure you are referencing the correct fields.

Workflow Timeout #

Workflow execution timed out

Causes: document taking too long to generate, network issues, or large file downloads.

Option 1: Increase the timeout in workflow settings (Execution Timeout).

Option 2: Disable “Wait for Document” and use a webhook trigger in a separate workflow.

Option 3: Split into multiple workflows and use webhooks to chain them together.

Memory Errors #

JavaScript heap out of memory

This happens when processing very large files or data in n8n.

Solutions:

  1. Process items in smaller batches (use the Split In Batches node)
  2. Use streaming where possible
  3. Increase n8n memory allocation (self-hosted only)
  4. Store large files externally instead of passing them through the workflow

Rate Limiting #

429 Too Many Requests - Rate limit exceeded

Add Wait nodes between operations when processing multiple documents:

1. Get items to process
2. Split In Batches (size: 10)
3. Wait (5 seconds)
4. PDFMonkey - Generate Document
5. Loop

Or use the Code node to add delays:

// Wait 1 second per item
await new Promise((resolve) => setTimeout(resolve, 1000));
return items;

JSON Formatting #

Line breaks in JSON: JSON does not support raw line breaks in strings:

// Wrong
{
  "description": "Line 1
Line 2"
}

// Correct
{
  "description": "Line 1\nLine 2"
}

Or use a Code node to escape:

items[0].json.description = items[0].json.description.replace(/\n/g, "\\n");
return items;

Special characters: Escape double quotes inside string values:

// Wrong - unescaped quotes break JSON
{ "html": "<div class=\"test\">Hello</div>" }

// Correct - escaped inner quotes
{ "html": "<div class=\"test\">Hello</div>" }

Or use Key-Value Pairs mode which handles escaping automatically.

Next Steps #

Need More Help? #

If you are still experiencing issues:

  1. n8n documentation: docs.n8n.io
  2. PDFMonkey status: status.pdfmonkey.io
  3. n8n Community: community.n8n.io
  4. PDFMonkey support: support@pdfmonkey.io

Include details in support requests

When asking for help, include the full error message, your n8n version, a workflow screenshot, and the steps to reproduce.