Setting Up AcelleMail as a SaaS Platform

AcelleMail can be operated as a multi-tenant SaaS where each customer signs up, picks a plan, pays via Stripe, and gets an isolated workspace (lists, campaigns, automations, sending quotas). This guide walks the end-to-end setup: Extended License, Stripe (the only first-class subscription gateway today), plan design, customer onboarding flow, tenant isolation, and the operational realities of running this as a business.

What this is for

AcelleMail can be operated as a multi-tenant SaaS — each customer signs up, picks a plan, pays via Stripe, and gets an isolated workspace (lists, campaigns, automations, sending quotas, even their own sending domain). The operator (you) collects subscription revenue, manages the underlying infrastructure, and keeps the margin.

This guide walks the end-to-end setup. Before you start, decide whether SaaS is actually the right model — see the Building Your Email Marketing SaaS sibling article for the pricing + GTM playbook.

Prerequisites:

  • An AcelleMail Extended License from CodeCanyon. The Regular License does not allow you to resell access to the platform — the Extended License is required for any SaaS use case.
  • A live AcelleMail install — see the canonical Ubuntu 24.04 guide. At SaaS scale, target Medium tier (4 vCPU, 8 GB) minimum.
  • A Stripe account (the only first-class subscription gateway in current Acelle Cashier — see the FAQ for why PayPal isn't recommended).
  • A sending relay configured (Amazon SES is the typical choice; you can also let customers BYO their own SES/Mailgun/SendGrid).

Step 1 — Enable customer signups

By default, AcelleMail is single-tenant — only admins can create accounts. For SaaS, you flip this in Admin → Settings → General → User Signup:

  • Enable user signup: Yes
  • Email confirmation required: Yes (essential — without it spammers will sign up to use your sending IPs)
  • Default plan after signup: the free / trial plan you'll create in Step 3
  • Welcome email template: pick or create one that sets customer expectations + links to your docs

The public /register route is now live. Customers who hit it will see your branded signup form (configured per the white-label guide).

Step 2 — Configure Stripe as your payment gateway

In Admin → Payment Gateways → Stripe → Edit:

Field Where it comes from
Publishable Key Stripe Dashboard → Developers → API keys → pk_live_...
Secret Key Stripe Dashboard → Developers → API keys → sk_live_...
Webhook Signing Secret Set up the webhook first (next paragraph), then paste the whsec_... from Stripe
Mode live (use test for staging)
Currency The Stripe-account default; must match plan currencies

Set up the Stripe webhook — required for renewals, cancellations, and dunning to work:

  • URL: https://your-domain.com/stripe/webhook (the URL is shown in your AcelleMail's Stripe gateway page after you save)
  • Events to send: customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, invoice.paid, invoice.payment_failed, payment_intent.succeeded

Test mode first. Use the Stripe test keys + test webhook on a staging install before flipping to live mode. A wrong webhook in production means missed renewals and angry customers.

Click Enable on the gateway. Disable any other gateways you're not using (Admin → Payment Gateways → toggle the rest off) — leaving inactive ones enabled shows confusing options to your customers at checkout.

Step 3 — Design plans

In Admin → Plans → Add new. Each plan controls what a customer can do, and the price they pay for it.

The Plan form has these fields (per the plans table schema):

Field What it sets
Name Plan display name on your pricing page
Description Markdown allowed; shows under the plan card
Price Decimal; the subscription amount per frequency
Currency Must match your Stripe account currency
Frequency amount + unit e.g. 1 + month for monthly; 1 + year for annual
Trial amount + unit e.g. 14 + day for a 14-day free trial; leave blank for no trial
Status Active / Inactive
Visible If false, plan exists but doesn't show on the public pricing page (use for grandfathered or hidden plans)
Own tracking domain required Forces each customer on this plan to set up their own tracking subdomain (recommended for higher tiers to protect your shared domain's reputation)
Options (JSON) The quota / feature flags — see below

The Options field is a JSON blob stored on the plan. This is where the per-plan quotas live:

{
  "max_subscribers": 5000,
  "max_lists": 10,
  "max_campaigns": -1,
  "max_emails_per_month": 50000,
  "max_subscribers_per_list": 5000,
  "max_segments_per_list": 10,
  "max_automations": 3,
  "max_templates": -1,
  "max_sending_servers": -1,
  "max_email_verification_servers": -1,
  "max_team_members": 2,
  "api_access": true,
  "create_sending_server_enabled": false,
  "automation_enabled": true,
  "custom_domain_enabled": false,
  "custom_html_enabled": false
}

Use -1 for "unlimited" on numeric quotas. true/false for feature toggles. The admin UI exposes these as labelled form fields, so you usually don't hand-edit JSON — but knowing the underlying shape helps when scripting plan creation.

Attach sending servers + email verification servers to the plan (via the Plan edit page → Sending Servers tab). A plan with no sending servers attached means the customer must BYO — set create_sending_server_enabled: true in that case so they can add their own SES/Mailgun creds.

Pricing-tier recipe (use as starting point)

Three-tier ladder that works for most B2B SaaS positionings:

Tier Price Subscribers Sends/mo Automations Custom domain Notes
Starter $19/mo 2,500 25,000 3 No Self-serve credit-card signup
Pro $79/mo 15,000 150,000 Unlimited Yes Most popular; the "anchor" tier
Agency $199/mo 50,000 500,000 Unlimited Yes + sub-accounts Sales-assisted; 14-day trial

Add an annual variant of each plan at ~17% discount (10 months for the price of 12) — this is the standard SaaS lever and meaningfully reduces churn.

For sizing the underlying server, see Server Requirements. 100 Pro-tier customers (≈15M sends/month aggregate) is the Medium tier; 500 is the Large tier.

Step 4 — Customer onboarding flow

What a new customer experiences end-to-end:

  1. Lands on your pricing page → clicks Get Started on a plan
  2. Hits your AcelleMail /register → fills email + password
  3. Confirms email via the link AcelleMail sends
  4. Logs in → lands on the welcome screen
  5. AcelleMail Cashier prompts for payment (unless the plan is free or has a trial) → Stripe Checkout
  6. On successful payment, the customer is auto-subscribed to the plan, and their tenant workspace is ready

You should customise the welcome flow:

  • Welcome email template (Admin → System Emails → Welcome) — set tone, link to your getting-started docs, embed a YouTube walkthrough
  • First-login flow — Acelle shows a generic empty dashboard. Consider linking from your welcome email directly to "Step 1: import your first list" rather than the dashboard
  • In-app tour — Acelle ships no in-app tour. For an upmarket SaaS, consider adding an Intercom / Userpilot overlay via your custom CSS/JS (see white-label guide)

Step 5 — Tenant isolation

Each AcelleMail customer is a separate tenant with their own:

  • Subscriber lists + segments
  • Campaigns + templates
  • Automation workflows
  • Sending quotas (enforced per-plan via the options JSON quotas)
  • Sending server attachments (per plan)
  • API key (each customer generates their own under their profile)
  • Optional: their own sending domain + tracking domain

What's NOT isolated (important to know):

  • The underlying database is single-database — all tenant data is in the same acelle MySQL database, scoped by customer_id on each row. This is fine for legitimate SaaS, but means a database-level compromise affects all tenants.
  • Storage paths are also shared (storage/uploads/<customer_id>/...) — same shared-file-system caveat.
  • Sending IPs are shared by default — unless a customer is on a plan that requires "Own sending server" + has their own SES configured, they share your shared IP pool. One bad-actor customer can degrade reputation for everyone — see the FAQ for mitigations.

Step 6 — Day-2 operations

After the system is live, the recurring operational tasks:

  • Monitor signups for spam. Spammers will sign up to use your free trial / shared sending pool. Watch the email_log table for unusual sending patterns; consider requiring credit-card-on-file even for free trials.
  • Reconcile Stripe ↔ AcelleMail subscriptions. The webhooks are reliable but not perfect — once a week, spot-check that Stripe's active subscription count matches AcelleMail's. Acelle ships a reconcile job (see acelle:remote-subscriptions:reconcile in php artisan list).
  • Sending quota enforcement. AcelleMail enforces max_emails_per_month at send time — a customer hitting their cap will see "You have exceeded your sending quota". They have to either wait until the next cycle or upgrade their plan.
  • Customer support tickets. Most common: bounce-handling failures (the customer's SES setup), authentication issues, "where do I add SPF/DKIM" (link them to your guide). Stand up a help-desk early.
  • Plan migrations. When you change a plan's price or limits, existing subscribers stay on their old terms unless you explicitly migrate them. Acelle's admin UI has a Plan → Migrate flow.

Step 7 — White-labelling

If you're running this under your own brand (not "Powered by AcelleMail"), follow the white-label customization guide. The Extended License explicitly allows full re-branding.

Common issues

What you see Likely cause Fix
Stripe charge succeeds but customer plan not updated in AcelleMail Webhook not configured or signing secret wrong Re-create the webhook in Stripe Dashboard; paste the whsec_... into AcelleMail's Stripe gateway settings
Customer can't add their own SES — "feature not enabled" Plan's options.create_sending_server_enabled is false Edit the plan; toggle Allow custom sending server: Yes
Customer sends are counted against an old plan after upgrade Plan migration wasn't propagated Run php artisan acelle:remote-subscriptions:reconcile or use Admin → Subscriptions → Sync
Customers complain about deliverability after a spam-blast tenant Shared-IP pool reputation hit Move legitimate customers to a dedicated sending server; suspend the bad-actor tenant
Trial customers never convert No automation, no email reminders Set up a 3-step trial-conversion automation targeting customers near trial expiry
Free-tier customers send to spam-trap lists No list-hygiene policy enforced Require double opt-in on free tier (require_confirmation_on_signup per plan), or run a list-hygiene check at signup

FAQ

Why is PayPal not a first-class option anymore? Acelle Cashier (the subscription billing layer) consolidated on Stripe + Offline in the recent refactor. PayPal recurring subscriptions are operationally messier (no webhook for failed payments, no first-class trial support, currency conversion overhead) and the maintenance burden didn't justify keeping it. If you need PayPal, run it as "Offline" mode with manual reconciliation, or set up a separate Stripe → PayPal Express Checkout integration.

Can I migrate from PayPal to Stripe? Yes — existing PayPal subscriptions stay until they next renew, at which point they'll need to re-enter payment via Stripe. Communicate this to customers in advance via a 30-day notice email.

Can I run as a hybrid (some self-hosted, some SaaS)? Yes. Disable user signup (Step 1) for the self-hosted tier; only your team creates those customer accounts. Use plans for both, just hide non-public plans (visible: false).

How do I bill customers in multiple currencies? Each plan has a single currency. Create parallel plans in each currency (e.g. "Pro $79" and "Pro €72"). Customer's billing currency is locked to the plan they signed up on; you can't auto-convert.

Can a customer have multiple subscriptions? Yes — Acelle's data model supports it (a customer can have multiple active subscriptions to different plans, e.g. a "Sending credit" add-on alongside a base plan). The UI for end-customers shows them their primary plan plus any add-on subscriptions.

Can I require credit card on file for free trials? Set require_payment_method_on_trial: true in the plan options. Customers hit Stripe's payment method capture flow before the trial starts. Significantly reduces signup fraud.

Does AcelleMail send abandoned-checkout emails? No — that's outside Acelle's scope (it's not an e-commerce platform). Integrate via your own marketing-automation tool (HubSpot, Customer.io, ActiveCampaign), or write a custom Laravel listener on the SubscriptionAbandoned event.

What happens when a subscription is cancelled? Per Stripe's webhook, AcelleMail flips the subscription status to cancelled and downgrades the customer to the default free plan (or suspends if there's no free plan). Existing campaigns / lists / templates are retained; only sending is blocked.

GDPR / right-to-erasure? When a customer requests deletion, Admin → Customers → ... → Delete removes the customer record and all their related data (lists, subscribers, campaigns, automations). Sending logs may be retained per your retention policy — document this in your privacy policy.

Related articles

12 コメント

コメント 4 件

  1. v.petrova.ru
    how do you handle per-tenant dns? each customer's domain needs spf/dkim — do you walk them through it manually or have an automated dns-checker?
    1. admin
      right — for RDS specifically, you can change wait_timeout via the parameter group without a reboot if it's set as 'dynamic'. Most defaults are.
    2. admin (編集済み)
      Good question — and one that comes up often enough we should add an FAQ section. Short answer: yes for the common case; the exception is when you're running custom plugins that override the default behavior.
    3. admin (編集済み)
      For your specific case, I'd recommend testing with `--dry-run` first. The behavior under high load isn't 100% deterministic and we want you to see yur own pattern before committing...
    4. admin (編集済み)
      Short answer: yes — set the MySQL session variable from your worker's .env on boot and you'll get the longer timeout per connection. We'll add an explicit recipe in the next refresh.
    5. admin (編集済み)
      Suppression list import via CSV captures all opt-outs including preference-center ones if you exported with the right field set. The export filter defaults exclude some — check the 'include unsubscribed' checkbox on Mailchimp's export wizard
  2. linhvu.dev
    tip for multi-tenant operators: enforce per-tenant rate limits at the queue layer, not just the api layer. one tenant's bad import shouldn't slow the others...
    1. admin (編集済み)
      Good tip. The Cloudflare-outbound-rate-limit case is something we hadn't documented.
  3. aisha.khan.pak
    The white-label setup walkthrough is exactly what we needed. Most SaaS-on-Acelle guides assume youve already figured this out.
    1. admin (編集済み)
      Thanks. Pass it along if it helps your team.
    2. admin (編集済み)
      Thanks for the kind words. We try to keep these source-grounded so they age well
  4. cw.dev.sh
    running ~140 customer tenants on a single AcelleMail install. Resource usage scales reasonably with subscriber count, not tenant count, which is nice

More in SaaS & Multi-tenant