Audience Manager v1.0

Part of CloudWizard Email Suite

Audience Manager is the subscriber identity layer of the suite — lists, members, consent, and cross-list segmentation. It's the single source of truth for who a contact is and what they've consented to, feeding Mail Engine campaigns directly and sharing a stable identity token with every other module.

Requirements

RequirementDetail
PHP8.1 or higher
MariaDB / MySQL5.7+ / 10.3+
Web serverNginx or Apache
Outgoing mailSMTP or PHPMailer, for double opt-in confirmation emails
Outbound HTTPSServer must reach cloudwizard.eu for license verification
PHP intl extensionRecommended. It lets a list slug be made from a name in Arabic, Greek, Hebrew, Chinese, Japanese and other non-Latin scripts. Without it those names get a random slug such as list-a3f9c2. Latin (including accents) and Cyrillic names work either way.

Step 1 — Install the Controller

Audience Manager runs inside the CloudWizard Controller. The controller handles authentication, shared database config, and module detection. Install it first.

Upload the controller

Unzip the download. It holds one folder, CW/, with the controller and Audience Manager inside. Upload that whole folder to a directory on your server — typically public_html/CW/. The controller part looks like this:

public_html/CW/
  bootstrap.php
  config_path.php
  index.php          ← sends /CW/ to the admin login
  vendor/            ← bundled PHPMailer, shared across every module
  admin/
    index.php
    login.php
    logout.php
    settings.php
    setup.php
    style.css
    ...

Run the setup wizard

Visit https://yourdomain.com/CW/admin/setup.php to run the wizard. Fill in:

  • Database — host, name, username, password, table prefix (default: cw_)
  • SMTP — outgoing mail server credentials and sender address, used for confirmation and welcome emails
  • Admin password — minimum 8 characters
  • Site URL — your public domain

Click Complete setup. The config file is written outside your webroot where possible.

Log in

You're redirected to the login page. Enter your admin password. You should see the CloudWizard Email Suite dashboard.

Controller ready. Continue to install Audience Manager.

Step 2 — Install Audience Manager

Upload the module

The module is already inside the zip you just uploaded, at CW/AudienceManager/. Adding it to an existing CloudWizard install instead? Don't upload the whole CW/ folder again: copy this module's AudienceManager/ folder into your existing controller directory, together with the vendor/ folder if you do not already have CW/vendor/, then reload the dashboard. Its file layout:

public_html/CW/AudienceManager/
  module.php
  bootstrap.php
  confirm.php
  unsubscribe.php
  lib/
    mailer.php
    strings.php
    audience.php   ← the one definition of "who is in this audience"
  admin/
    _bootstrap.php
    index.php
    lists.php
    list_edit.php
    subscribers.php
    subscriber_edit.php
    audience.php
    create_campaign.php
    unsubscribe_import.php
    stats.php

Verify detection

Reload the controller dashboard. The Audience Manager card should show Open →. If it still shows "Not installed", confirm AudienceManager/module.php is on the server and readable by PHP.

All module directories must sit directly inside the controller root (CW/AudienceManager/), not nested deeper.

Step 3 — Activate your license

Get your license key

Whether you purchase through cloudwizard.eu or via Gumroad, your license key is shown on the confirmation page and emailed to you. Format: XXXX-XXXX-XXXX.

Enter the key

In the controller go to Settings → License key, paste the key, click Save settings. The key is verified against the CloudWizard license server immediately.

Verification

Once active, your install domain is registered. The license is re-verified automatically in the background every few days — no manual action required.

License active. Go to Audience Manager and create your first list.

Overview

Audience Manager → Overview shows five stat cards — Total subscribers, Confirmed, Pending, Unsubscribed, and Lists — followed by a Subscribers by List table breaking those same confirmed/pending/unsubscribed counts down per individual list, so you can see at a glance which list is actually healthy versus which one is mostly unsubscribes.

