From Data to Document: Automating PDF Reports
This step-by-step guide will walk you through a complete, real-world example: building an application that allows a user to fill out and sign an acceptance report, which is then saved to a database and emailed as a PDF.
Before You Begin
To get started, the first step for everyone is to download the blank acceptance_report.pdf
file. You will need to upload this file to your File Server to serve as the template's background, regardless of which method you choose below.
Once you have the PDF, you have two options:
Build from Scratch: Follow all the steps in this guide to learn the entire process.
Start from a Template: If you'd like to start with the finished application logic, download the Heisenware Tag (
acceptance-report-app.hwt
) file below and import it into your account. You will still need to link your uploaded PDF to the template.
Downloads
Step 1: Create the Database Table
First, we need a place to store the submitted reports. We'll use the defineTable
function to create a table in the internal PostgreSQL database.
In the Flow Builder, place the defineTable
function on the canvas and configure its inputs as shown below.
#name
acceptanceReports
#fields
customerName: string
projectID: string
acceptanceDate: date
comments: text
signature: text
After configuring the function, you must trigger it once to execute the logic and create the table in your database.

Step 2: Build the Form and Submission Logic
Now, we will create the user interface for data entry and build the backend logic to save the submitted data to our database table.
Build the User Interface
First, in the UI Builder, add the widgets for your form.
Drag a Form widget onto the canvas. Select it and configure its fields. It is critical that the field names and data types exactly match your database columns (customerName
, projectID
, acceptanceDate
, comments
). You can also set user-friendly labels for each field.
Drag a Signature widget onto the canvas, placing it below the form.
Drag a Button below the Signature
widget and change its label to Submit
.

Construct the Submission Logic
Next, switch to the Flow Builder to define what happens when the user clicks the Submit
button.
Prepare the Signature Data
The Signature
widget outputs a raw Base64 string, but our database expects an object. We need to format it correctly.
Drag a Memory function onto the canvas and connect the output of the Signature widget to its input.
Add a JavaScript Modifier after the
Memory
. Use the following expression in it. This wraps the signature string in an object with the keysignature
, matching our database column.
{signature: x}

Combine All Form Data
Now we'll merge the main form data with our newly created signature object.
Drag the
mergeObjects
function onto the canvas.Connect the Form widget to the first input of
mergeObjects
.Connect the signature modifier to the second input of
mergeObjects
.Connect both inputs to the trigger of
mergeObjects
to trigger the functionon input update
.

Save to the Database & Trigger the Flow
Drag the
addRow
function onto the canvas.Set its
table
input toacceptanceReports
.Connect the output of the
mergeObjects
function to thedata
input ofaddRow
.Finally, connect the Submit Button to the trigger of the
addRow
functions trigger. This makes the button start the entire save process.

Clear the Form After Submission:
To provide a good user experience, we'll clear the form after a successful submission.
Connect the output of the
addRow
function to the Form widget.Set the command to
call clear
. This will empty the form for the next entry after the data has been saved.

Step 3: Design and Configure the PDF Template
Now that our data submission logic is in place, we will create the visual PDF template and configure the logic to populate it.
Design the Template Layout
First, we need to create the visual layout in the Template Editor.
Upload the acceptance_report.pdf
file to the File server via the Resources panel.
Navigate to PDF Template Editing mode.
Create a new template and name it AcceptanceReport
.
From the Resources Panel, drag the acceptance_report.pdf
file onto the page canvas. This sets it as the static background.
Using the Add Text Placeholder and Add Image Placeholder tools, place placeholders on the background for each of your fields (customerName
, projectID
, acceptanceDate
, comments
, and signature
).
Configure each placeholder with the correct Variable Name in its settings. Optionally, adjust sizes and colors of your placeholders.

Construct the PDF Generation Logic
Now, return to the Flow Builder to connect the template to your existing logic.
From the Functionality Panel, find your AcceptanceReport
instance (under the PDF Templates
Class) and drag its fillTemplate
function onto the canvas.
Connect the output of the mergeObjects
function (from Step 2) to the Values
input of fillTemplate
. This provides the data for the placeholders.
Connect the output of the addRow
function to the trigger of the fillTemplate
function. This ensures the PDF is only generated after the data has been successfully saved to the database.
Our flow is now complete up to the point of generating the PDF. The final step will be to email this document.

Step 4: Send the Report via Email
The final step in our workflow is to automatically email the generated PDF report. We will use the send
function from the internal mailer and configure it to send an email automatically whenever a new report is created.
Return to the Flow Builder to add the final piece of logic to your flow.
From the Functionality Panel, find your internal mailer instance and drag its send
function onto the canvas.
Configure the static inputs for the email
#address
to: [your email address]
#subject
New Acceptance Report
#content
A new report has been submitted for review. Please see attached PDF.
Format the Attachment Data
The send
function's attachment
input requires a specific object format. We can create this directly on the fillTemplate
function's output using a built-in Modifier.
Add a JSONata Modifier to the
fillTemplate
function.In the Modifier's settings, enter the following JSONata expression. This wraps the raw Base64 output (
$
) with a filename and encoding:
{
"filename": "Acceptance Report.pdf",
"content": $,
"encoding": "base64"
}
Connect the PDF and Set the Trigger
Connect the Modifier (from the
fillTemplate
function) to theattachment
input of thesend
function.Now, connect the attachment input to the trigger of the
send
function.Set the trigger to
on input change
.
This creates a simple and powerful data-driven workflow. Whenever a new PDF is generated by the fillTemplate
function, its output is passed to the attachment
input, and this change automatically triggers the send
function.

Step 5: Deployment
Congratulations! 🥳 You have now built a complete application that captures user input, saves it to a database, generates a customized PDF, and emails it as an attachment.
The final step is to deploy your app. Click the Deploy button in the App Builder's toolbar to publish the latest version and make it live.
Once deployed, open the application and test the full workflow. After you fill out and submit the report, the static email address you configured in Step 4 should receive an email with the PDF attachment, which will look similar to the image below.
From here, you can expand on this project by adding more fields, using your own report templates, or integrating this logic into a larger application.

Last updated