New: PDF/A-3b conversion with a compliance report included.See Docs

How to Generate a PDF from Airtable Linked Records

7 Minutes Read

Generate a PDF invoice containing an Airtable invoice record and its linked products. Zapier collects the data, a JavaScript step prepares the product rows, and PDF.co fills an HTML template.

You’ll need Airtable, PDF.co, Google Drive, and a Zapier account supporting multi-step Zaps.

Prepare Your Airtable Tables

Create a Products table with:

  • Product Name
  • Price

Create an Invoices table with:

  • Invoice Number
  • Client Name
  • Invoice Date
  • Products: A linked-record field connected to the Products table.
  • Product Names: A lookup of Product Name through the Products field.
  • Product Prices: A lookup of Price through the same Products field.

Create a sample invoice linked to two products. Ensure both products have prices.

Step 1: Create the PDF.co Template

Open HTML to PDF Templates and create a template using this HTML:

<h1>Invoice {{invoice_id}}</h1>
<p>Client: {{client_name}}</p>
<p>Date: {{invoice_date}}</p>

<table>
  <thead>
    <tr>
      <th>Product</th>
      <th>Price</th>
    </tr>
  </thead>
  <tbody>
    {{#each items}}
    <tr>
      <td>{{name}}</td>
      <td>{{price}}</td>
    </tr>
    {{/each}}
  </tbody>
</table>

The {{#each items}} loop creates one row for each product. Save the template and copy its ID.

For more template options, see the HTML template documentation.

Step 2: Create the Airtable Trigger

Create a Zap and select Airtable → New Record.

Sign in to Airtable and authorize Zapier. Select your base, the Invoices table, and the relevant view.

Test the trigger with your completed sample invoice. Confirm that the result includes the invoice details and both product lookup fields.

Step 3: Format the Linked Records

Add Code by Zapier → Run JavaScript.

Under Input Data, create these keys and map their Airtable fields:

  • Invoice_Num: Invoice Number.
  • Client_Name: Client Name.
  • Invoice_Date: Invoice Date.
  • Product_Names: Product Names lookup.
  • Product_Prices: Product Prices lookup.

Inspect the mapped values before continuing. This code accepts arrays, JSON arrays stored as text, or simple comma-separated lists.

For comma-separated input, product names must not contain commas, and prices must use plain numbers without thousands separators. Otherwise, provide arrays or JSON arrays that preserve each complete value.

Paste this code:

function readList(value, field) {
  let list;

  if (Array.isArray(value)) {
    list = value;
  } else {
    const text = String(value ?? "").trim();

    if (!text) {
      throw new Error(`${field} is empty`);
    }

    list = text.startsWith("[")
      ? JSON.parse(text)
      : text.split(",");
  }

  if (!Array.isArray(list) || list.length === 0) {
    throw new Error(`${field} must contain a nonempty list`);
  }

  if (list.some(value =>
    value === null ||
    !["string", "number"].includes(typeof value)
  )) {
    throw new Error(`${field} contains an unsupported value`);
  }

  const result = list.map(value => String(value).trim());

  if (result.some(value => value === "")) {
    throw new Error(`${field} contains a blank item`);
  }

  return result;
}

const names = readList(
  inputData.Product_Names,
  "Product_Names"
);

const prices = readList(
  inputData.Product_Prices,
  "Product_Prices"
);

if (names.length !== prices.length) {
  throw new Error(
    "Product names and prices have different counts"
  );
}

const templateData = {
  invoice_id: inputData.Invoice_Num,
  client_name: inputData.Client_Name,
  invoice_date: inputData.Invoice_Date,
  items: names.map((name, index) => ({
    name,
    price: prices[index]
  }))
};

return {
  template_data: JSON.stringify(templateData)
};

For a first test, use:

  • Product_Names: Product 1,Product 2
  • Product_Prices: 122.00,202.00

Inspect the returned template_data. It should pair Product 1 with 122.00 and Product 2 with 202.00.

The code rejects blank items and unequal list lengths. Matching lengths alone do not prove that the products and prices are correctly paired—compare them with Airtable and never sort the two lists separately.

Step 1: Create a Trigger on Airtable New Record

  • We’ll be creating a new trigger for “New Record in Airtable”.
  • Next, we’ll be adding an Airtable API key to connect to our account. We’ll be configuring all trigger details like Airtable Base, Table name, etc.
  • Finally, we’ll be testing Airtable Trigger and reviewing the result data.
Zapier record
Zapier record

As we notice in the above screenshot, we are receiving invoice id, company name, etc. Interestingly, we are receiving different arrays for product names and prices. One drawback here is that whenever we will be using this information, we’ll have it in a comma-separated list.

Hence, we need to prepare our JSON data in the proper format prior to sending it for PDF creation. In the next step, we need to execute Javascript code to format this data.

Step 2: Add the “Code by Zapier” action as the next step

Here, we’ll be configuring the “Code by Zapier” action to run JavaScript code.

Run JavaScript code by Zapier
Run JavaScript code by Zapier

Next, we’ll be setting up action. At first, we’ll be adding Input Data for Airtable output like “Invoice_Num”, “Product_Names”, etc.

Run JavaScript code by Zapier
Run JavaScript code by Zapier

Input data will be used in JS code as shown below.

const allProducts = inputData.Product_Names.split(',');
const allPrices = inputData.Product_Prices.split(',');
const arrProducts = [];

for(let i = 0; i < allProducts.length; i++){
  arrProducts.push({"name": allProducts[i], "price": allPrices[i]});
}

const finalReturn = {
  invoice_id: inputData.Invoice_Num,
  client_name: inputData.Company_Name,
  invoice_date: inputData.Invoice_Date,
  items: arrProducts 
};

output = {obj_finalReturn: finalReturn, str_finalReturn: JSON.stringify(finalReturn )};

Here, we’re simply creating JSON objects as per our requirements. This object will be used in our next PDF.co “HTML to PDF” action.

The following is the output of the JavaScript code.

Resulting output
Resulting output

Please note the output property “str_finalReturn”. It contains all stringified JSON data that will be sent to PDF.co.

Step 3: Add PDF.co “HTML to PDF Converter” Action

In this step, we’ll be adding PDF.co “HTML to PDF Converter” action.

First, configure PDF.co HTML Template Id. This is the ID of the template we created in the PDF.co app.

Configure PDF.co HTML Template Id
Configure PDF.co HTML Template Id

Second, we’ll be adding value to the “HTML Template Data” field from the JavaScript object we created. Since we already created all formatting there, we just need to pass only the property here.

HTML Template Data
HTML Template Data

Upon testing this action, it’s returning the URL of the output PDF.

Send HTML to PDF result
Send HTML to PDF result

This URL points to the PDF containing all invoice information with multiple/dynamic product data.

Step 4: Generate the PDF

Add PDF.co → HTML to PDF. Sign in to your PDF.co account and approve Zapier’s access.

Configure:

  • HTML Template ID: Enter your saved template’s ID.
  • HTML Template Data: Map template_data from the Code step.
  • HTML or URL Input: Leave blank.
  • Name: Map Invoice Number and append .pdf.
  • Page Size: A4.
  • Page Orientation: Portrait.
  • Render Page Background: Enable if your template uses background colors or images.

Map template_data directly. Do not add surrounding quotes or stringify it again.

See the HTML to PDF action reference for additional settings.

Step 5: Test and Save the PDF

Test the PDF.co action and open its output URL. Confirm that the invoice contains all linked products with the correct prices.

Test an invoice with one product, another with several products, and one with a blank price. The blank-price test should stop in the Code step rather than generate an invoice with shifted prices. If the connector returns a different data format, adjust the mapping before activating the Zap.

To retain the finished PDFs, add Google Drive → Upload File:

  • Folder: Select your invoice folder.
  • File: Map the PDF.co output URL.
  • File Name: Map Invoice Number.
  • File Extension: Enter pdf.
  • Convert to Document?: Select No.

Test the upload and open the saved PDF. PDF.co’s output links are temporary, so save each file during the workflow.

Publish the Zap after the tests succeed. Each new invoice will generate a PDF with a separate row for every linked product.

Conclusion

The Zap now collects an Airtable invoice and its linked product information, converts the lookup values into structured template data, and generates a PDF containing a dynamic row for every linked product.

Related Tutorials

See Related Tutorials