Creating lists

From Audience Manager → Lists, click + New list. Every list needs a name, sender email, and sender domain. The slug is created from the name if left blank; accents and non-Latin letters are converted, so Uppdatering Månadsvis becomes uppdatering-manadsvis. A slug you type yourself is cleaned the same way.

Fill in list details

List name, optional custom slug, sender name (defaults to the list name if left blank), sender email, sender domain, and the list's language.

Save

Click Create list →. Double opt-in and one-click unsubscribe are both on by default for every new list, and the confirmation and welcome emails are filled in with default text in the list's language. You land on the list's Edit page to adjust anything, including the public name.

List created. It now appears in Lists with confirmed/pending/unsubscribed/bounced counts.

Each list in the table shows its status, subscriber counts by state, and sender email, with Edit, Members, Stats, and Delete actions. Deleting a list requires typing the exact list name to confirm — this cannot be undone.

Confirmation & welcome emails

Every one of these settings lives on the list itself, not globally — open a list and click Edit to configure them. This means two lists can have entirely different confirmation wording, sender identity, and redirect behavior even on the same install.

SectionWhat it controls
IdentityName, public name (what subscribers see instead of the name), slug (used in signup form markup — changing it after forms are already live elsewhere will break those forms), status, and language.
SenderSender name, sender email, reply-to (optional), and sender domain — used both for outgoing mail and for scoping suppression by domain.
Opt-in & unsubscribeDouble opt-in (requires email confirmation) and one-click unsubscribe, both independently toggleable per list.
RedirectsPost-signup redirect (where a visitor lands right after submitting) and post-confirm redirect (where a subscriber lands after clicking the confirmation link) — both optional; leave blank to use the built-in default page.
Confirmation emailSubject and HTML body — only sent when Double opt-in is on. Merge tags: {{confirm_url}} {{first_name}} {{email}} {{list_name}}.
Welcome emailSubject and HTML body, sent once confirmation is complete. Merge tags: {{first_name}} {{email}} {{unsubscribe_url}} {{list_name}}.
Leave Subject blank on either email to skip sending it entirely — an empty confirmation subject on a double opt-in list means subscribers never receive a link to confirm.

What Audience Manager adds for you

The welcome email goes out when a member confirms through the emailed link. Members you add as Confirmed, or import, are not sent one.

Languages & public name

Each list has one language, chosen from 25: Arabic, Bulgarian, Chinese, Czech, Danish, Dutch, English, Finnish, French, German, Greek, Hebrew, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, Portuguese, Romanian, Russian, Spanish, Swedish, Turkish and Ukrainian. Need the same newsletter in two languages? Make two lists. That keeps every email reviewable before it goes out, and each subscriber sees one language only.

The list's language decides:

It never translates text you write yourself, which is sent exactly as typed. A member's own language tag (set on Add member and Edit member) is for your records: emails always follow the list's language.

A link that is invalid or already used cannot say which list it belonged to, so that page uses the visitor's browser language, and English if the language is not one of the 25.

Changing a list's language

When you change the language on a list's Edit page and save, each email is switched to the new language's default text only if its subject and body are both still the old language's default, or both blank. An email you have edited is left exactly as you wrote it. The message shown after saving says which emails were switched.

Public name

The list name is yours to label things, for example Newsletter EN. The public name is what subscribers see: on the confirmed and unsubscribe pages, and as {{list_name}} in emails. Leave it blank to use the list name. Set it on the list's Edit page; the admin screens keep showing the list name.

Managing members

Open Members on any list to see every subscriber on it, searchable by email or name and filterable by status. The table shows email, name, country, status, source and join date, plus — when Mail Engine is installed — how many of its campaigns the member has opened.

Adding one member

The Add member manually card takes an email, an optional name, a status and a language. A full name is split at the first space: Jan R Linnaey becomes first name Jan and last name R Linnaey, so a greeting can use just the first name. Correct the odd one on the member's Edit page. If the address is already in the system, for example on another list, its existing name and language are kept.

