If you've ever integrated Brevo (formerly Sendinblue) into a Node.js or Next.js SaaS app, you already know the pain:
- You copy-paste transactional email logic across three projects
- Your webhook handler has no signature verification and breaks randomly
- Your "drip sequence" is three setTimeout calls held together with prayer
- GDPR right-to-erasure is a TODO comment from six months ago
- Every new dev on the team re-implements the same contact sync logic
I've shipped three SaaS products in production using Brevo. After extracting and refining the same patterns over and over, I packaged everything into brevo-saas-kit — a production-grade, zero-opinion email automation library for Node.js SaaS applications.
This isn't a thin wrapper around the Brevo API. It's the actual email layer I use in production — with drip workflows, HMAC-verified webhooks, GDPR compliance, BullMQ queue support, and native Next.js App Router + Express integrations.
Transactional emails
Welcome, verification, invoice, password reset, role change, account deletion
Contact management
Create, update, tag, segment, bulk sync
Automation workflows
Onboarding drip, trial reminders, re-engagement, upsell sequences
Webhook handling
HMAC-SHA256 verification + typed event routing
Next.js integration
Server actions (use server) + App Router webhook handler
Express integration
Middleware + webhook router
Queue system
Immediate or async BullMQ processing
Analytics
Campaign stats, contact engagement scoring, CSV/JSON export
Security
In-memory rate limiter, HTML sanitizer, API key validator
GDPR
Right to erasure, signed consent logging, unsubscribe handler
Installation
npm install brevo-saas-kit
or
yarn add brevo-saas-kit
pnpm add brevo-saas-kit
bun add brevo-saas-kit
Optional: async queue support
npm install brevo-saas-kit bullmq
Set your environment variables:
BREVO_API_KEY=xkeysib-your-api-key
BREVO_SENDER_EMAIL=no-reply@yourdomain.com
BREVO_SENDER_NAME=YourApp
BREVO_WEBHOOK_SECRET=your-webhook-secret
Contact list IDs from Brevo dashboard
BREVO_LIST_FREE=1
BREVO_LIST_PRO=2
BREVO_LIST_ENTERPRISE=3
Only needed for async queue mode
REDIS_URL=redis://localhost:6379
Scenario 1: User Signup Flow (Next.js SaaS)
This is the most common real-world use case. User signs up → create Brevo contact → send welcome email → start onboarding drip.
`// app/api/auth/signup/route.js
import { createContact, sendWelcomeEmail, startOnboardingSequence } from 'brevo-saas-kit';
export async function POST(req) {
const { email, name, plan } = await req.json();
// Save to your DB first
const user = await db.users.create({ email, name, plan });
// Brevo: create contact, tag with plan, add to right list
await createContact({
email,
name,
plan, // automatically adds to BREVO_LIST_FREE/PRO/ENTERPRISE
updateIfExists: true,
});
// Send welcome email
await sendWelcomeEmail({
email,
name,
dashboardUrl: https://yourapp.com/dashboard,
});
// Start 5-step onboarding drip (Day 0, 1, 3, 7, 14)
await startOnboardingSequence({ email, name, mode: 'inline' });
return Response.json({ success: true, userId: user.id });
}`
Scenario 2: Production Webhook Handling (Next.js App Router)
The #1 thing I see broken in Brevo integrations: webhooks with no signature verification. Anyone can POST to your endpoint and fake a bounce or unsubscribe event.
brevo-saas-kit handles HMAC-SHA256 verification automatically:
`// app/api/brevo/webhook/route.js
import { createNextjsWebhookHandler } from 'brevo-saas-kit/integrations/nextjs/webhook';
export const POST = createNextjsWebhookHandler({
secret: process.env.BREVO_WEBHOOK_SECRET,
onDelivered: async ({ email, messageId }) => {
await db.emails.update({ status: 'delivered' }, { where: { messageId } });
},
onBounced: async ({ email, isHardBounce, reason }) => {
if (isHardBounce) {
// Suppress future sends, notify your team
await db.users.update({ emailBounced: true }, { where: { email } });
await notifyTeam(Hard bounce: ${email} — ${reason});
}
},
onUnsubscribed: async ({ email }) => {
await db.users.update({ unsubscribed: true }, { where: { email } });
},
onSpamComplaint: async ({ email }) => {
// Immediately suppress — legal obligation in most jurisdictions
await db.users.update({ markedSpam: true }, { where: { email } });
},
onError: async (error) => {
console.error('[brevo-webhook]', error.message);
// Send to your error monitoring (Sentry, etc.)
},
});`
Every event is typed. No more if (event.event === 'hard_bounce') string matching across 200 lines.
Scenario 3: Trial Expiry + Re-engagement (SaaS Lifecycle)
This is where most SaaS email integrations fall apart. You need timed sequences, not just one-off sends.
Scenario 4: Invoice Emails (B2B SaaS)
Stop attaching PDFs nobody reads. Send a clean, structured invoice email.
Scenario 5: GDPR Right to Erasure
If you're selling to EU customers, this isn't optional.
Scenario 6: Express Middleware (REST APIs)
Scenario 7: Contact Engagement Analytics
Know which users are actually reading your emails before you trigger upsell campaigns.
Why Not Just Use the Official @getbrevo/brevo SDK?
The official SDK is a raw API client. You still need to:
- Build every template yourself
- Write webhook signature verification from scratch
- Implement retry logic, rate limiting, error normalization
- Create your own drip/sequence orchestration
- Handle GDPR compliance manually
brevo-saas-kit is the production layer on top of that. It handles the patterns, not just the API calls. Think of it the same way you think of next-auth vs raw JWT — same relationship.
Links
npm: npmjs.com/package/brevo-saas-kit
GitHub: github.com/msal95/brevo-saas-kit
If this saved you time, a ⭐ on GitHub goes a long way. PRs, issues, and feedback welcome.
Top comments (0)