MailWizz API Reference

Technical reference

MailWizz API Reference

Complete reference for the MailWizz 2.x REST API as deployed on Cloud Server for Email managed infrastructure. Covers SDK setup (PHP, Python, Ruby), authentication, HTTP semantics, and every endpoint we support: lists, fields, segments, subscribers, campaigns, tracking, bounces, countries, customers and templates. Examples are tested against production MailWizz instances we operate.

REST JSON PHP / Python / Ruby SDK MailWizz 2.x Updated 2026
If you are still on MailWizz 1.x

The 1.x API documentation is preserved at api-docs.mailwizz.com/v1. We recommend migrating to 2.x; if you operate a managed instance with us, contact the infrastructure team and we will plan the upgrade with you.

01
Installing the SDKs

The MailWizz 2.x API is HTTP+JSON, so any language with an HTTP client can call it directly. In practice we recommend the maintained SDKs for the three most common languages used by clients on our infrastructure.

PHP

The PHP client is the reference implementation. Install via Composer:

composer require ems-api/php-client

The package exposes endpoint classes under the EmsApi\Endpoint namespace. The example files under vendor/ems-api/php-client/examples/ are the fastest way to see every endpoint in action.

Python

Install the community Python SDK:

pip install mailwizz-python-sdk

Source and examples are at github.com/twisted1919/mailwizz-python-sdk. The setup pattern mirrors PHP: configure once, then instantiate endpoint objects.

Ruby

Clone the repository and install the only required gem:

git clone https://github.com/twisted1919/mailwizz-ruby-sdk.git
cd mailwizz-ruby-sdk
gem install excon

Follow examples/setup_api.rb for initial configuration.

Other languages

Node.js, Go and other languages can use any HTTP client. A community Node package exists at npmjs.com/package/node-mailwizz. For languages without an SDK, calling the REST endpoints directly with a standard HTTP library is straightforward — the conventions in section 3 are all you need.

02
Configuration and setup

Every SDK needs two pieces of information: the API URL of your MailWizz installation, and your API key. Both come from the MailWizz admin panel.

Finding your API URL and key

Inside MailWizz, navigate to Customer Area → Profile → API Keys. Generate a new public API key and copy both the key string and the API URL shown next to it. On our managed installations the URL has the form https://your-mailwizz.example.com/api.

Clean URLs

If your MailWizz instance is not configured with clean URLs (rewrites disabled), the API URL must include /index.php — for example https://example.com/api/index.php. Clients we manage have clean URLs enabled by default.

PHP configuration

require_once __DIR__ . '/vendor/autoload.php';

$config = new \EmsApi\Config([
    'apiUrl' => 'https://your-mailwizz.example.com/api',
    'apiKey' => 'YOUR_PUBLIC_API_KEY',
    'components' => [
        'cache' => [
            'class'     => \EmsApi\Cache\File::class,
            'filesPath' => __DIR__ . '/data/cache',
        ],
    ],
]);

\EmsApi\Base::setConfig($config);
date_default_timezone_set('UTC');

The cache component is optional but strongly recommended. The MailWizz API returns ETag headers on GET requests; the file cache stores responses keyed by ETag and returns the cached body when MailWizz responds with 304 Not Modified. The cache directory must be writable by the PHP process.

Python configuration

from mailwizz.config import Config

Config.set_config({
    'api_url': 'https://your-mailwizz.example.com/api',
    'api_key': 'YOUR_PUBLIC_API_KEY',
    'charset': 'utf-8',
})

TLS

HTTPS is required for production. Self-signed certificates work for development but the SDKs verify by default — disable verification only against an explicit dev URL, never against your production instance. All MailWizz installations we manage ship with valid Let’s Encrypt certificates auto-renewed.

03
HTTP methods and conventions

The API follows REST conventions. Every endpoint matches an HTTP verb to a CRUD operation:

VerbOperationIdempotent
GETRetrieve a resource or collectionYes
POSTCreate a new resourceNo
PUTUpdate an existing resource (full or partial)Yes
DELETEDelete a resourceYes
Server must allow PUT and DELETE

Some shared hosting environments and reverse proxies block PUT and DELETE. If your update or delete calls return 403 Forbidden, the issue is almost always at the web server level, not in MailWizz itself. Apache typically needs <LimitExcept GET POST> directives reviewed; nginx needs proxy_method passed through. On our managed infrastructure both are allowed by default.

