Mailgun
Send transactional and marketing email with Mailgun. Use this skill when the project needs to integrate Mailgun for email sending via API or SMTP, mailing lists, email validation, template management, route-based inbound processing, event tracking, or deliverability optimization. Covers the Mailgun REST API, templates, mailing lists, routes, webhooks, and email validation.
You are an email operations specialist who integrates Mailgun into projects. Mailgun
provides a developer-friendly API for transactional and marketing email with strong
support for mailing lists, inbound routing, and email validation.
## Key Points
- Use recipient variables for batch personalization — more efficient than individual sends
- Validate email addresses before adding to mailing lists
- Use tags on every send for filtering events and analytics
- Set up custom tracking domains for branded click/open tracking
- Use Mailgun routes for inbound email processing instead of polling
- Monitor events dashboard for delivery issues
- Use dedicated IPs when sending 50K+ emails/day
- Sending to mailing lists without unsubscribe handling
- Not checking email validation results before bulk sends
- Using the sandbox domain in production
- Ignoring permanent failure webhooks — address will keep bouncing
- Sending marketing email through transactional infrastructure without separation
## Quick Example
```bash
npm install mailgun.js form-data
```
```typescript
const validation = await mg.validate.get('user@example.com');
if (validation.result === 'undeliverable') {
// Don't send — address is bad
}
```skilldb get email-services-skills/mailgunFull skill: 235 linesMailgun Email Integration
You are an email operations specialist who integrates Mailgun into projects. Mailgun provides a developer-friendly API for transactional and marketing email with strong support for mailing lists, inbound routing, and email validation.
Core Philosophy
API and SMTP parity
Mailgun offers identical functionality through REST API and SMTP. Use the API for modern applications and SMTP for legacy systems. Both support tags, variables, and tracking — choose based on your architecture, not feature needs.
Mailing lists are first-class
Unlike services where you bolt on list management, Mailgun's mailing lists are deeply integrated. You can send to a list address, manage subscribers, and handle unsubscribes natively without external tools.
Routes enable email-driven workflows
Mailgun's route system matches incoming email by recipient or header pattern and forwards, stores, or webhooks the message. This enables reply processing, support ticket creation, and email-driven automation.
Setup
Install
npm install mailgun.js form-data
Initialize
import Mailgun from 'mailgun.js';
import formData from 'form-data';
const mailgun = new Mailgun(formData);
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY,
url: 'https://api.mailgun.net', // or https://api.eu.mailgun.net for EU
});
Key Techniques
Simple send
await mg.messages.create('yourdomain.com', {
from: 'App <noreply@yourdomain.com>',
to: ['user@example.com'],
subject: 'Your password reset link',
text: 'Reset: https://...',
html: '<p>Click <a href="...">here</a> to reset.</p>',
'o:tag': ['security', 'password_reset'],
'v:userId': '123',
});
Template send
Create templates in the Mailgun dashboard or via API, then send with variables.
// Create template via API
await mg.domains.domainTemplates.create('yourdomain.com', {
name: 'password-reset',
description: 'Password reset email',
template: '<p>Hi {{name}}, click <a href="{{resetUrl}}">here</a></p>',
});
// Send with template
await mg.messages.create('yourdomain.com', {
from: 'App <noreply@yourdomain.com>',
to: ['user@example.com'],
subject: 'Reset your password',
template: 'password-reset',
'h:X-Mailgun-Variables': JSON.stringify({
name: 'Alice',
resetUrl: 'https://...',
}),
});
Batch send with recipient variables
Send personalized email to many recipients in one API call (up to 1,000).
await mg.messages.create('yourdomain.com', {
from: 'App <noreply@yourdomain.com>',
to: ['user1@example.com', 'user2@example.com'],
subject: 'Your weekly summary, %recipient.name%',
html: '<p>Hi %recipient.name%, here is your summary...</p>',
'recipient-variables': JSON.stringify({
'user1@example.com': { name: 'Alice', id: '1' },
'user2@example.com': { name: 'Bob', id: '2' },
}),
});
Scheduled send
await mg.messages.create('yourdomain.com', {
from: 'App <noreply@yourdomain.com>',
to: ['user@example.com'],
subject: 'Trial ending tomorrow',
html: trialEndingHtml,
'o:deliverytime': 'Thu, 25 Dec 2025 09:00:00 UTC',
});
Mailing lists
// Create list
await mg.lists.create({
address: 'newsletter@yourdomain.com',
name: 'Newsletter',
access_level: 'readonly',
});
// Add member
await mg.lists.members.createMember('newsletter@yourdomain.com', {
address: 'user@example.com',
name: 'Alice',
vars: JSON.stringify({ plan: 'pro' }),
subscribed: true,
});
// Send to list
await mg.messages.create('yourdomain.com', {
from: 'Updates <updates@yourdomain.com>',
to: ['newsletter@yourdomain.com'],
subject: 'What shipped this week',
html: newsletterHtml,
});
Email validation
Verify addresses before sending to reduce bounces.
const validation = await mg.validate.get('user@example.com');
if (validation.result === 'undeliverable') {
// Don't send — address is bad
}
Inbound routes
// Create a route that forwards matching inbound email to your webhook
await mg.routes.create({
priority: 0,
expression: 'match_recipient("support@yourdomain.com")',
action: ['forward("https://yourdomain.com/api/webhooks/mailgun-inbound")', 'store()'],
description: 'Support inbox',
});
Webhook Processing
| Event | Action |
|---|---|
delivered | Mark delivered in message log |
failed (permanent) | Suppress address |
failed (temporary) | Log, monitor for repeated failures |
complained | Suppress from non-essential sends |
opened | Update engagement metrics |
clicked | Update engagement metrics |
unsubscribed | Update subscription state |
export async function POST(req: Request) {
const form = await req.formData();
const event = form.get('event') as string;
const recipient = form.get('recipient') as string;
switch (event) {
case 'failed':
const severity = form.get('severity') as string;
if (severity === 'permanent') {
await suppressAddress(recipient, 'hard_bounce');
}
break;
case 'complained':
await suppressAddress(recipient, 'complaint');
break;
case 'unsubscribed':
await updateSubscription(recipient, false);
break;
}
return new Response('OK');
}
EU vs US Region
Mailgun offers EU and US hosting. For GDPR compliance, use the EU region:
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY,
url: 'https://api.eu.mailgun.net', // EU region
});
Domains must be configured in the matching region.
Best Practices
- Use recipient variables for batch personalization — more efficient than individual sends
- Validate email addresses before adding to mailing lists
- Use tags on every send for filtering events and analytics
- Set up custom tracking domains for branded click/open tracking
- Use Mailgun routes for inbound email processing instead of polling
- Monitor events dashboard for delivery issues
- Use dedicated IPs when sending 50K+ emails/day
Anti-Patterns
- Sending to mailing lists without unsubscribe handling
- Not checking email validation results before bulk sends
- Using the sandbox domain in production
- Ignoring permanent failure webhooks — address will keep bouncing
- Sending marketing email through transactional infrastructure without separation
- Hardcoding API keys instead of using environment variables
Install this skill directly: skilldb add email-services-skills
Related Skills
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.
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.