# PDFMonkey Documentation > Generate PDF documents from HTML templates via API --- # From zero to generating your first Document Source: https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md ## Create an account on PDFMonkey To create an account, go to [the Register page](https://dashboard.pdfmonkey.io/register) and fill in your email and password. > [!NOTE] > We do not ask for a password confirmation but you can toggle password hiding by clicking on _Show my password_. Once you've filled in your credentials, we will send you an activation email. Click the link in the email to activate your account. **You will not be able to sign in until your account is confirmed.** ## Sign in Once your account confirmed, fill in your credentials in [the Sign in page](https://dashboard.pdfmonkey.io/login) to get access to your dashboard. ## Create your first Template To create a template, head to [the Templates page](https://dashboard.pdfmonkey.io/templates) and click on **Create my first Template**. Give a name to your template, select the **Code** edition mode and select the _Base Template_ named **Blank**. It will open the Template Editor, where PDFMonkey's magic happens. > [!NOTE] > You can change the name of your Template as often as you want, it will have no impact on your client applications. ## Writing your first Template In the **HTML** tab, insert the following code: ```liquid

Hello {{name}}!

``` In the **CSS** tab, insert the following code: ```css p { color: #6D28D9; font-size: 24px; } ``` And finally in the **Test data** tab, insert this: ```json { "name": "Peter Parker" } ``` You can now click on the **Save** button, and you should see the preview panel on the right update to reflect your changes. > [!NOTE] > **Real PDF preview** > > The PDF preview you see is actually a 100% accurate version of what the final result will be as it is a real PDF, generated using the same process we use to generate your Documents. The last step is to make your Template available for generation. To do so, click on the **Publish** button. For more on template syntax, see the [Code Templates](https://pdfmonkey.io/docs/code-templates) section. Learn about [using dynamic data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md) to preview your templates. ## Generating your first Document Now that your Template is complete, let's head to [the Documents page](https://dashboard.pdfmonkey.io/documents). Once there, click on the **Create my first Document** button. In the next page, select your template from the dropdown list presented to you and click on **Create a draft**. It will open the Document Editor, letting you specify data and meta-data for your Document. By default you will be shown an editable copy of the test data you've added to your Template. Let's update it to the following: ```json { "name": "Spider-man" } ``` Now head to the **Meta data** tab and insert the following to [customise the PDF filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md): ```json { "_filename": "hello-spider-man.pdf" } ``` Click on the **Save** button. The preview will refresh, showing you an up-to-date version of the PDF. Finally click on **Generate**. That's it! You generated your very first Document with PDFMonkey! If something goes wrong, check the [Troubleshooting](https://pdfmonkey.io/docs/troubleshooting) section. --- # Dashboard UI Tour Source: https://pdfmonkey.io/docs/getting-started/dashboard-ui-tour.md This page gives you a quick tour of the main areas of the PDFMonkey dashboard. Each section links to more detailed documentation so you can dive deeper when you are ready. ## Templates ![Templates list page showing template cards and workspace selector](https://pdfmonkey.io/docs/img/dashboard-templates-list.webp) The Templates page is the first thing you see after signing in. It lists every template in your current workspace, displayed as cards showing the template name, its status (draft or published), and when it was last modified. From this page you can: - **Create a new template** using the _Create Template_ button at the top of the page. - **Duplicate or delete** a template from its context menu. To learn how to build a template from scratch, see [Your First Template (Builder)](https://pdfmonkey.io/docs/builder-templates/your-first-template.md) or the [Code Templates](https://pdfmonkey.io/docs/code-templates) section. ## Template Editor ![Template editor with HTML, CSS, and Test Data tabs alongside the live preview pane](https://pdfmonkey.io/docs/img/dashboard-template-editor.webp) The template editor is where you design your PDF layout and preview the result in real time. PDFMonkey offers two editing modes: - **Code editor** -- Write HTML, CSS, and Liquid markup directly. The editor has three tabs: _HTML_, _CSS_, and _Test Data_. Changes are reflected in the preview pane on the right after you save. - **Builder** -- A drag-and-drop interface that lets you assemble pages from pre-built blocks without writing code. The preview pane renders a real PDF using the same engine that produces your final documents, so what you see is exactly what your users will receive. When your template is ready, click **Publish** to make it available for document generation. Only published templates can be used to create documents through the API or integrations. For a hands-on walkthrough, see [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). ## Documents ![Documents page listing generated documents with status indicators and filters](https://pdfmonkey.io/docs/img/dashboard-documents.webp) The Documents page shows every document that has been generated in your workspace. Each entry displays the document name, the template it was created from, its current status, and when it was generated. Documents go through several statuses during their lifecycle: - **Draft** -- Created but not yet sent for generation. - **Pending** -- Queued for generation. - **Generating** -- Currently being rendered. - **Success** -- Generation complete; the PDF is ready to download. - **Failure** -- Something went wrong during generation. You can filter the list by template or by status to find specific documents quickly. The **Create Document** button lets you generate a document manually by selecting a template and providing JSON data. For a detailed breakdown of each status, see [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md). ## Settings ![Settings page showing workspace name and workspace ID](https://pdfmonkey.io/docs/img/dashboard-settings.webp) The Settings page lets you manage your workspace identity. From this page you can: - **Rename your workspace** -- Edit the workspace name and click _Save_ to apply the change. - **Copy your Workspace ID** -- The unique identifier for your workspace, useful when contacting support or working with the API. ## FAQ **What are the main sections of the PDFMonkey dashboard?** The dashboard has four main areas: Templates (listing all templates in your workspace), the Template Editor (for designing PDF layouts with code or the visual builder), Documents (showing generated documents and their statuses), and Settings (for managing workspace identity). **What document statuses exist in PDFMonkey?** Documents go through five statuses: Draft (created but not queued), Pending (queued for generation), Generating (currently rendering), Success (PDF ready to download), and Failure (something went wrong during generation). **Does the PDFMonkey template editor show a real preview?** Yes. The preview pane renders a real PDF using the same engine that produces your final documents, so what you see in the editor is exactly what your users will receive. --- # PDFMonkey 30-Day Pro Trial: Features, Limits, and What Happens Next Source: https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md Every new PDFMonkey account starts with a **30-day Pro trial** so you can explore premium features before choosing a plan. During the trial you have access to the same capabilities as the [Pro plan](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md#pro), with no credit card required. ## What the trial includes {#what-the-trial-includes} | Feature | Trial (Pro) | Free plan (after trial) | |:--------|:-----------:|:-----------------------:| | Document generations | 300/month | 20/month | | [External resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) (images, fonts, CSS, JS via URL) | Yes | No | | Max generation time per document | 2 minutes | 30 seconds | | [Document retention](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) | 24 hours | 24 hours | > [!NOTE] > **No payment required** > > The trial activates automatically when you [create your account](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). You are never charged during the trial, and you do not need to cancel anything. ## What changes when the trial ends? {#what-changes-when-the-trial-ends} When the 30-day trial expires, your account **downgrades to the Free plan** automatically. You are not charged. The main differences you will notice: - **External resources stop working.** Images, CSS, JavaScript, and fonts loaded via URL are restricted on the Free plan. Documents that reference them will fail at generation time. See [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) for the full definition of what counts. - **Your generation quota drops to 20 documents per month** instead of 300. - **Max generation time drops to 30 seconds** instead of 2 minutes. > [!TIP] > **Upgrade before the trial ends** > > If the trial features match your needs, [upgrade to a paid plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) any time during or after the trial. Changes take effect immediately and billing is prorated. See [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) for a full comparison. ## What you can use on the Free plan {#what-can-i-use-on-the-free-plan} After the trial, the following resources continue working on the Free plan because they do not count as external resources: - **Images** loaded using [data-uri or inline SVG](https://pdfmonkey.io/docs/code-templates/images.md) - **CSS** written directly in the template's HTML or CSS tab - **CSS** sent in the document's dynamic data and inlined in the HTML using a variable - **JavaScript** written directly in the template's HTML tab - **JavaScript** stored in the document's dynamic data and inlined in the HTML using a variable - **Hosted Tailwind CSS** versions that PDFMonkey provides (see [Hosted Tailwind CSS Versions](https://pdfmonkey.io/docs/code-templates/custom-css.md#hosted-tailwind-css-versions)) For more detail on what qualifies as an external resource and what does not, see the dedicated [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) page. ## Frequently asked questions {#faq} **Can I extend the trial?** The trial is a one-time, 30-day window per account. That said, we understand that sometimes you may have missed the trial entirely or need more time to evaluate the product. If that is your situation, [contact our support team](https://pdfmonkey.io/docs/getting-started/support.md) and we will see what we can do. **What happens to my documents when the trial ends?** Your existing documents are not deleted. They remain accessible according to the [retention settings](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) on your templates. Only new generation requests are affected by the Free plan limits. **Can I upgrade before the trial ends?** Yes. You can [switch to any paid plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) at any point during the trial. The change takes effect immediately. **Do unused trial documents roll over?** No. Your monthly quota resets at the start of each billing cycle. Unused documents do not carry over. See [Reaching Your Quota](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md#reaching-your-quota) for details. ## Related pages {#related-pages} - [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md): get started with your first template and document - [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md): what counts as an external resource and free-plan alternatives - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md): compare all plan tiers side by side - [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md): how upgrades, downgrades, and proration work - [Document Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md): TTL settings and plan-based retention limits ## FAQ **What does the PDFMonkey 30-day trial include?** The trial gives you Pro-level access for 30 days: 300 document generations per month, external resources (images, fonts, CSS, and JS via URL), and a 2-minute maximum generation time per document. **What happens when the PDFMonkey trial ends?** Your account automatically downgrades to the Free plan at no charge. External resources stop working, your generation quota drops to 20 documents per month, and the maximum generation time drops to 30 seconds. **Do I need a credit card for the PDFMonkey trial?** No. The trial activates automatically when you create your account. You are never charged during the trial and you do not need to cancel anything. **Can I upgrade during the PDFMonkey trial?** Yes. You can switch to any paid plan at any point during the trial. The change takes effect immediately and billing is prorated. --- # External Resources in PDFMonkey Source: https://pdfmonkey.io/docs/getting-started/external-resources.md # External Resources PDFMonkey templates can load assets from external URLs -- images, stylesheets, scripts, and fonts. These are called **external resources**, and their availability depends on your plan. This page explains what qualifies, how to tell if your template uses one, and what to do if you are on the Free plan. ## What Counts as an External Resource? An external resource is any file your template loads via a URL (`http://...` or `https://...`). This includes: * **Remote images** -- loaded from a server or CDN ([data-uri and inline SVG](https://pdfmonkey.io/docs/code-templates/images.md) still work on all plans) * **CSS files** -- stylesheets hosted on your servers or a CDN * **JavaScript files** -- scripts hosted on your servers or a CDN * **Web fonts** -- like Google Fonts or Bunny Fonts * **Visual Editor fonts** -- any font selected in the builder's font-family picker ### How to tell if your template uses external resources Look for URLs in your template code. Anything that starts with `http://` or `https://` is an external resource: ```html ``` ```css /* Also external */ @import url("https://fonts.googleapis.com/css2?family=Inter&display=swap"); ``` Resources written directly in your template are **not** external resources and work on every plan: ```html ... ``` ## Plan Availability External resources are available on **all paid plans** and during the **30-day Pro trial**. On the Free plan, documents that reference external resources will **fail at generation time**. For a full breakdown of what each plan includes, see [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md). For details on what the trial unlocks, see [30-day Pro trial](https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md). > [!TIP] > **Free-plan alternatives** > > You can still build full-featured templates on the Free plan using inline CSS, inline JavaScript, data-uri images, and inline SVG. See [What you can use on the Free plan](https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md#what-can-i-use-on-the-free-plan) for the full list. ## PDFMonkey-Hosted Resources Resources hosted on PDFMonkey's own servers (`pdfmonkey-resources.s3-eu-west-3.amazonaws.com`) are the exception: they work on **all plans**, including Free. The most common example is the hosted Tailwind CSS builds. See [Hosted Tailwind CSS Versions](https://pdfmonkey.io/docs/code-templates/custom-css.md#hosted-tailwind-css-versions) for the full list of available versions. ## FAQ **Can I use web fonts in PDFMonkey templates?** Yes, but only on paid plans (Starter and above). Web fonts loaded via URL (e.g., Google Fonts) count as external resources. On the Free plan, use system fonts or embed fonts as Base64 data URIs instead. **What counts as an external resource in PDFMonkey?** Any file your template loads via a URL (http:// or https://) is an external resource. This includes remote images, CSS files, JavaScript files, web fonts, and fonts selected in the Builder's font picker. Inline code and data URIs are not external resources and work on all plans. **Why are my images or fonts not loading in PDFMonkey?** If you are on the Free plan, external resources (remote images, web fonts, external CSS/JS) are not available. Upgrade to Starter or higher, or switch to inline alternatives: Base64 data URIs for images, inline SVGs, or system fonts. --- # Builder vs. Code Templates Source: https://pdfmonkey.io/docs/getting-started/builder-vs-code-templates.md PDFMonkey offers two ways to create templates: the visual **[Builder](https://pdfmonkey.io/docs/builder-templates)** and **[Code Templates](https://pdfmonkey.io/docs/code-templates)** written in HTML, CSS, and Liquid. Both produce the same high-quality PDFs. The difference is how you design them, and you'll want to pick the right approach before you start. ## Comparison at a glance {#builder-vs-code-templates} | | Builder | Code Templates | |---|---|---| | **Design method** | Visual drag-and-drop | Write HTML, CSS, and Liquid | | **Dynamic data** | Logic panel (conditions, repetition) | Liquid tags (`{% if %}`, `{% for %}`) | | **Styling** | Styles panel + Custom CSS editor | Full CSS control in the template | | **JavaScript** | Custom JS editor + External Assets | ` ``` You can add as many script tags as needed, anywhere in your HTML. ## Accessing Document Data To make JavaScript more powerful in your templates, you can access your Document payload as a JavaScript object. Start by enabling JavaScript injection for your template in its Settings tab: ![](https://pdfmonkey.io/docs/img/template-settings-js-injection.webp) Once enabled, a `$docPayload` object becomes available in your scripts. For example, given this payload: ```json title="JSON" { "movie": { "title": "12 Monkeys", "year": 1995 } } ``` You can access the data like this: ```html title="HTML"
``` > [!WARNING] > **Data access limitation with JavaScript** > > When accessing data with JS, **you cannot insert it** using the `{{movieTitle}}` syntax. This syntax is specific to [Liquid](https://pdfmonkey.io/docs/code-templates/the-liquid-syntax.md) and is executed before your JS code runs. > > That said, you can generate JS code using Liquid: > > ```html > <script> > const movieTitle = "{{movieTitle | upcase}}"; > </script> > ``` ## External Libraries > [!WARNING] > **Paid account only** > > Including JavaScript based on their URL is only possible on a paid account. Documents including external JavaScript files will fail on a free account. See [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) for the full list. If you need a library that transforms text, applies filters to images, or [inserts charts](#including-charts) in your Document, you can import it like you would in a normal HTML page. For example, to render a Markdown string from your payload using [marked.js](https://marked.js.org/): ```html title="HTML"
``` Any library loadable via CDN can be used in your templates this way. ## Formatting Dates and Times PDFMonkey's engine runs on AWS servers located in Europe. The timezone of the server might not be the one you want your dates displayed in. > [!TIP] > **Use Liquid first** > > Most date formatting needs are covered by Liquid filters alone, no JavaScript required. Use the [in\_time\_zone](https://pdfmonkey.io/docs/code-templates/filters.md#in_time_zone) filter to set a timezone and the [date](https://pdfmonkey.io/docs/code-templates/filters.md#date) filter to format a date. If Liquid filters don't cover your needs, JavaScript offers additional options. ### toLocaleString(locales, options) The [toLocaleString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString) method returns a string with a language-sensitive representation of a date. The `locales` and `options` arguments let you specify the language whose formatting conventions should be used and customize the behavior of the function. ```html title="HTML" ``` You can learn more about the toLocaleString method in its [MDN documentation page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString). ### Using a Library > [!WARNING] > **Paid account only** > > Using an external JS library will only work on a paid account. On a free account preview and generation will be blocked. If you need the help of a more powerful library, you can load the one you need. The most frequently used one is MomentJS but we recommend [Luxon](https://moment.github.io/luxon/#/?id=luxon), a more recent iteration of the same idea by the actual author of MomentJS: ```html title="HTML" ``` ## Including Charts > [!WARNING] > **Paid account only** > > Including JavaScript based on their URL is only possible on a paid account. Documents including external JavaScript files will fail on a free account. See [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) for the full list. You can use most charting libraries to generate charts in PDFMonkey. From our experience, [Chart.js](https://www.chartjs.org/) is the most reliable option ---it renders consistently across PDF readers and offers a wide variety of chart types. Two settings are essential for charts in PDFs: - **`animation: false`** ---Animations don't play in a PDF. If left enabled, the chart may render mid-transition, producing a blank or half-drawn graphic. - **`responsive: false`** ---Disabling this lets you control the chart size through the `canvas` element's `width` and `height` attributes, avoiding layout surprises. ### Chart.js Example The example below loads Chart.js from a CDN and renders a bar chart using data from the Document payload. It includes both critical settings mentioned above. Given this payload: ```json title="Payload" { "chart": { "labels": ["Jan", "Feb", "Mar", "Apr", "May"], "values": [120, 190, 150, 210, 175] } } ``` Add this to your template's HTML: ```html title="HTML" ``` ![Example bar chart rendered with Chart.js](https://pdfmonkey.io/docs/img/chart-example.webp) You can adapt this example to any [Chart.js chart type](https://www.chartjs.org/docs/latest/charts/) (line, pie, doughnut, radar, etc.) by changing the `type` value and adjusting the `data` structure. > [!WARNING] > **ApexCharts compatibility** > > [ApexCharts.js](https://apexcharts.com/) is known to produce black borders around charts in certain PDF readers, particularly on Windows and Android. If you use ApexCharts, test your output across multiple devices before going to production. ## Debugging If your JavaScript is not behaving as expected, use the **Debug** button at the top of the template editor. It opens the HTML version of your template, generated from your test data, directly in your browser. ![](https://pdfmonkey.io/docs/img/template-debug-button.webp) This gives you a chance to inspect JS errors in the console and should help in understanding what is going wrong. ## Browser Compatibility Our current PDF engine (v5) is based on **Chrome 133**. You can check if a specific JS feature is available using the [caniuse comparison table](https://caniuse.com/?compare=chrome+133&compareCats=JS,JS%20API). > [!NOTE] > If your templates use an older engine version, check [Our Engines](https://pdfmonkey.io/docs/code-templates/our-engines.md) to see which Chrome version applies to your engine. ## FAQ **Can I use JavaScript in PDFMonkey templates?** Yes. Add script tags directly in the HTML tab of the template editor. Enable JavaScript injection in the template settings to access your document payload via the $docPayload object. **What JavaScript libraries are available in PDFMonkey?** Chart.js for charts and graphs, and Day.js for date formatting are built into the rendering engine. On paid plans, you can also load external JavaScript libraries from a CDN. **How do I access document data from JavaScript in PDFMonkey?** Enable JavaScript injection in the template settings. A $docPayload object then becomes available in your scripts, containing the full JSON payload you passed when creating the document. --- # Custom CSS for Code Templates Source: https://pdfmonkey.io/docs/code-templates/custom-css.md The template editor provides a dedicated CSS tab where you can write any standard CSS you need. Your styles apply to the HTML elements you insert in the HTML tab during rendering. If you want to know whether a specific CSS feature is available, you can check [this compatibility list for Chrome 133](https://caniuse.com/?compare=chrome+133&compareCats=CSS), the version used by our current engine (v5). Chrome 133 supports virtually all modern CSS features, including **Flexbox**, **CSS Grid**, **Custom properties**, **Container queries**, and the **`:has()` pseudo-class**. > [!NOTE] > If your templates use an older engine version, check [Our Engines](https://pdfmonkey.io/docs/code-templates/our-engines.md) to see which Chrome version applies to your engine. > [!TIP] > **Looking for page layout?** > > For page sizing, margins, single-page layouts, full-page backgrounds, and page breaks, see [Page Layout](https://pdfmonkey.io/docs/code-templates/page-layout.md). ## External CSS Libraries Writing your own CSS is always an option, but a utility-first CSS library can speed up template development significantly. Frameworks like Bootstrap tend to enforce strong opinions about print styles and can interfere with your layout. We recommend lighter-touch tools like [Tailwind CSS](https://tailwindcss.com/) or [Tachyons](https://tachyons.io/) instead. > [!WARNING] > **Paid account only (mostly)** > > Loading CSS from a URL requires a paid account. See [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) for the full definition and plan availability. > > That said, **an exception is made for CSS hosted on our own servers** — see [Hosted Tailwind CSS Versions](#hosted-tailwind-css-versions) below. ## Hosted Tailwind CSS Versions {#hosted-tailwind-css-versions} PDFMonkey provides Master Templates, and we made hosted versions of the resources used to build them available to everyone. Because these files are hosted on our servers, they work on **both Free and paid accounts** — no external resource restriction applies. ### Tailwind CSS v4.x and v3.x (JIT) These versions work just like the one available using the [Tailwind CSS CDN](https://tailwindcss.com/docs/installation/play-cdn). Include the script in your HTML tab, replacing the official CDN link with ours: ```html title="HTML" ``` Available versions: * [tailwindcss-4.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-4.js) (latest, currently 4.1.17) * [tailwindcss-4.1.17.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-4.1.17.js) * [tailwindcss-4.0.1.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-4.0.1.js) * [tailwindcss-3.4.3.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-3.4.3.js) * [tailwindcss-3.3.3.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-3.3.3.js) * [tailwindcss-3.2.6.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-3.2.6.js) * [tailwindcss-3.2.0.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-3.2.0.js) * [tailwindcss-3.0.23.js](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/js/tailwindcss-3.0.23.js) For v3.x versions, you can customize the configuration just like you would with the official CDN: ```html title="HTML" ``` ### Tailwind CSS v2.0.3 (Legacy) We provide a light version of Tailwind CSS v2.0.3, stripped of any style that does not make sense in a printing context, like animations or responsive utilities. It weighs 165 KB. You can import it from your CSS tab using: ```css title="CSS" @import url("https://pdfmonkey-resources.s3.eu-west-3.amazonaws.com/css/tailwind-light.min.css"); ``` You can also import it from the HTML tab but know that your styles will be defined before, and it can make it harder to override Tailwind's classes. ## Per-Document Styles {#the-css-custom-properties-technique} Code you write in the CSS tab cannot call variables provided in the Document payload. That said, you can use CSS custom properties (a.k.a CSS variables) to pass dynamic values to your CSS on a per-document basis. For example, if you want to pass a color in your data: ```json title="JSON payload" { "color": "#db2777" } ``` You can create a custom property that takes its value and makes it available to your CSS: ```html title="HTML" ``` ```css title="CSS" h1 { color: var(--color); } ``` This technique works for any CSS value: colors, font sizes, spacing, or even font families. ## FAQ **Can I use Tailwind CSS in PDFMonkey templates?** Yes. PDFMonkey hosts several Tailwind CSS versions (v2 through v4) on its own servers, so they work on both free and paid plans. Include the script tag in your HTML tab pointing to the hosted version. **How do I pass dynamic CSS values in PDFMonkey?** Use CSS custom properties. Define a variable in a

Hello\nWorld!

" } } }' ``` Most HTTP libraries (Node.js `fetch`, Python `requests`, Ruby `net/http`) serialize strings automatically — you pass the HTML as a normal string and the library handles escaping. If you build JSON by hand, escape: - double quotes → `\"` - line breaks → `\n` - backslashes → `\\` See [Generate PDFs with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) for the full generation flow, including how to poll for status and retrieve the download URL. ## Mixing your HTML with Liquid The template still runs through Liquid, so you can layer PDFMonkey logic on top of your HTML. For example, this template appends a watermark without touching your HTML: ```liquid {{htmlContent}} {% if watermark %}
{{watermark}}
{% endif %} ``` Send both `htmlContent` and `watermark` in the payload. The `{% raw %}` tag is available if any field in your payload contains literal `{{` characters that should not be evaluated. ## Assets, fonts, and encoding A few things to get right before your HTML renders as expected. **Images and external stylesheets must use absolute URLs.** The PDF engine has no concept of your file system or server. A relative path like `./img/logo.png` or `/assets/style.css` will not resolve and the asset will be silently skipped. Use full `https://` URLs instead. **External fonts load fine if accessible.** A `` to Google Fonts or any public CDN works as expected — the engine fetches it during rendering. Fonts hosted behind authentication or on localhost will not load. **Declare your character encoding.** Always include `` in the ``. Without it, non-ASCII characters (accented letters, CJK, symbols) may render as garbled text depending on the engine version. ## Trade-offs and limitations | Trade-off | Details | |-----------|---------| | No reusable layout | Headers, footers, and shared styles must live in your own HTML pipeline | | Larger payloads | Each document carries the full HTML string; large payloads count against [storage limits](https://pdfmonkey.io/docs/account-and-security/data-storage-and-retention.md) | | No live preview | The Test Data tab can hold a sample, but changes don't preview like a normal template | | No built-in Liquid logic | Conditions, loops, and filters must be in your renderer, not PDFMonkey's | If you find yourself needing any of these features, switch to a [Code Template](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md) and let PDFMonkey own the rendering. ## Builder Templates do not support this pattern Builder Templates render bindings through Vue, which **escapes HTML by default** in `{{ ... }}` expressions. Sending raw HTML through a Builder binding prints the markup as visible text. To inject pre-built HTML in a Builder Template, drop an [HTML block](https://pdfmonkey.io/docs/builder-templates/available-blocks.md#html) on the canvas and paste your markup directly into its code editor. Note that HTML blocks are static — they are part of the template itself, not driven by payload data. ## Related pages - [Generate PDFs with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) — the complete generation flow with status polling and download - [Using Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md) — the standard way to pass structured data to a template - [The Liquid Syntax](https://pdfmonkey.io/docs/code-templates/the-liquid-syntax.md) — output tags, logic, and filters in Code Templates - [Available Blocks in the Builder](https://pdfmonkey.io/docs/builder-templates/available-blocks.md#html) — the HTML block for Builder Templates ## FAQ **Can I use PDFMonkey as an HTML-to-PDF API?** Yes. Create a Code Template containing only {{htmlContent}}, then POST your full HTML as the htmlContent value inside the document payload. PDFMonkey's Liquid engine does not escape HTML, so the markup renders as-is and returns a generated file. **How do I send pre-built HTML to PDFMonkey and get a PDF back?** 1. Create a Code Template with a single line: {{htmlContent}}. 2. Publish the template. 3. POST to /api/v1/documents with your HTML as a string in the payload field htmlContent. PDFMonkey renders the HTML and returns the document with a download URL once generation is complete. **Does this HTML-to-PDF approach work in a Builder Template?** No. Builder Templates use Vue, which escapes interpolated content by default. Raw HTML sent through a Builder binding renders as visible text, not markup. Use a Code Template instead, or drop an HTML block on the Builder canvas. **Why do I still need a template if I send the full HTML myself?** PDFMonkey always generates documents from a template. The template carries the page size, margins, engine version, and access controls for the generation — even when it contains nothing but a Liquid placeholder for your HTML. **What HTML escaping is needed when sending HTML in a JSON payload?** Double quotes must be escaped as \", line breaks as \n, and backslashes as \\. Most HTTP client libraries and JSON serializers handle this automatically — you only need to escape manually when building raw JSON strings by hand. --- # Generate PDFs with No-Code Integrations Source: https://pdfmonkey.io/docs/generating-documents/using-an-integration.md PDFMonkey integrates with popular automation platforms so you can generate documents without writing a single line of code. Connect your CRM, form builder, database, or any other app to PDFMonkey and produce PDFs automatically. ## When to use an integration No-code integrations are the right choice when: - You want to **automate PDF generation** in response to events in other apps (new form submission, new CRM deal, updated spreadsheet row, etc.) - You prefer a **visual, drag-and-drop workflow** over writing code - You need to connect PDFMonkey to **apps that don't have a backend** you control (Google Sheets, Airtable, Typeform, etc.) If you need full programmatic control or are building a custom application, use the [REST API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) instead. ## How it works Regardless of which platform you choose, the general workflow is the same: 1. **Design a template** in PDFMonkey -- either with the [visual Builder](https://pdfmonkey.io/docs/builder-templates/_index.md) or with [HTML/CSS code](https://pdfmonkey.io/docs/code-templates/_index.md). 2. **Connect your PDFMonkey account** in the automation platform using your API Secret Key (found on the [My Account](https://dashboard.pdfmonkey.io/account) page). 3. **Set up a trigger** -- the event that starts PDF generation (e.g., a new row in a spreadsheet, a form submission, a new record in a CRM). 4. **Map your data** to the template variables. The automation platform passes values from the trigger into your template's dynamic fields. 5. **Generate and deliver** -- PDFMonkey creates the PDF and returns a download URL. You can then email it, upload it to cloud storage, or pass it to another step in your workflow. > [!TIP] > Most integrations return a **download URL** after generation. This URL is temporary (expires after 1 hour). If you need a permanent link, use a [share link](https://pdfmonkey.io/docs/generating-documents/share-links.md) or upload the PDF to your own storage as part of the workflow. ## Supported integrations | Platform | Best for | Details | | -------- | -------- | ------- | | [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md) | Connecting to 6,000+ apps with a simple trigger-action model | Triggers, actions, and field mapping | | [Make](https://pdfmonkey.io/docs/integrations/make.md) | Building multi-step visual scenarios with branching logic | Full module with generate, retrieve, and download | | [n8n](https://pdfmonkey.io/docs/integrations/n8n.md) | Self-hosted or cloud workflows with an open-source tool | Dedicated node with all CRUD operations | | [Workato](https://pdfmonkey.io/docs/integrations/workato.md) | Enterprise-grade automation with advanced orchestration | Recipes for generating, deleting, and reacting to documents | | [Bubble](https://pdfmonkey.io/docs/integrations/bubble.md) | Adding PDF generation to a Bubble no-code app | Plugin for direct integration | | [Glide](https://pdfmonkey.io/docs/integrations/glide.md) | Generating documents from a Glide app | Lightweight integration | Browse all integrations -- including the [Ruby SDK](https://pdfmonkey.io/docs/integrations/ruby-sdk.md) -- in the [Integrations](https://pdfmonkey.io/docs/integrations/_index.md) section. ## Custom integrations For platforms not listed above, you have two options: - **Webhooks** -- Configure a [webhook endpoint](https://pdfmonkey.io/docs/generating-documents/webhooks.md) in PDFMonkey to receive a notification (with the document data and download URL) whenever a document finishes generating. Your system can then process the PDF however you need. - **Direct HTTP requests** -- Any platform that supports HTTP requests can call the [PDFMonkey REST API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) directly. Send a POST request with your template ID and dynamic data to create a document. ## Next steps - Pick a platform and follow its dedicated guide in the [Integrations](https://pdfmonkey.io/docs/integrations/_index.md) section - New to PDFMonkey? Start with [From Zero to Your First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md) to create a template first - Need to react to generated documents? Learn about [webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) ## FAQ **Can I generate PDFs without code using PDFMonkey?** Yes. PDFMonkey integrates with Zapier, Make, n8n, Workato, Bubble, and Glide. You design a template in PDFMonkey, then connect it to your automation platform to generate documents automatically when events occur in other apps — no coding required. **Which no-code platforms does PDFMonkey integrate with?** PDFMonkey has official integrations with Zapier (6,000+ app connectors), Make (multi-step visual scenarios), n8n (open-source workflows), Workato (enterprise automation), Bubble (no-code web apps), and Glide (no-code mobile apps). **How do no-code integrations work with PDFMonkey?** The workflow is: 1) Design a template in PDFMonkey, 2) Connect your PDFMonkey account in the automation platform using your API key, 3) Set up a trigger event (form submission, new CRM deal, etc.), 4) Map data fields to template variables, 5) PDFMonkey generates the document and returns a download URL. --- # Document Statuses and Lifecycle Source: https://pdfmonkey.io/docs/generating-documents/document-statuses.md Every document in PDFMonkey moves through a series of statuses on its way from creation to final PDF. Understanding this lifecycle helps you build reliable integrations and troubleshoot generation issues. ## Status overview | Status | Meaning | What you can do | |:---|:---|:---| | `draft` | Created but not yet queued for generation | Edit payload, preview, delete | | `pending` | Queued and waiting for a worker | Wait -- processing starts automatically | | `generating` | Actively being rendered into a PDF | Wait -- the worker is processing | | `success` | PDF is ready | Download, share, embed a preview | | `failure` | An error occurred during generation | Inspect the error, fix the issue, retry | ```mermaid stateDiagram-v2 [*] --> draft: Created via API or Dashboard draft --> pending: Generation requested pending --> generating: Picked up by worker generating --> success: PDF ready generating --> failure: Error occurred failure --> pending: Retry requested ``` ## Draft {#draft} A document starts in the **draft** status when you create it, whether through the API or the Dashboard. This is the default status -- if you don't set `"status": "pending"` in your API call, the document stays here. **What you can do:** - Edit the document's dynamic data (payload) - Preview the document using the `preview_url` field (see [Embedding a Document Preview](https://pdfmonkey.io/docs/generating-documents/embedding-a-preview.md)) - Delete the document **Transition out:** Request generation by calling the API with `"status": "pending"` or by clicking **Generate** in the Dashboard. The document moves to **pending**. > [!TIP] > You can skip the draft step entirely. Set `"status": "pending"` when creating the document to queue generation immediately -- this is the most common pattern for API integrations. ## Pending {#pending} A document in the **pending** status is queued and waiting to be picked up by a generation worker. **What it means:** The generation request has been accepted but processing has not started yet. Under normal conditions, documents leave this status within seconds. **Transition out:** A worker picks up the document and moves it to **generating**. This happens automatically -- no action is required on your end. > [!NOTE] > Setting `"status": "pending"` triggers a quota check. If your account has exceeded its monthly document quota, the API rejects the request with a validation error. See [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) for quota details. ## Generating {#generating} A document in the **generating** status is actively being processed. The template is rendered with the document's payload and converted to a PDF (or image, depending on the template's [output format](https://pdfmonkey.io/docs/generating-documents/output-format.md)). **What it means:** The worker is building the document. This typically takes a few seconds, but complex templates with many pages or heavy assets can take longer. **Transition out:** The document moves to either **success** or **failure** depending on the outcome. ## Success {#success} A document in the **success** status has been generated and its PDF is available for download. **What you can do:** - Download the PDF via the `download_url` field -- this is a temporary signed URL valid for 1 hour, refreshed on each API fetch (see [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md)) - Share the document via its `public_share_link` if enabled (see [Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md)) - Receive a [webhook](https://pdfmonkey.io/docs/generating-documents/webhooks.md) notification when generation completes > [!WARNING] > The `download_url` expires after 1 hour. Fetch the document again from the API to get a fresh URL. See [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) for details. ## Failure {#failure} A document in the **failure** status encountered an error during generation. The `failure_cause` field in the API response contains a human-readable description of what went wrong. The `generation_logs` field provides additional detail for debugging. **Common causes:** - Syntax errors in the template (Liquid, HTML, or CSS) - Invalid or missing dynamic data - Asset loading failures (broken image URLs, unreachable external resources) - Timeout due to excessive template complexity **What you can do:** - Inspect `failure_cause` and `generation_logs` to understand the error - Fix the underlying issue in the template or payload - Retry generation by setting the document status back to `"pending"` via the API **Transition out:** When you request a retry, the document returns to **pending** and goes through the generation cycle again. ## Reacting to status changes You don't need to poll the API in a loop to know when a document is ready. Set up a [webhook endpoint](https://pdfmonkey.io/docs/generating-documents/webhooks.md) to receive a notification when generation succeeds or fails. This is the recommended approach for production integrations. If you prefer a synchronous workflow, use the `/api/v1/documents/sync` endpoint to wait for the result in a single request. See [Synchronous Generation](https://pdfmonkey.io/docs/api/documents.md#synchronous-generation) for details and caveats. ## Related pages - [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) -- how temporary download links work - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) -- receive notifications on status changes - [API Documents reference](https://pdfmonkey.io/docs/api/documents.md) -- full endpoint and field documentation - [Troubleshooting: The Download URL is Empty](https://pdfmonkey.io/docs/troubleshooting/download-url-is-empty.md) -- common reasons why `download_url` is `null` - [Troubleshooting: The Download URL Gives a 403 Error](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md) -- how to handle expired URLs ## FAQ **What statuses can a PDFMonkey document have?** Documents move through five statuses: draft (created but not queued), pending (queued for processing), generating (actively being rendered), success (PDF is ready to download), and failure (an error occurred during generation). **Can I skip the draft status when generating a PDFMonkey document?** Yes. Set the status to pending when creating the document via the API to queue generation immediately. This is the most common pattern for API integrations. **What happens when a PDFMonkey document fails to generate?** The document moves to failure status with a failure_cause field describing the error. You can inspect the error, fix the template or payload issue, and retry by setting the status back to pending. --- # Download URL Source: https://pdfmonkey.io/docs/generating-documents/download-url.md When a document reaches the **success** [status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md), the API response includes a `download_url` field. This is a temporary signed URL pointing to the generated PDF or image stored on S3. It triggers a file download (not inline display) and expires after 1 hour. ```json title="Successful document response (trimmed)" { "document": { "id": "a5e86d72-f5b7-43d4-a04e-8b7e08e6741c", "status": "success", "download_url": "https://pdfmonkey-production.s3.eu-west-1.amazonaws.com/...", "preview_url": "https://preview.pdfmonkey.io/...", "public_share_link": null } } ``` ## When the download URL appears The `download_url` is only populated when the document status is `success`. For all other statuses (`draft`, `pending`, `generating`, `failure`), the field returns `null`. If you just created a document and the URL is null, the most likely reason is that generation has not finished yet. See [The Download URL is Empty](https://pdfmonkey.io/docs/troubleshooting/download-url-is-empty.md) for a full diagnosis. ## URL expiration (1 hour) > [!WARNING] > **Download URLs expire after 1 hour** > > Each download URL is valid for **1 hour**. After that, the link returns a **403 Forbidden** error. This is expected behavior -- the URL is designed to be short-lived for security. See [The Download URL Gives a 403 Error](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md) if you encounter this. Every time you fetch the document details (via the API or an integration), you receive a **fresh URL** with a new 1-hour window. You never need to regenerate the document itself to get a working link. If you need a permanent URL that never expires, consider using [Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md) instead. ## Refreshing an expired download URL If a download URL has expired, fetch the document again to get a new one: - **Via the API:** `GET /api/v1/document_cards/:id` -- see [API Documents reference](https://pdfmonkey.io/docs/api/documents.md) - **Via an integration:** Use a "Get Document" or "Find Document" action in [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md), [Make](https://pdfmonkey.io/docs/integrations/make.md), or another platform - **Via the Dashboard:** Open the document detail page -- the download link is always current You do **not** need to regenerate the document. The file remains on S3 until it is [deleted](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- only the signed URL expires. ## Download URL vs. other URL fields | Field | Purpose | Expiration | |:------|:--------|:-----------| | `download_url` | Temporary signed URL that downloads the generated file. Only populated on `success` status. | 1 hour | | `preview_url` | Renders a preview of the document in the browser. Works even for drafts. **Not a download link** -- do not use it to retrieve the final file. See [Embedding a Preview](https://pdfmonkey.io/docs/generating-documents/embedding-a-preview.md). | Does not expire | | `public_share_link` | Permanent public URL to view and download the file. Available on Pro+ and Premium plans only. See [Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md). | Does not expire | ## Best practices - **Download promptly.** When you receive a [webhook](https://pdfmonkey.io/docs/generating-documents/webhooks.md) notification or poll for completion, download the file right away. - **Store the file, not the URL.** If you need the file later, save it to your own storage (S3, Google Drive, etc.) rather than relying on the temporary download URL. - **Re-fetch when needed.** If you do need the URL later, call the API again to get a fresh one -- it takes a single GET request. - **Use share links for distribution.** If you need a stable URL to embed in emails or web pages, use `public_share_link` instead (requires Pro+ or Premium plan). See [Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md). ## Troubleshooting - [The Download URL is Empty](https://pdfmonkey.io/docs/troubleshooting/download-url-is-empty.md) -- common reasons why the URL is `null` - [The Download URL Gives a 403 Error](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md) -- how to handle expired URLs ## Related pages - [Document Statuses and Lifecycle](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) -- understand when `download_url` becomes available - [Generate PDFs with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) -- end-to-end generation workflow - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) -- get notified when a document is ready to download - [Automatic Deletion (TTL)](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- how long files are stored before cleanup - [API Documents reference](https://pdfmonkey.io/docs/api/documents.md) -- full field documentation ## FAQ **How long is a PDFMonkey download URL valid?** Each download URL is a temporary signed link valid for 1 hour. After that, it returns a 403 Forbidden error. Fetch the document again via the API to get a fresh URL — no need to regenerate the document. **Why is the download_url field null?** The download_url is only populated when the document status is "success". If the document is in draft, pending, generating, or failure status, the field returns null. Make sure you set status to "pending" when creating the document and wait for generation to complete. **How do I get a new download URL after it expires?** Fetch the document again via the API (GET /api/v1/documents/:id) or through an integration. Each fetch returns a fresh URL with a new 1-hour window. You never need to regenerate the document itself. --- # Set a Custom Filename for Generated Documents Source: https://pdfmonkey.io/docs/generating-documents/custom-filename.md By default, PDFMonkey names generated files using an internal identifier. You can override this by passing a `_filename` key in the document's **metadata** -- this works for both PDFs and images. ## Setting the filename via the API Include the `_filename` key inside the `meta` object when you [create](https://pdfmonkey.io/docs/api/documents.md#create-a-document) or [update](https://pdfmonkey.io/docs/api/documents.md#update-a-document) a document: ```bash curl https://api.pdfmonkey.io/api/v1/documents \ -X POST \ -H 'Authorization: Bearer YOUR_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{ "document": { "document_template_id": "YOUR_TEMPLATE_ID", "status": "pending", "payload": { "name": "Jane Doe" }, "meta": { "_filename": "invoice-2050-03.pdf" } } }' ``` The `meta` field accepts a JSON object. You can include other metadata keys alongside `_filename` -- see [API Documents reference](https://pdfmonkey.io/docs/api/documents.md#create-a-document) for full details. ## Setting the filename from the Dashboard 1. Open a document in the Document Editor. 2. Switch to the **Meta data** tab. 3. Enter the `_filename` key and your desired name: ```json { "_filename": "invoice-2050-03.pdf" } ``` 4. Click **Save**, then **Generate**. For a full walkthrough, see [Generate Documents from the Dashboard](https://pdfmonkey.io/docs/generating-documents/using-the-dashboard.md). ## File extension handling PDFMonkey manages the file extension automatically based on the output format (PDF, PNG, WebP, or JPG): - **If you include the matching extension** (e.g. `invoice.pdf` for a PDF template), PDFMonkey strips it and re-appends it to keep the filename clean. - **If you omit the extension**, PDFMonkey appends the correct one for you. - **If you include a mismatched extension** (e.g. `.png` on a PDF document), it is treated as part of the name and the correct extension is still appended. > [!TIP] > You don't need to worry about the extension. Just provide the name you want and let PDFMonkey handle the rest. ## Filename sanitization PDFMonkey sanitizes the `_filename` value to ensure it is safe for all file systems: | Rule | Example | |:-----|:--------| | Accented characters are transliterated to ASCII | `cafe-resume` becomes `cafe-resume` | | Characters other than letters, digits, dashes, underscores, and spaces are replaced with a dash | `report@2050/03` becomes `report-2050-03` | | Consecutive dashes are collapsed | `report---final` becomes `report-final` | | Leading and trailing dashes are removed | `-my-file-` becomes `my-file` | | Whitespace is trimmed from both ends | `" invoice "` becomes `"invoice"` | For example, an input of `"Facture n 2050/03"` for a PDF template produces the filename `Facture n-2050-03.pdf`. ## Works with images too The `_filename` key works the same way for [image output formats](https://pdfmonkey.io/docs/generating-documents/output-format.md). The extension is adjusted to match the image type (`.webp`, `.png`, or `.jpg`): ```json { "meta": { "_filename": "social-card-march", "_type": "png" } } ``` This produces a file named `social-card-march.png`. ## Frequently asked questions **What happens if I don't set a custom filename?** PDFMonkey uses an internal default name. If you need predictable filenames for archival or downstream systems, always set `_filename`. **Can I use dynamic data in the filename?** Not directly inside the `_filename` value. However, in most integration platforms (Zapier, Make, n8n) you can use dynamic fields or expressions to build the filename string before passing it to PDFMonkey. **Is there a maximum filename length?** There is no explicit limit enforced by PDFMonkey, but keep filenames reasonable (under 200 characters) to avoid issues with file systems and browsers. ## Related pages - [API Documents reference](https://pdfmonkey.io/docs/api/documents.md) -- full parameter documentation for `meta` and other fields - [Generate PDFs with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) -- end-to-end generation workflow - [Generate Documents from the Dashboard](https://pdfmonkey.io/docs/generating-documents/using-the-dashboard.md) -- using the Meta data tab - [Output Formats](https://pdfmonkey.io/docs/generating-documents/output-format.md) -- image generation and meta keys like `_type` - [Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) -- how to retrieve the generated file ## FAQ **How do I set a custom filename for a PDFMonkey document?** Pass a "_filename" key in the document's meta field. Via the API, include it in the meta object when creating or updating a document: "meta": { "_filename": "invoice-2050.pdf" }. In the Dashboard, enter it in the Meta data tab of the Document Editor. **Does PDFMonkey add the file extension automatically?** Yes. If you include the correct extension (.pdf, .png, .webp, .jpg), PDFMonkey strips it and re-adds it to keep the filename clean. If you omit the extension, PDFMonkey appends it automatically based on the output format. **What characters are allowed in the custom filename?** PDFMonkey sanitizes the filename: accented characters are transliterated to ASCII, any character that is not a letter, digit, dash, underscore, or space is replaced with a dash, and consecutive or leading/trailing dashes are removed. --- # Password-Protect Generated PDFs Source: https://pdfmonkey.io/docs/generating-documents/pdf-password-protection.md You can generate password-protected PDFs by passing a `_password` key in the document's **metadata**. The generated file is encrypted with AES-256 and requires the password to open. If you omit `_password`, documents generate without encryption as usual. ## Setting the password via the API Include the `_password` key inside the `meta` object when you [create](https://pdfmonkey.io/docs/api/documents.md#create-a-document) a document: ```bash curl https://api.pdfmonkey.io/api/v1/documents \ -X POST \ -H 'Authorization: Bearer YOUR_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{ "document": { "document_template_id": "YOUR_TEMPLATE_ID", "status": "pending", "payload": { "name": "Jane Doe" }, "meta": { "_password": "s3cur3-p@ss!" } } }' ``` You can combine `_password` with other meta keys like [`_filename`](https://pdfmonkey.io/docs/generating-documents/custom-filename.md): ```json { "meta": { "_filename": "contract-jane-doe.pdf", "_password": "s3cur3-p@ss!" } } ``` ## Setting the password from the Dashboard 1. Open a document in the Document Editor. 2. Switch to the **Meta data** tab. 3. Enter the `_password` key and your desired password: ```json { "_password": "s3cur3-p@ss!" } ``` 4. Click **Save**, then **Generate**. For a full walkthrough, see [Generate Documents from the Dashboard](https://pdfmonkey.io/docs/generating-documents/using-the-dashboard.md). ## How it works When `_password` is present in the meta, PDFMonkey encrypts the generated PDF with **AES-256** after rendering. The recipient needs the exact password to open the document. Without it, the content stays locked. - **Any template works.** Code editor, Builder, existing templates -- password protection works with all of them. - **No configuration needed.** You don't need to enable anything on the template or your account. - **Works with all integrations.** Zapier, Make, Workato, n8n, Bubble, Glide, direct API -- anywhere you can set the `meta` field. - **Per-document control.** One document can be password-protected while the next one isn't. - **Available on all plans.** Free plan included. ## Frequently asked questions **What happens if I don't set a password?** Documents generate without encryption, exactly as before. Password protection is opt-in. **Can I use dynamic data in the password?** Not directly inside the `_password` value. However, in most integration platforms (Zapier, Make, n8n) you can use dynamic fields or expressions to build the password string before passing it to PDFMonkey. **Does password protection work with image output formats?** No. Password protection only applies to PDF output. Image formats (PNG, WebP, JPG) do not support encryption. ## Related pages - [Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md) -- another meta key that controls generation behavior - [API Documents reference](https://pdfmonkey.io/docs/api/documents.md) -- full parameter documentation for `meta` and other fields - [Generate PDFs with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) -- end-to-end generation workflow - [Generate Documents from the Dashboard](https://pdfmonkey.io/docs/generating-documents/using-the-dashboard.md) -- using the Meta data tab ## FAQ **How do I password protect a PDF with PDFMonkey?** Pass a "_password" key in the document's meta field. Via the API, include it in the meta object when creating a document: "meta": { "_password": "your-password" }. The generated PDF will be encrypted with AES-256 and require the password to open. **What encryption does PDFMonkey use for password-protected PDFs?** PDFMonkey uses AES-256 encryption. The PDF is encrypted after generation, and the recipient needs the exact password you specified to open the document. **Can I generate PDFs without a password?** Yes. If you don't include the _password field in your meta, PDFs generate exactly as before: no encryption, no password. Password protection is entirely opt-in, per document. --- # Generate Images Instead of PDFs Source: https://pdfmonkey.io/docs/generating-documents/output-format.md PDFMonkey generates PDFs by default, but templates with the **Image** output type produce **WebP, PNG, or JPG images** instead. The API workflow is identical; only the template type and a few meta options differ. ## Creating an image template To generate images, you need a template configured for image output. Select **Image** as the output type when creating your template: > [!TIP] > **Pro tip** > > You can also duplicate an existing template and change its output type to **Image**. > [!NOTE] > **Why separate template types?** > > PDFs and images have fundamentally different capabilities. PDF documents support headers, footers, and various paper sizes. Image templates support transparent backgrounds and pixel-based dimensions instead. Once the template is created, open its settings to configure: - **Width and height**: in pixels (defaults to 500 x 500) - **Transparent background**: supported by WebP and PNG ## Generating an image via the API Generate an image the same way you generate a PDF: send a `POST` request to `/api/v1/documents` (or `/api/v1/documents/sync` for [synchronous generation](https://pdfmonkey.io/docs/api/documents.md#synchronous-generation)). As long as the `document_template_id` points to an image template, PDFMonkey produces an image instead of a PDF. ```bash curl https://api.pdfmonkey.io/api/v1/documents \ -X POST \ -H 'Authorization: Bearer YOUR_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{ "document": { "document_template_id": "YOUR_IMAGE_TEMPLATE_ID", "status": "pending", "payload": { "title": "Hello World" } } }' ``` By default, a **WebP** image is created. WebP offers good quality at a reasonable file size and supports transparent backgrounds. ## Generation-time meta options You can override the template defaults at generation time by passing special keys in the document's `meta` field. These options only apply to image templates; they are ignored for PDF templates. | Key | Type | Description | Default | |:---|:---|:---|:---| | `_type` | string | Image format: `webp`, `png`, or `jpg` | `webp` | | `_width` | integer | Image width in pixels | Template setting (500) | | `_height` | integer | Image height in pixels | Template setting (500) | | `_quality` | integer | Compression quality from 1 to 100 (WebP only) | `100` | ```json5 title="Request body with meta overrides" { "document": { "document_template_id": "YOUR_IMAGE_TEMPLATE_ID", "payload": { "title": "Hello World" }, "status": "pending", "meta": { "_type": "png", "_width": 1200, "_height": 630 } } } ``` > [!WARNING] > The `_quality` option only applies to WebP images. PNG is always lossless, and JPG uses a fixed quality setting. > [!TIP] > For social media cards and Open Graph images, `1200x630` is the most widely recommended size. Pass `"_type": "png"` for maximum compatibility. ## Transparent backgrounds Transparency is controlled in the **template settings**, not at generation time. Enable it when you need images without a background (useful for logos, watermarks, and overlay graphics). Transparent backgrounds are supported by **WebP** and **PNG**. JPG does not support transparency: any transparent areas render as white. ## Custom filenames for images The `_filename` [meta key](https://pdfmonkey.io/docs/generating-documents/custom-filename.md) works the same way for images as it does for PDFs. PDFMonkey appends the correct extension (`.webp`, `.png`, or `.jpg`) automatically: ```json { "meta": { "_filename": "social-card-march", "_type": "png" } } ``` This produces a file named `social-card-march.png`. ## Frequently asked questions **What happens if I use image meta options on a PDF template?** They are ignored. The `_type`, `_width`, `_height`, and `_quality` meta keys only affect image templates. **Can I change the output format of an existing template?** Yes. Open the template settings in the Dashboard and change the output type. Existing documents generated from the template are not affected; only new documents use the updated setting. **Is there a maximum image size?** There is no hard pixel limit, but very large images (above 4000 x 4000 px) increase generation time and memory usage. Keep dimensions reasonable for your use case. **Do webhooks work with image generation?** Yes. [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) fire on `documents.generation.success` and `documents.generation.failure` regardless of output type. ## Related pages - [Generate PDFs with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md): end-to-end generation workflow - [Set a Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md): control the filename of generated files - [API Documents reference](https://pdfmonkey.io/docs/api/documents.md): full parameter documentation for `meta` and other fields - [Document Statuses and Lifecycle](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): understand the generation lifecycle - [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md): how to retrieve the generated file ## FAQ **Can PDFMonkey generate images instead of PDFs?** Yes. PDFMonkey supports WebP, PNG, and JPG image generation alongside PDFs. Create a template with the "Image" output type, then use the same API endpoints to generate images. **What is the default image format in PDFMonkey?** WebP is the default format for image templates. It provides good quality at a smaller file size and supports transparent backgrounds. You can override this by passing "_type": "png" or "_type": "jpg" in the document metadata. **How do I set the dimensions of a generated image?** Set the default dimensions in the template settings (width and height in pixels, defaulting to 500x500). You can override these at generation time by passing "_width" and "_height" in the document metadata. --- # Permanent Share Links for Generated Documents Source: https://pdfmonkey.io/docs/generating-documents/share-links.md Every successfully generated document on a **Pro+** or **Premium** plan includes a `public_share_link` field: a permanent public URL that never expires and requires no authentication. Use it to share generated files in emails, embed them in web pages, or distribute them to external recipients. ```json title="Successful document response (trimmed)" { "document": { "id": "a5e86d72-f5b7-43d4-a04e-8b7e08e6741c", "status": "success", "download_url": "https://pdfmonkey-production.s3.eu-west-1.amazonaws.com/...", "public_share_link": "https://files.pdfmonkey.io/share/72BE2293-D130-4C19-9E11-C82B5CEA8C37/invoice-2050-03.pdf" } } ``` > [!WARNING] > **Pro+ and Premium plans only** > > The share links feature is only available on Pro+ and Premium plans. On other plans, `public_share_link` returns `null`. See [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) for a feature comparison. ## When the share link appears The `public_share_link` is populated when **both** conditions are met: 1. The document [status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) is `success`. 2. The account is on a **Pro+** or **Premium** plan. For any other status (`draft`, `pending`, `generating`, `failure`) or on lower-tier plans, the field returns `null`. ## URL format Share links follow this pattern: ``` https://files.pdfmonkey.io/share/{share_token}/{filename} ``` - **`share_token`** is a UUID assigned to each document at creation. It does not change. - **`filename`** is the document's filename, including the extension (`.pdf`, `.png`, `.webp`, or `.jpg`). If you set a [custom filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md), it appears here. ## Inline display vs. download By default, opening a share link triggers a **file download**. To display the file inline in the browser instead, append a `disposition` query parameter: ``` https://files.pdfmonkey.io/share/{share_token}/{filename}?disposition=inline ``` | Value | Behavior | |:------|:---------| | `attachment` (default) | Browser downloads the file | | `inline` | Browser displays the file (PDFs render in the built-in viewer; images display directly) | > [!TIP] > Use `?disposition=inline` when embedding share links in web pages where you want the user to view the document before downloading. ## Share links vs. download URLs | | `public_share_link` | `download_url` | |:---|:---|:---| | **Expiration** | Never expires | Expires after 1 hour | | **Authentication** | None required | None required | | **Availability** | Pro+ and Premium plans only | All plans | | **Inline display** | Supported via `?disposition=inline` | No (always downloads) | | **When populated** | `success` status on qualifying plan | `success` status | If you need a URL that stays valid indefinitely, use `public_share_link`. If you only need a short-lived download link (for example, to save the file to your own storage immediately after generation), `download_url` is sufficient. See [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) for more details. ## Plan changes and share links Share links are tied to your account plan, not to individual documents: - **Upgrading to Pro+ or Premium:** All past documents that were generated successfully gain a working share link retroactively. You do not need to regenerate them. - **Downgrading from Pro+ or Premium:** Existing share links stop working immediately. The `public_share_link` field returns `null` in API responses. Additionally, documents that fall outside the [retention window](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) of your new plan are permanently deleted. - **Re-upgrading later:** Share links reactivate automatically for documents that still exist. However, any documents deleted during the downgrade due to shorter retention are gone permanently and cannot be recovered. See [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) for more details on how plan changes affect features. ## Works with images too Share links work for all [output formats](https://pdfmonkey.io/docs/generating-documents/output-format.md), not just PDFs. If your template produces a WebP, PNG, or JPG image, the share link points to the image file with the correct extension. ## Frequently asked questions **Why is `public_share_link` null even though my document is successful?** Your account is likely on a plan that does not include share links (Free, Starter, or Pro). Upgrade to Pro+ or Premium to enable the feature. See [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md). **Can I revoke a share link for a specific document?** Deleting the document removes the share link permanently. There is no way to disable a share link without deleting the document itself. **Do share links survive document deletion?** No. Once a document is [deleted](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) (manually or via TTL), the share link returns a 404 error. **Is the share link included in webhook payloads?** Yes. The `public_share_link` field is part of the document payload sent to your [webhook endpoint](https://pdfmonkey.io/docs/generating-documents/webhooks.md) when generation succeeds. ## Related pages - [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) -- temporary signed links and how they differ from share links - [Document Statuses and Lifecycle](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) -- understand when `public_share_link` becomes available - [Set a Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md) -- control the filename that appears in the share link - [Automatic Deletion (TTL)](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- how document deletion affects share links - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) -- which plans include share links - [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) -- what happens to share links when you change plans - [API Documents reference](https://pdfmonkey.io/docs/api/documents.md) -- full field documentation including `public_share_link` ## FAQ **What is a PDFMonkey share link?** A share link is a permanent public URL for a generated document. It never expires, requires no authentication, and can be embedded in emails, web pages, or shared directly with recipients. Available on Pro+ and Premium plans. **How is a share link different from a download URL?** Download URLs are temporary signed links that expire after 1 hour and always trigger a file download. Share links are permanent public URLs that never expire. By default, share links trigger a download, but you can append ?disposition=inline to display the file in the browser instead. **Do share links still work if I downgrade my plan?** No. Share links stop working when you downgrade from a Pro+ or Premium plan. Documents outside the retention window of your new plan are permanently deleted. If you upgrade back, share links reactivate for the documents that still exist. --- # Document Retention and Automatic Deletion (TTL) Source: https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md Every generated document is stored on PDFMonkey's servers until it expires or you delete it. You control how long documents live by setting a **TTL (Time To Live)** on each template. When a document's TTL expires, PDFMonkey automatically deletes it along with its generated file. ## How TTL works TTL is a per-template setting expressed in seconds. When a document finishes generating successfully, PDFMonkey calculates its expiration time by adding the TTL to the generation timestamp. A background job runs every minute, deleting all documents whose expiration has passed. The countdown starts when the document reaches the `success` [status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md), not when it is created or queued. > [!NOTE] > **Deletion may take up to one minute** > > The auto-deletion job runs every minute. In practice, a document may persist up to one minute past its expiration time. ## Configuring TTL in the dashboard Open a template's **Settings** tab and locate the **Deletion** dropdown. Select the retention period that fits your needs: ![The Deletion dropdown in template settings, showing options from 5 minutes to 1 week, plus Never](https://pdfmonkey.io/docs/img/auto-deletion-settings.webp) The available options depend on your [plan](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md). If you select a value your plan does not support, PDFMonkey caps it to your plan's maximum. ## Available TTL values by plan | TTL option | Seconds | Free | Starter | Pro | Pro+ | Premium | |:-----------|--------:|:----:|:-------:|:---:|:----:|:-------:| | 5 minutes | 300 | Yes | Yes | Yes | Yes | Yes | | 20 minutes | 1,200 | Yes | Yes | Yes | Yes | Yes | | 1 hour | 3,600 | Yes | Yes | Yes | Yes | Yes | | 1 day | 86,400 | Yes | Yes | Yes | Yes | Yes | | 1 week | 604,800 | No | No | Yes | Yes | Yes | | 1 month | 2,592,000 | No | No | No | Yes | Yes | | 1 year | 31,536,000 | No | No | No | Yes | Yes | | Never (unlimited) | 0 | No | No | No | Yes | Yes | > [!WARNING] > **Plan-based limits** > > Each plan has a maximum TTL. If you set a TTL higher than your plan allows (for example, selecting "1 week" on a Starter plan), PDFMonkey automatically uses the highest value your plan supports. ## Setting TTL via the API When creating or updating a template through the [Templates API](https://pdfmonkey.io/docs/api/templates.md), include the `ttl` field with a value in seconds: ```json title="Setting TTL when creating a template" { "document_template": { "identifier": "invoice-template", "ttl": 3600 } } ``` The same plan-based capping applies: if you request a TTL that exceeds your plan's maximum, the API silently uses the highest allowed value instead of returning an error. ## What gets deleted When a document is deleted (whether by TTL expiration, manual deletion in the dashboard, or an [API call](https://pdfmonkey.io/docs/api/documents.md#delete-a-document)), PDFMonkey removes: - **The generated file** (PDF, PNG, WebP, or JPG) from storage - **The payload** (the dynamic data you sent to generate the document) - **The meta data** associated with the document The document no longer appears in API responses or the dashboard. Any [share link](https://pdfmonkey.io/docs/generating-documents/share-links.md) pointing to the document returns a 404 error. > [!CAUTION] > **Deletion is permanent** > > There is no way to recover a deleted document, its generated file, or its payload. If you need to keep files long-term, download them to your own storage before the TTL expires. See [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) for details. ## Plan changes and retention When you change plans, PDFMonkey adjusts TTL settings to match the new plan's limits: - **Upgrading:** Templates keep their current TTL. If you previously set a lower TTL because of plan restrictions, you can now increase it up to your new plan's maximum. - **Downgrading:** Any template with a TTL exceeding the new plan's maximum is automatically capped. Existing documents from those templates have their expiration dates recalculated, which may cause documents to expire sooner than originally expected. See [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) for a broader overview of how plan changes affect your account. ## Manual deletion If you prefer to delete documents on your own schedule rather than relying on TTL, you have two options: 1. **Dashboard:** Open the document and click the delete button. 2. **API:** Send a `DELETE` request to `/api/v1/documents/:id`. See the [API reference](https://pdfmonkey.io/docs/api/documents.md#delete-a-document) for details. Manual deletion removes the same data as automatic TTL deletion. ## Frequently asked questions **Why was my document deleted before the TTL I set?** If you recently downgraded your plan, PDFMonkey may have recalculated expiration dates for existing documents. A template set to "1 week" on a Pro plan that is downgraded to Starter (1-day maximum) sees its documents' TTL shortened accordingly. **Can I set different TTL values for different templates?** Yes. TTL is a per-template setting. Each template in your account can have its own retention period, within your plan's limits. **Does TTL apply to failed documents?** No. Only documents with a `success` [status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) receive an expiration date. Failed documents are not automatically deleted by the TTL system. **Is there an API to check when a document expires?** The document object does not expose the `expires_at` field directly. However, you can calculate it from the template's `ttl` value and the document's `updated_at` timestamp. ## Related pages - [Document Statuses and Lifecycle](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) -- understand when documents become eligible for auto-deletion - [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) -- save files to your own storage before they expire - [Permanent Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md) -- how deletion affects share links - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) -- plan-based retention limits at a glance - [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) -- what happens to TTL settings when you switch plans - [Data Storage and Retention](https://pdfmonkey.io/docs/account-and-security/data-storage-and-retention.md) -- broader overview of what PDFMonkey stores - [API: Templates](https://pdfmonkey.io/docs/api/templates.md) -- set TTL when creating or updating a template - [API: Documents](https://pdfmonkey.io/docs/api/documents.md#delete-a-document) -- delete documents programmatically ## FAQ **How long does PDFMonkey keep my generated documents?** It depends on your plan and TTL setting. Free and Starter plans retain documents for up to 1 day. Pro allows up to 7 days. Pro+ and Premium offer unlimited retention. Within those limits, you set the exact TTL per template. **What data is deleted when a document expires?** When a document is deleted (manually or via TTL), PDFMonkey removes the generated file from storage and clears the payload and meta data. The document no longer appears in the API or dashboard. **Can I delete documents manually instead of using TTL?** Yes. You can delete documents from the PDFMonkey dashboard or by calling the DELETE /api/v1/documents/:id endpoint. Manual deletion removes the same data as automatic TTL deletion. **What happens to my TTL settings if I downgrade my plan?** When you downgrade, any template with a TTL exceeding your new plan's maximum is automatically capped to that maximum. Existing documents from those templates have their expiration dates recalculated accordingly. --- # Webhooks: Receive Real-Time Document Generation Notifications Source: https://pdfmonkey.io/docs/generating-documents/webhooks.md Webhooks let you receive an HTTP notification whenever a document finishes generating, so you can react immediately without polling the API. PDFMonkey uses [Svix](https://www.svix.com/) to deliver webhooks with automatic retries and signature verification. This page covers [event types](#event-types), the [webhook payload format](#webhook-payload), how to [configure endpoint routing](#set-up-a-workspace-wide-webhook) (workspace-wide, template-specific, or custom channels), and how to [verify webhook signatures](#verify-webhook-signatures). ## Event types PDFMonkey fires two webhook event types: | Event | When it fires | |:------|:--------------| | `documents.generation.success` | A document has been generated successfully. The `download_url` is available. | | `documents.generation.failure` | A document failed to generate. The `failure_cause` field describes what went wrong. | Subscribe to one or both depending on your needs. Most integrations subscribe to `documents.generation.success` at minimum. ## Webhook payload Every webhook delivers a [DocumentCard](https://pdfmonkey.io/docs/api/documents.md#the-documentcard-object) object wrapped in a `document` key: ```json title="Webhook payload" { "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" } } ``` > [!NOTE] > **DocumentCard, not Document** > > The webhook payload is a **DocumentCard**, not a full Document. It does not include the `payload` (dynamic data), `generation_logs`, or `checksum` fields. If you need to pass data through to your webhook handler, use the `meta` field when creating the document. See [The DocumentCard Object](https://pdfmonkey.io/docs/api/documents.md#the-documentcard-object) for a full attribute reference. ## Set up a workspace-wide webhook To receive notifications for every document generated in a workspace: 1. Click the **Webhooks** link in the main navigation. 2. Click the **+ Add Endpoint** button. 3. Enter your endpoint URL. 4. Select the event type(s) you want to receive (`documents.generation.success`, `documents.generation.failure`, or both). Your endpoint is now called whenever any document in the workspace finishes generating. ## Set up a template-specific webhook In many cases (particularly when using no-code platforms like [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md), [Make](https://pdfmonkey.io/docs/integrations/make.md), or [n8n](https://pdfmonkey.io/docs/integrations/n8n.md)), you need a webhook that fires only for a specific template. 1. Copy the **ID** of the template you want to target. 2. Click the **Webhooks** link in the main navigation. 3. Click the **+ Add Endpoint** button. 4. Enter your endpoint URL and select the event type(s). 5. In the **channels** field, add `template-YOUR_TEMPLATE_ID`. ``` title="Channel example" template-07C63E0B-620F-44A9-AF9F-4CA0DA025A0A ``` Your endpoint is now called only when a document is generated from that specific template. > [!WARNING] > **Channels are case-sensitive** > > When adding a template or folder ID to the channels list, always paste it in **upper-case**. A lower-case ID will silently fail to match, and your endpoint will never be called. ### Target multiple templates or a folder If you need the same webhook endpoint for several templates but not all of them, you can add up to **3 channels** to a single endpoint. If 3 channels are not enough, group the templates in a folder and use the folder ID as a channel. This way, any template in that folder triggers the webhook. Follow the same steps as above, but add `folder-YOUR_FOLDER_ID` in the channels list: ``` title="Channel example" folder-440DA20B-C5A0-A0A9-C62F-4070FAF963EA ``` ### Custom channels via meta You can route webhooks dynamically on a per-document basis by setting the `_webhook_channel` key in the document's `meta` field. This adds an extra channel to the webhook notification for that specific document. ```json title="Setting a custom webhook channel via the API" { "document": { "document_template_id": "2903f5b4-623b-4e10-b2e3-dc7e2e67ea39", "payload": "{}", "status": "pending", "meta": "{\"_webhook_channel\": \"order-42\"}" } } ``` Then configure a webhook endpoint with `order-42` as a channel. Only documents that include this value in their `meta` will trigger that endpoint. > [!TIP] > **Combine with template channels** > > A document's webhook notification always includes its template channel (and folder channel, if applicable) in addition to any custom channel from `meta`. This means a single webhook event can match multiple endpoints. ## Verify webhook signatures Because webhooks are HTTP requests sent to a public URL, an attacker could impersonate PDFMonkey by sending a fake request to your endpoint. To prevent this, Svix signs every webhook with a unique secret key for each endpoint. You should verify the signature on every incoming webhook before processing it. Svix provides libraries for popular languages that handle verification in a few lines of code. Learn how to verify signatures in [Svix's verification documentation](https://docs.svix.com/receiving/verifying-payloads/how). > [!CAUTION] > **Always verify in production** > > Skipping signature verification exposes your endpoint to spoofed requests. Always verify the webhook signature before acting on the payload. ## Frequently asked questions **Do webhooks work with image generation?** Yes. Webhooks fire for all [output formats](https://pdfmonkey.io/docs/generating-documents/output-format.md) (PDF, PNG, JPG, WebP). The event types are the same regardless of output type. **Is the `download_url` in the webhook payload permanent?** No. The `download_url` is a temporary signed link valid for 1 hour. Download the file immediately when you receive the webhook, or fetch a fresh URL later via the API. See [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) for details. **Does the webhook include the original dynamic data (payload)?** No. The webhook delivers a [DocumentCard](https://pdfmonkey.io/docs/api/documents.md#the-documentcard-object), which omits the `payload` field. Store any data you need in the `meta` field when creating the document; `meta` is included in the webhook. **What happens if my endpoint is down?** Svix automatically retries failed deliveries with exponential backoff over several hours. You can monitor delivery attempts and manually replay failed messages from the **Webhooks** section in the PDFMonkey dashboard. **How do I test webhooks during development?** Use a tunneling service like [ngrok](https://ngrok.com/) or [localtunnel](https://theboringtech.io/localtunnel/) to expose your local server to the internet. Point your webhook endpoint at the tunnel URL, then generate a test document from the [Dashboard](https://pdfmonkey.io/docs/generating-documents/using-the-dashboard.md). **Can I use the synchronous endpoint instead of webhooks?** Yes. The `/api/v1/documents/sync` endpoint waits for generation to complete and returns the result in a single request. This is convenient for prototyping but has a 6-minute timeout. For production workloads, webhooks are more reliable and scalable. See [Synchronous Generation](https://pdfmonkey.io/docs/api/documents.md#synchronous-generation). ## Related pages - [Document Statuses and Lifecycle](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) -- understand when webhooks fire in the document lifecycle - [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) -- how temporary download links work and how to refresh them - [API Documents reference](https://pdfmonkey.io/docs/api/documents.md) -- full endpoint and field documentation - [The DocumentCard Object](https://pdfmonkey.io/docs/api/documents.md#the-documentcard-object) -- complete attribute reference for webhook payloads - [Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md) -- use the `meta` field to set filenames and pass data through to webhooks - [Generate Documents with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) -- end-to-end asynchronous generation workflow - [Generate Documents with No-Code Integrations](https://pdfmonkey.io/docs/generating-documents/using-an-integration.md) -- use webhooks with Zapier, Make, or n8n ## FAQ **What data does PDFMonkey send in a webhook?** PDFMonkey sends a DocumentCard object wrapped in a "document" key. It includes the document ID, status, download URL, filename, meta, output type, template ID, and more. The payload does not include the original dynamic data — use the meta field to pass through any data you need in the webhook. **How do I verify that a webhook really comes from PDFMonkey?** PDFMonkey uses Svix to deliver webhooks. Every webhook is signed with a unique key for each endpoint. You can verify the signature using Svix's official libraries for Node.js, Python, Ruby, Go, and other languages. **Can I receive webhooks for only specific templates?** Yes. When creating a webhook endpoint, add a channel in the format template-YOUR_TEMPLATE_ID to filter notifications to a specific template. You can also use folder-YOUR_FOLDER_ID to receive webhooks for all templates in a folder. **Does PDFMonkey send webhooks for failed documents?** Yes. PDFMonkey fires two event types: documents.generation.success when a document is generated successfully, and documents.generation.failure when generation fails. Subscribe to both to handle errors in your integration. **What happens if my webhook endpoint is down?** Svix automatically retries failed deliveries with exponential backoff over several hours. You can monitor delivery attempts and manually replay failed messages from the Webhooks section in the PDFMonkey dashboard. --- # API Authentication Source: https://pdfmonkey.io/docs/api/authentication.mdEvery request to the PDFMonkey API must include your secret API key. This page explains how to find your key, send it with requests, and verify that authentication works. ## Find your API secret key Your API secret key is available on the dedicated API Key page in the PDFMonkey Dashboard, accessible directly from the sidebar. > [!WARNING] > **Keep your key secret** > > Your API secret key carries the same privileges as your account. Do not share it publicly or commit it to version control. If you believe your key has been compromised, regenerate it from the API Key page. ## Send your key with requests Pass your key in the `Authorization` HTTP header, prefixed with `Bearer`: ```http Authorization: Bearer YOUR_SECRET_KEY ``` ## Make a test API call To verify that authentication works, call the `current_user` endpoint: ```http GET https://api.pdfmonkey.io/api/v1/current_user Authorization: Bearer YOUR_SECRET_KEY ``` Using curl, the request looks like this: ```bash curl https://api.pdfmonkey.io/api/v1/current_user \ -H "Authorization: Bearer YOUR_SECRET_KEY" ``` A successful response returns your account information: ```json { "current_user": { "id": "12345678-90ab-cdef-1234-567890abcdef", "auth_token": "1234567890ABCDEF1234", "available_documents": 300, "created_at": "2022-01-01T12:34:56.123+00:00", "current_plan": "free", "current_plan_interval": "month", "desired_name": "Jane Doe", "email": "jane.doe@example.com", "lang": "en", "paying_customer": false, "trial_ends_on": "2022-01-15", "updated_at": "2022-01-01T12:34:56.123+00:00", "block_resources": true, "share_links": false } } ``` ### Authentication errors If the key is missing or invalid, the API returns a `401 Unauthorized` response: ```json { "errors": [ { "status": "401", "title": "Unauthorized", "detail": "We were unable to authenticate you based on the provided API key. Please verify that you provided the intended key." } ] } ``` ## Next steps Now that you can authenticate, you are ready to: - [Generate your first document](https://pdfmonkey.io/docs/api/documents.md#create-a-document) using the Documents API - [Generate a PDF from the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md) with step-by-step examples in multiple languages - [Set up webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) to get notified when your documents are ready ## FAQ **How do I authenticate with the PDFMonkey API?** Pass your secret API key in the Authorization HTTP header as a Bearer token. The format is: Authorization: Bearer YOUR_SECRET_KEY. Every request to the PDFMonkey API requires this header. **Where do I find my PDFMonkey API key?** Your API secret key is available on the API Key page in the PDFMonkey Dashboard, accessible directly from the sidebar. **What should I do if my PDFMonkey API key is compromised?** Regenerate it immediately from the API Key page in the Dashboard. The old key will stop working and a new one will be issued. --- # Documents API: Create, List, and Manage Generated Documents Source: https://pdfmonkey.io/docs/api/documents.mdComplete API reference for creating, retrieving, updating, and deleting documents in PDFMonkey. ## Quick start Generation is asynchronous: you create a document, then check its status until it's ready. ```bash # 1. Create a document and queue it for generation curl https://api.pdfmonkey.io/api/v1/documents \ -X POST \ -H 'Authorization: Bearer YOUR_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{ "document": { "document_template_id": "YOUR_TEMPLATE_ID", "status": "pending", "payload": { "name": "Jane Doe" } } }' # Returns a document with "status": "pending" and "download_url": null # 2. Poll the document status until it reaches "success" curl -H 'Authorization: Bearer YOUR_SECRET_KEY' \ https://api.pdfmonkey.io/api/v1/document_cards/DOCUMENT_ID # Once status is "success", read the download_url from the response ``` Setting `"status": "pending"` queues the document for generation immediately. The file isn't ready yet at this point — you need to poll the document or set up a [webhook](https://pdfmonkey.io/docs/generating-documents/webhooks.md) to know when it's done. Once the status reaches `success`, the `download_url` field contains a signed link to the generated file. Read on for the full details, including the [synchronous endpoint](#synchronous-generation) that waits for the result in a single request. ## Authentication All requests require a Bearer token in the `Authorization` header: ``` Authorization: Bearer YOUR_SECRET_KEY ``` See [Authentication](https://pdfmonkey.io/docs/api/authentication.md) for how to find your API key and manage access tokens. ## Create a document **`POST /api/v1/documents`** Creates a new document. Set `status` to `"pending"` to queue generation immediately, or omit it (defaults to `"draft"`) to generate later. ### Parameters All parameters are nested under a `document` key. | Name | Type | Required | Description | |:--- |:--- |:--- |:--- | | `document_template_id` | String (UUID) | **Yes** | ID of the template to use for generation. | | `payload` | Object or String | No | The [dynamic data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md) used to build the document. Must be a JSON object (not an array). Can be passed as a JSON object or a JSON string. | | `meta` | Object or String | No | Metadata to attach to the document. Supports [special keys](https://pdfmonkey.io/docs/generating-documents/custom-filename.md) like `_filename`. Must be a JSON object. Max 200 KB. | | `status` | String | No | `"draft"` (default) or `"pending"`. Set to `"pending"` to trigger generation on creation. | > [!TIP] > Set `"status": "pending"` in your create request to generate the document in one step. Without it, the document is created as a draft and you must [update it later](#update-a-document) to trigger generation. ### Request **curl** ```bash curl https://api.pdfmonkey.io/api/v1/documents \ -X POST \ -H 'Authorization: Bearer YOUR_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{ "document": { "document_template_id": "2903f5b4-623b-4e10-b2e3-dc7e2e67ea39", "status": "pending", "payload": { "clientName": "Peter Parker", "orderDate": "2050-03-14", "products": [ { "name": "Spider silk", "quantity": 12, "unitPrice": 123.50 }, { "name": "Spandex fabric", "quantity": 2, "unitPrice": 28.90 } ] }, "meta": { "_filename": "2050-03-14 Peter Parker.pdf", "clientRef": "spidey-616" } } }' ``` **Ruby** ```ruby document = Pdfmonkey::Document.generate( document_template_id: '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', payload: { clientName: 'Peter Parker', orderDate: '2050-03-14', products: [ { name: 'Spider silk', quantity: 12, unitPrice: 123.50 }, { name: 'Spandex fabric', quantity: 2, unitPrice: 28.90 } ] }, meta: { _filename: '2050-03-14 Peter Parker.pdf', clientRef: 'spidey-616' } ) document.status # => 'pending' ``` **Node.js** ```javascript const response = await fetch('https://api.pdfmonkey.io/api/v1/documents', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_SECRET_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ document: { document_template_id: '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', status: 'pending', payload: { clientName: 'Peter Parker', orderDate: '2050-03-14', products: [ { name: 'Spider silk', quantity: 12, unitPrice: 123.50 }, { name: 'Spandex fabric', quantity: 2, unitPrice: 28.90 }, ], }, meta: { _filename: '2050-03-14 Peter Parker.pdf', clientRef: 'spidey-616', }, }, }), }); const data = await response.json(); console.log(data); ``` **Python** ```python import requests response = requests.post( 'https://api.pdfmonkey.io/api/v1/documents', headers={ 'Authorization': 'Bearer YOUR_SECRET_KEY', }, json={ 'document': { 'document_template_id': '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', 'status': 'pending', 'payload': { 'clientName': 'Peter Parker', 'orderDate': '2050-03-14', 'products': [ {'name': 'Spider silk', 'quantity': 12, 'unitPrice': 123.50}, {'name': 'Spandex fabric', 'quantity': 2, 'unitPrice': 28.90}, ], }, 'meta': { '_filename': '2050-03-14 Peter Parker.pdf', 'clientRef': 'spidey-616', }, }, }, ) print(response.json()) ``` **PHP** ```php <?php $ch = curl_init('https://api.pdfmonkey.io/api/v1/documents'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer YOUR_SECRET_KEY', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'document' => [ 'document_template_id' => '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', 'status' => 'pending', 'payload' => [ 'clientName' => 'Peter Parker', 'orderDate' => '2050-03-14', 'products' => [ ['name' => 'Spider silk', 'quantity' => 12, 'unitPrice' => 123.50], ['name' => 'Spandex fabric', 'quantity' => 2, 'unitPrice' => 28.90], ], ], 'meta' => [ '_filename' => '2050-03-14 Peter Parker.pdf', 'clientRef' => 'spidey-616', ], ], ]), ]); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ### Response ```json title="201 Created" { "document": { "id": "a5e86d72-f5b7-43d4-a04e-8b7e08e6741c", "app_id": "d6b4e8f2-7a3c-4d1e-9f5b-2c8a1d3e6f90", "checksum": "c230530f6f0aa32900af0924cf228e5c", "created_at": "2050-03-13T12:34:56.181+02:00", "document_template_id": "2903f5b4-623b-4e10-b2e3-dc7e2e67ea39", "download_url": null, "failure_cause": null, "filename": null, "generation_logs": [], "meta": { "_filename": "2050-03-14 Peter Parker.pdf", "clientRef": "spidey-616" }, "output_type": "pdf", "payload": { "clientName": "Peter Parker", "orderDate": "2050-03-14", "products": [ { "name": "Spider silk", "quantity": 12, "unitPrice": 123.50 }, { "name": "Spandex fabric", "quantity": 2, "unitPrice": 28.90 } ] }, "preview_url": "https://preview.pdfmonkey.io/pdf/web/viewer.html?file=...", "public_share_link": null, "status": "pending", "updated_at": "2050-03-13T12:34:56.181+02:00" } } ``` ### Validation errors If required fields are missing or invalid, the API returns a `422 Unprocessable Entity`: ```json title="422 Unprocessable Entity" { "errors": { "document_template_id": ["can't be blank"] } } ``` ## Synchronous generation **`POST /api/v1/documents/sync`** A convenience wrapper that creates a document and polls for completion server-side, so you get the finished result in a single request. The response is a [DocumentCard](#the-documentcard-object) (not a Document). For a guided walkthrough, see [Generate Documents with the API](https://pdfmonkey.io/docs/generating-documents/api-pdf-generation.md). This endpoint accepts the same parameters as [Create a document](#create-a-document). You must set `"status": "pending"` for generation to start. ### When to use sync vs async | | Async | Sync | |:--- |:--- |:--- | | **Response** | Returns immediately | Waits until generation completes | | **Getting the result** | Poll or use [webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) | Included in the response | | **Response object** | [Document](#the-document-object) | [DocumentCard](#the-documentcard-object) | | **Scales to high volume** | Yes | No — one open request per document | | **Timeout** | None | 6 minutes | | **Best for** | Production pipelines, batch generation | Low-volume or interactive use cases | > [!WARNING] > The sync endpoint will wait up to 6 minutes for generation to complete. For production pipelines or batch generation, prefer async generation with [webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md). ### Request **curl** ```bash curl https://api.pdfmonkey.io/api/v1/documents/sync \ -X POST \ -H 'Authorization: Bearer YOUR_SECRET_KEY' \ -H 'Content-Type: application/json' \ -d '{ "document": { "document_template_id": "2903f5b4-623b-4e10-b2e3-dc7e2e67ea39", "status": "pending", "payload": { "clientName": "Peter Parker", "orderDate": "2050-03-14", "products": [ { "name": "Spider silk", "quantity": 12, "unitPrice": 123.50 }, { "name": "Spandex fabric", "quantity": 2, "unitPrice": 28.90 } ] }, "meta": { "_filename": "2050-03-14 Peter Parker.pdf", "clientRef": "spidey-616" } } }' ``` **Ruby** ```ruby document = Pdfmonkey::Document.generate!( document_template_id: '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', payload: { clientName: 'Peter Parker', orderDate: '2050-03-14', products: [ { name: 'Spider silk', quantity: 12, unitPrice: 123.50 }, { name: 'Spandex fabric', quantity: 2, unitPrice: 28.90 } ] }, meta: { _filename: '2050-03-14 Peter Parker.pdf', clientRef: 'spidey-616' } ) document.status # => 'success' document.download_url # => 'https://…' ``` **Node.js** ```javascript const response = await fetch('https://api.pdfmonkey.io/api/v1/documents/sync', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_SECRET_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ document: { document_template_id: '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', status: 'pending', payload: { clientName: 'Peter Parker', orderDate: '2050-03-14', products: [ { name: 'Spider silk', quantity: 12, unitPrice: 123.50 }, { name: 'Spandex fabric', quantity: 2, unitPrice: 28.90 }, ], }, meta: { _filename: '2050-03-14 Peter Parker.pdf', clientRef: 'spidey-616', }, }, }), }); const data = await response.json(); console.log(data.document_card.download_url); ``` **Python** ```python import requests response = requests.post( 'https://api.pdfmonkey.io/api/v1/documents/sync', headers={ 'Authorization': 'Bearer YOUR_SECRET_KEY', }, json={ 'document': { 'document_template_id': '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', 'status': 'pending', 'payload': { 'clientName': 'Peter Parker', 'orderDate': '2050-03-14', 'products': [ {'name': 'Spider silk', 'quantity': 12, 'unitPrice': 123.50}, {'name': 'Spandex fabric', 'quantity': 2, 'unitPrice': 28.90}, ], }, 'meta': { '_filename': '2050-03-14 Peter Parker.pdf', 'clientRef': 'spidey-616', }, }, }, timeout=400, ) data = response.json() print(data['document_card']['download_url']) ``` **PHP** ```php <?php $ch = curl_init('https://api.pdfmonkey.io/api/v1/documents/sync'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_TIMEOUT => 400, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer YOUR_SECRET_KEY', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'document' => [ 'document_template_id' => '2903f5b4-623b-4e10-b2e3-dc7e2e67ea39', 'status' => 'pending', 'payload' => [ 'clientName' => 'Peter Parker', 'orderDate' => '2050-03-14', 'products' => [ ['name' => 'Spider silk', 'quantity' => 12, 'unitPrice' => 123.50], ['name' => 'Spandex fabric', 'quantity' => 2, 'unitPrice' => 28.90], ], ], 'meta' => [ '_filename' => '2050-03-14 Peter Parker.pdf', 'clientRef' => 'spidey-616', ], ], ]), ]); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); echo $data['document_card']['download_url']; ``` ### Response > [!NOTE] > The sync endpoint returns a **DocumentCard**, not a Document. The result is nested under `document_card` and does not include `payload` or `generation_logs`. ```json title="200 OK" { "document_card": { "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/production/backend/document/a5e86d72-f5b7-43d4-a04e-8b7e08e6741c/2050-03-14-peter-parker.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&...", "failure_cause": null, "filename": "2050-03-14 Peter Parker.pdf", "id": "a5e86d72-f5b7-43d4-a04e-8b7e08e6741c", "meta": { "_filename": "2050-03-14 Peter Parker.pdf", "clientRef": "spidey-616" }, "output_type": "pdf", "preview_url": "https://preview.pdfmonkey.io/pdf/web/viewer.html?file=...", "public_share_link": null, "status": "success", "updated_at": "2050-03-13T12:34:59.412+02:00" } } ``` ## The Document object The Document object is the full representation of a generated document, including its payload and generation logs. ```json title="Document" { "id": "a5e86d72-f5b7-43d4-a04e-8b7e08e6741c", "app_id": "d6b4e8f2-7a3c-4d1e-9f5b-2c8a1d3e6f90", "checksum": "c230530f6f0aa32900af0924cf228e5c", "created_at": "2050-03-13T12:34:56.181+02:00", "document_template_id": "2903f5b4-623b-4e10-b2e3-dc7e2e67ea39", "download_url": "https://pdfmonkey.s3.eu-west-1.amazonaws.com/production/backend/document/a5e86d72-f5b7-43d4-a04e-8b7e08e6741c/2050-03-14-peter-parker.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&...", "failure_cause": null, "filename": "2050-03-14 Peter Parker.pdf", "generation_logs": [ { "type": "info", "message": "Starting PDF generation.", "timestamp": "Thu, 13 Mar 2050 10:34:57 GMT" }, { "type": "info", "message": "Generation took 1617ms to load and render.", "timestamp": "Thu, 13 Mar 2050 10:34:58 GMT" }, { "type": "info", "message": "Content size is 96302.", "timestamp": "Thu, 13 Mar 2050 10:34:59 GMT" } ], "meta": { "_filename": "2050-03-14 Peter Parker.pdf", "clientRef": "spidey-616" }, "output_type": "pdf", "payload": { "clientName": "Peter Parker", "orderDate": "2050-03-14", "products": [ { "name": "Spider silk", "quantity": 12, "unitPrice": 123.50 }, { "name": "Spandex fabric", "quantity": 2, "unitPrice": 28.90 } ] }, "preview_url": "https://preview.pdfmonkey.io/pdf/web/viewer.html?file=...", "public_share_link": "https://files.pdfmonkey.io/share/72BE2293-D130-4C19-9E11-C82B5CEA8C37/2050-03-14-peter-parker.pdf", "status": "success", "updated_at": "2050-03-13T12:34:59.412+02:00" } ``` ### Attributes | Name | Type | Description | |:--- |:--- |:--- | | `id` | String (UUID) | Unique identifier for the document. | | `app_id` | String (UUID) | Unique identifier of the document's workspace. | | `checksum` | String | Internal checksum used for preview rendering. | | `created_at` | String (ISO 8601) | Timestamp when the document was created. | | `document_template_id` | String (UUID) | ID of the template used to generate this document. | | `download_url` | String (URL) or `null` | Temporary signed URL to download the generated file. `null` until generation succeeds. Valid for **1 hour** (see [Download URLs](#download-urls)). | | `failure_cause` | String or `null` | Reason for generation failure. `null` unless status is `failure`. | | `filename` | String or `null` | Name of the generated file. `null` until generation succeeds. Controllable via the `_filename` meta key. | | `generation_logs` | Array | Logs collected during generation. Each entry has `type` (`"info"` or `"error"`), `message`, and `timestamp`. Empty array before generation starts. | | `meta` | Object or `null` | Arbitrary metadata attached to the document. Supports [special keys](https://pdfmonkey.io/docs/generating-documents/custom-filename.md) like `_filename`. Also accessible in no-code integrations (Zapier, Make) where `payload` is not available. | | `output_type` | String | Output format: `"pdf"` or `"image"`. Inherited from the template. | | `payload` | Object or `null` | The [dynamic data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md) used to generate the document. | | `preview_url` | String (URL) | URL for embedding a visual preview of the document. Intended for use in an ` Before you begin, make sure you have a PDFMonkey account and a published template. See [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md) for setup instructions. ### Writing Your First Template In the **HTML** tab of your template, insert the following code: ```liquid title="HTML"

Hello {{name}}!

Your favorite number is {{favoriteNumber}}.

``` In the **CSS** tab, insert the following code: ```css .greeting { color: #6D28D9; font-size: 24px; } ``` And finally in the **Test data** tab, insert this: ```json { "name": "Peter Parker", "favoriteNumber": 8 } ``` Click **Save** to see the preview update, then click **Publish** to make the template available for generation. > [!WARNING] > **Always publish your template!** > > Forgetting to publish your template is a frequent mistake, especially when switching back and forth between Zapier and PDFMonkey. If the documents you generate don't match your template, always check you published it. ### Generating Your First Document Now that your template is complete, let's head to Zapier. For this guide we will define a Zap triggered by a form submission on [Tally](https://tally.so/), generate a document in PDFMonkey, and then send it by email. #### The Tally Form We will use the following Tally form as our source of information in our Zap: ![Tally form used as Zap trigger](https://pdfmonkey.io/docs/img/zapier-guide-select-pdfmonkey.webp) #### Setting Up Your Zap Start by creating a new Zap. As trigger we will use Tally's **New Response** event: ![Creating a new Zap with Tally trigger](https://pdfmonkey.io/docs/img/zapier-guide-create-document.webp) We then proceed to fill out our form and check it's correctly connected in Zapier: ![Selecting the PDFMonkey template](https://pdfmonkey.io/docs/img/zapier-guide-select-template.webp) ![Connecting your PDFMonkey account](https://pdfmonkey.io/docs/img/zapier-guide-connect-account.webp) #### Calling PDFMonkey Now that we have some data to work with, we will configure our PDFMonkey action. Select the PDFMonkey app and choose the **Generate Document** action. ![Mapping fields in the PDFMonkey action](https://pdfmonkey.io/docs/img/zapier-guide-map-fields.webp) You can then map the data from the form to variables for your document. Make sure to use the same names as the ones used when writing the template. ![Testing the PDFMonkey action](https://pdfmonkey.io/docs/img/zapier-guide-test-action.webp) > [!NOTE] > **Naming variables** > > If you're wondering how to write proper names so it works in PDFMonkey, we provide [a list of correct/incorrect variable name formats](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md#naming-your-variables). We're now ready to test the document generation. ![Document successfully created](https://pdfmonkey.io/docs/img/zapier-guide-document-created.webp) ![Downloading the generated document](https://pdfmonkey.io/docs/img/zapier-guide-download-pdf.webp) #### Sending the Document by Email Now that our document is generated, let's send it by email. Add a new action and select the Email by Zapier app, then choose the **Send Outbound Email** action. ![Configuring the send email action](https://pdfmonkey.io/docs/img/zapier-guide-send-email.webp) We can now use our document's **Download URL** as attachment for the email. Zapier automatically downloads the file and attaches it. ![The complete Zap from trigger to email](https://pdfmonkey.io/docs/img/zapier-guide-final-zap.webp) You can now test this last action and you should receive an email with the document attached. Congrats! You generated your very first PDFMonkey document using Zapier! ## Generate a Document {#generate-a-document} The **Generate Document** action creates a document from one of your PDFMonkey templates. You select a template, map your data fields, and PDFMonkey generates the document. The action returns a [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) you can use in subsequent Zap steps. For a step-by-step walkthrough of your first Zap, see the [Getting Started](#getting-started) section above. ### Advanced JSON Payload {#advanced-json} > [!WARNING] > **Be careful!** > > With great power comes great trouble sometimes. Only switch to advanced JSON if you know what you are doing and know how to escape your data properly. > > See [Troubleshooting](#troubleshooting) for more information. By default, the integration provides a simple mapping field to define what gets sent to PDFMonkey. In most cases the simple mapping is sufficient, but there are situations where you need to structure your data differently. The **Use a custom JSON structure** option switches from the simple mapping to a text area where you can insert your own JSON with nested properties: ![Custom JSON payload option in the PDFMonkey Zapier action](https://pdfmonkey.io/docs/img/zapier-custom-json-payload.webp) ### Custom Filename {#custom-filename} You can specify a custom name for the generated file. This is useful if you plan on storing the document in a service like Drive or Dropbox. You can use dynamic parts to build the filename, but keep these restrictions in mind: - The filename must not contain any slash `/` or backslash `\` - Non-latin characters should be avoided as they can be corrupted when saving the file Short names based on dates or IDs tend to work best. > [!TIP] > For more details on filename options (including setting filenames via the API), see [Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md). ### Metadata {#metadata} When you generate a document, you can attach metadata to it. Metadata is not available in the template and cannot influence the content of the document; it is simply attached to it. A common use case is receiving the document via a [webhook](https://pdfmonkey.io/docs/generating-documents/webhooks.md) and using its metadata to route or handle it differently. ![Metadata fields in the PDFMonkey Zapier action](https://pdfmonkey.io/docs/img/zapier-metadata-fields.webp) ### Line Items {#line-items} If you are generating an invoice or a quote, you will likely need to deal with line items. Zapier provides a way to access those items if the trigger you use supports them. Take the example of a Stripe invoice. Items in the invoice are available through a Line Items collection. To send those items in a way that PDFMonkey can understand, set **Add Line Items** to **Yes**. This opens a section where you specify what a **single item** looks like. For example, consider a Stripe invoice with these items: ``` Delicious waffle 3.50€ x12 = 42.00€ Perfectly round donut 4.99€ x5 = 24.95€ TOTAL 66.95€ ``` The `TOTAL` should be sent as basic data for the document, while the name, unit price, quantity, and total amount for each item should be sent as line items from Zapier: ![Line items mapping in the PDFMonkey Zapier action](https://pdfmonkey.io/docs/img/zapier-line-items-mapping.webp) > [!TIP] > **Mapping or JSON** > > The field to define the data for a line item uses the same form as the main data. If you use [advanced JSON](#advanced-json) for the main data, the line item payload is also defined using advanced JSON. #### Using the Line Items in Your Template When Zapier sends the line items to PDFMonkey, they are accessible through a special `lineItems` variable. In the example above, the payload would look like this: ```json title="JSON" { "lineItems": [ { "product": "Delicious waffle", "quantity": "12", "unitPrice": "3.50", "totalAmount": "42.00" }, { "product": "Perfectly round donut", "quantity": "5", "unitPrice": "4.99", "totalAmount": "24.95" } ] } ``` > [!NOTE] > **Only strings attached** > > If you look closely at the payload above, only strings are sent. Zapier does not differentiate between text and numbers. > > Luckily, Liquid (the template language we use) is smart enough to convert those values to numbers when you do math with them. Just keep this in mind if you run into trouble with numbers. See [How to Do Maths](https://pdfmonkey.io/docs/code-templates/how-to-do-maths.md) for arithmetic tips. You can loop over the `lineItems` array to display content for each item individually: ```liquid title="HTML" {% for item in lineItems %}

{{item.product}} at {{item.unitPrice}}€ x{{item.quantity}} = {{item.totalAmount}}€

{% endfor %} ``` This produces the following HTML: ```html title="HTML"

Delicious waffle at 3.50€ x12 = 42.00€

Perfectly round donut at 4.99€ x5 = 24.95€

``` You will probably want to build a table row for each item, but this gives you a good idea of how to get started. See [Conditions and Loops](https://pdfmonkey.io/docs/code-templates/conditions-and-loops.md) for more Liquid loop techniques. ## Document Generated Trigger {#document-generated-trigger} The **Document Generated** trigger fires your Zap every time a document finishes generating (reaches the `success` [status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md)). This is useful for automating post-generation tasks like storing the file in Dropbox, sending it by email, or logging it in a spreadsheet. ![Selecting the Document Generated event in Zapier](https://pdfmonkey.io/docs/img/zapier-trigger-select-event.webp) You can choose to trigger your Zap for: - Any template in your workspace - A single template - Several specific templates ![Connecting your PDFMonkey account in the trigger](https://pdfmonkey.io/docs/img/zapier-trigger-connect-account.webp) Make sure to have at least one generated document before testing your trigger in Zapier. ![Selecting which template to trigger on](https://pdfmonkey.io/docs/img/zapier-trigger-select-template.webp) You can then use the generated file to store it, send it by email, or get notified in some way. > [!NOTE] > **Leveraging metadata** > > The document exposed by the trigger does not contain your payload. If you need any information in the following actions, we recommend [attaching metadata](#metadata) to the document. > > That is what we did with the Tally information in this example. ![Test result showing the trigger output](https://pdfmonkey.io/docs/img/zapier-trigger-test-result.webp) ## Retrieve and Delete Documents {#retrieve-and-delete} ### Retrieve a Document The **Retrieve Document** action fetches a previously generated document by its ID. Use this when you need to access a document's details or [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) in a later step of your Zap; for example, after a delay or in a separate workflow from the one that generated it. ### Delete a Document The **Delete Document** action removes a single document from PDFMonkey. Use this to clean up documents after you have processed them; for example, after downloading and storing the file elsewhere. See [Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) for more on document lifecycle management. ## Troubleshooting {#troubleshooting} ### Download URL Expired {#download-url-expired} If you get an expired download URL error while building your Zap, reload the example record in your trigger. The [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) is only valid for 1 hour. You need to refresh the documents in Zapier to get a new URL. Click the **Load More** button in the Test section of your trigger: ![Refreshing trigger fields to get a new download URL](https://pdfmonkey.io/docs/img/zapier-refresh-trigger-fields.webp) > [!NOTE] > **Generation output** > > This tip applies to the trigger only. For the output of a Generate Document action, you need to generate a new document to get fresh data and a valid download URL. > [!TIP] > If your Zaps fail because the URL has expired by the time a later step runs, see [Download URL Returns 403](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md) for solutions. ### 422 Responses {#422-responses} This error usually happens when you use the custom JSON option and have values that need escaping before being sent to PDFMonkey. #### Values Containing Double Quotes Consider the following custom JSON: ```json5 title="Custom JSON" { "user": { "name": "(Dynamic Field From Zapier)" } } ``` If the value in `Dynamic Field From Zapier` contains quotes, the resulting JSON breaks. Take `Peter "Spiderman" Parker` as an example: ```json title="Invalid JSON" { "user": { "name": "Peter "Spiderman" Parker" } } ``` This is not valid JSON. What you need is to _escape_ the double quotes: ```json title="Valid JSON" { "user": { "name": "Peter \"Spiderman\" Parker" } } ``` > [!NOTE] > **HTML attributes** > > If you send HTML, this happens frequently since attributes almost always use double quotes (e.g., `<div class="test">`). #### Values Containing Line Breaks JSON does not support values that include raw line breaks: ```json title="Invalid JSON" { "userBio": "Once upon a time, They lived happily ever after." } ``` Escape the line breaks by replacing them with a `
` tag or using the text representation `\n`: ```json title="Valid JSON" { "userBio": "Once upon a time,\nThey lived happily ever after." } ``` #### Solution {#escaping-solution} > [!NOTE] > **Do you really need custom JSON?** > > The first question to ask yourself is whether you truly need custom JSON. In most cases the simple mapping approach is sufficient and takes care of escaping automatically. If you cannot escape the values before they reach Zapier, add a **Code by Zapier** action before calling PDFMonkey. Suppose your data contains both line breaks and double quotes: | Item | Value | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Trigger App User Bio** |

This is the bio of the user.

It's a free form text that the user can edit so it can contain line breaks.


It could even be split in a few paragraphs... why not?

| | **Trigger App User Name** | Peter Parker | | **Trigger App User Full Name** | Peter "Spiderman" Parker | You need to escape values for both `Trigger App User Bio` and `Trigger App User Full Name`. Start by adding a **Code by Zapier** action to your Zap and selecting **Run Javascript**: ![Adding a Code by Zapier action with raw input data](https://pdfmonkey.io/docs/img/zapier-error-422-raw-body.webp) Give your fields names that help identify them in the following steps: ![Naming the input fields in Code by Zapier](https://pdfmonkey.io/docs/img/zapier-error-422-json-format.webp) Now add the code that escapes the values: ```javascript title="Code by Zapier" let data = Object.assign({}, inputData); for (let key in data) { if (typeof(data[key]) === "string" && data[key].length > 0) { data[key] = data[key].replace(/\r?\n/g, '
').replace(/\t/g, " ").replace(/"/g, '\\\"'); } } output = [data]; ``` This gives you the same fields in the output but with their values properly escaped: ![The escaped output from Code by Zapier](https://pdfmonkey.io/docs/img/zapier-error-422-custom-request.webp) You can now use those values when building your PDFMonkey payload: ![Using the escaped values in the PDFMonkey action](https://pdfmonkey.io/docs/img/zapier-error-422-fixed-payload.webp) Notice how we used the fields from the Code by Zapier step, not from the trigger directly. ## Next Steps {#next-steps} - [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md): Set up your PDFMonkey account and create your first template - [Defining Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md): Learn how to structure the JSON payload for your templates - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): Understand the lifecycle of a generated document - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md): Receive real-time notifications when documents finish generating - Explore other integrations: [Make](https://pdfmonkey.io/docs/integrations/make.md), [n8n](https://pdfmonkey.io/docs/integrations/n8n.md), [Workato](https://pdfmonkey.io/docs/integrations/workato.md) --- # Make Integration: Automate Document Generation Source: https://pdfmonkey.io/docs/integrations/make.md The PDFMonkey module for [Make](https://www.make.com/) lets you generate documents as part of your automated scenarios. Combine it with triggers from form builders, CRMs, or databases to produce and deliver files without writing code. ## Setup {#setup} Before you begin, make sure you have: - A PDFMonkey account ([sign up here](https://dashboard.pdfmonkey.io/register)) - A [Make](https://www.make.com/) account - A published template in PDFMonkey For account creation and template setup, see [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). To connect PDFMonkey to Make, add the PDFMonkey module to your scenario and click **Create a connection**. Enter your API Secret Key from the [My Account](https://dashboard.pdfmonkey.io/account) page. ## Generate a Document {#generate-a-document} Add the PDFMonkey **Generate a Document** module to your scenario. Select your Workspace and Template, then map fields from previous modules to the variables defined in your template. ![PDFMonkey field mapping in Make](https://pdfmonkey.io/docs/img/make-pdfmonkey-field-mapping.webp) > [!NOTE] > **Naming variables** > > Variable names in your template must follow specific formatting rules. See the [naming variables](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md#naming-your-variables) reference for correct and incorrect formats. After the module runs, it returns data about the generated document, including the [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) and filename. ![Document generated result in Make](https://pdfmonkey.io/docs/img/make-document-generated.webp) ## Working with the Generated File {#working-with-the-generated-file} ### Downloading the File {#downloading-the-file} To use the generated file in downstream modules, download it first. Add the HTTP **Get a File** module and pass it the **Download URL** returned by the PDFMonkey module. ![HTTP Get a File module configuration](https://pdfmonkey.io/docs/img/make-http-get-file.webp) > [!TIP] > The download URL expires after 1 hour. If you plan to use it later, download the file immediately or upload it to permanent storage. See [Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) for details. ### Sending by Email {#sending-by-email} Add your email provider module (or Make's built-in **Send an email** module) and attach the downloaded file. Customize the **File name** field to avoid the default `file.pdf` name; the PDFMonkey module returns a filename you can use directly. ![Email attachment configuration with custom filename](https://pdfmonkey.io/docs/img/make-email-attachment.webp) > [!TIP] > You can also set a [custom filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md) in PDFMonkey so it arrives with a meaningful name in every scenario. ## Example: Tally Form to Document {#example-tally-form} This example shows a complete scenario that receives a [Tally](https://tally.so/) form submission, generates a document, and sends it by email. ### The Tally Form {#the-tally-form} The scenario uses a Tally form as its data source: ![Tally form used as scenario trigger](https://pdfmonkey.io/docs/img/make-tally-form.webp) ### Setting Up the Trigger {#setting-up-the-trigger} Create a new scenario with Tally's **Watch New Responses** trigger. Make creates a webhook URL and attaches it to your Tally form automatically. ![Tally webhook setup in Make](https://pdfmonkey.io/docs/img/make-tally-webhook-setup.webp) Submit a test form entry and click **Run once** to verify the connection: ![Run once button in Make scenario](https://pdfmonkey.io/docs/img/make-run-once-trigger.webp) ![Trigger data received from Tally](https://pdfmonkey.io/docs/img/make-trigger-data-received.webp) ### Mapping Fields and Generating {#mapping-fields-and-generating} With the trigger data available, configure the PDFMonkey module to map Tally fields to your template variables, then add the HTTP and email modules as described in [Working with the Generated File](#working-with-the-generated-file). > [!WARNING] > **Always publish your template** > > If the documents you generate don't match your template, check that you published it. Forgetting to publish after making changes is a common mistake, especially when switching between Make and PDFMonkey. ## Troubleshooting {#troubleshooting} ### JSON Escaping {#json-escaping} The Make module sends data as JSON. If your data contains double quotes or line breaks, the JSON becomes invalid. For example, a name like `Peter "Spider-Man" Parker` breaks the JSON structure. Replace `"` with `\"` inside values to escape double quotes: ![JSON escaping configuration in Make](https://pdfmonkey.io/docs/img/make-json-escaping.webp) Line breaks (`\n`) require the same treatment: replace `\n` with `\\n` to keep the JSON valid. ### Template Not Published {#template-not-published} If the generated documents don't reflect your latest template changes, the most likely cause is an unpublished template. Open your template in PDFMonkey and click **Publish** before running the scenario again. ### Download URL Expired {#download-url-expired} The [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) is valid for 1 hour. If a later module in your scenario fails because the URL has expired, move the HTTP **Get a File** step closer to the PDFMonkey module so the download happens immediately. For more details, see [Download URL Returns 403](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md). ## Next Steps {#next-steps} - [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md): Set up your PDFMonkey account and create your first template - [Defining Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md): Learn how to structure the JSON payload for your templates - [Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md): Control the name of generated files - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): Understand the lifecycle of a generated document - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md): Receive real-time notifications when documents finish generating - Explore other integrations: [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md), [n8n](https://pdfmonkey.io/docs/integrations/n8n.md), [Workato](https://pdfmonkey.io/docs/integrations/workato.md) --- # n8n Integration: Automate Document Generation Source: https://pdfmonkey.io/docs/integrations/n8n.md 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](#getting-started) section below. ## Getting Started {#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](https://pdfmonkey.io/docs/integrations/zapier.md) and [Make](https://pdfmonkey.io/docs/integrations/make.md), offering both cloud and self-hosted options. Before you begin, make sure you have: - A PDFMonkey account ([sign up here](https://dashboard.pdfmonkey.io/register)) - An n8n account or instance ([sign up at n8n.io](https://n8n.io) or [self-host](https://docs.n8n.io/hosting/)) - Your PDFMonkey API key - A published template in PDFMonkey For the full account creation and template setup walkthrough, see [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). ### Setting Up Credentials {#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](https://dashboard.pdfmonkey.io/account)) 4. Click **Save** > [!TIP] > **Where to find your API key** > > Your API key is available in your PDFMonkey dashboard under **Account** then **API**. See [Authentication](https://pdfmonkey.io/docs/api/authentication.md) for more details. ### Your First Workflow {#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](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md), 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` > [!NOTE] > **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](#generate-a-document) section below. > [!NOTE] > **Variable naming** > > Variable names in your template must follow specific formatting rules. See the [naming variables](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md#naming-your-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: Download the File (Optional) If you want to download the generated file as binary data in n8n: 1. Enable **Download File** in the PDFMonkey node options 2. The file is available as binary data in the next nodes ### Example: Webhook to Document to Email {#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 }}` - Enable **Download File** option **Send Email node:** - Use Gmail, SendGrid, or any SMTP service - In attachments, reference the PDFMonkey binary data: - Property Name: `data` - File Name: `document.pdf` > [!TIP] > **Testing webhooks** > > Use the **Test URL** provided by n8n to send POST requests with JSON data like: > ```json > { > "name": "John Doe", > "favoriteNumber": 42 > } > ``` You've successfully set up your first PDFMonkey workflow in n8n. ## Generate a Document {#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](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md), and PDFMonkey renders the file. ### Payload Input Methods {#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: ```json title="JSON Payload" { "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: ```liquid title="HTML"

Invoice for {{customer.name}}

{{customer.address.street}}, {{customer.address.city}}

{% for item in items %}

{{item.product}} x{{item.quantity}} = {{item.price}}

{% endfor %} ``` For more on structuring your dynamic data, see [Defining Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md). ### Using Expressions {#using-expressions} n8n expressions let you reference data from previous nodes dynamically: ```javascript {{ $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 {#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 ``` > [!WARNING] > **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](https://pdfmonkey.io/docs/generating-documents/custom-filename.md). ### Metadata {#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](https://pdfmonkey.io/docs/generating-documents/webhooks.md). 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: ```json { "orderId": "ORD-12345", "customerEmail": "john@example.com", "environment": "production" } ``` You can retrieve metadata later using the Get Document operation or in [webhook triggers](#pdfmonkey-trigger). > [!NOTE] > **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 {#wait-for-document} By default, the Generate Document operation waits for the document to be fully generated before continuing. > [!NOTE] > **Document generation time** > > Most documents generate in a few seconds. Complex documents with many pages, images, or JavaScript may take longer. See [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) for the full generation lifecycle. If you need to disable waiting and handle completion asynchronously, you can use the [PDFMonkey Trigger](#pdfmonkey-trigger) or [poll for completion](#polling-for-completion) instead. ### Arrays and Complex Data {#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:** ```json [ { "product": "Widget", "qty": 5, "price": 10 }, { "product": "Gadget", "qty": 2, "price": 25 } ] ``` **In PDFMonkey node (JSON mode):** ```json { "customerName": "{{ $json.customerName }}", "items": {{ $node["Google Sheets"].json }} } ``` The array is passed directly to your template where you can loop over it: ```liquid {% for item in items %} {{item.product}} {{item.qty}} {{item.price}} {% endfor %} ``` For more on loops and conditional rendering, see [Conditions and Loops](https://pdfmonkey.io/docs/code-templates/conditions-and-loops.md). ### Handling Multiple Items {#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. > [!WARNING] > **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](#rate-limiting) for details, and [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) for quota information. ### Error Handling {#error-handling} Use n8n's error handling features to manage failures: 1. Click on the PDFMonkey node 2. Go to **Settings** tab 3. Configure **On Error**: - **Stop Workflow** (default) - **Continue With Last Successful Item** - **Continue Execution** For more robust error handling, add an **IF** node after PDFMonkey to check the [document status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): ``` IF: {{ $json.status }} equals "success" -> TRUE: Continue with the file -> FALSE: Send error notification ``` ## PDFMonkey Trigger {#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](https://pdfmonkey.io/docs/generating-documents/webhooks.md). ### Setup {#trigger-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 which templates to listen to: - **Any Template:** trigger for all documents in your workspace - **Specific Template:** trigger for a single template - **Multiple Templates:** trigger for several selected templates 5. Copy the **Webhook URL** displayed by the trigger node 6. In your [PDFMonkey dashboard](https://dashboard.pdfmonkey.io), go to the **Webhooks** section 7. Create a new endpoint with the n8n webhook URL 8. Check the success and failure events, then press **Create** 9. Back in n8n, click the **Activate** toggle to enable your workflow > [!NOTE] > **Production vs Test webhooks** > > n8n provides two URLs: > - **Test URL:** For testing in the n8n editor > - **Production URL:** For activated/published workflows > > Make sure to update your PDFMonkey webhook URL when activating your workflow. ### Trigger Data {#trigger-data} When a document finishes generating, the trigger receives a [DocumentCard](https://pdfmonkey.io/docs/api/documents.md#the-documentcard-object) object. This includes document metadata, status, and download URL, but does not include the original dynamic data you sent: ```json { "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" } } ``` > [!WARNING] > **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](#metadata) field when generating the document. Metadata is included in every webhook notification. ### Auto-Download {#auto-download} The PDFMonkey Trigger has a **Download File** option that automatically downloads the generated file when the trigger fires. When enabled: - The file is downloaded as binary data - It is available immediately in the next nodes - No need for a separate Download File operation To enable it: 1. Open the trigger node settings 2. Expand **Additional Options** 3. Toggle **Download File** to ON The binary data is available with the key `data`. ### Use Cases {#trigger-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-for-routing} Metadata enables conditional workflows. When generating a document, attach routing information: ```json { "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 {#trigger-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 > [!TIP] > **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 {#delivery-guarantees} PDFMonkey uses [Svix](https://www.svix.com/) 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 > [!NOTE] > **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](https://pdfmonkey.io/docs/generating-documents/webhooks.md). ## Other Operations {#other-operations} Beyond generating documents and reacting to triggers, the PDFMonkey node supports several operations for managing documents. ### Get Document {#get-document} The Get Document operation retrieves information about a previously generated document. Use it to check a document's [status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md), retrieve its [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md), 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](https://pdfmonkey.io/docs/generating-documents/document-statuses.md). ### Download File {#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. > [!NOTE] > **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 {#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 }} ``` > [!WARNING] > **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. > [!NOTE] > **TTL as alternative** > > Consider using [automatic deletion (TTL)](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) instead of manually deleting documents. You can set documents to auto-delete after a configurable period when generating them. ### Polling for Completion {#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 ``` > [!NOTE] > **Better approach: Use the trigger** > > Instead of polling, consider using the [PDFMonkey Trigger](#pdfmonkey-trigger) to be notified when documents are ready. Triggers are more efficient and respond faster. ### Combining Operations {#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 {#troubleshooting} ### Authentication Errors {#authentication-errors} **Invalid API key:** ``` 401 Unauthorized - Invalid API key ``` 1. Go to your [PDFMonkey dashboard](https://dashboard.pdfmonkey.io/account) 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](https://pdfmonkey.io/docs/api/authentication.md). > [!NOTE] > **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 {#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. > [!WARNING] > **Common mistake** > > This is one of the most frequent errors. Always remember to publish your template after making changes. ### Invalid Payload {#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:** ```json // Wrong { name: "John" } // Correct { "name": "John" } ``` **Trailing commas:** ```json // Wrong { "name": "John", "age": 30, } // Correct { "name": "John", "age": 30 } ``` **Unescaped double quotes in values:** ```json // 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: ```json // Wrong - Will create invalid JSON { "items": {{ $json.items }} } // Correct - Properly stringify { "items": {{ JSON.stringify($json.items) }} } ``` > [!TIP] > **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-errors} **Download URL expired:** ``` 403 Forbidden - Download URL has expired ``` [Download URLs](https://pdfmonkey.io/docs/generating-documents/download-url.md) 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](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md). **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-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](https://pdfmonkey.io/docs/generating-documents/webhooks.md) 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 {#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:** ```javascript // Wrong node name {{ $node["PDFmonkey"].json.id }} // Correct node name (capital M) {{ $node["PDFMonkey"].json.id }} ``` **Check the data exists:** ```javascript // 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-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](#pdfmonkey-trigger) in a separate workflow. **Option 3:** Split into multiple workflows and use webhooks to chain them together. ### Memory Errors {#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 {#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: ```javascript // Wait 1 second per item await new Promise(resolve => setTimeout(resolve, 1000)); return items; ``` ### JSON Formatting {#json-formatting} **Line breaks in JSON:** JSON does not support raw line breaks in strings: ```json // Wrong { "description": "Line 1 Line 2" } // Correct { "description": "Line 1\nLine 2" } ``` Or use a **Code** node to escape: ```javascript items[0].json.description = items[0].json.description.replace(/\n/g, '\\n'); return items; ``` **Special characters:** Escape double quotes inside string values: ```json // Wrong - unescaped quotes break JSON { "html": "
Hello
" } // Correct - escaped inner quotes { "html": "
Hello
" } ``` Or use **Key-Value Pairs** mode which handles escaping automatically. ## Next Steps {#next-steps} - [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md): Set up your PDFMonkey account and create your first template - [Defining Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md): Learn how to structure the JSON payload for your templates - [Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md): Control the name of generated files - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): Understand the lifecycle of a generated document - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md): Receive real-time notifications when documents finish generating - [Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md): How temporary download links work and how to refresh them - [Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md): Set up automatic document cleanup with TTL - Explore other integrations: [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md), [Make](https://pdfmonkey.io/docs/integrations/make.md), [Workato](https://pdfmonkey.io/docs/integrations/workato.md) ## Need More Help? {#need-more-help} If you are still experiencing issues: 1. **n8n documentation:** [docs.n8n.io](https://docs.n8n.io) 2. **PDFMonkey status:** [status.pdfmonkey.io](https://status.pdfmonkey.io) 3. **n8n Community:** [community.n8n.io](https://community.n8n.io) 4. **PDFMonkey support:** support@pdfmonkey.io > [!NOTE] > **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. --- # Workato Integration: Automate Document Generation Source: https://pdfmonkey.io/docs/integrations/workato.md The PDFMonkey connector for [Workato](https://www.workato.com/) lets you generate, monitor, and clean up documents as part of your automated recipes. Combine it with triggers from CRMs, form builders, or databases to produce and deliver files without writing code. ## Setup {#setup} Before you begin, make sure you have: - A PDFMonkey account ([sign up here](https://dashboard.pdfmonkey.io/register)) - A [Workato](https://www.workato.com/) account - A published template in PDFMonkey For account creation and template setup, see [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). To connect PDFMonkey to Workato: 1. Add the **PDFMonkey** connector to your recipe. 2. Create a new connection and enter your API Secret Key, available on the [My Account](https://dashboard.pdfmonkey.io/account) page. Once connected, the PDFMonkey actions and triggers are available in your recipe steps. ## Generate a Document {#generate-a-document} Use the **Generate Document** action to create a document from one of your templates. 1. Select the template to use for generation. 2. Provide the [dynamic data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md) as a JSON payload. This data populates the variables defined in your template. 3. Optionally attach [metadata](#metadata) (such as a custom filename or a reference ID for downstream processing). > [!NOTE] > **Naming variables** > > Variable names in your template must follow specific formatting rules. See the [naming variables](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md#naming-your-variables) reference for correct and incorrect formats. Document generation is asynchronous: the action sends the generation request and returns immediately. Use the [Document Generated trigger](#document-generated-trigger) below to react when the file is ready. ### Custom Filename {#custom-filename} You can specify a custom name for the generated file. This is useful when storing documents in services like Google Drive, Dropbox, or another cloud storage platform. Keep these restrictions in mind: - The filename must not contain any slash `/` or backslash `\` - Non-latin characters should be avoided as they can be corrupted when saving the file Short names based on dates or IDs tend to work best. > [!TIP] > For more details on filename options (including setting filenames via the API), see [Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md). ### Metadata {#metadata} When you generate a document, you can attach metadata to it. Metadata is not available in the template and does not influence the document content; it is simply attached to the document for later use. A common use case is receiving the document via a [webhook](https://pdfmonkey.io/docs/generating-documents/webhooks.md) and using its metadata to route or handle it differently in your automation. ## Document Generated Trigger {#document-generated-trigger} Use the **Document Generated** trigger to start a recipe when a document finishes generating. This is the recommended way to handle the asynchronous generation workflow. The trigger provides the completed document's data, including: - **Download URL** — a temporary link to the generated file (valid for 1 hour). See [Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) for details. - **Filename** — the name of the generated file - **Metadata** — any metadata attached during generation - **Status** — the generation result (`success` or `failure`). See [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) for the full lifecycle. You can filter by template to ensure the recipe only runs for documents from a specific template. > [!WARNING] > **Dynamic data is not included** > > The trigger does not include the original dynamic data (payload) you sent to generate the document. If you need this information in later recipe steps, store it in the [metadata](#metadata) field when generating the document. ## Delete a Document {#delete-a-document} Use the **Delete Document** action to remove a previously generated document from your PDFMonkey account. This is useful for cleaning up documents after they have been downloaded, emailed, or stored elsewhere in your workflow. > [!TIP] > **Automatic cleanup** > > Instead of manually deleting documents, consider using [automatic deletion (TTL)](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) to have documents cleaned up after a configurable period. ## Example Recipes {#example-recipes} ### Form Submission to Document to Email {#form-to-email} 1. Trigger on a form submission (from a connected app). 2. Use **Generate Document** to create the document with the form data. 3. Use **Document Generated** to wait for the file. 4. Download the file and send it as an email attachment. ### CRM Record to Document to Cloud Storage {#crm-to-storage} 1. Trigger on a new or updated CRM record. 2. Use **Generate Document** to create a contract, invoice, or report. 3. Use **Document Generated** to get the [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md). 4. Upload the file to Google Drive, Dropbox, or another storage service. ### Generate, Store, and Clean Up {#generate-store-cleanup} 1. Trigger on an event in your app (new order, approved contract, etc.). 2. Use **Generate Document** to create the file. 3. Use **Document Generated** to get the download URL. 4. Upload the file to permanent storage (S3, Google Drive, Sharepoint). 5. Use **Delete Document** to remove the file from PDFMonkey. ## Troubleshooting {#troubleshooting} ### Download URL Expired {#download-url-expired} The [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) is valid for 1 hour. If a later step in your recipe fails because the URL has expired, restructure your recipe so the download happens immediately after the **Document Generated** trigger fires. For more details, see [Download URL Returns 403](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md). ### Template Not Published {#template-not-published} If the generated documents do not reflect your latest template changes, the most likely cause is an unpublished template. Open your template in PDFMonkey and click **Publish** before running the recipe again. > [!WARNING] > **Common mistake** > > Forgetting to publish your template is a frequent error, especially when switching between Workato and PDFMonkey. Always publish after making changes. ## Next Steps {#next-steps} - [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md): Set up your PDFMonkey account and create your first template - [Defining Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md): Learn how to structure the JSON payload for your templates - [Custom Filename](https://pdfmonkey.io/docs/generating-documents/custom-filename.md): Control the name of generated files - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): Understand the lifecycle of a generated document - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md): Receive real-time notifications when documents finish generating - [Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md): Set up automatic document cleanup with TTL - Explore other integrations: [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md), [Make](https://pdfmonkey.io/docs/integrations/make.md), [n8n](https://pdfmonkey.io/docs/integrations/n8n.md) ## FAQ **How do I generate documents with PDFMonkey and Workato?** Add the PDFMonkey connector to your Workato recipe, enter your API Secret Key to connect, then use the Generate Document action to create a document from a template with your dynamic data payload. **How do I know when a PDFMonkey document is ready in Workato?** Use the Document Generated trigger to start a recipe step when a document finishes generating. It provides the download URL, filename, metadata, and status. You can filter by template so the recipe only runs for specific documents. **Can I automatically delete documents after processing in Workato?** Yes. Use the Delete Document action in a later recipe step to remove the document from PDFMonkey after you’ve downloaded, emailed, or stored it. Alternatively, configure automatic deletion (TTL) in PDFMonkey to clean up documents after a set period. --- # Bubble Integration: Generate Documents in Your Bubble App Source: https://pdfmonkey.io/docs/integrations/bubble.md The official PDFMonkey plugin for [Bubble](https://bubble.io/) lets you generate documents directly from your Bubble workflows without writing backend code. Install it from the Bubble marketplace, add a single workflow action, and get a download URL back when the document is ready. There are two ways to connect PDFMonkey to Bubble: - **[PDFMonkey plugin](#pdfmonkey-plugin)** (recommended) — install from the marketplace for a no-code setup with built-in polling. - **[API Connector](#api-connector)** — call the PDFMonkey REST API directly for full control over requests and responses. ## Prerequisites {#prerequisites} Before you begin, make sure you have: - A PDFMonkey account ([sign up here](https://dashboard.pdfmonkey.io/register)) - A [Bubble](https://bubble.io/) account with an app - A published template in PDFMonkey For account creation and template setup, see [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). You also need your **API Secret Key**, available on the [My Account](https://dashboard.pdfmonkey.io/account) page. See [Authentication](https://pdfmonkey.io/docs/api/authentication.md) for details. ## PDFMonkey Plugin {#pdfmonkey-plugin} The official PDFMonkey plugin is the fastest way to generate documents from Bubble. It handles authentication, document creation, and polling automatically: you add a single action to your workflow, and it returns the finished document. ### Installing the Plugin {#installing-the-plugin} 1. Open your Bubble app editor. 2. Go to **Plugins** and click **Add plugins**. 3. Search for **PDFMonkey** and install the [PDFMonkey](https://bubble.io/plugin/pdfmonkey-uploader-1674229694738x439725903180202000) plugin. 4. In the plugin settings, paste your **API Secret Key** from the [My Account](https://dashboard.pdfmonkey.io/account) page. That is all you need for authentication. The plugin passes your key via the `Authorization` header on every request. ### Generate a Document Action {#generate-a-document-action} The plugin provides a server-side action called **PDFMonkey - Generate a Document**. Add it to any Bubble workflow to create and generate a document from one of your templates. #### Input Fields {#input-fields} | Field | Type | Required | Description | | --- | --- | --- | --- | | **Template ID** | Dynamic value | Yes | The ID of your PDFMonkey template (found in the template settings) | | **Document data** | Key/value pairs | Yes | The dynamic data for your template; keys must match the variable names defined in your template | | **Document meta-data** | Key/value pairs | No | Metadata attached to the document (not available inside the template) | | **Filename** | Dynamic value | No | A custom filename for the generated file | > [!NOTE] > **Naming variables** > > The keys in **Document data** must match the variable names in your template exactly. See the [naming variables](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md#naming-your-variables) reference for formatting rules. #### Returned Values {#returned-values} Once the action completes, the following values are available for use in subsequent workflow steps (via **Result of step X**): | Field | Type | Description | | --- | --- | --- | | **id** | text | The unique ID of the generated document | | **Download URL** | file | A temporary URL to download the file (valid for 1 hour) | | **Public Share Link** | text | A permanent share link (available on Premium plans) | | **Filename** | text | The filename of the generated file | | **Document meta-data** | text | The metadata attached to the document | | **Status** | text | The document status (`success` or `failure`) | | **Failure cause** | text | The error message if generation failed | | **Creation Date** | date | When the document was created | | **Generation Date** | date | When the document finished generating | | **Workspace ID** | text | The ID of the workspace containing the template | | **Template ID** | text | The ID of the template used | | **Template name** | text | The name (identifier) of the template used | ### How the Action Works {#how-the-action-works} When the action runs, it: 1. Creates a document via the PDFMonkey API with status `pending`, which starts generation immediately. 2. Polls the API automatically until the document reaches `success` or `failure`. 3. Returns the result to your workflow. This means the action **blocks until the document is ready**. You do not need to set up polling, webhooks, or any async handling. When the next step in your workflow runs, the document is already generated and the download URL is available. > [!TIP] > Because the action handles polling for you, it may take a few seconds to complete depending on your template's complexity. This is normal behavior; Bubble waits for the result before moving to the next step. ### Workflow Example {#workflow-example} Here is a typical workflow that generates an invoice and emails it to the customer: 1. A user clicks a **Generate Invoice** button. 2. In the workflow, add the **PDFMonkey - Generate a Document** action. 3. Set **Template ID** to the ID of your invoice template (you can hardcode it or pull it dynamically from your database). 4. Map your Bubble data to the **Document data** keys. For example: - `customerName` → Current User's Name - `invoiceNumber` → Current Invoice's Number - `lineItems` → Current Invoice's Items (formatted as JSON) 5. The action waits until the document is ready. 6. In the next step, add a **Send Email** action and use **Result of step X's Download URL** as the attachment. > [!WARNING] > **Publish your template first** > > If your generated documents don't reflect your latest changes, check that you published the template in PDFMonkey. Forgetting to publish after editing is a common mistake. ### List Workspaces Data Source {#list-workspaces} The plugin also exposes a **List Workspaces** data source that returns the workspaces available in your PDFMonkey account. You can use it to build dynamic dropdowns or let users select a workspace before generating a document. ## API Connector {#api-connector} If you need more control over requests (custom headers, specific endpoints, or direct access to the full API response), you can call the PDFMonkey REST API using Bubble's built-in API Connector plugin. > [!NOTE] > **When to use the API Connector** > > For most use cases, the [PDFMonkey plugin](#pdfmonkey-plugin) is simpler and sufficient. Consider the API Connector if you need to call endpoints the plugin does not cover (such as listing or deleting documents), or if you want to handle polling or webhooks yourself. ### Setting Up the API Connector {#setting-up-the-api-connector} 1. In your Bubble app editor, go to **Plugins** and install the **API Connector** plugin (if not already installed). 2. Click **Add another API** and name it `PDFMonkey`. 3. Set **Authentication** to **Private key in header** and configure: - **Key name:** `Authorization` - **Key value:** `Bearer YOUR_API_SECRET_KEY` 4. Add a new API call with the following settings: | Setting | Value | | ------- | ----- | | **Name** | Create Document | | **Use as** | Action | | **Method** | POST | | **URL** | `https://api.pdfmonkey.io/api/v1/documents` | | **Headers** | `Content-Type`: `application/json` | 5. In the **Body**, paste the following JSON: ```json { "document": { "document_template_id": "", "status": "pending", "payload": { "name": "" } } } ``` Replace `` with your template's ID (found in your PDFMonkey dashboard) and adjust the `payload` keys to match your template's [dynamic data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md). 6. Click **Initialize call** to test the connection. If successful, Bubble detects the response fields automatically. > [!WARNING] > **Set status to pending** > > You must set `"status": "pending"` for PDFMonkey to start generating the document immediately. If you omit it or set it to `"draft"`, the document is created but not generated. See [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) for details. ### Using Dynamic Values {#using-dynamic-values} To pass dynamic data from your Bubble app, wrap placeholders in angle brackets (``) in the API Connector body. Bubble turns these into fields you can map to dynamic values in your workflows. For example, to generate an invoice, your body might look like this: ```json { "document": { "document_template_id": "", "status": "pending", "payload": { "customerName": "", "invoiceNumber": "", "totalAmount": "" } } } ``` Each `` becomes a field in the workflow action that you can populate with data from your Bubble app. > [!NOTE] > **Naming variables** > > Variable names in your payload must match the ones used in your template. See the [naming variables](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md#naming-your-variables) reference for formatting rules. ### Retrieving the Generated File {#retrieving-the-generated-file} Document generation is asynchronous. After the Create Document call returns, the document is not ready yet. You have two options to retrieve the finished file: - **Polling** — add a second API call (`GET https://api.pdfmonkey.io/api/v1/documents/`) and check the `status` field until it reaches `success`. The response then includes a `download_url`. - **Webhook** — configure a [webhook](https://pdfmonkey.io/docs/generating-documents/webhooks.md) in PDFMonkey to notify your Bubble app when the document is ready. This avoids polling and is more efficient for production use. > [!TIP] > The [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) expires after 1 hour. If you need a permanent link, use a [share link](https://pdfmonkey.io/docs/generating-documents/share-links.md) or store the file in your own storage. ## Troubleshooting {#troubleshooting} ### Document Generation Fails {#document-generation-fails} If the plugin action returns a `failure` status, check the **Failure cause** field (or `failureCause` in the API response) for the error message. Common causes include syntax errors in your template or invalid dynamic data. ### Invalid JSON in API Connector {#invalid-json} If you get a `422` error when using the API Connector, your JSON body is likely malformed. Common causes: - **Unescaped double quotes** in dynamic values — if a user-provided value contains `"`, it breaks the JSON structure. Sanitize inputs before passing them. - **Line breaks** in values — raw line breaks are invalid in JSON. Replace them with `\n` or `
` before sending. For more on JSON escaping, see the [422 Responses](https://pdfmonkey.io/docs/integrations/zapier.md#422-responses) section of the Zapier guide (the same principles apply). ### Document Stuck in Draft {#document-stuck-in-draft} If your document is created but never generates (API Connector only), check that `"status": "pending"` is included in the request body. Without it, PDFMonkey creates the document as a draft and waits for you to trigger generation. See [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md). > [!TIP] > This issue does not affect the PDFMonkey plugin, which always sets the status to `pending` automatically. ### Download URL Expired {#download-url-expired} The [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) is valid for 1 hour. If your workflow takes longer to process the file, download it immediately after generation or use a [share link](https://pdfmonkey.io/docs/generating-documents/share-links.md) for permanent access. For more details, see [Download URL Returns 403](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md). ## Next Steps {#next-steps} - [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md): Set up your PDFMonkey account and create your first template - [Defining Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md): Learn how to structure the JSON payload for your templates - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): Understand the lifecycle of a generated document - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md): Receive real-time notifications when documents finish generating - [Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md): How download URLs work and when they expire - Explore other integrations: [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md), [Make](https://pdfmonkey.io/docs/integrations/make.md), [n8n](https://pdfmonkey.io/docs/integrations/n8n.md) --- # Glide Integration: Generate Documents From Your No-Code App Source: https://pdfmonkey.io/docs/integrations/glide.md > [!WARNING] > **Help us reach the Glide team** > > We have reached out to the Glide team multiple times to improve this integration — in particular to add support for arrays and line items — but have never received a response. If you have a direct contact at Glide or any way to push this forward, we would greatly appreciate it. A better integration would benefit everyone building invoices, quotes, and other documents with line items from Glide. The PDFMonkey integration for [Glide](https://www.glideapps.com/) lets you generate documents directly from your no-code app. Add a **Generate PDF file** action to a button, form submission, or workflow, map your data to a PDFMonkey template, and get a download link back when the document is ready. ## Prerequisites {#prerequisites} Before you begin, make sure you have: - A PDFMonkey account ([sign up here](https://dashboard.pdfmonkey.io/register)) - A [Glide](https://www.glideapps.com/) account on a paid plan (the integration is not available on free plans) - A published template in PDFMonkey For account creation and template setup, see [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). You also need your **API Secret Key**, available on the [My Account](https://dashboard.pdfmonkey.io/account) page. See [Authentication](https://pdfmonkey.io/docs/api/authentication.md) for details. ## Setting Up the Integration {#setting-up-the-integration} 1. In your Glide app, go to **Settings**. 2. Open the **Integrations** section and find **PDFMonkey**. 3. Click **Add to app**. 4. Paste your PDFMonkey **API Secret Key** and save. Once connected, the **Generate PDF file** action becomes available in your app's actions and workflows. ## Generate PDF File Action {#generate-pdf-file-action} The **Generate PDF file** action creates a document from one of your PDFMonkey templates and returns a link to the finished file. You can trigger it from: - A **Button** or other interactive component - A **Form** submission - The **Workflow Editor** ### Action Fields {#action-fields} | Field | Required | Description | | --- | --- | --- | | **Template ID** | Yes | The ID of your PDFMonkey template. Copy it from the template settings in your PDFMonkey dashboard. | | **Filename** | Yes | The name of the generated file. You can use dynamic values from your Glide data. | | **File output** | Yes | The column where the download link is saved once the document is ready. Use a **text** column (not a URL column). | | **Dynamic values** | No | Key/value pairs that replace placeholders in your template. Click **+Add value** to map Glide columns to template variables. | > [!NOTE] > **Naming variables** > > The keys you add in the dynamic values section must match the variable names in your PDFMonkey template exactly. See the [naming variables](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md#naming-your-variables) reference for formatting rules. ### How the Action Works {#how-the-action-works} When the action runs, it sends the template ID and your dynamic values to the PDFMonkey API. PDFMonkey generates the document and returns a download link, which Glide saves in the column you specified as **File output**. > [!WARNING] > **Publish your template first** > > If the generated documents don't reflect your latest edits, check that you published the template in PDFMonkey. Forgetting to publish after making changes is a common mistake. ### Workflow Example {#workflow-example} Here is a typical flow that generates a certificate when a user completes a course: 1. The user taps a **Download Certificate** button in your Glide app. 2. The button triggers the **Generate PDF file** action. 3. Set **Template ID** to the ID of your certificate template. 4. Set **Filename** to something descriptive, for example the user's name combined with the course title. 5. Map your dynamic values: - `recipientName` → the user's name column - `courseName` → the course title column - `completionDate` → the completion date column 6. Set **File output** to a text column in your data source. 7. Once generation completes, the download link appears in the output column. You can display it as a link or use it in a subsequent action (such as sending an email). ## Working with Line Items {#working-with-line-items} The Glide integration passes data to PDFMonkey as flat key/value pairs. It does not natively support arrays or nested JSON, which means you cannot send a list of line items (such as invoice rows) the same way you would with [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md#line-items) or [Make](https://pdfmonkey.io/docs/integrations/make.md). This is a Glide-side limitation, not a PDFMonkey one. PDFMonkey templates fully support arrays and loops, but the Glide integration cannot send structured array data directly. The workarounds below let you work around this limitation depending on whether you use Code Templates or Builder Templates. ### Code Templates: JSON String with parse_json {#workaround-json-string} The cleanest approach for [Code Templates](https://pdfmonkey.io/docs/code-templates) is to pass your line items as a JSON string and parse it in the template using the [`parse_json`](https://pdfmonkey.io/docs/code-templates/filters.md#parse_json) filter. **In Glide:** 1. **Create a template column** in your related items table that formats each row as a JSON object string. For example: `{"product":"Waffle","qty":12,"unitPrice":"3.50","total":"42.00"}` 2. **Create a Joined List column** in the parent table that joins all rows with commas. 3. **Create another template column** that wraps the joined list in square brackets to form a valid JSON array: `[]` 4. **Pass this string** as a dynamic value in the Generate PDF file action, for example as `lineItemsJson`. **In your PDFMonkey template**, parse the string and loop over the resulting array: ```liquid title="HTML" {% assign items = lineItemsJson | parse_json %} {% for item in items %} {% endfor %}
Product Qty Unit Price Total
{{ item.product }} {{ item.qty }} {{ item.unitPrice }} {{ item.total }}
``` This gives you a proper array to loop over, with full access to individual fields — exactly as if the data had been sent as structured JSON. > [!TIP] > If the JSON string is empty or invalid, `parse_json` returns `nil`. You can provide a fallback: `lineItemsJson | parse_json: "[]"`. See the full [parse_json reference](https://pdfmonkey.io/docs/code-templates/filters.md#parse_json). ### Builder Templates: Custom JS with JSON.parse {#workaround-builder-json-parse} For [Builder Templates](https://pdfmonkey.io/docs/builder-templates), the approach is similar: pass a JSON string from Glide, then parse it in the [Custom JS](https://pdfmonkey.io/docs/builder-templates/custom-js.md) editor so you can use the resulting array in your template bindings. **In Glide**, build the JSON string the same way as described [above](#workaround-json-string). **In the Custom JS editor** of your Builder template, parse the string and assign the result to `window`: ```javascript window.lineItems = JSON.parse($docPayload.lineItemsJson || '[]'); ``` You can then use `lineItems` in the builder's loop and binding features to iterate over the array and display each item. ### Alternative: Pre-Built HTML {#workaround-pre-built-html} If you prefer to handle the formatting entirely in Glide, you can build the line items as an HTML string and pass the result as a single variable: 1. **Create a template column** in your related items table that formats each row as an HTML table row. For example: ```html Product Name 2 $15.00 $30.00 ``` Use Glide column references to pull the actual values. 2. **Create a Joined List column** in the parent table that concatenates all the formatted rows into a single string. 3. **Pass the joined HTML** as a dynamic value in the Generate PDF file action. For example, map it to a variable called `lineItemsHtml`. 4. **In your PDFMonkey template**, output the variable so the HTML renders correctly: ```liquid title="HTML" {{ lineItemsHtml }}
Product Qty Unit Price Total
``` > [!NOTE] > This approach is simpler to set up but less flexible — you cannot reuse the individual item data for calculations or conditional logic in the template. If you need that, use the [JSON string approach](#workaround-json-string) or the [Builder approach](#workaround-builder-json-parse) instead. ### Alternative: Use an Automation Platform {#alternative-automation-platform} If you need structured array data without any workaround, consider routing the generation through an automation platform like [Make](https://pdfmonkey.io/docs/integrations/make.md) or [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md). These platforms support line items natively and can receive a trigger from Glide via webhook, then call the PDFMonkey API with a properly structured JSON payload. ## Troubleshooting {#troubleshooting} ### Blank Documents {#blank-documents} If the download link is generated but the document is blank: - **Check your variable names.** Column names with spaces do not work as variable keys. Use `camelCase` or `snake_case` instead. For example, use `customerName`, not `Customer Name`. - **Check your test data in PDFMonkey.** Open your template, go to the **Test data** tab, and verify the document renders correctly with sample JSON. If it works in PDFMonkey but not from Glide, the issue is in the data mapping. - **Check the PDFMonkey history.** Open your PDFMonkey dashboard and look at the document in the **History** tab. The payload shows exactly what data Glide sent, making it easy to spot missing or misnamed variables. ### File Output Column Type {#file-output-column-type} The **File output** field should point to a **text** column in your Glide data source. Using a URL column or other types may cause the link to not be saved correctly. ### Document Generation Fails {#document-generation-fails} If no link appears in the output column: - Verify your **API Secret Key** is correct in the integration settings. - Confirm your **Template ID** is valid and the template is published. - Check the PDFMonkey dashboard for error details on the failed document. ### Download URL Expired {#download-url-expired} The [download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) is valid for 1 hour. If the link stops working after that, the URL has expired. You can: - Generate a new document to get a fresh link. - Use a [share link](https://pdfmonkey.io/docs/generating-documents/share-links.md) for permanent access (available on Premium plans). For more details, see [Download URL Returns 403](https://pdfmonkey.io/docs/troubleshooting/download-url-gives-403.md). ### Glide Updates Consumption {#glide-updates-consumption} Each successful execution of the Generate PDF file action consumes Glide updates. The exact number is shown when you configure the action. Failed actions do not consume updates. See [Glide's billing documentation](https://www.glideapps.com/docs/billing) for details on update costs and quotas. ## Next Steps {#next-steps} - [From Zero to First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md): Set up your PDFMonkey account and create your first template - [Defining Dynamic Data](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md): Learn how to structure the JSON payload for your templates - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md): Understand the lifecycle of a generated document - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md): Receive real-time notifications when documents finish generating - [Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md): How download URLs work and when they expire - Explore other integrations: [Zapier](https://pdfmonkey.io/docs/integrations/zapier.md), [Make](https://pdfmonkey.io/docs/integrations/make.md), [n8n](https://pdfmonkey.io/docs/integrations/n8n.md), [Workato](https://pdfmonkey.io/docs/integrations/workato.md), [Bubble](https://pdfmonkey.io/docs/integrations/bubble.md) ## FAQ **How do I generate PDFs from a Glide app with PDFMonkey?** In your Glide app Settings, add the PDFMonkey integration with your API Secret Key. Then use the Generate PDF file action in a button, form, or workflow, providing your Template ID, a filename, dynamic values, and a text column for the output link. **Can I send line items or arrays from Glide to PDFMonkey?** Not directly. Glide passes flat key/value pairs, so it cannot send arrays natively. The workaround is to build a JSON string in Glide using template columns and a Joined List column, then parse it in your PDFMonkey template with parse_json (Code Templates) or JSON.parse in Custom JS (Builder Templates). **Why are my Glide-generated PDFMonkey documents blank?** The most common cause is mismatched variable names. Column names with spaces don’t work as variable keys—use camelCase or snake_case instead. Check the document payload in PDFMonkey’s History tab to see exactly what data Glide sent. --- # InvoiceBerry (via Zapier) Source: https://pdfmonkey.io/docs/integrations/invoiceberry.md ![](https://pdfmonkey.io/docs/img/invoiceberry-logo.png) ## What is InvoiceBerry? InvoiceBerry is an online invoicing tool preferably used by small business owners for their business. It helps the customer create and customize invoices according to their business needs, create and send quotes to their customers, setup recurring invoices, send reminders, and track expenses for their businesses. It provides user-friendly and feasible environment to keep finance and business accounting at one place. ## How does InvoiceBerry work with PDFMonkey? InvoiceBerry is online invoicing software made to simplify your invoicing processes and PDFMonkey is an easy way to generate PDF documents. Integrating these two apps will help to automatically generate PDF documents and find documents in response to newly created InvoiceBerry clients, invoices, expenses and more. Currently there are [14 possible Zapier integrations between PDFMonkey and InvoiceBerry](https://www.zapier.com/apps/pdfmonkey/integrations/invoiceberry). ## What can you do with InvoiceBerry and PDFMonkey? * Generate PDF from newly created InvoiceBerry invoice * Create client in InvoiceBerry when a new document is generated in PDFMonkey * Find document in PDFMonkey from newly created client in InvoiceBerry * Find document In PDFMonkey in response to newly created InvoiceBerry invoice ## FAQ **How does InvoiceBerry integrate with PDFMonkey?** InvoiceBerry connects to PDFMonkey through Zapier. You can set up automated workflows that generate custom PDF documents whenever a new invoice, client, or expense is created in InvoiceBerry. **What can I automate between InvoiceBerry and PDFMonkey?** You can generate PDFs from newly created invoices, create InvoiceBerry clients when documents are generated, and find documents in response to new clients or invoices. There are 14 possible Zapier integrations between the two apps. --- # Ruby SDK Source: https://pdfmonkey.io/docs/integrations/ruby-sdk.md We provide a [Ruby SDK](https://github.com/pdfmonkey/pdfmonkey-ruby) to connect to PDFMonkey. This gem is the quickest way to use our API with Ruby. GitHub: [https://github.com/pdfmonkey/pdfmonkey-ruby](https://github.com/pdfmonkey/pdfmonkey-ruby) ## Installation Add this line to your application's Gemfile: ```ruby title="Gemfile" gem 'pdfmonkey' ``` And then execute: ```bash $ bundle ``` Or install it yourself as: ```bash $ gem install pdfmonkey ``` ## Usage ### Setting up authentication #### Using the default environment variable PDFMonkey looks for the `PDFMONKEY_PRIVATE_KEY` environment variable. This variable should contain your private key obtained at https://dashboard.pdfmonkey.io/account. ```bash PDFMONKEY_PRIVATE_KEY=j39ckj4… ``` #### Setting credentials manually You can set up your credentials explicitly in your application: ```ruby Pdfmonkey.configure do |config| config.private_key = 'j39ckj4…' end ``` #### Per-request credentials Configuration is global. If you need per-request credentials (e.g. multi-tenant scenarios), use `with_adapter`: ```ruby config = Pdfmonkey::Configuration.new config.private_key = 'tenant-specific-key' adapter = Pdfmonkey::Adapter.new(config: config) Pdfmonkey.with_adapter(adapter) do Pdfmonkey::Document.generate!( document_template_id: 'b13ebd75-…', payload: { name: 'John Doe' } ) end ``` All operations inside the block use the given adapter. Calls outside the block continue using the global configuration. ### Documents #### Synchronous generation If you want to wait for a document's generation before continuing with your workflow, use the `generate!` method. It requests a document generation and waits for it to succeed or fail before returning. ```ruby document = Pdfmonkey::Document.generate!( document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785', payload: { name: 'John Doe' } ) document.status # => 'success' document.download_url # => 'https://…' ``` > [!WARNING] > **The download URL is temporary** > > The download URL of a document is only valid **for 1 hour**. Past this delay, reload the document to obtain a new one: > > ```ruby > document.reload! > ``` #### Asynchronous generation PDFMonkey was created with an asynchronous workflow in mind. It provides [webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) to inform you of a document's generation success or failure. To leverage this behavior and continue working while your document is being generated, use the `generate` method: ```ruby document = Pdfmonkey::Document.generate( document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785', payload: { name: 'John Doe' } ) document.status # => 'pending' document.download_url # => nil ``` If you have a webhook URL set up, it will be called with your document once the generation is complete. You can simulate it for testing with the following cURL command: ```bash curl \ -X POST \ -H 'Content-Type: application/json' \ -d '{ "document": { "id": "76bebeb9-9eb1-481a-bc3c-faf43dc3ac81", "app_id": "d9ec8249-65ae-4d50-8aee-7c12c1f9683a", "created_at": "2020-01-02T03:04:05.000+01:00", "document_template_id": "f7fbe2b4-a57c-46ee-8422-5ae8cc37daac", "meta": "{\"_filename\":\"my-doc.pdf\"}", "output_type": "pdf", "status": "success", "updated_at": "2020-01-02T03:04:15.000+01:00", "xml_data": null, "payload": "{\"name\": \"John Doe\"}", "download_url": "https://example.com/76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf", "checksum": "ac0c2b6bcc77e2b01dc6ca6a9f656b2d", "failure_cause": null, "filename": "my-doc.pdf", "generation_logs": [], "preview_url": "https://preview.pdfmonkey.io/pdf/web/viewer.html?file=…", "public_share_link": "https://example.com/76bebeb9-9eb1-481a-bc3c-faf43dc3ac81.pdf" } }' ``` #### Draft documents You can create a draft document that won't be queued for generation. > [!TIP] > This is useful for embedding a preview via `preview_url` before triggering generation. That's what we do in the PDFMonkey dashboard to show you a preview of your document before generating it, using an `iframe`. ```ruby draft = Pdfmonkey::Document.create_draft( document_template_id: 'b13ebd75-d290-409b-9cac-8f597ae3e785', payload: { name: 'John Doe' } ) draft.status # => 'draft' draft.preview_url # => 'https://…' # When ready, trigger generation and wait for completion: draft.generate! draft.status # => 'success' draft.download_url # => 'https://…' # Or trigger generation without waiting: draft.generate draft.status # => 'pending' ``` #### Attaching meta data In addition to the document's payload you can add meta data when generating a document. Pass the `meta:` keyword argument to the `generate!` and `generate` methods: ```ruby meta = { _filename: 'john-doe-contract.pdf', client_id: '123xxx123' } document = Pdfmonkey::Document.generate!( document_template_id: template_id, payload: payload, meta: meta ) document.meta # => '{"_filename":"john-doe-contract.pdf","client_id":"123xxx123"}' document = Pdfmonkey::Document.generate( document_template_id: template_id, payload: payload, meta: meta ) document.meta # => '{"_filename":"john-doe-contract.pdf","client_id":"123xxx123"}' ``` #### Image generation Image generation uses the same API flow as PDF generation. The template's `output_type` attribute indicates whether it produces `'pdf'` or `'image'` output. Image-specific options are passed through the `meta` parameter: ```ruby doc = Pdfmonkey::Document.generate!( document_template_id: template_id, payload: payload, meta: { _type: 'png', # webp (default), png, or jpg _width: 800, # pixels _height: 600, # pixels _quality: 80 # webp only, default 100 } ) doc.download_url # => URL to the generated image ``` #### Updating a document ```ruby document.update!(status: 'pending') ``` #### Listing documents ```ruby cards = Pdfmonkey::Document.list_cards(page: 1, status: 'success') cards.each do |card| puts card.id puts card.status end cards.current_page # => 1 cards.total_pages # => 5 # Navigate pages next_cards = cards.next_page prev_cards = cards.prev_page ``` You can filter by `document_template_id:`, `status:`, `workspace_id:`, and `updated_since:`. #### Fetching a document > [!CAUTION] > **Prefer fetch_card over fetch** > > Fetching a full document includes its payload, meaning it could be large depending on the data you provided. We **strongly** recommend using only `fetch_card` unless you have a specific reason to fetch the full document. You can fetch an existing document using `.fetch` (or its explicit alias `.fetch_full`): ```ruby document = Pdfmonkey::Document.fetch('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81') ``` To fetch just the lightweight card representation (recommended): ```ruby card = Pdfmonkey::Document.fetch_card('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81') ``` #### Deleting a document You can delete an existing document using the `.delete` method: ```ruby Pdfmonkey::Document.delete('76bebeb9-9eb1-481a-bc3c-faf43dc3ac81') #=> true ``` Alternatively you can call the `#delete!` method on a document instance: ```ruby document.delete! #=> true ``` ### Error handling API errors and network errors raise exceptions: ```ruby begin document = Pdfmonkey::Document.generate( document_template_id: template_id, payload: data ) rescue Pdfmonkey::ApiError => e e.message # => "Document template must exist" e.errors # => ["Document template must exist"] e.status_code # => 422 rescue Pdfmonkey::ConnectionError => e e.message # => "Failed to open TCP connection to api.pdfmonkey.io:443 ..." end ``` When using `generate!`, an additional exception may be raised if the document's status is `'error'` or `'failure'`: ```ruby begin document = Pdfmonkey::Document.generate!( document_template_id: template_id, payload: data ) rescue Pdfmonkey::GenerationError => e e.message # => "Document generation failed: Template error" e.document # => # (the failed document) end ``` All exception classes inherit from `Pdfmonkey::Error`, so you can rescue broadly: ```ruby begin document = Pdfmonkey::Document.generate!( document_template_id: template_id, payload: data ) rescue Pdfmonkey::Error => e puts "Something went wrong: #{e.message}" end ``` ### Templates #### Fetching a template > [!CAUTION] > **Full templates can be large** > > Fetching a full template includes its body and settings, which can be large. Use `list_cards` when you only need metadata. ```ruby template = Pdfmonkey::Template.fetch('b13ebd75-d290-409b-9cac-8f597ae3e785') template.identifier # => 'my-invoice' template.body # => '

Invoice

…' (published version) template.body_draft # => '

Invoice v2

…' (draft version) ``` You can also use the explicit alias `.fetch_full`: ```ruby template = Pdfmonkey::Template.fetch_full('b13ebd75-d290-409b-9cac-8f597ae3e785') ``` #### Creating a template When creating a template, attributes like `body`, `scss_style`, `settings`, `sample_data`, and `pdf_engine_id` are automatically written to their draft counterparts (`body_draft`, `scss_style_draft`, etc.): ```ruby template = Pdfmonkey::Template.create( identifier: 'my-invoice', body: '

Invoice

' ) template.body_draft # => '

Invoice

' ``` #### Updating a template Like `create`, `update!` writes to the draft fields: ```ruby template.update!(body: '

Updated Invoice

') template.body_draft # => '

Updated Invoice

' ``` #### Publishing a template Once you're happy with the draft, publish it to copy all draft fields to their published counterparts: ```ruby template.publish! template.body # => '

Updated Invoice

' ``` #### Listing templates ```ruby cards = Pdfmonkey::Template.list_cards(workspace_id: 'f4ab650c-…') ``` #### Deleting a template ```ruby Pdfmonkey::Template.delete('b13ebd75-…') # or template.delete! ``` ### Template Folders ```ruby # List folders folders = Pdfmonkey::TemplateFolder.list # Create a folder folder = Pdfmonkey::TemplateFolder.create(identifier: 'invoices') # Fetch a folder folder = Pdfmonkey::TemplateFolder.fetch('folder-id') # Update a folder folder.update!(identifier: 'receipts') # Delete a folder Pdfmonkey::TemplateFolder.delete('folder-id') # or folder.delete! ``` To create a template inside a specific folder, pass the `template_folder_id`: ```ruby folder = Pdfmonkey::TemplateFolder.create(identifier: 'invoices') template = Pdfmonkey::Template.create( identifier: 'monthly-invoice', body: '

Invoice

', template_folder_id: folder.id ) ``` ### Snippets Snippets are reusable HTML components that can be included in templates. ```ruby # List snippets snippets = Pdfmonkey::Snippet.list # Create a snippet snippet = Pdfmonkey::Snippet.create( identifier: 'header', code: '
', workspace_id: 'f4ab650c-…' ) # Fetch a snippet snippet = Pdfmonkey::Snippet.fetch('snippet-id') # Update a snippet snippet.update!(code: '
Updated
') # Delete a snippet Pdfmonkey::Snippet.delete('snippet-id') # or snippet.delete! ``` ### Workspaces Workspaces are read-only resources. They can be listed and fetched but not created, updated, or deleted through the API. ```ruby # List workspaces workspaces = Pdfmonkey::Workspace.list_cards workspaces.each do |workspace| puts workspace.identifier end # Fetch a workspace workspace = Pdfmonkey::Workspace.fetch('workspace-id') workspace.identifier # => 'my-app' ``` ### Webhooks Webhooks allow you to receive notifications when documents are generated. ```ruby # Create a webhook for all templates in a workspace webhook = Pdfmonkey::Webhook.create( url: 'https://example.com/webhooks/pdfmonkey', event: 'document.generation.completed', workspace_id: 'f4ab650c-…' ) # Optionally restrict to specific templates webhook = Pdfmonkey::Webhook.create( url: 'https://example.com/webhooks/pdfmonkey', event: 'document.generation.completed', workspace_id: 'f4ab650c-…', document_template_ids: ['tpl-1', 'tpl-2'] ) # You can also specify a custom channel for routing webhook = Pdfmonkey::Webhook.create( url: 'https://example.com/webhooks/pdfmonkey', event: 'document.generation.completed', workspace_id: 'f4ab650c-…', custom_channel: 'invoices' ) # Delete a webhook Pdfmonkey::Webhook.delete('webhook-id') # or webhook.delete! ``` ### Engines List available PDF rendering engines: ```ruby engines = Pdfmonkey::Engine.list engines.each do |engine| puts "#{engine.name} (deprecated: #{engine.deprecated_on || 'no'})" end ``` You can use an engine when creating a template: ```ruby engines = Pdfmonkey::Engine.list v4 = engines.find { |e| e.name == 'v4' } template = Pdfmonkey::Template.create( identifier: 'my-template', body: '

Hello

', pdf_engine_id: v4.id ) ``` ### Current User Retrieve information about the authenticated user: ```ruby user = Pdfmonkey::CurrentUser.fetch user.email # => 'user@example.com' user.current_plan # => 'pro' user.available_documents # => 1000 ``` ### Pagination All list methods return `Pdfmonkey::Collection` objects that support pagination: ```ruby collection = Pdfmonkey::Document.list_cards(page: 1) collection.current_page # => 1 collection.total_pages # => 5 collection.next_page_number # => 2 collection.prev_page_number # => nil # Navigate to next/previous pages next_page = collection.next_page # => Collection or nil prev_page = collection.prev_page # => Collection or nil ``` > [!NOTE] > Collections are `Enumerable`. Methods like `.each`, `.map`, and `.select` operate on the items **of the current page only** -- they do not automatically fetch subsequent pages. ```ruby # These all act on the current page's items collection.each { |item| puts item.id } collection.map(&:status) collection.select { |item| item.status == 'success' } # To process all pages, navigate manually page = Pdfmonkey::Template.list_cards(page: 1) while page page.each { |item| process(item) } page = page.next_page end ``` ### Serialization All resources support `to_json` and `to_h`: ```ruby document.to_json # => '{"document":{"id":"…","status":"success",…}}' document.to_h # => {id: "…", status: "success", errors: nil, …} ``` > [!TIP] > `to_json` wraps attributes under the resource's API member key, omits `nil` values, and strips the `errors` attribute (it is intended for API requests). Use `to_h` when you need the full attribute hash for logging or caching. ## FAQ **How do I install the PDFMonkey Ruby SDK?** Add gem ‘pdfmonkey’ to your Gemfile and run bundle install, or run gem install pdfmonkey directly. Set your API key via the PDFMONKEY_PRIVATE_KEY environment variable or configure it in an initializer. **What is the difference between generate! and generate in the PDFMonkey Ruby gem?** generate! is synchronous—it blocks until the document reaches success or failure, then returns the document with a download URL. generate is asynchronous—it returns immediately with a pending status, and you handle the result via webhooks. **Can I use per-request API keys with the PDFMonkey Ruby SDK?** Yes. Create a Pdfmonkey::Configuration with a tenant-specific key, wrap it in an Adapter, and pass it to Pdfmonkey.with_adapter. All operations inside the block use that adapter while calls outside continue using the global configuration. **How do I handle errors in the PDFMonkey Ruby SDK?** Rescue Pdfmonkey::ApiError for API errors (includes status_code and errors), Pdfmonkey::ConnectionError for network issues, or Pdfmonkey::GenerationError when using generate! and the document fails. All inherit from Pdfmonkey::Error for broad rescuing. --- # How to Delete Your PDFMonkey Account Source: https://pdfmonkey.io/docs/account-and-security/account-deletion.md You can delete your PDFMonkey account at any time from the [My Account](https://dashboard.pdfmonkey.io/account) page in the Dashboard. The process takes two clicks if your account has no blockers, but you may need to resolve workspace ownership or invoice issues first. ## Starting the deletion process 1. Go to the [My Account](https://dashboard.pdfmonkey.io/account) page. 2. Scroll to the **Account deletion** section at the bottom. 3. Click **Delete my account**. PDFMonkey then runs a pre-deletion check and shows you the results. Depending on your account status, you may see one or more items to address before proceeding. ## Pre-deletion checks When you click the delete button, PDFMonkey validates three things: | Check | What it means | How to resolve | |:------|:--------------|:---------------| | **Shared workspace ownership** | You own one or more Workspaces that have collaborators. | Transfer ownership of each shared Workspace to another member before deleting your account. Go to the Workspace settings to reassign ownership. | | **Pending invoices** | You have invoices in a pending or error state. | [Contact us](https://pdfmonkey.io/contact) to resolve the billing issue before proceeding. | | **Existing invoices** | You have past invoices on file (already paid). | Check the confirmation box to acknowledge that you have downloaded all invoices you need. Once your account is deleted, invoices are no longer accessible. | If you have no shared Workspaces and no pending invoices, the process goes straight to the confirmation step. > [!WARNING] > **Resolve blockers first** > > You cannot delete your account while you own shared Workspaces or have pending invoices. Both must be resolved before the final confirmation button becomes active. ## Confirming the deletion Once all checks pass (green checkmarks), click **I am sure, delete my account** to confirm. PDFMonkey immediately: - Cancels your subscription and archives your Stripe customer record - Deletes your user account and all associated data - Signs you out of the Dashboard > [!CAUTION] > **Deletion is permanent** > > There is no way to undo account deletion. All your Templates, Documents, generated files, and account settings are permanently removed. Make sure you have downloaded any files or invoices you need before confirming. ## What gets deleted When your account is deleted, PDFMonkey removes: - **Your user profile** (email, name, company information) - **All Workspaces you own** (along with their Templates and Documents) - **All generated files** (PDFs, images) stored on PDFMonkey servers - **Your Stripe subscription and customer record** Shared resources like community snippets and suggestions that you contributed are reassigned to a placeholder account rather than deleted. > [!NOTE] > Workspaces where you are a collaborator (not the owner) are not affected. Only the Workspaces you own are removed with your account. ## Before you go > [!TIP] > **Help us improve** > > If you are leaving because PDFMonkey did not meet your needs, we would genuinely appreciate hearing why. [Send us a message](https://pdfmonkey.io/contact) with your feedback. We read every response and use it to shape the product. ## Related pages - [Data Storage and Retention](https://pdfmonkey.io/docs/account-and-security/data-storage-and-retention.md) -- what data PDFMonkey keeps and for how long - [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) -- how your data is protected while your account is active - [Document Retention and Automatic Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- TTL-based document cleanup - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) -- subscription details and plan comparison --- # How to Reset or Change Your PDFMonkey Password Source: https://pdfmonkey.io/docs/account-and-security/changing-password.md PDFMonkey does not have a password change option inside the Dashboard. To change or reset your password, use the password reset flow from the [Sign in page](https://dashboard.pdfmonkey.io/login). ## How to reset your password 1. Go to the [Sign in page](https://dashboard.pdfmonkey.io/login). 2. Click **Forgot your password?** below the sign-in button. 3. Enter the email address associated with your account and submit the form. 4. Check your inbox for an email from PDFMonkey containing a password reset link. 5. Click the link in the email. It opens a page where you can set a new password. 6. Enter your new password and submit. You are redirected to the Sign in page. 7. Sign in with your new password. > [!WARNING] > **Reset link expiration** > > The password reset link expires after **6 hours**. If it has expired, go back to step 1 and request a new one. ## Password requirements Your new password must be between **6 and 128 characters**. There are no additional complexity requirements (uppercase, symbols, etc.). ## Social login accounts If you signed up using **Google**, **GitHub**, or **Microsoft**, you do not have a PDFMonkey password. You sign in through your social provider instead. The password reset flow does not apply to social login accounts. If you want to add a password to a social login account, [contact support](https://pdfmonkey.io/docs/getting-started/support.md) for assistance. ## Troubleshooting ### I did not receive the reset email - Check your spam or junk folder. - Make sure you entered the exact email address you used to register. - If you signed up via Google, GitHub, or Microsoft, you do not have a password-based account (see [Social login accounts](#social-login-accounts) above). - If you still cannot find the email, [contact support](https://pdfmonkey.io/docs/getting-started/support.md). ### The reset link is not working - The link may have expired. Reset links are valid for 6 hours. Request a new one from the [Sign in page](https://dashboard.pdfmonkey.io/login). - Make sure you are clicking the most recent reset link. If you requested multiple resets, only the last link is valid. ## Related pages - [Account Deletion](https://pdfmonkey.io/docs/account-and-security/account-deletion.md) -- how to permanently delete your PDFMonkey account - [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) -- how your data is protected - [Support](https://pdfmonkey.io/docs/getting-started/support.md) -- how to reach the PDFMonkey team --- # PDFMonkey Security: How Your Data Is Protected Source: https://pdfmonkey.io/docs/account-and-security/security-measures.md PDFMonkey is designed to keep your data safe at every stage: when you send it, while it is stored, and when it is no longer needed. This page covers the specific measures in place to protect your Templates, Documents, and dynamic data. ## Encryption in transit All communication with PDFMonkey is encrypted over HTTPS (TLS). The API enforces SSL on every request; plain HTTP connections are automatically redirected to HTTPS. This applies to: - API requests and responses - Dashboard and Builder sessions - Webhook deliveries to your server - Download URLs for generated files ## Encryption at rest The dynamic data you send to generate a Document (the `payload` and `meta` fields) is stored in an encrypted database column. It is decrypted on-the-fly only when needed for Document generation, and is never stored in plaintext. Generated files (PDFs and images) are stored in private S3 buckets. Access is controlled through short-lived presigned URLs that expire after one hour. ## Data isolation and access PDFMonkey processes your data with strict boundaries: | Principle | Details | |:----------|:--------| | **Purpose limitation** | Your data is used exclusively to generate the requested Document. It is never shared with partners, sold, or used for analytics. | | **No third-party analysis** | Your data is not analyzed by PDFMonkey or any external service. | | **Account isolation** | Each account's Templates, Documents, and generated files are isolated. You can only access resources that belong to your own Workspaces. | | **API authentication** | Every API request requires a secret API key sent as a [Bearer token](https://pdfmonkey.io/docs/api/authentication.md). Keys are scoped to your account. | > [!WARNING] > **Keep your API key secret** > > Your API secret key carries the same privileges as your account. Never share it publicly or commit it to version control. If your key is compromised, regenerate it from the API Key page in the Dashboard. ## European hosting PDFMonkey infrastructure is hosted on Amazon Web Services (AWS) in the **EU (Paris)** region. This includes: - The application servers - The database (with encrypted columns) - The S3 buckets storing generated files and large payloads ## Data deletion Your data is removed when you no longer need it: - **Manual deletion**: deleting a Document through the Dashboard or API permanently removes its payload, metadata, and generated file. - **Automatic deletion (TTL)**: each Template has a configurable retention period. When a Document's TTL expires, it is automatically deleted. See [Retention and Automatic Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) for details. - **Account deletion**: deleting your account permanently removes all Workspaces, Templates, Documents, and generated files. See [Account Deletion](https://pdfmonkey.io/docs/account-and-security/account-deletion.md). Temporary HTML pages generated during the rendering process are discarded immediately after generation. ## Webhook security When PDFMonkey sends webhook notifications to your server, the payload is delivered over HTTPS. You can verify the authenticity of webhook deliveries using signature verification. See [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) for the full setup guide. ## Related pages - [API Authentication](https://pdfmonkey.io/docs/api/authentication.md) -- how API requests are authenticated - [Data Storage and Retention](https://pdfmonkey.io/docs/account-and-security/data-storage-and-retention.md) -- what data PDFMonkey stores and for how long - [Retention and Automatic Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- TTL-based document cleanup - [Compliance and DPA](https://pdfmonkey.io/docs/account-and-security/compliance-and-dpa.md) -- GDPR alignment, data processing agreements, and sub-processor list - [Webhooks](https://pdfmonkey.io/docs/generating-documents/webhooks.md) -- webhook setup and signature verification ## FAQ **Is PDFMonkey secure?** Yes. PDFMonkey encrypts all data in transit over HTTPS (TLS) and at rest using encrypted database columns. Generated files are stored in private S3 buckets with presigned URLs that expire after one hour. All infrastructure is hosted on AWS in the EU (Paris) region. **Where is PDFMonkey data hosted?** All PDFMonkey infrastructure is hosted on Amazon Web Services (AWS) in the EU (Paris) region, including application servers, the database, and the S3 buckets that store generated files. **Does PDFMonkey share or analyze my data?** No. Your data is used exclusively to generate the requested document. It is never shared with partners, sold, or analyzed by PDFMonkey or any third-party service. --- # PDFMonkey Data Storage and Retention Policy Source: https://pdfmonkey.io/docs/account-and-security/data-storage-and-retention.md PDFMonkey stores only the data needed to generate your documents and serve the resulting files. This page explains what is stored, where it lives, and when it is removed. ## What data PDFMonkey stores There are three categories of data involved in document generation: | Data | Description | Storage location | Lifetime | |:-----|:------------|:-----------------|:---------| | **Dynamic data** | The `payload` and `meta` fields you send when creating a Document | Database | Until the Document is deleted | | **Temporary HTML page** | The rendered HTML used to produce the final file | S3 (temporary) | Discarded after generation | | **Generated file** | The output file (PDF, PNG, WebP, or JPG) | S3 (private bucket) | Until the Document is deleted | ### Dynamic data (payload and meta) When you create a Document, you send a JSON `payload` containing the data for your Template, along with optional `meta` fields. PDFMonkey stores this data so you can review it later in the Dashboard or retrieve it through the API. This data is removed when the Document is deleted, whether manually or by automatic TTL expiration. > [!NOTE] > **Large payloads** > > If a payload exceeds the database column size limit, PDFMonkey stores it in a private S3 bucket instead. It is still accessible through the API in the same way and follows the same deletion rules. ### Temporary HTML page During generation, PDFMonkey renders your Template with the provided payload into a complete HTML page. This page is uploaded to a temporary S3 location and passed to the rendering engine. Once generation finishes (whether the Document succeeds or fails), the reference to this HTML page is discarded. The HTML file itself is cleaned up by an S3 lifecycle policy. The temporary HTML is never accessible through the API or Dashboard. ### Generated file The output file (PDF or image) is stored in a private S3 bucket. It is only accessible through short-lived presigned URLs that expire after one hour. Each time you request a download URL, PDFMonkey generates a fresh presigned URL. See [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) for details on accessing your generated files. ## Where data is hosted All PDFMonkey infrastructure runs on Amazon Web Services (AWS) in the **EU (Paris)** region. This includes: - Application servers - The database - S3 buckets storing generated files, large payloads, and temporary HTML pages For more details on encryption and access controls, see [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md). ## When data is deleted Your data is removed in three ways: ### Manual deletion You can delete a Document at any time through the [Dashboard](https://dashboard.pdfmonkey.io) or via the [API](https://pdfmonkey.io/docs/api/documents.md#delete-a-document). Deleting a Document permanently removes its dynamic data (`payload` and `meta`) and its generated file. ### Automatic deletion (TTL) Each Template has a configurable **TTL (Time To Live)** that determines how long its Documents are kept after successful generation. When a Document's TTL expires, PDFMonkey automatically deletes it. Available TTL values range from 5 minutes to unlimited, depending on your plan. See [Document Retention and Automatic Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) for the full TTL reference, including values by plan and how plan changes affect retention. ### Account deletion Deleting your PDFMonkey account permanently removes all Workspaces, Templates, Documents, and generated files associated with your account. See [Account Deletion](https://pdfmonkey.io/docs/account-and-security/account-deletion.md) for the full process. > [!CAUTION] > **Deletion is permanent** > > There is no way to recover deleted Documents, generated files, or dynamic data. If you need to keep files long-term, download them to your own storage before deletion occurs. ## Frequently asked questions **Is my data shared with third parties?** No. Your data is used exclusively to generate the Documents you request. It is never shared with partners, sold, or used for analytics. See [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) for the full data isolation policy. **Where can I find PDFMonkey's Data Processing Agreement?** PDFMonkey provides a DPA and a Data SubProcessing Agreement for organizations that require them. See [Compliance and DPA](https://pdfmonkey.io/docs/account-and-security/compliance-and-dpa.md) for download links and contact information. **How do I know when a Document will be automatically deleted?** The Document object does not expose an `expires_at` field directly. You can calculate it from the Template's `ttl` value and the Document's `updated_at` timestamp. See [Document Retention and Automatic Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md#how-ttl-works) for details. ## Related pages - [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) -- encryption, access controls, and data isolation - [Document Retention and Automatic Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- TTL configuration and plan-based limits - [The Download URL](https://pdfmonkey.io/docs/generating-documents/download-url.md) -- accessing generated files before they expire - [Account Deletion](https://pdfmonkey.io/docs/account-and-security/account-deletion.md) -- what happens when you delete your account - [Compliance and DPA](https://pdfmonkey.io/docs/account-and-security/compliance-and-dpa.md) -- data processing agreements - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) -- plan comparison including retention limits ## FAQ **What data does PDFMonkey store?** PDFMonkey stores three types of data: the dynamic data you send (payload and meta), a temporary HTML page used during rendering, and the generated file (PDF or image). The dynamic data and generated file persist until the document is deleted; the HTML page is discarded after generation. **Where is PDFMonkey data hosted?** All PDFMonkey infrastructure is hosted on Amazon Web Services (AWS) in the EU (Paris) region, including application servers, the database, and S3 storage buckets. **How long does PDFMonkey keep my documents?** Each template has a configurable TTL (Time To Live). When a document's TTL expires, PDFMonkey automatically deletes it along with its generated file and dynamic data. You can also delete documents manually at any time. TTL options range from 5 minutes to unlimited depending on your plan. **Can I delete my data from PDFMonkey?** Yes. You can delete individual documents through the Dashboard or API, configure automatic deletion via TTL, or delete your entire account to remove all data permanently. --- # PDFMonkey GDPR Compliance and Data Processing Agreement (DPA) Source: https://pdfmonkey.io/docs/account-and-security/compliance-and-dpa.md PDFMonkey aligns its data handling practices with GDPR requirements. This page covers the company's compliance posture, the available legal agreements, and the key privacy commitments built into the platform. ## Compliance posture PDFMonkey does not hold a formal compliance certification (such as SOC 2 or ISO 27001). As a small, focused company, pursuing these certifications is not currently feasible. Instead, PDFMonkey implements concrete [security measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) that protect your data at every stage: encryption in transit and at rest, EU-only hosting, strict access controls, and configurable data retention. > [!NOTE] > **No certification does not mean no security** > > The absence of a certification badge does not reflect the absence of security practices. PDFMonkey's technical and organizational measures are documented in the [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) page and formalized in the DPA below. ## GDPR data roles When you use PDFMonkey, the GDPR roles are defined as follows: | Role | Party | Responsibility | |:-----|:------|:---------------| | **Data Controller** | You (the User) | You determine what personal data is sent to PDFMonkey and for what purpose. | | **Data Processor** | PDFMonkey | PDFMonkey processes your data exclusively to generate the Documents you request, following your instructions. | PDFMonkey processes your data only for the purpose of providing the service. It is never analyzed, shared with partners, sold, or used for any other purpose. If you are yourself a Processor acting on behalf of a Controller (for example, generating documents for your own clients), PDFMonkey provides a separate **Data SubProcessing Agreement** (DsPA) that formalizes the Controller-Processor-SubProcessor chain. ## Data Processing Agreement (DPA) PDFMonkey provides two standard agreements covering data processing obligations under GDPR: | Document | Use case | Link | |:---------|:---------|:-----| | **Data Processing Agreement (DPA)** | You are the Controller; PDFMonkey is your Processor | [Download DPA (PDF)](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/documents/2023-03-01%20PDFMonkey%20DPA.pdf) | | **Data SubProcessing Agreement (DsPA)** | You are a Processor; PDFMonkey is a SubProcessor acting on your Controller's behalf | [Download DsPA (PDF)](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/documents/2023-03-01%20PDFMonkey%20DsPA.pdf) | Both agreements are dated March 1, 2023 and cover: - Definitions aligned with GDPR terminology - Data processing obligations and instructions - Security measures and confidentiality - Audit rights (once per year, 30 days' notice) - Breach notification procedures - International data transfer safeguards - Approved sub-processor list - Data deletion on termination > [!TIP] > **Need a custom agreement?** > > If your organization requires PDFMonkey to review and sign a custom data privacy agreement, [contact us](https://pdfmonkey.io/contact). DPA-related inquiries can also be sent directly to tinymonkey@pdfmonkey.io. ## Key GDPR commitments The following commitments are formalized in the DPA and reflected in PDFMonkey's technical implementation: ### EU-only data hosting All infrastructure runs on AWS in the **EU (Paris)** region. Your data does not leave the EEA unless a sub-processor requires it, in which case the transfer is protected by GDPR-compliant safeguards (adequacy decisions or standard contractual clauses). See [Security Measures: European hosting](https://pdfmonkey.io/docs/account-and-security/security-measures.md#european-hosting) for details. ### Purpose limitation PDFMonkey processes your data exclusively to generate the Documents you request. It is never analyzed by PDFMonkey or any external service, never shared with third parties, and never used for profiling or analytics. ### Data minimization and retention You control how long your data is stored. Each Template has a configurable [TTL (Time To Live)](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) that automatically deletes Documents after the retention period expires. You can also [delete Documents manually](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md#manual-deletion) at any time. See [Data Storage and Retention](https://pdfmonkey.io/docs/account-and-security/data-storage-and-retention.md) for the full picture of what is stored and when it is removed. ### Breach notification If PDFMonkey becomes aware of a personal data breach affecting your data, it notifies you without undue delay. The notification includes the nature and scope of the breach, a point of contact, likely consequences, and remediation measures taken or proposed. ### Data deletion on termination When you [delete your account](https://pdfmonkey.io/docs/account-and-security/account-deletion.md), PDFMonkey ceases all processing and permanently destroys all your data, including Templates, Documents, generated files, and dynamic data. ## Approved sub-processors PDFMonkey uses a limited set of sub-processors. The DPA requires PDFMonkey to notify you in writing before adding or replacing a sub-processor, giving you time to object. | Sub-processor | Purpose | |:--------------|:--------| | **Amazon Web Services** | Hosting and storage | | **Heroku** | Hosting | | **SendGrid** | Email delivery | | **Dropbox** | Invoice storage | | **Svix** | Webhook delivery | | **Zapier** | Integration platform | | **Rollbar** | Error logging | | **Sentry** | Error logging | | **Cloudflare** | DNS and network services | For full contact details and compliance links for each sub-processor, see Section 5 of the [DPA](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/documents/2023-03-01%20PDFMonkey%20DPA.pdf) or [DsPA](https://pdfmonkey-resources.s3-eu-west-3.amazonaws.com/documents/2023-03-01%20PDFMonkey%20DsPA.pdf). ## Frequently asked questions **Does PDFMonkey have SOC 2 or ISO 27001 certification?** No. PDFMonkey is a small company and does not currently hold formal compliance certifications. The [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) page describes the technical and organizational measures in place. **Can I audit PDFMonkey?** Yes. The DPA grants you the right to audit PDFMonkey's data processing practices once per year, during business hours, with at least 30 days' written notice. Audit costs are borne by you unless the audit reveals non-compliance on PDFMonkey's part. **What happens to my data if I cancel my subscription?** When you [delete your account](https://pdfmonkey.io/docs/account-and-security/account-deletion.md), PDFMonkey permanently deletes all your Workspaces, Templates, Documents, and generated files. See [Account Deletion](https://pdfmonkey.io/docs/account-and-security/account-deletion.md) for the full process. **Is my data transferred outside the EU?** PDFMonkey commits to processing data within the EEA. If a sub-processor requires a transfer outside the EEA, GDPR-compliant safeguards (adequacy decisions or standard contractual clauses) are in place. ## Related pages - [Security Measures](https://pdfmonkey.io/docs/account-and-security/security-measures.md) -- encryption, access controls, and data isolation - [Data Storage and Retention](https://pdfmonkey.io/docs/account-and-security/data-storage-and-retention.md) -- what data PDFMonkey stores and for how long - [Document Retention and Automatic Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- TTL configuration and plan-based limits - [Account Deletion](https://pdfmonkey.io/docs/account-and-security/account-deletion.md) -- what happens when you delete your account - [Support](https://pdfmonkey.io/docs/getting-started/support.md) -- how to reach the PDFMonkey team ## FAQ **Is PDFMonkey GDPR compliant?** PDFMonkey follows GDPR principles: data is processed exclusively within the EU (AWS Paris region), encrypted in transit and at rest, never shared with third parties, and deleted when no longer needed. PDFMonkey acts as a data Processor on your behalf. **Does PDFMonkey provide a Data Processing Agreement (DPA)?** Yes. PDFMonkey provides a standard DPA and a Data SubProcessing Agreement (DsPA), both available for download. If your organization requires a custom data privacy agreement, you can contact PDFMonkey to arrange review and signing. **Where is PDFMonkey data hosted?** All PDFMonkey infrastructure is hosted on Amazon Web Services (AWS) in the EU (Paris) region. This includes application servers, the database, and S3 storage. Data is processed exclusively within the EEA. **What sub-processors does PDFMonkey use?** PDFMonkey uses a limited set of sub-processors: AWS (hosting and storage), Heroku (hosting), SendGrid (email), Dropbox (invoice storage), Svix and Zapier (integrations), Rollbar and Sentry (error logging), and Cloudflare (DNS). The full list is detailed in the DPA. --- # PDFMonkey Plans and Pricing: Free, Starter, Pro, Pro+, Premium Source: https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md PDFMonkey offers five plans to match different document generation volumes. Every new account starts with a [30-day Pro trial](https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md) so you can test premium features before choosing a plan. Visit [pdfmonkey.io/pricing](https://pdfmonkey.io/pricing) for current prices. ## Feature comparison {#feature-comparison} | Feature | Free | Starter | Pro | Pro+ | Premium | |:--------|:----:|:-------:|:---:|:----:|:-------:| | Documents per month | 20 | 300 | 3,000 | 5,000 | 60,000 | | [Document retention](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) | 1 day | 1 day | 7 days | Unlimited | Unlimited | | [External resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) | No | Yes | Yes | Yes | Yes | | Team members | 1 | 1 | 2 | Unlimited | Unlimited | | [Share links](https://pdfmonkey.io/docs/generating-documents/share-links.md) | No | No | No | Yes | Yes | | Max generation time | 30 sec | 30 sec | 2 min | 3 min | 5 min | | [Pay-as-you-go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md) | No | Yes | Yes | Yes | Yes | > [!TIP] > **Not sure which plan you need?** > > Start with the Free plan and upgrade any time. Plan changes take effect immediately and billing is prorated. See [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md). ## Plan details {#plan-details} ### Free The Free plan gives you 20 documents per month with a 30-second generation time limit. It is a good fit for testing PDFMonkey before committing to a paid plan. [External resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) (web fonts, remote images) are not available on this tier. ### Starter The Starter plan raises your quota to 300 documents per month and unlocks [external resources](https://pdfmonkey.io/docs/getting-started/external-resources.md). If you generate a small volume of documents and do not need team collaboration, Starter covers the basics. ### Pro The Pro plan supports up to 3,000 documents per month with [7-day document retention](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) and a 2-minute generation time limit. You can invite one additional team member, bringing the total to two seats. ### Pro+ The Pro+ plan provides 5,000 documents per month with unlimited document retention, unlimited team members, [share links](https://pdfmonkey.io/docs/generating-documents/share-links.md), and a 3-minute generation time limit. ### Premium The Premium plan handles up to 60,000 documents per month with all the features of Pro+, plus a 5-minute generation time limit. ## Annual billing {#annual-billing} All paid plans offer a 10% discount when you choose yearly billing. You can switch between monthly and annual billing at any time from the Dashboard. Check [pdfmonkey.io/pricing](https://pdfmonkey.io/pricing) for exact annual prices. ## Reaching your quota {#reaching-your-quota} When you reach 80% of your monthly quota, PDFMonkey sends you a warning email. At 100%, new generation requests are blocked and documents are set to **draft** [status](https://pdfmonkey.io/docs/generating-documents/document-statuses.md). You are not charged for blocked generations. Your quota resets automatically at the start of each billing cycle. If you need more documents before the reset, you have three options: - **Upgrade your plan:** move to a higher tier for a larger monthly quota. See [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md). - **Buy a Boost Pack:** purchase a one-time block of extra document credits. See [Boost Packs](https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md). - **Enable pay-as-you-go:** get billed automatically for each document beyond your quota (all paid plans). See [Pay-As-You-Go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md). ## Custom plans {#custom-plans} If you need more than 60,000 documents per month, [contact us](https://pdfmonkey.io/contact) to discuss a custom plan tailored to your volume. ## Frequently asked questions {#faq} **What happens to my documents when I downgrade?** Documents within the new plan's [retention window](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) are kept. Documents outside it are permanently deleted. Features exclusive to higher tiers (such as [share links](https://pdfmonkey.io/docs/generating-documents/share-links.md)) stop working until you upgrade again. **Can I generate images as well as PDFs?** Yes. All plans support PDF, PNG, WebP, and JPG output. See [Output Format](https://pdfmonkey.io/docs/generating-documents/output-format.md) for details. **Do unused documents roll over to the next month?** No. Your monthly quota resets to zero at the start of each billing cycle. Unused documents do not accumulate. **Is there a free trial?** Yes. Every new account gets a [30-day Pro trial](https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md) with 300 documents, external resources, and a 2-minute generation time limit. When the trial ends, your account moves to the Free plan automatically. ## Related pages {#related-pages} - [Boost Packs](https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md) -- one-time extra document credits - [Pay-As-You-Go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md) -- automatic per-document billing beyond your quota - [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) -- how upgrades, downgrades, and proration work - [30-Day Pro Trial](https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md) -- what the trial includes and what happens when it ends - [Document Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- TTL settings and plan-based retention limits - [Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md) -- permanent public URLs for generated documents - [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) -- web fonts, remote images, and external CSS/JS ## FAQ **How much does PDFMonkey cost?** PDFMonkey offers five plans: Free (20 documents/month), Starter (300/month), Pro (3,000/month), Pro+ (5,000/month), and Premium (60,000/month). All paid plans offer a 10% discount on annual billing. Every new account starts with a 30-day Pro trial. Visit pdfmonkey.io/pricing for current prices. **What happens when I reach my PDFMonkey document quota?** At 80% of your monthly quota, PDFMonkey sends a warning email. At 100%, new generation requests are blocked. You can upgrade your plan, buy a one-time Boost Pack, or enable pay-as-you-go billing to continue generating. **Do unused PDFMonkey documents roll over to the next month?** No. Your monthly quota resets to zero at the start of each billing cycle. Unused documents do not accumulate. **Does PDFMonkey have a free plan?** Yes. The Free plan gives you 20 documents per month with a 30-second generation time limit. External resources (web fonts, remote images) are not available on the free tier. --- # Boost Packs: Buy Extra Document Generation Credits Source: https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md Boost Packs are one-time purchases of additional document generation credits. Each Boost Pack adds 1,000 documents to your quota for **5 EUR** (excl. taxes). Credits are available immediately and last until the end of your current billing cycle. > [!TIP] > **Quick summary** > > Plan quota is consumed first, then Boost Pack credits. Unused Boost Pack credits expire when your subscription renews. ## Pricing and availability {#pricing} | Detail | | |:-------|:--| | **Price** | 5 EUR per Boost Pack (excl. taxes) | | **Credits per pack** | 1,000 documents | | **Minimum purchase** | 1 pack (1,000 documents) | | **Maximum purchase** | 1,000 packs (1,000,000 documents) per transaction | | **Available on** | All paid plans (Starter, Pro, Pro+, Premium) | | **Refunds** | Non-refundable | > [!WARNING] > Boost Packs are only available to paying customers. If you are on the [Free plan](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md#plan-details), you need to upgrade to a paid plan first. Visit [pdfmonkey.io/pricing](https://pdfmonkey.io/pricing) for the latest pricing details. ## Boost Packs vs. pay-as-you-go {#boost-packs-vs-payg} PDFMonkey offers two ways to handle overage. They are mutually exclusive: you must choose one or the other. | | Boost Packs | [Pay-as-you-go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md) | |---|---|---| | **Billing** | One-time, upfront charge | Automatic, per-document charge | | **Pricing** | 5 EUR per 1,000 documents (0.005 EUR/doc) | 0.005--0.008 EUR per document (varies by plan) | | **Minimum purchase** | 1,000 documents (1 pack) | None (billed per document used) | | **Availability** | All paid plans | All paid plans | | **Action needed** | Manual purchase in Dashboard | None (automatic once enabled) | | **Best for** | Predictable overage with a fixed budget | Unpredictable volume with no upfront commitment | > [!NOTE] > If you are on an annual plan, pay-as-you-go overage is still billed monthly for the extra usage. ## Credit consumption order {#consumption-order} When you generate a document, PDFMonkey draws from your credits in this order: 1. **Monthly plan quota** is consumed first. 2. **Boost Pack credits** are consumed next, once your plan quota is fully depleted. As long as your plan quota has remaining credits, your Boost Pack balance stays untouched. ## How to buy Boost Packs {#how-to-buy} 1. Open your Dashboard and go to the **Billing** section. 2. Scroll down to the **Boost Packs** area. 3. Use the **+** and **-** buttons to select the number of packs you need (each pack is 1,000 documents). 4. Check the agreement box to confirm the charge amount and terms. 5. Click **Give me a Boost Pack** (or **Give me X Boost Packs** for multiple). The purchase is charged immediately to your default payment method on file. Credits are added to your account as soon as the payment succeeds. > [!NOTE] > If you see an error during purchase, make sure your payment method is up to date in the Billing section. Contact support through the in-app chat if the issue persists. ## Expiry {#expiry} Boost Pack credits **expire at the end of your current billing cycle**. When your subscription renews, any unused Boost Pack credits are reset to zero and your quota reverts to your plan's base allowance. If you need credits that persist across billing cycles, consider upgrading to a higher [plan tier](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) instead. ## Frequently asked questions {#faq} **Can I buy Boost Packs on the Free plan?** No. Boost Packs require an active paid subscription (Starter, Pro, Pro+, or Premium). [Upgrade your plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) first. **Do unused Boost Pack credits roll over to the next billing cycle?** No. Unused credits are reset to zero when your subscription renews. Buy Boost Packs only when you need them within the current cycle. **Can I get a refund for unused Boost Pack credits?** No. Boost Packs are non-refundable. **Are Boost Pack credits shared across Workspaces?** Boost Pack credits are tied to the Workspaces you own and increase the document quota for those Workspaces. They do not apply to Workspaces owned by other accounts that you have been invited to. **What happens if my payment method fails?** The purchase is not completed and no credits are added. Update your payment method in the Billing section of the Dashboard and try again. ## Related pages {#related-pages} - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) -- compare plan tiers and monthly quotas - [Pay-As-You-Go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md) -- automatic per-document billing beyond your quota - [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) -- how upgrades, downgrades, and proration work - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) -- understand what happens when your quota is exhausted ## FAQ **What are PDFMonkey Boost Packs?** Boost Packs are one-time purchases of additional document generation credits. Each Boost Pack adds 1,000 documents to your quota for 5 EUR (excl. taxes). Credits are available immediately and last until the end of your current billing cycle. **Can I buy Boost Packs on the Free plan?** No. Boost Packs require an active paid subscription (Starter, Pro, Pro+, or Premium). You need to upgrade your plan first. **Do unused Boost Pack credits roll over?** No. Unused credits are reset to zero when your subscription renews. Buy Boost Packs only when you need them within the current billing cycle. **How much do Boost Packs cost?** Each Boost Pack costs 5 EUR (excl. taxes) and includes 1,000 document generation credits. You can purchase between 1 and 1,000 packs per transaction. --- # Pay-As-You-Go: Automatic Per-Document Billing on PDFMonkey Source: https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md Pay-as-you-go (PAYG) is an automatic billing mode that charges you for each document generated after your monthly quota is depleted. Instead of being blocked when your quota runs out, you keep generating documents and pay a small per-document fee. > [!TIP] > **Quick summary** > > PAYG has no minimum purchase and no upfront commitment. You only pay for documents generated beyond your monthly quota, billed automatically at your plan's per-document rate. ## Per-document rates {#per-document-rates} | Plan | Rate per document | |:-----|:------------------| | Starter | 0.008 EUR | | Pro | 0.007 EUR | | Pro+ | 0.006 EUR | | Premium | 0.005 EUR | These rates apply to every document generated beyond your monthly plan quota, whether it produces a PDF or an [image](https://pdfmonkey.io/docs/generating-documents/output-format.md). Visit [pdfmonkey.io/pricing](https://pdfmonkey.io/pricing) for the latest pricing details. ## Availability {#availability} Pay-as-you-go is available on all paid plans: **Starter**, **Pro**, **Pro+**, and **Premium**. It is not available on the Free plan. See [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md#feature-comparison) for a full feature comparison. ## How it works {#how-it-works} 1. Your **monthly plan quota** is consumed first. 2. Once your quota is depleted, each additional document is billed at the per-document rate for your plan. 3. Overage charges are calculated at the end of the billing period and added to your next invoice. PAYG applies to all Workspaces **you own**. It does not apply to Workspaces owned by other accounts that you have been invited to. > [!NOTE] > **If you are on an annual plan, PAYG overage is still billed monthly for the extra usage.** > > ## Enabling pay-as-you-go {#enabling} To enable or disable PAYG on your account, open the **Billing** section of your Dashboard and look for the pay-as-you-go toggle. If you do not see it, contact support through the in-app chat for assistance. Once enabled, PAYG activates automatically whenever your monthly quota runs out. You do not need to take any manual action each time your quota is depleted. ## Pay-as-you-go vs. Boost Packs {#payg-vs-boost-packs} PDFMonkey offers two ways to handle overage beyond your monthly quota. They are **mutually exclusive**: you must choose one or the other, not both. | | Pay-as-you-go | [Boost Packs](https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md) | |---|---|---| | **Billing** | Automatic, per-document charge | One-time, upfront charge | | **Pricing** | 0.005--0.008 EUR per document (varies by plan) | 5 EUR per 1,000 documents (0.005 EUR/doc) | | **Minimum purchase** | None (billed per document used) | 1,000 documents (1 pack) | | **Availability** | All paid plans | All paid plans | | **Action needed** | None (automatic once enabled) | Manual purchase in Dashboard | | **Expiry** | N/A (billed per use) | End of current billing cycle | | **Best for** | Unpredictable volume with no upfront commitment | Predictable overage with a fixed budget | > [!WARNING] > **You cannot have both PAYG and Boost Packs active at the same time. If you want to switch from one overage method to the other, disable the current one first.** > > ## Frequently asked questions {#faq} **Is there a minimum number of documents for PAYG?** No. PAYG has no minimum purchase. You are billed only for the documents you actually generate beyond your quota, even if that is a single document. **Does PAYG apply to all my Workspaces?** PAYG applies to Workspaces you own. If you have been invited to Workspaces owned by other accounts, your PAYG setting does not cover those Workspaces. **What happens if I downgrade to the Free plan?** PAYG is automatically disabled because it is not available on the Free plan. Any overage accrued before the downgrade is still billed. See [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) for details on how plan changes work. **Can I set a spending cap?** There is currently no built-in spending cap for PAYG. If you want to limit overage costs, consider using [Boost Packs](https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md) instead, which give you a fixed, known cost. **Does PAYG count image generation as well as PDFs?** Yes. Every document generated counts against your quota and toward PAYG billing, regardless of whether the output is a PDF or an [image](https://pdfmonkey.io/docs/generating-documents/output-format.md). ## Related pages {#related-pages} - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) -- compare plan tiers and monthly quotas - [Boost Packs](https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md) -- one-time extra document credits (mutually exclusive with PAYG) - [Changing My Plan](https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md) -- how upgrades, downgrades, and proration work - [Document Statuses](https://pdfmonkey.io/docs/generating-documents/document-statuses.md) -- understand what happens when your quota is exhausted - [Generate Images Instead of PDFs](https://pdfmonkey.io/docs/generating-documents/output-format.md) -- image output counts toward your quota ## FAQ **What is PDFMonkey pay-as-you-go?** Pay-as-you-go (PAYG) is an automatic billing mode that charges you for each document generated after your monthly quota is depleted. Instead of being blocked when your quota runs out, you keep generating documents and pay a small per-document fee. **Is there a minimum number of documents for pay-as-you-go?** No. PAYG has no minimum purchase. You are billed only for the documents you actually generate beyond your quota, even if that is a single document. **Can I set a spending cap on pay-as-you-go?** There is currently no built-in spending cap for PAYG. If you want to limit overage costs, consider using Boost Packs instead, which give you a fixed, known cost. **What is the per-document rate for pay-as-you-go?** The per-document rate depends on your plan: Starter at 0.008 EUR, Pro at 0.007 EUR, Pro+ at 0.006 EUR, and Premium at 0.005 EUR. These rates apply to every document generated beyond your monthly plan quota. --- # Changing Your PDFMonkey Plan: Upgrades, Downgrades, and Billing Source: https://pdfmonkey.io/docs/pricing-and-billing/changing-my-plan.md You can change your PDFMonkey plan at any time from the Billing section of your Dashboard. Upgrades take effect immediately; downgrades apply at the start of your next billing cycle. ## How to change your plan {#how-to-change-your-plan} 1. Open your Dashboard and go to the **Billing** section. 2. Click on **Manage My Subscription** to open the billing portal. 3. Choose a new plan tier and confirm the change. For upgrades, PDFMonkey uses a dedicated upgrade flow that applies the change immediately after confirmation. For downgrades or cancellations, the Stripe billing portal lets you schedule the change for the end of your current billing cycle. ## Upgrading {#upgrading} When you upgrade to a higher plan, the change takes effect **immediately**. You get instant access to the new plan's features, higher document quota, and longer generation time limit. > [!NOTE] > **Prorated billing** > > You are only charged for the remainder of your current billing cycle at the new plan's rate. The unused portion of your old plan is credited toward the new charge. If you have specific questions about how proration is calculated, contact support through the in-app chat. ### What changes on upgrade - **Document quota** increases immediately to the new plan's allowance. - **Generation time limit** increases to the new plan's maximum (for example, 30 seconds on Free to 2 minutes on Pro). - **Document retention** is extended. Templates that were at the old plan's maximum TTL are automatically updated to the new plan's maximum. See [Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md). - **Features** unlock instantly. For example, upgrading to Pro+ or Premium activates [share links](https://pdfmonkey.io/docs/generating-documents/share-links.md) for all existing successful documents — you do not need to regenerate them. ## Downgrading {#downgrading} When you downgrade to a lower plan, the change is **scheduled for the start of your next billing cycle**. You keep your current plan's features and quota until the cycle ends. > [!WARNING] > **Feature and data impact** > > Downgrading can permanently affect your documents. Review the sections below before confirming a downgrade. ### What changes on downgrade - **Document quota** decreases to the new plan's allowance when the next billing cycle starts. - **Generation time limit** decreases. Documents that need more time than the new plan allows may fail at generation time. - **Document retention** is reduced. Templates with a TTL above the new plan's maximum are automatically lowered to that maximum, and existing documents' expiration dates are recalculated accordingly. Documents that fall outside the new retention window are permanently deleted and cannot be recovered. See [Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md). - **Share links** stop working if you move from Pro+ or Premium to a plan that does not include them (Pro, Starter, or Free). If you later upgrade back to Pro+ or Premium, share links reactivate for documents that still exist. See [Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md). - **External resources** (web fonts, remote images, external CSS/JS) stop working if you downgrade to the Free plan. Documents that reference them will fail at generation time. See [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md). - **Pay-as-you-go** is disabled automatically if you downgrade to the Free plan. Any overage accrued before the downgrade is still billed. See [Pay-As-You-Go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md). ## Switching billing frequency {#switching-billing-frequency} You can switch between monthly and yearly billing. Yearly billing saves you **10%** compared to the monthly rate. You can make this change at any time from the billing portal. When you switch from monthly to yearly, you are charged the annual price immediately (prorated for any remaining time on your current monthly cycle). Switching from yearly to monthly takes effect at the end of your current annual period. See [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) for details on available plan tiers, or visit [pdfmonkey.io/pricing](https://pdfmonkey.io/pricing) for current prices. ## Effect on overage methods {#effect-on-overage-methods} If you have [Boost Packs](https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md) or [Pay-As-You-Go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md) enabled, keep the following in mind: - **Boost Pack credits** are preserved when you change plans within the same billing cycle. They still expire at the end of the cycle, regardless of the plan change. - **Pay-as-you-go** remains active across paid plan changes. It is disabled automatically only if you move to the Free plan. - Both overage methods are unavailable on the Free plan. ## Frequently asked questions {#faq} **Is there a contract or minimum commitment?** No. All PDFMonkey plans are billed on a recurring basis with no long-term contract. You can upgrade, downgrade, or cancel at any time. **What happens to my documents when I downgrade?** Documents within the new plan's [retention window](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) are kept. Documents outside it are permanently deleted when the downgrade takes effect. Features exclusive to higher tiers (such as [share links](https://pdfmonkey.io/docs/generating-documents/share-links.md)) stop working until you upgrade again. **Can I switch plans during my 30-day trial?** Yes. You can [upgrade to any paid plan](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) at any point during the trial. The change takes effect immediately. See [30-Day Pro Trial](https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md) for details on what the trial includes. **Will I lose my templates if I downgrade?** No. Your templates are never deleted when you change plans. Only generated documents are affected by retention changes. **How is proration calculated?** Proration is handled by Stripe. When you upgrade mid-cycle, you are credited for the unused time on your old plan and charged the proportional cost of the new plan for the remaining days in the cycle. The exact amounts appear as line items on your next invoice. ## Related pages {#related-pages} - [Our Plans](https://pdfmonkey.io/docs/pricing-and-billing/our-plans.md) -- compare plan tiers, quotas, and features - [Boost Packs](https://pdfmonkey.io/docs/pricing-and-billing/boost-packs.md) -- one-time extra document credits - [Pay-As-You-Go](https://pdfmonkey.io/docs/pricing-and-billing/pay-as-you-go.md) -- automatic per-document billing beyond your quota - [30-Day Pro Trial](https://pdfmonkey.io/docs/getting-started/the-30-day-trial.md) -- what the trial includes and what happens when it ends - [Share Links](https://pdfmonkey.io/docs/generating-documents/share-links.md) -- permanent public URLs, and how plan changes affect them - [Document Retention and Deletion](https://pdfmonkey.io/docs/generating-documents/retention-and-deletion.md) -- TTL settings and plan-based retention limits - [External Resources](https://pdfmonkey.io/docs/getting-started/external-resources.md) -- what counts as an external resource and free-plan restrictions ## FAQ **Is there a contract or minimum commitment on PDFMonkey?** No. All PDFMonkey plans are billed on a recurring basis with no long-term contract. You can upgrade, downgrade, or cancel at any time. **What happens to my documents when I downgrade my PDFMonkey plan?** Documents within the new plan's retention window are kept. Documents outside it are permanently deleted when the downgrade takes effect. Features exclusive to higher tiers, such as share links, stop working until you upgrade again. **Will I lose my templates if I downgrade?** No. Your templates are never deleted when you change plans. Only generated documents are affected by retention changes. **How is proration calculated when upgrading a PDFMonkey plan?** Proration is handled by Stripe. When you upgrade mid-cycle, you are credited for the unused time on your old plan and charged the proportional cost of the new plan for the remaining days in the cycle. The exact amounts appear as line items on your next invoice. --- # Why Is My PDFMonkey Document Blank? Source: https://pdfmonkey.io/docs/troubleshooting/document-is-blank.md A blank document usually means PDFMonkey generated the document successfully, but the output contains no visible content. The most common cause is an unpublished template. ## The template is not published {#template-not-published} This is by far the most frequent cause. PDFMonkey templates have two versions of their content: a **draft** (what you edit) and a **published** version (what PDFMonkey uses to generate documents). If you have never clicked **Publish**, the published content is empty and every document generated from that template comes out blank. ### How to fix it {#fix-unpublished-template} 1. Open your template in the editor (Dashboard or Builder). 2. Click the **Publish** button. 3. Generate the document again. > [!WARNING] > **Publish after every change** > > Clicking **Publish** copies the current draft to the published version. If you edit your template and forget to publish again, documents continue to use the old published content. Always publish after making changes you want to appear in generated documents. For a walkthrough of the full template-to-document workflow, see [From Zero to Your First Document](https://pdfmonkey.io/docs/getting-started/from-zero-to-first-document.md). ## Conditional logic hides all content {#conditional-logic} If your template wraps content in conditional blocks, the output can be blank when the conditions are not met. **Code templates** use Liquid syntax: ```liquid {% if items.size > 0 %} {% endif %} ``` If `items` is missing from the payload or is an empty array, nothing renders. **Builder templates** use visibility rules on components. If a visibility condition evaluates to false for every component, the page appears empty. ### How to fix it {#fix-conditional-logic} - Compare the variable names in your conditions against the keys in your [payload](https://pdfmonkey.io/docs/code-templates/defining-dynamic-data.md). A typo or casing mismatch (`Items` vs `items`) causes the condition to fail silently. - Test with the payload you are actually sending, not just the template's sample data. - Temporarily remove conditions to confirm the content renders without them, then add them back one at a time. ## A JavaScript or expression error in a Builder template {#builder-js-error} Builder templates can break silently when they contain a runtime error. Two common scenarios: - **External JavaScript libraries or custom code in HTML blocks.** If a script throws an uncaught error, rendering can stop and produce a blank document. - **Syntax errors in conditions or repetitions.** A typo in a visibility condition or a repetition expression (for example a missing closing parenthesis or a misspelled variable) prevents the component from evaluating, which can result in an empty output. ### How to fix it {#fix-builder-js-error} 1. Open the template in the Builder and check the **preview**. If the preview itself is blank or broken, the error is in the template logic. 2. Look at any **HTML blocks** that include `