Integrating AcelleMail with WordPress and WooCommerce

WordPress + WooCommerce is the most common stack people connect to AcelleMail. There's no first-class plugin — instead, you wire up the REST API from a few WordPress hooks. This guide walks the three integration patterns (user registration, WooCommerce purchase, optional unsubscribe sync) end-to-end, plus the choice of "API from theme/plugin" vs "embedded signup form" vs "third-party Zapier".

What this is for

WordPress + WooCommerce is the most common stack people connect to AcelleMail. There's no first-class AcelleMail plugin in the WordPress.org plugin directory — instead, you wire up the REST API from a few WordPress hooks. The good news: it's ~30 lines of PHP and works reliably.

This guide is the master overview with all three integration patterns explained. Each pattern has a dedicated deep-dive article linked at the end.

Three integration patterns

Pattern Use when Code goes in
User-register sync You want every WordPress account creation to add a subscriber functions.php or a custom mu-plugin, hooking user_register
WooCommerce purchase sync You want every paying customer added to a list (and possibly trigger automations) Same place, hooking woocommerce_order_status_completed
Embedded signup form You want a marketing-list signup form on a WP page (no PHP) Paste AcelleMail's form embed code into a Custom HTML block
Zapier You want point-and-click without writing PHP See Zapier Integration Guide

Pick the one that matches your use case. Most stores end up using #2 + #3 together — purchase sync for paying customers + an embedded form for newsletter signups.

Prerequisites (all patterns)

  • A working AcelleMail install
  • An AcelleMail API token — see Getting Started with the REST API Step 1
  • An AcelleMail list (UID copied from Lists → Overview → click the list → URL contains the UID)
  • WordPress 5.5+ (for the modern wp_remote_post reliability)

Field name gotcha: AcelleMail's subscriber API uses UPPERCASE field names (EMAIL, FIRST_NAME, LAST_NAME, MAIL_LIST_UID) — not lowercase. Older guides get this wrong. The API accepts uppercase only.

Pattern 1 — Sync on WordPress user registration

Detail in WordPress Subscriber Sync. Summary:

add_action('user_register', function ($user_id) {
    $user = get_user_by('id', $user_id);

    wp_remote_post('https://mail.example.com/api/v1/subscribers', [
        'headers' => [
            'Authorization' => 'Bearer ' . ACELLEMAIL_API_TOKEN,
            'Accept'        => 'application/json',
            'Content-Type'  => 'application/json',
        ],
        'body' => wp_json_encode([
            'MAIL_LIST_UID' => 'YOUR_LIST_UID',
            'EMAIL'         => $user->user_email,
            'FIRST_NAME'    => $user->first_name ?: '',
            'LAST_NAME'     => $user->last_name ?: '',
        ]),
        'timeout' => 10,
        'blocking' => false,   // fire and forget
    ]);
});

Define ACELLEMAIL_API_TOKEN in wp-config.php (not in the theme — credentials must not be in version control).

Pattern 2 — Sync on WooCommerce purchase

Detail in WooCommerce Post-Purchase Emails. Summary:

add_action('woocommerce_order_status_completed', function ($order_id) {
    $order = wc_get_order($order_id);
    if (!$order) return;

    $tags = [];
    foreach ($order->get_items() as $item) {
        $cats = get_the_terms($item->get_product_id(), 'product_cat');
        if ($cats) foreach ($cats as $c) $tags[] = $c->slug;
    }

    wp_remote_post('https://mail.example.com/api/v1/subscribers', [
        'headers' => [
            'Authorization' => 'Bearer ' . ACELLEMAIL_API_TOKEN,
            'Accept'        => 'application/json',
            'Content-Type'  => 'application/json',
        ],
        'body' => wp_json_encode([
            'MAIL_LIST_UID' => 'POST_PURCHASE_LIST_UID',
            'EMAIL'         => $order->get_billing_email(),
            'FIRST_NAME'    => $order->get_billing_first_name(),
            'LAST_NAME'     => $order->get_billing_last_name(),
            'ORDER_TOTAL'   => $order->get_total(),
            'ORDER_ID'      => $order_id,
            'tags'          => implode(',', array_unique($tags)),
        ]),
        'timeout' => 10,
        'blocking' => false,
    ]);
});

This subscribes the customer to the "post-purchase" list with the order context. Configure an automation in AcelleMail to send a welcome → review-request → upsell sequence — see the post-purchase emails article for the 4-step sequence template.

