MailPoet Documentation

repository·trunk·Indexed 41 days ago

https://github.com/mailpoet/mailpoet

A WordPress plugin for email marketing. Includes documentation for the MailPoet API v1 for managing subscribers, lists, and tags, as well as developer guides for local installation, pnpm workflow commands, and the experimental MailPoet Email Editor. Also features an experimental MCP server (v0.0.2) for AI agents to interact with local development environments.

Tokens
60.9K
Snippets
161
Records
247
Agent score
51%

What's inside MailPoet

  1. Overview of MailPoet Dev MCP Server

    trunk

    The MailPoet Dev MCP Server is an experimental Model Context Protocol (MCP) server that exposes MailPoet local development tooling to AI agents like Claude Code or Claude Desktop.

    It works through a hybrid architecture:

    • TS MCP server: Runs on the host machine, handling MCP protocol communication, shell commands (pnpm/Robo/Docker), and Mailpit calls.
    • PHP companion mu-plugin: A dev-only plugin located at .wp-env/mu-plugins/mailpoet-dev-companion.php that provides REST endpoints under /wp-json/mailpoet-dev/v1/. These endpoints are backed by MailPoet's Dependency Injection (DI) container and Doctrine repositories.
    • Shared Secret: A secret stored at .wp-env/.mailpoet-dev-companion-secret is used to authenticate requests between the TS server and the PHP companion plugin.
  2. Overview of MailPoet features

    trunk

    MailPoet is an email marketing solution for WordPress that allows you to create, send, and manage email campaigns directly from your dashboard.

    Core Capabilities:

    • Newsletter Management: Build responsive newsletters using a drag-and-drop builder and manage subscriber lists.
    • Subscription Forms: Add signup forms to your site via shortcodes, popups, fixed bars, slide-ins, or exit intent.
    • Automations: Set up automatic blog post notifications, welcome emails, and advanced behavior-based automations (requires paid plan).
    • WooCommerce Integration: Automate emails for abandoned carts, first purchases, specific products/categories, and order status changes. You can also customize WooCommerce transactional emails.
    • Segmentation: Segment subscribers based on behavior, interests, or WooCommerce data (country, order count, spend, etc.).
    • Email Delivery: Use the MailPoet Sending Service for high-speed, reliable delivery (up to 50,000 emails per hour) with built-in SPF/DKIM authentication.
  3. Integrate with the MailPoet API

    trunk

    The MailPoet API is the officially supported way to integrate with the MailPoet plugin, specifically for managing subscribers. It is implemented as a PHP class within the MailPoet3 plugin.

    Important Note: Developers are strongly discouraged from using other functions or classes within the MailPoet codebase, as internal refactoring occurs frequently and backward compatibility is not guaranteed for non-API components.

  4. How Mailpit routing works in the dev environment

    trunk

    Mailpit integration is automated through several components:

    1. Container: pnpm env:start triggers scripts/launch-smtp-catcher.sh, which starts an axllent/mailpit container named mailpoet-smtp (Web UI: 8082, SMTP: 1026).
    2. WordPress Hook: A mounted mu-plugin (.wp-env/mu-plugins/mailpoet-dev-smtp.php) hooks phpmailer_init to route wp_mail() through host.docker.internal:1026.
    3. MailPoet Configuration: A script (.wp-env/scripts/configure-mailpoet-dev-smtp.php) runs via lifecycleScripts.afterStart to configure MailPoet's internal mailer to use SMTP at host.docker.internal:1026 with no authentication (mta.authentication=-1).
    4. Persistence: Captured emails are stored in a named Docker volume mailpoet-smtp-data so they persist across container restarts.
  5. Format tags in the `$subscriber` array

    trunk

    When providing a tags array in the $subscriber data, each item can take several formats:

    • String: The tag name (e.g., 'VIP'). If the tag doesn't exist, MailPoet creates it.
    • Integer/Numeric String: The ID of an existing tag (e.g., 5 or '5').
    • Associative array with name: Equivalent to a string name (e.g., ['name' => 'VIP']).
    • Associative array with id: References an existing tag ID. If the ID does not exist, an exception with code 29 is thrown (e.g., ['id' => 5]).

    Adding a tag triggers the mailpoet_subscriber_tag_added WordPress action.

  6. How the MailPoet Dev MCP Server architecture works

    trunk

    The server facilitates communication between an AI agent and the local MailPoet development environment using three main components:

    1. AI Agent (Claude/etc.): Communicates via stdio with the TypeScript MCP server.
    2. TS MCP Server: Executes shell commands and HTTP calls to interact with the environment.
    3. Local Environment:
      • WordPress: Runs on :8888 with the mailpoet-dev-companion mu-plugin.
      • Mailpit: Runs on :8082 for capturing outgoing mail.
      • MailPoet Core: Used for tests and QA.

    Authentication is handled by a shared secret file (.wp-env/.mailpoet-dev-companion-secret) which is mapped into the WordPress container via .wp-env.json. The mu-plugin reads this secret to authorize requests sent to the /wp-json/mailpoet-dev/v1/ endpoints.

    ┌────────────────────┐     stdio     ┌─────────────────────┐
    │   Claude / agent   │ ───────────▶  │  TS MCP server      │
    └────────────────────┘               │  (this package)     │
                                         └──────┬──────────────┘
                                                │ shell-out (pnpm/Robo, docker)
                                                │ HTTP (Mailpit, companion)
                                  ┌─────────────┼─────────────────────┐
                                  ▼             ▼                     ▼
                             mailpoet/       Mailpit :8082      WordPress :8888
                             (tests, QA)     (captured mail)    + companion mu-plugin
                                                                (DI → repositories)
  7. Understand CLI execution and the `cli` status

    trunk

    When using wp mailpoet cron run, run-daemon, or add --run, tasks are executed directly inside the WP-CLI process instead of the site's web-request daemon.

    Key behaviors:

    • Execution Limits: The standard 20-second web-triggered execution limit is lifted. Tasks run to completion in a single invocation. You can restore a cap using the --timeout flag.
    • Task Claiming: When a task is run via CLI, it is assigned a dedicated cli status. This status makes the task invisible to the site daemon, preventing the web worker from picking up or rescheduling the same task simultaneously.
    • Task Outcomes:
      • Completed: Status becomes completed. A meta.cli breadcrumb containing the pid and started_at time is recorded.
      • Partial/Error: If the worker throws an error or fails to finish, the task is handed back to the site cron with a scheduled status (due now) so the daemon can retry it.
      • Requirements not met: The task is removed.

    This mechanism ensures that long-running CLI tasks are not 'rescued' or stolen by the daemon's stuck-task recovery logic.

  8. Understand MailPoet Email Editor compatibility and limitations

    trunk

    When using the new MailPoet Email Editor, be aware of the following constraints:

    • Email Migration: Emails created with the old editor are not compatible with the new editor. There is currently no migration tool available to convert old emails.
    • WordPress Version: The new editor is designed to support only the latest WordPress version.
    • Gutenberg Compatibility: While you can install the latest Gutenberg plugin, doing so carries a high risk of the email editor not working properly.
  9. MCP Tool Development Conventions

    trunk

    When developing tools for the MailPoet MCP server, follow these standards:

    • Naming Pattern: mp.<domain>.<resource>.<verb> (e.g., mp.data.newsletters.list).
    • Domains: env, test, mail, data, qa, logs.
    • Permissions: Tools are read-only by default. For writes, set readOnlyHint: false. For destructive writes, set destructiveHint: true to trigger a confirmation gate.
    • Data Formats: Use JSON-in and JSON-out. Avoid passing raw text blobs if a structured JSON object is possible.
    • List vs Get:
      • list methods should return a compact core set of data plus essential relations, including an items[] array and a total count.
      • get methods should return full, detailed object information.
    • Timestamps: Always use ISO 8601 UTC strings.
  10. How MailPoet runs background work

    trunk

    MailPoet manages background tasks by storing them as rows in the scheduled_tasks table. Each task has a type (the specific worker responsible for the task) and a status.

    Background work is executed by a daemon that is periodically triggered by a site-side runner (typically Action Scheduler or WordPress cron). The daemon identifies due tasks and processes them using the appropriate worker type.

    Task Statuses

    StatusMeaning
    scheduledWaiting to run at or after its scheduled time.
    runningBeing processed by the site daemon. (Stored as NULL in the database).
    cliClaimed and being processed by a wp mailpoet cron process. This task is invisible to the site daemon.
    completedFinished.
    pausedOn hold.
    cancelledCancelled.
    invalidCould not be processed.
  11. Process a subscription form submission

    trunk

    When processing a subscription form submission (e.g., from $_POST data), follow this workflow to handle both new and existing subscribers:

    1. Instantiate the API: Use \MailPoet\API\API::MP('v1').
    2. Map Data: Map your input data (like $_POST) to the subscriber field IDs retrieved via getSubscriberFields().
    3. Check Existence: Attempt to retrieve the subscriber using getSubscriber($email).
    4. Handle Logic:
      • If the subscriber does not exist (the call throws an exception or returns null), use addSubscriber($subscriber, $list_ids) to create them.
      • If the subscriber already exists, use subscribeToLists($email, $list_ids) to add them to the requested lists.
    5. Error Handling: Wrap the process in a try/catch block to capture exceptions thrown by the API.
    <?php
    
    add_action('init', function () {
      if (!class_exists(\MailPoet\API\API::class)) {
        return;
      }
    
      try {
        // Get MailPoet API instance
        $mailpoet_api = \MailPoet\API\API::MP('v1');
    
        // Fill subscribed data from $_POST
        $subscriber = [];
        $subscriber_form_fields = $mailpoet_api->getSubscriberFields();
        foreach ($subscriber_form_fields as $field) {
          if (!isset($_POST[$field['id']])) {
            continue;
          }
          $subscriber[$field['id']] = $_POST[$field['id']];
        }
        $list_ids = $_POST['list_ids'];
    
        // Check if subscriber exists. If subscriber doesn't exist an exception is thrown
        try {
          $get_subscriber = $mailpoet_api->getSubscriber($subscriber['email']);
        } catch (\Exception $e) {
          $get_subscriber = false;
        }
    
        if (!$get_subscriber) {
          // Subscriber doesn't exist let's create one
          $mailpoet_api->addSubscriber($subscriber, $list_ids);
        } else {
          // In case subscriber exists just add him to new lists
          $mailpoet_api->subscribeToLists($subscriber['email'], $list_ids);
        }
      } catch (\Exception $e) {
        $error_message = $e->getMessage();
      }
    });
  12. Required tools for MailPoet development

    trunk

    To develop with MailPoet, ensure the following tools are installed:

    • Docker Desktop: Required for wp-env and the tests_env/ environment.
    • PHP: Must match the requirements in composer.json.
    • Node.js: As specified by .nvmrc (use nvm, fnm, or volta).
    • pnpm: As specified in package.json (enable via corepack enable).
    • GitHub CLI (gh): Required to download private WooCommerce test plugins. Run gh auth login to authenticate.
    • @wordpress/env: Must be version ≥ 11.4. Do not downgrade to 10.x as it lacks required flags like testsEnvironment and wp-env destroy --force.