Mailgun Connector

Connect Mailgun to Brevo through Tajo to unify your transactional and marketing email data, sync delivery events and engagement metrics, and consolidate your email infrastructure into a single customer view.

Overview

PropertyValue
PlatformMailgun (by Sinch)
CategoryEmail Marketing
Setup ComplexityEasy
Official IntegrationNo
Data SyncedEvents, Contacts, Deliverability, Campaigns
Auth MethodAPI Key (HTTP Basic Auth)

Features

  • Delivery event sync - Track delivered, bounced, opened, and clicked events
  • Engagement metrics - Sync open and click rates to Brevo contact attributes
  • Bounce management - Automatically suppress bounced addresses in Brevo
  • Complaint handling - Sync spam complaints for list hygiene
  • Domain reputation - Monitor sending domain health and deliverability
  • Transactional email tracking - Correlate transactional sends with marketing data

Prerequisites

Before you begin, ensure you have:

  1. A Mailgun account with a verified sending domain
  2. A Mailgun API key from the Mailgun Dashboard
  3. A Brevo account with API access
  4. A Tajo account with connector permissions

Authentication

API Key Authentication

Mailgun uses HTTP Basic Authentication with api as the username and your API key as the password:

Terminal window
# Get your API key from https://app.mailgun.com/settings/api_security
export MAILGUN_API_KEY=key-your-api-key
export MAILGUN_DOMAIN=your-domain.com
export TAJO_API_KEY=your_tajo_api_key
export BREVO_API_KEY=your_brevo_api_key
// HTTP Basic Auth format
const headers = {
'Authorization': `Basic ${Buffer.from(
`api:${process.env.MAILGUN_API_KEY}`
).toString('base64')}`
};
// Or using curl
// curl -s --user 'api:YOUR_API_KEY' ...

API Key Types

Mailgun provides domain-specific sending keys and account-level API keys. Use domain sending keys for message operations and the account API key for management operations.

Configuration

Basic Setup

connectors:
mailgun:
enabled: true
api_key: "${MAILGUN_API_KEY}"
domain: "${MAILGUN_DOMAIN}"
region: "us" # or "eu" for EU region
sync:
events: true
contacts: true
bounces: true
complaints: true
schedule: "*/15 * * * *" # Every 15 minutes
webhook:
signing_key: "${MAILGUN_WEBHOOK_SIGNING_KEY}"
lists:
engaged: 30
bounced: 31
complained: 32

Field Mapping

field_mapping:
email: email
first_name: FIRSTNAME
last_name: LASTNAME
open_rate: MG_OPEN_RATE
click_rate: MG_CLICK_RATE
last_delivered: MG_LAST_DELIVERED
bounce_type: MG_BOUNCE_TYPE
engagement_score: MG_ENGAGEMENT
unsubscribed: MG_UNSUBSCRIBED

API Endpoints

EndpointMethodDescription
https://api.mailgun.net/v3/{domain}/messagesPOSTSend email messages
https://api.mailgun.net/v3/{domain}/eventsGETQuery event logs
https://api.mailgun.net/v3/{domain}/bouncesGETList bounces
https://api.mailgun.net/v3/{domain}/complaintsGETList complaints
https://api.mailgun.net/v3/{domain}/unsubscribesGETList unsubscribes
https://api.mailgun.net/v3/{domain}/tagsGETList tags
https://api.mailgun.net/v3/{domain}/tags/{tag}/statsGETGet tag statistics
https://api.mailgun.net/v3/listsGETList mailing lists
https://api.mailgun.net/v3/domainsGETList domains
https://api.mailgun.net/v4/address/validatePOSTValidate email address

EU Region

For EU-based Mailgun accounts, use https://api.eu.mailgun.net instead of https://api.mailgun.net for all API endpoints.

Code Examples

Initialize Connector

import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({
apiKey: process.env.TAJO_API_KEY,
brevoApiKey: process.env.BREVO_API_KEY
});
await tajo.connectors.connect('mailgun', {
apiKey: process.env.MAILGUN_API_KEY,
domain: process.env.MAILGUN_DOMAIN,
region: 'us'
});

Send a Message via Mailgun API

// Send an email using Mailgun's Messages API
const formData = new URLSearchParams();
formData.append('from', `Your App <noreply@${domain}>`);
formData.append('to', '[email protected]');
formData.append('subject', 'Welcome to our platform');
formData.append('html', '<h1>Welcome!</h1><p>Thanks for signing up.</p>');
formData.append('o:tag', 'welcome-email');
formData.append('o:tracking', 'yes');
const response = await fetch(
`https://api.mailgun.net/v3/${domain}/messages`,
{
method: 'POST',
headers: {
'Authorization': `Basic ${Buffer.from(`api:${apiKey}`).toString('base64')}`
},
body: formData
}
);
const result = await response.json();
// { id: '<[email protected]>', message: 'Queued. Thank you.' }

Sync Email Events to Brevo

// Query Mailgun events and sync engagement data
const eventsResponse = await fetch(
`https://api.mailgun.net/v3/${domain}/events?` +
new URLSearchParams({
begin: lastSyncDate,
ascending: 'yes',
limit: 300,
event: 'delivered OR opened OR clicked'
}),
{
headers: {
'Authorization': `Basic ${Buffer.from(`api:${apiKey}`).toString('base64')}`
}
}
);
const { items, paging } = await eventsResponse.json();
for (const event of items) {
const email = event.recipient;
switch (event.event) {
case 'delivered':
await tajo.contacts.update(email, {
attributes: { MG_LAST_DELIVERED: event.timestamp }
});
break;
case 'opened':
await tajo.events.track({
email,
event: 'email_opened',
properties: { subject: event.message.headers.subject }
});
break;
case 'clicked':
await tajo.events.track({
email,
event: 'email_clicked',
properties: { url: event.url }
});
break;
}
}
// Follow pagination for more events
if (paging.next) {
// Fetch next page using paging.next URL
}