Response envelope

Every response is JSON with a consistent envelope. A successful call returns status: "success" plus a data object. A failed call returns status: "error" plus an error message (and sometimes a structured errors object describing per-field problems).

// Success
{
  "status": "success",
  "data": { /* resource or collection */ }
}

// Error
{
  "status": "error",
  "error": "Invalid API key"
}

Pagination

Every list endpoint accepts pageNumber (default 1) and perPage (default 10, max 100). The response carries count, total_pages, current_page, next_page and prev_page — use these to drive your pagination loop rather than guessing.

Caching with ETag

GET responses include an ETag header. Sending it back as If-None-Match on subsequent requests gets you a 304 Not Modified with no body when the resource has not changed. The PHP SDK does this automatically when a cache component is configured (section 2).

04
Lists

Lists are the top-level container for subscribers in MailWizz. Every subscriber, segment, field and campaign hangs off a list.

GETGet all lists

// PHP
$endpoint = new \EmsApi\Endpoint\Lists();
$response = $endpoint->getLists($pageNumber = 1, $perPage = 10);

// HTTP
GET API-URL/lists?page=1&per_page=10

Returns a paginated collection. Each record exposes general (name, display name, description, list_uid), defaults (from_name, from_email, reply_to, subject), notifications (subscribe/unsubscribe webhooks) and company (legal address required by anti-spam laws).

GETGet one list

// PHP
$response = $endpoint->getList('LIST-UNIQUE-ID');

// HTTP
GET API-URL/lists/LIST-UNIQUE-ID

POSTCreate a list

$response = $endpoint->create([
    'general' => [
        'name'        => 'My API List',
        'description' => 'Created from the API',
    ],
    'defaults' => [
        'from_name'  => 'John Doe',
        'from_email' => 'john@example.com',
        'reply_to'   => 'john@example.com',
        'subject'    => 'Hello!',
    ],
    'company' => [
        'name'      => 'John Doe LLC',
        'country'   => 'United States',
        'zone'      => 'New York',
        'address_1' => '500 5th Ave',
        'city'      => 'New York City',
        'zip_code'  => '10019',
    ],
]);

// Response
{
  "status": "success",
  "list_uid": "hv4163y076d84"
}

Required blocks: general and defaults. Optional: notifications and company (if omitted, the customer’s default company is used). For the full list of accepted countries and zones, query the Countries endpoint (section 11).

PUTUpdate a list

$response = $endpoint->update('LIST-UNIQUE-ID', [
    'general' => ['name' => 'Renamed', 'description' => 'Updated via API'],
    /* same block structure as create */
]);

POSTCopy a list

$response = $endpoint->copy('LIST-UNIQUE-ID');
// Returns a new list_uid for the copy.

Copy duplicates the list’s configuration (fields, segments, defaults) but not the subscribers. This is the right operation when you want to fork a list’s structure for a new audience.

DELETEDelete a list

$response = $endpoint->delete('LIST-UNIQUE-ID');
Deletion is destructive and immediate

Deleting a list removes all subscribers, segments, fields and historical campaign references. There is no soft-delete or trash. For production lists, take a backup of subscribers via the Subscribers endpoint before deleting.

05
List fields

Fields define the custom attributes attached to each subscriber: email (always required and built-in), names, segmentation tags, anything specific to your data model.

GETGet all list fields

$endpoint = new \EmsApi\Endpoint\ListFields();
$response = $endpoint->getFields('LIST-UNIQUE-ID');

// HTTP
GET API-URL/lists/LIST-UNIQUE-ID/fields

Each field record returns tag (the column name in API calls and merge tags), label (human-readable name in the MailWizz UI), required (yes/no), help_text and type. Built-in fields are EMAIL, FNAME and LNAME; everything else is custom.

Field types

IdentifierUse for
textSingle-line text (default for names, codes, tags)
textareaMulti-line free text
dropdownPre-defined options (industries, plan tiers)
checkboxBoolean consent or feature flags
radioMutually exclusive options
dateBirthdays, anniversaries, expirations
countryISO-aware country picker, segmentable by region

06
List segments

Segments are saved filters over a list. Once defined, a segment can be the target of a campaign or referenced from autoresponder rules.

GETGet all segments of a list

$endpoint = new \EmsApi\Endpoint\ListSegments();
$response = $endpoint->getSegments('LIST-UNIQUE-ID');

