SEPTEMBER 2025 · MAILWIZZ TECHNICAL REFERENCE

MailWizz REST API — Integration Guide for Subscriber Management and Campaign Control

MailWizz provides a REST API that enables external applications to manage subscribers, lists, and campaigns programmatically. The API is enabled in the MailWizz backend and authenticated via API key.

Enabling the API

# Backend → API Keys → Add New
# Create an API key with appropriate permissions:
# - lists: read, create, update, delete
# - subscribers: read, create, update, delete
# - campaigns: read, create
# Copy the generated API key

API Authentication

# All requests require the X-MW-PUBLIC-KEY header
curl -H 'X-MW-PUBLIC-KEY: your-api-key-here' \
     https://yourdomain.com/api/v1/lists

Common API Operations

# Add a subscriber to a list
curl -X POST \
  -H 'X-MW-PUBLIC-KEY: your-key' \
  -H 'Content-Type: application/json' \
  -d '{"EMAIL":"user@example.com","FNAME":"John","LNAME":"Doe","status":"confirmed"}' \
  https://yourdomain.com/api/v1/lists/LIST-UID/subscribers

# Get subscriber status
curl -H 'X-MW-PUBLIC-KEY: your-key' \
  https://yourdomain.com/api/v1/lists/LIST-UID/subscribers?email=user@example.com

# Unsubscribe a recipient
curl -X PUT \
  -H 'X-MW-PUBLIC-KEY: your-key' \
  -d 'status=unsubscribed' \
  https://yourdomain.com/api/v1/lists/LIST-UID/subscribers/SUBSCRIBER-UID

Campaign Control via API

# Mark a campaign as sending (trigger send)
curl -X PUT \
  -H 'X-MW-PUBLIC-KEY: your-key' \
  -d 'status=sending' \
  https://yourdomain.com/api/v1/campaigns/CAMPAIGN-UID

Rate Limits and Error Handling

  • The API returns JSON with a 'status' field: success or error
  • HTTP 429 indicates rate limiting — add delay between bulk API calls
  • HTTP 401 indicates invalid API key or insufficient permissions
  • Bulk subscriber imports via API are slower than CSV import — use CSV for batches above 10,000

When adding subscribers via API in bulk, batch requests into groups of 100-500 rather than one per API call. Each API call has overhead from authentication and request processing. Batch inserts also reduce the number of database transactions, improving performance at high volume.

Troubleshooting Common Issues

Production MailWizz deployments encounter predictable issues at predictable stages. Understanding the diagnostic workflow for the most common problems in this configuration area saves time and prevents the escalating complexity that comes from applying fixes to a misdiagnosed problem. The diagnostic approach is always the same: identify the symptom precisely (not just "it's not working"), isolate the layer where the failure occurs (MailWizz application, delivery server connection, DNS, ISP rejection), and fix at the correct layer.

Systematic Diagnosis Approach

Check MailWizz logs first (available in Backend → Misc → Application Logs), then check the delivery server SMTP logs, then check the PowerMTA accounting log. Most issues surface in one of these three places. A problem that does not appear in any of these logs is almost always a configuration issue — the system is not attempting what you expect it to attempt.

# MailWizz diagnostic log locations:
# Application logs: Backend → Misc → Application Logs
# Delivery logs: Backend → Campaigns → [Campaign] → Delivery Logs
# Bounce logs: Backend → Bounce Servers → [Server] → Logs

# Server-side logs:
# MailWizz application: /path/to/mailwizz/apps/common/runtime/application.log
# PowerMTA delivery: /var/log/pmta/pmta.log
# PowerMTA accounting: /var/log/pmta/accounting.csv

Performance Optimization for Production Scale

MailWizz performance at scale depends on three infrastructure layers: the web application server (PHP/nginx or Apache), the database (MySQL — query optimization is critical at high subscriber counts), and the delivery infrastructure (PowerMTA connection pool sizing). Performance problems in any of these layers manifest as slow campaign sends, delayed processing, or timeouts that appear unrelated to the specific configuration area being managed.

The most common performance constraint in production MailWizz environments is MySQL query efficiency. As subscriber lists grow beyond 500,000 records, unoptimized database queries for segmentation, bounce processing configuration, and campaign statistics become significant bottlenecks. Ensure that subscriber tables have appropriate indexes on email, status, date_added, and any custom field columns used for segmentation.

# MySQL optimization for large MailWizz installations
# Check slow query log:
SHOW VARIABLES LIKE 'slow_query_log%';
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  # Log queries over 1 second

# Key indexes to verify exist:
SHOW INDEX FROM mailwizz_lists_subscribers;
# Should have indexes on: email, status, date_added, list_id

# Add missing index if needed:
ALTER TABLE mailwizz_lists_subscribers 
  ADD INDEX idx_email_status (email, status);
  
# Campaign sends table — index on campaign_id + subscriber_id:
ALTER TABLE mailwizz_campaigns_tracking_opens
  ADD INDEX idx_campaign_sub (campaign_id, subscriber_id);

Security Considerations

MailWizz installations handling production sending volumes are valuable targets. Key security practices: use HTTPS for all MailWizz access (including tracking and unsubscribe links), restrict Backend access to authorized IP ranges via web server configuration, rotate API keys periodically and revoke unused keys, maintain regular database backups (automated, offsite), and ensure PHP and MailWizz are kept current with security patches.

