Call SendGrid v3 endpoints through managed OAuth to send email, manage contacts, templates, and senders.
Documents
Mailgun
Try itSend emails and manage Mailgun domains, routes, templates, mailing lists, suppressions, and webhooks via managed OAuth.
What it does
Covers Mailgun domains, messages, events, routes, webhooks, templates, mailing lists and members, suppressions (bounces, unsubscribes, complaints, whitelists), stats, tags, IPs, and tracking settings. Calls are routed through the Maton gateway at https://api.maton.ai/mailgun/v3 (default US region, api.mailgun.net) and authenticated with a Bearer token from the MATON_API_KEY env variable; OAuth credentials are injected automatically. Write operations—create, update, delete—must be confirmed with the user before they run.
When to use it
- Sending transactional or MIME messages through Mailgun
- Listing, creating, or deleting domains and routes
- Managing mailing list members and suppression lists
- Configuring webhooks and open/click/unsubscribe tracking
The skill document
Mailgun
Access the Mailgun API with managed OAuth authentication. Send transactional emails, manage domains, routes, templates, mailing lists, suppressions, and webhooks.
Quick Start
# List domains
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/mailgun/v3/domains')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://api.maton.ai/mailgun/v3/{resource}
Maton proxies requests to api.mailgun.net/v3 (US region) and automatically injects your OAuth token.
Regional Note: Mailgun has US and EU regions. The gateway defaults to US region (api.mailgun.net).
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Mailgun OAuth connections at https://api.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=mailgun&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'mailgun'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2026-02-12T02:24:16.551210Z",
"last_updated_time": "2026-02-12T02:25:03.542838Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "mailgun",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Mailgun connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/mailgun/v3/domains')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always include this header to ensure requests go to the intended account.
Security & Permissions
- Access is scoped to messages, domains, routes, events, and mailing lists within the connected Mailgun account.
- All write operations require explicit user approval. Before executing any create, update, or delete call, confirm the target resource and intended effect with the user.
API Reference
Important: Mailgun API uses application/x-www-form-urlencoded for POST/PUT requests, not JSON.
Domains
List Domains
GET /mailgun/v3/domains
Returns all domains for the account.
Get Domain
GET /mailgun/v3/domains/{domain_name}
Create Domain
POST /mailgun/v3/domains
Content-Type: application/x-www-form-urlencoded
name=example.com&smtp_password=supersecret
Delete Domain
DELETE /mailgun/v3/domains/{domain_name}
Messages
Send Message
POST /mailgun/v3/{domain_name}/messages
Content-Type: application/x-www-form-urlencoded
from=sender@example.com&to=recipient@example.com&subject=Hello&text=Hello World
Parameters:
from(required) - Sender email addressto(required) - Recipient(s), comma-separatedcc- CC recipientsbcc- BCC recipientssubject(required) - Email subjecttext- Plain text bodyhtml- HTML bodytemplate- Name of stored template to useo:tag- Tag for trackingo:tracking- Enable/disable tracking (yes/no)o:tracking-clicks- Enable click trackingo:tracking-opens- Enable open trackingh:X-Custom-Header- Custom headers (prefix with h:)v:custom-var- Custom variables for templates (prefix with v:)
Send MIME Message
POST /mailgun/v3/{domain_name}/messages.mime
Content-Type: multipart/form-data
to=recipient@example.com&message=
Events
List Events
GET /mailgun/v3/{domain_name}/events
Query parameters:
begin- Start time (RFC 2822 or Unix timestamp)end- End timeascending- Sort order (yes/no)limit- Results per page (max 300)event- Filter by event type (accepted, delivered, failed, opened, clicked, unsubscribed, complained, stored)from- Filter by senderto- Filter by recipienttags- Filter by tags
Routes
Routes are defined globally per account, not per domain.
List Routes
GET /mailgun/v3/routes
Query parameters:
skip- Number of records to skiplimit- Number of records to return
Create Route
POST /mailgun/v3/routes
Content-Type: application/x-www-form-urlencoded
priority=0&description=My Route&expression=match_recipient(".*@example.com")&action=forward("https://example.com/webhook")
Parameters:
priority- Route priority (lower = higher priority)description- Route descriptionexpression- Filter expression (match_recipient, match_header, catch_all)action- Action(s) to take (forward, store, stop)
Get Route
GET /mailgun/v3/routes/{route_id}
Update Route
PUT /mailgun/v3/routes/{route_id}
Content-Type: application/x-www-form-urlencoded
priority=1&description=Updated Route
Delete Route
DELETE /mailgun/v3/routes/{route_id}
Webhooks
List Webhooks
GET /mailgun/v3/domains/{domain_name}/webhooks
Create Webhook
POST /mailgun/v3/domains/{domain_name}/webhooks
Content-Type: application/x-www-form-urlencoded
id=delivered&url=https://example.com/webhook
Webhook types: accepted, delivered, opened, clicked, unsubscribed, complained, permanent_fail, temporary_fail
Get Webhook
GET /mailgun/v3/domains/{domain_name}/webhooks/{webhook_type}
Update Webhook
PUT /mailgun/v3/domains/{domain_name}/webhooks/{webhook_type}
Content-Type: application/x-www-form-urlencoded
url=https://example.com/new-webhook
Delete Webhook
DELETE /mailgun/v3/domains/{domain_name}/webhooks/{webhook_type}
Templates
List Templates
GET /mailgun/v3/{domain_name}/templates
Create Template
POST /mailgun/v3/{domain_name}/templates
Content-Type: application/x-www-form-urlencoded
name=my-template&description=Welcome email&template=Hello {{name}}
Get Template
GET /mailgun/v3/{domain_name}/templates/{template_name}
Delete Template
DELETE /mailgun/v3/{domain_name}/templates/{template_name}
Mailing Lists
List Mailing Lists
GET /mailgun/v3/lists/pages
Create Mailing List
POST /mailgun/v3/lists
Content-Type: application/x-www-form-urlencoded
address=newsletter@example.com&name=Newsletter&description=Monthly newsletter&access_level=readonly
Access levels: readonly, members, everyone
Get Mailing List
GET /mailgun/v3/lists/{list_address}
Update Mailing List
PUT /mailgun/v3/lists/{list_address}
Content-Type: application/x-www-form-urlencoded
name=Updated Newsletter
Delete Mailing List
DELETE /mailgun/v3/lists/{list_address}
Mailing List Members
List Members
GET /mailgun/v3/lists/{list_address}/members/pages
Add Member
POST /mailgun/v3/lists/{list_address}/members
Content-Type: application/x-www-form-urlencoded
address=member@example.com&name=John Doe&subscribed=yes
Get Member
GET /mailgun/v3/lists/{list_address}/members/{member_address}
Update Member
PUT /mailgun/v3/lists/{list_address}/members/{member_address}
Content-Type: application/x-www-form-urlencoded
name=Jane Doe&subscribed=no
Delete Member
DELETE /mailgun/v3/lists/{list_address}/members/{member_address}
Suppressions
Bounces
# List bounces
GET /mailgun/v3/{domain_name}/bounces
# Add bounce
POST /mailgun/v3/{domain_name}/bounces
Content-Type: application/x-www-form-urlencoded
address=bounced@example.com&code=550&error=Mailbox not found
# Get bounce
GET /mailgun/v3/{domain_name}/bounces/{address}
# Delete bounce
DELETE /mailgun/v3/{domain_name}/bounces/{address}
Unsubscribes
# List unsubscribes
GET /mailgun/v3/{domain_name}/unsubscribes
# Add unsubscribe
POST /mailgun/v3/{domain_name}/unsubscribes
Content-Type: application/x-www-form-urlencoded
address=unsubscribed@example.com&tag=*
# Delete unsubscribe
DELETE /mailgun/v3/{domain_name}/unsubscribes/{address}
Complaints
# List complaints
GET /mailgun/v3/{domain_name}/complaints
# Add complaint
POST /mailgun/v3/{domain_name}/complaints
Content-Type: application/x-www-form-urlencoded
address=complainer@example.com
# Delete complaint
DELETE /mailgun/v3/{domain_name}/complaints/{address}
Whitelists
# List whitelists
GET /mailgun/v3/{domain_name}/whitelists
# Add to whitelist
POST /mailgun/v3/{domain_name}/whitelists
Content-Type: application/x-www-form-urlencoded
address=allowed@example.com
# Delete from whitelist
DELETE /mailgun/v3/{domain_name}/whitelists/{address}
Statistics
Get Stats
GET /mailgun/v3/{domain_name}/stats/total?event=delivered&event=opened
Query parameters:
event(required) - Event type(s): accepted, delivered, failed, opened, clicked, unsubscribed, complainedstart- Start date (RFC 2822 or Unix timestamp)end- End dateresolution- Data resolution (hour, day, month)duration- Period to show stats for
Tags
List Tags
GET /mailgun/v3/{domain_name}/tags
Get Tag
GET /mailgun/v3/{domain_name}/tags/{tag_name}
Delete Tag
DELETE /mailgun/v3/{domain_name}/tags/{tag_name}
IPs
List IPs
GET /mailgun/v3/ips
Get IP
GET /mailgun/v3/ips/{ip_address}
Domain Tracking
Get Tracking Settings
GET /mailgun/v3/domains/{domain_name}/tracking
Update Open Tracking
PUT /mailgun/v3/domains/{domain_name}/tracking/open
Content-Type: application/x-www-form-urlencoded
active=yes
Update Click Tracking
PUT /mailgun/v3/domains/{domain_name}/tracking/click
Content-Type: application/x-www-form-urlencoded
active=yes
Update Unsubscribe Tracking
PUT /mailgun/v3/domains/{domain_name}/tracking/unsubscribe
Content-Type: application/x-www-form-urlencoded
active=yes&html_footer=Unsubscribe
Credentials
List Credentials
GET /mailgun/v3/domains/{domain_name}/credentials
Create Credential
POST /mailgun/v3/domains/{domain_name}/credentials
Content-Type: application/x-www-form-urlencoded
login=alice&password=supersecret
Delete Credential
DELETE /mailgun/v3/domains/{domain_name}/credentials/{login}
Pagination
Mailgun uses cursor-based pagination:
{
"items": [...],
"paging": {
"first": "https://api.mailgun.net/v3/.../pages?page=first&limit=100",
"last": "https://api.mailgun.net/v3/.../pages?page=last&limit=100",
"next": "https://api.mailgun.net/v3/.../pages?page=next&limit=100",
"previous": "https://api.mailgun.net/v3/.../pages?page=prev&limit=100"
}
}
Use limit parameter to control page size (default: 100).
Code Examples
JavaScript - Send Email
const formData = new URLSearchParams();
formData.append('from', 'sender@example.com');
formData.append('to', 'recipient@example.com');
formData.append('subject', 'Hello');
formData.append('text', 'Hello World!');
const response = await fetch(
'https://api.maton.ai/mailgun/v3/example.com/messages',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formData.toString()
}
);
const result = await response.json();
console.log(result);
Python - Send Email
import os
import requests
response = requests.post(
'https://api.maton.ai/mailgun/v3/example.com/messages',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
data={
'from': 'sender@example.com',
'to': 'recipient@example.com',
'subject': 'Hello',
'text': 'Hello World!'
}
)
print(response.json())
Python - List Domains
import os
import requests
response = requests.get(
'https://api.maton.ai/mailgun/v3/domains',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
domains = response.json()
for domain in domains['items']:
print(f"{domain['name']}: {domain['state']}")
Python - Create Route and Webhook
import os
import requests
headers = {'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
domain = 'example.com'
# Create route
route_response = requests.post(
'https://api.maton.ai/mailgun/v3/routes',
headers=headers,
data={
'priority': 0,
'description': 'Forward to webhook',
'expression': 'match_recipient("support@example.com")',
'action': 'forward("https://myapp.com/incoming-email")'
}
)
print(f"Route created: {route_response.json()}")
# Create webhook
webhook_response = requests.post(
f'https://api.maton.ai/mailgun/v3/domains/{domain}/webhooks',
headers=headers,
data={
'id': 'delivered',
'url': 'https://myapp.com/webhook/delivered'
}
)
print(f"Webhook created: {webhook_response.json()}")
Notes
- Mailgun uses
application/x-www-form-urlencodedfor POST/PUT requests, not JSON - Domain names must be included in most endpoint paths
- Routes are global (per account), not per domain
- Sandbox domains require authorized recipients for sending
- Dates are returned in RFC 2822 format
- Event logs are stored for at least 3 days
- Stats require at least one
eventparameter - Templates use Handlebars syntax by default
- IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets to disable glob parsing - IMPORTANT: When piping curl output to
jq, environment variables may not expand correctly. Use Python examples instead.
Rate Limits
| Operation | Limit |
|---|---|
| Sending | Varies by plan |
| API calls | No hard limit, but excessive requests may be throttled |
When rate limited, implement exponential backoff for retries.
Error Handling
| Status | Meaning |
|---|---|
| 400 | Bad request or missing Mailgun connection |
| 401 | Invalid or missing Maton API key |
| 403 | Forbidden (e.g., sandbox domain restrictions) |
| 404 | Resource not found |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Mailgun API |
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Troubleshooting: Invalid App Name
- Ensure your URL path starts with
mailgun. For example:
- Correct:
https://api.maton.ai/mailgun/v3/domains - Incorrect:
https://api.maton.ai/v3/domains
Troubleshooting: Sandbox Domain Restrictions
Sandbox domains can only send to authorized recipients. To send emails:
- Upgrade to a paid plan, or
- Add recipient addresses to authorized recipients in the Mailgun dashboard
Resources
Questions people ask
- How is authentication handled?
- Set the MATON_API_KEY env variable and send it as a Bearer token to https://api.maton.ai/mailgun/v3. The gateway injects your Mailgun OAuth token and proxies requests to api.mailgun.net (US region).
- Are write operations approved automatically?
- No. Create, update, and delete calls must be confirmed with the user before execution.
- What Content-Type do POST and PUT requests use?
- Mailgun expects application/x-www-form-urlencoded bodies for POST and PUT, not JSON.
- How are multiple Mailgun accounts handled?
- Pass the Maton-Connection header to target a specific connection when more than one OAuth connection exists.
Related skills
Send and manage Resend emails, domains, contacts, templates, and audiences through one authenticated gateway.
Read and manage Mailchimp audiences, subscribers, campaigns, segments, and tags via an OAuth-handled API proxy.
Manage MailerLite subscribers, groups, campaigns, automations, forms, fields, and segments via OAuth-authenticated API calls.
Call Brevo's contacts, lists, transactional email, templates, and campaign endpoints through a managed OAuth proxy.
Call the Instantly API v2 with managed auth to handle cold email campaigns, leads, sending accounts, and analytics.