<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DocuGenerate</title>
    <description>The latest articles on DEV Community by DocuGenerate (@docugenerate).</description>
    <link>https://dev.to/docugenerate</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4123459%2Ff3e347ca-c895-411b-acb0-09b979ea2eb8.png</url>
      <title>DEV Community: DocuGenerate</title>
      <link>https://dev.to/docugenerate</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/docugenerate"/>
    <language>en</language>
    <item>
      <title>Generate and Email Certificates of Participation with Pipedream</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Mon, 28 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/generate-and-email-certificates-of-participation-with-pipedream-1jei</link>
      <guid>https://dev.to/docugenerate/generate-and-email-certificates-of-participation-with-pipedream-1jei</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Events, workshops, and training programs usually end the same way: every participant expects a certificate, and someone on the organizing team has to produce and send them. Filling in names by hand, exporting each certificate to PDF, and attaching it to an individual email is slow work that doesn’t scale past a handful of attendees, and a single typo in a name is enough to make a certificate unusable. In this tutorial, we’ll build an automated workflow with &lt;a href="https://pipedream.com/" rel="noopener noreferrer"&gt;Pipedream&lt;/a&gt; and &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; that takes care of the whole process, from receiving the participant’s data to delivering the finished PDF to their inbox.&lt;/p&gt;

&lt;p&gt;The workflow has three steps. A webhook trigger receives the participant’s details as a JSON payload, the DocuGenerate &lt;strong&gt;Generate Document&lt;/strong&gt; action merges that data into a Word template to produce a PDF certificate, and the Gmail &lt;strong&gt;Send Email&lt;/strong&gt; action sends the certificate to the participant as an attachment. Because the entry point is a plain HTTP endpoint, the same workflow can be called from a registration platform, a form, a spreadsheet script, or your own backend. By the end of this guide, you’ll have a working workflow that you can copy into your own Pipedream account with a single click.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Certificate Template
&lt;/h2&gt;

&lt;p&gt;Before building the workflow, we need a certificate template in DocuGenerate. For this tutorial, we’ll use the &lt;a href="https://www.docugenerate.com/assets/posts/037-pipedream-certificates/Certificate%20Of%20Participation.docx" rel="noopener noreferrer"&gt;Certificate Of Participation&lt;/a&gt; Word template, a landscape certificate with an ornate gold border, a large script font for the recipient’s name, a wax seal in the center, and a signature block at the bottom right.&lt;/p&gt;

&lt;p&gt;Once you’ve &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-new-template/" rel="noopener noreferrer"&gt;uploaded the template&lt;/a&gt; to your DocuGenerate account, 6 &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; are detected automatically: &lt;code&gt;Full Name&lt;/code&gt;, &lt;code&gt;Event Name&lt;/code&gt;, &lt;code&gt;Organizer&lt;/code&gt;, &lt;code&gt;Certificate ID&lt;/code&gt;, &lt;code&gt;Signer Position&lt;/code&gt;, and &lt;code&gt;Signer Name&lt;/code&gt;. The &lt;code&gt;Full Name&lt;/code&gt; tag is displayed prominently in the middle of the certificate, &lt;code&gt;Event Name&lt;/code&gt; and &lt;code&gt;Organizer&lt;/code&gt; appear in the recognition text below it, and &lt;code&gt;Organizer&lt;/code&gt; is also printed inside the wax seal. The &lt;code&gt;Certificate ID&lt;/code&gt; sits next to the issuing date, while &lt;code&gt;Signer Name&lt;/code&gt; and &lt;code&gt;Signer Position&lt;/code&gt; fill the signature block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8r9mdo4c867q3d1xwfu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8r9mdo4c867q3d1xwfu8.png" alt="Certificate Of Participation template with 6 merge tags in DocuGenerate" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The issuing date next to the certificate ID doesn’t need a merge tag at all. It’s a &lt;code&gt;DATE&lt;/code&gt; field inserted directly in Word, so it’s filled in with the current date every time a certificate is generated. Our blog post on &lt;a href="https://www.docugenerate.com/blog/format-dates-in-templates-with-data-filters-and-automatic-fields/#todays-date" rel="noopener noreferrer"&gt;inserting today’s date in a template&lt;/a&gt; explains how to add this kind of field in Word or LibreOffice and choose its display format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Webhook Trigger
&lt;/h2&gt;

&lt;p&gt;With the template ready, we can create a new workflow in Pipedream. Every workflow starts with a trigger, which determines where the data comes from and when the workflow runs. The data could come from a spreadsheet, a database like Airtable or Notion, a form builder, or any other app Pipedream connects to. For this tutorial, we’ll use an &lt;a href="https://pipedream.com/docs/workflows/building-workflows/triggers/#http" rel="noopener noreferrer"&gt;HTTP trigger&lt;/a&gt; that listens for incoming requests on a unique URL, since it’s the most flexible option and works with any system that can send an HTTP request.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Add Trigger&lt;/strong&gt; and select the &lt;strong&gt;New HTTP / Webhook Requests&lt;/strong&gt; trigger. The default configuration is a good fit for this workflow, so you can leave the parameters as they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event Data&lt;/strong&gt; : &lt;code&gt;Full HTTP request&lt;/code&gt;, which exposes the body, headers, and other request details to the following steps via the &lt;code&gt;steps&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Response&lt;/strong&gt; : &lt;code&gt;Return HTTP 200 OK&lt;/code&gt;, so the caller receives an immediate confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domains&lt;/strong&gt; : &lt;code&gt;pipedream.net&lt;/code&gt;, the domain used for the generated trigger URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt; : &lt;code&gt;None&lt;/code&gt;, meaning any request sent to the URL is accepted. We’ll come back to this in the production section.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frdwj3c3gbvr8hl23z5hd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frdwj3c3gbvr8hl23z5hd.png" alt="Configuring the HTTP / Webhook trigger in Pipedream" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save and continue&lt;/strong&gt; to create the trigger. In the &lt;strong&gt;SELECT EVENT&lt;/strong&gt; section, Pipedream displays the unique URL generated for this workflow, which in our case is &lt;code&gt;https://eorgxgd03g7mu1b.m.pipedream.net&lt;/code&gt;. Any HTTP request sent to this URL will start the workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft1ultbdl6ocmgsliydas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft1ultbdl6ocmgsliydas.png" alt="The unique URL generated for the webhook trigger" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending a Test Event
&lt;/h3&gt;

&lt;p&gt;To configure the next steps, Pipedream needs a sample event so it can show the structure of the incoming data and offer it for autocompletion. Click &lt;strong&gt;Generate Test Event&lt;/strong&gt; , open the &lt;strong&gt;Body&lt;/strong&gt; tab, select &lt;code&gt;application/json&lt;/code&gt; as the &lt;strong&gt;Content-Type&lt;/strong&gt; , and enter the following payload in the &lt;strong&gt;Raw Request Body&lt;/strong&gt; field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Signer Position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Program Director"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Signer Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Margaret Ellison"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Certificate ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FLN-2025-00143"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Organizer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Future Learning Network"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Event Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"International Student Leadership Forum 2025"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Full Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Aisha Rahman"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aisha.rahman@example.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first six keys match the merge tags in the certificate template exactly. The &lt;code&gt;Email&lt;/code&gt; key isn’t used in the template, but we include it in the payload because the workflow needs to know where to send the certificate once it’s generated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hmbxl7aqtr4h4blsnef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5hmbxl7aqtr4h4blsnef.png" alt="Sending a test HTTP request with the participant's data" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Send HTTP Request&lt;/strong&gt; to submit the test event. The &lt;strong&gt;RESULTS&lt;/strong&gt; section confirms that the request was received and shows how the data is exported under &lt;code&gt;steps.trigger.event&lt;/code&gt;. The seven fields from our payload are available in the &lt;code&gt;body&lt;/code&gt; object, alongside other request details like the &lt;code&gt;headers&lt;/code&gt;, the &lt;code&gt;method&lt;/code&gt;, and the &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkuwgg7mw4z7sgl0p2cx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkuwgg7mw4z7sgl0p2cx.png" alt="Webhook trigger results showing the request body" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the Certificate
&lt;/h2&gt;

&lt;p&gt;Next, click the &lt;strong&gt;+&lt;/strong&gt; button below the trigger, search for DocuGenerate, and select the &lt;strong&gt;Generate Document&lt;/strong&gt; action from the &lt;a href="https://pipedream.com/apps/docugenerate" rel="noopener noreferrer"&gt;DocuGenerate App&lt;/a&gt;. The first time you use it, you’ll need to connect your DocuGenerate account with your &lt;a href="https://www.docugenerate.com/help/articles/where-can-i-find-my-api-key/" rel="noopener noreferrer"&gt;API Key&lt;/a&gt;. The &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-pipedream/#install-docugenerate" rel="noopener noreferrer"&gt;integration guide&lt;/a&gt; covers this setup in more detail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7sa2jlg182yfteo9vgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7sa2jlg182yfteo9vgz.png" alt="Configuring the Generate Document action" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the action with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DocuGenerate Account&lt;/strong&gt; : The account connected with your API Key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template&lt;/strong&gt; : The &lt;strong&gt;Certificate Of Participation&lt;/strong&gt; template uploaded in the template setup step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : &lt;code&gt;Certificate for {{steps.trigger.event.body["Full Name"]}}&lt;/code&gt;, which gives each document a name based on the participant, such as &lt;code&gt;Certificate for Aisha Rahman&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt; : &lt;code&gt;PDF (.pdf)&lt;/code&gt;, so the certificate can be attached to an email and opened on any device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; : An object mapping each merge tag to the corresponding value from the trigger, as explained below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the &lt;strong&gt;Data&lt;/strong&gt; parameter, select the &lt;strong&gt;Construct an object&lt;/strong&gt; option and add one row for each of the 6 merge tags. The key of each row is the merge tag name, and the value references the matching field in &lt;code&gt;steps.trigger.event.body&lt;/code&gt;. Keys that contain spaces must use bracket notation, as in &lt;code&gt;{{steps.trigger.event.body["Full Name"]}}&lt;/code&gt;, while single-word keys like &lt;code&gt;Organizer&lt;/code&gt; can use the shorter dot notation, &lt;code&gt;{{steps.trigger.event.body.Organizer}}&lt;/code&gt;. The &lt;code&gt;Email&lt;/code&gt; field is left out, since the template doesn’t use it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F31stcv43oodmfo6v01vo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F31stcv43oodmfo6v01vo.png" alt="Constructing the Data object from the webhook body" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Test&lt;/strong&gt; to run the action with the test event. The &lt;strong&gt;RESULTS&lt;/strong&gt; section confirms that the document was generated, and the exported &lt;code&gt;$return_value&lt;/code&gt; contains the details of the new document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Successfully generated the document S1AG7iucebUnYdv9RY73"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$return_value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1790522684107&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"template_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bjBXM4QZeg88Hf7ULwgl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Certificate for Aisha Rahman"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data_length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Certificate for Aisha Rahman.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"document_uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://firebasestorage.googleapis.com/v0/b/storage.us.docugenerate.com/o/documents%2FS1AG7iucebUnYdv9RY73%2FCertificate%20for%20Aisha%20Rahman.pdf?alt=media&amp;amp;token=81c6ffdb-282d-4a89-a2b9-e96542f6aee2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S1AG7iucebUnYdv9RY73"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two fields we’ll need in the next step are &lt;code&gt;filename&lt;/code&gt;, the name of the generated file, and &lt;code&gt;document_uri&lt;/code&gt;, a URL pointing to the generated PDF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgih0uh6ae2d5g16yxyd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgih0uh6ae2d5g16yxyd7.png" alt="Generate Document action results in Pipedream" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also verify the result in DocuGenerate. The &lt;strong&gt;Documents&lt;/strong&gt; tab of the &lt;strong&gt;Certificate Of Participation&lt;/strong&gt; template now lists a document named &lt;strong&gt;Certificate for Aisha Rahman&lt;/strong&gt; , with every merge tag replaced by the values from the test event, including the organizer’s name inside the seal and the signer’s details in the signature block. You can download the &lt;a href="https://www.docugenerate.com/assets/posts/037-pipedream-certificates/Certificate%20for%20Aisha%20Rahman.pdf" rel="noopener noreferrer"&gt;generated certificate&lt;/a&gt; to see the final PDF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jtmzqytoityl0mf9dat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jtmzqytoityl0mf9dat.png" alt="The generated certificate in DocuGenerate" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending the Certificate by Email
&lt;/h2&gt;

&lt;p&gt;The final step delivers the certificate to the participant. Click the &lt;strong&gt;+&lt;/strong&gt; button below the &lt;strong&gt;Generate Document&lt;/strong&gt; action and select the &lt;a href="https://pipedream.com/apps/gmail/actions/send-email" rel="noopener noreferrer"&gt;Send Email&lt;/a&gt; action from the &lt;a href="https://pipedream.com/apps/gmail" rel="noopener noreferrer"&gt;Gmail&lt;/a&gt; app, then connect the Gmail account the emails should be sent from. Pipedream supports many other email providers, but the Gmail action is complete and quick to set up, which makes it a good choice for this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8fu7qelgkgwu3cbmfo5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8fu7qelgkgwu3cbmfo5.png" alt="Configuring the Gmail Send Email action" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the action with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;To&lt;/strong&gt; : &lt;code&gt;{{steps.trigger.event.body.Email}}&lt;/code&gt;, the email address received in the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subject&lt;/strong&gt; : &lt;code&gt;Your certificate {{steps.trigger.event.body["Certificate ID"]}}&lt;/code&gt;, which includes the certificate ID so the participant can reference it later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email Body&lt;/strong&gt; : A short message personalized with the participant’s name, the event name, and the signer’s details (shown below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attachment Filenames&lt;/strong&gt; : &lt;code&gt;{{steps.generate_document.$return_value.filename}}&lt;/code&gt;, the filename returned by the Generate Document action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attachments&lt;/strong&gt; : &lt;code&gt;{{steps.generate_document.$return_value.document_uri}}&lt;/code&gt;, the URL of the generated PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the complete text used in the &lt;strong&gt;Email Body&lt;/strong&gt; field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight email"&gt;&lt;code&gt;&lt;span class="nt"&gt;Dear {{steps.trigger.event.body["Full Name"]}},

Thank you for taking part in {{steps.trigger.event.body["Event Name"]}}.

Please find your certificate of participation attached to this email.

We hope you enjoyed the experience and look forward to seeing you at future events.

Best regards,
{{steps.trigger.event.body["Signer Name"]}}
{{steps.trigger.event.body["Signer Position"]}}

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A convenient detail of the Gmail action is that the &lt;strong&gt;Attachments&lt;/strong&gt; field accepts public URLs as well as file paths. This means the &lt;code&gt;document_uri&lt;/code&gt; returned by DocuGenerate can be passed directly, without adding an intermediate step to download the PDF to temporary storage first. Click &lt;strong&gt;Test&lt;/strong&gt; to send the email for the test event. The participant receives a message with the subject &lt;strong&gt;Your certificate FLN-2025-00143&lt;/strong&gt; , the personalized body text, and the certificate attached as &lt;code&gt;Certificate for Aisha Rahman.pdf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2so1dj2trinfazr8bjyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2so1dj2trinfazr8bjyh.png" alt="The email received with the certificate attached" width="799" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Workflow End to End
&lt;/h2&gt;

&lt;p&gt;So far, each step has been tested individually using the sample event. To run the complete workflow the way it will run in practice, click &lt;strong&gt;Deploy&lt;/strong&gt; so the trigger URL starts processing live requests. Then send a real HTTP request to the trigger URL from outside Pipedream. If you’re not sure how to do this from your own system, the &lt;strong&gt;See Code Examples&lt;/strong&gt; button in the trigger’s &lt;strong&gt;SELECT EVENT&lt;/strong&gt; section provides ready-made snippets in several languages, including cURL, JavaScript, Node, Python, PHP, Go, and Ruby.&lt;/p&gt;

&lt;p&gt;We’ll use the following cURL command, which sends the same participant data as the test event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://eorgxgd03g7mu1b.m.pipedream.net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Signer Position": "Program Director",
    "Signer Name": "Margaret Ellison",
    "Certificate ID": "FLN-2025-00143",
    "Organizer": "Future Learning Network",
    "Event Name": "International Student Leadership Forum 2025",
    "Full Name": "Aisha Rahman",
    "Email": "aisha.rahman@example.com"
  }'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within a few seconds of sending the request, the workflow runs through all three steps: the trigger receives the payload, DocuGenerate generates the certificate, and Gmail sends it to the participant’s inbox. Each run is recorded in the workflow’s event history in Pipedream, where you can inspect the inputs and exports of every step if something doesn’t look right. To generate certificates for a whole list of participants, send one request per participant with their own details.&lt;/p&gt;

&lt;p&gt;If you’d like to try this workflow yourself, you can use this &lt;a href="https://pipedream.com/new?h=tch_lXfoja" rel="noopener noreferrer"&gt;share link&lt;/a&gt; to copy it into your own Pipedream account. Connected accounts are not included in &lt;a href="https://pipedream.com/docs/workflows/sharing" rel="noopener noreferrer"&gt;shared workflows&lt;/a&gt;, so you’ll need to connect your own DocuGenerate and Gmail accounts and select your own copy of the certificate template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking the Workflow to Production
&lt;/h2&gt;

&lt;p&gt;The workflow we’ve built is complete, but a couple of adjustments are worth making before relying on it for a real event. The most important one is the trigger’s &lt;strong&gt;Authorization&lt;/strong&gt; setting. With &lt;code&gt;None&lt;/code&gt;, anyone who knows the trigger URL can generate a certificate and have it emailed to any address. Pipedream lets you &lt;a href="https://pipedream.com/docs/workflows/building-workflows/triggers/#authorizing-http-requests" rel="noopener noreferrer"&gt;authorize HTTP requests&lt;/a&gt; with a custom token or OAuth, so that only requests from your own systems are accepted and all others are discarded.&lt;/p&gt;

&lt;p&gt;The second consideration is where the data comes from. Calling the webhook from a registration platform, a form builder, or your own backend works well when certificates should go out as soon as someone completes an event. If your participant list lives in a spreadsheet or a database instead, you can swap the HTTP trigger for a Pipedream trigger that fires on a new row in Google Sheets or a new record in a database. The Generate Document and Send Email steps stay the same, and only the references to &lt;code&gt;steps.trigger.event.body&lt;/code&gt; need to be updated to point at the new trigger’s data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we built a Pipedream workflow that turns a single HTTP request into a personalized PDF certificate delivered by email. The webhook trigger receives the participant’s details, the DocuGenerate &lt;strong&gt;Generate Document&lt;/strong&gt; action merges them into a Word template, and the Gmail &lt;strong&gt;Send Email&lt;/strong&gt; action attaches the PDF directly from its URL, with no intermediate download step and no manual work once the workflow is deployed.&lt;/p&gt;

&lt;p&gt;The same pattern works for any document that needs to reach a recipient right after it’s created, such as course completion certificates, event tickets, registration confirmations, or receipts. Replace the template, adjust the payload to match its merge tags, and update the email text, and the rest of the workflow remains unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/037-pipedream-certificates/Certificate%20Of%20Participation.docx" rel="noopener noreferrer"&gt;Certificate Of Participation&lt;/a&gt; Word template used in this tutorial.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.docugenerate.com/assets/posts/037-pipedream-certificates/Certificate%20for%20Aisha%20Rahman.pdf" rel="noopener noreferrer"&gt;sample generated PDF&lt;/a&gt; showing the final output.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://pipedream.com/new?h=tch_lXfoja" rel="noopener noreferrer"&gt;Certificate of Participation Generator&lt;/a&gt; link to copy this workflow in Pipedream.&lt;/li&gt;
&lt;li&gt;The DocuGenerate &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-pipedream/" rel="noopener noreferrer"&gt;integration guide&lt;/a&gt; for Pipedream.&lt;/li&gt;
&lt;li&gt;The official &lt;a href="https://pipedream.com/apps/docugenerate" rel="noopener noreferrer"&gt;DocuGenerate App&lt;/a&gt; for Pipedream.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://pipedream.com/apps/gmail/actions/send-email" rel="noopener noreferrer"&gt;Gmail Send Email&lt;/a&gt; action on Pipedream.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>certificates</category>
      <category>pipedream</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Create Salary Increase Letters with n8n and DocuGenerate</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Tue, 18 Aug 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/create-salary-increase-letters-with-n8n-and-docugenerate-3im7</link>
      <guid>https://dev.to/docugenerate/create-salary-increase-letters-with-n8n-and-docugenerate-3im7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Salary increase letters are one of those documents that need to go out to every affected employee at the same time, worded consistently, and free of copy-paste mistakes. When a review cycle covers dozens of employees, writing each letter by hand or manually merging a spreadsheet into a Word document quickly becomes a bottleneck for HR teams. In this tutorial, we’ll build an automated workflow using &lt;a href="https://n8n.io/" rel="noopener noreferrer"&gt;n8n&lt;/a&gt; and &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; that turns a Google Sheets spreadsheet of salary review data into personalized PDF letters, with no manual document creation involved.&lt;/p&gt;

&lt;p&gt;The workflow watches a Google Drive folder for a new spreadsheet. As soon as one appears, it reads every row, and for each employee it merges that row’s data into a Word template, generates a PDF, and uploads the finished letter to a dedicated output folder in Drive. HR only has to prepare the spreadsheet with that cycle’s raises; the letters are generated and filed automatically. By the end of this guide, you’ll have a fully functional n8n workflow that you can import and adapt to your own salary review process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Salary Increase Letter Template
&lt;/h2&gt;

&lt;p&gt;Before building the n8n workflow, we need a document template in DocuGenerate. We’ll use the &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Salary%20Increase%20Letter.docx" rel="noopener noreferrer"&gt;Salary Increase Letter&lt;/a&gt; Word template, a short remuneration review letter that informs an employee of their raise, the new total pay, and who to contact with questions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fov2da6ty9xe7p9dgj578.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fov2da6ty9xe7p9dgj578.png" alt="Salary Increase Letter template with merge tags" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-new-template/" rel="noopener noreferrer"&gt;uploaded the template&lt;/a&gt; to your DocuGenerate account, 13 merge tags are detected automatically: &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Employee First Name&lt;/code&gt;, &lt;code&gt;Employee Last Name&lt;/code&gt;, &lt;code&gt;Employee Address&lt;/code&gt;, &lt;code&gt;Increase Amount&lt;/code&gt;, &lt;code&gt;Remuneration Type&lt;/code&gt;, &lt;code&gt;Total Sum&lt;/code&gt;, &lt;code&gt;Increase Date&lt;/code&gt;, &lt;code&gt;Contact Name&lt;/code&gt;, &lt;code&gt;Contact Number&lt;/code&gt;, &lt;code&gt;Company Name&lt;/code&gt;, &lt;code&gt;Signatory Name&lt;/code&gt;, and &lt;code&gt;Signatory Position&lt;/code&gt;. These &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; cover both the employee-specific details of the raise and the fixed details of the company and signatory, all of which will come from the spreadsheet rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the Data in Google Sheets
&lt;/h2&gt;

&lt;p&gt;The data for this workflow lives in Google Sheets. For this tutorial, we’re using the sample &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Salary%20Increase%20Data.xlsx" rel="noopener noreferrer"&gt;Salary Increase Data&lt;/a&gt; Excel file, imported into a Google Sheets spreadsheet named &lt;strong&gt;Salary Increase August 2026&lt;/strong&gt;. Each of the 10 rows represents one employee’s raise, with a column for every merge tag in the template: name, address, increase amount, remuneration type, total sum, and the increase date, alongside the shared contact and signatory details repeated on every row.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Far38w06yb2ohw4cg8oyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Far38w06yb2ohw4cg8oyf.png" alt="Salary Increase August 2026 spreadsheet in Google Sheets" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This spreadsheet lives inside a &lt;strong&gt;Salary Increase Letter&lt;/strong&gt; folder in Google Drive, which also holds the folders of letters generated for past review cycles. Each new cycle starts the same way: a new spreadsheet with that cycle’s data is added to this folder, which is exactly the event our workflow will watch for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3n2d7r2bge6hxge3qj4b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3n2d7r2bge6hxge3qj4b.png" alt="Salary Increase Letter folder in Google Drive" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The n8n Workflow Overview
&lt;/h2&gt;

&lt;p&gt;With the template and the data source ready, here’s what the complete workflow looks like in n8n. You can download the &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Salary%20Increase%20Letter%20Workflow.json" rel="noopener noreferrer"&gt;workflow JSON file&lt;/a&gt; and import it directly into your n8n instance, then update the DocuGenerate template, the Google credentials, and the Drive folder ID to match your own setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4uamfosvkc0k31q4pxeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4uamfosvkc0k31q4pxeq.png" alt="The complete n8n workflow" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workflow begins with a trigger that watches the &lt;strong&gt;Salary Increase Letter&lt;/strong&gt; folder for a new Google Sheets file. When one appears, it creates a matching output folder, reads every row from the sheet, and loops through the rows one at a time, generating a PDF letter for each employee and uploading it to the new folder. Let’s walk through each node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching for New Spreadsheets with the Google Drive Trigger
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.googledrivetrigger/" rel="noopener noreferrer"&gt;Google Drive Trigger&lt;/a&gt; node polls a specific Google Drive folder and starts the workflow when a matching change occurs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwep3mtexggdwqqtvyqp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwep3mtexggdwqqtvyqp9.png" alt="Google Drive Trigger configuration" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The node is set to poll &lt;strong&gt;Every X&lt;/strong&gt; &lt;code&gt;30&lt;/code&gt; &lt;strong&gt;Minutes&lt;/strong&gt; , with &lt;strong&gt;Trigger On&lt;/strong&gt; set to &lt;strong&gt;Changes Involving a Specific Folder&lt;/strong&gt;. The &lt;strong&gt;Folder&lt;/strong&gt; parameter accepts the target folder in three ways: &lt;strong&gt;From list&lt;/strong&gt; , &lt;strong&gt;By URL&lt;/strong&gt; , or &lt;strong&gt;By ID&lt;/strong&gt; ; here it’s set to &lt;strong&gt;By ID&lt;/strong&gt; with the ID of the &lt;strong&gt;Salary Increase Letter&lt;/strong&gt; folder.&lt;/p&gt;

