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
| Requirement | Detail |
|---|---|
| PHP | 8.1 or higher |
| MariaDB / MySQL | 5.7+ / 10.3+ |
| Web server | Nginx or Apache |
| Outgoing mail | SMTP or PHPMailer, for double opt-in confirmation emails |
| Outbound HTTPS | Server must reach cloudwizard.eu for license verification |
PHP intl extension | Recommended. 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.
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.
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.
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.
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.
| Section | What it controls |
|---|---|
| Identity | Name, 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. |
| Sender | Sender name, sender email, reply-to (optional), and sender domain — used both for outgoing mail and for scoping suppression by domain. |
| Opt-in & unsubscribe | Double opt-in (requires email confirmation) and one-click unsubscribe, both independently toggleable per list. |
| Redirects | Post-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 email | Subject and HTML body — only sent when Double opt-in is on. Merge tags: {{confirm_url}} {{first_name}} {{email}} {{list_name}}. |
| Welcome email | Subject and HTML body, sent once confirmation is complete. Merge tags: {{first_name}} {{email}} {{unsubscribe_url}} {{list_name}}. |
What Audience Manager adds for you
- A confirm link. If a confirmation email's body has no
{{confirm_url}}, a "Confirm my subscription" link is added at the bottom, in the list's language, so the email is never a dead end. - An unsubscribe link. If a welcome email's body has no
{{unsubscribe_url}}, an unsubscribe link is added at the bottom. Confirmation emails never carry one, because nobody has subscribed yet. - Unsubscribe headers. Welcome emails carry the standard
List-Unsubscribeheaders, which lets mail clients such as Gmail offer their own Unsubscribe button. The one-click part follows the list's One-click unsubscribe setting. - A plain-text version of every email, with the addresses of links kept in the text.
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:
- the default confirmation and welcome email text, written when the list is created;
- the confirmed page and the unsubscribe page a subscriber sees;
- the wording of the links Audience Manager adds to emails ("Confirm my subscription", "Unsubscribe");
- right-to-left layout for Arabic and Hebrew pages.
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.
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.
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.
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).
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.
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.
- Apply to lists — every active list is checked by default; uncheck any you don't want touched.
- Also add to suppression list — on by default. Prevents these addresses from being re-added even if they appear in a future import.
- Suppression context — leave blank for a global suppression (blocks sending from every domain), or enter one or more domains to scope the suppression to just those.
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.
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
- Always use HTTPS. Subscriber records include email, name, and geo data — never run this over plain HTTP.
- The controller admin is protected by your admin password. Use a strong password (the setup wizard enforces a minimum length).
- List deletion requires typed confirmation. This is deliberate friction against an accidental click deleting thousands of subscribers — don't work around it with muscle-memory typing.
- Restrict admin access by IP if possible. In Nginx, inside the
locationblock for your CW directory:
allow 1.2.3.4; deny all; - Review suppression scope carefully before a bulk unsubscribe import — a blank Suppression context blocks sending across every domain in the install, not just the lists you selected.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| "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