// HTTP
GET API-URL/lists/LIST-UNIQUE-ID/segments

Each record returns segment_uid, name and subscribers_count. The count is materialised — it does not re-execute the segment query on every call, so for very fresh data you should rely on the engagement webhooks rather than polling the segment endpoint.

Segment depth and performance

Segments with many conditions or that reference custom fields with low cardinality can become expensive to evaluate at campaign send time. If a campaign appears stuck at 0 sent, the most common cause we see in production is a segment that times out before MailWizz can enumerate it. Keep segments to 3-5 conditions where possible and prefer indexed fields. We tune MySQL on managed installations to handle deep segments, but no infrastructure makes a poorly-shaped segment fast.

07
Subscribers

The subscribers endpoint is the most-used part of the API. It supports the full CRUD plus several search and bulk operations.

GETGet all subscribers of a list

$endpoint = new \EmsApi\Endpoint\ListSubscribers();
$response = $endpoint->getSubscribers('LIST-UNIQUE-ID', $pageNumber = 1, $perPage = 10);

// HTTP
GET API-URL/lists/LIST-UNIQUE-ID/subscribers?page=1&per_page=10

Returns records with subscriber_uid, all merge tag values (EMAIL, FNAME, LNAME, plus any custom fields you defined), status (confirmed, unconfirmed, unsubscribed, blacklisted), source (api, import, web, etc.) and date_added.

POSTCreate a subscriber

$response = $endpoint->create('LIST-UNIQUE-ID', [
    'EMAIL' => 'subscriber@example.com',
    'FNAME' => 'John',
    'LNAME' => 'Doe',
    // any custom field tag goes here too
]);
A confirmation email is sent by default

If the list is configured for double opt-in (the default and recommended setting), creating a subscriber via the API triggers the confirmation email. Use real email addresses during testing. To bypass confirmation entirely, switch the list to single opt-in in the MailWizz admin — not at the API level — before importing.

POSTCreate subscribers in bulk

$response = $endpoint->createBulk('LIST-UNIQUE-ID', [
    ['EMAIL' => 'a@example.com', 'FNAME' => 'Anna'],
    ['EMAIL' => 'b@example.com', 'FNAME' => 'Bob'],
    ['EMAIL' => 'c@example.com', 'FNAME' => 'Carol'],
    // up to 1000 per request on managed installations
]);

// HTTP
POST API-URL/lists/LIST-UNIQUE-ID/subscribers/bulk

Bulk create is dramatically faster than calling create in a loop because it issues a single transaction. On infrastructure we operate, we lift the default 1000-per-request limit when justified by a one-off import; ask the infrastructure team before designing a sustained pipeline that exceeds it.

PUTUpdate a subscriber

// By subscriber_uid
$response = $endpoint->update('LIST-UNIQUE-ID', 'SUBSCRIBER-UNIQUE-ID', [
    'FNAME' => 'John',
    'LNAME' => 'Doe Updated',
]);

// By email (search-then-update under the hood)
$response = $endpoint->updateByEmail('LIST-UNIQUE-ID', 'subscriber@example.com', [
    'FNAME' => 'John',
    'LNAME' => 'Doe Updated',
]);

POSTCreate or update (upsert)

$response = $endpoint->createUpdate('LIST-UNIQUE-ID', [
    'EMAIL' => 'subscriber@example.com',
    'FNAME' => 'John',
    'LNAME' => 'Doe',
]);

This is the right call when you sync from an external system and do not want to track which subscribers already exist. MailWizz performs a search-by-email, updates if found, creates otherwise.

Search endpoints

// Search by email within one list
$endpoint->emailSearch('LIST-UNIQUE-ID', 'subscriber@example.com');

// Search by email across all lists
$endpoint->emailSearchAllLists('subscriber@example.com');

// Search by custom field values
$endpoint->searchByCustomFields('LIST-UNIQUE-ID', [
    'COMPANY' => 'Acme Inc',
]);

// Search by status: confirmed | unconfirmed | unsubscribed | blacklisted
$endpoint->searchByStatus('LIST-UNIQUE-ID', 'confirmed');

The convenience getters getConfirmedSubscribers(), getUnconfirmedSubscribers() and getUnsubscribedSubscribers() wrap the same status search with pagination.

PUTUnsubscribe

// Silent unsubscribe (no email sent)
$endpoint->unsubscribe('LIST-UNIQUE-ID', 'SUBSCRIBER-UNIQUE-ID');
$endpoint->unsubscribeByEmail('LIST-UNIQUE-ID', 'subscriber@example.com');

