Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
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).
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).
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Access the Mailgun API with managed OAuth authentication. Send transactional emails, manage domains, routes, templates, mailing lists, suppressions, and webhooks.
# 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_KEY Environment Variable: Set your API key as MATON_API_KEY: export MATON_API_KEY="YOUR_API_KEY"
Sign in or create an account at maton.ai Go to maton.ai/settings Copy your API key
Manage your Mailgun OAuth connections at https://ctrl.maton.ai.
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)) EOF
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)) EOF
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.
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)) EOF
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)) 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.
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}
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 address to (required) - Recipient(s), comma-separated cc - CC recipients bcc - BCC recipients subject (required) - Email subject text - Plain text body html - HTML body template - Name of stored template to use o:tag - Tag for tracking o:tracking - Enable/disable tracking (yes/no) o:tracking-clicks - Enable click tracking o:tracking-opens - Enable open tracking h: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=<MIME content>
List Events GET /mailgun/v3/{domain_name}/events Query parameters: begin - Start time (RFC 2822 or Unix timestamp) end - End time ascending - 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 sender to - Filter by recipient tags - Filter by tags
Routes are defined globally per account, not per domain. List Routes GET /mailgun/v3/routes Query parameters: skip - Number of records to skip limit - 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 description expression - 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}
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}
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}
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}
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}
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}
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, complained start - Start date (RFC 2822 or Unix timestamp) end - End date resolution - Data resolution (hour, day, month) duration - Period to show stats for
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}
List IPs GET /mailgun/v3/ips Get IP GET /mailgun/v3/ips/{ip_address}
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>
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}
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).
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);
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())
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']}")
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()}")
Mailgun uses application/x-www-form-urlencoded for 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 event parameter Templates use Handlebars syntax by default IMPORTANT: When using curl commands, use curl -g when URLs contain brackets to disable glob parsing IMPORTANT: When piping curl output to jq, environment variables may not expand correctly. Use Python examples instead.
OperationLimitSendingVaries by planAPI callsNo hard limit, but excessive requests may be throttled When rate limited, implement exponential backoff for retries.
StatusMeaning400Bad request or missing Mailgun connection401Invalid or missing Maton API key403Forbidden (e.g., sandbox domain restrictions)404Resource not found429Rate limited4xx/5xxPassthrough error from Mailgun API
Check that the MATON_API_KEY environment 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://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
Ensure your URL path starts with mailgun. For example: Correct: https://gateway.maton.ai/mailgun/v3/domains Incorrect: https://gateway.maton.ai/v3/domains
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
Mailgun API Documentation Mailgun API Reference Mailgun Postman Collection Maton Community Maton Support
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.