Choose Confirmed for someone who has agreed to hear from you, such as a business card handed over in person. Choose Pending if they still need to confirm, then click Send confirmation on their row. Confirmations are sent one member at a time on purpose. Sending again issues a fresh link and disables the old one.

Importing a CSV

The file needs an email column. Optional columns are name (split at the first space) or first_name and last_name, plus country, city and timezone. Details already stored for a subscriber are never overwritten, addresses already on the list are skipped, and everyone imported is added as Confirmed.

Members added as Confirmed, and every CSV import, skip double opt-in. Use them only for people who have actually agreed to receive your emails.

Editing and erasing

Edit opens the member's details: email, first and last name, language, country (ISO code such as BG or SE), city and timezone, plus their status on every list. Erase on a row removes the member from this list permanently, and deletes the subscriber record too if they are on no other list.

Editing a member's status inline (Confirmed / Pending / Unsubscribed) takes effect immediately — there's no separate save step for status changes made from the member list.

List stats

The Stats link on each list shows how it is doing: counts of confirmed, pending, unsubscribed and bounced or complained members, the confirm rate (confirmed as a share of confirmed plus pending), where members came from, and a chart of activity over the last 7 days, 30 days, 90 days or a year. The chart loads its library (Chart.js) from cdnjs.cloudflare.com in your browser. If that is blocked, the numbers still show but the chart does not.

When Mail Engine is installed and has sent campaigns, a tile shows Opened (of mailed): of the confirmed members who have been sent at least one Mail Engine campaign, the share who opened at least one. Hover over it for the raw counts. Measuring against members who were actually mailed keeps a large, partly mailed list from looking unengaged. Opens are counted with tracking images, and some mail apps, Apple Mail for one, load images automatically, so read the figure as an upper bound. Without Mail Engine there is no open data, and the tile and the Opens column are hidden.

Audience Builder

The Audience Builder lets you combine one or more lists into a single filtered audience — for sending, exporting, or both.

Select lists

Tick one or more lists — each shows its confirmed subscriber count next to the checkbox — then click Select these lists →.

Apply filters

Country, City, and Timezone are single-value filters — pick one and the result updates immediately. Domain is different: it supports Select only or Exclude, letting you isolate or remove a specific email domain (e.g. only Gmail addresses, or everyone except Gmail).

Select only / Exclude applies to the Domain filter specifically — Country, City, and Timezone are always "equals this value", not include/exclude.

Export or send

Once you have a result, ↓ Export CSV downloads the filtered set. If Mail Engine is installed, you'll also see + Create new campaign and, if a draft campaign already exists, an → Add to campaign selector to push the filtered audience straight into it.

Every push is written to the campaign's audience log (lists and filters used, how many were added), which the Mail Engine campaigns list shows. Mail Engine's Duplicate + audience uses the same log to give a copied campaign the same recipients.

A preview of the first 50 matching recipients (email, name, country, city, timezone) is shown below the count for a quick sanity check before exporting or sending.

How unsubscribing works

The unsubscribe link in an email opens a page, in the list's language, that asks the subscriber to confirm. They are removed only when they press the button, so mail scanners that open every link in a message cannot unsubscribe anyone by accident. Unsubscribing applies to that one list. The member stays in the system with the status unsubscribed and a timestamp, the event is recorded in the consent log, and a member marked bounced or complained keeps that stronger status.

Mail clients that offer their own Unsubscribe button use the List-Unsubscribe headers described above and skip the page. Audience Manager itself sends only confirmation and welcome emails. Campaign emails sent through Mail Engine use Mail Engine's own unsubscribe link.