Pattern 3 — Embedded signup form

Inside AcelleMail:

  1. Lists → Your list → Forms → Create new
  2. Customize fields shown (email + first name is typical for newsletters)
  3. Copy embed code — gives you either an <iframe> snippet or a JS-rendered snippet

In WordPress:

  1. Edit the page where you want the form (homepage, footer widget, etc.)
  2. Insert a Custom HTML block
  3. Paste the embed code
  4. Save → the form renders inline

Trade-offs: the embedded form posts directly to AcelleMail — no WordPress code needed, no API token needed. Downside: limited styling (iframe constraints) and no opportunity to add extra logic on submit. For a simple newsletter signup, it's perfect.

For more customisation, build your own HTML form that POSTs to /api/v1/public/subscribers (the unauthenticated endpoint — see REST API reference).

Optional — sync unsubscribes back to WordPress

When a subscriber unsubscribes from your AcelleMail list, you may want to update WordPress user metadata (e.g. clear a newsletter_subscribed flag, set a "do not contact" tag).

Two approaches:

A. Per-campaign webhook on campaign.unsubscribe

Configure an AcelleMail webhook (Campaign → Webhooks) on the campaign.unsubscribe event, pointing to a custom REST endpoint in WordPress:

// In a plugin file
add_action('rest_api_init', function () {
    register_rest_route('acellemail/v1', '/unsubscribe', [
        'methods'  => 'POST',
        'callback' => function (\WP_REST_Request $req) {
            // Verify auth (Bearer token or shared header — match what you set in AcelleMail webhook config)
            $auth = $req->get_header('authorization');
            if ($auth !== 'Bearer ' . WEBHOOK_SHARED_SECRET) {
                return new \WP_REST_Response('Unauthorized', 401);
            }

            $email = $req->get_param('subscriber_email');
            $user = get_user_by('email', $email);
            if ($user) {
                update_user_meta($user->ID, 'newsletter_subscribed', '0');
            }

            return new \WP_REST_Response(['ok' => true], 200);
        },
        'permission_callback' => '__return_true',
    ]);
});

The endpoint is at https://your-wp-site.com/wp-json/acellemail/v1/unsubscribe. Configure that URL in the AcelleMail webhook config (per webhook reference).

B. Periodic sync via REST API

Run a daily cron in WordPress that fetches the list's current unsubscribed subscribers via the API and updates user meta:

add_action('acellemail_daily_sync', function () {
    $response = wp_remote_get(
        'https://mail.example.com/api/v1/subscribers?filter[list_uid]=YOUR_LIST_UID&filter[status]=unsubscribed&per_page=200',
        ['headers' => ['Authorization' => 'Bearer ' . ACELLEMAIL_API_TOKEN, 'Accept' => 'application/json']]
    );
    $data = json_decode(wp_remote_retrieve_body($response), true);
    foreach (($data['data'] ?? []) as $sub) {
        $user = get_user_by('email', $sub['email']);
        if ($user) update_user_meta($user->ID, 'newsletter_subscribed', '0');
    }
});

if (!wp_next_scheduled('acellemail_daily_sync')) {
    wp_schedule_event(time(), 'daily', 'acellemail_daily_sync');
}

Pattern A is real-time but more setup; Pattern B is simpler but lags up to 24 hours. Pick based on your urgency.

Where to put the code

Don't paste into functions.php unless you're 100% comfortable losing it on a theme switch. Better:

# Create a custom mu-plugin (must-use; auto-loaded; survives any theme change)
sudo mkdir -p /path/to/wp-content/mu-plugins
sudo tee /path/to/wp-content/mu-plugins/acellemail-integration.php <<'EOF'
<?php
/**
 * Plugin Name: AcelleMail Integration
 * Description: Sync WordPress + WooCommerce users to AcelleMail
 */

if (!defined('ACELLEMAIL_API_TOKEN') || !defined('ACELLEMAIL_LIST_UID')) {
    return; // Skip if not configured (defined in wp-config.php)
}

// ... your add_action hooks here ...
EOF

Then in wp-config.php:

define('ACELLEMAIL_API_TOKEN', 'your-token-here');
define('ACELLEMAIL_LIST_UID', 'your-list-uid');

Testing the integration