// Unsubscribe from every list at once
$endpoint->unsubscribeByEmailFromAllLists('subscriber@example.com');

All unsubscribe operations are silent — they do not send a goodbye email. For GDPR-style erasure requests, use unsubscribe-from-all-lists then follow with delete if you have a legal obligation to remove the record entirely.

DELETEDelete a subscriber

$endpoint->delete('LIST-UNIQUE-ID', 'SUBSCRIBER-UNIQUE-ID');
$endpoint->deleteByEmail('LIST-UNIQUE-ID', 'subscriber@example.com');

08
Campaigns

Campaigns are messages sent to a list (optionally narrowed by a segment). The API supports regular and autoresponder campaigns, with full lifecycle control: create, schedule, pause, resume, mark sent, delete.

GETGet all campaigns

$endpoint = new \EmsApi\Endpoint\Campaigns();
$response = $endpoint->getCampaigns($pageNumber = 1, $perPage = 10);

// HTTP
GET API-URL/campaigns

GETGet one campaign

$response = $endpoint->getCampaign('CAMPAIGN-UNIQUE-ID');

Returns the full campaign record with metadata, scheduling, template reference, list and segment binding, and current status.

POSTCreate a campaign

$response = $endpoint->create([
    'name'        => 'My API Campaign',
    'type'        => 'regular',            // regular | autoresponder
    'from_name'   => 'John Doe',
    'from_email'  => 'john@example.com',
    'subject'     => 'Testing the API',
    'reply_to'    => 'john@example.com',
    'send_at'     => date('Y-m-d H:i:s', strtotime('+10 hours')),
    'list_uid'    => 'LIST-UNIQUE-ID',
    'segment_uid' => 'SEGMENT-UNIQUE-ID',  // optional
    'options' => [
        'url_tracking'     => 'yes',
        'json_feed'        => 'no',
        'xml_feed'         => 'no',
        'plain_text_email' => 'yes',
        'email_stats'      => 'reports@example.com',
    ],
    'template' => [
        // exactly one of: archive | template_uid | content
        'content'         => file_get_contents(__DIR__ . '/template.html'),
        'inline_css'      => 'yes',
        'plain_text'      => null,        // null = auto-generate
        'auto_plain_text' => 'yes',
    ],
]);

// Response
{
  "status": "success",
  "campaign_uid": "hv4163y076d84"
}

Autoresponder campaigns

Set type to autoresponder and add three extra fields in the options block:

'autoresponder_event'            => 'AFTER-SUBSCRIBE',
'autoresponder_time_unit'        => 'hour',   // minute | hour | day | week | month | year
'autoresponder_time_value'       => 1,
// only when event is AFTER-CAMPAIGN-OPEN:
'autoresponder_open_campaign_id' => 123,

Available events: AFTER-SUBSCRIBE (fires when the subscriber joins the list) and AFTER-CAMPAIGN-OPEN (fires when they open a specific other campaign).

Recurring campaigns

For repeated sends on a schedule, add a cron expression:

'cronjob'         => '0 9 * * MON',  // every Monday at 09:00
'cronjob_enabled' => 1,

Recurring is only valid for type=regular. The campaign re-evaluates the list (and segment, if any) each time it fires, so new subscribers are included on subsequent runs.

PUTUpdate / POSTCopy / PUTPause-Unpause / PUTMark sent

$endpoint->update('CAMPAIGN-UNIQUE-ID', [/* same shape as create */]);
$endpoint->copy('CAMPAIGN-UNIQUE-ID');
$endpoint->pauseUnpause('CAMPAIGN-UNIQUE-ID');
$endpoint->markSent('CAMPAIGN-UNIQUE-ID');
$endpoint->delete('CAMPAIGN-UNIQUE-ID');

markSent exists for operational recovery: if a campaign was sent through some external mechanism and you need to update MailWizz state without resending, this is the correct call.

GETCampaign stats

$response = $endpoint->getStats('CAMPAIGN-UNIQUE-ID');

Returns the full stats envelope: subscribers_count, processed_count, delivery_success/error counts and rates, opens (total and unique), clicks (total and unique), unsubscribes, complaints, bounces broken down by hard, soft and internal. Open rate after May 2021 should be treated cautiously because of Apple Mail Privacy Protection — for the operational context, see our note on Apple MPP engagement metric reliability.

