Master Sending Transactional Emails with Brevo Templates
A Complete Guide to Using Brevo Templates and Dynamic Data for Personalized Emails

⚡ Next.js Implementation Guides
In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
Last month I was automating customer notifications for a local business and realized how messy it becomes when HTML and CSS for emails live inside code. Updating text meant redeploying the backend. Changing layout meant editing dozens of string literals. Sharing the design with non-developers was impossible.
That changed once I started using Brevo’s transactional email templates. Instead of building emails in code, you design and manage them directly in Brevo’s visual editor, then send only the data. This separation of logic and presentation makes life easier for both developers and designers—and adds analytics automatically.
This guide walks through the full implementation: creating a Brevo template, passing parameters dynamically, and sending personalized emails from a Next.js project or an n8n workflow.
Why Templates Beat Inline HTML
When you create emails programmatically, you usually embed long strings of HTML in your codebase. Over time this becomes difficult to maintain and nearly impossible to style visually.
Brevo templates solve this problem by keeping the design in one place and using params to inject personalized content at send time. Designers can edit and preview templates visually, while developers send structured data. Brevo also provides delivery logs and analytics for every message.
Step 1 – Create a Transactional Template in Brevo
-
Log in to Brevo and go to Transactional → Templates.
-
Create a new template and open the Design tab.
-
Use the visual builder or import your own HTML.
-
Reference variables dynamically using double curly brackets:
<h2>Hello {{ params.FIRSTNAME }},</h2> <p>Your order <strong>{{ params.ORDER_ID }}</strong> is ready for pickup.</p> <p> {{ params.LOCATION_NAME }}<br> {{ params.LOCATION_ADDRESS }}<br> {{ params.TIME_START }} – {{ params.TIME_END }}<br> <a href="{{ params.MAPS_URL }}">Open in Google Maps</a> </p> -
Save and note the template ID (you’ll need it for the API call).
Lesson learned: you don’t need to pre-create “attributes” inside Brevo. Any key inside your
paramsobject automatically becomes available in the template. Brevo reads them dynamically when the email is sent.

Step 2 – Send a Test Email from n8n (optional)
If you want to test before integrating into your codebase, you can send an email directly from n8n. Create a Function node and use this snippet:
const fetch = require("node-fetch")
const BREVO_API_URL = "https://api.brevo.com/v3/smtp/email"
const BREVO_API_KEY = "YOUR_BREVO_API_KEY"
const emailBody = {
sender: { email: "no-reply@yourdomain.com", name: "Your Company" },
to: [{ email: "recipient@example.com", name: "Ana" }],
templateId: 11,
params: {
FIRSTNAME: "Ana",
ORDER_ID: "NAROCILO-48",
LOCATION_NAME: "Maribor - parkirišče pri Leclerku",
LOCATION_ADDRESS: "Parkirišče pri Leclerku, Maribor",
TIME_START: "16:00",
TIME_END: "16:10",
MAPS_URL: "https://www.google.com/maps/@46.55625,15.64754,17z"
}
}
return fetch(BREVO_API_URL, {
method: "POST",
headers: {
"accept": "application/json",
"content-type": "application/json",
"api-key": BREVO_API_KEY
},
body: JSON.stringify(emailBody)
})
.then(res => res.json())
.then(data => [{ json: data }])
This gives you a complete round-trip test before writing any application code.

Step 3 – Send Transactional Emails from a Next.js Project
Once your template is ready, sending an email from Next.js is just another fetch call. You can do this from a server action, an API route, or directly in any server-side logic.
// File: app/api/send-email/route.ts
import { NextResponse } from "next/server"
export async function POST() {
const BREVO_API_URL = "https://api.brevo.com/v3/smtp/email"
const BREVO_API_KEY = process.env.BREVO_API_KEY
const body = {
sender: { email: "no-reply@yourdomain.com", name: "Your Company" },
to: [{ email: "customer@example.com", name: "Ana" }],
templateId: 11,
params: {
FIRSTNAME: "Ana",
ORDER_ID: "NAROCILO-48",
LOCATION_NAME: "Maribor - parkirišče pri Leclerku",
LOCATION_ADDRESS: "Parkirišče pri Leclerku, Maribor",
TIME_START: "16:00",
TIME_END: "16:10",
MAPS_URL: "https://www.google.com/maps/@46.55625,15.64754,17z"
}
}
const res = await fetch(BREVO_API_URL, {
method: "POST",
headers: {
accept: "application/json",
"content-type": "application/json",
"api-key": BREVO_API_KEY!
},
body: JSON.stringify(body)
})
const data = await res.json()
return NextResponse.json(data)
}
In this setup:
- The route can be called from anywhere in your app when an order is created or status changes.
- The environment variable
BREVO_API_KEYis securely loaded from.env.local. - You keep your design and copy in Brevo, not in the codebase.
Step 4 – Monitor and Iterate
Brevo’s dashboard gives you analytics, delivery logs, and open/click tracking for every transactional email. You can adjust wording or layout visually in the template editor without redeploying your Next.js project. All data placeholders continue working automatically as long as the keys in params remain consistent.
Conclusion
By switching from inline HTML emails to Brevo’s templates with dynamic params, you achieve a clean separation of logic and design:
- Maintainability: Change templates visually without code changes.
- Collaboration: Designers manage templates, developers send structured data.
- Visibility: Built-in delivery and engagement analytics.
If you’re still embedding HTML emails in your backend or workflows, try this approach. It simplifies maintenance, reduces code churn, and gives your team full visibility into how messages perform.
Thanks, Matija