---
title: "Mailgun Connector"
description: "Complete Mailgun integration guide for syncing email delivery data, engagement metrics, and transactional email events with Brevo through Tajo."
url: https://tajo.io/docs/connectors/marketing/mailgun/
markdown: https://tajo.io/docs/connectors/marketing/mailgun.md
lang: en-US
robots: noindex
---

# Mailgun Connector

Connector status: Planned Not built yet. Tajo does not run this connector today.

| Source field | Destination field | Sample value |
| --- | --- | --- |
| `record.id` | `source_id` | `record_1042` |
| `record.updated_at` | `updated_at` | `2026-10-18T08:00:00Z` |
| `record.value` | `value` | `sample_value` |

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

| Property | Value |
| --- | --- |
| **Platform** | Mailgun (by Sinch) |
| **Category** | Email Marketing |
| **Setup Complexity** | Easy |
| **Official Integration** | No |
| **Data Synced** | Events, Contacts, Deliverability, Campaigns |
| **Auth Method** | API 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](https://app.mailgun.com/settings/api_security)
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:

```bash
# 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
```

```javascript
// 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

```yaml
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

```yaml
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

| Endpoint | Method | Description |
| --- | --- | --- |
| `https://api.mailgun.net/v3/{domain}/messages` | POST | Send email messages |
| `https://api.mailgun.net/v3/{domain}/events` | GET | Query event logs |
| `https://api.mailgun.net/v3/{domain}/bounces` | GET | List bounces |
| `https://api.mailgun.net/v3/{domain}/complaints` | GET | List complaints |
| `https://api.mailgun.net/v3/{domain}/unsubscribes` | GET | List unsubscribes |
| `https://api.mailgun.net/v3/{domain}/tags` | GET | List tags |
| `https://api.mailgun.net/v3/{domain}/tags/{tag}/stats` | GET | Get tag statistics |
| `https://api.mailgun.net/v3/lists` | GET | List mailing lists |
| `https://api.mailgun.net/v3/domains` | GET | List domains |
| `https://api.mailgun.net/v4/address/validate` | POST | Validate 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

```javascript
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

```javascript
// Send an email using Mailgun's Messages API
const formData = new URLSearchParams();
formData.append('from', `Your App <noreply@${domain}>`);
formData.append('to', 'recipient@example.com');
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: '<message-id@domain.com>', message: 'Queued. Thank you.' }
```

### Sync Email Events to Brevo

```javascript
// 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

```javascript
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

```javascript
// 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

| Endpoint | Limit | Notes |
| --- | --- | --- |
| Messages API | Varies by plan | 100/hr (free), unlimited (paid) |
| Events API | No explicit limit | Use pagination with 300 items max |
| Validation API | Based on plan | Pay-per-validation |
| Webhooks | Real-time | No rate limit on delivery |
| Suppressions API | No explicit limit | Standard 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

| Issue | Cause | Solution |
| --- | --- | --- |
| 401 Unauthorized | Invalid API key | Verify API key in Mailgun dashboard |
| Domain not verified | DNS records missing | Add required TXT, CNAME, MX records |
| Webhook not received | URL not accessible | Ensure webhook URL is publicly reachable |
| Events missing | Time range too narrow | Expand `begin`/`end` parameters |
| Low deliverability | Domain reputation | Check domain stats and authentication |

### Debug Mode

```yaml
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

## Related Resources

- [Brevo Connector](https://tajo.io/docs/connectors/marketing/brevo)
- [Segment Connector](https://tajo.io/docs/connectors/custom/segment)
- [Customer Sync Skill](https://tajo.io/docs/skills/data-sync/customer-sync)
- [Mailgun API Documentation](https://documentation.mailgun.com/)

## 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

| Repository | Commit | Languages / formats | Files |
| --- | --- | --- | --- |
| [mailgun/mailgun-mcp-server](https://github.com/mailgun/mailgun-mcp-server) | 3495e24 | TypeScript (10), JSON (5), Markdown (4), YAML (1), gitignore (1), npmignore (1) | 26 |

### Integration Shape

```mermaid
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

- [mailgun/mailgun-mcp-server](https://github.com/mailgun/mailgun-mcp-server)
- [Vendor documentation](https://documentation.mailgun.com/docs/mailgun/mcp)