&lt;p&gt;You can find a folder’s ID in its Google Drive URL, it’s the string of characters after &lt;code&gt;/folders/&lt;/code&gt;. For example, the ID for the URL &lt;code&gt;https://drive.google.com/drive/folders/1dcLLzfZaH38hDd0acfUru2ISOoTsbGPI&lt;/code&gt; is &lt;code&gt;1dcLLzfZaH38hDd0acfUru2ISOoTsbGPI&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;Watch For&lt;/strong&gt; is set to &lt;strong&gt;File Created&lt;/strong&gt; , and the &lt;strong&gt;File Type&lt;/strong&gt; option under &lt;strong&gt;Options&lt;/strong&gt; is restricted to &lt;strong&gt;Google Spreadsheets&lt;/strong&gt; , so the trigger only fires when a new spreadsheet is added to the folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an Output Folder
&lt;/h2&gt;

&lt;p&gt;Once a new spreadsheet is detected, the workflow needs somewhere to put the generated letters. The &lt;a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledrive/" rel="noopener noreferrer"&gt;Google Drive&lt;/a&gt; node’s &lt;strong&gt;Create&lt;/strong&gt; operation on the &lt;strong&gt;Folder&lt;/strong&gt; resource handles this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpwlf3k6mqmf0bbfosrs8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpwlf3k6mqmf0bbfosrs8.png" alt="Create folder node configuration" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Folder Name&lt;/strong&gt; parameter uses the expression &lt;code&gt;{{ $('Google Drive Trigger').item.json.name }}&lt;/code&gt;, which resolves to &lt;code&gt;Salary Increase August 2026&lt;/code&gt;, the name of the spreadsheet that triggered the workflow. The &lt;strong&gt;Parent Folder&lt;/strong&gt; is set to the same folder ID used in the trigger, so the new output folder is created as a sibling of the source spreadsheet inside &lt;strong&gt;Salary Increase Letter&lt;/strong&gt;. Naming the folder after its source spreadsheet keeps each review cycle’s letters grouped with the data that produced them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the Employee Data
&lt;/h2&gt;

&lt;p&gt;With the destination folder ready, the &lt;a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets/" rel="noopener noreferrer"&gt;Google Sheets&lt;/a&gt; node’s &lt;strong&gt;Get Row(s)&lt;/strong&gt; operation on the &lt;strong&gt;Sheet Within Document&lt;/strong&gt; resource reads the employee data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faquwbqnju9s92rcyt916.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faquwbqnju9s92rcyt916.png" alt="Get row(s) in sheet node configuration" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Document&lt;/strong&gt; is set to &lt;strong&gt;Salary Increase August 2026&lt;/strong&gt; and the &lt;strong&gt;Sheet&lt;/strong&gt; to &lt;strong&gt;Salary Increase Data&lt;/strong&gt; , both picked from the &lt;strong&gt;From list&lt;/strong&gt; dropdowns. The node returns all 10 rows as separate items, each containing the full set of fields we saw in the spreadsheet: employee details, raise figures, and the shared contact and signatory information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looping Through Each Employee
&lt;/h2&gt;

&lt;p&gt;To generate one letter per employee, the workflow needs to process the 10 rows one at a time. The &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.splitinbatches/" rel="noopener noreferrer"&gt;Loop Over Items&lt;/a&gt; node handles this with a batch size of 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqlerxdn8z4nlfbl5ev61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqlerxdn8z4nlfbl5ev61.png" alt="Loop Over Items node configuration" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each pass through the loop carries a single employee’s row, such as the first one for James Mitchell with a 4% increase bringing his annual salary to $87,500, into the nodes that follow. The loop continues until all 10 rows have been processed, generating and saving one letter before moving on to the next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the Letter
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Generate document&lt;/strong&gt; node uses the &lt;a href="https://n8n.io/integrations/docugenerate/" rel="noopener noreferrer"&gt;DocuGenerate integration&lt;/a&gt; for n8n to merge each row’s data with the Word template. To use this node, install DocuGenerate in your n8n instance and &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-n8n/#install-docugenerate" rel="noopener noreferrer"&gt;configure it&lt;/a&gt; with your &lt;a href="https://www.docugenerate.com/help/articles/where-can-i-find-my-api-key/" rel="noopener noreferrer"&gt;API Key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxxjug4xl8sdxo5f8fbx0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxxjug4xl8sdxo5f8fbx0.png" alt="Generate document node configuration" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The node is configured with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template Name or ID&lt;/strong&gt; : The &lt;strong&gt;Salary Increase Letter&lt;/strong&gt; template uploaded earlier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : An expression, &lt;code&gt;Letter for {{ $json['Employee First Name'] }} {{ $json['Employee Last Name'] }}&lt;/code&gt;, which produces a name like &lt;code&gt;Letter for James Mitchell&lt;/code&gt; for the first row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt; : Set to &lt;code&gt;PDF (.pdf)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; : Set to &lt;code&gt;{{ $json }}&lt;/code&gt;, passing the entire current row, with all 13 merge tag values, straight to the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the first row, this produces the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1774348731006&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"template_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ARnboMDE3zyzMrdTZJ2e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Letter for James Mitchell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data_length"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Letter for James Mitchell.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"document_uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://firebasestorage.googleapis.com/v0/b/storage.us.docugenerate.com/o/documents%2FA1blGMwZETC4hrP5Zr2C%2FLetter%20for%20James%20Mitchell.pdf?alt=media&amp;amp;token=6383b541-f2b9-4c79-8161-063d287a5887"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A1blGMwZETC4hrP5Zr2C"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the generated document below, or download the &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Letter%20for%20James%20Mitchell.pdf" rel="noopener noreferrer"&gt;sample PDF&lt;/a&gt; directly.&lt;/p&gt;



&lt;h2&gt;
  
  
  Downloading the Generated Letter
&lt;/h2&gt;

&lt;p&gt;DocuGenerate returns a &lt;code&gt;document_uri&lt;/code&gt; pointing to the generated PDF rather than the file itself, so the workflow needs to fetch it before it can be uploaded elsewhere. The &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/" rel="noopener noreferrer"&gt;HTTP Request&lt;/a&gt; node makes a &lt;code&gt;GET&lt;/code&gt; request to that URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4qvrfwtval8vsnwg4oat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4qvrfwtval8vsnwg4oat.png" alt="HTTP Request node configuration" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;URL&lt;/strong&gt; parameter is set to &lt;code&gt;{{ $json.document_uri }}&lt;/code&gt;, referencing the Generate document node’s response. &lt;strong&gt;Authentication&lt;/strong&gt; is left as &lt;code&gt;None&lt;/code&gt;, since the document URI already includes an access token. The node’s output holds the file’s binary content in a &lt;code&gt;data&lt;/code&gt; field, ready for the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving the Letter to Google Drive
&lt;/h2&gt;

&lt;p&gt;The final node uploads the PDF to the output folder created at the start of the run, using the Google Drive node’s &lt;strong&gt;Upload&lt;/strong&gt; operation on the &lt;strong&gt;File&lt;/strong&gt; resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4jvhf5j48j5aj2bqvot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4jvhf5j48j5aj2bqvot.png" alt="Upload file node configuration" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Input Data Field Name&lt;/strong&gt; is set to &lt;code&gt;data&lt;/code&gt;, matching the binary field produced by the HTTP Request node. The &lt;strong&gt;File Name&lt;/strong&gt; field is left empty, so the upload keeps the original filename carried in the binary data, &lt;code&gt;Letter for James Mitchell.pdf&lt;/code&gt; for this row. The &lt;strong&gt;Parent Folder&lt;/strong&gt; uses the expression &lt;code&gt;{{ $('Create folder').item.json.id }}&lt;/code&gt;, so every letter lands in the folder created for this review cycle rather than loose in Drive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Workflow
&lt;/h2&gt;

&lt;p&gt;With every node configured, adding a new spreadsheet to the &lt;strong&gt;Salary Increase Letter&lt;/strong&gt; folder is all it takes to generate a full batch of letters. The animation below shows a full run: each node turns green with a checkmark as it completes, and the Loop Over Items segment runs 10 times in a row, once for every employee in the spreadsheet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F036-n8n-salary-increase-letters%2Fn8n-workflow-testing.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F036-n8n-salary-increase-letters%2Fn8n-workflow-testing.gif" alt="n8n workflow running end to end" width="479" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the run finishes, all 10 letters are sitting in the output folder in Google Drive, each named after its recipient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3oufufehnfsjtancwitl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3oufufehnfsjtancwitl.png" alt="Generated letters in the Salary Increase August 2026 folder" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we built an n8n workflow that turns a Google Sheets spreadsheet of salary review data into a folder of personalized PDF letters, with no manual document creation involved. The workflow watches a Drive folder for a new spreadsheet, creates a matching output folder, loops through every row, and generates and files a letter for each employee, all triggered by nothing more than adding a spreadsheet to the right folder.&lt;/p&gt;

&lt;p&gt;The same pattern applies to any HR document tied to a periodic review, from bonus letters to renewal notices. The trigger and data source could just as easily be a webhook from a Human Resources Information System (HRIS) or a query against a database instead of a Drive folder, while the generation and delivery steps stay the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Salary%20Increase%20Letter.docx" rel="noopener noreferrer"&gt;Salary Increase Letter&lt;/a&gt; Word template used in this tutorial.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Salary%20Increase%20Data.xlsx" rel="noopener noreferrer"&gt;sample Excel file&lt;/a&gt; with employee raise data.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Salary%20Increase%20Letter%20Workflow.json" rel="noopener noreferrer"&gt;workflow JSON file&lt;/a&gt; that you can import into your n8n instance.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.docugenerate.com/assets/posts/036-n8n-salary-increase-letters/Letter%20for%20James%20Mitchell.pdf" rel="noopener noreferrer"&gt;sample generated PDF&lt;/a&gt; showing the final output.&lt;/li&gt;
&lt;li&gt;The DocuGenerate &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-n8n/" rel="noopener noreferrer"&gt;integration guide&lt;/a&gt; for n8n.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>letters</category>
      <category>n8n</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Generate Form W-9 PDFs with Power Automate and Smartsheet</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/generate-form-w-9-pdfs-with-power-automate-and-smartsheet-3lp4</link>
      <guid>https://dev.to/docugenerate/generate-form-w-9-pdfs-with-power-automate-and-smartsheet-3lp4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Form W-9, “Request for Taxpayer Identification Number and Certification,” is an IRS form that businesses in the United States use to collect the legal name, address, and taxpayer identification number of vendors, freelancers, and independent contractors before paying them. The information reported on a W-9 is later used to prepare 1099 forms at tax time, which makes accurate and consistently formatted W-9 records an important part of any vendor onboarding process. Companies that work with a steady stream of new contractors often end up managing this data in a spreadsheet or a tool like &lt;a href="https://www.smartsheet.com/" rel="noopener noreferrer"&gt;Smartsheet&lt;/a&gt;, which makes it a natural candidate for automation.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll build a &lt;a href="https://www.microsoft.com/en-us/power-platform/products/power-automate" rel="noopener noreferrer"&gt;Power Automate&lt;/a&gt; flow that watches a Smartsheet sheet for new rows and automatically generates a filled-in W-9 PDF using &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt;. Whenever a new vendor record is added to the sheet, whether by a team member entering it directly or by an import from another system, the flow merges that row’s data into a Word template and saves the finished PDF to Azure File Storage. The same pattern applies to any document that needs to be generated automatically from spreadsheet data, but a tax form is a good illustration because it involves a mix of individual and business filers, conditional fields, and a layout that must remain precise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the DocuGenerate Template
&lt;/h2&gt;

&lt;p&gt;The first step is to create a Word template in DocuGenerate that mirrors the official W-9 layout. We started from the &lt;a href="https://www.irs.gov/forms-pubs/about-form-w-9" rel="noopener noreferrer"&gt;IRS Form W-9&lt;/a&gt; and replaced each fillable field with a &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tag&lt;/a&gt;, then &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-new-template/" rel="noopener noreferrer"&gt;uploaded it&lt;/a&gt; as a new template named &lt;strong&gt;Form W-9&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxrfcqsr9a60ke6jho46s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxrfcqsr9a60ke6jho46s.png" alt="Form W-9 template with merge tags in DocuGenerate" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The template contains 17 merge tags: &lt;code&gt;firstName&lt;/code&gt;, &lt;code&gt;lastName&lt;/code&gt;, &lt;code&gt;businessName&lt;/code&gt;, &lt;code&gt;taxClassification&lt;/code&gt;, &lt;code&gt;exemptPayeeCode&lt;/code&gt;, &lt;code&gt;fatcaCode&lt;/code&gt;, &lt;code&gt;streetAddress&lt;/code&gt;, &lt;code&gt;requesterName&lt;/code&gt;, &lt;code&gt;requesterAddress&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;zipCode&lt;/code&gt;, &lt;code&gt;accountNumber&lt;/code&gt;, &lt;code&gt;ssn&lt;/code&gt;, &lt;code&gt;ein&lt;/code&gt;, &lt;code&gt;signature&lt;/code&gt;, and &lt;code&gt;signatureDate&lt;/code&gt;. Most of these map directly to a labeled box on the form. &lt;code&gt;taxClassification&lt;/code&gt; corresponds to the “Other” line in the federal tax classification section and holds a short description such as “C Corporation” or “Trust/estate,” while &lt;code&gt;ssn&lt;/code&gt; and &lt;code&gt;ein&lt;/code&gt; populate the two boxes in Part I depending on whether the filer is an individual or a business entity. You can download the &lt;a href="https://www.docugenerate.com/assets/posts/035-power-automate-form-w9/Form%20W-9.docx" rel="noopener noreferrer"&gt;Word template&lt;/a&gt; used in this tutorial and adapt it to your own layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Data Source in Smartsheet
&lt;/h2&gt;

&lt;p&gt;With the template in place, the next step is to prepare the sheet that will act as the trigger for our flow. In Smartsheet, click &lt;strong&gt;Create&lt;/strong&gt; &amp;gt; &lt;strong&gt;Sheet&lt;/strong&gt; &amp;gt; &lt;strong&gt;Table&lt;/strong&gt; to create a new sheet, then rename it to &lt;strong&gt;Form W-9&lt;/strong&gt;. Rather than typing sample rows in by hand, we’ll import them from an Excel file by opening the sheet menu and selecting &lt;strong&gt;Import&lt;/strong&gt;. Keep in mind that importing replaces any existing data in the sheet with the data from the source file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzvygrmiyc6rvkcvdqt7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzvygrmiyc6rvkcvdqt7e.png" alt="Importing Excel data into the Form W-9 sheet in Smartsheet" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can download the &lt;a href="https://www.docugenerate.com/assets/posts/035-power-automate-form-w9/Form%20W-9%20Data.xlsx" rel="noopener noreferrer"&gt;sample Excel file&lt;/a&gt; used for this import. It contains 10 rows matching the 18 columns that will map to the merge tags in the template: a &lt;code&gt;Record ID&lt;/code&gt; column (&lt;code&gt;REC-001&lt;/code&gt; through &lt;code&gt;REC-010&lt;/code&gt;) followed by one column for each field on the form. The data mixes individuals and business entities to exercise the different branches of the form: six individuals or sole proprietors with an SSN and no EIN, and four entities (an LLC, a C Corp, a trust/estate, and a partnership) with an EIN and no SSN. One row also has the Exempt Payee Code and FATCA Code columns populated, since those only apply in specific cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fct55k16ftoweehyggyqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fct55k16ftoweehyggyqz.png" alt="Form W-9 data imported into the Smartsheet table" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Power Automate Flow
&lt;/h2&gt;

&lt;p&gt;With the template and the data source ready, we can build the flow itself. In Power Automate, click &lt;strong&gt;New flow&lt;/strong&gt; &amp;gt; &lt;strong&gt;Automated cloud flow&lt;/strong&gt;. In the &lt;strong&gt;Build an automated cloud flow&lt;/strong&gt; dialog, give the flow a name such as &lt;code&gt;Form W-9&lt;/code&gt;, search for “Smartsheet” in the trigger list, and select &lt;strong&gt;When a new row is created&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffa9gwafjx16ewesdczeh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffa9gwafjx16ewesdczeh.png" alt="Selecting the Smartsheet trigger when creating a new flow" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flow we’ll build has three steps: a Smartsheet trigger that fires when a row is added, a DocuGenerate action that generates the PDF, and an Azure File Storage action that saves it. Let’s configure each one in turn.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring the Smartsheet Trigger
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;When a new row is created&lt;/strong&gt; trigger needs a connection to your Smartsheet account, which Power Automate will prompt you to create the first time you use it. Once connected, set the &lt;strong&gt;Sheet Id&lt;/strong&gt; parameter to the &lt;strong&gt;Form W-9&lt;/strong&gt; sheet we created earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffp84e9ypzdlcj971wekc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffp84e9ypzdlcj971wekc.png" alt="Configuring the Sheet Id parameter on the Smartsheet trigger" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding the Generate Document Action
&lt;/h3&gt;

