Mailjet
Send transactional and marketing email with Mailjet. Use this skill when the project needs to integrate Mailjet for email delivery via REST API or SMTP, template management with MJML, contact list management, segmentation, real-time event tracking, send-time optimization, and A/B testing. Covers the Send API v3.1, template language, contact management, and event webhooks.
You are an expert in Mailjet for transactional and marketing email. You understand
the Send API v3.1, MJML-based template system, contact and list management, real-time
event webhooks, and how to leverage Mailjet for both transactional reliability and
marketing campaigns.
## Key Points
- **Exceeding the 50-message batch limit** -- Mailjet's v3.1 API limits each request to 50 messages. Sending more in a single call returns an error. Chunk recipients into batches of 50.
- **Skipping the TextPart in email sends** -- If you omit the plain-text version, Mailjet auto-generates one poorly. Always provide a real plain-text alternative alongside HTML.
- **Not URL-encoding email addresses in REST paths** -- When fetching contacts by email, the `@` character must be URL-encoded. Unencoded `@` breaks the REST path and returns 404.
- **Use the v3.1 Send API, not v3.** The v3.1 endpoint returns structured per-message
- **Always include TextPart.** Email clients that cannot render HTML fall back to
- **Use template variables, not string interpolation.** Pass dynamic data through
- **Chunk batch sends to 50 messages per call.** Mailjet's v3.1 API limits each
- **Configure event webhooks for every event type.** Set up webhooks in the Mailjet
- **Use contact properties for segmentation.** Store structured data as contact
- **Using v3 Send API by mistake.** If you omit `{ version: "v3.1" }` in the SDK,
- **Exceeding the 15 MB attachment limit.** Mailjet limits total message size to
- **Not URL-encoding contact email in REST paths.** When fetching a contact by email,
## Quick Example
```bash
npm install node-mailjet
```
```
MAILJET_API_KEY=your-public-api-key
MAILJET_SECRET_KEY=your-secret-key
MAILJET_WEBHOOK_URL=https://app.example.com/webhooks/mailjet
```skilldb get email-services-skills/mailjetFull skill: 318 linesMailjet — Email Service
You are an expert in Mailjet for transactional and marketing email. You understand the Send API v3.1, MJML-based template system, contact and list management, real-time event webhooks, and how to leverage Mailjet for both transactional reliability and marketing campaigns.
Core Philosophy
Mailjet unifies transactional and marketing email under a single API and dashboard, which is its primary advantage. Instead of integrating one service for receipts and another for newsletters, Mailjet handles both with a consistent template system, unified event tracking, and shared contact management. This reduces operational overhead but demands discipline: you must still separate transactional and marketing sending identities at the domain level to protect deliverability.
Templates should live in Mailjet, not in your codebase. Mailjet's template system supports variables ({{var:firstName}}), conditional blocks, and MJML for responsive layouts. Keeping templates in the platform lets non-developers update copy, preview renders, and collaborate without code deploys. Your application code should pass structured data through the Variables field and let the template handle presentation.
Event webhooks are your window into deliverability. Configure webhooks for every event type -- sent, open, click, bounce, spam, unsub, blocked -- and process them reliably. Bounces and spam complaints require immediate suppression. Opens and clicks feed engagement metrics that inform future sends. Without webhooks, you are sending email into a void with no feedback loop on what is working and what is damaging your reputation.
Anti-Patterns
- Using the v3 Send API instead of v3.1 -- The v3 endpoint is legacy with a different request/response format. Always use
{ version: "v3.1" }in the SDK for modern features and structured per-message status. - Omitting
TemplateLanguage: truewhen using stored templates -- Without this flag, Mailjet sends the raw template without variable substitution, delivering broken placeholder text to recipients. - Exceeding the 50-message batch limit -- Mailjet's v3.1 API limits each request to 50 messages. Sending more in a single call returns an error. Chunk recipients into batches of 50.
- Skipping the TextPart in email sends -- If you omit the plain-text version, Mailjet auto-generates one poorly. Always provide a real plain-text alternative alongside HTML.
- Not URL-encoding email addresses in REST paths -- When fetching contacts by email, the
@character must be URL-encoded. Unencoded@breaks the REST path and returns 404.
Overview
Mailjet is an email delivery platform offering both transactional and marketing email through a unified REST API. Key differentiators include native MJML support for responsive email templates, real-time collaboration on templates, built-in A/B testing, send-time optimization, and a contact segmentation engine. Mailjet provides both API and SMTP sending, with the v3.1 Send API supporting batch sends with per-recipient personalization in a single call.
Setup & Configuration
Installation
npm install node-mailjet
Client setup
import Mailjet from "node-mailjet";
const mailjet = Mailjet.apiConnect(
process.env.MAILJET_API_KEY!,
process.env.MAILJET_SECRET_KEY!
);
Environment variables
MAILJET_API_KEY=your-public-api-key
MAILJET_SECRET_KEY=your-secret-key
MAILJET_WEBHOOK_URL=https://app.example.com/webhooks/mailjet
Domain authentication
Add the DKIM and SPF records Mailjet provides in Settings > Sender Domains. Set up a custom return-path (bounce) domain for full alignment. Verify the domain to unlock higher sending limits:
// Programmatic sender validation (email-based)
const request = mailjet.post("sender").request({
Email: "notifications@example.com",
Name: "Example App",
});
Core Patterns
Send a single transactional email
async function sendTransactional(
to: string,
toName: string,
subject: string,
html: string
) {
const result = await mailjet.post("send", { version: "v3.1" }).request({
Messages: [
{
From: {
Email: "app@example.com",
Name: "Example App",
},
To: [{ Email: to, Name: toName }],
Subject: subject,
HTMLPart: html,
TextPart: "", // always include a text fallback
},
],
});
return result.body; // { Messages: [{ Status: "success", To: [...], MessageID, MessageUUID }] }
}
Send with a stored template and variables
async function sendFromTemplate(
to: string,
toName: string,
templateId: number,
variables: Record<string, unknown>
) {
return mailjet.post("send", { version: "v3.1" }).request({
Messages: [
{
From: { Email: "app@example.com", Name: "Example App" },
To: [{ Email: to, Name: toName }],
TemplateID: templateId,
TemplateLanguage: true,
Variables: variables,
},
],
});
}
// Usage — template uses {{var:firstName}} syntax
await sendFromTemplate("alice@example.com", "Alice", 12345, {
firstName: "Alice",
resetUrl: "https://app.example.com/reset?token=abc",
});
Batch send with per-recipient personalization
async function sendBatch(
recipients: Array<{ email: string; name: string; variables: Record<string, unknown> }>,
templateId: number
) {
// Mailjet v3.1 supports up to 50 messages per API call
const messages = recipients.map((r) => ({
From: { Email: "app@example.com", Name: "Example App" },
To: [{ Email: r.email, Name: r.name }],
TemplateID: templateId,
TemplateLanguage: true,
Variables: r.variables,
}));
// Chunk into batches of 50
const chunks = [];
for (let i = 0; i < messages.length; i += 50) {
chunks.push(messages.slice(i, i + 50));
}
const results = [];
for (const chunk of chunks) {
const result = await mailjet.post("send", { version: "v3.1" }).request({
Messages: chunk,
});
results.push(result.body);
}
return results;
}
Manage contacts and lists
// Create a contact list
const list = await mailjet.post("contactslist").request({
Name: "Newsletter Subscribers",
});
// Add a contact to a list
await mailjet.post("contactslist", { version: "v3" })
.id(list.body.Data[0].ID)
.action("managecontact")
.request({
Email: "alice@example.com",
Name: "Alice",
Action: "addnoforce", // add without overwriting existing
Properties: {
plan: "pro",
signup_date: "2025-01-15",
},
});
// Remove from list (does not delete the contact)
await mailjet.post("contactslist", { version: "v3" })
.id(listId)
.action("managecontact")
.request({
Email: "alice@example.com",
Action: "remove",
});
Process event webhooks
import { Router, Request, Response } from "express";
const router = Router();
interface MailjetEvent {
event: string;
MessageID: number;
email: string;
mj_campaign_id?: number;
error_related_to?: string;
error?: string;
source?: string;
url?: string;
}
router.post("/webhooks/mailjet", (req: Request, res: Response) => {
const events: MailjetEvent[] = Array.isArray(req.body)
? req.body
: [req.body];
for (const event of events) {
switch (event.event) {
case "sent":
// Successfully delivered to recipient server
break;
case "open":
// Recipient opened
break;
case "click":
// Link clicked — event.url has the target
break;
case "bounce":
// Hard bounce — suppress address
break;
case "blocked":
// Mailjet blocked the send (suppression, policy)
break;
case "spam":
// Spam complaint — suppress immediately
break;
case "unsub":
// Unsubscribe via Mailjet's built-in link
break;
}
}
res.status(200).end();
});
Send via SMTP (alternative to API)
Host: in-v3.mailjet.com
Port: 587 (STARTTLS) or 465 (SSL)
Username: your MAILJET_API_KEY
Password: your MAILJET_SECRET_KEY
import nodemailer from "nodemailer";
const transport = nodemailer.createTransport({
host: "in-v3.mailjet.com",
port: 587,
secure: false,
auth: {
user: process.env.MAILJET_API_KEY,
pass: process.env.MAILJET_SECRET_KEY,
},
});
Best Practices
- Use the v3.1 Send API, not v3. The v3.1 endpoint returns structured per-message status responses and supports modern features. The v3 send endpoint is legacy.
- Always include TextPart. Email clients that cannot render HTML fall back to plain text. If you omit TextPart, Mailjet auto-generates one, but the result is often poor. Write a real text version or use a library to convert HTML.
- Use template variables, not string interpolation. Pass dynamic data through the Variables field so templates can be managed in the Mailjet UI. This separates content from code.
- Chunk batch sends to 50 messages per call. Mailjet's v3.1 API limits each request to 50 messages. Exceeding this returns an error.
- Configure event webhooks for every event type. Set up webhooks in the Mailjet dashboard for sent, open, click, bounce, spam, unsub, and blocked. This gives you full visibility into deliverability.
- Use contact properties for segmentation. Store structured data as contact properties rather than embedding logic in your code. Let Mailjet's segment builder handle audience targeting.
Common Pitfalls
- Using v3 Send API by mistake. If you omit
{ version: "v3.1" }in the SDK, it defaults to v3. The request and response formats differ, causing confusing errors. - Exceeding the 15 MB attachment limit. Mailjet limits total message size to 15 MB. For large attachments, host files externally and link to them.
- Not URL-encoding contact email in REST paths. When fetching a contact by email,
URL-encode the
@character or use the contact ID instead. Unencoded@breaks the REST path. - Forgetting TemplateLanguage: true. If you reference a template by ID but omit
TemplateLanguage: true, Mailjet sends the raw template without variable substitution. - Webhook endpoint returning non-200 status. Mailjet retries failed webhook deliveries. If your endpoint consistently fails, Mailjet disables the webhook. Always return 200 even if you defer processing to a queue.
Install this skill directly: skilldb add email-services-skills
Related Skills
Mailtrap
Send and test email with Mailtrap. Use this skill when the project needs to integrate Mailtrap for email testing in development, transactional email sending in production, email previewing, HTML validation, spam score checking, or sandbox email capture. Covers both Mailtrap Email Testing (sandbox) and Mailtrap Email Sending (production transactional API).
Nodemailer
Send email with Nodemailer in Node.js. Use this skill when the project needs to send email via SMTP, integrate with any SMTP relay (Gmail, Outlook, SES, custom), send attachments, embed images, use HTML templates, or build custom email infrastructure without a managed email API. Covers SMTP transports, pooled connections, attachments, HTML templating, OAuth2, and common relay configurations.
Plunk
Send transactional and marketing email with Plunk. Use this skill when the project needs to integrate Plunk for open-source email, transactional sends, automated sequences, contact management, or self-hosted email infrastructure. Covers the Plunk API, events-based email triggers, automations, contacts, and self-hosting.
Postmark
Send transactional email with Postmark. Use this skill when the project needs to integrate Postmark for high-deliverability transactional email, server-side templates, inbound email processing, message streams, bounce handling, or DMARC reporting. Covers the Postmark API, template system, message streams, webhooks, and deliverability features.
React Email
Build email templates with React Email. Use this skill when the project needs to create type-safe, component-based email templates using React, preview emails during development, render to HTML for any email provider, or build a shared email design system. Covers React Email components, preview server, rendering, and integration with Resend, SendGrid, Postmark, AWS SES, and Nodemailer.
Resend
Send transactional and marketing email with Resend. Use this skill when the project needs to integrate Resend for sending email, managing contacts, handling webhooks, building templates, or setting up mailing lists and broadcasts. Covers single sends, batch sends, scheduling, React Email templates, domain verification, webhook processing, idempotency, deliverability, and compliance.