The tracking domain (used for open and click tracking) requires special attention: it must have a valid SSL certificate (Let's Encrypt is acceptable), and its DNS records must point exclusively to your MailWizz server. A compromised tracking domain can redirect recipients to malicious sites or reveal subscriber click data to third parties.

Every configuration decision in MailWizz has a corresponding deliverability implication — because MailWizz is the layer that determines what gets sent, to whom, and when, while PowerMTA determines how it gets delivered. Operators who understand both layers produce better outcomes than those who treat either layer as a black box. The configuration coverage in this reference is designed to bridge that understanding.

For MailWizz deployments on managed infrastructure operated by the Cloud Server for Email team, the configuration tuning, monitoring, and incident response described here is handled operationally as part of the service. Contact infrastructure@cloudserverforemail.com for a technical assessment of your current MailWizz configuration.

MailWizz REST API: Architecture and Authentication

MailWizz exposes a comprehensive REST API that enables external systems to manage subscribers, campaigns, lists, and sending operations programmatically. The API uses HTTP Basic Authentication with API keys generated in the MailWizz backend. All endpoints return JSON responses. The base URL format is: https://your-mailwizz-domain.com/api/index.php.

Authentication and API Key Setup

# Generate API keys in MailWizz:
# Backend → API Keys → Create New

# Two types of keys:
# Public key: used for API identification
# Private key: used to sign requests (HMAC-SHA256 in some endpoints)

# Basic authentication header format:
# Authorization: Basic base64(PUBLIC_KEY:PRIVATE_KEY)

# Python example:
import requests, base64

PUBLIC_KEY = 'your-public-key'
PRIVATE_KEY = 'your-private-key'
BASE_URL = 'https://your-mailwizz.com/api/index.php'

auth = base64.b64encode(f'{PUBLIC_KEY}:{PRIVATE_KEY}'.encode()).decode()
headers = {
    'Authorization': f'Basic {auth}',
    'Content-Type': 'application/json',
    'Accept': 'application/json'
}

Core API Endpoints Reference

ResourceMethodEndpointCommon Use
ListsGET/api/listsRetrieve all mailing lists
SubscribersPOST/api/lists/{uid}/subscribersAdd subscriber to list
SubscribersDELETE/api/lists/{uid}/subscribers/{sub_uid}Unsubscribe a recipient
CampaignsGET/api/campaignsList all campaigns
CampaignsPOST/api/campaigns/{uid}/send-testSend test email
Campaign statsGET/api/campaigns/{uid}/statsGet campaign statistics
Bounce serversGET/api/bounce-serversList bounce server configuration configs

Adding Subscribers via API

# Add a subscriber to a specific list
import requests, json

def add_subscriber(list_uid, email, first_name, last_name):
    url = f'{BASE_URL}/api/lists/{list_uid}/subscribers'
    payload = {
        'EMAIL': email,
        'FNAME': first_name,
        'LNAME': last_name,
        'status': 'confirmed',   # Skip double opt-in for API adds
    }
    response = requests.post(url, headers=headers, json=payload)
    return response.json()

# Response on success:
# {'status': 'success', 'data': {'record': {'subscriber_uid': 'abc123', ...}}}

# Bulk add (process in batches of 100-500):
def bulk_add_subscribers(list_uid, subscribers):
    results = {'success': 0, 'failed': 0}
    for sub in subscribers:
        result = add_subscriber(list_uid, sub['email'], sub['fname'], sub['lname'])
        if result.get('status') == 'success':
            results['success'] += 1
        else:
            results['failed'] += 1
    return results

Unsubscribe Processing via API

# Process unsubscribes in real-time via API
# Used when your application handles unsubscribe clicks before MailWizz

def unsubscribe_by_email(list_uid, email):
    # First: find the subscriber_uid
    search_url = f'{BASE_URL}/api/lists/{list_uid}/subscribers/search-by-email'
    r = requests.put(search_url, headers=headers, json={'EMAIL': email})
    data = r.json()
    
    if data.get('status') == 'success':
        sub_uid = data['data']['record']['subscriber_uid']
        # Then: unsubscribe
        unsub_url = f'{BASE_URL}/api/lists/{list_uid}/subscribers/{sub_uid}/unsubscribe'
        return requests.put(unsub_url, headers=headers).json()
    
    return {'status': 'not_found'}

# For one-click unsubscribe (RFC 8058) endpoint:
# POST /your-unsub-endpoint → calls this function → returns 200 OK immediately

Webhook Integration for Real-Time Events

MailWizz supports webhooks that POST event data to your configured endpoint when specific events occur: subscriber confirmed, subscriber unsubscribed, campaign opened, campaign clicked, and bounce received. Configure webhooks in Backend → Settings → API → Webhooks.

# Webhook handler example (Python Flask):
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/mailwizz-webhook', methods=['POST'])
def handle_mailwizz_event():
    data = request.json
    event_type = data.get('type')
    
    if event_type == 'unsubscribe':
        email = data['subscriber']['EMAIL']
        add_to_global_suppression(email)
    
    elif event_type == 'bounce':
        email = data['subscriber']['EMAIL']
        bounce_type = data.get('bounce_type', 'soft')
        if bounce_type == 'hard':
            add_to_suppression(email)
    
    return jsonify({'status': 'ok'})

API rate limiting in MailWizz is configurable per API key. For high-volume integrations (bulk subscriber imports, real-time event processing), request a higher rate limit in the API key settings or process operations in batches with appropriate inter-batch delays to avoid hitting limits.

Need managed MailWizz infrastructure? We operate fully managed MailWizz and PowerMTA environments for high-volume senders.

Need Managed MailWizz Hosting?

Cloud Server for Email provides fully managed MailWizz + PowerMTA environments on EU dedicated servers. Unlimited subscribers, daily monitoring, GDPR-compliant EU data residency.

Need MailWizz support?

Our team manages MailWizz + PowerMTA environments daily. Contact us for a technical assessment.