&lt;p&gt;Next, click the &lt;strong&gt;+&lt;/strong&gt; button below the trigger and search for “DocuGenerate” to see the list of available actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj83wlehn3kpck0sh2mrk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj83wlehn3kpck0sh2mrk.png" alt="Searching for the DocuGenerate connector in Power Automate" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Generate Document&lt;/strong&gt;. The first time you use a DocuGenerate action in your environment, you’ll be prompted to &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-power-apps/#add-the-custom-connector" rel="noopener noreferrer"&gt;create a connection&lt;/a&gt; using your DocuGenerate &lt;a href="https://www.docugenerate.com/help/articles/where-can-i-find-my-api-key/" rel="noopener noreferrer"&gt;API Key&lt;/a&gt;. Once connected, set the &lt;strong&gt;Template&lt;/strong&gt; field to the &lt;strong&gt;Form W-9&lt;/strong&gt; template created in DocuGenerate.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Data&lt;/strong&gt; field, we need a JSON object containing the values for all 17 merge tags. Smartsheet’s trigger does not expose the row’s cells by column name, only as an array, so each value has to be referenced by its numeric index. Based on how the columns were ordered during the import, the mapping is: column 0 is Record ID, column 1 is SSN, column 2 is First Name, column 3 is Last Name, and so on through column 17, which is Signature Date. We built the JSON with the &lt;code&gt;concat&lt;/code&gt; and &lt;code&gt;json&lt;/code&gt; expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;json(concat(&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[2]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[3]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"businessName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[4]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"taxClassification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[5]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"exemptPayeeCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[6]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fatcaCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[7]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"streetAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[8]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"requesterName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[9]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"requesterAddress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[10]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[11]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[12]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"zipCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[13]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"accountNumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[14]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ssn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[1]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ein"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[15]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[16]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"signatureDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"', triggerBody()?.cells?[17]?.value,
  '"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;?&lt;/code&gt; characters are null-conditional operators: &lt;code&gt;triggerBody()?.cells?[2]?.value&lt;/code&gt; reads the &lt;code&gt;value&lt;/code&gt; property of the cell at index 2, and returns &lt;code&gt;null&lt;/code&gt; instead of throwing an error at any point where &lt;code&gt;cells&lt;/code&gt; or that specific cell happens to be missing. This matters here because several columns, like &lt;code&gt;businessName&lt;/code&gt; or &lt;code&gt;ssn&lt;/code&gt;, are intentionally blank on many rows. The outer &lt;code&gt;concat&lt;/code&gt; function stitches all these values into a single JSON string, and &lt;code&gt;json()&lt;/code&gt; parses that string into the object the &lt;strong&gt;Data&lt;/strong&gt; field expects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4mvflmuw0gdxze2ufvwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4mvflmuw0gdxze2ufvwt.png" alt="Configuring the Data field on the Generate Document action" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Name&lt;/strong&gt; field, we used an expression that checks whether the Business Name column (index 4) is populated and picks the appropriate naming pattern, using the same index map as above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if(
  empty(triggerBody()?.cells?[4]?.value),
  concat('Form W-9 ', triggerBody()?.cells?[2]?.value, ' ', triggerBody()?.cells?[3]?.value),
  concat('Form W-9 ', triggerBody()?.cells?[4]?.value)
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a name like “Form W-9 John Smith” for an individual filer, or “Form W-9 Bluepeak Software LLC” for a business filer. Finally, leave the &lt;strong&gt;Format&lt;/strong&gt; field set to its default value of &lt;strong&gt;.pdf&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fowa20lr1r8w41s32i15n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fowa20lr1r8w41s32i15n.png" alt="Configuring the Name field on the Generate Document action" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Saving the Document to Azure File Storage
&lt;/h3&gt;

&lt;p&gt;Once the document is generated, there are many ways to deliver it: send it by email, add it to SharePoint or OneDrive, upload it to an S3 bucket, and so on. For this tutorial, we’ll save it to &lt;a href="https://learn.microsoft.com/en-us/connectors/azurefile/" rel="noopener noreferrer"&gt;Azure File Storage&lt;/a&gt;. Click the &lt;strong&gt;+&lt;/strong&gt; button below the &lt;strong&gt;Generate Document&lt;/strong&gt; action, search for “Create file,” and select the action from &lt;strong&gt;Azure File Storage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F684h15p4zauiq89xnhny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F684h15p4zauiq89xnhny.png" alt="Adding the Create file action from Azure File Storage" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a connection by providing a &lt;strong&gt;Connection name&lt;/strong&gt; , your &lt;strong&gt;Azure Storage account name or file endpoint&lt;/strong&gt; , and your &lt;strong&gt;Azure Storage Account Access Key&lt;/strong&gt; , both of which are available from your storage account in the Azure portal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh95xrasmltgi8gcd83jy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh95xrasmltgi8gcd83jy.png" alt="Creating a connection to Azure File Storage" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the connection in place, configure the &lt;strong&gt;Create file&lt;/strong&gt; action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Folder path&lt;/strong&gt; : &lt;code&gt;/form-w-9&lt;/code&gt;, the destination folder in the storage account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File name&lt;/strong&gt; : &lt;code&gt;outputs('Generate_Document')?['body/filename']&lt;/code&gt;, the filename of the generated PDF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File content&lt;/strong&gt; : &lt;code&gt;outputs('Generate_Document')?['body/document_uri']&lt;/code&gt;, the generated PDF URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvbe7ljvwwgjl4p5uhski.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvbe7ljvwwgjl4p5uhski.png" alt="Configuring the Create file action parameters" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Flow
&lt;/h2&gt;

&lt;p&gt;To test the flow, click &lt;strong&gt;Test&lt;/strong&gt; on the flow page, select &lt;strong&gt;Manually&lt;/strong&gt; , then click &lt;strong&gt;Test&lt;/strong&gt; again. Power Automate will display a message asking you to add a row to the selected sheet to trigger the run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0c27olq8qu8dopxeo78r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0c27olq8qu8dopxeo78r.png" alt="Testing the flow manually from Power Automate" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a new row to the &lt;strong&gt;Form W-9&lt;/strong&gt; sheet in Smartsheet with a fresh set of vendor details. Once the flow runs, the generated document appears in the &lt;strong&gt;Documents&lt;/strong&gt; panel of the &lt;strong&gt;Form W-9&lt;/strong&gt; template in DocuGenerate, and the PDF is saved to the specified folder in Azure File Storage. You can see an example of the &lt;a href="https://www.docugenerate.com/assets/posts/035-power-automate-form-w9/Form%20W-9%20Jessica%20Martinez.pdf" rel="noopener noreferrer"&gt;generated PDF&lt;/a&gt; below.&lt;/p&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This tutorial covered the full path from a row in Smartsheet to a ready-to-file W-9 PDF, using a Power Automate flow with no custom code involved. The trigger detects new vendor records as they’re entered, the &lt;strong&gt;Generate Document&lt;/strong&gt; action fills in the official form layout with that row’s data, and the &lt;strong&gt;Create file&lt;/strong&gt; action delivers the result to Azure File Storage automatically.&lt;/p&gt;

&lt;p&gt;The same three-step pattern (trigger, generate, deliver) works for any recurring document your team produces from spreadsheet or table data, whether that’s onboarding paperwork, invoices, or compliance forms. Because the DocuGenerate actions are available across the whole Power Platform, you could just as easily trigger this flow from a Power Apps form or a SharePoint list instead of a Smartsheet row, while keeping the same &lt;strong&gt;Generate Document&lt;/strong&gt; configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/035-power-automate-form-w9/Form%20W-9.docx" rel="noopener noreferrer"&gt;Form W-9&lt;/a&gt; Word template used in this tutorial.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/035-power-automate-form-w9/Form%20W-9%20Data.xlsx" rel="noopener noreferrer"&gt;sample Excel file&lt;/a&gt; with vendor data for import into Smartsheet.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.docugenerate.com/assets/posts/035-power-automate-form-w9/Form%20W-9%20Jessica%20Martinez.pdf" rel="noopener noreferrer"&gt;sample generated PDF&lt;/a&gt; showing the final output.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://learn.microsoft.com/en-us/connectors/smartsheet/" rel="noopener noreferrer"&gt;Smartsheet connector reference&lt;/a&gt; for Power Automate.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://learn.microsoft.com/en-us/connectors/azurefile/" rel="noopener noreferrer"&gt;Azure File Storage connector reference&lt;/a&gt; for Power Automate.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://learn.microsoft.com/en-us/connectors/docugenerate/" rel="noopener noreferrer"&gt;DocuGenerate connector reference&lt;/a&gt; for Power Automate.&lt;/li&gt;
&lt;li&gt;The official &lt;a href="https://www.irs.gov/forms-pubs/about-form-w-9" rel="noopener noreferrer"&gt;IRS Form W-9&lt;/a&gt; and instructions.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>taxforms</category>
      <category>powerplatform</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Organize Your Templates in Folders for Easy Access</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/organize-your-templates-in-folders-for-easy-access-4m33</link>
      <guid>https://dev.to/docugenerate/organize-your-templates-in-folders-for-easy-access-4m33</guid>
      <description>&lt;h2&gt;
  
  
  What are Template Folders?
&lt;/h2&gt;

&lt;p&gt;Template Folders bring a familiar organizational model to DocuGenerate, allowing you to group related templates together in a hierarchical structure. Whether you’re managing a handful of templates or hundreds, folders provide a clean, intuitive way to find what you need and keep your template library organized as it grows.&lt;/p&gt;

&lt;p&gt;Until now, all templates in your DocuGenerate account were displayed in a single flat list, sorted by creation date. With this new feature, we’re introducing a folder system that follows the conventions you already know from file managers and email clients, with arbitrary nesting depth, intuitive reorganization, and full support across both the web app and the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Folder Organization Matters
&lt;/h2&gt;

&lt;p&gt;For organizations that rely on a growing library of templates, the absence of organizational structure can become a real productivity drag over time. As your template library grows from a few dozen to hundreds of templates, locating the right one becomes harder, onboarding new team members slows down, and templates drift apart from any logical structure that once existed in someone’s head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Faster Template Discovery&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
 With folders, you no longer need to scroll through a long list of templates or rely on partial name matching to find the right one. A well-organized folder hierarchy mirrors how your team actually thinks about templates, whether by department, by client, by document type, or by project phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Clearer Mental Model for Teams&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
 When templates are organized into folders, the structure itself communicates intent. A new team member looking at your template library can quickly grasp how documents are categorized in your organization, reducing the need for explanations or onboarding documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Filterable Views&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
 Folders aren’t just visual containers. Selecting a folder filters the templates view to show only the templates inside it, giving you a focused workspace when you’re working within a specific category. The current folder is reflected in the page URL, so you can bookmark or share a filtered view.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Template Folders Work in the Web App
&lt;/h2&gt;

&lt;p&gt;Folders are accessible throughout the app via the folder icon in the navbar. Clicking it opens the folder menu, which displays your folder tree and lets you create, edit, or delete folders directly. On mobile devices, the folder menu is available from the main user menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy5ylkmud0xgg6rf27hhx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy5ylkmud0xgg6rf27hhx.png" alt="Folder menu in the navbar" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Folder
&lt;/h3&gt;

&lt;p&gt;To &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-folder/" rel="noopener noreferrer"&gt;create a new folder&lt;/a&gt;, click the &lt;strong&gt;New Folder&lt;/strong&gt; icon next to the &lt;strong&gt;Template Folders&lt;/strong&gt; header in the folder menu. A dialog opens where you can enter the folder name and optionally select a parent folder if you want to create the folder inside an existing one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdkhdm75ds8eue0uzk93g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdkhdm75ds8eue0uzk93g.png" alt="New folder dialog" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-nested-folders/" rel="noopener noreferrer"&gt;create a sub-folder&lt;/a&gt; directly from an existing folder, you can also use the &lt;strong&gt;Add&lt;/strong&gt; action in the folder’s context menu, which pre-fills the parent selection for you. Folder names support letters, numbers, and spaces. Each folder gets a unique URL derived from its name, as described in the filtering section below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assigning Templates to Folders
&lt;/h3&gt;

&lt;p&gt;You can &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-assign-a-template-to-a-folder/" rel="noopener noreferrer"&gt;assign a template to a folder&lt;/a&gt; either when creating a new template or by editing an existing template’s settings. The folder dropdown in the template settings shows the full tree of available folders, with indentation reflecting the parent-child relationships.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqoiwy15nhnxz7gdoavop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqoiwy15nhnxz7gdoavop.png" alt="Folder selection in template settings" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each template belongs to a single folder at a time. To move a template to a different folder, you change the folder selection in the template settings and save. To remove a template from any folder, you select &lt;strong&gt;None&lt;/strong&gt; in the folder dropdown.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering Templates by Folder
&lt;/h3&gt;

&lt;p&gt;Selecting a folder in the folder menu &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-filter-templates-by-folder/" rel="noopener noreferrer"&gt;filters the templates view&lt;/a&gt; to show only the templates within that folder. The page URL updates to reflect the current filter. This means you can bookmark a folder view, share the URL with a teammate, or open multiple browser tabs each focused on a different folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F02qjljk01q6rapt03s9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F02qjljk01q6rapt03s9l.png" alt="Templates filtered by folder" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The folder breadcrumb appears in the navbar, showing the full path of the selected folder. To return to the default view with all the templates, click the &lt;strong&gt;Clear Folder&lt;/strong&gt; icon next to the breadcrumb or select &lt;strong&gt;All Templates&lt;/strong&gt; in the folder menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5wqxbszr3dg4dxjmfh7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5wqxbszr3dg4dxjmfh7v.png" alt="Nested folders filtered view" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The folder a template belongs to is visible in both &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-toggle-view-options/" rel="noopener noreferrer"&gt;Grid and List views&lt;/a&gt; of the templates page. In the &lt;strong&gt;Grid&lt;/strong&gt; view, the folder name appears under the template title alongside a small folder icon. In the &lt;strong&gt;List&lt;/strong&gt; view, a dedicated &lt;strong&gt;Folder&lt;/strong&gt; column shows the same information. Clicking the folder name in either view filters the templates list to that folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Editing, Moving, and Deleting Folders
&lt;/h3&gt;

&lt;p&gt;Each folder in the folder menu has a context menu with three actions: &lt;strong&gt;Edit&lt;/strong&gt; to &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-edit-a-folder/" rel="noopener noreferrer"&gt;rename the folder&lt;/a&gt; or change its parent, &lt;strong&gt;Delete&lt;/strong&gt; to &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-delete-a-folder/" rel="noopener noreferrer"&gt;remove the folder&lt;/a&gt; and all its sub-folders, and &lt;strong&gt;Add&lt;/strong&gt; to create a new sub-folder inside it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxf9m5jm4cbiiqh5ich9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpxf9m5jm4cbiiqh5ich9.png" alt="Folder context menu" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you delete a folder, all of its sub-folders are also removed. Templates inside deleted folders are not themselves deleted, they simply become orphaned and return to the &lt;strong&gt;All Templates&lt;/strong&gt; view. This means deleting a folder is always a safe operation from the perspective of your template content.&lt;/p&gt;

&lt;p&gt;Moving a folder under a different parent is supported, with one safety guard: a folder cannot be moved into itself or any of its own descendants. The folder dialog automatically disables those options when you’re editing an existing folder, so the constraint is enforced visually before you can even attempt an invalid move.&lt;/p&gt;

&lt;h2&gt;
  
  
  Folders for Team Collaboration
&lt;/h2&gt;

&lt;p&gt;For users with the &lt;a href="https://www.docugenerate.com/help/articles/introduction-to-team-collaboration/" rel="noopener noreferrer"&gt;team collaboration&lt;/a&gt; feature, folder organization extends naturally to shared templates. Team members see their team owner’s folders in a dedicated &lt;strong&gt;Shared&lt;/strong&gt; section within the folder menu, separated from their own folders by a visual divider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcybzzj26j0zgh132ijl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcybzzj26j0zgh132ijl.png" alt="Folder menu with personal and shared folders" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The visibility model is intentional: team members can browse and filter by the team owner’s shared folders, but they cannot create, rename, or delete folders in the team owner’s hierarchy. This keeps the folder structure consistent across the team and gives the owner clear ownership of how templates are organized for everyone.&lt;/p&gt;

&lt;p&gt;When a team member is added or removed, the folder structure reflects this change immediately, without requiring any action on the member’s part. Similarly, when the owner makes changes to their folder structure, those changes propagate to every team member’s view as soon as they’re saved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Folder Support in the API
&lt;/h2&gt;

&lt;p&gt;For users who manage templates through the &lt;a href="https://api.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate API&lt;/a&gt;, folder organization is accessible through the same endpoints you already use. The folder a template belongs to is represented as a &lt;code&gt;folder&lt;/code&gt; parameter on the template, expressed as an array of folder names that traces the path from the root to the assigned folder. For example, a template inside a sub-folder named “Contracts” within a parent folder named “Legal” has its &lt;code&gt;folder&lt;/code&gt; field set to &lt;code&gt;["Legal", "Contracts"]&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Specifying a Folder When Creating a Template
&lt;/h3&gt;

&lt;p&gt;When &lt;a href="https://api.docugenerate.com/#/Template/createTemplate" rel="noopener noreferrer"&gt;creating a template&lt;/a&gt; via the API, you can include the &lt;code&gt;folder&lt;/code&gt; parameter in your request to assign the template to a specific folder. If the folder path doesn’t yet exist in your account, DocuGenerate creates it automatically, including any intermediate folders along the way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1e4ng2qq7g76dwjgiv6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj1e4ng2qq7g76dwjgiv6.png" alt="Folder parameter when creating a template" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This auto-creation behavior makes it easy to set up your folder structure programmatically as you create templates, without needing to pre-configure the hierarchy through the web app first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Moving Templates Between Folders
&lt;/h3&gt;

&lt;p&gt;To move an existing template to a different folder, you can use the &lt;a href="https://api.docugenerate.com/#/Template/updateTemplate" rel="noopener noreferrer"&gt;update template&lt;/a&gt; endpoint with a new value for the &lt;code&gt;folder&lt;/code&gt; parameter. Setting it to an empty array (&lt;code&gt;[]&lt;/code&gt;) removes the template from any folder, returning it to the orphaned state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7he0nzwul3w1n5jnpan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7he0nzwul3w1n5jnpan.png" alt="Folder parameter when updating a template" width="799" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As with template creation, if the new folder path doesn’t exist yet, DocuGenerate creates the missing folders automatically (provided you’re the template owner). Team members with write permission on a shared template can update the template’s contents and settings, but they cannot create new folders in the template owner’s hierarchy, mirroring the web app behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering Templates by Folder
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://api.docugenerate.com/#/Template/getTemplates" rel="noopener noreferrer"&gt;list templates&lt;/a&gt; endpoint accepts a &lt;code&gt;folder&lt;/code&gt; query parameter that filters the response to a specific folder. The parameter takes the same form as for the creating or updating a template, expressed as an array of folder names that traces the path from the root to the assigned folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9mxhl1ww9qvumeye6v6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq9mxhl1ww9qvumeye6v6.png" alt="Folder parameter when querying the templates" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This filter follows the same logic as the web app filtering: selecting a folder returns only templates directly assigned to it, not templates from any sub-folders. To gather templates from a folder and all of its sub-folders, you’d issue separate requests for each sub-folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case Scenarios
&lt;/h2&gt;

&lt;p&gt;Let’s explore some real-world scenarios where template folders can provide significant value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Department-based Organization
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;mid-sized company&lt;/strong&gt; uses DocuGenerate to manage templates across multiple departments: HR, Legal, Sales, and Finance. With dozens of templates in each department, finding the right one had become time-consuming, and new hires struggled to locate the templates relevant to their role.&lt;/p&gt;

&lt;p&gt;After organizing templates into top-level folders by department, with sub-folders for specific document types (offer letters, contracts, proposals, invoices), team members can navigate directly to their department’s folder and see only the templates relevant to them. Shareable folder URLs are added to each team’s onboarding documentation, giving new hires a direct link to the templates they’ll be using from day one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Client-based Organization for an Agency
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;marketing agency&lt;/strong&gt; generates personalized contracts, proposals, and reports for dozens of clients, each with their own branding and terms. Before folders, the agency’s team relied on a naming convention like &lt;code&gt;[client-name]_[document-type]&lt;/code&gt; to locate templates, but this created long, hard-to-scan template names and made it difficult to onboard new account managers.&lt;/p&gt;

&lt;p&gt;By creating a top-level folder per client and organizing document types as sub-folders, the agency’s account managers can now browse a clean, hierarchical view of all materials for any given client. When onboarding a new client, the account manager creates a new client folder and replicates the standard sub-folder structure inside it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Programmatic Folder Setup via API
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;document automation team&lt;/strong&gt; integrates DocuGenerate into their internal tooling, generating templates programmatically from a database of document patterns. Their internal system organizes patterns by region and document category, and they want their DocuGenerate account to mirror that structure exactly.&lt;/p&gt;

&lt;p&gt;Using the API, their integration sends the appropriate &lt;code&gt;folder&lt;/code&gt; parameter when creating each template, automatically building out a folder hierarchy that matches their internal taxonomy. This keeps their DocuGenerate account synchronized with their source-of-truth without any manual folder management in the web app, even as their pattern catalog evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Folder Organization
&lt;/h2&gt;

&lt;p&gt;To get the most out of template folders as your library grows, consider these practices that help keep your structure useful and maintainable over time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with broad categories and refine as needed.&lt;/strong&gt; Resist the temptation to create deep folder hierarchies from day one. Begin with a few top-level folders that match the highest-level distinctions in your workflow, and add sub-folders only when a category accumulates enough templates to justify subdivision.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Match folders to how your team thinks, not how your data is structured.&lt;/strong&gt; A folder structure works best when it reflects the mental model your team already uses. If everyone refers to “Q1 contracts” rather than “January through March contracts”, let the folder name match the spoken vocabulary, even if it diverges from your internal naming standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use shareable folder URLs for onboarding.&lt;/strong&gt; When new team members join, share direct links to the folders they’ll be working with most often. This shortens the learning curve significantly and ensures everyone is aware of the relevant templates from the start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Re-evaluate periodically.&lt;/strong&gt; Folder structures that made sense a year ago may not match how your team works today. Schedule a periodic review (quarterly or semi-annually) to consolidate underused folders, split folders that have grown too large, or restructure entirely if your workflow has changed substantially.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Template Folders are one of the most-requested additions to DocuGenerate, and we’re glad to finally deliver an organizational model that scales with your template library. Whether you’re managing a small handful of templates or hundreds across multiple departments and clients, folders give you the structure and flexibility to find what you need and share it with your team.&lt;/p&gt;

&lt;p&gt;As always, we welcome your feedback as you start organizing your templates with this new feature. We’re particularly interested in hearing about workflows where folders save your team meaningful time, as well as any rough edges you encounter in real-world use that we should consider for future improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-folder/" rel="noopener noreferrer"&gt;How do I create a folder?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docugenerate.com/help/articles/how-do-i-assign-a-template-to-a-folder/" rel="noopener noreferrer"&gt;How do I assign a template to a folder?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docugenerate.com/help/articles/how-do-i-filter-templates-by-folder/" rel="noopener noreferrer"&gt;How do I filter templates by folder?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-nested-folders/" rel="noopener noreferrer"&gt;How do I create nested folders?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docugenerate.com/help/articles/how-do-i-edit-a-folder/" rel="noopener noreferrer"&gt;How do I edit a folder?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docugenerate.com/help/articles/how-do-i-delete-a-folder/" rel="noopener noreferrer"&gt;How do I delete a folder?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://api.docugenerate.com/#/Template/createTemplate" rel="noopener noreferrer"&gt;Create template&lt;/a&gt; API documentation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://api.docugenerate.com/#/Template/updateTemplate" rel="noopener noreferrer"&gt;Update template&lt;/a&gt; API documentation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://api.docugenerate.com/#/Template/getTemplates" rel="noopener noreferrer"&gt;List templates&lt;/a&gt; API documentation.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productupdate</category>
      <category>webapp</category>
      <category>templates</category>
    </item>
    <item>
      <title>Format Dates in Templates with Data Filters and Automatic Fields</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Sun, 31 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/format-dates-in-templates-with-data-filters-and-automatic-fields-dfl</link>
      <guid>https://dev.to/docugenerate/format-dates-in-templates-with-data-filters-and-automatic-fields-dfl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Dates appear in nearly every business document. Contracts specify effective dates, invoices carry due dates, certificates bear issuance dates, and recommendation letters are dated to establish their relevance. How those dates are presented matters, because date format conventions vary significantly by country and region. In the United States, the standard format is &lt;code&gt;MM/DD/YYYY&lt;/code&gt;, while most of Europe and much of the rest of the world uses &lt;code&gt;DD/MM/YYYY&lt;/code&gt;. Without control over date formatting, a date like December 4, 2026 could appear as &lt;code&gt;12/04/2026&lt;/code&gt; to a US reader or &lt;code&gt;04/12/2026&lt;/code&gt; to a European one, with each reading it correctly only for their own convention.&lt;/p&gt;

&lt;p&gt;DocuGenerate gives you two distinct ways to control how dates appear in your generated documents, each suited to a different use case. The first is the &lt;code&gt;date&lt;/code&gt; filter, which formats a date value supplied by your data set and renders it in whatever output format you specify. The second is inserting the current date directly into the document template using Word or LibreOffice, so the field is populated with today’s date automatically at generation time.&lt;/p&gt;

&lt;p&gt;This article walks through both approaches using the &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter.docx" rel="noopener noreferrer"&gt;Recommendation Letter&lt;/a&gt; template from our &lt;a href="https://www.docugenerate.com/templates/#letters" rel="noopener noreferrer"&gt;Template Library&lt;/a&gt;. For each approach, we’ll show how to produce both US and European date formats, so you have a concrete reference regardless of which convention your documents follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting Dates from Your Data Set
&lt;/h2&gt;

&lt;p&gt;When your data set contains date values, DocuGenerate’s &lt;a href="https://www.docugenerate.com/help/articles/how-to-format-data-with-filters/#format-dates" rel="noopener noreferrer"&gt;date filter&lt;/a&gt; gives you full control over how those dates appear in the generated document. This is the right approach when the date is a meaningful piece of data tied to a specific event, such as the signing date of a contract or the date a letter is formally addressed.&lt;/p&gt;

&lt;p&gt;The filter is part of the &lt;a href="https://www.docugenerate.com/help/articles/power-up-with-enhanced-syntax/" rel="noopener noreferrer"&gt;enhanced syntax&lt;/a&gt; feature set, so you need to &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-enable-the-enhanced-syntax/" rel="noopener noreferrer"&gt;enable it&lt;/a&gt; before it takes effect. To do that, open the template, go to &lt;strong&gt;Settings&lt;/strong&gt; , check the &lt;strong&gt;Enable enhanced syntax&lt;/strong&gt; checkbox, and click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparing the Data
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;date&lt;/code&gt; filter expects the input value to be in &lt;a href="https://en.wikipedia.org/wiki/ISO_8601" rel="noopener noreferrer"&gt;ISO 8601&lt;/a&gt; format, specifically &lt;code&gt;yyyy-MM-dd&lt;/code&gt;. This is an internationally recognized standard for representing dates that avoids the ambiguity of regional conventions. If your data currently stores dates in another format, you will need to convert them to the ISO format before passing them to DocuGenerate. The filter is ignored for non-ISO formatted dates, and the input value will be displayed as-is in the generated document.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the Template
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter.docx" rel="noopener noreferrer"&gt;original template&lt;/a&gt; uses the default DocuGenerate syntax with square bracket delimiters. When enhanced syntax is enabled, the tag parser is stricter and does not tolerate extra whitespace inside merge tags, so tags like &lt;code&gt;[Job Title]&lt;/code&gt; need to become &lt;code&gt;[Job_Title]&lt;/code&gt;. The &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter%20with%20Date%20Filter.docx" rel="noopener noreferrer"&gt;updated template&lt;/a&gt; has already been edited to have whitespace-free tags and is ready for use with enhanced syntax.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxb5bws8wh04v2x6ti9bx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxb5bws8wh04v2x6ti9bx.png" alt="Recommendation Letter template with date filter merge tag" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Applying the Date Filter
&lt;/h3&gt;

&lt;p&gt;With enhanced syntax enabled, you can format the date directly inside the merge tag using the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;[Date | date:'date_format']

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;date_format&lt;/code&gt; string uses symbols from the &lt;a href="https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table" rel="noopener noreferrer"&gt;Unicode Technical Standard&lt;/a&gt; for date and time patterns. The two most commonly needed formats for US and European conventions are:&lt;/p&gt;

&lt;p&gt;| Merge Tag | Result | Convention |&lt;br&gt;
| --- | --- | --- |&lt;br&gt;
   | &lt;code&gt;[Date | date:'MM/dd/yyyy']&lt;/code&gt; | 12/04/2026 | US (Month/Day/Year) |&lt;br&gt;
 | &lt;code&gt;[Date | date:'dd/MM/yyyy']&lt;/code&gt; | 04/12/2026 | European (Day/Month/Year) |&lt;br&gt;
 | &lt;code&gt;[Date | date:'MMMM d, yyyy']&lt;/code&gt; | December 4, 2026 | Written month name |&lt;br&gt;
 | &lt;code&gt;[Date | date:'dd-MM-yyyy']&lt;/code&gt; | 04-12-2026 | European with dashes |&lt;/p&gt;

&lt;p&gt;The filter also supports more expressive formats if you need to show the full month name, include the day of the week, or add time components. For a complete list of formatting options and examples, see the &lt;a href="https://www.docugenerate.com/help/articles/how-to-format-data-with-filters/#format-dates" rel="noopener noreferrer"&gt;How to format data with filters&lt;/a&gt; article from our Help Center.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inserting Today’s Date in the Template
&lt;/h2&gt;

&lt;p&gt;For documents where the date should always reflect the current date at generation time, passing a date through the data set is unnecessary. A recommendation letter, a daily status report, or an ad-hoc certificate typically just needs today’s date, and it would be redundant to include it in every API request or spreadsheet row. For these cases, you can insert a date field directly into the Word template. When DocuGenerate processes the document, the field evaluates and outputs the current date automatically, without any date value in the data at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Today’s Date in Microsoft Word
&lt;/h3&gt;

&lt;p&gt;Microsoft Word lets you insert the current date through the &lt;a href="https://support.microsoft.com/en-US/Word/insert-today-s-date-in-a-word-document" rel="noopener noreferrer"&gt;Date &amp;amp; Time&lt;/a&gt; field. This opens a dialog with a list of date and time formats and a checkbox labeled &lt;strong&gt;Update automatically&lt;/strong&gt;. Selecting this option converts the static date into a live field that updates whenever the document is opened or processed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg2zr9owniwzn1bwqohep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg2zr9owniwzn1bwqohep.png" alt="Inserting today's date and time field in Microsoft Word" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The available formats in Word are predetermined and tied to the system’s regional settings. You can change the language from the drop-down menu and the available formats list is updated to match the selected language.&lt;/p&gt;

&lt;p&gt;If you want more control over the date format, you can choose to &lt;strong&gt;Insert a Field&lt;/strong&gt; instead, and select the &lt;strong&gt;Date and Time&lt;/strong&gt; category and the &lt;strong&gt;Date&lt;/strong&gt; field name. Click on &lt;strong&gt;Options&lt;/strong&gt; to select from a list of pre-defined date formats, or enter the desired format in the &lt;strong&gt;Field code&lt;/strong&gt; text input. To get the same result and have the date formatted for the &lt;strong&gt;French&lt;/strong&gt; language, you’ll need to use the &lt;code&gt;DATE \@ "dd/MM/yyyy"&lt;/code&gt; value in the field code. But this requires familiarity with Word’s field syntax and is not practical for most users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6nj8prnkqu3a7km5qyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6nj8prnkqu3a7km5qyh.png" alt="Inserting today's date field in Microsoft Word" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a template built using this approach, see the &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter%20with%20Today's%20Date%20in%20Word.docx" rel="noopener noreferrer"&gt;Word-specific template&lt;/a&gt;, where the &lt;code&gt;[Date]&lt;/code&gt; merge tag has been replaced with Word’s automatic date field set to the European format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Today’s Date in LibreOffice
&lt;/h3&gt;

&lt;p&gt;LibreOffice Writer provides a more flexible way to insert today’s date, with locale-aware formatting that covers a wide range of regional date conventions. To insert a date field, go to &lt;strong&gt;Insert&lt;/strong&gt; &amp;gt; &lt;strong&gt;Field&lt;/strong&gt; &amp;gt; &lt;strong&gt;Date (variable)&lt;/strong&gt;, which adds a date field to your document using a default format. Right-click on the newly inserted date and select &lt;strong&gt;Edit Fields&lt;/strong&gt; , which opens the Fields dialog.&lt;/p&gt;

&lt;p&gt;In this dialog, the &lt;strong&gt;Format&lt;/strong&gt; column on the right displays a list of predefined formats for the default system language. These defaults cover common patterns, but to access formats for different locales, click the &lt;strong&gt;Additional formats&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frhgj6ucm8ac8outb0h1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frhgj6ucm8ac8outb0h1s.png" alt="Inserting a date field in LibreOffice using the Fields dialog" width="800" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This opens a new dialog where LibreOffice’s date formatting capability becomes particularly useful. A &lt;strong&gt;Language&lt;/strong&gt; dropdown on the right side of the dialog lets you select from hundreds of language and locale combinations. Once you pick a language, the &lt;strong&gt;Format&lt;/strong&gt; list updates to show date patterns that are natural to that locale.&lt;/p&gt;

&lt;p&gt;For example, selecting &lt;strong&gt;French (France)&lt;/strong&gt; makes patterns like &lt;code&gt;JJ/MM/AAAA&lt;/code&gt; available, which is the French notation for &lt;code&gt;DD/MM/YYYY&lt;/code&gt;. You can also type a custom format code directly into the &lt;strong&gt;Format Code&lt;/strong&gt; field if none of the listed options matches what you need.&lt;/p&gt;

&lt;p&gt;For a template built this way, see the &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter%20with%20Today's%20Date%20in%20LibreOffice.docx" rel="noopener noreferrer"&gt;LibreOffice-specific template&lt;/a&gt;. This approach is particularly practical when you maintain multiple variants of the same template for different regions. Each version can use the same content but have the date field configured for the target locale, ensuring the output matches local expectations without modifying the data or the generation workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Approach
&lt;/h2&gt;

&lt;p&gt;The two approaches address different scenarios and can both apply within the same organization depending on the document type. The date filter is the right choice when the date is a meaningful data field tied to a specific event. A contract’s signing date, an invoice’s due date, or a letter’s reference date all belong in the data, and the filter lets you render them in whatever regional convention the recipient expects. Since it takes a single ISO 8601 input and formats it on output, you can serve US and European audiences with the same data by simply changing the format string in the template.&lt;/p&gt;

&lt;p&gt;Inserting today’s date directly in the template is the right choice when no date value exists in the data and the document simply needs to be stamped with the current date. This keeps the data set clean and the generation request simple. Teams that generate documents for multiple regions often maintain a set of locale-specific templates, each with the date field pre-configured for its target audience, and point each regional workflow at the appropriate template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Getting dates to display correctly in generated documents comes down to identifying where the date value comes from. If it comes from your data set, use DocuGenerate’s filter with a format string that matches your target convention. If the document should always reflect today’s date, insert a date field into the template using Word or LibreOffice, for more advanced locale control. Both approaches work with the same template format and fit naturally into DocuGenerate’s generation workflow without requiring any changes to how documents are generated or delivered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter%20with%20Date%20Filter.docx" rel="noopener noreferrer"&gt;Recommendation Letter&lt;/a&gt; template configured for the date filter.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter%20with%20Today's%20Date%20in%20Word.docx" rel="noopener noreferrer"&gt;Recommendation Letter&lt;/a&gt; template using Word’s automatic date field.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/033-date-formatting/Recommendation%20Letter%20with%20Today's%20Date%20in%20LibreOffice.docx" rel="noopener noreferrer"&gt;Recommendation Letter&lt;/a&gt; template using LibreOffice’s date field.&lt;/li&gt;
&lt;li&gt;DocuGenerate’s Help Center article on &lt;a href="https://www.docugenerate.com/help/articles/how-to-format-data-with-filters/" rel="noopener noreferrer"&gt;how to format data with filters&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Microsoft’s documentation on &lt;a href="https://support.microsoft.com/en-US/Word/insert-today-s-date-in-a-word-document" rel="noopener noreferrer"&gt;inserting today’s date in a Word document&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>letters</category>
      <category>microsoftword</category>
      <category>expressions</category>
    </item>
    <item>
      <title>Mail Merge using DocuGenerate Compared to Microsoft Word</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Wed, 22 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/mail-merge-using-docugenerate-compared-to-microsoft-word-45p</link>
      <guid>https://dev.to/docugenerate/mail-merge-using-docugenerate-compared-to-microsoft-word-45p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Mail merge is the process of combining a document template with a data source to produce multiple personalized documents in one operation. It is commonly used for contracts, offer letters, invoices, certificates, and any other document where the overall structure stays the same but specific fields change per recipient or record. &lt;a href="https://www.microsoft.com/en-us/microsoft-365/word" rel="noopener noreferrer"&gt;Microsoft Word&lt;/a&gt; has included mail merge as a built-in feature for decades, making it a familiar starting point for many teams already working in the Microsoft 365 ecosystem. Dedicated document generation platforms like &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; approach the same problem differently: cloud-based processing, a REST API, and a set of template features that extend well beyond what Word supports natively.&lt;/p&gt;

&lt;p&gt;In this article, we evaluate both approaches using the same template and data file so the results are directly comparable. We walk through each workflow from start to finish, then examine how the two tools handle specific features including output formats, bulk generation, dynamic filenames, conditional content, images, and QR codes. The goal is not to pick a universal winner but to give you a clear picture of where each tool fits so you can choose the right one for your workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sample Files
&lt;/h2&gt;

&lt;p&gt;To keep the comparison grounded, both workflows in this article use the same two files. The template is the &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contract.docx" rel="noopener noreferrer"&gt;Employment Contract.docx&lt;/a&gt; Word document from our &lt;a href="https://www.docugenerate.com/templates/#contracts" rel="noopener noreferrer"&gt;Template Library&lt;/a&gt;. It contains merge fields for employee details, contract dates, compensation, and other standard contract terms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk7uz5v1t9d3zgk42ybct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk7uz5v1t9d3zgk42ybct.png" alt="Employment Contract template with merge tags" width="800" height="746"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The data source is the matching &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contract.xlsx" rel="noopener noreferrer"&gt;Employment Contract.xlsx&lt;/a&gt; spreadsheet, where each row represents one employee and the column headers correspond exactly to the merge field names in the template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqkfk0kycuije8gjzpb0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqkfk0kycuije8gjzpb0.png" alt="Employment Contract data spreadsheet" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing to note is that the column names in the spreadsheet must match the merge tag names in the template exactly for either tool to work correctly. This is a requirement shared by both Word and DocuGenerate, and it applies regardless of which delimiter format is used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mail Merge in Microsoft Word
&lt;/h2&gt;

&lt;p&gt;Word’s mail merge is accessible from the &lt;a href="https://support.microsoft.com/en-us/office/use-mail-merge-for-bulk-email-letters-labels-and-envelopes-f488ed5b-b849-4c11-9cff-932c49474705" rel="noopener noreferrer"&gt;Mailings tab&lt;/a&gt;, which provides a structured workflow for connecting a template to a data source, inserting fields, previewing the output, and completing the merge. All of this happens locally within the Word application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting the Excel Data Source
&lt;/h3&gt;

&lt;p&gt;To begin a mail merge, open the Employment Contract template in Word and navigate to &lt;strong&gt;Mailings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Select Recipients&lt;/strong&gt; &amp;gt; &lt;strong&gt;Use an Existing List&lt;/strong&gt;. Word will prompt you to select a file. After selecting &lt;code&gt;Employment Contract.xlsx&lt;/code&gt;, a dialog appears asking you to &lt;a href="https://support.microsoft.com/en-us/office/mail-merge-using-an-excel-spreadsheet-858c7d7f-5cc0-4ba1-9a7b-0a948fa3d7d3" rel="noopener noreferrer"&gt;choose which sheet&lt;/a&gt; to use as the data source, which is useful when your workbook contains multiple tabs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fprrorcmg02acf22lxahr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fprrorcmg02acf22lxahr.png" alt="Selecting the Excel data source in Word" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the data source connected, Word reads the column headers and makes them available as merge fields throughout the session. You can also open &lt;strong&gt;Edit Recipient List&lt;/strong&gt; to filter records, sort them, or exclude specific rows before running the merge.&lt;/p&gt;

&lt;p&gt;One thing to be aware of: when saving the Word template after connecting it to an Excel file, it stores the path to that file inside the document. If the Excel file is later moved, renamed, or deleted, reopening the template will trigger an error saying &lt;em&gt;“Word cannot find its data source”&lt;/em&gt;. It will then prompt you to locate the file again or remove the data source link entirely. This linking behavior means the template and data file need to stay in sync, which can become a maintenance concern when sharing templates across a team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inserting the Merge Fields
&lt;/h3&gt;

&lt;p&gt;Once the data source is connected, you can place merge fields anywhere in the template using &lt;strong&gt;Mailings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Insert Merge Field&lt;/strong&gt;. Word wraps each field in its fixed &lt;code&gt;«Field_Name»&lt;/code&gt; delimiter format, using guillemet characters that are inserted through the interface rather than typed directly. This delimiter format is not configurable: all Word mail merge fields use &lt;code&gt;«»&lt;/code&gt; and cannot be changed to another character pair.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F032-word-mail-merge%2Fword-insert-merge-fields.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F032-word-mail-merge%2Fword-insert-merge-fields.gif" alt="Inserting Merge Fields in the Word template" width="200" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that Word replaces the spaces in the column names with underscores when creating the field names for the merge. So the column named &lt;code&gt;Contract Date&lt;/code&gt; in the spreadsheet becomes &lt;code&gt;«Contract_Date»&lt;/code&gt; in the merge field list. This works fine in Word, but it’s useful to keep that in mind if you want to use the same template in DocuGenerate, for example, because the column names in Excel will no longer match the merge tags exactly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Previewing the Results
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Preview Results&lt;/strong&gt; button on the &lt;strong&gt;Mailings&lt;/strong&gt; tab replaces each merge field with the corresponding value from the first row of data. Navigation arrows let you step through the remaining records to spot-check the output before committing to the full merge. This instant preview is one of Word’s most useful features for template development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F032-word-mail-merge%2Fword-preview-results.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F032-word-mail-merge%2Fword-preview-results.gif" alt="Previewing merge results in Word" width="560" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Completing the Merge
&lt;/h3&gt;

&lt;p&gt;With the template verified, &lt;strong&gt;Finish &amp;amp; Merge&lt;/strong&gt; offers three options: edit the merged documents in a new Word file, send directly to a printer, or send as individual email messages via Outlook. Selecting &lt;strong&gt;Edit Individual Documents&lt;/strong&gt; produces a single Word file where each merged record is separated by a page break.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6v1s8uhbv97sxk0v55y4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6v1s8uhbv97sxk0v55y4.png" alt="Completing the mail merge in Word" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can download a copy of the &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contract%20Merged.docx" rel="noopener noreferrer"&gt;merged document&lt;/a&gt; to see what the output looks like. To get a PDF, you need to save or export that file manually after it was generated.&lt;/p&gt;

&lt;p&gt;Word does not generate individual files per record as part of the merge process. The entire workflow is desktop-only: there is no API, no cloud processing, and no way to trigger a mail merge programmatically from another application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mail Merge in DocuGenerate
&lt;/h2&gt;

&lt;p&gt;DocuGenerate is a cloud-based document generation platform that works from a web app and a &lt;a href="https://www.docugenerate.com/help/articles/where-can-i-find-the-api-reference/" rel="noopener noreferrer"&gt;REST API&lt;/a&gt;. The template remains a standard DOCX file, so you can design and edit it in Word as usual. The generation step happens in the cloud and produces the final document without requiring any local software beyond a browser or an HTTP client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up the Template
&lt;/h3&gt;

&lt;p&gt;To get started, upload the Employment Contract template from the &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-new-template/" rel="noopener noreferrer"&gt;New Template&lt;/a&gt; page in the web app. DocuGenerate reads the &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; from the document and uses them as the fields to populate at generation time.&lt;/p&gt;

&lt;p&gt;The merge tags can use square brackets &lt;code&gt;[]&lt;/code&gt; as &lt;a href="https://www.docugenerate.com/help/articles/what-are-tag-delimiters/" rel="noopener noreferrer"&gt;delimiters&lt;/a&gt;, but it’s also possible to &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-update-the-tag-delimiters/" rel="noopener noreferrer"&gt;customize the delimiters&lt;/a&gt; to any character pair you prefer, including the Word-style &lt;code&gt;«»&lt;/code&gt; guillemets if you want to reuse an existing Word mail merge template without modifying it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk0irwwied6r0lcuv0a37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk0irwwied6r0lcuv0a37.png" alt="Employment Contract template in DocuGenerate" width="799" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating from Excel
&lt;/h3&gt;

&lt;p&gt;To &lt;a href="https://www.docugenerate.com/help/articles/generate-documents-from-an-excel-or-csv-file/" rel="noopener noreferrer"&gt;generate documents from Excel&lt;/a&gt;, click &lt;strong&gt;New Document&lt;/strong&gt; on the template, choose the &lt;strong&gt;Excel or CSV file&lt;/strong&gt; option, and upload &lt;code&gt;Employment Contract.xlsx&lt;/code&gt;. DocuGenerate detects the sheets in the workbook and lets you choose which one to use. As with Word, the column names in the spreadsheet must match the merge tag names in the template exactly for the generation to succeed.&lt;/p&gt;

&lt;p&gt;From the &lt;strong&gt;Data Items&lt;/strong&gt; section, you can select which records to use for the merge by clicking on them. By default, all the records in the spreadsheet are used for generating the documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F032-word-mail-merge%2Fdocugenerate-generate-from-excel.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F032-word-mail-merge%2Fdocugenerate-generate-from-excel.gif" alt="Employment Contracts generation in batch in DocuGenerate" width="480" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.docugenerate.com/help/articles/select-the-merge-export-format/" rel="noopener noreferrer"&gt;merge export format&lt;/a&gt; can be a Word document, matching Word’s native output, or a PDF generated directly in the cloud. For multiple records, you can either &lt;a href="https://www.docugenerate.com/help/articles/combine-the-generated-documents-into-a-single-file/" rel="noopener noreferrer"&gt;combine all documents&lt;/a&gt; into a single file (see &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contracts.docx" rel="noopener noreferrer"&gt;Employment Contracts.docx&lt;/a&gt; for an example) or download them as &lt;a href="https://www.docugenerate.com/help/articles/generate-separate-documents-for-each-data-item/" rel="noopener noreferrer"&gt;separate files&lt;/a&gt; grouped in a ZIP archive (see &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contracts.zip" rel="noopener noreferrer"&gt;Employment Contracts.zip&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating via the API
&lt;/h3&gt;

&lt;p&gt;DocuGenerate also exposes an API that lets you trigger document generation from any external system. When generating from an Excel file &lt;a href="https://api.docugenerate.com/#/Document/generateDocument" rel="noopener noreferrer"&gt;via the API&lt;/a&gt;, you pass the &lt;code&gt;file&lt;/code&gt; as a parameter, and optionally the &lt;code&gt;sheet&lt;/code&gt; parameter to specify which tab to use. While the web app supports row range selection, the API processes all rows in the selected sheet by default. This makes it possible to integrate document generation into automation workflows and back-end applications that need to produce documents automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Comparison
&lt;/h2&gt;

&lt;p&gt;With both workflows covered, here is a closer look at how Word and DocuGenerate handle specific features that matter most in document generation workflows. You can click on each feature in the table below to jump directly to its details.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Word&lt;/th&gt;
&lt;th&gt;DocuGenerate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delimiters&lt;/td&gt;
&lt;td&gt;❌ Fixed&lt;/td&gt;
&lt;td&gt;✅ Customizable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview&lt;/td&gt;
&lt;td&gt;✅ Instant in-app preview&lt;/td&gt;
&lt;td&gt;❌ Requires generating a document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF export&lt;/td&gt;
&lt;td&gt;❌ Only via manual save&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulk generation&lt;/td&gt;
&lt;td&gt;✅ Single merged Word file&lt;/td&gt;
&lt;td&gt;✅ Single combined file or individual files grouped in a ZIP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic filenames&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes, using merge tags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API access&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email delivery&lt;/td&gt;
&lt;td&gt;✅ Yes, via Outlook&lt;/td&gt;
&lt;td&gt;❌ No, possible via automation tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditions&lt;/td&gt;
&lt;td&gt;✅ Yes, via Rules&lt;/td&gt;
&lt;td&gt;✅ Yes, including enhanced expressions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images&lt;/td&gt;
&lt;td&gt;✅ Local files only&lt;/td&gt;
&lt;td&gt;✅ By URL or Base64, with size control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QR codes and barcodes&lt;/td&gt;
&lt;td&gt;✅ Yes, via field codes&lt;/td&gt;
&lt;td&gt;✅ Yes, with rendering options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Append PDF&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Delimiters
&lt;/h3&gt;

&lt;p&gt;Word requires the &lt;code&gt;«»&lt;/code&gt; guillemet characters as merge field delimiters. These are inserted through the Word interface and cannot be typed or changed to another format. DocuGenerate lets you &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-update-the-tag-delimiters/" rel="noopener noreferrer"&gt;set any delimiters&lt;/a&gt; in the template settings, making it easy to migrate an existing Word template or match a format your team already uses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preview
&lt;/h3&gt;

&lt;p&gt;Word’s &lt;strong&gt;Preview Results&lt;/strong&gt; button shows the populated document instantly, which makes it easy to iterate on the template during development without generating a file every time. DocuGenerate does not have an equivalent in-app preview. To see the output, you generate a document. For templates under active development, this adds a small feedback loop compared to Word, though the generated file is typically available in a few seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  PDF export
&lt;/h3&gt;

&lt;p&gt;Word’s mail merge produces a Word document as its primary output. Converting to PDF requires saving or exporting the file separately, and the process does not create individual PDF files per record automatically. DocuGenerate can &lt;a href="https://www.docugenerate.com/help/articles/select-the-merge-export-format/" rel="noopener noreferrer"&gt;generate both&lt;/a&gt; DOCX and PDF output directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bulk Generation
&lt;/h3&gt;

&lt;p&gt;Both tools support generating multiple documents from a spreadsheet in one pass. Word assembles all merged records into a single Word file with page breaks between records. DocuGenerate gives you more control: you can &lt;a href="https://www.docugenerate.com/help/articles/combine-the-generated-documents-into-a-single-file/" rel="noopener noreferrer"&gt;combine all documents&lt;/a&gt; into a single output file or produce individual files grouped in a ZIP archive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Filenames
&lt;/h3&gt;

&lt;p&gt;Word does not support dynamic filenames for merged output. DocuGenerate lets you use merge tags in the &lt;a href="https://www.docugenerate.com/help/articles/set-the-documents-name-and-file-name/" rel="noopener noreferrer"&gt;document name&lt;/a&gt;, so each generated file can be named using values from the data row, such as the employee’s full name or contract start date. This makes the output immediately organized without any manual renaming after the merge.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Access
&lt;/h3&gt;

&lt;p&gt;Word mail merge has no API and cannot be triggered from outside the Word application. DocuGenerate provides a REST API that integrates with external systems, making it possible to generate documents programmatically as part of a back-end application, or an automation platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email Delivery
&lt;/h3&gt;

&lt;p&gt;Word supports sending merged documents as individual email messages directly through Outlook, which is useful for bulk outreach or internal communications. DocuGenerate does not have built-in email delivery, but it can be combined with email tools through automation platforms like &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-zapier/" rel="noopener noreferrer"&gt;Zapier&lt;/a&gt;, &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-make/" rel="noopener noreferrer"&gt;Make&lt;/a&gt;, &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-n8n/" rel="noopener noreferrer"&gt;n8n&lt;/a&gt;, or &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-power-apps/" rel="noopener noreferrer"&gt;Power Automate&lt;/a&gt; to achieve the same result as part of a larger workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditions
&lt;/h3&gt;

&lt;p&gt;Both tools support conditional content inside templates. Word provides &lt;a href="https://support.microsoft.com/en-gb/office/set-the-rules-for-a-mail-merge-d546ee7e-ab7a-4d6d-b488-41f9e4bd1409" rel="noopener noreferrer"&gt;Rules&lt;/a&gt; such as &lt;code&gt;IF...THEN...ELSE&lt;/code&gt;, available under &lt;strong&gt;Mailings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Rules&lt;/strong&gt; , which let you show or hide content based on field values. DocuGenerate uses a &lt;a href="https://www.docugenerate.com/help/articles/how-to-add-conditions-to-a-template/" rel="noopener noreferrer"&gt;condition tag syntax&lt;/a&gt; written directly in the template body. With &lt;a href="https://www.docugenerate.com/help/articles/power-up-with-enhanced-syntax/" rel="noopener noreferrer"&gt;enhanced syntax&lt;/a&gt; enabled, DocuGenerate also supports logical expressions, comparisons, and arithmetic in conditions, which goes beyond the rule set available in Word.&lt;/p&gt;

&lt;h3&gt;
  
  
  Images
&lt;/h3&gt;

&lt;p&gt;Word allows dynamic images in mail merge, but the images must be stored locally on the machine running the merge and configured through field codes, which makes the setup difficult to reproduce or automate. DocuGenerate supports images referenced by &lt;a href="https://www.docugenerate.com/help/articles/how-to-add-images-to-a-template/" rel="noopener noreferrer"&gt;URL or Base64&lt;/a&gt;, which works reliably in cloud-based workflows where images are hosted externally. You can also control image dimensions directly in the merge tag using the &lt;code&gt;size&lt;/code&gt; filter, making layout control straightforward without touching the document structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  QR Codes and Barcodes
&lt;/h3&gt;

&lt;p&gt;Word supports &lt;a href="https://support.microsoft.com/en-us/office/field-codes-mergebarcode-812fc43f-cb53-4782-8f9f-290ed08d34f3" rel="noopener noreferrer"&gt;barcodes and QR codes&lt;/a&gt; via the &lt;code&gt;MergeBarCode&lt;/code&gt; and &lt;code&gt;DisplayBarcode&lt;/code&gt; field codes, though the syntax is not straightforward and visual customization options are limited. DocuGenerate has a dedicated &lt;a href="https://www.docugenerate.com/help/articles/how-to-add-qr-codes-and-barcodes/" rel="noopener noreferrer"&gt;QR code and barcode&lt;/a&gt; syntax that converts any field value into a QR code or barcode with configurable dimensions, colors, and formatting directly from the merge tag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Append PDF
&lt;/h3&gt;

&lt;p&gt;Word does not natively support appending additional PDF content to a merged output file. DocuGenerate lets you &lt;a href="https://www.docugenerate.com/help/articles/merge-a-pdf-at-the-end-of-generated-documents/" rel="noopener noreferrer"&gt;append a static PDF file&lt;/a&gt; at the end of each generated document, which is useful for contracts that need to include standard terms and conditions, legal notices, or fixed annexes as part of every output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;For teams that work entirely within the Microsoft 365 desktop environment and need to produce merged documents without involving any external systems, Word’s built-in mail merge covers the basics well. It handles field substitution, conditional rules, and direct email delivery through Outlook, all without requiring a separate tool.&lt;/p&gt;

&lt;p&gt;DocuGenerate is a better fit when document generation needs to participate in a larger workflow. Its API makes it usable from automation platforms, back-end systems, and no-code tools, and features like PDF output, dynamic filenames, and PDF appending cover scenarios that Word mail merge cannot address natively. For teams already using Word to design templates, the transition is minimal because the templates have the same format.&lt;/p&gt;

&lt;p&gt;The choice between the two comes down to where the generation step sits in your process. If it is a one-off task performed on a desktop, Word is already available and sufficient. If it is a recurring, automated step in a business workflow, a dedicated tool like DocuGenerate provides the flexibility to connect document generation to your existing systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contract.docx" rel="noopener noreferrer"&gt;Employment Contract.docx&lt;/a&gt; Word template used in this article.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contract.xlsx" rel="noopener noreferrer"&gt;Employment Contract.xlsx&lt;/a&gt; sample data file.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contract%20Merged.docx" rel="noopener noreferrer"&gt;Employment Contract Merged.docx&lt;/a&gt; merged output from Word.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/032-word-mail-merge/Employment%20Contracts.zip" rel="noopener noreferrer"&gt;Employment Contracts.zip&lt;/a&gt; with dynamic filenames from DocuGenerate.&lt;/li&gt;
&lt;li&gt;Microsoft’s documentation on &lt;a href="https://support.microsoft.com/en-us/office/use-mail-merge-for-bulk-email-letters-labels-and-envelopes-f488ed5b-b849-4c11-9cff-932c49474705" rel="noopener noreferrer"&gt;mail merge for letters and bulk documents&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Microsoft’s guide on &lt;a href="https://support.microsoft.com/en-us/office/mail-merge-using-an-excel-spreadsheet-858c7d7f-5cc0-4ba1-9a7b-0a948fa3d7d3" rel="noopener noreferrer"&gt;using an Excel spreadsheet&lt;/a&gt; as a mail merge data source.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>contracts</category>
      <category>microsoftword</category>
      <category>mailmerge</category>
    </item>
    <item>
      <title>Generate Property Listings with n8n and DocuGenerate</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Fri, 13 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/generate-property-listings-with-n8n-and-docugenerate-546h</link>
      <guid>https://dev.to/docugenerate/generate-property-listings-with-n8n-and-docugenerate-546h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Real estate agencies and property management companies regularly produce listing documents for new properties, whether for print, email campaigns, or client portals. Assembling these documents manually, from gathering agent details and writing descriptions to sourcing photos and formatting everything consistently, takes time that could be spent elsewhere. In this tutorial, we’ll build an automated workflow using &lt;a href="https://n8n.io/" rel="noopener noreferrer"&gt;n8n&lt;/a&gt;, &lt;a href="https://www.anthropic.com/claude" rel="noopener noreferrer"&gt;Claude&lt;/a&gt;, and &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; that produces a polished, ready-to-share property listing PDF in a matter of seconds.&lt;/p&gt;

&lt;p&gt;The workflow uses the Anthropic node in n8n, which calls Claude to generate realistic listing data on demand, and merges it into a professionally designed Word template via DocuGenerate. The finished PDF is then downloaded and saved directly to a Dropbox folder. While this tutorial uses Claude to simulate data input, the same workflow can be connected to any real data source: a web form, a CRM, a property database, or a spreadsheet. The document generation and delivery steps remain identical regardless of where the data comes from, making this a solid foundation for real-world automation. By the end of this guide, you’ll have a fully functional n8n workflow that you can import and adapt to your own use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Property Listing Template
&lt;/h2&gt;

&lt;p&gt;Before building the n8n workflow, we need a document template in DocuGenerate. For this tutorial, we’ll use the &lt;a href="https://www.docugenerate.com/assets/posts/031-n8n-property-listings/Property%20Listing.docx" rel="noopener noreferrer"&gt;Property Listing&lt;/a&gt; Word template, which features a clean real estate layout with a large hero image at the top, a row of three interior photos, key property details, a QR code linking to the listing page, and an agent card at the bottom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjif9468nfleyof2cg41h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjif9468nfleyof2cg41h.png" alt="Property Listing template with merge tags" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The template uses the following &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agent_photo&lt;/code&gt;, &lt;code&gt;agent_name&lt;/code&gt;, &lt;code&gt;agent_email&lt;/code&gt;, &lt;code&gt;agent_phone&lt;/code&gt;, &lt;code&gt;agent_address&lt;/code&gt;: Agent contact details displayed in the footer card.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;price&lt;/code&gt;: Displayed prominently next to the heading.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;surface&lt;/code&gt;, &lt;code&gt;bedrooms&lt;/code&gt;, &lt;code&gt;bathrooms&lt;/code&gt;, &lt;code&gt;kitchens&lt;/code&gt;: Key property stats shown in the details row.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: A short paragraph describing the property, displayed below the headline section.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main_image&lt;/code&gt;, &lt;code&gt;image1&lt;/code&gt;, &lt;code&gt;image2&lt;/code&gt;, &lt;code&gt;image3&lt;/code&gt;: Property photos embedded in the layout.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt;: Converted into a QR code displayed in the top-right corner of the document.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Several features of this template are worth highlighting in more detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Image sizing.&lt;/strong&gt; The hero image uses the &lt;code&gt;[%main_image | size:'auto':600]&lt;/code&gt; &lt;a href="https://www.docugenerate.com/help/articles/how-to-add-images-to-a-template/#resizing-images" rel="noopener noreferrer"&gt;size syntax&lt;/a&gt; to constrain its height to 600 pixels while preserving the aspect ratio. The three interior photos use &lt;code&gt;[%image1 | size:240:'auto']&lt;/code&gt; to fix their width at 240 pixels, keeping all three images the same size and aligned in a row. Images can be passed as either a public URL or a base64-encoded data URI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;QR code.&lt;/strong&gt; The &lt;code&gt;url&lt;/code&gt; field is not displayed as plain text. Instead, the template uses the &lt;code&gt;[%url | qrcode:60:60 | set:'barcolor':'#2f7498']&lt;/code&gt; syntax to render the value as a &lt;a href="https://www.docugenerate.com/help/articles/how-to-add-qr-codes-and-barcodes/#adding-qr-codes" rel="noopener noreferrer"&gt;QR code&lt;/a&gt; with custom dimensions and a brand color matching the document theme. Scanning the code in the generated PDF takes the reader directly to the property listing page online.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pluralization.&lt;/strong&gt; The stats row uses &lt;a href="https://www.docugenerate.com/help/articles/power-up-with-enhanced-syntax/#logical-and-mathematical-expressions" rel="noopener noreferrer"&gt;conditional syntax&lt;/a&gt; to handle pluralization automatically. For example, the bedroom count is written as &lt;code&gt;[bedrooms] Bedroom[#bedrooms &amp;gt; 1]s[/]&lt;/code&gt; in the template, which outputs “1 Bedroom” or “3 Bedrooms” depending on the value of the &lt;code&gt;bedrooms&lt;/code&gt; field. The same pattern applies to &lt;code&gt;bathrooms&lt;/code&gt; and &lt;code&gt;kitchens&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three of these features rely on DocuGenerate’s &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-enable-the-enhanced-syntax/" rel="noopener noreferrer"&gt;enhanced syntax&lt;/a&gt;, which must be enabled for the template before generating documents. You can turn it on in the template settings in the web app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The n8n Workflow Overview
&lt;/h2&gt;

&lt;p&gt;In this workflow, we’ll use Claude to generate dummy listing data so the process can be demonstrated end-to-end without requiring a live database or external system. In a real deployment, the manual trigger and &lt;strong&gt;Message a model&lt;/strong&gt; node would be replaced by a trigger and data source that reflects your actual process.&lt;/p&gt;

&lt;p&gt;The complete workflow consists of several nodes connected in a linear sequence:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3izvv5pvqv1yztduo90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3izvv5pvqv1yztduo90.png" alt="The complete n8n workflow" width="799" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can download the &lt;a href="https://www.docugenerate.com/assets/posts/031-n8n-property-listings/Property%20Listing%20Workflow.json" rel="noopener noreferrer"&gt;workflow JSON file&lt;/a&gt; and import it directly into your n8n instance. You’ll need to update the DocuGenerate template ID, your API credentials, and the Dropbox destination path to match your own setup. Let’s go through each node in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating Listing Data with Claude
&lt;/h2&gt;

&lt;p&gt;The first functional node is the &lt;strong&gt;Message a model&lt;/strong&gt; node from the Anthropic integration, configured to use the &lt;code&gt;claude-sonnet-4-6&lt;/code&gt; model. Its role in this workflow is to produce a realistic set of property listing values that can be passed to DocuGenerate. To use this node, you’ll need an Anthropic account and an API Key generated from the &lt;a href="https://console.anthropic.com/" rel="noopener noreferrer"&gt;Anthropic Console&lt;/a&gt;, which you add as a credential in n8n when setting up the node for the first time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqoeyaglcjxt5pguchce0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqoeyaglcjxt5pguchce0.png" alt="API Key configuration for the Anthropic node" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The prompt includes the full JSON structure that the template expects, with most fields left empty for Claude to fill in. The &lt;code&gt;agent_photo&lt;/code&gt; field is pre-filled with a URL pattern from &lt;a href="https://randomuser.me/" rel="noopener noreferrer"&gt;randomuser.me&lt;/a&gt;, a free service that returns a realistic portrait photo for a given numeric ID. Claude is instructed to replace &lt;code&gt;&amp;lt;AGENT_ID&amp;gt;&lt;/code&gt; with a random integer between 1 and 50, producing a different agent photo on each run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbkxed9jkmr5spmif5tdf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbkxed9jkmr5spmif5tdf.png" alt="Message a model node configuration" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the full prompt used in the node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;generate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dummy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;AGENT_ID&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;needs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;needs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;around&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;characters.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;needs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;just&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;updated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_photo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://randomuser.me/api/portraits/women/&amp;lt;AGENT_ID&amp;gt;.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kitchens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bathrooms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bedrooms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"surface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.docugenerate.com/blog/generate-property-listings-with-n8n-and-docugenerate/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main_image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://images.pexels.com/photos/1571459/pexels-photo-1571459.jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://images.pexels.com/photos/2062431/pexels-photo-2062431.jpeg?w=400"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://images.pexels.com/photos/2089698/pexels-photo-2089698.jpeg?w=400"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://images.pexels.com/photos/164595/pexels-photo-164595.jpeg?w=400"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The four image fields are left as static &lt;a href="https://www.pexels.com/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt; URLs for simplicity. In a real-world scenario, these would be actual photos of the property fetched from a media library, a CRM attachment, or a cloud storage bucket. The &lt;code&gt;url&lt;/code&gt; field points to this blog post and will be rendered as a QR code in the final document.&lt;/p&gt;

&lt;p&gt;Claude returns the completed JSON wrapped in a markdown code block. The response from the &lt;strong&gt;Message a model&lt;/strong&gt; node has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"```

json&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;agent_name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Sarah Mitchell&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$485,000&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; ...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;

```"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual listing data is nested inside &lt;code&gt;content[0].text&lt;/code&gt; and wrapped in markdown fences, which means it cannot be used directly by the next node. That is what the Code node in the following step handles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing the Claude Response
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Code in JavaScript&lt;/strong&gt; node extracts the JSON object from Claude’s response and exposes it as clean, structured data for the rest of the workflow. This step is necessary for two reasons: the &lt;strong&gt;Message a model&lt;/strong&gt; node returns the full API response object rather than just the text content, and Claude tends to wrap its JSON output in markdown code fences even when instructed not to. Rather than relying on prompt engineering to fix this inconsistency, the Code node handles it reliably every time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4tw901b3bz4v8814fht6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4tw901b3bz4v8814fht6.png" alt="Code in JavaScript node configuration" width="800" height="521"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/``&lt;/span&gt;&lt;span class="err"&gt;`
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;/, ''&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;.replace&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```/, '').trim();
return { json: JSON.parse(json) };

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line reads the text value from Claude’s response. The second line strips the markdown code fences using two regular expressions. The third line parses the cleaned string into a JavaScript object and returns it as the node output. After this step, all the listing fields (&lt;code&gt;agent_name&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;bedrooms&lt;/code&gt;, etc.) are available as top-level keys in the item’s JSON, ready to be passed directly to DocuGenerate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the Document
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Generate document&lt;/strong&gt; node uses the &lt;a href="https://n8n.io/integrations/docugenerate/" rel="noopener noreferrer"&gt;DocuGenerate integration&lt;/a&gt; for n8n to merge the listing data with the Word template and produce a PDF. To use this node, install DocuGenerate in your n8n instance and &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-n8n/#install-docugenerate" rel="noopener noreferrer"&gt;configure it&lt;/a&gt; with your &lt;a href="https://www.docugenerate.com/help/articles/where-can-i-find-my-api-key/" rel="noopener noreferrer"&gt;API Key&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F94kdg221y4hw91300yl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F94kdg221y4hw91300yl8.png" alt="Generate document node configuration" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The node is configured with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template Name or ID&lt;/strong&gt; : The &lt;strong&gt;Property Listing&lt;/strong&gt; template uploaded in the template setup step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : An n8n expression that generates a dynamic document name: &lt;code&gt;Property Listing for {{ $json.price }} ({{ $json.surface }})&lt;/code&gt;. This produces a filename like &lt;code&gt;Property Listing for $485,000 (1,850 sq ft)&lt;/code&gt; that makes each document easy to identify.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt; : Set to &lt;code&gt;PDF (.pdf)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; : Set to &lt;code&gt;{{ $json }}&lt;/code&gt;, which passes the entire output of the Code node as the data payload. Since the Code node already produces a flat JSON object with all the template fields at the top level, no additional mapping is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the node runs successfully, the response contains, among others, a &lt;code&gt;document_uri&lt;/code&gt; field with a URL pointing to the generated PDF, and a &lt;code&gt;filename&lt;/code&gt; field with the document’s filename.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downloading the Generated PDF
&lt;/h2&gt;

&lt;p&gt;DocuGenerate returns a URL to the generated document rather than the binary file content directly. To upload the PDF to Dropbox in the next step, we first need to fetch the file from that URL. The &lt;strong&gt;HTTP Request&lt;/strong&gt; node handles this by making a GET request to the &lt;code&gt;document_uri&lt;/code&gt; returned by the Generate document node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2aek6fr7uci6q5kofvk0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2aek6fr7uci6q5kofvk0.png" alt="HTTP Request node configuration" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;URL&lt;/strong&gt; parameter is set to &lt;code&gt;{{ $json.document_uri }}&lt;/code&gt;, referencing the document URL from the previous step. The binary file data is then available to the Dropbox node for upload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving the PDF to Dropbox
&lt;/h2&gt;

&lt;p&gt;The final node uploads the generated PDF to a designated Dropbox folder using the &lt;a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.dropbox/" rel="noopener noreferrer"&gt;Dropbox node&lt;/a&gt; in n8n, giving the team immediate access to the new listing document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4gnwrwwe6rhgac9ov9mh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4gnwrwwe6rhgac9ov9mh.png" alt="Upload a file node configuration" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The node uses OAuth2 authentication connected to your Dropbox account. The &lt;strong&gt;File Path&lt;/strong&gt; parameter is set to &lt;code&gt;/n8n/Property Listings/{{ $json.filename }}&lt;/code&gt;, saving each document in the &lt;code&gt;/n8n/Property Listings/&lt;/code&gt; folder using the filename returned by DocuGenerate. The &lt;strong&gt;Binary File&lt;/strong&gt; option is enabled so the node reads the file content from the HTTP Request node’s output rather than a static path.&lt;/p&gt;

&lt;p&gt;Once the workflow completes, the generated PDF is available in Dropbox and looks like this. You can also download a &lt;a href="https://www.docugenerate.com/assets/posts/031-n8n-property-listings/Property%20Listing%20for%20$875,000%20(2,450%20sq%20ft).pdf" rel="noopener noreferrer"&gt;sample PDF&lt;/a&gt; to see the final result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2p7xd4dohnha22njidp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2p7xd4dohnha22njidp.png" alt="Sample generated property listing PDF" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Data Sources
&lt;/h2&gt;

&lt;p&gt;The current workflow uses Claude as a convenient stand-in for a real data source, but in practice, property listing data rarely comes from an AI model. It typically lives in a form submission, a CRM record, a database row, or a spreadsheet. Since the document generation and delivery steps stay the same regardless of where the data originates, swapping out the Anthropic node for a different trigger and data source is all it takes to turn this tutorial workflow into a production-ready automation. Below are some common scenarios this workflow is well-suited for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web Forms
&lt;/h3&gt;

&lt;p&gt;Many agencies use online forms to collect property details from listing agents before preparing marketing materials. A form tool like Jotform, Typeform, or Tally can capture fields such as address, price, number of rooms, surface area, and a description directly from the agent. When a new submission arrives, the form platform can trigger the n8n workflow via a webhook, passing all collected data straight into the document generation step. This makes property listing creation a natural follow-up to the intake process, with no manual data transfer required.&lt;/p&gt;

&lt;h3&gt;
  
  
  CRM Systems
&lt;/h3&gt;

&lt;p&gt;Real estate businesses that manage their inventory in a CRM such as Salesforce, HubSpot, or Pipedrive can trigger document generation whenever a property record reaches a certain pipeline stage, for example when it is marked as “Ready to publish” or “New listing”. The workflow would pull the relevant fields from the CRM record, merge them into the template, and either save the PDF back to the record or deliver it to the assigned agent by email. This keeps the listing document in sync with the CRM data and eliminates duplicate data entry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Databases and Spreadsheets
&lt;/h3&gt;

&lt;p&gt;Teams that manage their property inventory in a database such as PostgreSQL, Airtable, or Notion, or in a spreadsheet like Google Sheets or Excel, can query new or updated records on a schedule or in response to a row being added. n8n has native nodes for all of these platforms, making it straightforward to read a row, map the columns to the template fields, and generate a PDF for each property. This approach works particularly well for agencies that add multiple listings at once and want to generate all documents in a single automated run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we built an n8n workflow that generates a property listing PDF automatically. The workflow uses Claude to simulate a realistic data input, parses the response into structured JSON, merges the data with a professionally designed Word template, and saves the resulting PDF to Dropbox. The template demonstrates several advanced DocuGenerate features including image sizing with filters, QR code generation, and automatic pluralization via conditional syntax.&lt;/p&gt;

&lt;p&gt;In a real deployment, the Anthropic node would be replaced by a connection to your actual data source, whether that is a web form, a CRM, a database, or a spreadsheet. The document generation and delivery steps remain the same, making this workflow straightforward to adapt to almost any property management setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/031-n8n-property-listings/Property%20Listing.docx" rel="noopener noreferrer"&gt;Property Listing&lt;/a&gt; Word template.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/031-n8n-property-listings/Property%20Listing%20Workflow.json" rel="noopener noreferrer"&gt;workflow JSON file&lt;/a&gt; that you can import into your n8n instance.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.docugenerate.com/assets/posts/031-n8n-property-listings/Property%20Listing%20for%20$875,000%20(2,450%20sq%20ft).pdf" rel="noopener noreferrer"&gt;sample generated PDF&lt;/a&gt; showing the final output.&lt;/li&gt;
&lt;li&gt;The DocuGenerate &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-n8n/" rel="noopener noreferrer"&gt;integration guide&lt;/a&gt; for n8n.&lt;/li&gt;
&lt;li&gt;Photos provided by &lt;a href="https://www.pexels.com/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt; for the property images.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>propertylistings</category>
      <category>n8n</category>
      <category>nocode</category>
    </item>
    <item>
      <title>How to Generate Business Letters in Coda with DocuGenerate</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Wed, 25 Feb 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/how-to-generate-business-letters-in-coda-with-docugenerate-18hk</link>
      <guid>https://dev.to/docugenerate/how-to-generate-business-letters-in-coda-with-docugenerate-18hk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Business letters are a staple of professional communication, whether you’re sending client proposals, formal notifications, or outreach campaigns. Writing each letter individually is repetitive and error-prone, and the problem compounds quickly when you have a long list of recipients. In this tutorial, we’ll show how to use &lt;a href="https://coda.io/" rel="noopener noreferrer"&gt;Coda&lt;/a&gt; and &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; together to generate personalized PDF business letters directly from a table, with a single button click per row.&lt;/p&gt;

&lt;p&gt;Coda is a flexible workspace that combines documents and spreadsheets into a single platform, making it easy to build interactive tools with tables, buttons, and custom formulas. The &lt;a href="https://www.docugenerate.com/integrations/coda/#pack" rel="noopener noreferrer"&gt;DocuGenerate Pack&lt;/a&gt; extends Coda with document generation capabilities, letting you produce professional PDFs from Word templates without writing any code. Each row in a Coda table becomes a personalized document, populated with the values from that row’s columns.&lt;/p&gt;

&lt;p&gt;By the end of this guide, you’ll have a Coda document with a table of recipients and a &lt;strong&gt;Generate&lt;/strong&gt; button that produces a personalized PDF business letter for each person. The generated files are saved directly into a &lt;strong&gt;PDF&lt;/strong&gt; column in the table, ready to be downloaded or shared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Business Letter Template
&lt;/h2&gt;

&lt;p&gt;Before working in Coda, we need a document template in DocuGenerate. We’ll use the &lt;a href="https://www.docugenerate.com/assets/posts/030-coda-business-letters/Business%20Letter.docx" rel="noopener noreferrer"&gt;Business Letter&lt;/a&gt; Word file from our &lt;a href="https://www.docugenerate.com/templates/" rel="noopener noreferrer"&gt;Template Library&lt;/a&gt;, which is a professionally formatted letter ready for immediate use.&lt;/p&gt;

&lt;p&gt;The template contains &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; for &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Name&lt;/code&gt;, &lt;code&gt;Job Title&lt;/code&gt;, &lt;code&gt;Company Name&lt;/code&gt;, &lt;code&gt;Street Address&lt;/code&gt;, &lt;code&gt;City&lt;/code&gt;, &lt;code&gt;State&lt;/code&gt;, &lt;code&gt;Zip Code&lt;/code&gt;, &lt;code&gt;Email&lt;/code&gt;, and &lt;code&gt;Phone&lt;/code&gt;. These tags act as placeholders that get replaced with actual data when a document is generated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftew1c0iwq31jrquywjsx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftew1c0iwq31jrquywjsx.png" alt="Business Letter template with merge tags" width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve downloaded the Word file, &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-new-template/" rel="noopener noreferrer"&gt;upload it&lt;/a&gt; to your DocuGenerate account to create a new template. The merge tags will be detected automatically, and you can preview the result on the template page before moving on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing the Data into Coda
&lt;/h2&gt;

&lt;p&gt;The Business Letter template in the Template Library comes with a matching &lt;a href="https://www.docugenerate.com/assets/posts/030-coda-business-letters/Business%20Letter.xlsx" rel="noopener noreferrer"&gt;Excel dataset&lt;/a&gt; that contains sample recipient data aligned with all the template’s merge tags. Each row represents one recipient, and each column maps directly to one of the merge tags in the letter.&lt;/p&gt;

&lt;p&gt;To bring this data into Coda, open the &lt;code&gt;Business Letter.xlsx&lt;/code&gt; file in Excel or any spreadsheet application, select the entire table including the header row, and copy it. Then open your Coda document, click in an empty area of the page, and paste. Coda will recognize the tabular structure and create a new table from the clipboard data automatically, preserving all column names and values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1eufrcpvueg8je5guyi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1eufrcpvueg8je5guyi.png" alt="Pasting the Business Letter dataset into a Coda table" width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Generate and PDF Columns
&lt;/h2&gt;

&lt;p&gt;With the data in place, we need to add two columns to the table: one for the generate button and one for storing the output file. Click the &lt;strong&gt;+&lt;/strong&gt; icon at the end of the table header row to add the first new column.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Button&lt;/strong&gt; as the column type, then choose &lt;strong&gt;New&lt;/strong&gt; to create a blank button configuration. Once the column appears, rename it to &lt;strong&gt;Generate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiv48psmpz7euyr4z7sd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiv48psmpz7euyr4z7sd.png" alt="Adding a Button column to the table" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat the same process to add a second column. This time, select &lt;strong&gt;File&lt;/strong&gt; as the column type and rename the column to &lt;strong&gt;PDF&lt;/strong&gt;. This column will hold the generated PDF file for each row after the button is clicked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqve8umvri2y2rrjgcnkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqve8umvri2y2rrjgcnkt.png" alt="Adding a File column to the table" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The table should now display all the original data columns along with the two new ones at the end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmibq5j2dsku3p9q7yfuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmibq5j2dsku3p9q7yfuw.png" alt="Business Letter table with the Generate and PDF columns added" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing the DocuGenerate Pack
&lt;/h2&gt;

&lt;p&gt;To connect the table to DocuGenerate, we need to add the &lt;a href="https://coda.io/packs/docugenerate-47041" rel="noopener noreferrer"&gt;DocuGenerate Pack&lt;/a&gt; to the Coda document. Click &lt;strong&gt;Insert&lt;/strong&gt; in the top-right corner of the Coda editor and search for &lt;strong&gt;DocuGenerate&lt;/strong&gt; in the Packs search box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F604777gezp3iupbvq4cr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F604777gezp3iupbvq4cr.png" alt="Searching for the DocuGenerate Pack in the Insert panel" width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;DocuGenerate&lt;/strong&gt; from the results and click &lt;strong&gt;Add to doc for free&lt;/strong&gt; to install the Pack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foazfxf4jqnymz8wvrs6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foazfxf4jqnymz8wvrs6q.png" alt="Adding the DocuGenerate Pack to the Coda document" width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once installed, the Pack’s building blocks will become available in your document: the &lt;strong&gt;Templates&lt;/strong&gt; table, the &lt;strong&gt;Documents&lt;/strong&gt; table, the &lt;strong&gt;Generate Document&lt;/strong&gt; button action, and the &lt;strong&gt;DataStructure&lt;/strong&gt; formula.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F200d7ficl4t7yjc079g5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F200d7ficl4t7yjc079g5.png" alt="DocuGenerate Pack building blocks available in Coda" width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To complete the setup, open the &lt;strong&gt;Settings&lt;/strong&gt; tab next to &lt;strong&gt;Building blocks&lt;/strong&gt; , click &lt;strong&gt;Select an account&lt;/strong&gt; , and then &lt;strong&gt;Add a new account&lt;/strong&gt;. Enter your &lt;a href="https://www.docugenerate.com/help/articles/where-can-i-find-my-api-key" rel="noopener noreferrer"&gt;API Key&lt;/a&gt; from your DocuGenerate account in the &lt;strong&gt;API token&lt;/strong&gt; field and click &lt;strong&gt;Continue&lt;/strong&gt;. Your DocuGenerate account is now connected and the Pack is ready to use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zs92iwk799aj3qhd2ju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zs92iwk799aj3qhd2ju.png" alt="Connecting a DocuGenerate account in Coda Pack settings" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Generate Document Button
&lt;/h2&gt;

&lt;p&gt;With the Pack installed, we can configure the &lt;strong&gt;Generate&lt;/strong&gt; column to call DocuGenerate when clicked. Click the column header menu for the &lt;strong&gt;Generate&lt;/strong&gt; column and select &lt;strong&gt;Edit column&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dyi9yy4v3w4ujguy1n2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dyi9yy4v3w4ujguy1n2.png" alt="Opening the Edit column panel for the Generate column" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;On click&lt;/strong&gt; action section, scroll down to the &lt;strong&gt;Packs&lt;/strong&gt; category at the bottom of the action list, find &lt;strong&gt;DocuGenerate&lt;/strong&gt; , and select the &lt;strong&gt;Generate Document&lt;/strong&gt; action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy4x11d7jfb5evv5o40ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy4x11d7jfb5evv5o40ig.png" alt="Selecting the Generate Document action from the DocuGenerate Pack" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;+ Connect an account&lt;/strong&gt; and select the DocuGenerate account you configured in the previous section. With the account connected, you can now fill in the action parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Template Selection
&lt;/h3&gt;

&lt;p&gt;From the &lt;strong&gt;Template&lt;/strong&gt; dropdown, select the &lt;strong&gt;Business Letter&lt;/strong&gt; template you uploaded to DocuGenerate in the template setup step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh0b01g4z2rk3lgkodc2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh0b01g4z2rk3lgkodc2o.png" alt="Selecting the Business Letter template in the Generate Document action" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Document Name and Format
&lt;/h3&gt;

&lt;p&gt;For the &lt;strong&gt;Name&lt;/strong&gt; field, you can leave it empty to use a default name, or set a dynamic value to make it easier to identify each file. Coda’s &lt;a href="https://coda.io/formulas#Format" rel="noopener noreferrer"&gt;Format formula&lt;/a&gt; is a good fit here. For example, &lt;code&gt;=Format("Letter for {1}", thisRow.Name)&lt;/code&gt; names each document after the recipient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F030-coda-business-letters%2Fgenerate-button-name-formula.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F030-coda-business-letters%2Fgenerate-button-name-formula.gif" alt="Using the Format formula to set a dynamic document name" width="599" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave &lt;strong&gt;Format&lt;/strong&gt; set to &lt;code&gt;.pdf&lt;/code&gt; to produce PDF files, which is the most commonly used format for sharing documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Data Values
&lt;/h3&gt;

&lt;p&gt;For the &lt;strong&gt;Data&lt;/strong&gt; field, we need to provide a JSON object that maps the merge tag names in the template to the corresponding column values in the current row. Coda’s &lt;a href="https://coda.io/resources/guides/intro-to-codas-formula-syntax" rel="noopener noreferrer"&gt;Object formula&lt;/a&gt; is the most direct way to build this mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Job Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="nx"&gt;Job&lt;/span&gt; &lt;span class="nx"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Company Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="nx"&gt;Company&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Street Address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="nx"&gt;Street&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;City&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;City&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;State&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Zip Code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="nx"&gt;Zip&lt;/span&gt; &lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Phone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;thisRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each key in the &lt;code&gt;Object&lt;/code&gt; call is a merge tag name from the template, and each value references the matching column in the current row using &lt;code&gt;thisRow&lt;/code&gt;. Column names that contain spaces must be wrapped in square brackets, as shown with &lt;code&gt;thisRow.[Job Title]&lt;/code&gt; and &lt;code&gt;thisRow.[Company Name]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1vjjxvf4rwerun9hx83e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1vjjxvf4rwerun9hx83e.png" alt="Configuring the Data parameter with the Object formula" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For templates with more complex or nested data, the &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-coda/#data-structure-formula" rel="noopener noreferrer"&gt;DataStructure formula&lt;/a&gt; included with the DocuGenerate Pack is a useful alternative. It generates the expected JSON structure for any template automatically, with numbered placeholders that you can fill in using the &lt;code&gt;Format&lt;/code&gt; formula.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result PDF Column
&lt;/h3&gt;

&lt;p&gt;Finally, set the &lt;strong&gt;Results column&lt;/strong&gt; to the &lt;strong&gt;PDF&lt;/strong&gt; column added earlier. This instructs Coda to store the generated file in that column after the button is clicked. You can also customize the button’s appearance in the &lt;strong&gt;Style&lt;/strong&gt; section by setting a label, choosing a color, and picking an icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnt6r8a1bul6pygzlsgup.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnt6r8a1bul6pygzlsgup.gif" alt="Customizing the button label, color, and icon" width="599" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the Documents
&lt;/h2&gt;

&lt;p&gt;With the configuration complete, click the &lt;strong&gt;Generate&lt;/strong&gt; button in any row to trigger document generation for that recipient. DocuGenerate will use the row’s data to fill in the merge tags in the template and return a PDF file. Coda then imports the file into its storage and displays it in the &lt;strong&gt;PDF&lt;/strong&gt; column for that row. The entire process takes a few seconds, so expect a brief delay before the file appears.&lt;/p&gt;

&lt;p&gt;You can click &lt;strong&gt;Generate&lt;/strong&gt; for multiple rows in quick succession without waiting for each document to finish. DocuGenerate processes the requests concurrently, so all documents are generated in parallel. The number of documents you can process at the same time depends on your plan’s &lt;a href="https://www.docugenerate.com/help/articles/is-there-a-concurrent-documents-limit/" rel="noopener noreferrer"&gt;concurrent documents limit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcz35bya5svm32xm11mq9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcz35bya5svm32xm11mq9.gif" alt="Generating multiple business letters from the Coda table" width="559" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we built a Coda document that generates personalized PDF business letters from a table of recipient data using the DocuGenerate Pack. The workflow covers uploading a Word template to DocuGenerate, importing the recipient data into Coda, adding the &lt;strong&gt;Generate&lt;/strong&gt; button and &lt;strong&gt;PDF&lt;/strong&gt; columns, and wiring up the &lt;strong&gt;Generate Document&lt;/strong&gt; action to map data from each row to the template’s merge tags. Once the setup is in place, generating a letter for any recipient takes a single button click.&lt;/p&gt;

&lt;p&gt;The same pattern applies to any document type you want to produce from tabular Coda data. Invoices, contracts, offer letters, and certificates all follow the same structure: a Word template with merge tags, a table with the corresponding data, and a button to trigger the process. You can extend this further by combining it with other Coda automations, such as generating documents automatically when a new row is added, or sending the generated files to recipients via email.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/030-coda-business-letters/Business%20Letter.docx" rel="noopener noreferrer"&gt;Business Letter&lt;/a&gt; Word template and &lt;a href="https://www.docugenerate.com/assets/posts/030-coda-business-letters/Business%20Letter.xlsx" rel="noopener noreferrer"&gt;Excel dataset&lt;/a&gt; used in this tutorial.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://coda.io/packs/docugenerate-47041" rel="noopener noreferrer"&gt;DocuGenerate Pack on Coda&lt;/a&gt; for adding document generation to your Coda docs.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-coda/" rel="noopener noreferrer"&gt;Coda integration guide&lt;/a&gt; in the Help Center, including setup instructions and formula references.&lt;/li&gt;
&lt;li&gt;Official &lt;a href="https://coda.io/formulas" rel="noopener noreferrer"&gt;Coda formula reference&lt;/a&gt; for formulas used in this tutorial.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>letters</category>
      <category>coda</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Automate PDF Certificate Generation with FlowRunner</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Wed, 21 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/automate-pdf-certificate-generation-with-flowrunner-30ji</link>
      <guid>https://dev.to/docugenerate/automate-pdf-certificate-generation-with-flowrunner-30ji</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Certificates are a common output in many business processes, from training completion records to achievement awards and event participation confirmations. Manually creating individual certificates for each recipient is tedious and error-prone, especially when dealing with dozens or hundreds of records. In this tutorial, we’ll build an automated workflow using &lt;a href="https://flowrunner.ai/" rel="noopener noreferrer"&gt;FlowRunner&lt;/a&gt; and &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; that generates PDF certificates every time a new record is added to a database table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.flowrunner.ai/" rel="noopener noreferrer"&gt;FlowRunner&lt;/a&gt; is part of the &lt;a href="https://backendless.com/" rel="noopener noreferrer"&gt;Backendless&lt;/a&gt; platform and makes it easy to create intelligent workflows with just a few clicks. It orchestrates automated workflows that combine AI agents, human decision-making, and system integrations. Both technical and non-technical users can build workflows. Non-technical users work through the visual interface while technical users can leverage custom code and advanced integrations.&lt;/p&gt;

&lt;p&gt;The workflow we’ll create listens for new records in a Backendless database table, generates a personalized PDF certificate using DocuGenerate, saves the generated file in the Backendless File Service, and updates the original database record with a link to the PDF file. By the end of this guide, you’ll have a fully functional certificate automation pipeline that you can adapt for any document type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Certificate Template
&lt;/h2&gt;

&lt;p&gt;Before building the FlowRunner workflow, we need to prepare the certificate template in DocuGenerate. To keep things simple, we’ll use the ready-made &lt;a href="https://www.docugenerate.com/assets/posts/029-flowrunner-certificates/Certificate%20of%20Achievement.doc" rel="noopener noreferrer"&gt;Certificate of Achievement&lt;/a&gt; from our &lt;a href="https://www.docugenerate.com/templates/" rel="noopener noreferrer"&gt;Template Library&lt;/a&gt;. This is a standard certificate layout with merge tags for &lt;code&gt;Company Name&lt;/code&gt;, &lt;code&gt;Name&lt;/code&gt;, &lt;code&gt;Course Name&lt;/code&gt;, &lt;code&gt;Diploma Title&lt;/code&gt;, and &lt;code&gt;Certificate Date&lt;/code&gt;. You can customize the template by modifying the design, adjusting the layout, or adding other &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; to match your organization’s branding and requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftghlqwqqlu2ctamtp46o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftghlqwqqlu2ctamtp46o.png" alt="Certificate of Achievement template with merge tags" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve downloaded the template file, &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-new-template/" rel="noopener noreferrer"&gt;upload it&lt;/a&gt; to your DocuGenerate account. After the upload is complete, take note of the &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-get-the-template-id/" rel="noopener noreferrer"&gt;template ID&lt;/a&gt; displayed on the template page, as you’ll need it when configuring the document generation step in the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Participants Table
&lt;/h2&gt;

&lt;p&gt;Next, we need a database table in Backendless to store the data used for generating certificates. Navigate to the &lt;strong&gt;Database&lt;/strong&gt; section of your Backendless app and create a new table called &lt;code&gt;Participants&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfllb2mxvspks52lsuhq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flfllb2mxvspks52lsuhq.png" alt="Creating the Participants table in Backendless" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the following columns to the table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Certificate Date&lt;/code&gt; (DATETIME): The date to be printed on the certificate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Certificate URL&lt;/code&gt; (STRING): Stores the URL of the generated certificate file. This column will be populated automatically by the workflow.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Company Name&lt;/code&gt; (STRING): The name of the company or organization issuing the certificate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Course Name&lt;/code&gt; (STRING): The name of the course or program the participant completed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Diploma Title&lt;/code&gt; (STRING): The title of the diploma or achievement being awarded.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Name&lt;/code&gt; (STRING): The participant’s full name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqod6nmz7r3o9z36dva8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqod6nmz7r3o9z36dva8.png" alt="Database table schema for the Participants table" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Certificate URL&lt;/code&gt; column starts empty for each new record. The workflow will fill it in automatically with the URL of the generated PDF after the entire flow completes, providing a direct link back to the certificate from the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Database Trigger
&lt;/h2&gt;

&lt;p&gt;With the table in place, head over to the &lt;strong&gt;FlowRunner Designer&lt;/strong&gt; page and create a new flow called “Certificate of Achievement Flow”. The first element we need is a trigger that starts the flow whenever a new participant record is created. In the &lt;strong&gt;Triggers&lt;/strong&gt; panel, select the &lt;strong&gt;Record Created in Database&lt;/strong&gt; trigger from the &lt;strong&gt;Data Service&lt;/strong&gt; category.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffo3t690nj1kdg36zqgzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffo3t690nj1kdg36zqgzo.png" alt="Adding the Record Created in Database trigger" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trigger configuration is straightforward. Select the &lt;code&gt;Participants&lt;/code&gt; table in the &lt;strong&gt;Table or View&lt;/strong&gt; dropdown and set &lt;strong&gt;Reference Trigger Data As&lt;/strong&gt; to &lt;code&gt;Participant&lt;/code&gt;. You can leave all other fields at their default values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnzf1u20jpti2ek5p1gez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnzf1u20jpti2ek5p1gez.png" alt="Configuring the Record Created in Database trigger" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that every time a new row is inserted into the &lt;code&gt;Participants&lt;/code&gt; table, the flow will execute and the record data will be accessible as the &lt;code&gt;Participant&lt;/code&gt; object throughout the remaining steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the Document
&lt;/h2&gt;

&lt;p&gt;With the trigger in place, we can add the document generation step. Find the &lt;a href="https://flowrunner.ai/integrations/docugenerate" rel="noopener noreferrer"&gt;DocuGenerate extension&lt;/a&gt; and select the &lt;strong&gt;Generate Document&lt;/strong&gt; action. This action will execute every time the trigger fires, generating a new certificate based on the participant’s data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffjy2op0dqyultelzniqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffjy2op0dqyultelzniqs.png" alt="Adding the Generate Document action from DocuGenerate" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the action with the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template ID&lt;/strong&gt; : The ID of the template you uploaded to DocuGenerate in the earlier step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; : Use the Expression input to create a dynamic name by concatenating the text &lt;code&gt;Certificate for&lt;/code&gt; with the &lt;code&gt;Participant -&amp;gt; Name&lt;/code&gt; value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output Name&lt;/strong&gt; : Leave empty, the generated file will use the document name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output Format&lt;/strong&gt; : Set to &lt;code&gt;.pdf&lt;/code&gt; to produce a PDF file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; : Use the Expression input to construct the JSON data object (explained below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, set &lt;strong&gt;Reference Result Data As&lt;/strong&gt; to &lt;code&gt;Document&lt;/code&gt; and leave the remaining parameters unchanged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsw7huerdzjle5pwjeiuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsw7huerdzjle5pwjeiuw.png" alt="Generate Document action configuration" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Data&lt;/strong&gt; parameter requires a JSON array that maps each merge tag in the template to the corresponding value from the &lt;code&gt;Participant&lt;/code&gt; record. FlowRunner’s Expression input lets you combine static text with dynamic block results to construct this JSON at runtime. You alternate between Text Inputs (for the JSON structure and key names) and Block Results (to reference the &lt;code&gt;Participant&lt;/code&gt; object’s properties). The resulting expression produces a value equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Company Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Participant -&amp;gt; Company Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Participant -&amp;gt; Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Course Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Participant -&amp;gt; Course Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Diploma Title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Participant -&amp;gt; Diploma Title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Certificate Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Participant -&amp;gt; Certificate Date"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The screenshot below shows the Expression input with all the text and reference blocks assembled. Each key in the JSON object corresponds to a merge tag in the certificate template, and each value is dynamically pulled from the participant’s database record.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2asztm9offvrgseqfei2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2asztm9offvrgseqfei2.png" alt="Expression input for constructing the Data parameter" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving the File to Backendless
&lt;/h2&gt;

&lt;p&gt;Now that the document generation step is configured, we can continue building the workflow by saving the generated PDF certificate to the Backendless File Service. In the &lt;strong&gt;Extensions&lt;/strong&gt; panel, go to &lt;strong&gt;Backendless File Services&lt;/strong&gt; and select the &lt;strong&gt;Add To File&lt;/strong&gt; action. This action appends content to an existing file, and can also create a new file from a remote URL using the &lt;strong&gt;Content From URL&lt;/strong&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqcoc3gxvglexvfsorc2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqcoc3gxvglexvfsorc2y.png" alt="Adding the Add To File action" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the action with the following body parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Directory Path&lt;/strong&gt; : Set to &lt;code&gt;certificates&lt;/code&gt; to save all generated files in a dedicated folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Name&lt;/strong&gt; : Use the expression &lt;code&gt;Document -&amp;gt; filename&lt;/code&gt; to name the file based on the document generated in the previous step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content From URL&lt;/strong&gt; : Use &lt;code&gt;Document -&amp;gt; document_uri&lt;/code&gt; to download the generated PDF from DocuGenerate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F10vbb69w7l756d5djd8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F10vbb69w7l756d5djd8x.png" alt="Add To File action configuration" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don’t forget to set &lt;strong&gt;Reference Result Data As&lt;/strong&gt; to &lt;code&gt;File&lt;/code&gt;. The &lt;code&gt;File&lt;/code&gt; object will contain a &lt;code&gt;fileURL&lt;/code&gt; property that we’ll use in the next step to store the file’s location back in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the Database Record
&lt;/h2&gt;

&lt;p&gt;For the final step of the workflow, we need to update the original &lt;code&gt;Participants&lt;/code&gt; record with the URL of the saved certificate. This creates a direct link between each participant’s database entry and their generated certificate, making it easy to retrieve or display the file later, whether from a web application, a report, or directly from the database table.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Actions&lt;/strong&gt; panel, go to &lt;strong&gt;Data Service&lt;/strong&gt; and select the &lt;a href="https://docs.flowrunner.ai/reference/save-record-action.html" rel="noopener noreferrer"&gt;Save Record In Database&lt;/a&gt; action. This action stores or updates data in your Backendless database when executed and can be placed at any point in the workflow to ensure timely data updates in response to specific events or actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd6zqgsjp6zxv0b6093ok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd6zqgsjp6zxv0b6093ok.png" alt="Adding the Save Record In Database action" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To configure the action, select the &lt;strong&gt;Update record from flow&lt;/strong&gt; option and choose &lt;strong&gt;Use data from&lt;/strong&gt; the &lt;code&gt;Participant&lt;/code&gt; object. In the &lt;strong&gt;Perform Changes&lt;/strong&gt; section, select the &lt;code&gt;Certificate URL&lt;/code&gt; property and enter the &lt;code&gt;File -&amp;gt; fileURL&lt;/code&gt; value. This writes the URL of the file we saved in the previous step back into the participant’s record. Optionally, you can set the &lt;strong&gt;Reference Result Data As&lt;/strong&gt; to &lt;code&gt;Record&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2kzr9bf3g4vig79byns3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2kzr9bf3g4vig79byns3.png" alt="Save Record In Database action configuration" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Workflow
&lt;/h2&gt;

&lt;p&gt;With all four steps in place, the workflow is complete and ready for an end-to-end test. The flow follows a clear sequence: a new database record triggers document generation, the generated PDF is saved to Backendless file storage, and the record is updated with the file URL.&lt;/p&gt;

&lt;p&gt;To &lt;a href="https://docs.flowrunner.ai/flow-execution/overview.html" rel="noopener noreferrer"&gt;start the flow&lt;/a&gt;, click the &lt;strong&gt;Play&lt;/strong&gt; button in the FlowRunner Designer. Note that once a flow is running, you cannot make changes to it, but you can stop it at any time and update the configuration if needed.&lt;/p&gt;

&lt;p&gt;There are multiple ways to test the workflow, and one of the easiest ways is to use the &lt;strong&gt;REST Console&lt;/strong&gt; in the &lt;code&gt;Participants&lt;/code&gt; table. For example, we can run a &lt;strong&gt;POST&lt;/strong&gt; request with the following request body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Company Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wikivu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Brandtr Lusk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Course Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Property-Casualty Insurers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Diploma Title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Food Chemist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Certificate Date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7/12/2024"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response body confirms that a new record has been created in the database. Within moments, the workflow executes and generates the certificate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy7udl0ue3ja4dhuf7hbl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy7udl0ue3ja4dhuf7hbl.png" alt="Testing the flow with the REST Console" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As expected, the generated &lt;a href="https://www.docugenerate.com/assets/posts/029-flowrunner-certificates/Certificate%20for%20Brandtr%20Lusk.pdf" rel="noopener noreferrer"&gt;PDF certificate&lt;/a&gt; appears in the &lt;code&gt;certificates&lt;/code&gt; folder in the &lt;strong&gt;File Service&lt;/strong&gt;. As a future improvement, you could also delete the document from DocuGenerate after saving it to Backendless, keeping your DocuGenerate account tidy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5jyjdw0zj730tork641w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5jyjdw0zj730tork641w.png" alt="Certificate PDF saved in the Backendless File Service" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Participants&lt;/code&gt; table, the &lt;code&gt;Certificate URL&lt;/code&gt; column now contains a link to the file stored in the Backendless file storage, confirming that the entire flow works correctly from trigger to completion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo5aertzdlhqjoe8vit5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo5aertzdlhqjoe8vit5a.png" alt="Database table updated with the certificate URL" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to try this workflow yourself, you can download the complete &lt;a href="https://www.docugenerate.com/assets/posts/029-flowrunner-certificates/Certificate%20of%20Achievement%20Flow.json" rel="noopener noreferrer"&gt;flow definition&lt;/a&gt; and import it into your FlowRunner instance. You’ll need to update the template ID and table references to match your own Backendless app configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we built a complete certificate automation workflow using FlowRunner and DocuGenerate. The flow listens for new records in a Backendless database, generates a personalized PDF certificate for each participant, saves the file to the Backendless File Service, and updates the database record with a link to the generated document. The entire process runs automatically without any manual intervention once the flow is started.&lt;/p&gt;

&lt;p&gt;This workflow demonstrates one practical example of what you can build with FlowRunner and DocuGenerate. The same pattern applies to any scenario where you need to generate documents from database records, such as invoices, contracts, reports, or any other templated document. You could also extend this workflow by adding email notifications, conditional branching based on record data, or integrations with other services available in the Backendless ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/029-flowrunner-certificates/Certificate%20of%20Achievement.doc" rel="noopener noreferrer"&gt;Certificate of Achievement&lt;/a&gt; template used in this tutorial.&lt;/li&gt;
&lt;li&gt;The DocuGenerate &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-backendless/" rel="noopener noreferrer"&gt;integration guide&lt;/a&gt; for Backendless.&lt;/li&gt;
&lt;li&gt;The complete &lt;a href="https://www.docugenerate.com/assets/posts/029-flowrunner-certificates/Certificate%20of%20Achievement%20Flow.json" rel="noopener noreferrer"&gt;flow definition&lt;/a&gt; that you can import and use.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://flowrunner.ai/integrations/docugenerate" rel="noopener noreferrer"&gt;DocuGenerate extension&lt;/a&gt; on FlowRunner.&lt;/li&gt;
&lt;li&gt;Official &lt;a href="https://docs.flowrunner.ai/" rel="noopener noreferrer"&gt;FlowRunner documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>certificates</category>
      <category>backendless</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Generate Health Plan Proposals in Power Apps with DocuGenerate</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Mon, 22 Dec 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/generate-health-plan-proposals-in-power-apps-with-docugenerate-53mk</link>
      <guid>https://dev.to/docugenerate/generate-health-plan-proposals-in-power-apps-with-docugenerate-53mk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Employee onboarding often involves helping new hires navigate complex benefit options, particularly when it comes to selecting health insurance plans. Companies typically offer multiple health plans with varying coverage levels, premiums, and out-of-pocket costs, which can overwhelm new employees trying to make the best decision for their families. In this tutorial, we’ll take an existing &lt;a href="https://www.microsoft.com/en-us/power-platform/products/power-apps" rel="noopener noreferrer"&gt;Power Apps&lt;/a&gt; Health Plan Selector app and enhance it with automated document generation capabilities using &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; and &lt;a href="https://www.microsoft.com/en-us/power-platform/products/power-automate" rel="noopener noreferrer"&gt;Power Automate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Health Plan Selector is a sample application that helps new employees explore company health plans through a series of guided questions. Based on the answers, the app recommends the most suitable plan. However, the original app simply sends the plan details via the user’s default email client. We’ll replace this basic functionality with an automated workflow that generates a professional PDF proposal and delivers it directly to the employee’s inbox without requiring any manual email composition.&lt;/p&gt;

&lt;p&gt;This approach creates a better experience for both HR teams and new employees. The automated workflow ensures consistency in how plan information is presented, creates a formal document that employees can review at their leisure and discuss with family members, and eliminates the manual steps involved in composing and sending these communications. Throughout this guide, we’ll walk through each step of the integration, from understanding the existing app structure to creating a complete document generation workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with the Health Plan Selector Template
&lt;/h2&gt;

&lt;p&gt;Power Apps provides a variety of ready-to-use templates that serve as excellent starting points for building business applications. For this tutorial, we’ll use the Health Plan Selector template, which demonstrates many key Power Apps concepts including user input validation, conditional logic, multi-screen navigation, and data collection.&lt;/p&gt;

&lt;p&gt;To get started, open Power Apps and click on &lt;strong&gt;Start with an app template&lt;/strong&gt;. This option allows you to select from a list of fully functional business app templates that you can use as-is or customize to suit your specific needs. Browse through the available templates until you find the Health Plan Selector app. This sample app is designed specifically for new employee onboarding scenarios, helping users explore company health plans and discover which option will work best for them and their families.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftapel2a5e5qntfzjhk35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftapel2a5e5qntfzjhk35.png" alt="Starting with the Health Plan Selector template" width="799" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you select this template, Power Apps will create a new app in your environment based on the template structure. The app includes all the screens, controls, data collections, and logic needed for the health plan selection workflow. You’ll be able to explore and modify any aspect of the app to fit your organization’s specific health plan offerings and branding guidelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Sample App
&lt;/h2&gt;

&lt;p&gt;The Health Plan Selector app guides users through a multi-step questionnaire to determine the most appropriate health insurance plan. The app asks about coverage needs (individual, family, etc.), desired services (primary care, dental, vision, specialists), budget preferences (basic or premium), and other factors that influence plan selection. Based on these inputs, the app recommends specific plans and displays detailed information including monthly premiums, coverage limits, and out-of-pocket costs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F028-power-apps-health-plan%2Fhealth-plan-app-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F028-power-apps-health-plan%2Fhealth-plan-app-demo.gif" alt="Demo of the Health Plan Selector app workflow" width="560" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app is built with multiple screens that represent different stages of the selection process. After creating the app from the template, you can view all screens in the &lt;strong&gt;Tree view&lt;/strong&gt; panel, which shows screens like &lt;code&gt;HomeScreen&lt;/code&gt;, &lt;code&gt;Step1_Screen&lt;/code&gt;, &lt;code&gt;Step2_Screen&lt;/code&gt;, &lt;code&gt;Step3_Screen&lt;/code&gt;, &lt;code&gt;Step4_Screen&lt;/code&gt;, &lt;code&gt;Recommendation_Screen&lt;/code&gt;, &lt;code&gt;ViewOptions_Screen&lt;/code&gt;, and &lt;code&gt;Share_Screen&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiakemn34fpoliv0jczik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiakemn34fpoliv0jczik.png" alt="App screens in the Tree view" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each screen contains various controls such as text inputs, radio buttons, checkboxes, galleries, and buttons that work together to collect user preferences and display recommendations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examining the Send Button Logic
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we’ll focus specifically on the &lt;code&gt;Share_Screen&lt;/code&gt;, which is where users see their final plan recommendation and have the option to send the details to their email address. This is the screen we’ll modify to integrate document generation capabilities instead of the basic email functionality that currently exists.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Share_Screen&lt;/code&gt; includes a &lt;strong&gt;SEND&lt;/strong&gt; button that currently triggers a &lt;code&gt;mailto&lt;/code&gt; link to compose an email with the plan details. To understand what we need to replace, let’s examine the existing button logic. When you select the &lt;strong&gt;SEND&lt;/strong&gt; button in the app designer, you can view its associated code in the formula bar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb17je3bqpr5d15zagruv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb17je3bqpr5d15zagruv.png" alt="Send button code in the Share_Screen" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code uses the &lt;code&gt;Launch&lt;/code&gt; function to open the user’s default email client with pre-populated subject and body content. This approach has several limitations. It requires the user to have a default email client configured, it opens the email in draft form requiring the user to manually click send, and the formatting is limited to plain text with URL-encoded line breaks.&lt;/p&gt;

&lt;p&gt;The code also performs data collection operations to store the user’s responses in a collection called &lt;code&gt;ResponsesCollect&lt;/code&gt;. Here’s the complete code from the original &lt;strong&gt;SEND&lt;/strong&gt; button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If(!IsBlank(TextInput1_1.Text);Collect(ResponsesCollect;{QuestionId:"1";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Radio1.SelectedText.OptionText});;

If(CountRows(BasicCollect)&amp;gt;=1;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(BasicCollect;1)).OptionText)}));;

If(CountRows(BasicCollect)&amp;gt;=2;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(BasicCollect;2)).OptionText)}));;

If(CountRows(BasicCollect)&amp;gt;=3;Collect(ResponsesCollect;{QuestionId:"3";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(BasicCollect;3)).OptionText)}));;If(CountRows(BasicCollect)&amp;gt;=4;Collect(ResponsesCollect;{QuestionId:"4";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(BasicCollect;4)).OptionText)}));;

If(CountRows(BasicCollect)&amp;gt;=5;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(BasicCollect;5)).OptionText)}));;

If(CountRows(AddONCollect)&amp;gt;=1;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(AddONCollect;1)).OptionText)}));;If(CountRows(AddONCollect)&amp;gt;=2;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(AddONCollect;2)).OptionText)}));;

If(CountRows(AddONCollect)&amp;gt;=3;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(AddONCollect;3)).OptionText)}));;

If(CountRows(AddONCollect)&amp;gt;=4;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(AddONCollect;4)).OptionText)}));;

If(CountRows(AddONCollect)&amp;gt;=5;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(AddONCollect;5)).OptionText)}));;

If(CountRows(AddONCollect)&amp;gt;=6;Collect(ResponsesCollect;{QuestionId:"2";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Text(Last(FirstN(AddONCollect;6)).OptionText)}));;

Collect(ResponsesCollect;{QuestionId:"3";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Radio1_2.SelectedText.OptionText});;

Collect(ResponsesCollect;{QuestionId:"4";ResponseId:Text(CountRows(ResponsesCollect)+1);Response:Radio1_3.SelectedText.OptionText});;

Launch("mailto:"&amp;amp;TextInput1_1.Text&amp;amp;"?subject="&amp;amp;TextBox1_16.Text&amp;amp;" &amp;amp;body=Plan Details:" &amp;amp;TextBox1_16.Text&amp;amp; "%0D%0A" &amp;amp; TextBox3_5.Text &amp;amp; "%0D%0A" &amp;amp;TextBox3_6.Text &amp;amp; "%0D%0A" &amp;amp; TextBox3_7.Text &amp;amp; "%0D%0A" &amp;amp;TextBox3_8.Text&amp;amp; "%0D%0A" &amp;amp; EncodeUrl(TextBox3_9.Text) &amp;amp; "%0D%0A" &amp;amp; EncodeUrl(TextBox3_11.Text));;

Navigate(Screen1;ScreenTransition.Fade);UpdateContext({emailreset:true}));;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code collects response data from various form controls and collections, then constructs a &lt;code&gt;mailto&lt;/code&gt; URL with the email address from &lt;code&gt;TextInput1_1.Text&lt;/code&gt;, the plan name from &lt;code&gt;TextBox1_16.Text&lt;/code&gt; as the subject, and several other text box values concatenated into the body. We’ll replace the &lt;code&gt;Launch&lt;/code&gt; function call with a call to a Power Automate flow that handles document generation and email delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying the Button to Call Power Automate
&lt;/h2&gt;

&lt;p&gt;Instead of launching the email client, we’ll modify the &lt;strong&gt;SEND&lt;/strong&gt; button to trigger a Power Automate flow. This flow will receive the plan details, generate a professional PDF document using DocuGenerate, and send it via email. Replace the entire &lt;code&gt;Launch&lt;/code&gt; function call with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
SendEmailWithDocument.Run(TextInput1_1.Text; TextBox1_16.Text; TextBox3_5.Text; TextBox3_6.Text; TextBox3_7.Text; TextBox3_8.Text; TextBox3_9.Text; TextBox3_11.Text; User().FullName);;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35jlt6uwv8qnj9geqqxc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F35jlt6uwv8qnj9geqqxc.png" alt="Updated button code calling SendEmailWithDocument flow" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This modified code calls a Power Automate flow named &lt;code&gt;SendEmailWithDocument&lt;/code&gt; and passes nine parameters: the email address, plan name, coverage details, price per month, price per individual, out-of-pocket limit, coinsurance percentage, additional services, and the current user’s full name.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;User().FullName&lt;/code&gt; function retrieves the authenticated user’s name from Azure Active Directory, which we can include in the personalized document. These parameters correspond to the data displayed on the &lt;code&gt;Share_Screen&lt;/code&gt; and represent the information we want to include in the generated proposal document.&lt;/p&gt;

&lt;p&gt;You’ll notice that Power Apps shows this flow reference in red initially because the flow doesn’t exist yet. We’ll create the flow in the next section, and once it’s connected to the app, the reference will resolve correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Power Automate Flow
&lt;/h2&gt;

&lt;p&gt;Now we’ll create the Power Automate flow that handles document generation and email delivery. In Power Apps, navigate to the &lt;strong&gt;Power Automate&lt;/strong&gt; section from the left menu and click &lt;strong&gt;Create a new flow&lt;/strong&gt;. Select &lt;strong&gt;Create from blank&lt;/strong&gt; to start with an empty flow canvas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9d89bqdfq9ko1wx4tfq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9d89bqdfq9ko1wx4tfq8.png" alt="Creating a new flow from blank" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Name your flow &lt;code&gt;SendEmailWithDocument&lt;/code&gt; to match the flow name we referenced in the button code. The first step in any Power Apps-triggered flow is the &lt;strong&gt;Power Apps (V2)&lt;/strong&gt; trigger, which defines the parameters that the app will pass to the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ywixaoz9hx0j6ir6rmo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ywixaoz9hx0j6ir6rmo.png" alt="Power Automate input parameters configuration" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Add an input&lt;/strong&gt; for each parameter you need to receive from Power Apps. The parameter types should match the data being passed (all text in this case). The table below shows how each flow parameter maps to the corresponding UI element:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flow Parameter&lt;/th&gt;
&lt;th&gt;UI Element&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextInput1_1.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recipient email address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Plan_Name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextBox1_16.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the recommended health plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Coverage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextBox3_5.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coverage level description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Price_Per_Month&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextBox3_6.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Monthly premium amount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Price_Per_Individual&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextBox3_7.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Individual coverage cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Out_Of_Pocket_Limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextBox3_8.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum out-of-pocket expenses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Coinsurance_Percentage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextBox3_9.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coinsurance percentage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Additional_Services&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TextBox3_11.Text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List of additional covered services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;User().FullName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Employee name from Azure Active Directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Preparing the DocuGenerate Template
&lt;/h2&gt;

&lt;p&gt;Before adding the document generation step to our flow, we need to create a Word template in DocuGenerate that defines the structure and formatting of our health plan proposal. The template uses &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; that correspond to the parameter names we defined in Power Automate, which will be replaced with the actual values from the flow.&lt;/p&gt;

&lt;p&gt;The Word document includes company branding, a clear header, sections for plan details, and additional information that should appear on every proposal. It has merge tags for all nine parameters: &lt;code&gt;[Name]&lt;/code&gt;, &lt;code&gt;[Email]&lt;/code&gt;, &lt;code&gt;[Plan_Name]&lt;/code&gt;, &lt;code&gt;[Coverage]&lt;/code&gt;, &lt;code&gt;[Price_Per_Month]&lt;/code&gt;, &lt;code&gt;[Price_Per_Individual]&lt;/code&gt;, &lt;code&gt;[Out_Of_Pocket_Limit]&lt;/code&gt;, &lt;code&gt;[Coinsurance_Percentage]&lt;/code&gt;, and &lt;code&gt;[Additional_Services]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97b4oyq496oip88amq1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97b4oyq496oip88amq1x.png" alt="Health Plan Proposal template with merge tags" width="799" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once your template is ready, &lt;a href="https://www.docugenerate.com/help/articles/how-do-i-create-a-new-template/" rel="noopener noreferrer"&gt;upload it&lt;/a&gt; to your DocuGenerate account and name it &lt;strong&gt;Health Plan Proposal&lt;/strong&gt;. You can download a copy of the template used in this tutorial &lt;a href="https://www.docugenerate.com/assets/posts/028-power-apps-health-plan/Health%20Plan%20Proposal.docx" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the Document Generation Step
&lt;/h2&gt;

&lt;p&gt;To use DocuGenerate from Power Automate, you need to install the &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-power-apps/#add-the-custom-connector" rel="noopener noreferrer"&gt;custom connector&lt;/a&gt; in your Power Platform environment. The first time you use it, you’ll need to authenticate with your DocuGenerate &lt;a href="https://www.docugenerate.com/help/articles/where-can-i-find-my-api-key/" rel="noopener noreferrer"&gt;API Key&lt;/a&gt;, which you can find in your DocuGenerate account settings. This creates a connection that can be reused across multiple flows in your environment.&lt;/p&gt;

&lt;p&gt;With the template created and the connector available, we can now add the document generation step to our flow. Click &lt;strong&gt;New step&lt;/strong&gt; in your flow and search for “DocuGenerate”. Select the &lt;strong&gt;Generate Document&lt;/strong&gt; action from the list of available operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmqxbjq82xslzbt4of7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbmqxbjq82xslzbt4of7o.png" alt="Searching for the DocuGenerate connector" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To configure the action, select your template from the &lt;strong&gt;Template&lt;/strong&gt; dropdown. If you uploaded your template with the name “Health Plan Proposal,” you should see it in the list. Next, set the &lt;strong&gt;Name&lt;/strong&gt; parameter to specify what the generated document should be called. Select &lt;code&gt;.pdf&lt;/code&gt; as the &lt;strong&gt;Format&lt;/strong&gt; so that the generated document is delivered as a professional PDF rather than an editable Word file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmmlv3tvm4ec5qm3ve3ky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmmlv3tvm4ec5qm3ve3ky.png" alt="Generate Document step configuration" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Data&lt;/strong&gt; parameter, you need to provide a JSON object that contains all the values for the merge tags in your template. Replace each empty string with the appropriate dynamic content from the trigger step. Power Automate will display these as tokens in the expression rather than literal strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Plan_Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Coverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Price_Per_Month"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Price_Per_Individual"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Out_Of_Pocket_Limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Coinsurance_Percentage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Additional_Services"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sending the Email with the Document
&lt;/h2&gt;

&lt;p&gt;The final step in our flow sends an email to the employee with the generated PDF proposal attached. Add a new step and search for the &lt;strong&gt;Send an email notification (V3)&lt;/strong&gt; action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9f2f9v7fzhg4x0utg00x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9f2f9v7fzhg4x0utg00x.png" alt="Send an email notification step configuration" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the email parameters as follows. Set the &lt;strong&gt;To&lt;/strong&gt; field to the &lt;strong&gt;Email&lt;/strong&gt; value from the Power Apps trigger, which is the address the employee entered in the app. For the &lt;strong&gt;Subject&lt;/strong&gt; , use something descriptive like &lt;code&gt;Your Personalized Healthcare Plan Proposal&lt;/code&gt;. In the &lt;strong&gt;Body&lt;/strong&gt; field, compose a friendly message that explains what the attachment contains and provides any next steps the employee should take.&lt;/p&gt;

&lt;p&gt;The most important configuration is the attachments section. This is where you connect the generated PDF to the email. For the &lt;strong&gt;Attachment&lt;/strong&gt; parameter, select the &lt;code&gt;document_uri&lt;/code&gt; output from the &lt;strong&gt;Generate Document&lt;/strong&gt; step. For the &lt;strong&gt;Attachment File Name&lt;/strong&gt; parameter, select the &lt;code&gt;filename&lt;/code&gt; output from the same step. This ensures that the email includes the actual PDF document with the correct filename.&lt;/p&gt;

&lt;p&gt;With this final step configured, save your flow and test it to ensure everything works correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Complete Integration
&lt;/h2&gt;

&lt;p&gt;Now that both the Power Apps app and the Power Automate flow are configured, it’s time to test the complete integration. Run the app in preview mode and go through the health plan selection process. Answer the questions about coverage needs, desired services, and budget preferences. When you reach the confirmation screen, enter your email address and click the &lt;strong&gt;SEND&lt;/strong&gt; button. If everything is configured correctly, the button will trigger the Power Automate flow, which generates the PDF and sends the email.&lt;/p&gt;

&lt;p&gt;The document contains all the plan details with the merge tags replaced by actual values from the app, and the formatting matches the professional template design.&lt;/p&gt;



&lt;p&gt;If you encounter any issues, check the flow run history in Power Automate to see where the process might have failed. Common issues include incorrect parameter mappings, or authentication problems with any of the connectors. The flow run history provides detailed information about each step, including the data that was passed and any error messages that occurred.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of This Approach
&lt;/h2&gt;

&lt;p&gt;Replacing the basic email sending functionality with automated document generation and email delivery offers several advantages for both HR teams and employees. The automated workflow ensures consistency in how health plan information is presented to all employees, eliminating variations that can occur when individuals compose emails manually. The generated PDF serves as a formal document that employees can save, print, and reference throughout the enrollment period, making it easier to compare options or discuss plans with family members.&lt;/p&gt;

&lt;p&gt;From an operational perspective, this integration reduces the manual effort required from HR staff. Instead of having to help employees compose emails or prepare plan summaries individually, the system handles this automatically based on the recommendations generated by the app. This frees up HR time for more valuable activities like answering specific questions or helping employees with complex situations.&lt;/p&gt;

&lt;p&gt;The approach also creates better documentation for compliance purposes. Each generated proposal is a record of what information was provided to the employee, which can be important for regulatory compliance or if questions arise later about what was communicated during the enrollment process. You could extend the workflow further by adding a step that saves each generated proposal to SharePoint or another document management system for centralized record-keeping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Enhancing Power Apps applications with automated document generation capabilities demonstrates the power of the Microsoft Power Platform ecosystem when combined with specialized services like DocuGenerate. The integration we’ve built transforms a simple health plan selector app into a complete onboarding solution that guides employees through plan selection and automatically delivers professional proposal documents.&lt;/p&gt;

&lt;p&gt;This tutorial focused on health plan proposals, but the same integration pattern can be applied to many other Power Apps scenarios. Any time your app collects data that needs to be formatted as a formal document and delivered to users or stored for records, this approach of using Power Automate to orchestrate document generation and distribution can add significant value. Whether you’re generating employment contracts, offer letters, training certificates, or custom reports, the combination of Power Apps for data collection, Power Automate for workflow orchestration, and DocuGenerate for document generation provides a powerful and flexible solution.&lt;/p&gt;

&lt;p&gt;The no-code nature of this integration makes it accessible to business analysts and power users who may not have traditional development skills. By leveraging pre-built connectors and visual workflow designers, you can create sophisticated document automation solutions without writing custom code or managing complex infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/028-power-apps-health-plan/Health%20Plan%20Proposal.docx" rel="noopener noreferrer"&gt;Health Plan Proposal&lt;/a&gt; template used in this tutorial.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/help/articles/automate-document-generation-with-power-apps/" rel="noopener noreferrer"&gt;DocuGenerate Guide&lt;/a&gt; for integrating with Power Apps.&lt;/li&gt;
&lt;li&gt;Official &lt;a href="https://learn.microsoft.com/en-us/power-automate/" rel="noopener noreferrer"&gt;Power Automate documentation&lt;/a&gt; from Microsoft.&lt;/li&gt;
&lt;li&gt;Official &lt;a href="https://learn.microsoft.com/en-us/power-apps/" rel="noopener noreferrer"&gt;Power Apps documentation&lt;/a&gt; from Microsoft.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>proposals</category>
      <category>powerplatform</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Build a Resume Analyzer with Pipedream and DocuGenerate</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Wed, 19 Nov 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/build-a-resume-analyzer-with-pipedream-and-docugenerate-28ba</link>
      <guid>https://dev.to/docugenerate/build-a-resume-analyzer-with-pipedream-and-docugenerate-28ba</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Resume screening is one of the most time-consuming aspects of the hiring process. HR teams often spend hours reviewing resumes to extract key information, evaluate candidates, and prepare summaries for hiring managers. In this tutorial, we’ll build an automated resume analyzer that handles this work for you, combining the power of &lt;a href="https://pipedream.com/" rel="noopener noreferrer"&gt;Pipedream&lt;/a&gt; workflow automation, &lt;a href="https://www.anthropic.com/claude" rel="noopener noreferrer"&gt;Claude AI&lt;/a&gt; for intelligent analysis, and &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; for professional PDF generation.&lt;/p&gt;

&lt;p&gt;The workflow operates automatically whenever a new resume is added to a Dropbox folder. It extracts the text from the resume, uses Claude to analyze the candidate’s experience and qualifications, generates a structured summary with key insights, and creates a professional PDF report that your hiring team can review immediately. The entire process takes just seconds and requires no manual intervention.&lt;/p&gt;

&lt;p&gt;This approach is particularly valuable for organizations that need to process multiple resumes quickly while maintaining consistency in candidate evaluation. By standardizing the analysis format and leveraging AI to extract insights, you can focus your time on interviewing the most promising candidates rather than spending hours on initial screening.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Workflow
&lt;/h2&gt;

&lt;p&gt;The complete workflow consists of seven connected steps that work together to transform a raw resume into a structured analysis report. Here’s how the process flows from start to finish:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4x8m4mem25kxsjdb9pse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4x8m4mem25kxsjdb9pse.png" alt="Complete workflow diagram showing all steps" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workflow begins when you upload a resume to a monitored Dropbox folder. Pipedream detects the new file, downloads it, and extracts the text content from the PDF. This text is then sent to Claude AI with specific instructions to analyze the resume and extract key information like work experience, skills, education, and notable achievements. Claude returns a structured JSON response containing all the extracted data.&lt;/p&gt;

&lt;p&gt;The JSON data is then passed to DocuGenerate along with a pre-designed template to generate a professional PDF summary. Once the document is created, it’s automatically uploaded back to Dropbox in a designated folder where your hiring team can access it. The entire process completes in under a minute, and you can process multiple resumes simultaneously simply by uploading them to the trigger folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Document Template
&lt;/h2&gt;

&lt;p&gt;Before building the Pipedream workflow, we need to create a template that will format our resume analysis reports. The template uses DocuGenerate’s &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; syntax to define where data from Claude’s analysis should appear in the final document. This ensures every candidate summary follows a consistent, professional format.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fltmyw60mgi0thll7wqrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fltmyw60mgi0thll7wqrq.png" alt="Screenshot of the resume summary template in DocuGenerate" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The template includes sections for candidate information (name, email, phone, location), a professional summary, experience overview, key skills, education, strengths, interview recommendations, and an overall assessment with a fit score. Some sections like skills, strengths, and recommendations use &lt;a href="https://www.docugenerate.com/help/articles/how-to-add-lists-to-a-template/" rel="noopener noreferrer"&gt;list syntax&lt;/a&gt; to display multiple items from arrays in the data. For example, the skills section uses &lt;code&gt;[#skills]&lt;/code&gt; to begin the loop, &lt;code&gt;[.]&lt;/code&gt; to display each skill as a bullet point, and &lt;code&gt;[/skills]&lt;/code&gt; to end the loop.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://www.docugenerate.com/assets/posts/027-pipedream-resume-analyzer/Resume%20Analysis%20Summary.docx" rel="noopener noreferrer"&gt;download the complete template&lt;/a&gt; and upload it to your DocuGenerate account. Once uploaded, take note of the template name or ID, as you’ll need this information when configuring the document generation step in the workflow. With the template ready, we can now build the Pipedream workflow that will automate the entire analysis process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Dropbox Trigger
&lt;/h2&gt;

&lt;p&gt;The workflow starts with a &lt;a href="https://pipedream.com/apps/dropbox/triggers/new-file" rel="noopener noreferrer"&gt;Dropbox trigger&lt;/a&gt; that monitors a specific folder for new files. This trigger is the entry point that activates the entire automation whenever a resume is uploaded. To configure this trigger, you’ll need to connect your Dropbox account to Pipedream and specify which folder to monitor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fothcwm99pyziocy6o2xz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fothcwm99pyziocy6o2xz.png" alt="Screenshot of Dropbox trigger configuration" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this tutorial, we’re monitoring a folder called &lt;code&gt;/Pipedream/Resumes&lt;/code&gt;. When you test the trigger with a sample resume, you’ll see that it returns metadata about the file including the filename, path, size, and modification dates. However, it’s important to note that the trigger only provides this metadata and not the actual file content. This is why we need a separate step to download the file, which we’ll configure in the next section.&lt;/p&gt;

&lt;p&gt;The trigger activates immediately when a new file is detected, making the workflow responsive to your hiring needs. You can upload resumes throughout the day, and each one will be processed automatically without any manual intervention. This real-time processing ensures that candidate summaries are available to your team as quickly as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downloading the Resume File
&lt;/h2&gt;

&lt;p&gt;After the trigger detects a new file, we need to actually download the file content before we can extract text from it. The &lt;a href="https://pipedream.com/apps/dropbox/actions/download-file-to-tmp" rel="noopener noreferrer"&gt;Download File to TMP&lt;/a&gt; action retrieves the resume from Dropbox and saves it to Pipedream’s temporary storage, where subsequent steps can access it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsd0ucd1o5hc56m0dbgam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsd0ucd1o5hc56m0dbgam.png" alt="Screenshot of download file configuration" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The configuration is straightforward. The &lt;strong&gt;Path&lt;/strong&gt; parameter uses &lt;code&gt;{{steps.trigger.event.path_display}}&lt;/code&gt; to reference the file path captured by the trigger. This action downloads the file and returns an object containing the temporary file path in the &lt;code&gt;tmpPath&lt;/code&gt; property. The file remains in temporary storage throughout the workflow execution, which is perfect for processing and then discarding after the workflow completes.&lt;/p&gt;

&lt;p&gt;The downloaded file is now ready for text extraction. The temporary file path will be used in the next step to read the PDF content and convert it into text that Claude can analyze.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting Text from the PDF
&lt;/h2&gt;

&lt;p&gt;With the resume downloaded to temporary storage, we now need to extract the text content from the PDF file. This step uses a Node.js code block with the &lt;a href="https://www.npmjs.com/package/pdf-parse" rel="noopener noreferrer"&gt;pdf-parse&lt;/a&gt; library to read the PDF and convert it into plain text that Claude can analyze.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1tq3htsxeq6yiqcas0cf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1tq3htsxeq6yiqcas0cf.png" alt="Screenshot of text extraction code" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code reads the file from the temporary path using Node.js’s built-in &lt;code&gt;fs&lt;/code&gt; module, then passes the file buffer to &lt;code&gt;pdf-parse&lt;/code&gt; for processing. The library handles the complexities of PDF parsing and returns the extracted text as a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFParse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdf-parse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the tmp file path&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download_file_to_tmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$return_value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tmpPath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Read the file from /tmp&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Parse the PDF&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PDFParse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dataBuffer&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resumeText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the PDF content&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resumeText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extracted text is now available in &lt;code&gt;resumeText&lt;/code&gt; and ready to be analyzed by Claude in the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyzing the Resume with Claude
&lt;/h2&gt;

&lt;p&gt;This is where the intelligence happens. The Claude API step sends the extracted resume text to &lt;a href="https://docs.anthropic.com/en/api/getting-started" rel="noopener noreferrer"&gt;Anthropic’s Claude AI&lt;/a&gt; with detailed instructions to analyze the content and return structured data in JSON format. Claude examines the resume and extracts key information including work experience, skills, education, and provides insights that would typically require manual review.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzlttvfj4hv33xlgq77jo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzlttvfj4hv33xlgq77jo.png" alt="Screenshot of Claude API configuration" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The configuration of the &lt;a href="https://pipedream.com/apps/anthropic/actions/chat" rel="noopener noreferrer"&gt;Chat with Anthropic (Claude)&lt;/a&gt; action requires your Anthropic API key, which you can obtain from your Anthropic account. The request uses the &lt;code&gt;claude-sonnet-4-5-20250929&lt;/code&gt; model, which provides an excellent balance of speed, cost, and analytical capability for this task. The &lt;strong&gt;Maximum Tokens to Sample&lt;/strong&gt; parameter is set to &lt;code&gt;4096&lt;/code&gt;, which gives Claude enough space to return a comprehensive analysis with all the fields we need.&lt;/p&gt;

&lt;p&gt;The prompt is carefully structured to guide Claude in extracting specific information and formatting it as valid JSON. Here’s the complete prompt that instructs Claude on what to analyze and how to structure the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;expert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;analyst.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Analyze&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;extract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;information&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;structured&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;format.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;Resume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;content:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="err"&gt;steps.code.$return_value.text&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;Please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;analyze&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;provide&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;comprehensive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;summary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;structure.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;thorough&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;concise:&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YYYY-MM-DD (today's date)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"candidateName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Full name from resume"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email address"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Phone number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"City, State/Country"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2-3 sentence professional summary highlighting key qualifications, experience, and strengths"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"yearsOfExperience"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"X years (calculate total)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currentPosition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Most recent job title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currentCompany"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Most recent company name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skills"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"List 6-10 key technical and professional skills with proficiency levels where relevant"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"highestDegree"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Degree name (e.g., Bachelor of Science in Computer Science)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"institution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"University/College name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fieldOfStudy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Major/Field"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strengths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"List 4-6 key strengths based on achievements, projects, and experience"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"recommendations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"List 4-5 specific interview questions or topics to explore based on their unique experience"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fitScore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rate 1-10 based on overall qualifications and experience"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"finalNotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2-3 sentence overall assessment and hiring recommendation"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;IMPORTANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;INSTRUCTIONS:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Extract&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;information&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;directly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resume&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resume,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Not specified"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;empty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;skills,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;proficiency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;levels&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;they&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;inferred&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;years&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;experience&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;explicit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mentions&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Make&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;summary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;compelling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;accurate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fitScore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;years&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;experience,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;skill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;diversity,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;education,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;career&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;progression&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Ensure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;properly&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;formatted&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;errors&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ONLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;object,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;additional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;markdown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;formatting&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;CRITICAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;OUTPUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;REQUIREMENTS:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ONLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;object&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;wrap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;markdown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;blocks&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;endraw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;raw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="err"&gt;```&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;markers&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;explanatory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;before&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;must&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;entire&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;must&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;valid,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;parseable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude processes the resume text and returns a JSON object containing all the extracted information. The response includes everything from basic contact details to insightful recommendations for interview questions based on the candidate’s unique experience. This structured format makes it easy to pass the data to DocuGenerate for document generation in the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing the JSON Response
&lt;/h2&gt;

&lt;p&gt;Claude returns its analysis as a JSON string, but we need to convert this into a structured object that we can work with in subsequent steps. Pipedream’s built-in &lt;a href="https://pipedream.com/apps/formatting/actions/parse-json" rel="noopener noreferrer"&gt;Parse JSON&lt;/a&gt; action found under &lt;a href="https://pipedream.com/apps/formatting" rel="noopener noreferrer"&gt;Formatting → Data&lt;/a&gt; handles this conversion automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6qjex8008iahpdvgmbzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6qjex8008iahpdvgmbzo.png" alt="Screenshot of Parse JSON configuration" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The configuration simply takes the Claude response from &lt;code&gt;{{steps.chat.$return_value.content[0].text}}&lt;/code&gt; and parses it into a structured object. This parsed data becomes available to all subsequent steps in the workflow, allowing us to reference specific fields like &lt;code&gt;{{steps.parse_json.$return_value.candidateName}}&lt;/code&gt; when we need them.&lt;/p&gt;

&lt;p&gt;Having the data parsed into individual fields is particularly useful for dynamic file naming and for passing the complete dataset to DocuGenerate. The parsed object contains all the fields defined in our template: candidate information, summary, skills array, strengths array, recommendations array, and the overall assessment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the Summary Document
&lt;/h2&gt;

&lt;p&gt;With the analyzed data now available as a structured object, we’re ready to generate the professional PDF summary. This step uses the &lt;a href="https://pipedream.com/apps/docugenerate" rel="noopener noreferrer"&gt;DocuGenerate App&lt;/a&gt; on Pipedream to merge the data with our template and create a formatted document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7byu1ytebrt9o4otj3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv7byu1ytebrt9o4otj3g.png" alt="Screenshot of DocuGenerate API configuration" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll need your DocuGenerate API Key to set up a connection, which you can find in your account settings. The action configuration includes several important parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template&lt;/strong&gt; specifies the &lt;strong&gt;Resume Analysis Summary&lt;/strong&gt; template from the setup section&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; uses &lt;code&gt;Resume Analysis for {{steps.parse_json.$return_value.candidateName}}&lt;/code&gt; to create a dynamic filename based on the candidate’s name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt; is set to &lt;code&gt;PDF (.pdf)&lt;/code&gt; to generate a PDF document&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; contains the complete parsed JSON object &lt;code&gt;{{steps.parse_json.$return_value}}&lt;/code&gt; from Claude with all the candidate information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API processes this request and returns a response containing a &lt;code&gt;document_uri&lt;/code&gt; field, which is a URL pointing to the generated PDF document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading the Analysis to Dropbox
&lt;/h2&gt;

&lt;p&gt;The final step completes the workflow by uploading the generated PDF analysis back to Dropbox where your hiring team can access it. The &lt;a href="https://pipedream.com/apps/dropbox/actions/upload-file" rel="noopener noreferrer"&gt;Upload a File&lt;/a&gt; action saves the document to a designated folder, organizing all candidate analyses in one location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft3kbzsbz9ep3dj7f7aan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft3kbzsbz9ep3dj7f7aan.png" alt="Screenshot of upload to Dropbox configuration" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The configuration requires specifying the destination path and the file content to upload:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Path&lt;/strong&gt; specifies the &lt;code&gt;/Pipedream/Analysis&lt;/code&gt; destination folder&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Name&lt;/strong&gt; is set to &lt;code&gt;{{steps.generate_document.$return_value.filename}}&lt;/code&gt; as the generated document’s file name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Path or URL&lt;/strong&gt; references the &lt;code&gt;{{steps.generate_document.$return_value.document_uri}}&lt;/code&gt; value from the previous step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mode&lt;/strong&gt; is set to &lt;code&gt;overwrite&lt;/code&gt; to define what to do if the file already exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once uploaded, the analysis is immediately available to your team, and you can optionally add an email notification step to alert hiring managers when a new candidate summary is ready for review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Complete Workflow
&lt;/h2&gt;

&lt;p&gt;With all steps configured, the workflow is ready to process resumes automatically. To test it, simply upload a resume PDF to the &lt;code&gt;/Pipedream/Resumes&lt;/code&gt; folder in your Dropbox. The workflow will detect the new file within seconds and begin processing it through each step.&lt;/p&gt;

&lt;p&gt;You can monitor the execution in real-time through Pipedream’s interface, which shows the data flowing through each step. Once finished, you’ll find the generated analysis PDF in your &lt;code&gt;/Pipedream/Analysis&lt;/code&gt; folder in Dropbox, ready for your hiring team to review.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7njo2zegg3zajjqvxzhq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7njo2zegg3zajjqvxzhq.png" alt="Example of the generated PDF analysis saved to Dropbox" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending the Workflow
&lt;/h2&gt;

&lt;p&gt;The resume analyzer we’ve built provides a solid foundation, but there are many ways you can extend it to better fit your hiring process. Here are some practical enhancements you might consider adding to make the workflow even more valuable for your organization.&lt;/p&gt;

&lt;p&gt;You could add an email notification step that alerts hiring managers immediately when a new candidate analysis is ready. This notification could include key highlights from Claude’s analysis, such as the fit score and professional summary, along with a direct link to the full PDF report in Dropbox. This eliminates the need for hiring managers to constantly check the folder for new analyses.&lt;/p&gt;

&lt;p&gt;Another useful extension is storing the structured JSON data in a database or spreadsheet. By adding a step that sends the parsed data to &lt;a href="https://pipedream.com/apps/airtable" rel="noopener noreferrer"&gt;Airtable&lt;/a&gt;, &lt;a href="https://pipedream.com/apps/google-sheets" rel="noopener noreferrer"&gt;Google Sheets&lt;/a&gt;, or your applicant tracking system, you can build a searchable database of all candidates. This makes it easy to filter candidates by skills, experience level, or fit score when you’re looking for specific qualifications.&lt;/p&gt;

&lt;p&gt;You might also want to customize the analysis based on the position you’re hiring for. You could create different templates for different roles and use the filename or a designated folder structure to determine which template to use. For example, resumes in &lt;code&gt;/Resumes/Engineering&lt;/code&gt; could use a template that emphasizes technical skills, while those in &lt;code&gt;/Resumes/Sales&lt;/code&gt; could focus more on communication abilities and deal experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building an automated resume analyzer with Pipedream, Claude, and DocuGenerate demonstrates how AI and workflow automation can transform time-consuming manual processes into efficient, consistent operations. The workflow we’ve created processes resumes in seconds, extracts meaningful insights that would take humans much longer to identify, and generates reports that make hiring decisions easier.&lt;/p&gt;

&lt;p&gt;This approach is particularly valuable because it maintains consistency in candidate evaluation. Every resume is analyzed using the same criteria, reducing unconscious bias and ensuring that all candidates are assessed fairly. The structured format of the analysis reports also makes it easier to compare candidates side-by-side and identify the most promising applicants for your open positions.&lt;/p&gt;

&lt;p&gt;The workflow pattern we’ve demonstrated here extends beyond resume analysis. The same approach of extracting text from documents, using AI to analyze and structure the content, and generating formatted reports can be applied to many other document processing scenarios. Whether you’re analyzing contracts, processing customer feedback, or extracting data from research papers, the core workflow remains similar with adjustments to the analysis prompt and output template.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/027-pipedream-resume-analyzer/Resume%20Analysis%20Summary.docx" rel="noopener noreferrer"&gt;Resume Analysis Summary&lt;/a&gt; template for DocuGenerate.&lt;/li&gt;
&lt;li&gt;The official &lt;a href="https://pipedream.com/apps/docugenerate" rel="noopener noreferrer"&gt;DocuGenerate App&lt;/a&gt; for Pipedream.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://pipedream.com/apps/anthropic" rel="noopener noreferrer"&gt;Anthropic (Claude) App&lt;/a&gt; on Pipedream.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://pipedream.com/apps/dropbox" rel="noopener noreferrer"&gt;Dropbox App&lt;/a&gt; on Pipedream.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>pipedream</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Automate PDF Invoice Generation with n8n and DocuGenerate</title>
      <dc:creator>DocuGenerate</dc:creator>
      <pubDate>Fri, 24 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/docugenerate/automate-pdf-invoice-generation-with-n8n-and-docugenerate-8bd</link>
      <guid>https://dev.to/docugenerate/automate-pdf-invoice-generation-with-n8n-and-docugenerate-8bd</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Document automation doesn’t always require cloud-based solutions, sometimes the most effective workflows are the ones that run locally on your machine. In this tutorial, we’ll explore how to build an automated invoice generation system using &lt;a href="https://n8n.io/" rel="noopener noreferrer"&gt;n8n&lt;/a&gt;, a powerful workflow automation tool, combined with &lt;a href="https://www.docugenerate.com/" rel="noopener noreferrer"&gt;DocuGenerate&lt;/a&gt; to create professional PDF invoices.&lt;/p&gt;

&lt;p&gt;The workflow we’ll build is straightforward yet powerful. It monitors a designated folder on your local machine for new Excel files containing invoice data. When a file appears, the workflow automatically reads the data, generates a separate invoice for each row, and saves the completed PDF documents to an output folder. This approach is particularly useful for businesses that need to process invoices locally and maintain full control over their data.&lt;/p&gt;

&lt;p&gt;Throughout this guide, we’ll walk through each node in the n8n workflow, explaining its purpose and configuration. By the end of this tutorial, you’ll have a fully functional local invoice generation system that you can adapt to your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Template
&lt;/h2&gt;

&lt;p&gt;Before building the n8n workflow, we need to prepare our invoice template. DocuGenerate’s &lt;a href="https://dev.to/templates/"&gt;Template Library&lt;/a&gt; includes a ready-to-use &lt;a href="https://www.docugenerate.com/assets/templates/invoices/repair/Repair%20Invoice.zip" rel="noopener noreferrer"&gt;Repair Invoice&lt;/a&gt; template that’s perfect for this tutorial. You can learn more about using templates from the library in our &lt;a href="https://dev.to/blog/how-to-use-the-templates-from-the-template-library/"&gt;previous guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The template contains merge tags like &lt;code&gt;Invoice No&lt;/code&gt;, &lt;code&gt;Customer Name&lt;/code&gt;, &lt;code&gt;Product Name&lt;/code&gt;, and other common invoice fields. These &lt;a href="https://www.docugenerate.com/help/articles/what-are-merge-tags/" rel="noopener noreferrer"&gt;merge tags&lt;/a&gt; will be automatically replaced with actual data from the Excel file during the document generation process. The template also includes a sample Excel file with ten rows of invoice data, which we’ll use to demonstrate the workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu8s2o7l7xy05oewrqs78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu8s2o7l7xy05oewrqs78.png" alt="The Repair Invoice template in DocuGenerate" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve downloaded the template from the library, upload the file &lt;a href="https://www.docugenerate.com/assets/posts/026-n8n-generate-invoices/Repair%20Invoice.docx" rel="noopener noreferrer"&gt;Repair Invoice.docx&lt;/a&gt; to your DocuGenerate account. With the template ready, we can now turn our attention to building the n8n workflow that will automate the entire invoice generation process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Workflow Overview
&lt;/h2&gt;

&lt;p&gt;The workflow we’re building consists of several interconnected nodes that work together to monitor for files, process data, generate documents, and save the results on the disk. Here’s what the complete workflow looks like in n8n:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8w0u6re98n3c5w9nb14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx8w0u6re98n3c5w9nb14.png" alt="The complete n8n workflow" width="799" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workflow begins with a trigger that watches a local folder for new files. When an Excel file is added, the workflow validates the file extension, reads the file content, extracts the data into structured rows, and then loops through each row to generate an individual invoice. Each generated invoice is downloaded and saved to a designated output folder on your local machine. This entire process runs automatically, requiring no manual intervention once configured.&lt;/p&gt;

&lt;p&gt;If you’d like to import this workflow directly into your n8n instance, you can download the &lt;a href="https://www.docugenerate.com/assets/posts/026-n8n-generate-invoices/n8n-workflow.json" rel="noopener noreferrer"&gt;workflow JSON file&lt;/a&gt; and import it. This can save you time and ensure all the nodes are configured correctly. Now let’s examine each node in detail to understand how the workflow operates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Local File Trigger
&lt;/h2&gt;

&lt;p&gt;The workflow starts with the &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.localfiletrigger/" rel="noopener noreferrer"&gt;Local File Trigger&lt;/a&gt; node, which monitors a specific folder on your computer for new files. This trigger is the entry point for the entire automation process. For this tutorial, we’re watching the &lt;code&gt;/Users/docugenerate/n8n/Data&lt;/code&gt; folder, but you can configure this to any directory on your system where you plan to add Excel files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnji9rzyykfjo4qromg1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnji9rzyykfjo4qromg1p.png" alt="Local File Trigger configuration" width="770" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trigger is configured to detect the &lt;code&gt;File Added&lt;/code&gt; event, meaning it activates whenever a new file appears in the monitored folder. This is ideal for batch processing scenarios where you might drop multiple Excel files into a folder throughout the day and want them processed automatically. The trigger captures the file path and passes it to the next node in the workflow.&lt;/p&gt;

&lt;p&gt;For this example, we’ll be using the &lt;strong&gt;Repair Invoice.xlsx&lt;/strong&gt; file from the sample template, which contains ten rows of invoice data. Each row represents a separate invoice with details like invoice number, customer information, service descriptions, and amounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating the File Extension
&lt;/h2&gt;

&lt;p&gt;After the trigger detects a new file, we need to verify that it’s actually an Excel file before attempting to process it. The &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.if/" rel="noopener noreferrer"&gt;If&lt;/a&gt; node serves this purpose by checking whether the file path ends with the &lt;code&gt;.xlsx&lt;/code&gt; extension. This validation step prevents errors that could occur if someone accidentally drops a different file type into the monitored folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4mnmg4iyguha1gdlf24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4mnmg4iyguha1gdlf24.png" alt="Checking if file is an Excel file" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the condition evaluates to true, the workflow continues to the next step. If false, the workflow stops, preventing unnecessary processing of incompatible files. This simple check adds robustness to the workflow and ensures that only valid Excel files are processed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the File from Disk
&lt;/h2&gt;

&lt;p&gt;Once we’ve confirmed that the file is an Excel spreadsheet, the &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.readwritefile/#read-files-from-disk" rel="noopener noreferrer"&gt;Read File from Disk&lt;/a&gt; node retrieves the actual file content from your local machine. This node uses the file path provided by the trigger to locate and read the binary data of the Excel file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foe5npyzm597o5qquapoq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foe5npyzm597o5qquapoq.png" alt="Reading the file from disk" width="799" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The configuration is straightforward. The &lt;strong&gt;File(s) Selector&lt;/strong&gt; parameter is set to &lt;code&gt;{{ $json.path }}&lt;/code&gt;, which references the path captured by the trigger. In the options section, we specify &lt;code&gt;data&lt;/code&gt; as the name of the output binary field where the file content will be stored. This field name will be used in subsequent nodes to access the file data.&lt;/p&gt;

&lt;p&gt;It’s important to note that this node works with files on the same computer running n8n. If you need to handle files between different computers, you would need to use alternative nodes such as FTP, HTTP Request, or AWS S3 nodes. For local processing scenarios like ours, this node provides a simple and efficient solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting Data from the Excel File
&lt;/h2&gt;

&lt;p&gt;With the file content now available as binary data, we need to convert it into a structured format that can be processed by the workflow. The &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.extractfromfile/" rel="noopener noreferrer"&gt;Extract from File&lt;/a&gt; node handles this transformation by parsing the Excel file and converting each row into a separate data item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fglv5rwxpp2lvcze8fx11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fglv5rwxpp2lvcze8fx11.png" alt="Extracting data from the Excel file" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The node is configured with the &lt;strong&gt;Extract from XLSX&lt;/strong&gt; operation, and the &lt;strong&gt;Input Binary Field&lt;/strong&gt; is set to &lt;code&gt;data&lt;/code&gt;, which matches the field name we specified in the previous node. This step is essential because it transforms the raw binary file data into structured JSON objects that contain the invoice information from each row. These structured data items can now be processed individually to generate invoices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looping Through Each Invoice Record
&lt;/h2&gt;

&lt;p&gt;Now that we have the structured data items from the Excel file, we need to process each one individually to generate a separate invoice. The &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.splitinbatches/" rel="noopener noreferrer"&gt;Loop Over Items&lt;/a&gt; node enables this by iterating through the items in batches. For this workflow, we’re using a batch size of one, meaning each invoice is generated and saved before moving to the next one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcwjbv8cbrsfjiq6bsj8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcwjbv8cbrsfjiq6bsj8j.png" alt="Looping over items with batch size of 1" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This batching approach is particularly useful for document generation because it ensures that each invoice is completely processed before starting the next one. It also helps manage system resources when dealing with larger datasets. The loop continues until all items have been processed, with the workflow executing the same sequence of nodes for each invoice record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the Invoice Document
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://dev.to/help/articles/automate-document-generation-with-n8n/#generate-document"&gt;Generate Document&lt;/a&gt; node that takes the data from each row and merges it with the template to create a PDF invoice. To use this node, you’ll need to install the &lt;a href="https://dev.to/help/articles/automate-document-generation-with-n8n/#install-docugenerate"&gt;DocuGenerate node&lt;/a&gt; in your n8n instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhevml15kqnjzdy0doofi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhevml15kqnjzdy0doofi.png" alt="Generating the invoice document" width="800" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The node configuration requires several parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Template Name or ID&lt;/strong&gt; is set to &lt;code&gt;Repair Invoice&lt;/code&gt;, which matches the template we uploaded earlier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt; uses an expression &lt;code&gt;Invoice no {{ $json['Invoice No'] }}&lt;/code&gt; to dynamically name each document based on the invoice number from the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt; is set to &lt;code&gt;PDF (.pdf)&lt;/code&gt; to generate PDF documents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; is configured with &lt;code&gt;{{ $json }}&lt;/code&gt;, which passes the entire current data item to the document generation API. This means all the fields from the Excel row are available for merge tags in the template.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API processes this request and returns a response that includes a &lt;code&gt;document_uri&lt;/code&gt; field pointing to the generated document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downloading the Generated Document
&lt;/h2&gt;

&lt;p&gt;After DocuGenerate creates the invoice, &lt;strong&gt;Get File from URL&lt;/strong&gt; uses the built-in &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/" rel="noopener noreferrer"&gt;HTTP Request&lt;/a&gt; node to retrieve the generated PDF from the URL provided in the previous step’s response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12e1tw52q9nz178bym9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12e1tw52q9nz178bym9i.png" alt="Getting the file from URL" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;URL&lt;/strong&gt; parameter is set to &lt;code&gt;{{ $json.document_uri }}&lt;/code&gt;, which references the document URL returned by the Generate Document node. In the options section, the &lt;strong&gt;Response Format&lt;/strong&gt; is configured as &lt;code&gt;File&lt;/code&gt; to ensure the data is treated as binary content rather than text. The &lt;strong&gt;Put Output in Field&lt;/strong&gt; option is set to &lt;code&gt;data&lt;/code&gt;, storing the PDF content in a field that can be accessed by the next node.&lt;/p&gt;

&lt;p&gt;For this example, the generated document has the filename &lt;code&gt;Invoice no 58-223-0655.pdf&lt;/code&gt;, which was dynamically created based on the invoice number in the first row of our sample data. Each subsequent invoice will have its own unique filename following the same pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving the Invoice to Disk
&lt;/h2&gt;

&lt;p&gt;The final step in the workflow saves the generated invoice PDF to a designated folder on your local machine. The &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.readwritefile/#write-file-to-disk" rel="noopener noreferrer"&gt;Write File to Disk&lt;/a&gt; node handles this operation, storing each completed invoice where you can easily access it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftl2x3jnhbgrjer8a2xhi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftl2x3jnhbgrjer8a2xhi.png" alt="Writing the file to disk" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;File Path and Name&lt;/strong&gt; parameter is configured with &lt;code&gt;/Users/docugenerate/n8n/Invoices/{{ $data.filename }}&lt;/code&gt;, which combines a base directory path with the dynamic filename from the generated document. The &lt;strong&gt;Input Binary Field&lt;/strong&gt; is set to &lt;code&gt;data&lt;/code&gt;, which contains the PDF content retrieved in the previous step.&lt;/p&gt;

&lt;p&gt;This configuration saves all invoices to the same folder, but you could customize this further. For example, you might organize invoices by date, or group them by customer name. The flexibility of n8n expressions allows you to create any folder structure that suits your business needs. Just keep in mind that this node is designed for working with files on the same computer running n8n.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Workflow
&lt;/h2&gt;

&lt;p&gt;With all the nodes configured, the workflow is ready to process invoices automatically. To test it, simply add the &lt;strong&gt;Repair Invoice.xlsx&lt;/strong&gt; file to the &lt;code&gt;Data&lt;/code&gt; folder that the trigger is monitoring. The workflow will immediately detect the new file and begin processing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F026-n8n-generate-invoices%2Fexecute-workflow-v2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fwww.docugenerate.com%2Fassets%2Fposts%2F026-n8n-generate-invoices%2Fexecute-workflow-v2.gif" alt="Executing the workflow" width="559" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The animation above shows the Loop Over Items segment in action, iterating through all ten data items from the Excel file. For each item, the workflow generates an invoice using the template and data, downloads the PDF, and saves it to the destination folder. You can watch the progress in real-time through n8n’s visual interface, which provides excellent visibility into each step of the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fenoc7rjvaj4lagd61da3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fenoc7rjvaj4lagd61da3.png" alt="Generated invoices in the output folder" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the workflow completes, all ten invoices have been generated and saved in the &lt;code&gt;Invoices&lt;/code&gt; folder. This automated approach eliminates the need for manual document creation, saving significant time and reducing the potential for errors that can occur with manual data entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building local automation workflows with n8n and DocuGenerate offers a powerful solution for businesses that need to process documents efficiently while maintaining full control over their data. The workflow we’ve built demonstrates how easy it is to monitor folders, validate files, extract data, and generate professional documents without relying on cloud services or complex integrations.&lt;/p&gt;

&lt;p&gt;This approach is particularly valuable for scenarios where data privacy is paramount, internet connectivity is limited, or you simply prefer to keep your document processing pipeline local. The flexibility of n8n allows you to extend this workflow in numerous ways. You could add email notifications when invoices are generated, create backup copies, or integrate with your existing business systems with other solutions.&lt;/p&gt;

&lt;p&gt;The workflow we’ve created processes repair invoices, but the same pattern can be applied to any document type supported by DocuGenerate. Whether you need to generate contracts, letters, certificates, or reports, the core workflow remains the same. You simply need to adjust the template and ensure your Excel data matches the merge tags in your chosen template.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/posts/026-n8n-generate-invoices/n8n-workflow.json" rel="noopener noreferrer"&gt;n8n workflow JSON file&lt;/a&gt; that you can import into your n8n instance.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.docugenerate.com/assets/templates/invoices/repair/Repair%20Invoice.zip" rel="noopener noreferrer"&gt;Repair Invoice&lt;/a&gt; template from DocuGenerate’s Template Library.&lt;/li&gt;
&lt;li&gt;The official &lt;a href="https://docs.n8n.io/" rel="noopener noreferrer"&gt;n8n documentation&lt;/a&gt; page.&lt;/li&gt;
&lt;li&gt;The complete DocuGenerate &lt;a href="https://dev.to/help/articles/automate-document-generation-with-n8n/"&gt;integration guide&lt;/a&gt; for n8n.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>invoices</category>
      <category>n8n</category>
      <category>nocode</category>
    </item>
  </channel>
</rss>
