Created
August 18, 2025 01:19
-
-
Save itsnotyoutoday/e45ebbde8347c54e0c97f597c64381de to your computer and use it in GitHub Desktop.
Mailjet Postfix Transport
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Mailjet Email Relay Script | |
========================== | |
Background, I had some issues working with mailjet's smtp relay. However, I found | |
their API to be reliable. So here's my custom python script acts as a | |
Postfix transport to relay emails via Mailjet API. | |
Setup Instructions: | |
1. Install required Python package: pip3 install requests | |
2. Set your Mailjet API credentials as environment variables: | |
export MAILJET_API_KEY="your_api_key_here" | |
export MAILJET_API_SECRET="your_api_secret_here" | |
3. Set default sender domain (optional): | |
export MAILJET_DEFAULT_DOMAIN="yourdomain.com" | |
4. Make script executable: chmod +x mailjet-relay.py | |
Postfix Integration: | |
1. Add to /etc/postfix/master.cf: | |
mailjet-api unix - n n - - pipe | |
flags=FR user=postfix argv=/path/to/mailjet-relay.py ${recipient} | |
2. Add to transport maps (e.g., /etc/postfix/transport): | |
@yourdomain.com mailjet-api: | |
3. Update Postfix config: | |
postmap /etc/postfix/transport | |
postfix reload | |
Usage: | |
- Script reads email from stdin and recipient from command line argument | |
- Supports plain text, HTML, and attachments | |
- Uses Mailjet API v3.1 for sending | |
Environment Variables: | |
- MAILJET_API_KEY: Your Mailjet API key (required) | |
- MAILJET_API_SECRET: Your Mailjet API secret (required) | |
- MAILJET_DEFAULT_DOMAIN: Default domain for fallback sender (default: localhost) | |
- MAILJET_DEFAULT_USER: Default username for fallback sender (default: postmaster) | |
By: Rick L Bird | |
License: MIT | |
""" | |
import sys | |
import json | |
import base64 | |
import requests | |
import os | |
from email import message_from_string | |
from email.utils import parseaddr | |
# Mailjet API configuration | |
API_KEY = os.environ.get('MAILJET_API_KEY') | |
API_SECRET = os.environ.get('MAILJET_API_SECRET') | |
API_URL = "https://api.mailjet.com/v3.1/send" | |
# Default sender configuration | |
DEFAULT_DOMAIN = os.environ.get('MAILJET_DEFAULT_DOMAIN', 'localhost') | |
DEFAULT_USER = os.environ.get('MAILJET_DEFAULT_USER', 'postmaster') | |
DEFAULT_SENDER = f"{DEFAULT_USER}@{DEFAULT_DOMAIN}" | |
def send_via_mailjet(): | |
# Check for required credentials | |
if not API_KEY or not API_SECRET: | |
print("Error: MAILJET_API_KEY and MAILJET_API_SECRET environment variables must be set", file=sys.stderr) | |
sys.exit(74) # Configuration error | |
# Get recipient from command line argument | |
recipient_email = sys.argv[1] if len(sys.argv) > 1 else None | |
# Read the email from stdin | |
raw_email = sys.stdin.buffer.read() | |
# Parse the email | |
from email.parser import BytesParser | |
from email.policy import default | |
msg = BytesParser(policy=default).parsebytes(raw_email) | |
# Extract headers | |
from_addr = parseaddr(msg.get('From', DEFAULT_SENDER)) | |
to_header = msg.get('To', '') | |
cc_header = msg.get('Cc', '') | |
bcc_header = msg.get('Bcc', '') | |
subject = msg.get('Subject', 'No Subject') | |
# Use command line recipient if no To header | |
to_addrs = [] | |
if recipient_email: | |
to_addrs = [('', recipient_email)] | |
elif to_header: | |
to_addrs = [parseaddr(addr.strip()) for addr in to_header.split(',') if addr.strip()] | |
cc_addrs = [] | |
if cc_header: | |
cc_addrs = [parseaddr(addr.strip()) for addr in cc_header.split(',') if addr.strip()] | |
bcc_addrs = [] | |
if bcc_header: | |
bcc_addrs = [parseaddr(addr.strip()) for addr in bcc_header.split(',') if addr.strip()] | |
# Get message body and attachments | |
body = "" | |
html_body = "" | |
attachments = [] | |
if msg.is_multipart(): | |
for part in msg.walk(): | |
content_type = part.get_content_type() | |
content_disposition = str(part.get('Content-Disposition', '')) | |
if content_type == "text/plain" and 'attachment' not in content_disposition: | |
body = part.get_payload(decode=True).decode('utf-8', errors='ignore') | |
elif content_type == "text/html" and 'attachment' not in content_disposition: | |
html_body = part.get_payload(decode=True).decode('utf-8', errors='ignore') | |
# Remove any escaping that might cause issues with DOCTYPE | |
html_body = html_body.replace('<\\!', '<!') | |
elif 'attachment' in content_disposition or part.get_filename(): | |
# Handle attachment | |
filename = part.get_filename() | |
if filename: | |
attachment_data = part.get_payload(decode=True) | |
if attachment_data: | |
attachments.append({ | |
"ContentType": content_type, | |
"Filename": filename, | |
"Base64Content": base64.b64encode(attachment_data).decode('utf-8') | |
}) | |
else: | |
body = msg.get_payload(decode=True).decode('utf-8', errors='ignore') | |
# If no HTML body, use plain text | |
if not html_body: | |
html_body = f"<pre>{body}</pre>" | |
# Build Mailjet API payload | |
message_data = { | |
"From": { | |
"Email": from_addr[1] if from_addr[1] else DEFAULT_SENDER, | |
"Name": from_addr[0] if from_addr[0] else "Mail System" | |
}, | |
"To": [ | |
{ | |
"Email": to_addr[1], | |
"Name": to_addr[0] if to_addr[0] else to_addr[1] | |
} for to_addr in to_addrs if to_addr[1] | |
], | |
"Subject": subject, | |
"TextPart": body, | |
"HTMLPart": html_body | |
} | |
# Add CC if any | |
if cc_addrs: | |
message_data["Cc"] = [ | |
{ | |
"Email": cc_addr[1], | |
"Name": cc_addr[0] if cc_addr[0] else cc_addr[1] | |
} for cc_addr in cc_addrs if cc_addr[1] | |
] | |
# Add BCC if any | |
if bcc_addrs: | |
message_data["Bcc"] = [ | |
{ | |
"Email": bcc_addr[1], | |
"Name": bcc_addr[0] if bcc_addr[0] else bcc_addr[1] | |
} for bcc_addr in bcc_addrs if bcc_addr[1] | |
] | |
# Add attachments if any | |
if attachments: | |
message_data["Attachments"] = attachments | |
data = {"Messages": [message_data]} | |
# Send via Mailjet API | |
try: | |
response = requests.post( | |
API_URL, | |
auth=(API_KEY, API_SECRET), | |
headers={'Content-Type': 'application/json'}, | |
json=data | |
) | |
if response.status_code == 200: | |
result = response.json() | |
if result.get('Messages', [{}])[0].get('Status') == 'success': | |
sys.exit(0) # Success | |
else: | |
print(f"Mailjet API error: {result}", file=sys.stderr) | |
sys.exit(75) # Temporary failure | |
else: | |
print(f"HTTP {response.status_code}: {response.text}", file=sys.stderr) | |
sys.exit(75) # Temporary failure | |
except Exception as e: | |
print(f"Error sending via Mailjet API: {e}", file=sys.stderr) | |
sys.exit(75) # Temporary failure | |
if __name__ == "__main__": | |
send_via_mailjet() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment