Using Merge Variables from Lob API

Nancy Luu
5 min readJan 31, 2024

--

Intro:

Lob is a platform that is used to automate the sending of direct mail, making it as easy as sending an email. With the Lob API, you can send an audience file that includes 3rd and 1st party data along with a creative file that has HTML conversion and dynamic parameters. Lob’s HTML templates support the inclusion of dynamic fields, called merge variables‍, which can be populated individually for each mail piece. Once Lob validates the send address for you, they’ll forward your file to their print partners who will send the prints through USPS. Best of all, at the end of the process, you’ll receive the analytics. In this article, I’ll outline building dynamically personalized creatives through the use of merged variables as I feel this capability stands as the linchpin of Lob’s utility, allowing for a sophisticated and tailored approach to content generation.

Diagram illustrating Lob’s real-time direct mail engagement.
Diagram by Lob.js

Context:

Through Program Equity’s application Amplify, community members can champion campaigns for sustainability, fostering activism and catalyzing substantive change. We aim to streamline community advocacy by leveraging the Lob API to craft tailored letters for local representatives to receive. We implemented a system for generating and handling letters using Vue.js on the client side and Express.js on the server side. The following examples serve as a foundational representation of our application, offering a generalized overview and providing a starting point for potential implementations.

Implementation:

FRONTEND:
In the Amplify application we defined a Vue component (LetterLoad.vue) to represent the frontend template for composing and displaying letters. Merge variables in HTML templates are placeholders that allow dynamic content insertion during runtime. In Vue components, you can use double curly braces ({{ }}) to define and utilize merge variables. For instance, if you have an input field in a Vue component, you can capture user input and then use the merge variable syntax to dynamically insert this input into your HTML template.

<template>
<div>
<label for="userInput">Enter Your Name:</label>
<input v-model="userName" id="userInput" />

<label for="constituentInput">I'm in support because:</label>
<textarea v-model="constituentInput" id="constituentInput"></textarea>

<!-- Using merge variable to display user input dynamically -->
<p>My name is {{ userName }}.</p>
<p>{{ constituentInput }}</p>
</div>
</template>

<script>
export default {
data() {
return {
userName: '' // Initialized to an empty string
constituentInput: ''
};
}
};
</script>

BACKEND:
While Amplify had various Express routes, such as those for fetching specific letter templates and validating addresses via the Lob API, let’s narrow our focus to a post route named /generateLetter and dive into the details of merge variables to stay on track. Here is the whole request but let’s break this down step by step:

const express = require('express');
const Lob = require('lob');

const router = express.Router();
const lobApiKey = process.env.LOB_API_KEY; // Assuming you have a Lob API key stored in an environment variable
const lob = new Lob({ apiKey: lobApiKey });

router.post('/generateLetter', async (req, res) => {
try {
const { userName, constituentInput } = req.body;

// Check against Lob's constraints
const mergeVariablesString = JSON.stringify({ userName, constituentInput });

if (mergeVariablesString.length > 25000) {
return res
.status(400)
.json({
msg: "Merge variables payload exceeds Lob's character limit."
})
.end();
}

// Creating the HTML content of the letter with merge variables
const letterBody = `
<p>My name is {{userName}}.</p>
<p>{{constituentInput}}</p>
`;

// Creating Lob letter
const letter = await lob.letters.create({
description: 'Generated Letter',
to: {
name: 'Recipient Name',
address_line1: 'Recipient Address Line 1',
address_line2: 'Recipient Address Line 2',
address_city: 'Recipient City',
address_state: 'Recipient State',
address_zip: 'Recipient Zip Code',
},
from: 'Your Name',
file: letterBody,
color: false,
merge_variables: {
userName,
constituentInput,
},
});

// Assuming you want to send back Lob's response or any other relevant data
res.status(200).json({ letterId: letter.id, expectedDeliveryDate: letter.expected_delivery_date });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});

module.exports = router;

Before anything is done within the route we need to ensure that the merge variables payload is within Lob’s constraints. Any type of value is accepted as long as the object is valid JSON. You can use strings, numbers, booleans, arrays, objects, or null. The max length of the object is 25,000 characters.

(Note: Your variable names cannot contain any whitespace or any of the following special characters: !, ", #, %, &, ', (, ), *, +, ,, /, ;, <, =, >, @, [, \, ], ^, `, {, |, }, ~.)

const mergeVariablesString = JSON.stringify({ userName, constituentInput });

if (mergeVariablesString.length > 25000) {
return res
.status(400)
.json({
msg: "Merge variables payload exceeds Lob's character limit."
})
.end();
}

This post request expects a request body to contain the userName and constituentInput fields we defined in the component above. The route then creates HTML content for the letter using these variables and submits the data in the ‘file’ field to Lob API to generate the letter. Remember, it is important to utilize the merge variable syntax within the letter body to ensure dynamic content is correctly passed to Lob API.

const letterBody = `
<p>My name is {{userName}}.</p>
<p>{{constituentInput}}</p>
`;

This next section uses the Lob SDK to create a new letter. The lob.letters.create method is called with an object containing various parameters. (Note: to and from could also have been merge variables.)

  • description: A description for the letter.
  • to: An object specifying the recipient's details (name, address, etc.).
  • from: The sender's name.
  • file: The HTML content of the letter. Here you can also fetch specific letter templates like we did instead of setting the HTML content inside this post.
  • color: A boolean indicating whether the letter should be in color or black and white.
  • merge_variables: A set of variables that will replace the placeholders in the letter's content.

The method returns a promise, and the result is stored in the letter variable.

const letter = await lob.letters.create({
description: 'Generated Letter',
to: {
name: 'Recipient Name',
address_line1: 'Recipient Address Line 1',
address_line2: 'Recipient Address Line 2',
address_city: 'Recipient City',
address_state: 'Recipient State',
address_zip: 'Recipient Zip Code',
},
from: 'Your Name',
file: letterBody,
color: false,
merge_variables: {
userName,
constituentInput,
},
});

And finally, assuming the Lob Letter creation is successful, the server responds with a JSON object containing information about the generated letter such as such as its letterId and the expectedDeliveryDate. If an error occurs, it is caught and logged to the console and a HTTP status of 500 is sent to the client.

// Assuming you want to send back Lob's response or any other relevant data
res.status(200).json({ letterId: letter.id, expectedDeliveryDate: letter.expected_delivery_date });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'Internal Server Error' });
}

Conclusion:

Utilizing the power of Lob’s API for dynamically personalized direct mail campaigns opens up a world of possibilities for community advocacy and engagement. I hope this article can serve as a building block for your understanding of Lob and it’s capabilities. I encourage you to explore innovative ways to leverage technology and effect positive change in your communities. It truly shines when used for good!

References:

https://docs.lob.com/#tag/Letters/operation/letter_retrieve

--

--

Nancy Luu

Formerly an architect with seven years of experience, now a software engineer with a passion for design, technology, and all things curious.