09
Campaign tracking

The tracking endpoint is used by MailWizz internally to record opens, clicks and unsubscribes triggered from email content. Direct calls are useful when relaying tracking through your own infrastructure or when reconstructing events after a webhook delivery failure.

GETTrack URL click

$endpoint = new \EmsApi\Endpoint\CampaignsTracking();
$response = $endpoint->trackUrl('CAMPAIGN-UID', 'SUBSCRIBER-UID', 'URL-HASH');

// HTTP
GET API-URL/campaigns/CAMPAIGN-UID/track-url/SUBSCRIBER-UID/URL-HASH

The URL-HASH is the SHA-256 of the original click target as MailWizz stored it when the campaign was prepared. You retrieve hashes from the campaign template or from the URL-stats endpoint of the MailWizz admin.

GETTrack open

$endpoint->trackOpening('CAMPAIGN-UID', 'SUBSCRIBER-UID');

// HTTP
GET API-URL/campaigns/CAMPAIGN-UID/track-opening/SUBSCRIBER-UID

POSTTrack unsubscribe

$endpoint->trackUnsubscribe('CAMPAIGN-UID', 'SUBSCRIBER-UID', [
    'ip_address' => '203.0.113.42',
    'user_agent' => 'Mozilla/5.0 ...',
    'reason'     => 'No longer interested',
]);

Recording the unsubscribe reason is optional but extremely useful for list hygiene analysis — correlating reasons against segments or campaigns surfaces content problems early.

10
Campaign bounces

The campaign bounces endpoint exposes the bounce records associated with a campaign. Use it to feed external monitoring, to drive a custom suppression workflow, or to backfill bounce data when the SMTP-level bounce processor was misconfigured during a send.

GETGet all bounces

$endpoint = new \EmsApi\Endpoint\CampaignBounces();
$response = $endpoint->getBounces('CAMPAIGN-UNIQUE-ID', $pageNumber = 1, $perPage = 10);

Each record returns the SMTP message (raw bounce string), processed flag, bounce_type (hard, soft or internal), and the subscriber identifier.

POSTCreate a bounce

$endpoint->create('CAMPAIGN-UNIQUE-ID', [
    'message'        => '5.1.1 Recipient address rejected: User unknown',
    'bounce_type'    => 'hard',        // hard | soft | internal
    'subscriber_uid' => 'SUBSCRIBER-UNIQUE-ID',
]);
Where bounces should normally come from

On managed infrastructure we operate, bounces are recorded automatically at the MTA layer by PowerMTA’s feedback-loop and accounting pipeline. You should only call this endpoint when integrating an external bounce processor or recovering from a configuration gap. Operational context on this is captured in our note on real-time bounce processing at the MTA layer.

11
Countries and zones

The Countries endpoint is read-only and powers the country/zone validation in list company blocks and customer creation. Use it to populate dropdowns or validate user input before sending it to the create endpoints.

GETGet all countries

$endpoint = new \EmsApi\Endpoint\Countries();
$response = $endpoint->getCountries($pageNumber = 1, $perPage = 100);

Returns country_id, name and ISO-3166 alpha-2 code. There are 240 countries in the dataset, so a single 100-per-page call gets you the first half.

GETGet zones (states / provinces) of a country

$response = $endpoint->getZones('COUNTRY-ID', $pageNumber = 1, $perPage = 100);

// HTTP
GET API-URL/countries/COUNTRY-ID/zones

Returns zone_id, name and code for every sub-region MailWizz knows about for that country (US states, Canadian provinces, Brazilian states, etc.).

12
Customers

The customers endpoint is used by resellers and platform operators creating new MailWizz customers programmatically. On managed installations this endpoint is restricted to admin keys; reach the infrastructure team to enable it for a delegated key if you need it for onboarding automation.

POSTCreate a customer

$endpoint = new \EmsApi\Endpoint\Customers();
$response = $endpoint->create([
    'customer' => [
        'first_name' => 'John',
        'last_name'  => 'Doe',
        'email'      => 'john@example.com',
        'password'   => 'STRONG_PASSWORD',
        'timezone'   => 'UTC',
        'birthDate'  => '1985-04-12',
    ],
    'company' => [
        'name'      => 'John Doe LLC',
        'country'   => 'United States',
        'zone'      => 'New York',
        'city'      => 'Brooklyn',
        'zip_code'  => '11222',
        'address_1' => '500 Some Street',
        'address_2' => '',
    ],
]);

