Skip to content

How to automate PDF generation: the four patterns and what breaks in each

Most teams go shopping for a renderer first. That is the wrong first question. There are four ways a document gets generated automatically, they fail in different ways, and picking the pattern before the vendor is what keeps a document pipeline from quietly dropping files on its first busy day.

Almost every team arrives at PDF automation the same way. Someone is producing documents by hand, it works fine at ten a week, and then it does not work at all at four hundred. The instinct at that point is to go shopping for a renderer. That is the wrong first question. The renderer is the easy part, and picking one before you know what fires it is how projects end up with a queue that silently drops documents on the first busy day.

There are four ways a document gets generated automatically, and they fail differently. Get the pattern right and the rest of the build is mechanical.

The short version

To automate PDF generation, first build the document as a web page in your application, then pick the trigger pattern that matches when the document is needed: on-event for receipts and confirmations, on-demand for user downloads, batch for month-end runs, and scheduled for recurring reports. Call a rendering API against that page URL from a background job, never from the request that the user is waiting on, and store the result before you deliver it. The template lives in your repository, the trigger lives in your job system, and the render is a single HTTP call.

Build the document as a web page first

Before any of the trigger patterns matter, get this one right. Make the document a real URL in your app, something like /invoices/8812/print, and let the PDF be a rendering of that page.

It sounds like extra work and it removes work. You develop the layout in a browser with devtools instead of regenerating a binary after every change. Your existing CSS, components and fonts apply unchanged. Customers who want a link get one for free. And when a number looks wrong, you are debugging a web page rather than a PDF.

The alternative, assembling documents with a drawing library, means writing coordinates and page-break logic by hand for something a browser already does. The choice between rendering your own HTML and merging a Word template is a real one with real trade-offs, and the document generation API comparison lays out which teams should pick which. But if you are automating documents your product already displays, rendering the page you already have is almost always the shorter road.

Pattern 1: on-event, when something happens in your system

A checkout completes, a subscription renews, a shipment goes out. The document is a consequence of a state change, and it needs to exist within seconds.

The mistake here is rendering inside the request or the webhook handler. A render takes somewhere between a few hundred milliseconds and several seconds depending on the page, and neither Stripe nor your user is going to wait politely for it. Emit an event, queue a job, render in the worker.

The second mistake is not making it idempotent. Payment webhooks get delivered more than once, and a retry that generates and emails a second copy of the same invoice looks worse than no automation at all. Key the job on the business object, not on the event: if a PDF already exists for invoice 8812, return that one.

Pattern 2: on-demand, when a user clicks Download

Someone wants the document right now. This is the only pattern where a user is genuinely waiting, and it is the one where caching pays for itself immediately.

Most on-demand documents are requested more than once and change rarely or never. An invoice from March is not going to change. Render it once, put it in object storage, and serve subsequent requests from there. Teams that skip this end up paying a rendering vendor repeatedly for identical output, which is a real line item once you work out what your actual cost per thousand documents is.

If the document genuinely cannot be pre-rendered, render asynchronously and show progress rather than holding an HTTP connection open. A spinner that resolves into a download link is a better experience than a request that dies at the 30 second gateway timeout.

Pattern 3: batch, when month-end arrives

Forty thousand statements on the first of the month. This is the pattern that breaks hardest, because everything works fine in testing with five documents.

Fan out one job per document rather than looping inside a single job. A serial loop over 40,000 renders will hit a job timeout somewhere in the middle, and the retry starts from the beginning. One job per document gives you per-document retries, visible progress, and bounded concurrency you can actually tune.

Bound that concurrency deliberately. Rendering is memory-hungry, and the natural instinct to run everything at once will either exhaust your own workers or hit a rate limit at your vendor. The specifics of running a large run safely, including retries and how to avoid stampeding your own database, are covered in bulk HTML to PDF.

Batch runs are also where document workflows stop being purely an engineering concern. Once statements go out automatically, the questions arrive from the people who answer for them: the back-office team that owns billing and document workflows is usually the group that discovers a broken template first, because a customer tells them.

Pattern 4: scheduled, when it happens every Monday

Recurring reports look like batch but behave differently, because the hard part is not volume, it is data readiness and time zones. A report that runs at midnight UTC before the overnight load has finished renders perfectly and shows the wrong numbers, which is worse than failing.

Make the report page take an explicit date range as a parameter rather than inferring one from the current time, so a failed run can be re-executed for that exact period later. The full pipeline, including how to render a page that requires authentication without embedding a long-lived key, is in the guide to scheduled PDF reports.

Which pattern should you use?

Most products end up running two or three of these. Match the pattern to when the document is needed, not to which one you built first.

PatternTypical documentLatency budgetWhat breaks first
On-eventReceipt, order confirmation, renewal invoiceSecondsDuplicate documents from retried webhooks
On-demandInvoice download, exported reportUnder a few seconds, a user is waitingGateway timeouts, and paying to re-render identical files
BatchMonthly statements, year-end tax documentsHoursOne serial job timing out halfway through
ScheduledWeekly or monthly reportsHoursRunning before the data has landed, and time zone drift

How do I automate PDF generation without writing code?

If the trigger already lives in a no-code tool, you do not need a service of your own. Zapier, Make and n8n can all call a rendering API as a step, take the returned file, and push it into Google Drive, Slack or an email. That covers a surprising amount of real business automation, and the practical setup for each is in generating PDFs from Zapier, Make and n8n. The limit you will hit is volume and error handling: these platforms bill per task and their retry behavior is coarse, so a few hundred documents a month is comfortable and forty thousand is not.

What should you store, and for how long?

This is the step teams skip, and the one they regret. Automating generation means documents go out without a human ever looking at them, so when a customer disputes an invoice in March, nobody on your team remembers what it said.

Storing the rendered PDF is the minimum. Storing what was rendered is better: the source page as it existed at that moment, timestamped, so you can answer what the document said rather than what it would say if you regenerated it today with current data. Regenerating is not the same answer, because your data has moved since. That is the reasoning behind making archive=true part of the same render call rather than a separate storage product you remember to wire up later.

Decide retention deliberately rather than by default. Financial documents usually have a statutory retention period, marketing exports usually have none, and paying to keep everything forever because nobody chose is a slow leak.

A build order that works

If you are starting from nothing, this sequence gets you to production without rework:

  1. Build the document as an authenticated page in your app, styled the way you want the PDF to look.
  2. Render it manually against a rendering API until the output is right. Page breaks and fonts are where the surprises live.
  3. Move the call into a background job, keyed idempotently on the business object.
  4. Add the trigger: an event listener, a scheduler entry, or a batch dispatcher.
  5. Store the result and the record of it before you deliver.
  6. Alert on failure. A document pipeline that fails silently is worse than one that does not exist, because everyone assumes it worked.

None of this is difficult. It just has to be done in an order where the expensive discoveries happen early, and step two is where nearly all of them are.

Written by the team building Sitepdf, an HTML to PDF API that archives every page it renders. The in-browser converter is free to try; early access locks the launch pricing.

§ Early access

Get on the early-access list

The API opens to the list first, in order. Early access locks the planned launch rates for 12 months. No card required, launching soon.

Render + archive, one API