Handle Mailgun Webhooks

const crypto = require('crypto');
app.post('/webhooks/mailgun', async (req, res) => {
// Verify webhook signature
const { timestamp, token, signature } = req.body.signature;
const encodedToken = crypto
.createHmac('sha256', process.env.MAILGUN_WEBHOOK_SIGNING_KEY)
.update(timestamp.concat(token))
.digest('hex');
if (encodedToken !== signature) {
return res.status(401).send('Unauthorized');
}
const eventData = req.body['event-data'];
const event = eventData.event;
const email = eventData.recipient;
await tajo.connectors.handleWebhook('mailgun', {
topic: event,
payload: eventData
});
// Handle bounce suppression
if (event === 'failed' && eventData.severity === 'permanent') {
await tajo.contacts.update(email, {
attributes: { MG_BOUNCE_TYPE: 'hard_bounce' },
emailBlacklisted: true
});
}
res.status(200).send('OK');
});

Sync Bounces and Complaints

// Sync bounced addresses for list hygiene
const bouncesResponse = await fetch(
`https://api.mailgun.net/v3/${domain}/bounces?limit=100`,
{
headers: {
'Authorization': `Basic ${Buffer.from(`api:${apiKey}`).toString('base64')}`
}
}
);
const { items: bounces } = await bouncesResponse.json();
for (const bounce of bounces) {
await tajo.contacts.update(bounce.address, {
attributes: {
MG_BOUNCE_TYPE: bounce.error.includes('550') ? 'hard_bounce' : 'soft_bounce',
MG_BOUNCE_DATE: bounce.created_at
},
emailBlacklisted: bounce.error.includes('550')
});
}

Rate Limits

EndpointLimitNotes
Messages APIVaries by plan100/hr (free), unlimited (paid)
Events APINo explicit limitUse pagination with 300 items max
Validation APIBased on planPay-per-validation
WebhooksReal-timeNo rate limit on delivery
Suppressions APINo explicit limitStandard rate limiting applies

Sending Limits

Mailgun enforces sending limits based on your plan and domain reputation. New domains start with lower limits that increase as your sender reputation improves. Monitor your domain stats in the Mailgun dashboard.

Troubleshooting

IssueCauseSolution
401 UnauthorizedInvalid API keyVerify API key in Mailgun dashboard
Domain not verifiedDNS records missingAdd required TXT, CNAME, MX records
Webhook not receivedURL not accessibleEnsure webhook URL is publicly reachable
Events missingTime range too narrowExpand begin/end parameters
Low deliverabilityDomain reputationCheck domain stats and authentication

Debug Mode

connectors:
mailgun:
debug: true
log_level: verbose
log_webhooks: true
log_events: true

Best Practices

  1. Verify sending domains - Complete DNS verification for optimal deliverability
  2. Use webhooks for events - Real-time webhook delivery vs. polling the Events API
  3. Handle bounces proactively - Suppress hard bounces immediately in Brevo
  4. Tag your messages - Use tags to categorize and analyze email performance
  5. Monitor domain reputation - Track deliverability metrics in Mailgun dashboard
  6. Use email validation - Validate addresses before adding to Brevo lists

Security

  • HTTP Basic Auth - API key transmitted via Authorization header
  • Webhook signatures - HMAC-SHA256 signature verification
  • Domain verification - SPF, DKIM, and DMARC DNS authentication
  • IP whitelisting - Available for dedicated IP plans
  • TLS encryption - All API endpoints require HTTPS
  • Key rotation - Rotate API keys periodically via Mailgun dashboard

Open-Source Implementation Map

This section is derived from official or public repository material discovered for the Mailgun connector. Use it as the engineering companion to the setup guide above: it shows where the API surface lives, what implementation assets exist, and how Tajo should translate them into reliable Brevo sync behavior.

Repository Snapshot

RepositoryCommitLanguages / formatsFiles
mailgun/mailgun-mcp-server3495e24TypeScript (10), JSON (5), Markdown (4), YAML (1), gitignore (1), npmignore (1)26

Integration Shape

graph LR
Source["Mailgun API / repository"] --> Auth["Auth and scopes"]
Source --> Objects["Objects, events, and schemas"]
Auth --> Tajo["Tajo connector runtime"]
Objects --> Tajo
Tajo --> Brevo["Brevo contacts, attributes, lists, campaigns"]
Tajo --> Ops["Backfill, cursor, retries, logs"]

What To Reuse

  • Mailgun MCP Server
  • Overview
  • Capabilities
    • Messaging — Send emails, retrieve stored messages, resend messages
    • Domains — View domain details, verify DNS configuration, manage tracking settings (click, open, unsubscribe)

Tajo Revamp Checklist

  • Keep authentication setup aligned with the vendor docs and the public repository’s current API shape.
  • Map primary resources into explicit Tajo sync objects with stable external IDs.
  • Prefer cursor-based or updated-at incremental sync where the API exposes it; otherwise document the fallback.
  • Treat webhook handlers as idempotent and replay-safe, especially for order, contact, ticket, and campaign events.
  • Capture pagination, rate limits, retry headers, and partial-failure behavior in connector smoke tests.
  • Keep examples small and runnable against sandbox or test-mode accounts.

Sources

Subscribe to updates

developer-docs

Drop your email or phone number — we'll send you what matters next.

auto-detect
AI Assistant

Hi! Ask me anything about the docs.