Set a fixed signing key before you send real mail. Every unsubscribe link carries a signature that only your install can produce, so nobody can unsubscribe other people by guessing addresses. The key comes from secret_key in your config file if you set one, and otherwise is derived from your database settings. Changing the database credentials, or adding a secret_key later, makes the links in emails already sent stop working. Add a line such as 'secret_key' => 'a-long-random-string', to the config file the setup wizard wrote before your first real send.

Bulk unsubscribe import

From Audience Manager → Unsub Import, upload a CSV of email addresses to unsubscribe them from every list you select. The file needs an email column header, or can be a single column of addresses with no header at all.

After import, a results summary shows how many were unsubscribed, how many were already unsubscribed, how many email addresses weren't found in the system at all, and (if enabled) how many were added to suppression.

Shared identity & suite integration

Audience Manager assigns a stable, permanent identity token to every subscriber it creates — whether via CSV import, manual entry, or a Signup Manager handoff. This token never changes for the lifetime of the install and is shared across every CloudWizard module that touches that email address, which is what keeps a subscriber's unsubscribe link valid for life regardless of which product or import they entered through.

When Signup Manager, Mail Engine, and Bounce Manager share the same database and table prefix as Audience Manager, they read and write this same identity — no manual sync step required.

Running standalone

Audience Manager works fully on its own — creating lists, managing members, running the Audience Builder, and bulk unsubscribe import all require nothing beyond the controller itself.

Without Mail Engine installed, the Audience Builder simply omits the campaign-related actions — you'll see Export CSV only, and can still filter and export your audience for use anywhere else.

A standalone install sends two kinds of email: the confirmation and the welcome email. Sending newsletters or campaigns takes Mail Engine.

Security

Troubleshooting

ErrorCauseFix
"Could not send" when clicking Send confirmation The list's confirmation subject or body is blank, or the mail server refused the message Fill in both on the list's Edit page. If they are filled in, look for AM confirm email failed in the PHP error log for the reason.
Unsubscribe link shows "invalid" The link was cut or changed by a mail client, or it was signed with a different key (database settings or secret_key changed since it was sent) Copy the full link from the email. For links sent before a key change, unsubscribe the member from the Members page.
"Opened (of mailed)" tile or Opens column is missing Mail Engine is not installed here, or has not sent a campaign yet Expected in standalone mode — open data comes only from Mail Engine.
A list slug looks like list-a3f9c2 The name is in a script the server cannot transliterate without the PHP intl extension Type a slug yourself (Latin letters and digits), or ask your host to enable intl.
Confirmation emails not sending SMTP not configured Check the controller's SMTP settings under Settings.
"Create new campaign" / "Add to campaign" missing from Audience Builder Mail Engine is not installed Expected behavior in standalone mode — install Mail Engine to enable campaign push, or use Export CSV instead.
Country/City/Timezone filter doesn't seem to apply These are exact-match filters, not include/exclude — a value that doesn't exactly match any subscriber returns nothing Check the dropdown options actually present for your selected lists; only values that occur are listed.
List won't delete Typed confirmation text didn't exactly match the list name Copy the list name exactly as shown, including capitalization and spacing.
Subscribers not appearing after CSV import CSV missing an email column, or malformed rows Confirm the file has a header row with email, or is a single unheaded column of addresses.
Unsubscribe import affected the wrong lists All active lists are checked by default in Unsub Import Uncheck any list you don't want touched before clicking Import & unsubscribe — there's no undo.
Module not detected on dashboard module.php missing or not readable Confirm CW/AudienceManager/module.php exists. Restart PHP-FPM: systemctl restart php8.3-fpm
License key not accepted Wrong key format or server can't reach cloudwizard.eu Key format: XXXX-XXXX-XXXX. Check outbound HTTPS from the server: curl -s https://cloudwizard.eu/license/ping.php
500 error on admin pages DB error, missing table, or file permissions tail -20 /home/yourusername/logs/php_log

CloudWizard Email Suite — Audience Manager v1.0
cloudwizard.eu — support@cloudwizard.eu