Mailgun
name: mailgun
by byungkyu · published 2026-03-22
$ claw add gh:byungkyu/byungkyu-mailgun-api---
name: mailgun
description: |
Mailgun API integration with managed OAuth. Transactional email service for sending, receiving, and tracking emails.
Use this skill when users want to send emails, manage domains, routes, templates, mailing lists, or suppressions in Mailgun.
For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
compatibility: Requires network access and valid Maton API key
metadata:
author: maton
version: "1.0"
clawdbot:
emoji: 🧠
homepage: "https://maton.ai"
requires:
env:
- MATON_API_KEY
---
# 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://gateway.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))
EOFBase URL
https://gateway.maton.ai/mailgun/v3/{resource}Replace `{resource}` with the actual Mailgun API endpoint path. The gateway 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
1. Sign in or create an account at [maton.ai](https://maton.ai)
2. Go to [maton.ai/settings](https://maton.ai/settings)
3. Copy your API key
Connection Management
Manage your Mailgun OAuth connections at `https://ctrl.maton.ai`.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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))
EOFCreate Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'mailgun'}).encode()
req = urllib.request.Request('https://ctrl.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))
EOFGet Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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": "78b5a036-c621-40c2-b74b-276195735af2",
"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://ctrl.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))
EOFSpecifying 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://gateway.maton.ai/mailgun/v3/domains')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '78b5a036-c621-40c2-b74b-276195735af2')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOFIf omitted, the gateway uses the default (oldest) active connection.
API Reference
**Important:** Mailgun API uses `application/x-www-form-urlencoded` for POST/PUT requests, not JSON.
Domains
#### List Domains
GET /mailgun/v3/domainsReturns 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 WorldParameters:
#### Send MIME Message
POST /mailgun/v3/{domain_name}/messages.mime
Content-Type: multipart/form-data
to=recipient@example.com&message=<MIME content>Events
#### List Events
GET /mailgun/v3/{domain_name}/eventsQuery parameters:
Routes
Routes are defined globally per account, not per domain.
#### List Routes
GET /mailgun/v3/routesQuery parameters:
#### 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:
#### 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/webhookWebhook 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=<html><body>Hello {{name}}</body></html>#### 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=readonlyAccess 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=openedQuery parameters:
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=<a href="%unsubscribe_url%">Unsubscribe</a>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://gateway.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://gateway.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://gateway.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://gateway.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://gateway.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
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
1. Check that the `MATON_API_KEY` environment variable is set:
echo $MATON_API_KEY2. Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.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))
EOFTroubleshooting: Invalid App Name
1. Ensure your URL path starts with `mailgun`. For example:
Troubleshooting: Sandbox Domain Restrictions
Sandbox domains can only send to authorized recipients. To send emails:
1. Upgrade to a paid plan, or
2. Add recipient addresses to authorized recipients in the Mailgun dashboard
Resources
More tools from the same signal band
Order food/drinks (点餐) on an Android device paired as an OpenClaw node. Uses in-app menu and cart; add goods, view cart, submit order (demo, no real payment).
Sign plugins, rotate agent credentials without losing identity, and publicly attest to plugin behavior with verifiable claims and authenticated transfers.
The philosophical layer for AI agents. Maps behavior to Spinoza's 48 affects, calculates persistence scores, and generates geometric self-reports. Give your...