# Tail Acelle's app log + WP debug log simultaneously
sudo tail -f /var/www/acellemail/storage/logs/laravel.log \
              /path/to/wp-content/debug.log

# Then in WordPress, register a test user OR place a test WC order
# You should see in Acelle's log: a subscribers.create API hit
# And in WP debug.log (if WP_DEBUG_LOG is on): the wp_remote_post response

If nothing happens:

  • Verify WP_DEBUG_LOG = true is set in wp-config.php to see PHP errors
  • Verify the API token works via curl (see getting-started)
  • Verify the MAIL_LIST_UID is the list UID, not the campaign UID (common confusion)

Common issues

What you see Likely cause Fix
Subscriber appears but field values are empty Used lowercase field names (email, not EMAIL) Switch to uppercase per the API spec
API returns 422 "MAIL_LIST_UID required" Posted to /lists/{uid}/subscribers without the field, or posted to /subscribers without the field Use /api/v1/subscribers + include MAIL_LIST_UID in the body
WordPress page loads slowly after adding the hook Default wp_remote_post is blocking — adds 200-500ms per registration Add 'blocking' => false to make it fire-and-forget
Duplicate subscribers (1 in WP user, 1 in WooCommerce) Both user_register AND woocommerce_order_status_completed fired for the same email Acelle handles duplicates (upserts by email) — verify; or scope each hook to a different list
Embedded form doesn't render iframe blocked by Content-Security-Policy Add the AcelleMail domain to your CSP frame-src; or use the JS-rendered embed instead
wp_remote_post returns WP_Error "cURL error 60" TLS verification failed (self-signed cert on dev install) Use ['sslverify' => false] for testing only; fix the cert before production
Unsubscribe webhook arrives but WP user not updated get_user_by('email') returned null — email mismatch Lowercase both sides before lookup (strtolower($email))

FAQ

Is there an official AcelleMail WordPress plugin? Not on WordPress.org as of 2026. The hooks-based integration is the recommended pattern — small, transparent, no plugin-dependency risk.

Can I import existing WordPress users into AcelleMail? Yes — export the WP user list as CSV (via a plugin like User Import Export) and import into AcelleMail via Lists → Your list → Subscribers → Import. See Importing Contacts.

WooCommerce Subscriptions / Memberships? Same approach — hook the relevant event (woocommerce_subscription_status_active, wc_memberships_user_membership_status_changed) and POST to the AcelleMail API. Read the WC documentation for the exact hook names.

Can AcelleMail send transactional emails for WooCommerce (order confirmation, password reset)? Yes via the Amazon SES integration — both WooCommerce and AcelleMail can send through the same SES sending identity. Don't try to route WooCommerce transactional through AcelleMail's send-a-campaign API (wrong shape).

Performance impact on WP site? With 'blocking' => false added, < 1ms overhead per user-register / order completion. Without it, 200-500ms — measurable on busy sites.

Multi-site WordPress (WPMU)? Same hooks work network-wide. Use is_multisite() ? $blog_id : null to differentiate, or scope per-blog to different AcelleMail lists.

Can I push back to WordPress from AcelleMail? Yes via webhooks — see the "Optional — sync unsubscribes back" section above.

Related articles

10 comentarios

5 comentarios

  1. aditi.s.bom
    If your webhook recever is unreliable, point AcelleMail at an intermediate proxy with retry logic (Cloudflare Worker works well).
    1. admin
      good tip. the cloudflare-outbound-rate-limit case is something we hadn't documented.
  2. jmorrison.itop…
    Set up the Zapier bridge last week. ~30 minutes from start to working flow. Cleaner than I expected.
    1. admin
      Thanks for the detail — adding the kernel-reboot edge case to the article on the next update
  3. priya.iyer.ops
    Any plans for a native Shopify app? The webhook approach works but a real app integration would be smoother for non-technical users
    1. 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. admin (editado)
      Good catch. The bounds (200/32) are hardcoded in the runtime. We've discussed making them configurable; not a near-term priority but it's tracked.
  4. tnovak.cz
    if your webhook receiver i unreliable, point acellemail at an intermediate proxy with retry logic (cloudflare worker works well).
  5. phuong.mai.hn
    woocommerce integration finally documented properly. was reverse-engineering the webhook payload before this.
    1. admin (editado)
      Glad it landed. Drop suggestions in the comments and well incorporate them on the next refresh. lol

More in Integrations