// Response
{
  "status": "success",
  "customer_uid": "wc149l7wdm9be"
}

The company block becomes mandatory if MailWizz is configured to require it. Country and zone names must match exactly what the Countries endpoint (section 11) returns — that is why we treat Countries as a hard dependency for any automation that creates customers or lists.

13
Templates

Templates are reusable HTML messages. Storing them through the API rather than inlining content into each campaign lets you version templates separately and reuse them across many campaigns.

GETGet / search templates

$endpoint = new \EmsApi\Endpoint\Templates();

// All templates
$endpoint->getTemplates($pageNumber = 1, $perPage = 10);

// One template
$endpoint->getTemplate('TEMPLATE-UNIQUE-ID');

// Search by name (MailWizz 1.4.4+)
$endpoint->searchTemplates(1, 10, ['name' => 'welcome']);

POSTCreate a template

$response = $endpoint->create([
    'name'       => 'Welcome Email v3',
    'content'    => file_get_contents(__DIR__ . '/welcome.html'),
    // OR: 'archive' => file_get_contents(__DIR__ . '/welcome.zip'),
    'inline_css' => 'yes',
]);

// Response
{
  "status": "success",
  "template_uid": "jo441taeq281e"
}

If your template uses external CSS files, ship them inside a zip archive and pass the binary contents under the archive key. MailWizz unpacks the archive, inlines the CSS at render time if inline_css is yes, and stores the result. Inlining is essential for legacy mail clients (Outlook, some webmail) that strip head-level styles.

PUTUpdate / DELETEDelete

$endpoint->update('TEMPLATE-UNIQUE-ID', [/* same shape as create */]);
$endpoint->delete('TEMPLATE-UNIQUE-ID');

14
Errors and troubleshooting

The error envelope is consistent across endpoints, but production debugging often comes down to four recurring problem shapes. We catalogue them here with the diagnostic approach we use on the infrastructure side.

Authentication errors

StatusLikely causeFix
401Missing or malformed API key headerCheck the X-MW-PUBLIC-KEY header in the SDK’s outgoing request log
403 methodServer blocks PUT/DELETECheck Apache/nginx config; managed installations allow all methods by default
403 IPAPI key has an IP allowlist configuredAdd the calling IP in MailWizz admin or use a key without IP restriction for non-production debugging

Validation errors on create / update

The error response includes an errors object keyed by field name with arrays of human-readable messages. Surface these to your end users rather than the generic error field, which only carries the first line of the failure.

// Example response
{
  "status": "error",
  "error": "Validation failed",
  "errors": {
    "EMAIL": ["Email is not a valid email address."],
    "FNAME": ["First name cannot be blank."]
  }
}

“Upload file size is fixed to 2 MB” on imports

This is not a MailWizz limit — it is PHP’s upload_max_filesize and post_max_size. The fix is two values in php.ini (or a per-vhost override). On our managed infrastructure both are set to 64 MB by default and we adjust higher on request. If you are self-hosting, edit php.ini, restart PHP-FPM and the limit increases immediately.

“Campaign stuck at 0 sent”

The most common production cause is a segment that times out before MailWizz can enumerate it (see the segment-depth note in section 6). The second most common is a delivery server with no remaining hourly quota. Check the campaign delivery log in the MailWizz admin: if the log is empty, the issue is segment evaluation; if entries exist but stop, the issue is downstream — usually the MTA queue or rate limiting.

HTTP 304 with empty body

Expected. The SDK’s cache component will return the previously cached response transparently when MailWizz answers 304. If you are not using the cache and receive 304, your client is sending an If-None-Match header; remove it for non-conditional requests.

When to involve the infrastructure team

Anything that affects throughput — sustained 5xx errors, very slow responses on bulk endpoints, or behaviour that is inconsistent across calls — is almost always infrastructure-side rather than API-side. Email infrastructure@cloudserverforemail.com with the campaign UID, the list UID and the time window; we have full access logs and can correlate against the MTA-side picture.

Related material

This reference is the technical complement to several operational notes and managed-service pages elsewhere on the site:

Need this API in production?

We operate MailWizz instances with this API enabled by default.

Managed PowerMTA + MailWizz, daily monitoring, EU data center, and direct engineer access. The API runs on the same infrastructure that handles your deliverability.

See Managed MailWizz Talk to engineering