Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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).
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 208% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 193% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 215% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 289% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 272% | 0% |
Access the Mailgun API with managed OAuth authentication. Send transactional emails, manage domains, routes, templates, mailing lists, suppressions, and webhooks.
bash# 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)) EOF
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).
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEYEnvironment Variable: Set your API key as MATON_API_KEY:
bashexport MATON_API_KEY="YOUR_API_KEY"
Manage your Mailgun OAuth connections at https://ctrl.maton.ai.
bashpython <<'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)) EOF
bashpython <<'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)) EOF
bashpython <<'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:
json{ "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.
bashpython <<'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)) EOF
If you have multiple Mailgun connections, specify which one to use with the Maton-Connection header:
bashpython <<'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)) EOF
If omitted, the gateway uses the default (oldest) active connection.
Important: Mailgun API uses application/x-www-form-urlencoded for POST/PUT requests, not JSON.
bashGET /mailgun/v3/domains
Returns all domains for the account.
bashGET /mailgun/v3/domains/{domain_name}
bashPOST /mailgun/v3/domains Content-Type: application/x-www-form-urlencoded name=example.com&smtp_password=supersecret
bashDELETE /mailgun/v3/domains/{domain_name}
bashPOST /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:)bashPOST /mailgun/v3/{domain_name}/messages.mime Content-Type: multipart/form-data to=recipient@example.com&message=<MIME content>
bashGET /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 tagsRoutes are defined globally per account, not per domain.
bashGET /mailgun/v3/routes
Query parameters:
skip - Number of records to skiplimit - Number of records to returnbashPOST /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)bashGET /mailgun/v3/routes/{route_id}
bashPUT /mailgun/v3/routes/{route_id} Content-Type: application/x-www-form-urlencoded priority=1&description=Updated Route
bashDELETE /mailgun/v3/routes/{route_id}
bashGET /mailgun/v3/domains/{domain_name}/webhooks
bashPOST /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
bashGET /mailgun/v3/domains/{domain_name}/webhooks/{webhook_type}
bashPUT /mailgun/v3/domains/{domain_name}/webhooks/{webhook_type} Content-Type: application/x-www-form-urlencoded url=https://example.com/new-webhook
bashDELETE /mailgun/v3/domains/{domain_name}/webhooks/{webhook_type}
bashGET /mailgun/v3/{domain_name}/templates
bashPOST /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>
bashGET /mailgun/v3/{domain_name}/templates/{template_name}
bashDELETE /mailgun/v3/{domain_name}/templates/{template_name}
bashGET /mailgun/v3/lists/pages
bashPOST /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
bashGET /mailgun/v3/lists/{list_address}
bashPUT /mailgun/v3/lists/{list_address} Content-Type: application/x-www-form-urlencoded name=Updated Newsletter
bashDELETE /mailgun/v3/lists/{list_address}
bashGET /mailgun/v3/lists/{list_address}/members/pages
bashPOST /mailgun/v3/lists/{list_address}/members Content-Type: application/x-www-form-urlencoded address=member@example.com&name=John Doe&subscribed=yes
bashGET /mailgun/v3/lists/{list_address}/members/{member_address}
bashPUT /mailgun/v3/lists/{list_address}/members/{member_address} Content-Type: application/x-www-form-urlencoded name=Jane Doe&subscribed=no
bashDELETE /mailgun/v3/lists/{list_address}/members/{member_address}
bash# 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}
bash# 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}
bash# 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}
bash# 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}
bashGET /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 forbashGET /mailgun/v3/{domain_name}/tags
bashGET /mailgun/v3/{domain_name}/tags/{tag_name}
bashDELETE /mailgun/v3/{domain_name}/tags/{tag_name}
bashGET /mailgun/v3/ips
bashGET /mailgun/v3/ips/{ip_address}
bashGET /mailgun/v3/domains/{domain_name}/tracking
bashPUT /mailgun/v3/domains/{domain_name}/tracking/open Content-Type: application/x-www-form-urlencoded active=yes
bashPUT /mailgun/v3/domains/{domain_name}/tracking/click Content-Type: application/x-www-form-urlencoded active=yes
bashPUT /mailgun/v3/domains/{domain_name}/tracking/unsubscribe Content-Type: application/x-www-form-urlencoded active=yes&html_footer=<a href="%unsubscribe_url%">Unsubscribe</a>
bashGET /mailgun/v3/domains/{domain_name}/credentials
bashPOST /mailgun/v3/domains/{domain_name}/credentials Content-Type: application/x-www-form-urlencoded login=alice&password=supersecret
bashDELETE /mailgun/v3/domains/{domain_name}/credentials/{login}
Mailgun uses cursor-based pagination:
json{ "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).
javascriptconst 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);
pythonimport 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())
pythonimport 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']}")
pythonimport 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()}")
application/x-www-form-urlencoded for POST/PUT requests, not JSONevent parametercurl -g when URLs contain brackets to disable glob parsingjq, environment variables may not expand correctly. Use Python examples instead.| 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.
| 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 |
MATON_API_KEY environment variable is set:bashecho $MATON_API_KEY
bashpython <<'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)) EOF
mailgun. For example:https://gateway.maton.ai/mailgun/v3/domainshttps://gateway.maton.ai/v3/domainsSandbox domains can only send to authorized recipients. To send emails:
Other measured skills in the registry, with their headline benchmark lift.