NAV
php json

Introduction

Welcome to the Mautic Developer Documentation. This documentation will go over how to build a Mautic Plugin that extends the features of Mautic, how to build custom themes, and and how to integrate applications outside of Mautic using its REST API.

Submitting Code to Mautic

Development is open and available to any member of the Mautic community. All fixes and improvements are done through pull requests to the code on GitHub. This code is open source and publicly available.

Read more about the PR process on the Mautic Governance page.

The code should try to follow Symfony’s Coding Standards

Symfony

Mautic is built on Symfony, the popular PHP framework (Mautic 1.0.x uses Symfony 2.5; Mautic 2.0.x uses Symfony 2.8). This document will go over the basics but most of their documentation applies to Mautic as well which can be used to obtain more advanced Symfony functionality.

There are some structural differences between Mautic and standard Symfony. Below is a list of where you will find some of standard Symfony locations in Mautic:

Symfony Mautic
src/ app/bundles (Mautic core) or plugins/ (Mautic plugins)
web/ /
AcmeBundle/Resources/config AcmeBundle/Config
AcmeBundle/Resources/views AcmeBundle/Views
AcmeBundle/Resources/public AcmeBundle/Assets
AcmeBundle/Resources/translations/domain.en_US.ini AcmeBundle/Translations/en_US/domain.ini

Most of Symfony’s standard locations, such as the Resources/views and Resources/translations directories, should still function with Mautic. However, it may be required to handle service registration, etc with native Symfony processes if not using the Mautic methods defined in this document.

Development Environment

Setup

It is assumed that the system already has composer and git installed and configured.

To setup the developer environment, simply fork and clone the source from GitHub. Then Run composer install on the source.

Open your browser and complete the installation through the Mautic installer.

Environments

There are three environments in Mautic: prod, dev, and test.

prod is used when accessing the site through index.php.

dev is used when accessing the site through index_dev.php. Using Mautic in the dev environment will activate Symfony’s profiler toolbar, has more strict error handling, will display information about exceptions, and will not cache as much (see below). Note that steps should be taken to ensure index_dev.php is not accessible to the public as it could potentially reveal sensitive information. It is restricted to localhost by default. However, there are two ways to allow access to index_dev.php from a non-localhost. The first option is to set a header from the web-server with the IP addresses assigned to MAUTIC_DEV_HOSTS. The second and easier option is to add an array to your installation’s app/config/local.php file as 'dev_hosts' = ['123.123.123.123', '124.124.124.124'], then clear the cache.

test is used mainly for PHP Unit Tests.

Cache

Symfony makes heavy use of a filesystem cache. Frequently clearing this cache will be required when developing for Mautic. By default, the cache is located in app/cache/ENV where ENV is the environment currently accessed (i.e. dev or prod). To rebuild the cache, the ENV can just be deleted or run the Symfony command php app/console cache:clear --env=ENV If a specific environment is not passed to the command via --env=ENV, the dev environment will be used by default.

In the dev environment, translations, views, and assets are not cached. However, changes to these files will require the cache to be cleared for them to take effect in the prod environment. Changes to Mautic config files, Symfony config files, etc will require the cache to be cleared regardless of environment.

Plugins

Plugins are bundles that can extend the functionality of Mautic. They can be very simple or very complex and have access to leverage pretty much all that Symfony offers. Just as as reminder, the basics are covered in this document. If more advanced processes are required, the Symfony book and cookbook will be valuable resources.

Migrations/Deprecations

The following is a list of deprecations and migrations required to support latest versions of Mautic.____

1.2.0

1.2.0 deprecated the Mautic Addon with it’s successor, the Plugin. Mautic addons will continue to work but should be migrated to a plugin before Mautic 2.0 when support will be dropped.

The migration path is as follows:

  1. Move the plugin from addons/ to plugins/
  2. Replace the namespace MauticAddon with MauticPlugin
  3. Replace the MauticFactory getModel() notation of addon. to plugin.
  4. Replace the permission notation of addon: to plugin:
  5. Change the plugin’s bundle class to extend PluginBundleBase instead of AddonBundleBase
  6. In the plugin’s bundle class, use the function’s onPluginInstall() and onPluginUpdate() instead of the deprecated onInstall() and onUpdate()
  7. Migrate Entity use of annotations for ORM to the static PHP function loadMetadata()
  8. Migrate Entity use of annotations for the serializer (used with the REST API) to the static PHP function loadApiMetadata()

2.0.0

The big ticket item with 2.0.0 is the deprecation of MauticFactory which will be phased out during the 2.x release cycles and to be removed in 3.0. Where possible, please use direct dependency injection of services rather than relying on MauticFactory.

  1. MauticFactory deprecated - use dependency injection of required services. Many MauticFactory helper functions have been replaced with services.
  2. Models need to be registered as services using a specific nomenclature. See Models for more information.
  3. The static callback function for campaign actions and decisions has been deprecated. Please see Extending Campaigns for more information on the new event based method. (Form submit actions, point triggers, and the like will follow suit with a similar migration throughout the lifecycle of 2.0 but for now still uses the static callback method).
  4. Minimum PHP version has been increased to 5.6.19 and thus newer PHP code goodies are available for developers (traits, etc)
  5. Themes have been completely revamped although the old format is still supported for now. Please see Themes for the new format.
  6. Some routes for /leads/* were removed in favor of /contacts/*. I.e. /api/leads is now /api/contacts, /s/leads is now /s/contacts, and so forth. The route names were also updated. For example, mautic_lead_action is now mautic_contact_action and so forth.

Plugin Directory Structure

<?php
// plugins/HelloWorldBundle/HelloWorldBundle.php

namespace MauticPlugin\HelloWorldBundle;

use Mautic\PluginBundle\Bundle\PluginBundleBase;

class HelloWorldBundle extends PluginBundleBase
{
    // Nothing more required
}

The directory structure of an plugin will vary based on the features implemented.

At a minimum, the following structure is required:

HelloWorldBundle/
- - - Config/
- - - - - config.php
- - - HelloWorldBundle.php

Read more about the config file.

The HelloWorldBundle.php file registers the bundle with Symfony. See the code block for the minimum required code.

A typical MVC plugin may look something like:

HelloWorldBundle/
- - - Assets/
- - - - - - images/
- - - - - - - - - earth.png
- - - - - - - - - mars.png
- - - - - - helloworld.js
- - - - - - helloworld.css
- - - Config/
- - - - - - config.php
- - - Controller/
- - - - - - DefaultController.php
- - - Model/
- - - - - - ContactModel.php
- - - - - - WorldModel.php
- - - Translations//
- - - - - - en_US/
- - - - - - - - - flashes.ini
- - - - - - - - - messages.ini
- - - Views/
- - - - - - Contact/
- - - - - - - - - form.html.php
- - - - - - World/
- - - - - - - - - index.html.php
- - - - - - - - - list.html.php
- - - HelloWorldBundle.php
- - - HelloWorldEvents.php

Each of the other directories and files are explained elsewhere in the document.

Install/Upgrade

<?php
// plugins/HelloWorldBundle/HelloWorldBundle.php

namespace MauticPlugin\HelloWorldBundle;

use Doctrine\DBAL\Schema\Schema;
use Mautic\PluginBundle\Bundle\PluginBundleBase;
use Mautic\PluginBundle\Entity\Plugin;
use Mautic\CoreBundle\Factory\MauticFactory;

class HelloWorldBundle extends PluginBundleBase
{

    /**
     * Called by PluginController::reloadAction when adding a new plugin that's not already installed
     *
     * @param Plugin        $plugin
     * @param MauticFactory $factory
     * @param null          $metadata
     */

    static public function onPluginInstall(Plugin $plugin, MauticFactory $factory, $metadata = null)
    {
        if ($metadata !== null) {
            self::installPluginSchema($metadata, $factory);
        }

        // Do other install stuff
    }

    /**
    * Called by PluginController::reloadAction when the plugin version does not match what's installed
    *
    * @param Plugin        $plugin
    * @param MauticFactory $factory
    * @param null          $metadata
    * @param Schema        $installedSchema
    *
    * @throws \Exception
    */
    static public function onPluginUpdate(Plugin $plugin, MauticFactory $factory, $metadata = null, Schema $installedSchema = null)
    {
        $db             = $factory->getDatabase();
        $platform       = $db->getDatabasePlatform()->getName();
        $queries        = array();
        $fromVersion    = $plugin->getVersion();

        // Simple example
        switch ($fromVersion) {
            case '1.0':
                switch ($platform) {
                    case 'mysql':
                        $queries[] = 'ALTER TABLE ' . MAUTIC_TABLE_PREFIX . 'worlds CHANGE CHANGE description description LONGTEXT DEFAULT NULL';
                        break;

                    case 'postgresql':
                        $queries[] = 'ALTER TABLE ' . MAUTIC_TABLE_PREFIX . 'worlds ALTER description ALTER TYPE TEXT';
                        break;
                }

                break;
        }

        if (!empty($queries)) {

            $db->beginTransaction();
            try {
                foreach ($queries as $q) {
                    $db->query($q);
                }

                $db->commit();
            } catch (\Exception $e) {
                $db->rollback();

                throw $e;
            }
        }
    }
}

Currently, plugins are installed by uploading the plugin to the plugins folder and the cache manually deleted (app/cache/prod). Then, the admin must click the Install/Upgrade Plugins button in the Plugin manager. When that happens, new plugins found will have the static method onPluginInstall() called from the plugin’s bundle file. If the plugin has already been installed, and the version has changed, then onPluginUpdate() is called.

By default, onPluginInstall() will execute installPluginSchema(). This function will generate and execute schema based on found Entity classes.

updatePluginSchema() is available for use with onPluginUpdate(), however it is not called by default. The reason is that it uses Doctrine to generate schema differences and executes the queries. This is not recommended due to the risk of Doctrine making incorrect assumptions that will lead to data loss. If updatePluginSchema() is relied upon, it is very important to test thoroughly to ensure the desired outcome. It is recommended to write a migration path for both MySQL and PostgreSQL with native queries instead.

onPluginInstall()

Executed when a plugin is first installed. By default, the plugin’s database schema will be generated and installed based on the Entity class’ loadMetadata function.

Argument Type Description
$plugin Mautic\PluginBundle\Entity\Plugin The plugin entity with information gleaned from the plugin’s config file.
$factory Mautic\CoreBundle\Factory\MauticFactory Mautic’s factory class to provide access to Mautic’s services.
$metadata array\ null

installPluginSchema()

Argument Type Description
$metadata array\ null
$factory Mautic\CoreBundle\Factory\MauticFactory Mautic’s factory class to provide access to Mautic’s services.

onPluginUpdate()

Argument Type Description
$plugin Mautic\PluginBundle\Entity\Plugin The plugin entity with information gleaned from the plugin’s config file.
$factory Mautic\CoreBundle\Factory\MauticFactory Mautic’s factory class to provide access to Mautic’s services.
$metadata array\ null
$installedSchema \Doctrine\DBAL\Schema\Schema Schema of currently installed tables

updatePluginSchema()

Argument Type Description
$metadata array\ null
$installedSchema \Doctrine\DBAL\Schema\Schema Schema of currently installed tables
$factory Mautic\CoreBundle\Factory\MauticFactory Mautic’s factory class to provide access to Mautic’s services.

Plugin Config File

Mautic leverages a simple array config file that will register routes, menu items, services and custom configuration parameters.

General

<?php
// plugins/HelloWorldBundle/Config/config.php

return array(
    'name'        => 'Hello World',
    'description' => 'This is an example config file for a simple Hellow World plugin.',
    'author'      => 'Marty Mautibot',
    'version'     => '1.0.0',

The general config options will define what should be listed in the Plugin manager.

The version should be “PHP-standardized” as the Plugin manager will use version_compare() to determine if the onUpdate callback should be executed.

Routes

If copying, do not copy <?php //continued as it is simply used to force sytax highlighting.

<?php // continued

    'routes'   => array(
        'main' => array(
            'plugin_helloworld_world' => array(
                'path'       => '/hello/{world}',
                'controller' => 'HelloWorldBundle:Default:world',
                'defaults'    => array(
                    'world' => 'earth'
                ),
                'requirements' => array(
                    'world' => 'earth|mars'
                )
            ),
            'plugin_helloworld_list'  => array(
                'path'       => '/hello/{page}',
                'controller' => 'HelloWorldBundle:Default:index'
             ),
            'plugin_helloworld_admin' => array(
                'path'       => '/hello/admin',
                'controller' => 'HelloWorldBundle:Default:admin'
            ),
        ),
        'public' => array(
            'plugin_helloworld_goodbye' => array(
                'path'       => '/hello/goodbye',
                'controller' => 'HelloWorldBundle:Default:goodbye'
            ),
            'plugin_helloworld_contact' => array(
                'path'       => '/hello/contact',
                'controller' => 'HelloWorldBundle:Default:contact'
            )
        ),
        'api' => array(
            'plugin_helloworld_api' => array(
                'path'       => '/hello',
                'controller' => 'HelloWorldBundle:Api:howdy',
                'method'     => 'GET'
            )
        )
    ),

Routes define the URL paths that will be used to execute the plugin’s controller actions. See Routing for specifics on how routes work.

Firewalls

There are three firewalls to define the routes behind.

Firewall Description
main Secure area of Mautic (/s/ will be auto prepended to the path). The user will be required to login to access this path.
public Public access without needing authentication. The URL will be appended directly to Mautic’s base URL.
api Secure API area of Mautic (/api/ will be auto prepended to the path). OAuth authorization will be required to access the path.

Each firewall accepts an array of defined routes. Each key, the route’s name, must be unique across all bundles and firewalls. Paths must be unique across the same firewall. Order does matter as the first matching route will be used.

Route definitions

Array Key Required Type Description
path REQUIRED string Defines the path for the URL. Placeholders can be defined using curly brackets. Parameters are passed into the controller function as arguments.
controller REQUIRED string Defines the controller and function to call when the path is accessed. This should be in Symfony’s controller notation of BundleName:ControllerClass:controllerMethod. See Controllers for more information.
method OPTIONAL string Restricts the route to a specific method, i.e. GET, POST, etc
defaults OPTIONAL array Defines default values for path placeholders. If a default is defined, it is not required in the URL. In the code example, /hello will be the same as /hello/earth and the controller’s $world argument will default to ‘earth’ as well.
requirements OPTIONAL array Defines regex matches placeholders must match in order for the route to be recognized. For example, for plugin_helloworld_world in the code example, world is restricted to earth or mars. Anything else will not be recognized by the route.
format OPTIONAL string Sets the request format for the Request response, i.e. Content-Type header. The api firewall will automatically set this to json.
condition OPTIONAL string Very flexible expression to set when the route should match. Refer to Symfony docs.

Note that there are some internally used placeholders that Mautic will set defaults and requirements for (if not overridden by the route)

{page} will default to 1 with a requirement of \d+

{objectId} will default to 0

{id} will have a requirement of \d+ if under the api firewall

Debugging Routes There are a few CLI commands that make help with debugging routes.

php app/console router:debug
php app/console router:debug article_show
php app/console router:match /blog/my-latest-post
<?php // continued

    'menu'     => array(
        'main' => array(
            'priority' => 4,
            'items'    => array(
                'plugin.helloworld.index' => array(
                    'id'        => 'plugin_helloworld_index',
                    'iconClass' => 'fa-globe',
                    'access'    => 'plugin:helloworld:worlds:view',
                    'parent'    => 'mautic.core.channels',
                    'children'  => array(
                        'plugin.helloworld.manage_worlds'     => array(
                            'route' => 'plugin_helloworld_list'
                        ),
                        'mautic.category.menu.index' => array(
                            'bundle' => 'plugin:helloWorld'
                        )
                    )
                )
            )
        ),
        'admin' => array(
            'plugin.helloworld.admin' => array(
                'route'     => 'plugin_helloworld_admin',
                'iconClass' => 'fa-gears',
                'access'    => 'admin',
                'checks'    => array(
                    'parameters' => array(
                        'helloworld_api_enabled' => true
                    )
                ),
                'priority'  => 60
            )
        )
    ),

Menu defines the menu items to display in the different menus.

Mautic 2.0 has four customizable menus.

Menu Name Location
main Main menu on the left
admin Admin menu accessible through the cogwheel in upper right hand side of Mautic
profile Profile menu accessible through clicking the username in upper right hand side of Mautic
extra Displays to the right of the Mautic logo in the upper left hand. Only shows if there are menu items injected.

Priority

To control the placement of the menu item set, set an array with 'priority’ and 'items’ keys. Priority can be negative to position the items lower than others or positive to position them higher. If the menu items are returned without setting priority, like the admin menu in the code example, priority is treated as 9999.

To control the priority of individual menu items, set priority it’s definition array.

Parent

To place a menu item in another bundles parent menu item, for example Channels or Components, define the parent key with the key of the menu item this item should display under. For example, to show an item under the Channels parent menu item, use 'parent' => 'mautic.core.channels',.

The menu item’s name should be the language string key that will be displayed as the item’s link.

Item definitions:

Array Key Required Type Description
route OPTIONAL string The route name as defined in routes. Do not set a route to treat the item as a parent to activate a submenu.
routeParameters OPTIONAL array Route placeholder values to use when generating the URL
id OPTIONAL string Sets the id of the <a /> attribute. This will default to what is set as route. This is used in conjuction with returnUrl returned in a controller’s response so that the correct menu item is highlighted when ajax is used to navigate the interface.
iconClass OPTIONAL string Font Awesome class to set the icon for the menu item.
access OPTIONAL string Set the permission required for this item to display to the user currently logged in. Can also set 'admin’ to restrict to Administrators only.
checks OPTIONAL array Restricts display of the link based on either configured parameters or the GET request. It will accept a 'parameters’ and/or 'request’ array of key => value pairs that must be true to display the menu item.
bundle OPTIONAL string Required only for category integration.
parent OPTIONAL string Display this item under another parent menu item.
priority OPTIONAL int Set the priority for ordering this menu item with it’s siblings.

Services

<?php // continued 

    'services'    => array(
        'events' => array(
            'plugin.helloworld.leadbundle.subscriber' => array(
                'class' => 'MauticPlugin\HelloWorldBundle\EventListener\LeadSubscriber'
            )
        ),
        'forms'  => array(
            'plugin.helloworld.form' => array(
                'class' => 'MauticPlugin\HelloWorldBundle\Form\Type\HelloWorldType',
                'alias' => 'helloworld'
            )
        ),
        'helpers' => array(
            'mautic.helper.helloworld' => array(
                'class'     => 'MauticPlugin\HelloWorldBundle\Helper\HelloWorldHelper',
                'alias'     => 'helloworld'
            )
        ),
        'other'   => array(
            'plugin.helloworld.mars.validator' => array(
                'class'     => 'MauticPlugin\HelloWorldBundle\Form\Validator\Constraints\MarsValidator',
                'arguments' => 'mautic.factory',
                'tag'       => 'validator.constraint_validator',
                'alias'     => 'helloworld_mars'
            )
        )
    ),

Services are PHP objects stored in the service container and are used all throughout Mautic. They can be as simple or as complex as required. Read more about Symfony’s service container here.

Service types

Mautic allows easy configuration for four types of services:

Type Description
events Defines event subscriber classes used to listen to events dispatched throughout Mautic and auto-tagged with 'kernel.event_subscriber.’ The defined class must extend \Mautic\CoreBundle\EventListener\CommonSubscriber. Read more about subscribers here.
forms Defines custom form types and auto-tagged with 'form.type.’
helpers Defines custom template helpers available through the $view variable in views. These services are auto-tagged with 'templating.helper.’
models Defines model services
other All other custom services.

Service definitions

Each key within the service types array is the name of the service and must be unique. Use the following to define the service:

Array Key Required Type Description
class REQUIRED string Namespace to the service class (not that it does not start with a backslash)
arguments OPTIONAL string or array String of a single argument to pass to the construct or an array of arguments to pass. Arguments enclosed with %% will be treated as a parameter. To pass a specific string, enclose the argument with double quotations “”. Anything else that is not a boolean or a namespaced class (string with \ in it) will be treated as the name of another registered service. Often, this will simply be mautic.factory.
alias OPTIONAL string Sets the alias used by the service. For example, the key for the template helpers, $view, array or the string to retrieve a specific form type.
tag OPTIONAL string Tags the service used by bundles to get a list of specific services (for example form types and event subscribers).
tags OPTIONAL array Array of of tags
tagArguments OPTIONAL array Array of attributes for the tag. See Symfony docs for more information.
scope OPTIONAL string Defines the service scope. Deprecated.
factory OPTIONAL string Preferred method for using a factory class. Factory class for managing creating the service.
factoryService OPTIONAL string Factory class for managing creating the service. Deprecated; use factory instead.
factoryMethod OPTIONAL string Method name in the factory service called to create the service. Deprecated; use factory instead.
methodCalls OPTIONAL array Array of methods to be called after a service is created passing in the array of arguments provided. Should be in the format of 'methodName’ => array('service_name’, ’%parameter%’)
decoratedService OPTIONAL array Decorated service
public OPTIONAL bool Public/private service
lazy OPTIONAL bool Lazy load service
synthetic OPTIONAL bool Synthetic service
synthetic OPTIONAL bool Synthetic service
file OPTIONAL string Include file prior to loading service
configurator OPTIONAL array Use a configurator to load service

Categories

<?php // continued

    'categories' => array(
        'plugin:helloWorld' => 'mautic.helloworld.world.categories'
    ),

Defines category types available or the Category manager. See Extending Categories.

Parameters

<?php // continued

    'parameters' => array(
        'helloworld_api_enabled' => false
    )
);

The parameters array define and set default values for custom configuration parameters specific to the plugin.

To obtain the values of these parameters, use the mautic.helper.core_parameters service.

Translations

Mautic uses INI files for translations.

HelloWorldBundle/
- - - Translations/
- - - - - - en_US/
- - - - - - - - - messages.ini

The directory structure for translations should be Translations/locale/domain.ini.

Domains

Language strings can be organized into domains. Each domain should be its own file in the plugin’s language locale folder(s). The plugin can use any domain it wants but Mautic makes consistent use of three domains:

Domain Description
messages Default domain for the translator service when no domain is specified
flashes Domain for flash messages
validators Domain for form validation messages

INI files

Sample INI files

; plugins/HelloWorldBundle/Translations/en_US/messages.ini

plugin.helloworld.contact_us="Contact Us"
plugin.helloworld.goodbye="Goodbye and have a good day!"
plugin.helloworld.greeting="Hello %name%!"
plugin.helloworld.index="Hello World"
plugin.helloworld.manage_worlds="Manage Worlds"
plugin.helloworld.number_of_planets="{0}0 planets|{1}1 planet|]1,Inf[%planets% planets"
plugin.helloworld.world="World"
plugin.helloworld.worlds="%world% Description"
; plugins/HelloWorldBundle/Translations/en_US/flashes.ini

plugin.helloworld.notice.thank_you="Thank you %name% for your interest! We will be in contact soon." 
plugin.helloworld.notice.planet_demoted="%planet% has been demoted to a dwarf planet." 
plugin.helloworld.error.planet_demotion_failed="%planet% could not be demoted because the scientists say so."

General guidelines for the translation keys:

  1. Segment the key using a period
  2. Use underscores to separate words
  3. Must be unique
  4. Be short yet descriptive
  5. Use all lowercase letters and numbers (no punctuation in the key other than period or underscore)

Guidelines for translation strings:

  1. Wrap placeholders with %%
  2. Use a single key for duplicate translation strings.
  3. Use “ for double quotes
  4. HTML is allowed

Using the translator

Refer to the translator service to learn how to use translations in the code.

Using the translator in your javascript

If your bundle implements custom javascript where translations are required, you can get them by the Mautic.translate(key, params) method.

Create a javascript.ini in the same directory as the messages.ini as described above. Any translation strings added to that file will be available when translating in javascript.

For example, if your javascript.ini file contained the following translation strings:

mautic.core.dynamicContent="Dynamic Content"
mautic.core.dynamicContent.new="Dynamic Content %number%"

You can request those translation strings in your javascript by passing the key to the Mautic.translate() function.

Mautic.translate("mautic.core.dynamicContent");
// outputs "Dynamic Content"

String interpolation for messages with variables works with js translations just as you’d expect.

Mautic.translate("mautic.core.dynamicContent.new", {number: 4});
// outputs "Dynamic Content 4"

MVC

Mautic uses a Model-View-Controller structure to manage user interaction on the frontend (views) with the underlying code processes (controllers and models). (Entity and Repository classes are also used to manage interaction with the database).

In Symfony, and thus Mautic, the controller is the center of the MVC structure. The route requested by the user determines the controller method to call. The controller will then communicate with the model to get or manipulate data then display it to the user through the view.

Controllers

<?php
// plugins/HelloWorldBundle/Controller/DefaultController.php

namespace MauticPlugin\HelloWorldBundle\Controller;

use Mautic\CoreBundle\Controller\FormController;

class DefaultController extends FormController
{
    /**
     * Display the world view
     *
     * @param string $world
     *
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function worldAction($world = 'earth')
    {
        /** @var \MauticPlugin\HelloWorldBundle\Model\WorldModel $model */
        $model = $this->getModel('helloworld.world');

        // Retrieve details about the world
        $worldDetails = $model->getWorldDetails($world);

        return $this->delegateView(
            array(
                'viewParameters'  => array(
                    'world'   => $world,
                    'details' => $worldDetails
                ),
                'contentTemplate' => 'HelloWorldBundle:World:details.html.php',
                'passthroughVars' => array(
                    'activeLink'    => 'plugin_helloworld_world',
                    'route'         => $this->generateUrl('plugin_helloworld_world', array('world' => $world)),
                    'mauticContent' => 'helloWorldDetails'
                )
            )
        );
    }

    /**
     * Contact form
     *
     * @return JsonResponse|\Symfony\Component\HttpFoundation\Response
     */
    public function contactAction()
    {
        // Create the form object
        $form = $this->get('form.factory')->create('helloworld_contact');

        // Handle form submission if POST        
        if ($this->request->getMethod() == 'POST') {
            $flashes = array();

            // isFormCancelled() checks if the cancel button was clicked
            if ($cancelled = $this->isFormCancelled($form)) {

                // isFormValid() will bind the request to the form object and validate the data
                if ($valid = $this->isFormValid($form)) {

                    /** @var \MauticPlugin\HelloWorldBundle\Model\ContactModel $model */
                    $model = $this->getModel('helloworld.contact');

                    // Send the email
                    $model->sendContactEmail($form->getData());

                    // Set success flash message
                    $flashes[] = array(
                        'type'    => 'notice',
                        'msg'     => 'plugin.helloworld.notice.thank_you',
                        'msgVars' => array(
                            '%name%' => $form['name']->getData()
                        )
                    );
                }
            }

            if ($cancelled || $valid) {
                // Redirect to /hello/world

                return $this->postActionRedirect(
                    array(
                        'returnUrl'       => $this->generateUrl('plugin_helloworld_world'),
                        'contentTemplate' => 'HelloWorldBundle:Default:world',
                        'flashes'         => $flashes
                    )
                );
            } // Otherwise show the form again with validation error messages
        }

        // Display the form
        return $this->delegateView(
            array(
                'viewParameters'  => array(
                    'form' => $form->createView()
                ),
                'contentTemplate' => 'HelloWorldBundle:Contact:form.html.php',
                'passthroughVars' => array(
                    'activeLink' => 'plugin_helloworld_contact',
                    'route'      => $this->generateUrl('plugin_helloworld_contact')
                )
            )
        );
    }
}

Matching Routes to Controller Methods

The controller method called is determined by the route defined in the config. Take this example,

plugin_helloworld_admin' => array(
    'path'       => '/hello/admin',
    'controller' => 'HelloWorldBundle:Default:admin'
 ),

The controller is noted as HelloWorldBundle:Default:admin. Broken down, that will translate to:

HelloWorldBundle \MauticPlugin\HelloWorldBundle\Controller
Default DefaultController
admin adminAction()

Thus when a browser calls up /hello/admin, \MauticPlugin\HelloWorldBundle\Controller\DefaultController\adminAction() will be called.

What about route placeholders? Symfony is super smart and will pass those into the controller’s method as arguments (the method’s arguments must be the same as the route’s placeholders to be matched up).

'plugin_helloworld_world' => array(
    'path'       => '/hello/{world}',
    'controller' => 'HelloWorldBundle:Default:world',
    'defaults'    => array(
        'world' => 'earth'
    ),
    'requirements' => array(
        'world' => 'earth|mars'
    )
),

The matching method for that route will look be public function worldAction($world = 'earth').

Notice that because the route defines a default for name, the controller method must also set the same default. If the route looked like this instead:

'plugin_helloworld_world' => array(
    'path'       => '/hello/{world}',
    'controller' => 'HelloWorldBundle:Default:world',
    'requirements' => array(
        'world' => 'earth|mars'
    )
),

Then the method would need to be public function worldAction($world).

Extending Mautic’s Controllers

Mautic has several controllers that provide some helper functions.

Mautic\CoreBundle\Controller\CommonController

Controllers extending this will make MauticFactory available via $this->factory and Request via $this->request.

It also provides the following helper methods:

delegateView($args)

Mautic is ajax driven and thus must support both http requests and ajax requests for content. delegateView is wrapper method that determines if the request is for ajax content or the full DOM then generates and returns the appropriate response.

The $args argument is an array with the required elements for generating the view, ajax or http. It will accept the following parameters:

Key Required Type Description
contentTemplate REQUIRED string Defines the view template to load. This should be in view notation of BundleName:ViewName:template.html.php. Refer to views for more information.
viewParameters OPTIONAL array Array of variables with values made available to the template. Each key will be a variable available to the template.
passthroughVars OPTIONAL array Array of variables returned as part of the ajax response used by Mautic and/or the plugin’s onLoad JS callback.

Due to the use of ajax, there are some elements of the passthroughVars array that Mautic will use internally to manipulate the user interface. For responses that include main content, i.e. routes a user would click to, should set at least activeLink and route.

Key Required Type Description
activeLink OPTIONAL string The ID of the menu item that should be activated dynamically to match the ajax response
route OPTIONAL string The route that should be pushed to the browser’s address bar to match the ajax response
mauticContent OPTIONAL string Used to generate the Javascript method to call after the ajax content has been injected into the DOM. The same function will also be called on a page refresh/full page load if set via $view['slots'] in the template. For example, if this is set as helloWorldDetails, Mautic will check for the existence of and executes Mautic.helloWorldDetailsOnLoad(). Refer to Processing Ajax Content for more information regarding this and Asset Helper for injecting assets into the head for ajax generated content.
callback OPTIONAL string A Mautic namespaced JS function that will be executed before the response is injected into the DOM. If set, Mautic will pass the response to the function and not process the content. It will be up to the callback function to handle the rest of the process.
redirect OPTIONAL string The URL to force a page redirect rather than inject ajax content.
target OPTIONAL string jQuery selector to inject the content into. By default, the app’s main content selector will be used.
replaceContent OPTIONAL string Determines if Mautic should replace the target selector with the ajax content or set as its inner HTML. Return ‘true’ as a string to replace the selector.
delegateRedirect($url)

Delegates the appropriate response for redirects.

If an ajax request, a json response with {redirect: $url} will be returned allowing the executing JS code to force the redirect.

If an http request, then a redirect response is returned (i.e redirect header).

postActionRedirect($args)

Similar to delegateView(), this method will delegate the appropriate response based on the request. This method can be used after performing some action, such as saving a form. It accepts the same elements in the $args array as delegateView() but also accepts the following:

Key Required Type Description
returnUrl OPTIONAL string The URL to redirect to. It will default to /s/dashboard if not set. This will also auto-populate passthroughVars[route] if not set.
flashes OPTIONAL array Array of flash messages to display after redirecting. See Flash Messages for more information.
forwardController OPTIONAL bool By default, the request is forwarded to a controller method rather than directly loading a view template. This means that contentTemplate should be in controller notation (BundleName:ControllerName:controllerMethod) rather than view notation (BundleName:ViewName:template.html.php). Set this to false to directly load a view template rather than forwarding to another controller.
Mautic\CoreBundle\Controller\FormController

This controller extends CommonController and provides helper methods for managing forms. See Forms for more information.

Mautic\CoreBundle\Controller\AjaxController

This controller also extends CommonController and is a companion to some of the built-in Javascript helpers. See Javascript methods for more information.

Models

<?php
// plugins/HelloWorldBundle/Model/ContactModel.php

namespace MauticPlugin\HelloWorldBundle\Model;

use Mautic\CoreBundle\Model\CommonModel;

class ContactModel extends CommonModel
{

    /**
     * Send contact email
     * 
     * @param array $data
     */
    public function sendContactEmail($data)
    {
        // Get mailer helper - pass the mautic.helper.mailer service as a dependency
        $mailer = $this->mailer;

        $mailer->message->addTo(
            $this->factory->getParameter('mailer_from_email')
        );

        $this->message->setFrom(
            array($data['email'] => $data['name'])
        );

        $mailer->message->setSubject($data['subject']);

        $mailer->message->setBody($data['message']);

        $mailer->send();
    }
}

Models are used to retrieve and process data between controllers and views. Models aren’t required for plugins but, if used, Mautic provides means to easily obtain the model objects and some commonly used methods.

Model Classes

Model’s should be registered as model services. The names of these services should match the following nomenclature: mautic.UNIQUE_BUNDLE_IDENTIFIER.model.MODEL_IDENTIFIER. UNIQUE_BUNDLE_IDENTIFIER can be whatever is desired but must be unique across all Mautic bundles and plugins. MODEL_IDENTIFIER just has to be unique for the given bundle. For example, the model example code could be registered as mautic.helloworld.model.contact. This allows the helper functions to retrieve model objects to find the correct model service.

Custom models can extend one of two Mautic base models to leverage some helper functions:

\Mautic\CoreBundle\Model\AbstractCommonModel

This is the basic model that mainly provides access to services frequently used with models.

Property Service
$this->factory Mautic’s factory service - deprecated as of 2.0; use direct dependency injection where possible
$this->em Entity manager service
$this->security Mautic’s security service
$this->dispatcher Event dispatcher service
$this->translator Translator service
\Mautic\CoreBundle\Model\FormModel

The FormModel extends AbstractCommonModel and provides a set of helper methods for interacting with entities and repositories. To read more about these methods, refer to the Database section.

Getting Model Objects

<?php

/** @var \Mautic\LeadBundle\Model\LeadModel $leadModel */
$leadModel = $this->getModel('lead'); // shortcut for lead.lead

/** @var \Mautic\LeadBundle\Model\ListModel $leadListModel */
$leadListModel = $this->getModel('lead.list');

/** @var \MauticPlugin\HelloWorldBundle\Model\ContactModel $contactModel */
$contactModel = $this->getModel('helloworld.contact');

If using a model in another service or model, inject the model service as a dependency. If in a controller, use the getModel() helper function.

Views

<?php
//plugins/HelloWorldBundle/Views/World/details.html.php

// Check if the request is Ajax
if (!$app->getRequest()->isXmlHttpRequest()) {

    // Set tmpl for parent template
    $view['slots']->set('tmpl', 'Details');

    // Extend index.html.php as the parent
    $view->extend('HelloWorldBundle:World:index.html.php');
}
?>

<div>
    <!-- Desired content/markup -->
</div>
<?php
// plugins/HelloWorldBundle/Views/World/index.html.php

// Extend the base content
$view->extend('MauticCoreBundle:Default:content.html.php');

// Get tmpl from sub-template
$tmpl = $view['slots']->get('tmpl', 'Details');

// Tell Mautic to call JS onLoad method
$view['slots']->set('mauticContent', 'helloWorld'.$tmpl);

// Set the page and header title
$header = ($tmpl == 'World')
    ? $view['translator']->trans(
        'plugin.helloworld.worlds',
        array('%world%' => ucfirst($world))
    ) : $view['translator']->trans('plugin.helloworld.manage_worlds');
$view['slots']->set('headerTitle', $header);
?>

<div class="helloworld-content">
    <?php $view['slots']->output('_content'); ?>
</div>

Views take data given it from the controller and displays the content to the user. Templates can be called from the controller or from within other templates.

It was discussed in the Controller’s delegateView() method how to render a view from a controller. Remember that delegateView uses contentTemplate to determine what view to render.

Similar to controller notation, views are noted as HelloWorldBundle:Contact:form.html.php which will point to the file /path/to/mautic/plugins/HelloWorldBundle/Views/Contact/form.html.php.

View Parameters

Remember the array passed via viewParameters in the Controller’s delegateView() method? Here is where the elements of that array will be made available as variables.

For example, $world will be available and assigned the value of mars from the following example:

 'viewParameters'  => array(
     'world' => 'mars'
 ),
 

By default there are a couple variables available and should not be overridden by the controller

Variable Description
$view Contains all the helper objects available to templates along with providing methods for extending and/or rendering other templates.
$app Gives access to request and session to views via $app->getRequest() and $app->getSession()

Extending Views

<?php

// Extends the full document with menu, page header, etc
$view->extend('MauticCoreBundle:Default:content.html.php');
<?php

// Extends the "slim" document which includes just the head and body with main content; does not include menu, page header, etc.
$view->extend('MauticCoreBundle:Default:content.html.php');

Because of the use of Ajax, views must also be able to return main content vs the entire document. This is done by extending Mautic’s base templates. Notice the notation is the same as what is used in controllers.

To determine if the request is Ajax or not, use $app->getRequest()->isXmlHttpRequest(). Another option is to make the determination in the controller and pass it in the viewParameters array.

Of course, this can also be used to extend custom templates as demonstrated in this code example.

To include the content from the sub-template in the parent template, use $view['slots']->output('_content');. See the Slots Helper for more information.

Rendering Views within Views

To render the content of another template from within a template, simply use echo $view->render('BundleName:ViewName:template.html.php', array('parameter' => 'value'));

Template Helpers

There are a number of template helper objects and helper view templates that are built into Mautic.

Slots Helper
<?php

// Set a slot with content; will overwrite if slot already exists 
$view['slots']->set('name', 'the content');

// Append content to slot rather than overwrite
$view['slots']->append('name', ' and more content');
$view['slots']->append('existingArray', 
    array(
        'append' => 'me'
    )
);

// Get the slot content
$content = $view['slots']->get('name', 'default value');

// Output the content; note that echo is not required
$view['slots']->output('name');

// Check if a slot exists
if ($view['slots']->has('name')) {
    // Do something
}

As seen with extending views, the slots helper is used to pass content up through parent templates. Remember that extended templates are rendered inside out. So, content can be set in a sub template and accessed from the parent template but content set in a parent template, will not be available to the sub-template.

Asset Helper
<?php

// Generate relative URL to image
echo '<img src="' . $view['assets']->getUrl('plugins/HelloWorldBundle/assets/images/earth.png') . '" />';

// Dynamically insert script into head
echo $view['assets']->includeScript('plugins/HelloWorldBundle/assets/helloworld.js');

// Dynamically insert stylesheet into head
echo $view['assets']->includeStylesheet('plugins/HelloWorldBundle/assets/helloworld.css');

The asset helper, accessed via $view['assets'], is used to load assets into the DOM including images, script and stylesheets.

The asset helper also provides a way to insert scripts and stylesheets into the head for ajax loaded content using $view['assets']->includeScript() and $view['assets']->includeStylesheet().

Router Helper
<a href="<?php echo $view['router']->generate('plugin_helloworld_world', array('world' => 'mars')); ?>" data-toggle="ajax" />Mars</a>

The router helper, $view['router'], works as explained in the Router.

Translation Helper
<h1><?php echo $view['translator']->trans('plugin.helloworld.worlds', array('%world%', 'Mars'); ?></h1>

The translation helper, $view['translator'], works as explained in the Translator section.

Date Helper
<?php

// Can be string or \DateTime object; if string, it's assumed local time unless noted otherwise via third argument to helper methods
$datetime = '2015-04-12 20:56:00';

// Formats per full date system config setting 
$fullDateTime = $view['date']->toFull($datetime);

// Formats per short date system config setting
$shortDateTime = $view['date']->toShort($datetime);

// Formats per date only system config setting
$date = $view['date']->toDate($datetime);

// Formats per time only system config setting
$time = $view['date']->toTime($datetime);

// Formats as date only config setting + time only config setting
$datetime = $view['date']->toFullConcat($datetime);

// Formats date as Yesterday, 8:02 pm (Today or Tomorrow); otherwise 'x days ago'  
$text = $view['date']->toText($datetime);

// Format a string that is not already in local time
$fullDateTime = $view['date']->toFull($datetime, 'Y-m-d H:i:s', 'UTC');

The date helper can be used to format dates based on system and/or user settings.

The first argument to the various methods can either be a date/time string or a \DateTime object. If a string, it is expected to be formatted as ‘Y-m-d H:i:s’ and already in the user’s and/or system’s local time. If it is not, pass the format as the second argument and the timezone (string) as the third.

Form Helper
<?php echo $view['form']->form($form); ?>

The form helper, $view['form'], works as explained in the Form section.

View Helper Templates

@todo

Processing Ajax Content

Mautic provides built in ways to ajaxify links, modals, and forms.

<a href="<?php echo $view['router']->generate('plugin_helloworld_world', array('world' => 'mars')); ?>" data-toggle="ajax" />Mars</a>

To ajaxify a link, set the attribute data-target="ajax"

Ajax Modals
<a href="<?php echo $view['router']->generate('plugin_helloworld_world', array('world' => 'mars')); ?>" 
   data-toggle="ajaxmodal"
   data-target="#MauticSharedModal"
   data-header="<?php echo $view['translator']->trans('plugin.helloworld.worlds', array('%world%', 'Mars')); ?>">Mars</a>

Mautic uses Bootstrap modals but Bootstrap lacks an easy way to dynamically retrieve content more than once. Thus, Mautic provides the attribute data-toggle="ajaxmodal" to help with this.

data-target should be the selector of the model to inject the content into. Mautic has a modal, #MauticSharedModal on standby to be used for this very purpose.

data-header will set the modal’s header.

Ajax Forms

When using Symfony’s form services, the form will be auto-ajaxified.

Ajax Content Callbacks
Mautic.helloWorldDetailsOnLoad = function(container, response) {
    // Manipulate content 
};

Mautic.helloWorldDetailsOnUnload = function(container, response) {
    // Manipulate content 
};

Mautic provides a way to execute a javascript function after it injects ajax content into the DOM. This can be useful to activate various JS driven features such as typeaheads, charts, bind events, etc.

To take advantage of this, utilize the mauticContent element in the passthroughVars array from the controller’s delegateView(). The value of this variable will determine what function should be called after the content has been injected.

For example, the method Mautic.helloWorldDetailsOnLoad() will be called for the following:

 'passthroughVars' => array(
     'activeLink'    => 'plugin_helloworld_world',
     'route'         => $this->generateUrl('plugin_helloworld_world', array('world' => $world)),
     'mauticContent' => 'helloWorldDetails'
 )
 

When the user browses away from the page, Mautic.helloWorldDetailsOnUnload() will be called to give opportunity to destroy objects if necessary.

Both the OnLoad and OnUnload methods are passed two arguments.

Argument Description
container The selector that was used as the target of the ajax content.
response The object from the ajax response, i.e. set in the passthroughVars from the Controller

mauticContent should also be set via $view['slots'] in the view’s main page.

$view['slots']->set('mauticContent', 'helloBundleDetails');

Doing so will ensure Mautic.helloWorldDetailsOnLoad() is also called when there is a page refresh.

Services

These are the services used commonly throughout Mautic.

Factory Service

\Mautic\Factory\MauticFactory is deprecated as of 2.0 and will be phased out through 2.x release cycles and completely removed in 3.0. Direct dependency injection into the services should be used instead where possible.

For controllers, extend either \Mautic\CoreBundle\Controller\CommonController or \Mautic\CoreBundle\Controller\FormController and it will be available via $this->factory by default. Otherwise, obtain the factory from the service container via $factory = $this->container->get('mautic.factory');

For models, it will be available via $this->factory by default.

For custom services, pass ‘mautic.factory’ as an argument and MauticFactory will be passed into the __construct of the service.

User

<?php
$user = $this->get('mautic.helper.user')->getUser();

$firstName = $user->getFirstname();
$lastName  = $user->getLastname();
$email     = $user->getEmail();
$profile   = $user->getProfile();

$role = $user->getRole()->getName();

if ($role->isAdmin()) {
    // do something
}

getUser() will return the entity, \Mautic\UserBundle\Entity\User that can then be used to get information about the currently logged in user.

Security

<?php

$security = $this->get('mautic.security');

// Check if user is granted a single permission
if ($security->isGranted('plugin:helloWorld:worlds:view')) {
    // do something
}

// Check if user is granted multiple permissions (must be granted to all to be true)
if ($security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:create',
    )
)
) {
    //do something
}

// Check if user is granted to at least one permission
if ($security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:edit',
    ),
    'MATCH_ONE'
)
) {
    //do something
}

// Get an array of user permissions
$permissions = $security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:edit',
    ),
    'RETURN_ARRAY'
);

if ($permissions['plugin:helloWorld:worlds:view']) {
    // do something
}

// Check to see if a user is anonymous (not logged in)
if ($security->isAnonymous()) {
    // do something
}

Using the service to check permissions is explained more in Using Permissions.

Translator

<?php

$translator = $this->get('translator');

// Simple string
echo $translator->trans('plugin.helloworld.goodbye');

// Simple string with placeholders
echo $translator->trans('plugin.helloworld.greeting', array('%name%' => $name));

// String from a domain other than messages (will use planets.ini)
echo $translator->trans('plugin.helloworld.worlds', array('%world%' => $world), 'planets');

// Plural translations
$planetCount = 3;
echo $translator->transChoice('plugin.helloworld.number_of_planets', $planetCount, array('%planets%' => $planetCount));

// Check to see if a translation key exists
if ($translator->hasId('plugin.helloworld.goodbye')) {
    echo $translator->trans('plugin.helloworld.goodbye');
} else {
    // other logic
}

// Use the first key if it exists, otherwise use the second (helpful to prevent managing duplicate keys with the same string)
echo $translator->transConditional('plugin.helloworld.planets.' . $planet, 'plugin.helloworld.dwarf_planets. ' . $planet);

Use the translator service to include translated strings in the code. Depending on where the translation is necessary will determine how to obtain the service.

To use the template service in view templates, simply use the template helper, $view['translator'].

The translator service has the following functions to help with translating strings:

Simple translation
trans($id, array $parameters = array(), $domain = null, $locale = null)

Pluralization
transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null)

Check to see if a key exists
hasId($id, $domain = null, $locale = null)

Use the $preferred key if it exists, if not, use $alternative
transConditional($preferred, $alternative, $parameters = array(), $domain = null, $locale = null)

Router

<?php 

$router = $this->get('router');

// Relative URL
$url = $router->generateUrl('plugin_helloworld_admin');

// URL with placeholders
$url = $router->generateUrl('plugin_helloworld_world', array('%world%', 'mars'));

// Absolute URL
$absoluteUrl = $router->generateUrl('plugin_helloworld_admin', array(), true);
<?php 
// Deprecated - use path or url instead
$url = $view['router']->generate('plugin_helloworld_admin'); // result is /hello/admin

// Generate a path in a view template
$path = $view['router']->path('plugin_helloworld_admin'); // result is /hello/admin
$url  = $view['router']->url('plugin_helloworld_admin'); // result is http://yoursite.com/hello/admin

For views, use the $view['router'] helper. The difference with the template helper is that url() or path() is used instead of generateUrl() of the router service.

Request

<?php

$request = $this->get('request_stack')->getCurrentRequest();

// $_GET
$get = $request->query->all();

// $_POST
$post = $request->request->all();

// $_COOKIE
$cookies = $request->cookies->all();

// $_SERVER
$server = $request->server->all();

// Headers
$headers = $request->headers->all();

// Attributes - custom parameters
$headers = $request->attributes->all();

// Check if a parameter exists
if ($request->request->has('hello')) {
    // do something
}

// Retrieve value of a specific parameter setting mars as default
$world = $request->query->get('world', 'mars');

// Set custom request value
$request->attributes->set('hello', 'world');

// Get the value of a nested array
$mars = $request->request->get('world[mars]', array(), true);

There are multiple ways to obtain the request service.

If the controller is extending one of Mautic’s controllers, it is already available via $this->request. Alternatively, Symfony will auto-inject the request object into the controller action method if the variable is type-hinted as Symfony\Component\HttpFoundation\Request.

For services, pass the request_stack service then use $request = $requestStack->getCurrentRequest().

From within a view, use $app->getRequest().

Session

<?php

$session        = $this->get('session');
$requestSession = $request->getSession(); // Shortcut to session

// Get all session parameters
$all = $session->all();

// Get specific parameter setting mars as default
$world = $session->get('helloworld.world', 'mars');

// Check if a parameter exists
if ($session->has('helloworld.world')) {
    // do something
}

// Set a session parameter
$session->set('helloworld.world', 'mars');

// Remove a session parameter
$session->remove('helloworld.world');

// Clear the whole session
$session->clear();

To access the session service in a view, use $app->getSession().

Database/Entity Manager

<?php
// From controller
$em         = $this->getDoctrine()->getManager();
$repository = $em->getRepository('HelloWorldBundle:World');
$worlds     = $repository->getEntities();

/** @var \MauticPlugin\HelloWorldBundle\Entity\World $world */
foreach ($worlds as $world) {
    $world->upVisitCount();
}

$repository->saveEntities($worlds);

Doctrine includes an ORM and DBAL layers.

ORM/entity manager:

DBAL (direct DB driver):

The entity manager can be used to interact with the bundle’s repositories and entities. See Database for more info.

Config Parameters

<?php 
$config = $this->get('mautic.helper.core_parameters');

$apiEnabled = $config->getParameter('helloworld_api_enabled', false);

Event Dispatcher

<?php

$dispatcher = $this->get('event_dispatcher');
if ($dispatcher->hasListeners(HelloWorldEvents::ARMAGEDDON)) {
    $event = $dispatcher->dispatch(HelloWorldEvents::ARMAGEDDON, new ArmageddonEvent($world));

    if ($event->shouldPanic()) {
        throw new \Exception("Run for the hills!");
    }
}

Dispatch custom events using the event_dispatcher service.

Paths Helper

<?php 
$pathsHelper = $this->get('mautic.helper.paths');

$relativeImagesDir = $pathsHelper->getSystemPath('images'); // media/images
$absoluteImageDir  = $pathsHelper->getSystemPath('images', true); // /home/user/public_html/media/images

This helper should be used when retrieving system paths for Mautic (images, themes, cache, etc)

There is also a tmp or temporary option to store temporary files. It should be used by developers for such use case instead of the general cache location.

IP Lookup Helper

<?php 
$ipHelper = $this->get('mautic.helper.ip_lookup');

$requestIp = $ipHelper->getIpAddressFromRequest(); // 1.2.3.4

/** @var \Mautic\CoreBundle\Entity\IpAddress $ipAddressEntity */
$ipAddressEntity = $ipHelper->getIpAddress();

/** @var array $details */
$details = $ipAddressEntity->getIpDetails();

echo $details['city'];

This helper can be used to retrieve the real IP for the request.

Plugin Config Helper

<?php 
$configHelper = $this->get('mautic.helper.bundle');

$menu = $configHelper->getBundleConfig('HelloWorldBundle', 'menu', true);

This can be used to get the configuration array of a bundle/plugin’s Config/config.php file.

<?php

$cookieHelper = $this->get('mautic.helper.cookie');
$cookieHelper->setCookie('name', 'value', 3600);

$cookieHelper->deleteCookie('name');

The cookie helper can be used to set cookies based on system settings.

Mail Helper

<?php
$mailer = $this->get('mautic.helper.mailer')->getMailer();

// To address; can use setTo(), addCc(), setCc(), addBcc(), or setBcc() as well
$mailer->addTo($toAddress, $toName);

// Set a custom from; will use system settings by default
$mailer->setFrom(
    $this->user->getEmail(),
    $this->user->getFirstName().' '.$this->user->getLastName()
);

// Set subject
$mailer->setSubject($email['subject']);

// Set content
$mailer->setBody($content);
$mailer->parsePlainText($content);

// Optional lead tracking (array)
$mailer->setLead($lead);
$mailer->setIdHash();

// Send the mail, pass true to dispatch through event listeners (for replacing tokens, etc)
if ($mailer->send(true)) {

    // Optional to create a stat to allow a web view, tracking, etc
    $mailer->createLeadEmailStat();
} else {
    $errors = $mailer->getErrors();
    $failedRecipients = $errors['failures'];
}
<?php
// Using queue() for tokenization support

use Mautic\EmailBundle\Swiftmailer\Exception\BatchQueueMaxException;

$mailer = $this->get('mautic.helper.mailer')->getMailer();
$failed = array();

$mailer->enableQueue();
foreach ($emailList as $email) {
    try {
        if (!$mailer->addTo($email['email'], $email['name'])) {
            // Clear the errors so it doesn't stop the next send
            $mailer->clearErrors();

            $failed[] = $email;

            continue;
        }
    } catch (BatchQueueMaxException $e) {
        // Queue full so flush (send) then try again
        if (!$mailer->flushQueue()) {
            $errors = $mailer->getErrors();
            $failed = array_merge($failed, $errors['failures']);
        }

        if (!$mailer->addTo($email['email'], $email['email'], $email['name'])) {
            // ...
        }
    }
}

// Flush pending
if (!$mailer->flushQueue()) {
    // ...
}

The mail helper can be used to send email, running the content through the event listeners to search/replace tokens, manipulate the content, etc.

Some transports, such as Mandrill, support tokenized emails for multiple recipients. The mail helper makes it easy to leverage this feature by using it’s queue() and flushQueue() functions in place of send(). If sending a batch of emails, it is recommended to use the queue() function. Although these classes will still work with using just send() for one off emails, if sending a batch of the same email to multiple contacts, enable tokenization/batch mode with enableQueue().

If using an Email entity (\Mautic\EmailBundle\Entity\Email), just pass the Email entity to $mailer->setEmail($email) and the subject, body, assets, etc will be extracted and automatically set.

Attachments

Attachments can be attached to emails by using the attachFile() function. You can also attach Mautic assets (\Mautic\AssetBundle\Entity\Asset) via attachAsset().

Refer to the class for more details on available functions.

Model Factory

<?php

$channel   = 'email';
$channelId = 1;

if ($modelFactory->hasModel($channel)) {
    $model = $modelFactory->getModel($channel);

    if ($entity = $model->getEntity($channelId)) {
        echo $entity->getName();
    }
}

Mautic/CoreBundle/Factory/ModelFactory can be used in services that a model dependency is unknown at the time the service is created. This is great for scenarios where the channel and channel ID are stored in a database and the executing code needs to obtain information on the channel entity (name, etc).

It has two methods: hasModel($modelNameKey) and getModel($modelNameKey). hasModel simple checks to see if a model exists. It uses the same format as using the controller helper method getModel(). For example, to obtain the Mautic\EmailBundle\Model\EmailModel class, you could use something like the code example.

Database

<?php
// plugins/HelloWorldBundle/Entity/World.php

namespace MauticPlugin\HelloWorldBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Mautic\CategoryBundle\Entity\Category;
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
use Mautic\CoreBundle\Entity\CommonEntity;

/**
 * Class World
 */
class World extends CommonEntity
{
    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    /**
     * @var string
     */
    private $description;

    /**
     * @var Category
     */
    private $category;

    /**
     * @var int
     */
    private $visitCount;

    /**
     * @var int
     */
    private $population = 0;

    /**
     * @var bool
     */
    private $isInhabited = false;

    /**
     * @param ORM\ClassMetadata $metadata
     */
    public static function loadMetadata (ORM\ClassMetadata $metadata)
    {
        $builder = new ClassMetadataBuilder($metadata);

        $builder->setTable('worlds')
            ->setCustomRepositoryClass('MauticPlugin\HelloWorldBundle\Entity\WorldRepository');

        // Helper functions
        $builder->addIdColumns();
        $builder->addCategory();
        $builder->addNamedField('visitorCount', 'int', 'visitor_count');
        $builder->addField('population', 'int');

        // Native means to build a field
        $builder->createField('isInhabited', 'bool')
            ->columnName('is_inhabited')
            ->nullable(false);
    }

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param mixed $name
     *
     * @return World
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @param mixed $description
     *
     * @return World
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * @return Category
     */
    public function getCategory()
    {
        return $this->category;
    }

    /**
     * @param mixed $category
     *
     * @return World
     */
    public function setCategory(Category $category)
    {
        $this->category = $category;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getVisitCount()
    {
        return $this->visitCount;
    }

    /**
     * @param mixed $visitCount
     *
     * @return World
     */
    public function setVisitCount($visitCount)
    {
        $this->visitCount = $visitCount;

        return $this;
    }

    /**
     * Increase the visit count by one
     */
    public function upVisitCount()
    {
        $this->visitCount++;
    }

    /**
     * Get planet population
     */
    public function getPopulation()
    {
        return $this->population;
    }

    /**
     * @param int $population
     *
     * @return World
     */
    public function setPopulation($population)
    {
        $this->population = $population;

        return $this;
    }

    /**
     * @return boolean
     */
    public function isIsInhabited()
    {
        return $this->isInhabited;
    }

    /**
     * @param boolean $isInhabited
     *
     * @return World
     */
    public function setIsInhabited($isInhabited)
    {
        $this->isInhabited = $isInhabited;

        return $this;
    }
}
<?php
// plugins/HelloWorldBundle/Entity/WorldRepository.php

namespace MauticPlugin\HelloWorldBundle\Entity;

use Mautic\CoreBundle\Entity\CommonRepository;

/**
 * WorldRepository
 */
class WorldRepository extends CommonRepository
{

    public function getEntities($args = array())
    {
        $q = $this
            ->createQueryBuilder('w')
            ->leftJoin('a.category', 'c');

        $args['qb'] = $q;

        return parent::getEntities($args);
    }
}

Mautic uses Doctrine, a database object relational mapper (ORM) and database abstraction layer (DBAL) library.

Most of Mautic use entity and repository classes to define the schema and interact with the database.

Usage

Refer to Symfony’s and Doctrine’s documentation on the specifics of how to interact with the Database.

Metadata/Schema

Mautic uses Doctrine’s PHP Driver to define schema. The plugin doesn’t necessarily have to use entities but should at least define Entity classes to create it’s schema.

Installing/Updating Schema

When an plugin is installed or updated, the bundle’s onInstall or onUpgrade functions are called. These functions can be used to manipulate the database schema. See Install/Upgrade.

Table Prefix

Mautic allows custom table prefixes. If using ORM, there is no need to include the prefix as Mautic will automatically handle it. However, if there is a need to use Doctrine’s DBAL layer directly, the contstant MAUTIC_TABLE_PREFIX can be used in conjuction with the table name.

ORM Arrays and DateTime

When using ORM, Mautic will automatically convert DateTime properties to UTC and to the system/user’s profile timezone on retrieval. However, if using DBAL, DateTime strings must be converted to UTC before persisting and to the local timezone on retrieval. See Date/Time Helper to assist with conversions.

For arrays, ORM will auto serialize and unserialize. DBAL will need to manually handle this.

Roles and Permissions

Mautic provides a means of defining custom permissions for roles through Permission objects.

How Permissions Work

Permissions are calculated based on bits assigned to an plugin’s level and permission. Bits are integers that increase by doubling the value. 1, 2, 4, 8, 16, 32, 64, 128, 512, 1024, and so forth. The bits should never be assigned numbers in between such as 3 or 5 as the permission will not be correctly calculated in such cases.

For example, let’s say HelloWorldBundle needs to manage access to user’s Worlds entity. A permission set for plugin:helloWorld:worlds might look like

Permission Bit
view 1
edit 2
create 4
delete 8
full 16

Mautic will take the summation of the bits for the permissions given to a role and store it in the database. For example, if a role is given view and edit access, the stored bit is 3. If given view and create access, the stored bit is 5.

When a permission check is required, e.g. plugin:helloWorld:worlds:create, Mautic will check if bit of 4 is set in the role’s generated bit for plugin:helloWorld:worlds. If so, permission is granted.

Using Permissions

<?php

$security = $this->get('mautic.security');

// Check if user is granted a single permission
if ($security->isGranted('plugin:helloWorld:worlds:view')) {
    // do something
}

// Check if user is granted multiple permissions (must be granted to all to be true)
if ($security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:create',
    )
)
) {
    //do something
}

// Check if user is granted to at least one permission
if ($security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:edit',
    ),
    'MATCH_ONE'
)
) {
    //do something
}

// Get an array of user permissions
$permissions = $security->isGranted(
    array(
        'plugin:helloWorld:worlds:view',
        'plugin:helloWorld:worlds:edit',
    ),
    'RETURN_ARRAY'
);

if ($permissions['plugin:helloWorld:worlds:view']) {
    // do something
}

// Check if user has access to view leads
if ($security->isGranted('lead:leads:viewother')) {
    // do something
}

To determine if a user has a specific permission, use Mautic’s security service which can be obtained from the mautic.security service.

As suggested above, Mautic uses a special permission notation to refer to a specific permission. For core bundles, bundleName:permissionLevel:permission is used. For plugins, append plugin:, i.e. plugin:bundleName:permissionLevel:permission. plugin: tells Mautic to look for the permission class in the plugins/ directory and MauticPlugin namespace.

The permission level and permissions are set by the core bundle or plugin. For example, Mautic’s core UserBundle has users and roles levels with view, edit, create, delete and full permissions for each.

To check if a user has permission to edit roles, use $mauticSecurity->isGranted('user:roles:edit');

Creating Custom Permissions

<?php
// plugins/HelloWorldBundle/Security/Permissions/HelloWorldPermissions.php

namespace MauticPlugin\HelloWorldBundle\Security\Permissions;

use Symfony\Component\Form\FormBuilderInterface;
use Mautic\CoreBundle\Security\Permissions\AbstractPermissions;

/**
 * Class HelloWorldPermissions
 */
class HelloWorldPermissions extends AbstractPermissions
{

    /**
     * Define available permissions
     *
     * @param $params
     */
    public function __construct($params)
    {
        parent::__construct($params);

        $this->permissions = array(

            // Custom level
            'worlds' => array(

                // Custom permissions
                'use_telescope' => 1,
                'send_probe'    => 2,
                'visit'         => 4,
                // Full will almost always be included and should be significantly higher than the
                // others in case new permissions need to be added later 
                'full'          => 1024
            )
        );

        // Add standard category permissions
        $this->addStandardPermissions('categories');
    }

    /**
     * Append the permission form fields to the Role form
     *
     * @param FormBuilderInterface $builder
     * @param array                $options
     * @param array                $data
     */
    public function buildForm(FormBuilderInterface &$builder, array $options, array $data)
    {
        // Add standard category form fields
        $this->addStandardFormFields('helloWorld', 'categories', $builder, $data);

        // Add custom 'worlds' level form fields
        $builder->add(

            // Form element name should be bundleName:permissionLevel
            'helloWorld:worlds',

            // Should always be permissionlist type
            'permissionlist',
            array(
                'choices' => array(
                    'use_telescope' => 'plugin.helloworld.permissions.use_telescope',
                    'send_probe'    => 'plugin.helloworld.permissions.send_probe',
                    'visit'         => 'plugin.helloworld.permissions.visit',
                    'full'          => 'mautic.core.permissions.full',
                ),
                'label'   => 'plugin.helloworld.permissions',

                // Set existing data
                'data'    => (!empty($data['worlds']) ? $data['worlds'] : array()),

                // Bundle name (used to build frontend form)
                'bundle'  => 'helloWorld',

                // Permission level (used to build frontend form)
                'level'   => 'worlds'
            )
        );
    }

    /**
     * Permission set identifier; should be bundleName
     * 
     * @return string
     */
    public function getName()
    {
        return 'helloWorld';
    }
}

An plugin can create it’s own set of permissions by creating a Permission class. See the code example for a skeleton outline of what the class will look like.

Each permission class should extend Mautic\CoreBundle\Security\Permissions\AbstractPermissions.

Then, for most permission classes, three methods are needed: __construct(), buildForm() and getName().

__construct()

The construct method should do two things. It should call parent::__construct($params) or it should set $this->params = $params;.

Then it should define $this->permissions. $this->permissions is an array of permission levels that are each arrays with permissions assigned to bits. For example, in the code block, a custom permission level of worlds is defined with the permissions of use_telescope, send_probe, visit and full. To check to see if a user has permission to the level worlds and permission send_probe, $mauticSecurity->isGranted('plugin:helloWorld:worlds:send_probe') would be used.

Mautic provides a few helper methods for common permission sets:

Method Description
addStandardPermissions() Set view, edit, create, delete, publish (with option to exclude), and full permissions.
addExtendedPermissions() Set creator level restrictions: viewown, viewother, editown, editother, create, deleteown, deleteother, publishown (with option to exclude), publishother (with option to exclude), and full
addManagePermission Set single manage permission

buildForm()

The buildForm method will append the permission toggles to the Role’s form (see Forms for details on form builders). Review the comments in the code sample.

There are complimentary helper methods for the common permission sets:

Method Description
addStandardFormFields() Appends the standard permission sets to the form
addExtendedFormFields() Appends the extended, aka creator restricted, permissions to the form
addManageFormFields() Appends the single manager element to the form

getName() This method is absolutely required and should match both the bundleName and the name of the file. For example, if HelloWorldBundle is the bundle’s name, then this would be helloWorld with a filename of HelloWorldPermissions.php.

Permission Aliases

<?php

    protected function getSynonym($name, $level)
    {
        if ($name == 'send_satellite') {
            // Set real permission name
            $name = 'send_probe';
        }

        return array($name, $level);
    }

To add a permission alias, use the getSynonym() method. Basically this method is called before each requested permission is determined giving opportunity to change the permission level or name as needed.

For example, parent::getSynonym() will recognize editown as edit if editown isn’t defined in the permission class’ $this->permissions property for the requested level.

Manipulating Permissions before Saving

 <?php

     /**
      * @param array $permissions     Plugin specific permissions
      * @param       $allPermissions  All role permissions
      * @param bool  $isSecondRound   Is round two after permissions have been updated by all permission classes 
      *
      * @return bool Return true if a second round is required; default false
      */
     public function analyzePermissions(array &$permissions, $allPermissions, $isSecondRound = false)
     {
         foreach ($permissions as $level => &$perms) {
             foreach ($perms as $perm) {
                 $include = array();
                 switch ($perm) {
                     case 'send_probe':
                         $include = array('use_telescope');
                         break;
                     case 'visit':
                         $include = array('use_telescope', 'send_probe');
                         break;
                 }
                 if (!empty($include)) {
                     foreach ($include as $r) {
                         list($ignore, $r) = $this->getSynonym($level, $r);
                         if ($this->isSupported($level, $r) && !in_array($r, $perms)) {
                             $perms[] = $r;
                         }
                     }
                 }
             }
         }

         // Return true if this method needs a second round after all the other bundles have adjusted permissions
         return false;
     }

Plugin’s can adjust permissions based on other selected permissions in order to prevent ‘user error.’ For example, if a user has permission to edit, then the user also needs permission to view whether that was selected in the Role form or not. The method analyzePermissions() can be be used for this which gives opportunity to the plugin to modify permissions based on other selections before persisting to the database.

Sometimes, it may be necessary to re-adjust based on a permission that is outside the plugin’s control. In this case, analyzePermissions() can return true and it will be called again after all the permissions have been analyzed by the other bundles and plugins. In this case, the argument $isSecondRound will be true.

Advanced isGranted Logic

If it is necessary to perform some logic other than simply comparing bits, the permission class can override the parent’s public function isGranted($userPermissions, $name, $level) and do whatever is necessary for it’s own permission levels and individual permissions.

Advanced isSupported Logic

The same can be applied for the method isSupported() which is used to determine if a bundle or plugin includes the requested permission and permission level. This can also be used to provide BC support.

Custom Config Params

Mautic’s configuration is stored in app/config/local.php. Plugin’s can leverage custom config parameters to use within it’s code.

Each configuration option desired should be defined and have a default set in the plugin’s config file. This prevents Symfony from throwing errors if the parameter is used during cache compilation or if accessed directly from the container without checking if it exists first. Defining the parameters in the plugin’s config file will ensure that it always exists.

To add config options to the Configuration page, an event subscriber, a config form type, and a specific view are required.

Config Event Subscriber

<?php
// plugins/HelloWorldBundle/EventListener/ConfigSubscriber.php

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\ConfigBundle\Event\ConfigEvent;
use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\ConfigBundle\ConfigEvents;
use Mautic\ConfigBundle\Event\ConfigBuilderEvent;

/**
 * Class ConfigSubscriber
 */
class ConfigSubscriber extends CommonSubscriber
{

    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return array(
            ConfigEvents::CONFIG_ON_GENERATE => array('onConfigGenerate', 0),
            ConfigEvents::CONFIG_PRE_SAVE    => array('onConfigSave', 0)
        );
    }

    /**
     * @param ConfigBuilderEvent $event
     */
    public function onConfigGenerate(ConfigBuilderEvent $event)
    {
        $event->addForm(
            array(
                'formAlias'  => 'helloworld_config',
                'formTheme'  => 'HelloWorldBundle:FormTheme\Config',
                'parameters' => $event->getParametersFromConfig('HelloWorldBundle')
            )
        );
    }

    /**
     * @param ConfigEvent $event
     */
    public function onConfigSave(ConfigEvent $event)
    {
        /** @var array $values */
        $values = $event->getConfig();

        // Manipulate the values
        if (!empty($values['helloworld_config']['custom_config_option'])) {
            $values['helloworld_config']['custom_config_option'] = htmlspecialchars($values['helloworld_config']['custom_config_option']);
        }

        // Set updated values 
        $event->setConfig($values);
    }
}

The event subscriber will listen to the ConfigEvents::CONFIG_ON_GENERATE and ConfigEvents::CONFIG_PRE_SAVE events.

The ConfigEvents::CONFIG_ON_GENERATE is dispatched when the configuration form is built giving the plugin an opportunity to inject it’s own tab and config options.

To do this, the plugin must register it’s configuration details through the method assigned to the ConfigEvents::CONFIG_ON_GENERATE event. The \Mautic\ConfigBundle\Event\ConfigBuilderEvent object is passed into the method and expects the method to call addForm(). addForm() expects an array with the following elements:

Key Description
formAlias Alias of the form type class that sets the expected form elements
formTheme View to format the configuration form elements, i.e, HelloWorldBundle:FormTheme\Config
parameters Array of custom config elements. $event->getParametersFromConfig('HelloWorldBundle'))) can be used to glean them from the plugin’s config file.

The ConfigEvents::CONFIG_PRE_SAVE is called before the values from the form are rendered and saved to the local.php file. This gives the plugin an opportunity to clean up or manipulate the data before it is written.

Remember that the subscriber must be registered through the plugin’s config in the services[events] section.

Config Form

<?php
// plugins/HelloWorldBundle/Form/Type/ConfigType.php

namespace MauticPlugin\HelloWorldBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

/**
 * Class ConfigType
 */
class ConfigType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array                $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add(
            'custom_config_option',
            'text',
            array(
                'label' => 'plugin.helloworld.config.custom_config_option',
                'data'  => $options['data']['custom_config_option'],
                'attr'  => array(
                    'tooltip' => 'plugin.helloworld.config.custom_config_option_tooltip'
                )
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'helloworld_config';
    }
}

The form type is used to generate the form fields in the main configuration form. Refer to Forms for more information on using form types.

Remember that the form type must be registered through the plugin’s config in the services[forms] section.

Config Template

<?php
// plugins/HelloWorldBundle/Views/FormTheme/Config/_config_helloworld_config_widget.html.php

?>

<div class="panel panel-primary">
    <div class="panel-heading">
        <h3 class="panel-title"><?php echo $view['translator']->trans('mautic.config.tab.helloworld_config'); ?></h3>
    </div>
    <div class="panel-body">
        <?php foreach ($form->children as $f): ?>
            <div class="row">
                <div class="col-md-6">
                    <?php echo $view['form']->row($f); ?>
                </div>
            </div>
        <?php endforeach; ?>
    </div>
</div>

Registering a formTheme as HelloWorldBundle:FormTheme\Config in the event listener told the ConfigBundle to look in the HelloWorldBundle’s Views/FormTheme/Config folder for templates. Specifically, it will look for a template named _config_{formAlias}_widget.html.php where {formAlias} is the same as formAlias set in the plugin’s ConfigEvents::CONFIG_ON_GENERATE event listener.

The template should be in a panel format to match the rest of the config UI.

Manipulating Contacts (Leads)

<?php

/** @var \Mautic\LeadBundle\Model\LeadModel $leadModel */
$leadModel = $this->getModel('lead');

/** @var \Mautic\LeadBundle\Entity\Lead $currentLead */
$currentLead = $leadModel->getCurrentLead();

// To obtain the tracking ID as well, pass true
list($currentLead, $trackingId, $trackingIdIsNewlyGenerated) = $leadModel->getCurrentLead(true);

// To obtain just the tracking ID; pass true as an argument to force regeneration of the tracking ID and cookies
list($trackingId, $trackingIdIsNewlyGenerated) = $leadModel->getTrackingCookie();

// Set the currently tracked lead and generate tracking cookies
$lead = new Lead();
// ...
$leadModel->setCurrentLead($lead);

// Set a lead for system use purposes (i.e. events that use getCurrentLead()) but without generating tracking cookies
$leadModel->setSystemCurrentLead($lead);

Many plugins extending Mautic will be manipulating leads in one way or another. Here is a quick summary of how to obtain the current lead and/or manipulate the leads data.

Lead Tracking

Leads are tracked by two cookies. The first cookie notes which ID the lead is tracked under Mautic as. The second is to track the lead’s activity for the current session (defaults to 30 minutes and resets during each lead interaction).

mautic_session_id holds the value of the lead’s current session ID. That value is then name of the cookie that holds the lead’s ID.

Review the sample code on how to obtain the currently tracked lead.

As of Mautic 2.2.0, a cookie is also placed on any domain with mtc.js embedded (that’s allowed by Mautic’s CORS settings) as mtc_id. This will contain the ID of the currently tracked contact.

Creating New Leads

<?php
// Currently tracked lead based on cookies
$leadModel = $this->getModel('lead');
$lead = $leadModel->getCurrentLead();
$leadId = $lead->getId();

// OR generate a completely new lead with
$lead = new Lead();
$lead->setNewlyCreated(true);
$leadId = null;

// IP address of the request
$ipAdddress = $this->get('mautic.helper.ip_lookup')->getIpAddress();

// Updated/new fields
$leadFields = array(
    'firstname' => 'Bob',
    //...
);

// Optionally check for identifier fields to determine if the lead is unique
$uniqueLeadFields    = $this->getModel('lead.field')->getUniqueIdentiferFields();
$uniqueLeadFieldData = array();

// Check if unique identifier fields are included
$inList = array_intersect_key($leadFields, $availableLeadFields);
foreach ($inList as $k => $v) {
    if (empty($query[$k])) {
        unset($inList[$k]);
    }

    if (array_key_exists($k, $uniqueLeadFields)) {
        $uniqueLeadFieldData[$k] = $v;
    }
}

// If there are unique identifier fields, check for existing leads based on lead data
if (count($inList) && count($uniqueLeadFieldData)) {
    $existingLeads = $this->getDoctrine()->getManager()->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields(
        $uniqueLeadFieldData,
        $leadId // If a currently tracked lead, ignore this ID when searching for duplicates
    );
    if (!empty($existingLeads)) {
        // Existing found so merge the two leads
        $lead = $leadModel->mergeLeads($lead, $existingLeads[0]);
    }

    // Get the lead's currently associated IPs
    $leadIpAddresses = $lead->getIpAddresses();

    // If the IP is not already associated, do so (the addIpAddress will automatically handle ignoring
    // the IP if it is set to be ignored in the Configuration)
    if (!$leadIpAddresses->contains($ipAddress)) {
        $lead->addIpAddress($ipAddress);
    }
}

// Set the lead's data
$leadModel->setFieldValues($lead, $leadFields);

// Save the entity
$leadModel->saveEntity($lead);

// Set the updated lead
$leadModel->setCurrentLead($lead);

To create a new lead, use the \Mautic\LeadBundle\Entity\Lead entity. Review the code sample.

Extending Mautic

Extending API

<?php
// plugins/HelloWorldBundle/Config/config.php

return array(
    // ...

    'routes'   => array(

        // ...

        'api' => array(
            'plugin_helloworld_api' => array(
                'path'       => '/hello/worlds',
                'controller' => 'HelloWorldBundle:Api:worlds',
                'method'     => 'GET'
            )
        )
    ),

    // ...
);
<?php
// plugins/HelloWorldBundle/Controller/ApiController.php

namespace Mautic\LeadBundle\Controller\Api;

use FOS\RestBundle\Util\Codes;
use Mautic\ApiBundle\Controller\CommonApiController;

class ApiController extends CommonApiController
{

    /**
     * Get a list of worlds
     * 
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function getWorldsAction()
    {
        if (!$this->get('mautic.security')->isGranted('plugin:helloWorld:worlds:view')) {
            return $this->accessDenied();
        }

        $filter  = $this->request->query->get('filter', null);
        $limit   = $this->request->query->get('limit', null);
        $start   = $this->request->query->get('start', null);

        /** @var \MauticPlugin\HelloWorldBundle\Model\WorldsModel $model */
        $model   = $this->getModel('helloworld.worlds');

        $worlds  = $model->getWorlds($filter, $limit, $start);
        $worlds  = $this->view($worlds, Codes::HTTP_OK);

        return $this->handleView($worlds);
    }
}

To add custom API endpoints, simply define the routes under the API firewall in the plugin’s config file. This will place the route behind /api which will only be accessible if the requester has been authorized via OAuth.

The api controller(s), should extend Mautic\ApiBundle\Controller\CommonApiController to leverage the helper methods provided and to utilize the REST views.

Extending Broadcasts

Broadcasts are communications sent in bulk through a channel such as email (segment emails). Mautic 2.2.0 introduced a new event to execute the sending of these bulk communications via the mautic:broadcasts:send command.

<?php
// plugins\HelloWorldBundle\EventListener\BroadcastSubscriber

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\CoreEvents;
use Mautic\CoreBundle\Event\ChannelBroadcastEvent;
use MauticPlugin\HelloWorldPlugin\Model\WorldModel;

/**
 * Class BroadcastSubscriber
 */
class BroadcastSubscriber extends CommonSubscriber
{
    /**
     * @var WorldModel
     */
    protected $model;

    /**
     * BroadcastSubscriber constructor.
     *
     * @param WorldModel $model
     */
    public function __construct(WorldModel $model)
    {
        $this->model = $model;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            CoreEvents::CHANNEL_BROADCAST => ['onChannelBroadcast', 0]
        ];
    }


    public function onDataCleanup (ChannelBroadcastEvent $event)
    {
        if (!$event->checkContext('world')) {
            return;
        }

        // Get list of published broadcasts or broadcast if there is only a single ID
        $id         = $event->getId();
        $broadcasts = $this->model->getRepository()->getPublishedBroadcasts($id);
        $output     = $event->getOutput();

        while (($broadcast = $broadcasts->next()) !== false) {
            list($sentCount, $failedCount, $ignore) = $this->model->sendIntergalacticMessages($broadcast[0], null, 100, true, $output);
            $event->setResults($this->translator->trans('plugin.helloworld').': '.$broadcast[0]->getName(), $sentCount, $failedCount);
        }
    }
}

To hook into the mautic:broadcasts:send command, create a listening for the \Mautic\CoreBundle\CoreEvents::CHANNEL_BROADCAST event. The event listener should check for the appropriate context and ID. See example code.

Extending Campaigns

<?php
// plugins/HelloWorldBundle/EventListener/CampaignSubscriber.php

namespace MauticPlugin\HelloWorldBundle\Events;

use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\CampaignBundle\Event as Events;
use Mautic\CampaignBundle\CampaignEvents;
use Mautic\CampaignBundle\Event\CampaignExecutionEvent;

/**
 * Class CampaignSubscriber
 */
class CampaignSubscriber extends CommonSubscriber
{

    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return array(
            CampaignEvents::CAMPAIGN_ON_BUILD => array('onCampaignBuild', 0),
            HelloWorldEvents::BLASTOFF        => array('executeCampaignAction', 0),
            HelloWorldEvents::VALIDATE_VISIT  => array('validateCampaignDecision', 0)
        );
    }

    /**
     * Add campaign decision and actions
     *
     * @param Events\CampaignBuilderEvent $event
     */
    public function onCampaignBuild(Events\CampaignBuilderEvent $event)
    {
        // Register custom action
        $event->addAction(
            'helloworld.send_offworld',
            array(
                'eventName'       => HelloWorldEvents::BLASTOFF,
                'label'           => 'plugin.helloworld.campaign.send_offworld',
                'description'     => 'plugin.helloworld.campaign.send_offworld_descr',
                // Set custom parameters to configure the decision
                'formType'        => 'helloworld_worlds',
                // Set a custom formTheme to customize the layout of elements in formType
                'formTheme'       => 'HelloWorldBundle:FormTheme\SubmitAction',
                // Set custom options to pass to the form type, if applicable
                'formTypeOptions' => array(
                    'world' => 'mars'
                )
            )
        );

        // Register custom decision (executes when a lead "makes a decision" i.e. executes some direct action
        $event->addDecision(
            'helloworld.visits_mars',
            array(
                'eventName'       => HelloWorldEvents::VALIDATE_VISIT,
                'label'           => 'plugin.helloworld.campaign.visits_mars',
                'description'     => 'plugin.helloworld.campaign.visits_mars_descr',
                // Same as registering an action
                'formType'        => false,
                'formTypeOptions' => array()
            )
        );
    }

    /**
     * Execute campaign action
     *
     * @param CampaignExecutionEvent $event
     */
    public function executeCampaignAction (CampaignExecutionEvent $event)
    {
        // Do blastoff

        $event->setResult(true);
    }

    /**
     * Validate campaign decision
     *
     * @param CampaignExecutionEvent $event
     */
    public function validateCampaignDecision (CampaignExecutionEvent $event)
    {
        $valid = ($event->getEventDetails()->getId() === $event->getConfig()['id']);
        $event->setResult($valid);
    }
}

Plugins can add their own campaign actions, decisions, or conditions by listening to the \Mautic\CampaignBundle\CampaignEvents::CAMPAIGN_ON_BUILD event. Read more about listeners and subscribers.

Campaign Actions

To add an action, use the $event->addAction($identifier, $parameters) method. $identifier must be something unique. The $parameters array can contain the following elements:

Key Required Type Description
label REQUIRED string The language string to use for the draggable’s label
eventName REQUIRED string The name of the event that should be dispatched to handle this action. The plugin will need to also create it’s own listener for the event.
description OPTIONAL string The language string to use for the draggable’s tooltip
formType OPTIONAL string The alias of a custom form type used to set config options for the decision
formTypeOptions OPTIONAL array Array of options to include into the formType’s $options argument
formTypeCleanMasks OPTIONAL array Array of input masks to clean a values from formType
formTypeTheme OPTIONAL string Theme to customize elements for formType
associatedDecisions OPTIONAL array Array of keys registered as decisions that this action can attached to. Defaults to any decision.
anchorRestrictions OPTIONAL array Array of anchors (the places on an event in the builder this action can be attached to). The format is eventType.anchorName. Event types can be source, decision, action, or condition. anchorName includes top, bottom, inaction (yes/green), action (no/red) or leadsource. For example, by passing an array with decision.inaction, this action will not be attachable to the inaction/red anchor of a decision.
callback DEPRECATED mixed Deprecated as of 2.0 and support to be removed in 3.0; use eventName instead. Static callback function that will be called for the action and should contain the logic to execute the custom action

The listener for dispatched event will have the Mautic\CampaignBundle\Event\CampaignExecutionEvent injected. To note that the action was successfully executed, use $event->setResult($result). $result can be a boolean or an array. Setting false will cause the action to be retried. If an array, it will be stored in the campaign event log’s metadata array (useful for displaying information in the contact time-line).

Use $event->setFailed() to note that an event failed but should not be retried. Failed events do not appear in a contact’s time-line.

Campaign Decisions

 <?php
 // Trigger configured 'helloworld.visits_mars' decisions

 /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */
 $campaignModel = $this->getModel('campaign.event');

 // Can be anything and passed into action callbacks as $eventDetails
 $customPassthrough = array();

 // Some optional unique identifier for this specific trigger that is used mainly for debug logging; for example, can be a concatenation of the decision name + lead ID
 $uniqueTriggerId   = 'something_something';

 $campaignModel->triggerEvent('helloworld.visits_mars', $customPassthroughToActions, $uniqueTriggerId);

Campaign decisions are registered exactly as a campaign action except it uses the $event->addLeadDecision($identifier, $parameters) method. The only difference in the $parameters arguments is that the listener for the eventName is used to validate the decision rather than execute some action and it accepts a associatedActions array instead of associatedDecisions. For example, if the decision is configured to only apply to a specific ID chosen by the user (defined in the formType), the listener could compare the decision’s $event->getEventDetails()->getId() (see example) with the event’s$event->getConfig()['id']. If the decision should execute the actions associated with it, set $event->setResult(true);. Otherwise $event->setResult(false); and nothing will be executed or logged.

For custom decisions to work, there must be a trigger executed when the lead makes the decision. Thus, where ever is appropriate in the plugin’s code logic, add something similar to what’s in the example code block.

The triggerEvent() method will pull all the triggered decisions (helloworld.visits_mars in the code example) for published campaigns the lead is in, dispatch the decisions event (if configured) for validation, then execute the associated actions if appropriate.

Campaign Conditions

Campaign conditions are registered with addCondition() and accepts the same arguments as addDecision(). The listener also receives an instance of Mautic\CampaignBundle\Event\CampaignExecutionEvent. To mark a condition as true or false, use $event->setResult($result);.

Extending Categories

Mautic has a CategoryBundle that can be leveraged to incorporate categories into a plugin.

Adding categories

As of Mautic 1.2.1, register categories through the plugin’s config.php file by adding the following as a key to the returned config array:

    'categories' => array(
        'plugin:helloWorld' => 'mautic.helloworld.world.categories'
    ),

The category keys need be prefixed with plugin: as it is used in determining permissions to manage categories. The helloWorld should match the permission class name.

Configuring Categories for Menu

It is now recommended to not show the category in the main Menu.

To add a category menu item for the plugin, simply add the following to menu config for whichever menu the item should appear (main or admin):

    'mautic.category.menu.index' => array(
        'bundle' => 'plugin:helloWorld'
    )

The bundle value needs be prefixed with plugin: as it is used in determining permissions to manage categories. The helloWorld should be the bundle name of the plugin.

Configuring Categories for Routes

There is no need to add custom routes for categories. However, when generating a URL to the plugin’s category list, use

$categoryUrl = $router->generateUrl('mautic_category_index', array('bundle' => 'plugin:helloWorld'));

Including Category in Forms

To add a category select list to a form, use category as the form type and pass bundle as an option:

    //add category
    $builder->add('category', 'category', array(
        'bundle' => 'plugin:helloWorld'
    ));

Restricting Category Management

To restrict access to catgories, use the following in the plugin’s Permission class.

In __construct() add $this->addStandardPermissions('categories'); then in buildForm(), add $this->addStandardFormFields('helloWorld', 'categories', $builder, $data);.

See a code example in Roles and Permissions.

The two standard helper methods will add the permissions of view, edit, create, delete, publish, and full for categories.

Extending Contacts (Leads)

Contact Timeline/History

<?php
// plugins/HelloWorldBundle/EventListener/LeadSubscriber.php

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\LeadBundle\Event\LeadTimelineEvent;
use Mautic\LeadBundle\LeadEvents;

/**
 * Class LeadSubscriber
 */
class LeadSubscriber extends CommonSubscriber
{

    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return [
            LeadEvents::TIMELINE_ON_GENERATE => ['onTimelineGenerate', 0]
        ];
    }

    /**
     * Compile events for the lead timeline
     *
     * @param LeadTimelineEvent $event
     */
    public function onTimelineGenerate(LeadTimelineEvent $event)
    {
        // Add this event to the list of available events which generates the event type filters
        $eventTypeKey  = 'visited.worlds';
        $eventTypeName = $this->translator->trans('mautic.hello.world.visited_worlds');
        $event->addEventType($eventTypeKey, $eventTypeName);

        // Determine if this event has been filtered out
        if (!$event->isApplicable($eventTypeKey)) {

            return;
        }

        /** @var \MauticPlugin\HelloWorldRepository\Entity\WorldRepository $repository */
        $repository = $this->em->getRepository('HelloWorldBundle:World');

        // $event->getQueryOptions() provide timeline filters, etc.
        // This method should use DBAL to obtain the events to be injected into the timeline based on pagination
        // but also should query for a total number of events and return an array of ['total' => $x, 'results' => []].
        // There is a TimelineTrait to assist with this. See repository example.
        $stats = $repository->getVisitedWorldStats($event->getLead()->getId(), $event->getQueryOptions());

        // If isEngagementCount(), this event should only inject $stats into addToCounter() to append to data to generate
        // the engagements graph. Not all events are engagements if they are just informational so it could be that this
        // line should only be used when `!$event->isEngagementCount()`. Using TimelineTrait will determine the appropriate
        // return value based on the data included in getQueryOptions() if used in the stats method above.
        $event->addToCounter($eventTypeKey, $stats);

        if (!$event->isEngagementCount()) {
            // Add the events to the event array
            foreach ($stats['results'] as $stat) {
                if ($stat['dateSent']) {
                    $event->addEvent(
                        [
                            // Event key type
                            'event'           => $eventTypeKey,
                            // Event name/label - can be a string or an array as below to convert to a link
                            'eventLabel'      => [
                                'label' => $stat['name'],
                                'href'  => $this->router->generate(
                                    'mautic_dynamicContent_action',
                                    ['objectId' => $stat['dynamic_content_id'], 'objectAction' => 'view']
                                )
                            ],
                            // Translated string displayed in the Event Type column
                            'eventType'       => $eventTypeName,
                            // \DateTime object for the timestamp column
                            'timestamp'       => $stat['dateSent'],
                            // Optional details passed through to the contentTemplate
                            'extra'           => [
                                'stat' => $stat,
                                'type' => 'sent'
                            ],
                            // Optional template to customize the details of the event in the timeline
                            'contentTemplate' => 'MauticDynamicContentBundle:SubscribedEvents\Timeline:index.html.php',
                            // Font Awesome class to display as the icon
                            'icon'            => 'fa-envelope'
                        ]
                    );
                }
            }
        }
    }
}
TIMELINE_ON_GENERATE Event Listener

To inject events into a contact’s timeline, create an event listener that listens to the LeadEvents::TIMELINE_ON_GENERATE event. Using this event, the plugin can inject unique items into the timeline and also into the engagements graph on each page.

The event listener will receive a Mautic\LeadBundle\Event\LeadTimelineEvent object. The commonly used methods are defined below:

Method Description
isApplicable Determines if this event is applicable and not filtered out.
addEventType() Required - Add this event to the list of available events.
getLead() Get the Lead entity the event is dispatched for
getQueryOptions() Used to get pagination, filters, etc needed to generate an appropriate query
addToCounter() Used to add total number of events (across all pages) to the counters. This also generates the numbers for the engagements graph.
addEvent() Required - Injects an event into the timeline. Accepts an array with the keys defined as below.

addEvent($event) Key Definitions Key|Required|Type|Description —|——–|—-|———– event|REQUIRED|string|The key for this event. Eg. world.visited eventType|REQUIRED|string|The translated string representing this event type. Eg. Worlds visited timestamp|REQUIRED|\DateTime|DateTime object when this event took place eventLabel|OPTIONAL|string/array|The translated string to display in the event name. Examples include names of items, page titles, etc. This can also be an array of [‘label’ => “, 'href’ => ”] to have the entry converted to a link. This will default to eventType if not defined. extra|OPTIONAL|array|Whatever should be passed through to the content template to generate the details view for this event contentTemplate|OPTIONAL|string|Template that should be used to generate the details view for this event. Eg. HelloBundle:SubscribedEvents\Timeline:index.html.php icon|OPTIONAL|Font Awesome class

TIMELINE_ON_GENERATE Repository Method
<?php
//plugins/HelloWorldBundle/Entity/WorldRepository.php

namespace Mautic\LeadBundle\Entity;

use Mautic\CoreBundle\Entity\CommonRepository;
use Mautic\LeadBundle\Entity\TimelineTrait;

/**
 * Class WorldRepository
 */
class WorldRepository extends CommonRepository
{
    use TimelineTrait;

    /**
     * @param       $leadId
     * @param array $options
     */
    public function getTimelineStats($leadId, array $options = [])
    {
        $query = $this->getEntityManager()->getConnection()->createQueryBuilder();

        $query->select('w.id, w.name, w.visited_count, w.date_visited, w.visit_details')
            ->from(MAUTIC_TABLE_PREFIX.'world_visits', 'w')
            ->where($query->expr()->eq('w.lead_id', (int) $leadId));

        if (isset($options['search']) && $options['search']) {
            $query->andWhere(
                $query->expr()->like('w.name', $query->expr()->literal('%' . $options['search'] . '%'))
            );
        }

        return $this->getTimelineResults($query, $options, 'w.name', 'w.date_visited', ['visit_details'], ['date_visited']);
    }
}

To assist with the generation of events, the Mautic\LeadBundle\Entity\TimelineTrait has been created.

To leverage this, accept the array from $event->getQueryOptions() in the repository method. Create a DBAL QueryBuilder object ($this->getEntityManager()->getConnection()->createQueryBuilder()) and define the basics of the array, including filtering by lead id and search filter. Then pass the QueryBuilder object to the getTimelineResults() method along with the following arguments:

Key Required Type Description
$query REQUIRED QueryBuilder DBAL QueryBuilder object defining basics of the query.
$options REQUIRED array Array generated and passed into method by $event->getQueryOptions() in the event listener above
$eventNameColumn REQUIRED string Name of the column (with table prefix) that should be used when sorting by event name
$timestampColumn REQUIRED string Name of the column (with table prefix) that should be used when sorting by timestamp
$serializedColumns OPTIONAL array When using DBAL, arrays are not auto unserialized by Doctrine. Define the columns here (as returned by the query results) to auto unserialize.
$dateTimeColumns OPTIONAL array When using DBAL, datetime columns are not auto converted to \DateTime objects by Doctrine. Define the columns here (as returned by the query results) to auto do so.
$resultsParserCallback OPTIONAL callback Callback to custom parse a result. This is optional and mainly used to handle a column result when all results are already being looped over for $serializedColumns and $dateTimeColumns.

Extending Emails

<?php
// plugins/HelloWorldBundle/EventListener/EmailSubscriber.php

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Event\EmailBuilderEvent;
use Mautic\EmailBundle\Event\EmailSendEvent;

/**
 * Class EmailSubscriber
 */
class EmailSubscriber extends CommonSubscriber
{

    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return array(
            EmailEvents::EMAIL_ON_BUILD   => array('onEmailBuild', 0),
            EmailEvents::EMAIL_ON_SEND    => array('onEmailGenerate', 0),
            EmailEvents::EMAIL_ON_DISPLAY => array('onEmailGenerate', 0)
        );
    }

    /**
     * Register the tokens and a custom A/B test winner
     *
     * @param EmailBuilderEvent $event
     */
    public function onEmailBuild(EmailBuilderEvent $event)
    {
        // Add email tokens
        $content = $this->templating->render('HelloWorldBundle:SubscribedEvents\EmailToken:token.html.php');
        $event->addTokenSection('helloworld.token', 'plugin.helloworld.header', $content);

        // Add AB Test Winner Criteria
        $event->addAbTestWinnerCriteria(
            'helloworld.planetvisits',
            array(
                // Label to group by
                'group'    => 'plugin.helloworld.header',

                // Label for this specific a/b test winning criteria
                'label'    => 'plugin.helloworld.emailtokens.',

                // Static callback function that will be used to determine the winner
                'callback' => '\MauticPlugin\HelloWorldBundle\Helper\AbTestHelper::determinePlanetVisitWinner'
            )
        );
    }

    /**
     * Search and replace tokens with content
     *
     * @param EmailSendEvent $event
     */
    public function onEmailGenerate(EmailSendEvent $event)
    {
        // Get content
        $content = $event->getContent();

        // Search and replace tokens
        $content = str_replace('{hello}', 'world!', $content);

        // Set updated content
        $event->setContent($content);
    }
}

There are two way to extend emails: email tokens used to insert dynamic content into an email and a/b test winning criteria . Both leverage the \Mautic\EmailBundle\EmailEvents::EMAIL_ON_BUILD event. Read more about listeners and subscribers.

Email Tokens

Email tokens are placeholders that are inserted into an email that can be replaced by dynamic content when the email is sent or viewed in the browser.

$event->addTokenSection($uniqueId, $headerTranslationKey, $htmlContent) is used to generate the section for drag and drop tokens in the email builder.

$uniqueId must be unique.

$headerTranslationKey is the translation key that will be used to create the section’s header.

$htmlContent is the HTML that will be inserted into the builder’s token list for this token’s section. $this->templating->render() can be used to render a specific view’s content (using view notation. There is free reign as to what the HTML will look like but the important part is that the elements representing the tokens must have the attribute data-token="{token_text}" in order for the builder to recognize them.

For example, <a href="#" data-token="{hello}" class="btn btn-default btn-block">Translated Token Text</a>

Custom Token Handling

To convert the token into a link while requesting what the links text should be, use the attributes data-token='<a href="%url={hello}%">%text%</a>' data-drop="showBuilderLinkModal" (replacing hello with the plugin’s custom token).

To request simple feedback from the user and inject it into the token, use the attributes data-token='{hello=%world%}' data-drop="showBuilderFeedbackModal". A modal will appear with a simple input box. Whatever the user inputs will replace %world%.

If you need more control or more customization, create and define a custom JS function within the Mautic namespace (i.e. Mautic.customFunction and use the attribute data-drop="customFunction". When a user drops the token, Mautic.customFunction() passing the arguments event (jQuery Event), ui (jQuery UI object with draggable, helper, etc), editorId (ID of the inline CkEditor).

Mautic.customFunction = function (event, ui, editorId) {
    var token  = mQuery(ui.draggable).data('token');
    
    // Do something fancy then insert token
        
    Mautic.insertBuilderEditorToken(editorId, token);
};

Email A/B Test Winner Criteria

To add a custom an a/b test winner criteria, i.e. a test to compare specific aspects of an email to determine what made it more successful over it’s variants, use $event->addAbTestWinnerCriteria($uniqueId, $parameters).

$parameters can have the following elements:

Key Required Type Description
group REQUIRED string Translation string to group criteria by in the dropdown select list
label OPTIONAL string Label for this option in the drop down list
callback REQUIRED mixed Static callback function that will be called to determine the winner when the email detail page is viewed
formType OPTIONAL string The alias of a custom form type used to set config options for the decision
formTypeOptions OPTIONAL array Array of options to include into the formType’s $options argument
formTypeCleanMasks OPTIONAL array Array of input masks to clean a values from formType
formTypeTheme OPTIONAL string Theme to customize elements for formType

The callback can accept the following variables (determined via ReflectionMethod::invokeArgs()):

Variable Type Description
$properties array Array of elements saved from the configured formType; keyed by email ID in the case of multiple variants
$factory Mautic\CoreBundle\Factory\MauticFactory Mautic’s factory service
$email Mautic\EmailBundle\Entity\Email Email entity for the displayed email
$parent Mautic\EmailBundle\Entity\Email Email entity for the parent of the email entity
$children Doctrine\Common\Collections\ArrayCollection All variants of the parent email

The callback function should return an array keyed with the following elements:

Key Type Description
winners array Array of IDs of the winners (array in case of a tie)
support mixed Passed to the view defined by supportTemplate below in order to render visual support for the winners (such as a graph, etc)
supportTemplate string View notation to render content for the A/B stats modal. For example, HelloWorldBundle:SubscribedEvents\AbTest:graph.html.php
return array(
   'winners'         => $winners,
   'support'         => $support,
   'supportTemplate' => 'HelloWorldBundle:SubscribedEvents\AbTest:graph.html.php'
);

Monitored Inbox Integration

<?php
// plugins/HelloWorldBundle/EventListener/MonitoredInboxSubscriber.php

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Event\MonitoredEmailEvent;
use Mautic\EmailBundle\Event\ParseEmailEvent;
use Mautic\EmailBundle\MonitoredEmail\Mailbox;

/**
 * Class MonitoredInboxSubscriber
 */
class MonitoredInboxSubscriber extends CommonSubscriber
{
    private $bundle = 'HelloWorldBundle';
    private $monitor =  'deep_space_emails';

    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return [
            EmailEvents::MONITORED_EMAIL_CONFIG => ['onConfig', 0],
            EmailEvents::EMAIL_PRE_FETCH        => ['onPreFetch', 0],
            EmailEvents::EMAIL_PARSE            => ['onParse', 0],
        ];
    }

    /**
     * Inject the IMAP folder settings into the Configuration
     *
     * @param MonitoredEmailEvent $event
     */
    public function onConfig(MonitoredEmailEvent $event)
    {
        /**
         * The first argument is something unique to recognize this plugin.
         * The second argument should be something unique to identify this monitored inbox.
         * The third argument is the label for this monitored inbox.
         */
         $event->addFolder($this->bundle, $this->monitor, 'mautic.world.monitored_deep_space_emails');
    }

    /**
     * Inject search criteria for which messages to fetch from the configured folder.
     *
     * @param ParseEmailEvent $event
     */
    public function onPreFetch(ParseEmailEvent $event)
    {
        $event->setCriteriaRequest($this->bundle, $this->monitor, Mailbox::CRITERIA_UNSEEN. " " . Mailbox::CRITERIA_FROM ." aliens@andromeda");
    }

    /**
     * Parse the messages
     *
     * @param ParseEmailEvent $event
     */
    public function onParse(ParseEmailEvent $event)
    {
        if ($event->isApplicable($this->bundle, $this->monitor)) {
            $messages = $event->getMessages();

            /** @var \Mautic\EmailBundle\MonitoredEmail\Message $message */
            foreach ($messages as $message) {
                // Do something
            }
        }
    }
}

Plugins have access to hook into the mautic:email:fetch command to fetch email from a specific inbox/folder and process the content of the message. Starting in 2.1.1, the plugin also has access to inject specific search criteria for the messages to be processed.

To do this, the plugin needs to add an event listener for three events:

  1. EmailEvents::MONITORED_EMAIL_CONFIG This event is dispatched to inject the fields into Mautic’s Configuration to configure the IMAP inbox and folder that should be monitored.
  2. EmailEvents::EMAIL_PRE_FETCH This event is dispatched during the execution of the mautic:email:fetch command. It’s used to inject search criteria for the messages desired.
  3. EmailEvents::EMAIL_PARSE This event parses the messages fetched by the command.

Extending Forms

<?php
// plugins/HelloWorldBundle/EventListener/FormSubscriber.php

namespace MauticPlugin\HelloWorldBundle\EventListener;

use MauticPlugin\HelloWorldBundle\HelloWorldEvents;
use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\FormBundle\Event as Events;
use Mautic\FormBundle\FormEvents;

/**
 * Class FormSubscriber
 */
class FormSubscriber extends CommonSubscriber
{

    /**
     * {@inheritdoc}
     */
    static public function getSubscribedEvents()
    {
        return array(
            FormEvents::FORM_ON_BUILD => array('onFormBuilder', 0)
        );
    }

    /**
     * Add a simple email form
     *
     * @param FormBuilderEvent $event
     */
    public function onFormBuilder(Events\FormBuilderEvent $event)
    {
        // Register a form submit actions
        $event->addSubmitAction(
            'helloworld.sendemail',
            [
                // Label to group by in the dropdown
                'group'       => 'plugin.helloworld.header',

                // Label to list by in the dropdown
                'label'       => 'plugin.helloworld.formaction.send_email',
                'description' => 'plugin.helloworld.formaction.send_email_descr',

                // Form service for custom config options
                'formType'    => 'helloworld_worlds',
                'formTheme'   => 'HelloWorldBundle:FormTheme\SubmitAction',

                // Callback method to be executed after the submission 
                'eventName'    => HelloWorldEvents::FORM_SUBMIT_ACTION
            ]
        );

        // Register a custom validation service
        $event->addValidator(
            'helloworld.customfield',
            [
                'eventName' => HelloWorldEvents::FORM_VALIDATION,
                'fieldType' => 'helloworld.customfield' // Optional - otherwise all fields will be sent through this listener for validation
            ]
        );

        // Register a custom form field
        $event->addFormField(
            'helloworld.customfield',
            [
                // Field label
                'label'    => 'plugin.helloworld.formfield.customfield',

                // Form service for the field's configuration
                'formType' => 'helloworld_worlds',

                // Template to use to render the formType
                'template' => 'HelloWorldBundle:SubscribedEvents\FormField:customfield.html.php'
            ]
        );
    }
}

Forms can be extended by listening to the \Mautic\FormBundle\FormEvents::FORM_ON_BUILD event. Read more about listeners and subscribers.

Form Fields

To add a custom form field, use the $event->addFormField($identifier, $parameters) method. $identifier must be something unique. The $parameters array can contain the following elements:

Key Required Type Description
label REQUIRED string The language string for the option in the dropdown
formType REQUIRED string The alias of a custom form type used to set config options
formTypeOptions OPTIONAL array Array of options to include into the formType’s $options argument
formTypeCleanMasks OPTIONAL array Array of input masks to clean a values from formType
formTypeTheme OPTIONAL string Theme to customize elements for formType
template REQUIRED string View template used to render the formType
valueFilter OPTIONAL mixed Filter to use to clean the submitted value as supported by InputHelper or a callback function that accepts the arguments \Mautic\FormBundle\Entity\Field $field and $value.
valueConstraints OPTIONAL mixed Callback function to use to validate the value; the function should accept the arguments \Mautic\FormBundle\Entity\Field $field and $filteredValue.
builderOptions OPTIONAL array Array of boolean options for the form builder:
addHelpMessage = true/false
addShowLabel = true/false
addDefaultValue = true/false
addLabelAttributes = true/false
addInputAttributes = true/false
addIsRequired = true/false

Form Submit Actions

To add an action, use the $event->addSubmitAction($identifier, $parameters) method. $identifier must be something unique. The $parameters array can contain the following elements:

Key Required Type Description
label REQUIRED string The language string for the option in the dropdown
description OPTIONAL string The language string to use for the option’s tooltip
eventName REQUIRED string This is the custom event name that will be dispatched to handle this action (callback has been deprecated)
formType OPTIONAL string The alias of a custom form type used to set config options
formTypeOptions OPTIONAL array Array of options to include into the formType’s $options argument
formTypeCleanMasks OPTIONAL array Array of input masks to clean a values from formType
formTypeTheme OPTIONAL string Theme to customize elements for formType
template OPTIONAL string View template used to render the formType
validator DEPRECATED mixed Static callback function called to validate the form submission. Deprecated - Register a validator using the $event->addValidator().
callback DEPRECATED mixed Static callback function called after a submission (submit action logic goes here). Deprecated - use eventName instead.

The subscriber registered to listen to the eventName will be passed an instance of Mautic\FormBundle\Events\SubmissionEvent with the details about the post.

Sometimes, it is necessary to handle something after all the other submit actions have done their thing - like redirect to another page. This is done by registering a post submit callback through the subscriber that processes the action. You can either inject the Symfony\Component\HttpFoundation\Response at that time with $event->setPostSubmitCallbackResponse($response); or register another custom event to be dispatched after all submit actions have been processed using $event->setPostSubmitCallback($key, ['eventName' => HelloWorld::ANOTHER_CUSTOM_EVENT]);.

Form Validations

To add a custom validation, use the $event->addValidator($identifier, $parameters) method. $identifier must be something unique. The $parameters array can contain the following elements:

Key Required Type Description
eventName REQUIRED string The name of the custom event that will be dispatched to validate the form or specific field
fieldType optional string The key to a custom form type (for example something registered by addFormField()) to limit this listener to. Otherwise every field will be sent to listener.

The listener for the form event will receive a Mautic\FormBundle\Event\ValidationEvent object. Obtain the field with $event->getField(); do the logic then to fail a validation, execute $event->failedValidation('I said so.');.

Extending Integrations

<?php
<?php
// plugins\HelloWorldBundle\Integration\MarsIntegration

namespace MauticPlugin\HelloWorldBundle\Integration;

use Mautic\PluginBundle\Entity\Integration;
use Mautic\PluginBundle\Integration\AbstractIntegration;
use Mautic\PluginBundle\Helper\oAuthHelper;

/**
 * Class MarsIntegration
 */
class MarsIntegration extends AbstractIntegration
{
    /**
     * Returns the name of the social integration that must match the name of the file
     * For example, IcontactIntegration would need Icontact here
     *
     * @return string
     */
    public function getName()
    {
        return 'Mars';
    }

    /**
     * Display name for the integration which defaults to getName() unless defined here
     */
    public function getDisplayName()
    {
        return 'Red Mars'
    }

    /**
     * Get the type of authentication required for this API.  Values can be none, key, oauth2 or callback
     * (will call $this->authenticationTypeCallback)
     *
     * @return string
     */
    public function getAuthenticationType()
    {
        return 'oauth2';
    }

    /**
     * OAuth2 authentication URL
     */
    public function getAuthenticationUrl()
    {
        return 'https://somesite.com/oauth/authorize';
    }

    /**
     * OAuth2 access token URL
     */
    public function getAccessTokenUrl()
    {
        return 'https://somesite.com/oauth/access_token';
    }

    /**
     * Get a list of supported features for this integration
     *
     * @return array
     */
    public function getSupportedFeatures()
    {
        return array(
            'public_profile',
            'public_activity'
        );
    }
}

Integrating 3rd party services in Mautic can be done by defining an Integration class for the service. For example, the MauticSocialBundle has several social media service classes defined in \Plugins\MauticSocialBundle\Integration. Each integration class handles the authorization process, integration configuration, etc.

Integration Class

Each plugin can have multiple integrations by defining each as it’s own Integration class in the bundle’s Integration folder. The class should extend \Mautic\PluginBundle\Integration\AbstractIntegration. It defines the integration and provides a number of helper functions including OAuth authorization/request signing functions.

Integration Image

Each integration is displayed on a “card” in the Manage Plugins area. To set an image for the integration, include an image in the bundle’s Assets\img. It should be 128x128px, be in a png, and have the same name as returned by getName() as lower case. For example, \MauticPlugin\HelloWorldBundle\Integration\MarsIntegration should have an image plugins\HelloWorldBundle\Assets\img\mars.png.

Authorization

Out of the box, the AbstractIntegration class can handle standard key, OAuth1a, and OAuth2 specifications. The authorization type is defined by the getAuthenticationType() function. Each input required by the user (i.e username, password, etc) are defined by an array of keyName => label elements returned by getRequiredKeyFields(). This function is not required if using standard specs of key, OAuth1a, or OAuth2.

Functions

Some of the main functions used are described below. Review the AbstractIntegration class and the functions docblocks for more details.

Keys saved by the integration are encrypted. To access the unencrypted versions in the Integration class, use the array $this->keys.

Any of the functions defined in AbstractIntegration can be overridden per special needs of the specific Integration being implemented.

Area Function Description
Auth getRequiredKeyFields Returns an array of keyName => label elements for settings required from the user, i.e. username, password, client id, client secret, key, etc. Each element will be displayed as an input in the integration’s settings.
Auth getSecretKeys Any keyName returned by getRequiredKeyFields that is considered secret or a password should be returned in an array by this function so that it’s properly masked in the form.
Auth & Request getClientIdKey Used to define the “username” for the integration. It defaults to ‘client_id’ for authentication type oauth2 and 'keyName’ for the keyName authentication type.
Auth & Request getClientSecretKey Used to define the “password” for the integration. By default, only oauth2 uses this and returns 'client_secret’.
Auth getAuthLoginUrl Defines the login URL for the oauth1a spec
Auth getRequestToken Used by the oauth1a spec to retrieve a request token during authorization
Auth getRequestTokenUrl Used by the oauth1a spec to define the request token URL
Auth getAuthenticationUrl Defines the login URL for the oauth2 spec
Auth getAccessTokenUrl Defines the access token URL for the oauth2 spec
Auth getAuthScope Defines the scope for the oauth2 spec
Auth getAuthCallbackUrl Used to define the callback URL for oauth1a or oauth2 specs. This defaults to the mautic_integration_auth_callback route.
Auth prepareResponseForExtraction Called by extractAuthKeys() to manipulate the data prior to checking validating the response by checking that the keyName returned by getAuthTokenKey() is part of the response. If not, it calls getErrorsFromResponse().
Auth getErrorsFromResponse Called by extractAuthKeys() to extract errors from the response into a string.
Auth & Request prepareRequest Called by makeRequest() to manipulate or prepare parameters, settings, headers, etc before sending to the URL
Auth & Request parseCallbackResponse Called by makeRequest() to parse the response for the request into an array.
Auth & Request getAuthTokenKey Returns the keyName that’s used to sign a request. Used by oauth1a (oauth_token) and oauth2 (access_token) specs.
Request getBearerToken Generate a bearer token if required by the oauth2 spec
General isConfigured Called to determine if the integration has been correctly configured.
General isAuthorized Called to determine if the integration is authorized (or peforms a reauth if an oauth2 spec has refresh tokens stored)
Request makeRequest Can be used to make API requests. It automatically handles standard key, oauth1a and oauth2 specs.
Form getFormSettings Returns an array of options of what to display in integration’s configuration form. The two options used at this time is requires_callback (true to show a readonly input with the callback returned by getAuthCallbackUrl()) and requires_authorization (true to display the authorization button).
Form getFormNotes Returns an array of “helper notes” to display in the various areas of the form.

makeRequest()

makeRequest() can be used to automatically sign outgoing requests and/or authentication processes. Of course, any integration can inherit and override this class to suit the integrations needs. It accepts the following parameters:

Name Type Description
$url string The url to make the request to
$parameters array An array of parameters to submit with the request. If $method is GET, these will be appended to the query string. Otherwise, they will be part of the POST body.
$method string The request method i.e. get, post, put, patch, delete
$settings array Configures the behavior of the makeRequest function. Built in optional settings are below.
Settings
Key Type Description
auth_type string Overrides the authentication type for the request. If not set, getAuthenticationType() is used.
query array Append parameters to the query of the request URL.
content_type string Sets the content type header for the request.
encode_parameters string If set to json, parameters in the POST will be json encoded prior to making the request.
headers array Array of custom headers to append to the request.
ssl_verifypeer bool Set the CURLOPT_SSL_VERIFYPEER to true.
curl_options array Custom set of curl options to apply to the request.
return_raw bool If true, return the response rather than running it through parseCallbackResponse first.
authorize_session bool Used by prepareRequest() and parseCallbackResponse() to change the behavior based on whether the if the request is obtaining authorization or just making an API call.

Extending Maintenance Cleanup

<?php
// plugins\HelloWorldBundle\EventListener\MaintenanceSubscriber

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Doctrine\DBAL\Connection;
use Mautic\CoreBundle\CoreEvents;
use Mautic\CoreBundle\Event\MaintenanceEvent;
use Mautic\CoreBundle\Factory\MauticFactory;

/**
 * Class MaintenanceSubscriber
 */
class MaintenanceSubscriber extends CommonSubscriber
{
    /**
     * @var Connection
     */
    protected $db;

    /**
     * MaintenanceSubscriber constructor.
     *
     * @param MauticFactory $factory
     * @param Connection    $db
     */
    public function __construct(MauticFactory $factory, Connection $db)
    {
        parent::__construct($factory);

        $this->db = $db;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            CoreEvents::MAINTENANCE_CLEANUP_DATA => ['onDataCleanup', -50]
        ];
    }

    /**
     * @param $isDryRun
     * @param $date
     *
     * @return int
     */
    public function onDataCleanup (MaintenanceEvent $event)
    {
        $qb = $this->db->createQueryBuilder()
            ->setParameter('date', $event->getDate()->format('Y-m-d H:i:s'));

        if ($event->isDryRun()) {
            $rows = (int) $qb->select('count(*) as records')
                ->from(MAUTIC_TABLE_PREFIX.'worlds', 'w')
                ->where(
                    $qb->expr()->gte('w.date_added', ':date')
                )
                ->execute()
                ->fetchColumn();
        } else {
            $rows = (int) $qb->delete(MAUTIC_TABLE_PREFIX.'worlds')
                ->where(
                    $qb->expr()->lte('date_added', ':date')
                )
                ->execute();
        }

        $event->setStat($this->translator->trans('mautic.maintenance.hello_world'), $rows, $qb->getSQL(), $qb->getParameters());
    }
}

To hook into the mautic:maintenance:cleanup command, create a listening for the \Mautic\CoreBundle\CoreEvents::MAINTENANCE_CLEANUP_DATA event. The event listener should check if data should be deleted or a counted. Use $event->setStat($key, $affectedRows, $sql, $sqlParameters) to give feedback to the CLI command. Note that $sql and $sqlParameters are only used for debugging and shown only in the dev environment.

Extending Landing Pages

<?php
// plugins/HelloWorldBundle/EventListener/PageSubscriber.php

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\PageBundle\PageEvents;
use Mautic\PageBundle\Event\PageBuilderEvent;
use Mautic\PageBundle\Event\PageSendEvent;

/**
 * Class PageSubscriber
 */
class PageSubscriber extends CommonSubscriber
{

    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return array(
            PageEvents::PAGE_ON_BUILD   => array('onPageBuild', 0),
            PageEvents::PAGE_ON_DISPLAY => array('onPageDisplay', 0)
        );
    }

    /**
     * Register the tokens and a custom A/B test winner
     *
     * @param PageBuilderEvent $event
     */
    public function onPageBuild(PageBuilderEvent $event)
    {
        // Add page tokens
        $content = $this->templating->render('HelloWorldBundle:SubscribedEvents\PageToken:token.html.php');
        $event->addTokenSection('helloworld.token', 'plugin.helloworld.header', $content);

        // Add AB Test Winner Criteria
        $event->addAbTestWinnerCriteria(
            'helloworld.planetvisits',
            array(
                // Label to group by
                'group'    => 'plugin.helloworld.header',

                // Label for this specific a/b test winning criteria
                'label'    => 'plugin.helloworld.pagetokens.',

                // Static callback function that will be used to determine the winner
                'callback' => '\MauticPlugin\HelloWorldBundle\Helper\AbTestHelper::determinePlanetVisitWinner'
            )
        );
    }

    /**
     * Search and replace tokens with content
     *
     * @param PageSendEvent $event
     */
    public function onPageDisplay(PageSendEvent $event)
    {
        // Get content
        $content = $event->getContent();

        // Search and replace tokens
        $content = str_replace('{hello}', 'world!', $content);

        // Set updated content
        $event->setContent($content);
    }
}

There are two way to extend pages: page tokens used to insert dynamic content into a page and a/b test winning criteria . Both leverage the \Mautic\PageBundle\PageEvents::PAGE_ON_BUILD event. Read more about listeners and subscribers.

Page Tokens

Page tokens are handled exactly the same as Email Tokens.

Page A/B Test Winner Criteria

Custom landing page A/B test winner criteria is handled exactly the same as page A/B test winner criteria with the only differences being that the callback function is passed Mautic\PageBundle\Entity\Page $page and Mautic\PageBundle\Entity\Page $parent instead. Of course $children is an ArrayCollection of Page entities as well.

Extending Points

Custom point actions and triggers can be added by listening to their respective on build events. Read more about listeners and subscribers.

Point Actions

To add a custom point action used to give a lead x points for doing a certain action, add a listener to the \Mautic\PointBundle\PointEvents::POINT_ON_BUILD event then configure the custom point action with $event->addAction($identifier, $parameters) method. $identifier must be something unique. The $parameters array can contain the following elements:

Key Required Type Description
label REQUIRED string The language string for the option in the dropdown
formType OPTIONAL string The alias of a custom form type used to set config options.
formTypeOptions OPTIONAL array Array of options to include into the formType’s $options argument
formTypeCleanMasks OPTIONAL array Array of input masks to clean a values from formType
formTypeTheme OPTIONAL string Theme to customize elements for formType
template OPTIONAL string View template used to render the formType
callback OPTIONAL mixed Static callback function used to validate the action. Return true to add the points to the lead.

In order for the custom point action to work, add something like the following in the code logic when the lead executes the custom action:

$this->getModel('point')->triggerAction('page.hit', $event->getHit());

Point Triggers

To add a custom point trigger used to execute a specific action once a lead hits X number of points, add a listener to the \Mautic\PointBundle\PointEvents::TRIGGER_ON_BUILD event then configure the custom point trigger with $event->addEvent($identifier, $parameters) method. $identifier must be something unique. The $parameters array can contain the following elements:

Key Required Type Description
label REQUIRED string The language string for the option in the dropdown
formType OPTIONAL string The alias of a custom form type used to set config options.
formTypeOptions OPTIONAL array Array of options to include into the formType’s $options argument
formTypeCleanMasks OPTIONAL array Array of input masks to clean a values from formType
formTypeTheme OPTIONAL string Theme to customize elements for formType
template OPTIONAL string View template used to render the formType
callback OPTIONAL mixed Static callback function used to execute the custom action.

Extending Reports

<?php
// plugins\HelloWorldBundle\EventListener\ReportSubscriber

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\CoreBundle\Helper\GraphHelper;
use Mautic\ReportBundle\Event\ReportBuilderEvent;
use Mautic\ReportBundle\Event\ReportGeneratorEvent;
use Mautic\ReportBundle\Event\ReportGraphEvent;
use Mautic\ReportBundle\ReportEvents;
use Mautic\CoreBundle\Helper\Chart\ChartQuery;
use Mautic\CoreBundle\Helper\Chart\LineChart;

/**
 * Class ReportSubscriber
 */
class ReportSubscriber extends CommonSubscriber
{

    /**
     * @return array
     */
    static public function getSubscribedEvents ()
    {
        return array(
            ReportEvents::REPORT_ON_BUILD          => array('onReportBuilder', 0),
            ReportEvents::REPORT_ON_GENERATE       => array('onReportGenerate', 0),
            ReportEvents::REPORT_ON_GRAPH_GENERATE => array('onReportGraphGenerate', 0)
        );
    }

    /**
     * Add available tables, columns, and graphs to the report builder lookup
     *
     * @param ReportBuilderEvent $event
     *
     * @return void
     */
    public function onReportBuilder (ReportBuilderEvent $event)
    {
        // Use checkContext() to determine if the report being requested is this report
        if ($event->checkContext(array('worlds'))) {
            // Define the columns that are available to the report.
            $prefix  = 'w.';
            $columns = array(
                $prefix . 'visit_count' => array(
                    'label' => 'mautic.hellobundle.report.visit_count',
                    'type'  => 'int'
                ),
                $prefix . 'world' => array(
                    'label' => 'mautic.hellobundle.report.world',
                    'type'  => 'text'
                ),
            );

             // Several helper functions are available to append common columns such as categories, publish state fields, lead, etc.  Refer to the ReportBuilderEvent class for more details.
            $columns = $filters = array_merge($columns, $event->getStandardColumns($prefix), $event->getCategoryColumns());

            // Optional to override and update filters, i.e. change it to a select list for the UI
            $filters[$prefix.'world']['type'] = 'select';
            $filters[$prefix.'world']['list'] = array(
                'earth' => 'Earth',
                'mars'  => 'Mars'
            );

            // Add the table to the list
            $event->addTable('worlds',
                array(
                    'display_name' => 'mautic.helloworld.worlds',
                    'columns'      => $columns,
                    'filters'      => $filters // Defaults to columns if not set
                )
            );

            // Register available graphs; can use line, pie, or table
            $event->addGraph('worlds', 'line', 'mautic.hellobundle.graph.line.visits');
        }
    }

    /**
     * Initialize the QueryBuilder object used to generate the report's data.
     * This should use Doctrine's DBAL layer, not the ORM so be sure to use
     * the real schema column names (not the ORM property names) and the
     * MAUTIC_TABLE_PREFIX constant.
     *
     * @param ReportGeneratorEvent $event
     *
     * @return void
     */
    public function onReportGenerate (ReportGeneratorEvent $event)
    {
        $context = $event->getContext();
        if ($context == 'worlds') {
            $qb = $event->getQueryBuilder();

            $qb->from(MAUTIC_TABLE_PREFIX . 'worlds', 'w');
            $event->addCategoryLeftJoin($qb, 'w');

            $event->setQueryBuilder($qb);
        }
    }

    /**
     * Generate the graphs
     *
     * @param ReportGraphEvent $event
     *
     * @return void
     */
    public function onReportGraphGenerate (ReportGraphEvent $event)
    {
        if (!$event->checkContext('worlds')) {
            return;
        }

        $graphs   = $event->getRequestedGraphs();
        $qb       = $event->getQueryBuilder();

        foreach ($graphs as $graph) {
            $queryBuilder = clone $qb;
            $options      = $event->getOptions($graph);
            /** @var ChartQuery $chartQuery */
            $chartQuery    = clone $options['chartQuery'];
            $chartQuery->applyDateFilters($queryBuilder, 'date_added', 'v');

            switch ($graph) {
                case 'mautic.hellobundle.graph.line.visits':
                    $chart = new LineChart(null, $options['dateFrom'], $options['dateTo']);
                    $chartQuery->modifyTimeDataQuery($queryBuilder, 'date_added', 'v');
                    $visits = $chartQuery->loadAndBuildTimeData($queryBuilder);
                    $chart->setDataset($options['translator']->trans('mautic.hellobundle.graph.line.visits'), $visits);
                    $data         = $chart->render();
                    $data['name'] = $graph;
                    $data['iconClass'] = 'fa-tachometer';
                    $event->setGraph($graph, $data);

                    break;
            }
        }
    }
}

Adding and rendering custom reports are done by listening to the \Mautic\ReportBundle\ReportEvents::REPORT_ON_BUILD, ReportEvents::REPORT_ON_GENERATE and ReportEvents::REPORT_ON_GRAPH_GENERATE events.

Defining the Report

Defining the report is done through the ReportEvents::REPORT_ON_BUILD event. This is where the plugin will define the context of the report, available columns for table data, available filters for the table data (defaults to columns) and available graphs. See the code example’s onReportBuilder for details.

Column Definition

Each column array can include the following properties:

Key Required Type Description
label REQUIRED string The language string for the column
type REQUIRED string Column type
alias OPTIONAL string An alias for the returned value. Useful in conjuction with formula
formula OPTIONAL string SQL formula instead of a column. e.g. SUBSTRING_INDEX(e.type, \'.\', 1)
link OPTIONAL string Route name to convert the value into a hyperlink. Used usually with an ID of an Entity. The route must accept objectAction and objectId parameters.
Filter Definition

Filter definitions are optional as Mautic will default to the column list. But sometimes it’s useful to replace filter values with a select list. Filter definitions can accept the same properties as columns but can also accept the following:

Key Required Type Description
list OPTIONAL array Used when type is select for a filter. Provides the dropdown options for a select input. Format should be value => label
operators OPTIONAL array Custom list of operators to allow for this filter. See Mautic\ReportBundle\Builder\MauticReportBuilder::OPERATORS for a examples.

Generate the QueryBuilder

The ReportEvents::REPORT_ON_GENERATE event is dispatched when a report is to be generated and displayed. In this function, the plugin should define the QueryBuilder object used to generate the table data.

Use $event->checkContext() to determine if the report requested is the subscribers report.

There are a number of helper functions to append joins for commonly used relationships such as category, leads, ip address, etc. Refer to the ReportGeneratorEvent class for more details.

Graphs

Graphs are generated using ReportEvents::REPORT_ON_GRAPH_GENERATE event. The listener should check the context then generate and set the graph data. There are several classes to assist with generating classes. See

Extending Webhooks

Webhooks allow for Mautic to send data to external services via an endpoint url.

Webhooks work by using event listeners for two main purposes:

  1. An event listener to add webhook types to the webhook user interface.
  2. An event listener to whichever action you want to trigger a webhook payload to be queued.
Additional Steps

Additional steps that you must take in your own bundle are:

  1. Create your own event dispatching for your bundle’s custom event and payload
  2. Register your custom event listeners in your bundle’s configuration file as a service.
  3. Refer to receiving webhooks section to receive payloads in your application.

Webhook Type Listener

<?php
namespace Mautic\YourBundle\EventListener;

use Mautic\WebhookBundle\WebhookEvents;
use Mautic\WebhookBundle\Event\WebhookBuilderEvent;
use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\YourBundle\YourBundleEvents;

/**
 * Class WebhookSubscriber
 *
 * @package Mautic\YourBundle\EventListener
 */

class WebhookSubscriber extends CommonSubscriber
{
    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return array(
            WebhookEvents::WEBHOOK_ON_BUILD => array('onWebhookBuild', 0)
        );
    }

    /**
     * Add event triggers and actions
     *
     * @param WebhookBuilderEvent $event
     */
    public function onWebhookBuild(WebhookBuilderEvent $event)
    {
        // add checkbox to the webhook form for new leads
        $type  = array(
            'label'       => 'mautic.bundlename.webhook.event.type.new',
            'description' => 'mautic.bundlename.webhook.event.type.new_desc', // note: we don't currently use a description, but may in the future so we have supported it here
        );

        // add it to the list
        $event->addEvent(YourBundle::ACTION_TO_TRIGGER, $type);

         // Note: you may create multiple arrays and call the $event->addEvent method multiple times
         // in this function to add several types all at once.
    }
}
?>

Add an event listener for the WebhookEvents::WEBHOOK_ON_BUILD event, call the addEvent method and pass it an argument for your payload event constant, and an array of a label and a description to be added to the Webhook user interface.

YourBundle::ACTION_TO_TRIGGER is a constant that should be an event registered in your bundle. We use the constant to save the type in the database and query for later. You will use the same constant later, so its important to be consistent.

Payload Event Listener

<?php

namespace Mautic\YourBundle\EventListener;

// its important to use the WebhookSubscriberBase event listener
// as it does a lot of heavy lifting for you.

use Mautic\WebhookBundle\EventListener\WebhookSubscriberBase;

// you should change this to your bundle's event class
use Mautic\LeadBundle\LeadEvents;

// you should change this to the event type that you are going to use.
// in our case it will be a lead event.
use Mautic\LeadBundle\Event\LeadEvent;

use JMS\Serializer\Serializer;
use Mautic\CoreBundle\Factory\MauticFactory;
use Doctrine\ORM\NoResultException;
use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\LeadBundle\Event\PointsChangeEvent;
use Mautic\ApiBundle\Serializer\Exclusion\PublishDetailsExclusionStrategy;

/**
 * Class LeadSubscriber
 *
 * @package Mautic\Webhook\EventListener
 */
class LeadSubscriber extends WebhookSubscriberBase
{
    /**
     * @return array
     */
    static public function getSubscribedEvents ()
    {
        return array(
            // note this is the same constant we would have used in the addEvent() listener!
            LeadEvents::LEAD_POST_SAVE => array('onLeadNewUpdate', 0)
        )
    }

    /*
     * Generic method to execute when a lead does something
     */
    public function onLeadNewUpdate(LeadEvent $event)
    {
        // serializer groups are defined and optionally used in your bundle's entity.
        // Use these format your payload in JSON formats.
        $serializerGroups = array("leadDetails", "userList", "publishDetails", "ipAddress");

        // the beginning part of building our webhook payload of a lead entity
        $entity = $event->getLead();

        // our payload goes into an array
        $payload = array(
            'lead'      => $entity,
        );

        // we want to only trigger a payload if the lead is new
        if ($event->isNew()) {

            // again please note the use of the constant here - this should be consistent with the constant we register to the webhook form
            $webhookEvents = $this->getEventWebooksByType(LeadEvents::LEAD_POST_SAVE);

            // The webhook model is made available because we've extended the WebhookSubscriberBase class.
            $this->webhookModel->QueueWebhooks($webhookEvents, $payload, $serializerGroups);


        }
    }
}

The next step is to add an event listener for the action you want to actually use for creating payloads. This could be anything you want, your bundle will have to have an event type and use the event dispatcher to execute the event.

Here is an example listener to create a lead payload whenever a new lead is added to Mautic.

You can add the listener to any bundle. Be sure to register it in your bundle’s config.php file.

You can refer to the model for more documentation on the QueueWebhooks method. In short you want to pass the event entities that this payload is being queued against The payload, which is an array, and finally the serializer groups for formatting the JSON.

This should complete the set up, registering and executing custom webhook events and payloads.

Receiving Webhook Payloads

A sample new lead post payload

{"mautic.lead_post_save":{"lead":{"id":null,"points":0,"color":null,"fields":{"core":{"title":{"id":1,"group":"core","label":"Title","alias":"title","type":"lookup","value":null},"firstname":{"id":2,"group":"core","label":"First Name","alias":"firstname","type":"text","value":"Hello"},"lastname":{"id":3,"group":"core","label":"Last Name","alias":"lastname","type":"text","value":"World"},"company":{"id":4,"group":"core","label":"Company","alias":"company","type":"lookup","value":null},"position":{"id":5,"group":"core","label":"Position","alias":"position","type":"text","value":null},"email":{"id":6,"group":"core","label":"Email","alias":"email","type":"email","value":"example@email.com"},"phone":{"id":7,"group":"core","label":"Phone","alias":"phone","type":"tel","value":null},"mobile":{"id":8,"group":"core","label":"Mobile","alias":"mobile","type":"tel","value":null},"fax":{"id":9,"group":"core","label":"Fax","alias":"fax","type":"text","value":null},"address1":{"id":10,"group":"core","label":"Address Line 1","alias":"address1","type":"text","value":null},"address2":{"id":11,"group":"core","label":"Address Line 2","alias":"address2","type":"text","value":null},"city":{"id":12,"group":"core","label":"City","alias":"city","type":"lookup","value":null},"state":{"id":13,"group":"core","label":"State","alias":"state","type":"region","value":null},"zipcode":{"id":14,"group":"core","label":"Zipcode","alias":"zipcode","type":"lookup","value":null},"country":{"id":15,"group":"core","label":"Country","alias":"country","type":"country","value":null},"website":{"id":16,"group":"core","label":"Website","alias":"website","type":"text","value":null}},"social":{"twitter":{"id":17,"group":"social","label":"Twitter","alias":"twitter","type":"text","value":null},"facebook":{"id":18,"group":"social","label":"Facebook","alias":"facebook","type":"text","value":null},"googleplus":{"id":19,"group":"social","label":"Google+","alias":"googleplus","type":"text","value":null},"skype":{"id":20,"group":"social","label":"Skype","alias":"skype","type":"text","value":null},"instagram":{"id":21,"group":"social","label":"Instagram","alias":"instagram","type":"text","value":null},"foursquare":{"id":22,"group":"social","label":"Foursquare","alias":"foursquare","type":"text","value":null}},"personal":[],"professional":[]},"lastActive":null,"owner":null,"ipAddresses":[],"dateIdentified":null,"preferredProfileImage":null},"timestamp":"2015-08-18T18:53:33+00:00"}}

Webhooks enable Mautic to send data for leads, points, and email opens to outside applications. It does this by taking an outside application’s endpoint url, and sending an HTTP post request to that location. In that post we include the relevant data the the event that has been fired.

To listen to the webhook posts, developers should create a publicly accessible endpoint location in their application. That endpoint should be available to receive a POST request from Mautic. The contents of the payload will vary based on the events that the user wishes to include in the payload.

An excellent way of testing to see the data that will be included is using Requestb.in. Requestb.in allows users to view the full contents of the hook payload. Developers can refer to a post to the bin to see the contents and craft the way their application receives that input accordingly.

Extending UI

Injecting Buttons

<?php
// plugins/HelloWorldBundle/Event/ButtonSubscriber.php

namespace MauticPlugin\HelloWorldBundle\EventListener;


use Mautic\CoreBundle\CoreEvents;
use Mautic\CoreBundle\Event\CustomButtonEvent;
use Mautic\CoreBundle\EventListener\CommonSubscriber;
use Mautic\CoreBundle\Templating\Helper\ButtonHelper;
use Mautic\LeadBundle\Entity\Lead;

class ButtonSubscriber extends CommonSubscriber
{
    public static function getSubscribedEvents()
    {
        return [
            CoreEvents::VIEW_INJECT_CUSTOM_BUTTONS => ['injectViewButtons', 0]
        ];
    }

    /**
     * @param CustomButtonEvent $event
     */
    public function injectViewButtons(CustomButtonEvent $event)
    {
        // Injects a button into the toolbar area for any page with a high priority (displays closer to first)
        $event->addButton(
            [
                'attr'      => [
                    'class'       => 'btn btn-default btn-sm btn-nospin',
                    'data-toggle' => 'ajaxmodal',
                    'data-target' => '#MauticSharedModal',
                    'href'        => $this->router->generate('mautic_world_action', ['objectAction' => 'doSomething']),
                    'data-header' => 'Extra Button',
                ],
                'tooltip'   => $this->translator->trans('mautic.world.dosomething.btn.tooltip'),
                'iconClass' => 'fa fa-star',
                'priority'  => 255,
            ],
            ButtonHelper::LOCATION_TOOLBAR_ACTIONS
        );

        // 
        if ($lead = $event->getItem()) {
            if ($lead instanceof Lead) {
                $sendEmailButton = [
                    'attr'      => [
                        'data-toggle' => 'ajaxmodal',
                        'data-target' => '#MauticSharedModal',
                        'data-header' => $this->translator->trans(
                            'mautic.world.dosomething.header',
                            ['%email%' => $event->getItem()->getEmail()]
                        ),
                        'href'        => $this->router->generate(
                            'mautic_world_action',
                            ['objectId' => $event->getItem()->getId(), 'objectAction' => 'doSomething']
                        ),
                    ],
                    'btnText'   => 'Extra Button',
                    'iconClass' => 'fa fa-star',
                    'primary'   => true,
                    'priority'  => 255,
                ];

                // Inject a button into the page actions for the specified route (in this case /s/contacts/view/{contactId})
                $event
                    ->addButton(
                        $sendEmailButton,
                        // Location of where to inject the button; this can be an array of multiple locations
                        ButtonHelper::LOCATION_PAGE_ACTIONS,
                        ['mautic_contact_action', ['objectAction' => 'view']]
                    )
                    // Inject a button into the list actions for each contact on the /s/contacts page
                    ->addButton(
                        $sendEmailButton,
                        ButtonHelper::LOCATION_LIST_ACTIONS,
                        'mautic_contact_index'
                    );
            }
        }
    }
}

As of Mautic 2.3.0, support for plugins to inject buttons throughout Mautic’s UI has been added by listening to the CoreEvents::VIEW_INJECT_CUSTOM_BUTTONS event.

There are five places in Mautic’s UI that buttons can be injected into:

Location Description
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::LOCATION_LIST_ACTIONS Drop down actions per each item in list views.
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::LOCATION_TOOLBAR_ACTIONS Top right above list view tables to the right of the table filter. Preferably buttons with icons only.
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::LOCATION_PAGE_ACTIONS Main page buttons to the right of the page title (New, Edit, etc). Primary buttons will be displayed as buttons while the rest will be displayed in a drop down.
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::LOCATION_NAVBAR Top of the page to the left of the account/profile menu. Buttons with text and/or icons.
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::LOCATION_BULK_ACTIONS Buttons inside the bulk dropdown (around the checkall checkbox of lists).

Buttons use a priority system to determine order. The higher the priority, the closer to first the button is displayed. The lower the priority, the closer to last. For a button dropdown, setting a button as primary will display the button in the button group rather than the dropdown.

Button Array Format

The array defining the button can include the following keys:

Key Type Description
attr array Array of attributes to be appended to the button (data attributes, href, etc)
btnText string Text to display for the button
iconClass string Font Awesome class to use as the icon within the button
tooltip string Text to display as a tooltip
primary boolean For button dropdown formats, this will display the button in the group rather than in the dropdown
priority int Determines the order of buttons. Higher the priority, closer to the first the button will be placed. Buttons with the same priority wil be ordered alphabetically.

If a button is to display a confirmation modal, the key confirm can be used. A confirm array can have the following keys:

Key Type Description
message string Translated message to display in the confirmation window
confirmText string Text to display as the confirm button
confirmAction string HREF of the button
cancelText string Text to display as the cancel button
cancelCallback string Mautic namespaced Javascript method to be executed when the cancel button is clicked
confirmCallback string Mautic namespaced Javascript method to be executed when the confirm button is clicked
precheck string Mautic namespaced Javascript method to be executed prior to displaying the confirmation modal
btnClass string Class for the button
iconClass string Font Awesome class to use as the icon
btnTextAttr string string of attributes to append to the button’s inner text
attr array Array of attributes to append to the button’s outer tag
tooltip string Translated string to display as a tooltip
tag string Tag to use as the button. Defaults to an a tag.
wrapOpeningTag string Tag/html to wrap button in. Defaults to nothing.
wrapClosingTag string Tag/thml to close wrapOpeningTag. Defaults to nothing.

On the same nested level as the confirm key can include primary and/or priority.

Defining Button Locations

<?php
$dropdownOpenHtml = '<button type="button" class="btn btn-default btn-nospin  dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><i class="fa fa-caret-down"></i></button>'
          ."\n";
$dropdownOpenHtml .= '<ul class="dropdown-menu dropdown-menu-right" role="menu">'."\n";

echo $view['buttons']->reset($app->getRequest(), 'custom_location')->renderButtons($dropdownOpenHtml, '</ul>');

A plugin can define it’s own locations that other plugins can leverage by using the template buttons helper.

There are three types of button groups supported:

Type Description
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::TYPE_BUTTON_DROPDOWN Primary buttons are displayed in a button group while others in a dropdown menu.
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::TYPE_DROPDOWN Buttons displayed in a dropdown menu.
\Mautic\CoreBundle\Templating\Helper\ButtonHelper::TYPE_GROUP A group of buttons side by side.

Dropdowns require the wrapping HTML to be passed to the renderButtons method.

Miscellaneous

Flash Messages

<?php

// From within a controller

$this->addFlash(
    'mautic.translation.key',
    array('%placeholder%' => 'some text'),
    'notice', // Notification type
    'flashes', // Translation domain
    $addNotification // Add a notification entry
);
<?php

// From within a model or other service with access to the translator and session services

$translatedString = $this->translator->trans(, 
    array(
        '%placeholder%' => 'some text'
    ),
    'flashes'
);
$this->session->getFlashBag()->add('notice', $translatedString);

To create an alert, aka flash message, you can use the flash bag in the session.

If your controller extends one of Mautic’s common controllers, you can simply use the helper function addFlash().

From a model, or any service, you can use the session to obtain the flash bag.

$flashBag = $this->get('session')->getFlashBag();

Notifications

<?php
// From within a controller

$this->addNotification($message, $type, $isRead, $header, $iconClass, new \DateTime());
<?php

// From within a model or other service that has access to the mautic.core.model.notification service

$notificationModel->addNotification($message, $type, $isRead, $header, $iconClass, $datetime );

Mautic also has a notification center. By default, addFlash() will also add a notification to the center. But, a message can be manually added as well.

Controllers can use the helper function while models and other services can obtain the NotificationModel.

Helpers

Mautic has many helper classes available. A few of the most commonly used ones are highlighted below.

Input Helper

The input helper can be used to ensure clean strings from user input.

<?php

use \Mautic\CoreBundle\Helper\InputHelper;

// ...

$clean = InputHelper::clean($input);
$clean = InputHelper::int($input);
$clean = InputHelper::alphanum($input);
$clean = InputHelper::html($input);

// and others; refer to the class for more options

Date/Time Helper

The date/time helper can be used to convert between UTC and the local timezone.

<?php

$dtHelper  = new \Mautic\CoreBundle\Helper\DateTimeHelper('2015-07-20 21:39:00', 'Y-m-d H:i:m', 'local');
$utcString = $dtHelper->toUtcString();

// refer to the class for other functions

ChartQuery and Graphs

There are several classes available to assist with generating chart data.

<?php
use Mautic\CoreBundle\Helper\Chart\LineChart;
use Mautic\CoreBundle\Helper\Chart\ChartQuery;

$chart = new LineChart($unit, $dateFrom, $dateTo, $dateFormat);
$query = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo);
$q     = $query->prepareTimeDataQuery('lead_points_change_log', 'date_added', $filter);
$data  = $query->loadAndBuildTimeData($q);
$chart->setDataset($this->translator->trans('mautic.point.changes'), $data);
$data  = $chart->render();
ChartQuery

ChartQuery is a helper class to get the chart data from the database. Instantiate it with the Doctrine connection object, date from and date to. Those dates must be instances of the DateTime class. ChartQuery will automatically guess the time unit (hours, days, weeks, months or years) based on the items between the date range. However, if you want to receive the data from a specific time unit, pass it as the fourth parameter (H, d, W, m, Y).

ChartQuery also fills in the missing items (if any) which is required to display the data properly in the line chart generated by the ChartJS.

LineChart

LineChart is used in Mautic to display a date/time related data where the time is on the horizontal axis and the values are in the vertical axis. ChartJS’ line charts can display multiple datasets in one chart. Instantiate the LineChart with a unit (null for unit guessing), from date, to date (the same as for the ChartQuery) and a date format (null for automatically generated date format).

All the params you need to instantiate the LineChart are used to generate the labels of the horizontal axis. To add the dataset to the LineChart object, use the setDataset($label, $data) method where the label is string and data is an array generated by the ChartQuery. The color of each dataset will be generated automatically.

Call the render() method to get the data prepared for ChartJS.

PieChart

A PieChart can be instantiated simply by new PieChart(). To add a dataset, use again the setDataset($label, $value) method where the label is a string and value is integer.

Call the render() method to get the data prepared for ChartJS.

BarChart

BarChart is used to display different variants of the same value where the variants are in the horizontal axis and the value is in vertical axis. To create a bar chart, use new BarChart($labels) where labels is an array of all the variants. Each variant can have multiple datasets. To add a dataset, use the setDataset($label, $data, $order) method where the label is string, data is array of values for each variant. Order can be used to move the dataset before already created dataset.

Call the render() method to get the data prepared for ChartJS.

<?php echo $view->render('MauticCoreBundle:Helper:chart.html.php', array('chartData' => $data, 'chartType' => 'line', 'chartHeight' => 300)); ?>
Frontend

At the frontend, simply use the prepared chart template, pass in the chart data, the chart type (line/bar/pie) and the chart height. The width is responsive.

Commands

Support for new CLI commands can be added using Symfony’s console component.

Moderated Commands

<?php
// plugins\HelloWorldBundle\Command\WorldCommand.php

namespace MauticPlugin\HelloWorldBundle\Command;

use Mautic\CoreBundle\Command\ModeratedCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * CLI Command to send a scheduled broadcast.
 */
class WorldCommand extends ModeratedCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        // ...

        // Important to append options used by ModeratedCommand
        parent::configure();
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // Validate if the command is already has an instance running
        // A third argument can be passed here if this command is running something unique such as an ID
        if (!$this->checkRunStatus($input, $output)) {
            return 0;
        }

        // Execute some stuff

        // Complete this execution
        $this->completeRun();

        return 0;
    }
}

Mautic provide an method for moderating commands meaning it will only allow one instance to run at a time. To utilize this method, extend the Mautic\CoreBundle\Command\ModeratedCommand class.

Forms

Mautic leverages Symfony’s Form component and form classes. Refer to Symfony’s documentation for more information.

Form Types

As stated in Symfony’s documentation referenced above, form type classes are the best way to go. Mautic makes it easy to register form type services through the bundle’s config file. Refer to the Services section.

Data Sanitization

<?php
// plugins/HelloWorldBundle/Form/Type/WorldType.php

// ...
use Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber;

// ...
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventSubscriber(
            new CleanFormSubscriber(
                [
                    'content'    => 'html', 
                    'customHtml' => 'html'
                ]
            )
        );

       // ...
    }
// ...

Form data is not automatically sanitized. Mautic provides a form event subscriber to handle this.

In your form type class, register the Mautic\CoreBundle\Form\EventListener\CleanFormSubscriber event subscriber.

The array provided to CleanFormSubscriber should contain the names of the form fields as keys and the values the masks to use to sanitize the data. Any un-specified form field will use the clean mask by default.

Manipulating Forms

A form event listener must be used if a form needs to be manipulated based on submitted data such as changing defined fields, adjust constraints, or changing select choices based on submitted values. Refer to Symfony’s documentation on this.

Validating Data

Review Symfony’s form validation documentation for a general overview.

There are two common means of validating form data.

Using Entity Static Callback
<?php
// plugins/HelloWorldBundle/Entity/World.php

// ...
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Form\Form;

// ...
  /**
     * @param ClassMetadata $metadata
     */
    public static function loadValidatorMetadata (ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint(
            'name',
            new NotBlank(
                array(
                    'message' => 'mautic.core.name.required'
                )
            )
        );

        $metadata->addPropertyConstraint(
            'population', 
            new NotBlank(
                array(
                    'message' => 'mautic.core.value.required',
                    'groups'  => array('VisitedWorld')
                )

            )
        );
    }

    /**
     * @param Form $form
     *
     * @return array
     */
    public static function determineValidationGroups (Form $form)
    {
        $data   = $form->getData();
        $groups = array('AllWorlds');

        if (!$data->getId() || ($data->getId() && $data->getVisitCount() > 0)) {
            $groups[] = 'VisitedWorld';
        }

        return $groups;
    }
// ...

If the underlying data of a form is an Entity object, a static method loadValidatorMetadata can be defined in the Entity class. This will automatically be called when Symfony is processing form data.

A form can also use validation_groups to change the order of data to be validated or only validate if certain criteria is true. For example, only validate a password confirmation field if the first password field passes validation. When registering a validation group in the form type class, one can use static callback that can be used to determine what validation group(s) should be used.

<?php
// plugins/HelloWorldBundle/Form/Type/WorldType.php

//...
    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions (OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class'        => 'MauticPlugin\HelloWorld\Entity\World',
            'validation_groups' => array(
                'MauticPlugin\HelloWorld\Entity\World',
                'determineValidationGroups',
            )
        ));
    }
// ...
Using Constraints

A form type service can also register constraints when defining the form fields.

<?php
// plugins/HelloWorldBundle/Form/Type/WorldType.php

// ...

use Symfony\Component\Validator\Constraints\NotBlank;

// ...

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
          $builder->add(
              'name',
              'text',
              array(
                  'label'       => 'mautic.core.name',
                  'label_attr'  => array('class' => 'control-label'),
                  'attr'        => array(
                      'class'   => 'form-control'
                  ),
                  'constraints' => array(
                      new NotBlank(
                          array(
                              'message' => 'mautic.core.value.required'
                          )
                      )
                  )
              )
          );
    }

// ...

Events

<?php
// plugins\HelloWorldBundle\EventListener\LeadSubscriber

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Mautic\LeadBundle\Event as Events;
use Mautic\LeadBundle\LeadEvents;

/**
 * Class LeadSubscriber
 *
 * @package Mautic\LeadBundle\EventListener
 */
class LeadSubscriber extends CommonSubscriber
{

    /**
     * @return array
     */
    static public function getSubscribedEvents()
    {
        return array(
            LeadEvents::LEAD_POST_SAVE     => array('onLeadPostSave', 0),
            LeadEvents::LEAD_POST_DELETE   => array('onLeadDelete', 0)
        );
    }

    public function onLeadPostSave(LeadEvent $event)
    {
        $lead = $event->getLead();

        // do something
    }

    public function onLeadDelete(LeadEvent $event)
    {
        $lead = $event->getLead();

        $deletedId = $lead->deletedId;

        // do something
    }
}

Mautic leverages Symfony’s EventDispatcher to execute and communicate various actions through Mautic. Plugin’s can hook into these to extend the functionality of Mautic. Refer to Extending Mautic for some of the ways to do this.

Subscribers

The easiest way to listen to various events is to use an event subscriber. Read more about subscribers in Symfony’s documentation.

Plugin event subscribers can extend \Mautic\CoreBundle\EventListener\CommonSubscriber which gives access to commonly used dependencies and also allows registering the subscriber service through the bundles’s config file. But, it does not have to and instead See Services for more information on registering event services.

Available Events

There are many events available throughout Mautic. Depending on the desired functionality, look at the core bundle’s *Event.php file in the root of the bundle. For example, Lead related events are defined and described in app\bundles\LeadBundle\LeadEvents.php. These final classes provide the names of the events to listen to. Always use the event constants to ensure future changes to event names will not break the plugin.

Custom Events

A plugin can create and dispatch it’s own events.

Custom events require three things:

<?php
// plugins\HelloWorldBundle\HelloWorldEvents.php

namespace MauticPlugin\HelloWorldBundle;

/**
 * Class HelloWorldEvents
 */
final class HelloWorldEvents
{
    /**
     * The helloworld.armageddon event is dispatched when a world is doomed by a giant meteor
     *
     * The event listener receives a MauticPlugin\HelloWorldBundle\Event\ArmageddonEvent instance.
     *
     * @var string
     */
    const ARMAGEDDON = 'helloworld.armageddon';
}

1) Class defining the available events for the plugin using a final class with constants

<?php
// plugins\HelloWorldBundle\Event\ArmageddonEvent.php

namespace MauticPlugin\HelloWorldBundle\Event;

use MauticPlugin\HelloWorldBundle\Entity\World;

class ArmageddonEvent 
{
    /** @var World  */
    protected $world;

    /** @var bool  */    
    protected $falseAlarm = false;

    public function __construct(World $world)
    {
        $this->world = $world;
    }

    public function shouldPanic()
    {
        return ('earth' == $this->world->getName());
    }

    public function setIsFalseAlarm()
    {
        $this->falseAlarm = true;
    }

    public function getIsFalseAlarm()
    {
        return $this->falseAlarm;
    }
}

2) The Event class that is received by the listeners. This class should extend Symfony\Component\EventDispatcher\Event. It will be created when the event is dispatched and should have any information listeners need to act on it.

<?php

$dispatcher = $this->get('event_dispatcher');
if ($dispatcher->hasListeners(HelloWorldEvents::ARMAGEDDON)) {
    $event = $dispatcher->dispatch(HelloWorldEvents::ARMAGEDDON, new ArmageddonEvent($world));

    if ($event->shouldPanic()) {
        throw new \Exception("Run for the hills!");
    }
}

3) The code that dispatches the event where appropriate using the event_dispatcher service.

Implementing Translation Support to Entities

Mautic has some helper methods with adding support for translated content to an entity.

\Mautic\CoreBundle\Entity\TranslationInterface

This Entity interface ensures that everything is needed in order for Mautic to handle translations correctly for an entity.

\Mautic\CoreBundle\Entity\TranslationEntityTrait

This trait provides properties needed to define an Entity’s language and relationships to other items. In the Entity’s loadMetadata() method, be sure to call $this->addTranslationMetadata().

\Mautic\CoreBundle\TranslationModelTrait

This trait provides the method getTranslatedEntity() that will determine the entity to use as the translation based on the $lead and/or the HTTP_ACCEPT_LANGUAGE header. It also has a postTranslationEntitySave() that should be called at the end of the Entity’s saveEntity() method.

\Mautic\CoreBundle\Doctrine\TranslationMigrationTrait

To ease the generation of schema to match the Entity, use this trait then execute $this->addTranslationSchema().

Translated Entity Form

Add a locale and translatedParent form fields like the code example.

<?php 
// plugins/HelloWorldPlugin/Form/Type/WorldType.php

    $transformer = new \Mautic\CoreBundle\Form\Transformer\IdToEntityModelTransformer($this->em, 'HelloWorldBundle:World');
    $builder->add(
        $builder->create(
            'translationParent',
            'world_list',
            array(
                'label'       => 'mautic.core.form.translation_parent',
                'label_attr'  => array('class' => 'control-label'),
                'attr'        => array(
                    'class'   => 'form-control',
                    'tooltip' => 'mautic.core.form.translation_parent.help'
                ),
                'required'    => false,
                'multiple'    => false,
                'empty_value' => 'mautic.core.form.translation_parent.empty',
                'top_level'   => 'translation',
                'ignore_ids'  => array((int) $options['data']->getId())
            )
        )->addModelTransformer($transformer)
    );

    $builder->add(
        'language',
        'locale',
        array(
            'label'      => 'mautic.core.language',
            'label_attr' => array('class' => 'control-label'),
            'attr'       => array(
                'class'   => 'form-control',
                'tooltip' => 'mautic.page.form.language.help',
            ),
            'required'   => false,
            'empty_data' => 'en'
        )
    );

Implementing Variant (A/B Test) Support to Entities

Mautic has some helper methods with adding support for creating variants of a given entity. This becomes particularly useful for A/B testing.

\Mautic\CoreBundle\Entity\VariantInterface

This Entity interface ensures that everything is needed in order for Mautic to handle the variants correctly for an entity.

\Mautic\CoreBundle\Entity\VariantEntityTrait

This trait provides properties needed to define an Entity’s relationship to other items. In the Entity’s loadMetadata() method, be sure to call $this->addVariantMetadata().

\Mautic\CoreBundle\VariantModelTrait

This trait provides the methods preVariantSaveEntity(), postVariantSaveEntity() and convertVariant(). preVariantSaveEntity() should be executed prior to saveEntity then postVariantSaveEntity(). See example.

<?php
// plugins/HelloWorldBundle/Model/WorldModel.php

// Reset a/b test if applicable
$variantStartDate = new \DateTime();
// setVariantHits is the stat tracker properties for this variant
$resetVariants    = $this->preVariantSaveEntity($entity, ['setVariantHits'], $variantStartDate);

parent::saveEntity($entity, $unlock);

$this->postVariantSaveEntity($entity, $resetVariants, $entity->getRelatedEntityIds(), $variantStartDate);

\Mautic\CoreBundle\Doctrine\VariantMigrationTrait

To ease the generation of schema to match the Entity, use this trait then execute $this->addVariantSchema().

Translated Entity Form

Add variantParent field’s like the code example. In the example, the variantParent value is set in the controller due to a Add A/B Test button is clicked. The specific use for the plugin may require a select list rather than a hidden field. Change this to meet the code’s needs.

<?php 
// plugins/HelloWorldPlugin/Form/Type/WorldType.php

$transformer = new \Mautic\CoreBundle\Form\Transformer\IdToEntityModelTransformer($this->em, 'HelloWorldBundle:World');
$builder->add(
   $builder->create(
       'variantParent',
       'hidden'
   )->addModelTransformer($transformer)
);

Themes

Custom themes for public facing areas of Mautic can be generated but require a bit of Twig experience.

The themes use the same templating formats as Symfony’s twig templates.

Theme Directory Structure

Each theme directory must have at least a config.json file and a html directory with the public facing template’s for the feature’s it supports. All themes should have a html/message.html.twig file. See below for a typical directory structure:

themes/blank/
- - - config.json
- - - thumbnail.png
- - - html/
- - - - - - base.html.twig
- - - - - - email.html.twig
- - - - - - form.html.twig
- - - - - - message.html.twig
- - - - - - page.html.twig

Theme zip package

If you want to make your theme installable via the Theme Manager, make a zip package from it. The zip package name must be the same as the final folder name of the theme in the /themes folder. The contents of the zip folder must contain the theme files directly, not in a subfolder. You can download an existing theme via the Theme Manager to see an example ZIP file.

Theme Config File

 {
   "name": "Theme Name",
   "author": "John Doe",
   "authorUrl": "https://john-doe-the-mautic-theme-builder.com",
   "features": [
     "page",
     "email",
     "form"
   ]
 }

The config file defines the name of the theme and the features it supports.

The config file should return an array with the following keys:

Key Type Description
name string Name of the theme
author string Name of the theme author
authorUrl string URL to the author’s website
features array Array of features the theme supports. Options currently are email, form, and/or page

Theme Thumbnail

The thumbnail should be a screenshot of the theme with demo content. The width x height should be 575 x 600 px. This thumbnail will be available for Mautic users for quick theme preview in the Email edit form, Landing Page edit form and the Theme Manager.

Example

Feature thumbnail name
email thumbnail_email.png
form thumbnail_form.png
page thumbnail_page.png

Slots

Slot definition

The slot can be defined by a single HTML attribute data-slot="{slot type here}". For example, the text slot can be defined even with the demo content.

When the theme is opened in the builder, the div with attribute data-slot="text" will make the text inside the div editable within the inline Froala editor.

Example:

<div data-slot=”text”>
    <a>@JaneDoe</a> has invited you to join Awesome inc!
</div>

The slot types currently built:

Image

Inserts a single image into the div. User can click on it and edit it with options which provides Froala editor (link, change image source, alt text, …)

Button

Inserts a HTML button. User can define text, URL as well as padding, size and position.

Text

Inserts a new text slot which you can edit with a HTML editor, so you can insert even media like images and videos in it.

Separator

Inserts a horizontal line to separate content.

Slot containers

As stated before, users can drag & drop the new slots into the theme. So as a theme developer, you have to define where the user can drop the slots. You can do it again with a single HTML attribute data-slot-container="1".

Example:

<div data-slot-container="1">
    <div data-slot=”text”>
        <a>@JaneDoe</a> has invited you to join Awesome inc!
    </div>
</div>

This way the builder will let users drop the new slots into this container. In the example above there is already one predefined slot which user can move to another container, remove or edit.

This functionality will provide you with lots of creative freedom for designing and developing your own unique email and landing pages. Have a unique design? Share it with the community! We would love to see how you’re using Mautic to engage your audience.

Sections

Sections are full width parts of the theme which can let user to change the background color in the section wrapper (full monitor width) and in the section content itself. Since Mautic 2.7.0 it’s possible to move the sections up or down, delete the sections and even create a new ones with layout of 1,2 or 3 columns.

Section

The section holds the content. It should be centered and should have fixed width. This fixed width should be consistent with all other sections. Section also wraps the content. The section can be any block HTML element with attribute data-section="1".

Example:

<div data-section="1">
    <div data-slot-container="1">
        <div data-slot=”text”>
            <a>@JaneDoe</a> has invited you to join Awesome inc!
        </div>
    </div>
</div>

Section Wrapper

Section wrapper must have 100% width of the browser window. You thus have to split your theme into several “rows” if you want to enable the users to change the background of each section. The section wrapper can be any block HTML element with attribute data-section-wrapper.

Example:

<div data-slot-container="1">
    <div data-section="1">
      <div data-slot-container="1">
          <div data-slot=”text”>
              <a>@JaneDoe</a> has invited you to join Awesome inc!
          </div>
      </div>
    </div>
</div>

Theme HTML Files

Notice that in the directory structure above, there is a base.html.twig file. This is not necessary but used in the example to define the base HTML document which each some of the following files extend.

email.html.twig

{# themes/HelloBundle/html/email.html.twig #} 
<html>
    <head>
        <title>{subject}</title>
    </head>
    <body style="margin:0">
        <div data-section-wrapper="1">
            <center>
                <table data-section="1" style="width: 600;" width="600" cellpadding="0" cellspacing="0">
                    <tbody>
                        <tr>
                            <td>
                                <div data-slot-container="1" style="min-height: 30px">
                                    <div data-slot="text">
                                        <br>
                                        <h2>Hello there!</h2>
                                        <br>
                                        We haven't heard from you for a while...
                                        <br>
                                        <br>
                                        {unsubscribe_text} | {webview_text}
                                        <br>
                                    </div>
                                </div>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </center>
        </div>
    </body>
</html>

This file defines the document for building an email template. Of course this file should follow html based email etiquette. Throughout the document should be output for the slots defined as data attributes.

form.html.twig

{# themes/thellotheme/html/form.html.twig #} 

{% extends ":"~template~":base.html.twig" %}

{% block content %}
    {% if message is defined %}
        <div>
            <h2>{{ message|raw }}</h2>
        </div>
    {% endif %}

    <div>
        {% if header is defined %}
        <h4>{{ header }}</h4>
        {% endif %}
        {{ content|raw }}
    </div>
{% endblock %}

This file generates the html document for a form when viewed via it’s public URL. This does not style the fields of a form. That will be described below.

Each form.html.twig file should output a message, header, and content variables.

Customizing the Form

To provide custom form field templates or to manipulate the form body, create the following directory structure:

themes/HelloWorld/
- - - html/
- - - - - - MauticFormBundle
- - - - - - - - - Builder <– for customizing the form structure itself
- - - - - - - - - Field <– for customizing form field types

Copy from app/bundles/FormBundle/Views/Builder/form.html.php in the theme’s Builder directory or one or more of the fields templates in app/bundles/FormBundle/Views/Field/*.html.php into the theme’s Field directory. Then customize to the desired layout. Note that these must be PHP templates.

Styling the embedded forms

The embedded forms can be styled by the themes/{your theme name}/html/MauticFormBundle/Builder/style.html.twig file. The best way is to copy the content of the default form styles and modify them to your needs.

message.html.twig

{# themes/hellotheme/html/message.html.twig #}

{% extends ":"~template~":base.html.twig" %}

{% block content %}
    <div>
        <h2>{{ message|raw }}</h2>
        {% if content is defined %}
        <div>{{ content|raw }}</div>
        {% endif %}
    </div>
{% endblock %}

This file is a simple message file mainly used as the landing page for when a lead unsubscribes or resubscribes to the system’s emails. But may be used by other areas so should be included in all themes.

It requires echo'ing two variables: message and content. message houses the string message such as “You have been unsubscribed…” content will either be empty or house the HTML of a form that’s been associated with the email as an “unsubscribe form.”

page.html.twig

{# themes/hellotheme/html/message.html.twig #}
{% extends ":"~template~":base.html.twig" %}

{% block content %}
<!DOCTYPE html>
<html>
    <head>
        {% if page is defined %}
        <title>{pagetitle}</title>
        <meta name="description" content="{pagemetadescription}">
        {% endif %}
        {{ outputHeadDeclarations() }}
    </head>
    <body>
        {{ outputScripts('bodyOpen') }}
        {% block content %}{% endblock %}
        {{ outputScripts('bodyClose') }}
    </body>
</html>
{% endblock %}

page.html.twig is exactly the same as email.html.twig except that it’ll be used for landing pages instead. Thus, it can be more robust with the HTML document.

REST API

Mautic provides a REST API to manipulate leads and/or obtain information for various entities of Mautic.

Error Handling

If an OAuth error is encountered, it’ll be a JSON encoded array similar to:

{
  "error": "invalid_grant",
  "error_description": "The access token provided has expired."
}

If a system error encountered, it’ll be a JSON encoded array similar to:

{
    "error": {
        "message": "You do not have access to the requested area/action.",
        "code": 403
    }
}

Mautic version check

In case your API service wants to support several Mautic versions with different features, you might need to check the version of Mautic you communicate with. Since Mautic 2.4.0 the version number is added to all API response headers. The header name is Mautic-Version. With Mautic PHP API library you can get the Mautic version like this:

// Make any API request:
$api = $this->getContext('contacts');
$response = $api->getList('', 0, 1);

// Get the version number from the response header:
$version = $api->getMauticVersion();

$version will be in a semantic versioning format: [major].[minor].[patch]. For example: 2.4.0. If you’ll try it on the latest GitHub version, the version will have -dev at the end. Like 2.5.1-dev.

Authorization

Mautic uses OAuth or Basic Authentication (as of Mautic 2.3.0) for API authorization. It supports both OAuth 1a and OAuth 2; however, as of 1.1.2, the administrator of the Mautic instance must choose one or the other. Of course OAuth 2 is only recommended for servers secured behind SSL. Basic authentication must be enabled in Configuration -> API Settings.

The Mautic administrator should enable the API in the Configuration -> API Settings. This will add the “API Credentials” to the admin menu. A client/consumer ID and secret should then be generated which will be used in the following processes.

All authorization requests should be made to the specific Mautic instances URL, i.e. https://your-mautic.com.

OAuth 1a

<?php
use Mautic\Auth\ApiAuth;

// $initAuth->newAuth() will accept an array of OAuth settings
$settings = array(
    'baseUrl'      => 'https://your-mautic.com',
    'version'      => 'OAuth1a',
    'clientKey'    => '5ad6fa7asfs8fa7sdfa6sfas5fas6asdf8',
    'clientSecret' => 'adf8asf7sf54asf3as4f5sf6asfasf97dd', 
    'callback'     => 'https://your-callback.com'
);

// Initiate the auth object
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);

// Initiate process for obtaining an access token; this will redirect the user to the authorize endpoint and/or set the tokens when the user is redirected back after granting authorization

if ($auth->validateAccessToken()) {
    if ($auth->accessTokenUpdated()) {
        $accessTokenData = $auth->getAccessTokenData();

        //store access token data however you want
    }
}

The OAuth 1a method is recommended for servers that are not behind https. Note that OAuth 1a access tokens do not expire.

OAuth 1a can be a complicated method due to the need to generate a signature for the request. If anything is off with the signature, the request will not be validated.

Authorization

Step One - Obtain a Request Token

The first step is to obtain a request token that will be used when directing the user to the authorization page.

Make a POST to the request token endpoint /oauth/v1/request_token:

POST /oauth/v1/request_token
Authorization: 
        OAuth oauth_callback="https%3A%2F%2Fyour-callback-uri.com",
              oauth_consumer_key="CONSUMER_KEY",
              oauth_nonce="UNIQUE_STRING",
              oauth_signature="GENERATED_REQUEST_SIGNATURE",
              oauth_signature_method="HMAC-SHA1",
              oauth_timestamp="1318467427",
              oauth_version="1.0"

(note that the header has been wrapped for legibility)

Review Generating the Authorization Header on the specifics of generating the OAuth header.

The response will be a query string:

oauth_token=REQUEST_TOKEN&oauth_token_secret=REQUEST_TOKEN_SECRET&oauth_expires_in=3600

Parse the string and use the parameters in the next step as indicated.

Note that the refresh token is only good for the number of seconds specified in oauth_expires_in.

Step Two - Authorization

Now redirect the user to the authorization endpoint oauth/v1/authorize with the request token as part of the URL’s query.

If the callback is something different than what is configured in Mautic, url encode it and include in the query as oauth_callback.

/oauth/v1/authorize?oauth_token=REQUEST_TOKEN&oauth_callback=https%3A%2F%2Fyour-callback-uri.com

The user will login and Mautic will redirect back to the either the consumer’s configured callback or to the oauth_callback included in the query.

The callback will include oauth_token and oauth_verifier in the URL’s query.

Compare the oauth_token in the query with that obtained in step two to ensure they are the same and prevent cross-site request forgery.

oauth_verifier will need to be part of the header generated in step three.

Step Three - Obtain an Access Token

Generate the Authorization header and make a POST to the access token endpoint /oauth/v1/access_token.

When generating the header, the oauth_token_secret returned in step two should be used as the TOKEN_SECRET in the composite key.

oauth_verifier from step two should be part of the Authorization header generated.

POST /oauth/v1/access_token
Authorization: 
        OAuth oauth_callback="https%3A%2F%2Fyour-callback-uri.com",
              oauth_consumer_key="CONSUMER_KEY",
              oauth_nonce="UNIQUE_STRING",
              oauth_signature="GENERATED_REQUEST_SIGNATURE",
              oauth_signature_method="HMAC-SHA1",
              oauth_timestamp="1318467427",
              oauth_verifier="OAUTH_VERIFIER_FROM_STEP_TWO"
              oauth_version="1.0"

(note that the header has been wrapped for legibility)

The response should include a query string with the access token:

oauth_token=ACCESS_TOKEN&oauth_token_secret=ACCESS_TOKEN_SECRET

The oauth_token can be included in the authorize header and the oauth_token_secret should be used as the TOKEN_SECRET in the composite key when signing API requests.

Generating the Authorization Header

The OAuth header may include the following parameters:

Key Description
oauth_callback Optional, URL encoded callback to redirect the user to after authorization. If the callback URL is set in Mautic, this must match.
oauth_consumer_key The consumer key obtained from Mautic’s API Credentials
oauth_nonce Uniquely generated string that should be used only once
oauth_signature Signature generated from all applicable data for the request
oauth_signature_method Method for creating the signature. Currently, only HMAC-SHA1 is supported.
oauth_timestamp Current unix timestamp
oauth_token The access token
oauth_verifier Returned after authorization and used when requesting an access token
oauth_version Should always be 1.0
Step One - Build the Base String

The base string is used to generate the oauth_signature.

The structure of the base string is as follows:

METHOD&URL_ENCODED_URI&NORMALIZED_PARAMETERS

METHOD should be the request method and should always be capitalized.

URL_ENCODED_URI should be the base URI the request is made to. It should not include any query parameters (query parameters should be part of NORMALIZED_PARAMETERS).

NORMALIZED_PARAMETERS should be a url encoded, alphabetically sorted query string of the above oauth parameters (except oauth_signature) plus the parameters of the request (query/post body).

Each key and each value of the parameters must be url encoded individually as well.

Then each url encoded key/value pair should be concatenated into a single string with an ampersand (&) as the glue character.

For example, let’s say a request includes a query of title=Mr&firstname=Joe&lastname=Smith. The process would look something like the following (replacing urlencode() with whatever function is appropriate for the language being used). Of course realistically, natural sort and loop functions would be used.

urlencode(
    urlencode(firstname)=urlencode(Joe)
    &urlencode(lastname)=urlencode(smith)
    &urlencode(oauth_callback)=urlencode(https%3A%2F%2Fyour-callback-uri.com)
    &urlencode(oauth_consumer_key)=urlencode(kdjafs7fsdf86ads7a98a87df6ad9fsf98ad7f)
    &urlencode(oauth_nonce)=urlencode(ak877asdf6adf68asd9fas)
    &urlencode(oauth_signature_method)=urlencode(HMAC-SHA1)
    &urlencode(oauth_timestamp)=urlencode(1437604736)
    &urlencode(oauth_version)=urlencode(1.0)
    &urlencode(title)=urlencode(Mr)
)

Put all together, a base string might look like:

GET&http%3A%2F%2Fyour-mautic.com%2Fapi&firstname%3DJoe%26lastName%3DSmith%26oauth_callback%3Dhttps%253A%252F%252Fyour-callback-uri.com%26oauth_consumer_key%3Dkdjafs7fsdf86ads7a98a87df6ad9fsf98ad7f%26oauth_nonce%3Dak877asdf6adf68asd9fas%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1437604736%26oauth_version%3D1.0%26title%3DMr
Step Two - Build the Composite Key

The composite key is used to encrypt the base string. It is composed of the consumer secret and the token secret if available (post authorization) combined with an ampersand (&).

CLIENT_SECRET&TOKEN_SECRET

If the token secret is not available, i.e. during the request token process, then an ampersand (&) should still be used.

 CLIENT_SECRET&
 

Step Three - Generate the Signature

Now, using the base string and the composite key, the signature can be generated using the appropriate HMAC function available to the language used. The signature generated should then be base64 encoded prior to being used in the Authorization header.

 base64_encode(
    hmac_sha1(BASE_STRING, COMPOSITE_KEY)
 )
 

The resulting string should then be used included in the header as oauth_signature.

Signing the API Request

To sign the API request, generate the Authorization header using the obtained access token.

POST /api/leads/new
Authorization: 
        OAuth oauth_callback="https%3A%2F%2Fyour-callback-uri.com",
              oauth_consumer_key="CONSUMER_KEY",
              oauth_nonce="UNIQUE_STRING",
              oauth_signature="GENERATED_REQUEST_SIGNATURE",
              oauth_signature_method="HMAC-SHA1",
              oauth_timestamp="1318467427",
              oauth_token="ACCESS_TOKEN"
              oauth_version="1.0"
              
title=Mr&firstname=Joe&lastname=Smith

(note that the header has been wrapped for legibility)

OAuth 2

<?php
use Mautic\Auth\ApiAuth;

// $initAuth->newAuth() will accept an array of OAuth settings
$settings = array(
    'baseUrl'      => 'https://your-mautic.com',
    'version'      => 'OAuth2',
    'clientKey'    => '5ad6fa7asfs8fa7sdfa6sfas5fas6asdf8',
    'clientSecret' => 'adf8asf7sf54asf3as4f5sf6asfasf97dd', 
    'callback'     => 'https://your-callback.com'
);

// Initiate the auth object
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);

// Initiate process for obtaining an access token; this will redirect the user to the authorize endpoint and/or set the tokens when the user is redirected back after granting authorization

if ($auth->validateAccessToken()) {
    if ($auth->accessTokenUpdated()) {
        $accessTokenData = $auth->getAccessTokenData();

        //store access token data however you want
    }
}

Authorization

Step One - Obtain Authorization Code

Redirect the user to the authorize endpoint oauth/v2/authorize:

GET /oauth/v2/authorize?
    client_id=CLIENT_ID
    &grant_type=authorization_code
    &redirect_uri=https%3A%2F%2Fyour-redirect-uri.com%2Fcallback
    &response_type=code
    &state=UNIQUE_STATE_STRING

(note that the query has been wrapped for legibility)

The user will be prompted to login. Once they do, Mautic will redirect back to the URL specified in redirect_uri with a code appended to the query.

It may look something like: https://your-redirect-uri.com?code=UNIQUE_CODE_STRING&state=UNIQUE_STATE_STRING

The state returned should be compared against the original to ensure nothing has been tampered with.

Step Two - Replace with an Access Token

Obtain the value of the code from Step One then immediately POST it back to the access token endpoint oauth/v2/token with:

POST /oauth/v2/token

client_id=CLIENT_ID
    &client_secret=CLIENT_SECRET
    &grant_type=authorization_code
    &redirect_uri=https%3A%2F%2Fyour-redirect-uri.com%2Fcallback
    &code=UNIQUE_CODE_STRING

(note that the post body has been wrapped for legibility)

The response returned should be a JSON encoded string:

    {
        access_token: "ACCESS_TOKEN",
        expires_in: 3600,
        token_type: "bearer",
        scope: "",
        refresh_token: "REFRESH_TOKEN"
    }

This data should be stored in a secure location and used to authenticate API requests.

Refresh Tokens

The response’s expires_in is the number of seconds the access token is good for and may differ based on what is configured in Mautic. The code handling the authorization process should generate an expiration timestamp based on that value. For example <?php $expiration = time() + $response['expires_in']; ?>. If the access token has expired, the refresh_token should be used to obtain a new access token.

The refresh token is by default good for 14 days in which the user will need to reauthorize the application with Mautic. However, the refresh token’s expiration time is configurable through Mautic’s Configuration.

To obtain a new access token, a POST should be made to the access token’s endpoint oauth/v2/token using the refresh_token grant type.

POST /oauth/v2/token
    
client_id=CLIENT_ID
    &client_secret=CLIENT_SECRET
    &grant_type=refresh_token
    &refresh_token=REFRESH_TOKEN
    &redirect_uri=https%3A%2F%2Fyour-redirect-uri.com%2Fcallback

(note that the post body has been wrapped for legibility)

The response returned should be a JSON encoded string:

    {
        access_token: "NEW_ACCESS_TOKEN",
        expires_in: 3600,
        token_type: "bearer",
        scope: "",
        refresh_token: "REFRESH_TOKEN"
    }

Authenticating the API Request

Authenticating the API request with OAuth2 is easy. Choose one of the following methods that is appropriate for the application’s needs.

Authorization Header

By using an authorization header, any request method can be authenticated.

However, note that this method requires that the server Mautic is installed on passes headers to PHP or has access to the apache_request_headers() function. apache_request_headers() is not available to PHP running under fcgi.

Authorization: Bearer ACCESS_TOKEN
Method Specific

The access token can also be appended to the query or included in the body of a POST.

GET https://your-mautic.com/api/leads?access_token=ACCESS_TOKEN
POST https://your-mautic.com/api/leads/new

firstname=John&lastname=Smith&access_token=ACCESS_TOKEN

Basic Authentication

As of Mautic 2.3.0, support for basic authentication can be enabled through Mautic’s Configuration -> API Settings. As with OAuth2, it is only recommended to use basic authentication over HTTPS.

To authorize a request for basic authentication, set an Authorization header.

  1. Combine the username and password of a Mautic user with a colon :. For example, user:password.
  2. Base64 encode the string from above. dXNlcjpwYXNzd29yZA==.
  3. Add an Authorization header to each API request as Authorization: Basic dXNlcjpwYXNzd29yZA==

Libraries

PHP Library

Mautic provides a PHP library on Github. It is recommended that it be used in PHP projects. Other languages will need to use custom means and/or a 3rd party library to handle the OAuth/request processes.

Install via Composer

To install using composer, simply run composer require mautic/api-library.

Install Manually

Download the package from Github. Extract then include the following code in your project (of course change the file path if needed):

require_once __DIR__ . '/lib/Mautic/MauticApi.php';

Endpoints

Assets

Use this endpoint to obtain details on Mautic’s assets.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$assetApi = $api->newApi("assets", $auth, $apiUrl);

Get Asset

<?php

//...
$asset = $assetApi->get($id);
{
    "asset": {
        "id": 1,
        "title": "Product Whitepaper",
        "description": "Some description",
        "alias": "whitepaper",
        "language": "en",
        "isPublished": true,
        "publishUp": "2015-06-07T06:28:27+00:00",
        "publishDown": "2015-06-30T06:28:27+00:00",
        "dateAdded": "2015-06-07T06:28:27+00:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-06-010T09:30:47+00:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "downloadCount": 10,
        "uniqueDownloadCount": 8,
        "revision": 1,
        "category": "Whitepapers",
        "extension": "pdf",
        "mime": "application/pdf",
        "size": 269128,
        "downloadUrl": "https://your-mautic.com/asset/1:whitepaper"
    }
}

Get an individual asset by ID.

HTTP Request

GET /assets/ID

Response

Expected Response Code: 200

See JSON code example.

Asset Properties

Name Type Description
id int ID of the asset
title string Title/name of the asset
description string/null Description of the asset
alias string Used to generate the URL for the asset
language string Locale of the asset
isPublished bool Published state
publishUp datetime/null Date/time when the asset should be published
publishDown datetime/null Date/time the asset should be un published
dateAdded datetime Date/time asset was created
createdBy int ID of the user that created the asset
createdByUser string Name of the user that created the asset
dateModified datetime/null Date/time asset was last modified
modifiedBy int ID of the user that last modified the asset
modifiedByUser string Name of the user that last modified the asset
downloadCount int Total number of downloads
uniqueDownloadCount int Unique number of downloads
revision int Revision version
category string/null Name of the category
extension string Extension of the asset
mime string Mime type of the asset
size int Filesize of the asset in bytes
downloadUrl string Public download URL for the asset

List Assets

<?php
// ...

$assets = $assetApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "assets": [
        {
            "id": 1,
            "title": "Product Whitepaper",
            "description": "Some description",
            "alias": "whitepaper",
            "language": "en",
            "isPublished": true,
            "publishUp": "2015-06-07T06:28:27+00:00",
            "publishDown": "2015-06-30T06:28:27+00:00",
            "dateAdded": "2015-06-07T06:28:27+00:00",
            "createdBy": 1,
            "createdByUser": "Joe Smith",
            "dateModified": "2015-06-010T09:30:47+00:00",
            "modifiedBy": 1,
            "modifiedByUser": "Joe Smith",
            "downloadCount": 10,
            "uniqueDownloadCount": 8,
            "revision": 1,
            "category": "Whitepapers",
            "extension": "pdf",
            "mime": "application/pdf",
            "size": 269128,
            "downloadUrl": "https://your-mautic.com/asset/1:whitepaper"
        }
    ]
}

HTTP Request

GET /assets

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Asset.

Create Asset

<?php 

/**
 * Local asset example
 */
// Upload a local file first
$apiContextFiles = $this->getContext('files');
$apiContextFiles->setFolder('assets');
$fileRequest = array(
    'file' => dirname(__DIR__).'/'.'mauticlogo.png'
);
$response = $apiContextFiles->create($fileRequest);

$data = array(
    'title' => 'Mautic Logo sent as a API request',
    'storageLocation' => 'local',
    'file' => $response['file']['name']
);

$asset = $assetApi->create($data);


/**
 * Remote asset example
 */
$data = array(
    'title' => 'PDF sent as a API request',
    'storageLocation' => 'remote',
    'file' => 'https://www.mautic.org/media/logos/logo/Mautic_Logo_DB.pdf'
);

$asset = $assetApi->create($data);

Create a new asset. There are 2 options: local or remote asset.

HTTP Request

POST /assets/new

Post Parameters

Name Description
title string
storageLocation string
file string

Response

Expected Response Code: 201

Properties

Same as Get Asset.

Edit Asset

<?php

$id   = 1;
$data = array(
    'type' => 'general',
);

// Create new a asset of ID 1 is not found?
$createIfNotFound = true;

$asset = $assetApi->edit($id, $data, $createIfNotFound);

Edit a new asset. Asset that this supports PUT or PATCH depending on the desired behavior.

PUT creates a asset if the given ID does not exist and clears all the asset information, adds the information from the request. PATCH fails if the asset with the given ID does not exist and updates the asset field values with the values form the request.

HTTP Request

To edit a asset and return a 404 if the asset is not found:

PATCH /assets/ID/edit

To edit a asset and create a new one if the asset is not found:

PUT /assets/ID/edit

Post Parameters

Name Description
title string
storageLocation string
file string

Response

If PUT, the expected response code is 200 if the asset was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Asset.

Delete Asset

<?php

$asset = $assetApi->delete($id);

Delete a asset. In case of local storage location, the local file will be deleted as well.

HTTP Request

DELETE /assets/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Asset.

Campaigns

Use this endpoint to obtain details on Mautic’s campaigns.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth    = new ApiAuth();
$auth        = $initAuth->newAuth($settings);
$apiUrl      = "https://your-mautic.com";
$api         = new MauticApi();
$campaignApi = $api->newApi("campaigns", $auth, $apiUrl);

Get Campaign

<?php

//...
$campaign = $campaignApi->get($id);
{
    "campaign": {
        "id": 3,
        "name": "Email A/B Test",
        "description": null,
        "isPublished": true,
        "publishUp": null,
        "publishDown": null,
        "dateAdded": "2015-07-15T15:06:02-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-20T13:11:56-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "category": null,
        "events": {
            "28": {
                "id": 28,
                "type": "lead.changepoints",
                "eventType": "action",
                "name": "Adjust lead points",
                "description": null,
                "order": 1,
                "properties": {
                  "points": 20
                },
                "triggerDate": null,
                "triggerInterval": 1,
                "triggerIntervalUnit": "d",
                "triggerMode": "immediate",
                "children": [],
                "parent": null,
                "decisionPath": null
            }
        }
    }
}

Get an individual campaign by ID.

HTTP Request

GET /campaigns/ID

Response

Expected Response Code: 200

See JSON code example.

Campaign Properties

Name Type Description
id int ID of the campaign
name string Name of the campaign
description string/null Description of the campaign
alias string Used to generate the URL for the campaign
isPublished bool Published state
publishUp datetime/null Date/time when the campaign should be published
publishDown datetime/null Date/time the campaign should be un published
dateAdded datetime Date/time campaign was created
createdBy int ID of the user that created the campaign
createdByUser string Name of the user that created the campaign
dateModified datetime/null Date/time campaign was last modified
modifiedBy int ID of the user that last modified the campaign
modifiedByUser string Name of the user that last modified the campaign
events array Array of Event entities for the campaign. See below.

Event Properties

Name Type Description
id int ID of the event
name string Name of the event
description string Optional description for the event
type string Type of event
eventType string “action” or “decision”
order int Order in relation to the other events (used for levels)
properties object Configured properties for the event
triggerMode string “immediate”, “interval” or “date”
triggerDate datetime/null Date/time of when the event should trigger if triggerMode is “date”
triggerInterval int/null Interval for when the event should trigger
triggerIntervalUnit string Interval unit for when the event should trigger. Options are i = minutes, h = hours, d = days, m = months, y = years
children array Array of this event’s children ,
parent object/null This event’s parent
decisionPath string/null If the event is connected into an action, this will be “no” for the non-decision path or “yes” for the actively followed path.

List Campaigns

<?php
// ...

$campaigns = $campaignApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "campaigns": [
        {
            "id": 3,
            "name": "Welcome Campaign",
            "description": null,
            "isPublished": true,
            "publishUp": null,
            "publishDown": null,
            "dateAdded": "2015-07-15T15:06:02-05:00",
            "createdBy": 1,
            "createdByUser": "Joe Smith",
            "dateModified": "2015-07-20T13:11:56-05:00",
            "modifiedBy": 1,
            "modifiedByUser": "Joe Smith",
            "category": null,
            "events": {
                "22": {
                    "id": 22,
                    "type": "email.send",
                    "eventType": "action",
                    "name": "Send welcome email",
                    "description": null,
                    "order": 1,
                    "properties": {
                        "email": 1
                    },
                    "triggerMode": "immediate",
                    "triggerDate": null,
                    "triggerInterval": null,
                    "triggerIntervalUnit": null,
                    "children": [],
                    "parent": null,
                    "decisionPath": null
                },
                "28": {
                    "id": 28,
                    "type": "lead.changepoints",
                    "eventType": "action",
                    "name": "Adjust lead points",
                    "description": null,
                    "order": 2,
                    "properties": {
                        "points": 20
                    },
                    "triggerMode": "immediate",                
                    "triggerDate": null,
                    "triggerInterval": null,
                    "triggerIntervalUnit": null,
                    "children": [],
                    "parent": null,
                    "decisionPath": null
                }
            }
        }
    ]
}

HTTP Request

GET /campaigns

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Campaign.

List Campaign Contacts

This endpoint is basically an alias for the stats endpoint with ‘campaign_leads’ table and campaign_id specified. Other parameters are the same as in the stats endpoint.

<?php
// ...

$response = $campaignApi->getContacts($campaignId, $start, $limit, $order, $where);
{  
  "total":"1",
  "contacts":[  
    {  
      "campaign_id":"311",
      "lead_id":"3126",
      "date_added":"2017-01-25 15:11:10",
      "manually_removed":"0",
      "manually_added":"1"
    }
  ]
}

HTTP Request

GET /campaigns/ID/contacts

Query Parameters

Response

Expected Response Code: 200

See JSON code example.

Create Campaign

<?php 

$data = array(
    'name'        => 'Campaign A',
    'description' => 'This is my first campaign created via API.',
    'isPublished' => 1
);

$campaign = $campaignApi->create($data);

Create a new campaign. To see more advanced example with campaing events and so on, see the unit tests.

HTTP Request

POST /campaigns/new

Post Parameters

Name Description
name Campaign name is the only required field
alias string
description A description of the campaign.
isPublished A value of 0 or 1

Response

Expected Response Code: 201

Properties

Same as Get Campaign.

Edit Campaign

<?php

$id   = 1;
$data = array(
    'name'        => 'New campaign name',
    'isPublished' => 0
);

// Create new a campaign of ID 1 is not found?
$createIfNotFound = true;

$campaign = $campaignApi->edit($id, $data, $createIfNotFound);

Edit a new campaign. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a campaign if the given ID does not exist and clears all the campaign information, adds the information from the request. PATCH fails if the campaign with the given ID does not exist and updates the campaign field values with the values form the request.

HTTP Request

To edit a campaign and return a 404 if the campaign is not found:

PATCH /campaigns/ID/edit

To edit a campaign and create a new one if the campaign is not found:

PUT /campaigns/ID/edit

Post Parameters

Name Description
name Campaign name is the only required field
alias Name alias generated automatically if not set
description A description of the campaign.
isPublished A value of 0 or 1

Response

If PUT, the expected response code is 200 if the campaign was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Campaign.

Delete Campaign

<?php

$campaign = $campaignApi->delete($id);

Delete a campaign.

HTTP Request

DELETE /campaigns/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Campaign.

Add Contact to a Campaign

<?php

//...
$response = $campaignApi->addContact($contactId, $campaignId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually add a contact to a specific campaign.

HTTP Request

POST /campaigns/CAMPAIGN_ID/contact/add/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Remove Contact from a Campaign

<?php

//...
$response = $listApi->removeContact($contactId, $listId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually remove a contact from a specific campaign.

HTTP Request

POST /campaigns/CAMPAIGN_ID/contact/remove/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Categories

Use this endpoint to obtain details on Mautic’s categories or to manipulate category memberships.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth    = new ApiAuth();
$auth        = $initAuth->newAuth($settings);
$apiUrl      = "https://your-mautic.com";
$api         = new MauticApi();
$categoryApi = $api->newApi("categories", $auth, $apiUrl);

Get Category

<?php

//...
$category = $categoryApi->get($id);
{  
  "category":{  
    "id":221,
    "title":"test",
    "alias":"test4",
    "description":null,
    "color":null,
    "bundle":"asset"
  }
}

Get an individual category by ID.

HTTP Request

GET /categories/ID

Response

Expected Response Code: 200

See JSON code example.

Category Properties

Name Type Description
id int ID of the category
isPublished boolean Whether the category is published
dateAdded datetime Date/time category was created
createdBy int ID of the user that created the category
createdByUser string Name of the user that created the category
dateModified datetime/null Date/time category was last modified
modifiedBy int ID of the user that last modified the category
modifiedByUser string Name of the user that last modified the category
title string The category title
alias string The category alias
description string The category description
color string The category color
bundle string The bundle where the category will be available

List Contact Categories

<?php

//...
$categories = $categoryApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
  "total":8,
  "categories":[  
    {  
      "id":1,
      "title":"Bold",
      "alias":"bold",
      "description":null,
      "color":"b36262",
      "bundle":"point"
    },
    [...]
  ]
}

Returns a list of contact categories available to the user. This list is not filterable.

HTTP Request

GET /categories

Response

Expected Response Code: 200

See JSON code example.

Category Properties

Name Type Description
id int ID of the category
isPublished boolean Whether the category is published
dateAdded datetime Date/time category was created
createdBy int ID of the user that created the category
createdByUser string Name of the user that created the category
dateModified datetime/null Date/time category was last modified
modifiedBy int ID of the user that last modified the category
modifiedByUser string Name of the user that last modified the category
title string The category title
alias string The category alias
description string The category description
color string The category color
bundle string The bundle where the category will be available

Create Category

<?php 

$data = array(
    'categoryname' => 'test',
    'categoryemail' => 'test@category.com',
    'categorycity' => 'Raleigh',
);

$category = $categoryApi->create($data);

Create a new category.

HTTP Request

POST /categories/new

Post Parameters

Name Description
title string
bundle string

Response

Expected Response Code: 201

Properties

Same as Get Category.

Edit Category

<?php

$id   = 1;
$data = array(
    'title' => 'test',
    'bundle' => 'asset'
);

// Create new a category of ID 1 is not found?
$createIfNotFound = true;

$category = $categoryApi->edit($id, $data, $createIfNotFound);

Edit a new category. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a category if the given ID does not exist and clears all the category information, adds the information from the request. PATCH fails if the category with the given ID does not exist and updates the category field values with the values form the request.

HTTP Request

To edit a category and return a 404 if the category is not found:

PATCH /categories/ID/edit

To edit a category and create a new one if the category is not found:

PUT /categories/ID/edit

Post Parameters

Name Description
title string
bundle string

Response

If PUT, the expected response code is 200 if the category was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Category.

Delete Category

<?php

$category = $categoryApi->delete($id);

Delete a category.

HTTP Request

DELETE /categories/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Category.

Companies

Use this endpoint to obtain details on Mautic’s companies or to manipulate contact-company memberships.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth   = new ApiAuth();
$auth       = $initAuth->newAuth($settings);
$apiUrl     = "https://your-mautic.com";
$api        = new MauticApi();
$companyApi = $api->newApi("companies", $auth, $apiUrl);

Get Company

<?php

//...
$company = $companyApi->get($id);
{  
    "company":{  
        "isPublished":true,
        "dateAdded":"2016-10-25T09:46:36+00:00",
        "createdBy":1,
        "createdByUser":"John Doe",
        "dateModified":null,
        "modifiedBy":null,
        "modifiedByUser":null,
        "id":176,
        "fields":{  
            "core":{  
                "companywebsite":{  
                    "id":"91",
                    "label":"Website",
                    "alias":"companywebsite",
                    "type":"url",
                    "group":"core",
                    "field_order":"8",
                    "object":"company",
                    "value":null
                },
                [...]
            },
            "professional":{  
                "companyannual_revenue":{  
                    "id":"90",
                    "label":"Annual Revenue",
                    "alias":"companyannual_revenue",
                    "type":"number",
                    "group":"professional",
                    "field_order":"10",
                    "object":"company",
                    "value":null
                },
                [...]
            },
            "other":[],
            "all":{  
                "companywebsite":null,
                "companycountry":null,
                "companyzipcode":null,
                "companystate":null,
                "companycity":"Raleigh",
                "companyphone":null,
                "companyemail":"test@company.com",
                "companyaddress2":null,
                "companyaddress1":null,
                "companyname":"test",
                "companyannual_revenue":null,
                "companyfax":null,
                "companynumber_of_employees":null,
                "companydescription":null
            }
        }
    }
}

Get an individual company by ID.

HTTP Request

GET /companies/ID

Response

Expected Response Code: 200

See JSON code example.

Company Properties

Name Type Description
id int ID of the company
isPublished boolean Whether the company is published
dateAdded datetime Date/time company was created
createdBy int ID of the user that created the company
createdByUser string Name of the user that created the company
dateModified datetime/null Date/time company was last modified
modifiedBy int ID of the user that last modified the company
modifiedByUser string Name of the user that last modified the company
fields array Custom fields for the company

List Contact Companies

<?php

//...
$companies = $companyApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
  "total": 13,
  "companies": [{  
    "isPublished":true,
    "dateAdded":"2016-10-25T09:46:36+00:00",
    "createdBy":1,
    "createdByUser":"John Doe",
    "dateModified":null,
    "modifiedBy":null,
    "modifiedByUser":null,
    "id":176,
    "fields":{  
        "core":{  
            "companywebsite":{  
                "id":"91",
                "label":"Website",
                "alias":"companywebsite",
                "type":"url",
                "group":"core",
                "field_order":"8",
                "object":"company",
                "value":null
            },
            [...]
        },
        "professional":{  
            "companyannual_revenue":{  
                "id":"90",
                "label":"Annual Revenue",
                "alias":"companyannual_revenue",
                "type":"number",
                "group":"professional",
                "field_order":"10",
                "object":"company",
                "value":null
            },
            [...]
        },
        "other":[],
        "all":{  
            "companywebsite":null,
            "companycountry":null,
            "companyzipcode":null,
            "companystate":null,
            "companycity":"Raleigh",
            "companyphone":null,
            "companyemail":"test@company.com",
            "companyaddress2":null,
            "companyaddress1":null,
            "companyname":"test",
            "companyannual_revenue":null,
            "companyfax":null,
            "companynumber_of_employees":null,
            "companydescription":null
        }
    }
  },
  [...]
  ]
}

Returns a list of contact companies available to the user. This list is not filterable.

HTTP Request

GET /companies

Response

Expected Response Code: 200

See JSON code example.

Company Properties

Name Type Description
id int ID of the company
isPublished boolean Whether the company is published
dateAdded datetime Date/time company was created
createdBy int ID of the user that created the company
createdByUser string Name of the user that created the company
dateModified datetime/null Date/time company was last modified
modifiedBy int ID of the user that last modified the company
modifiedByUser string Name of the user that last modified the company
fields array Custom fields for the company

Create Company

<?php 

$data = array(
    'companyname' => 'test',
    'companyemail' => 'test@company.com',
    'companycity' => 'Raleigh',
);

$company = $companyApi->create($data);

Create a new company.

HTTP Request

POST /companies/new

Post Parameters

Name Description
companyname Company name is the only required field. Other company fields can be sent with a value
isPublished A value of 0 or 1

Response

Expected Response Code: 201

Properties

Same as Get Company.

Edit Company

<?php

$id   = 1;
$data = array(
    'companyname' => 'test',
    'companyemail' => 'test@company.com',
    'companycity' => 'Raleigh',
);

// Create new a company of ID 1 is not found?
$createIfNotFound = true;

$company = $companyApi->edit($id, $data, $createIfNotFound);

Edit a new company. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a company if the given ID does not exist and clears all the company information, adds the information from the request. PATCH fails if the company with the given ID does not exist and updates the company field values with the values form the request.

HTTP Request

To edit a company and return a 404 if the company is not found:

PATCH /companies/ID/edit

To edit a company and create a new one if the company is not found:

PUT /companies/ID/edit

Post Parameters

Name Description
companyname Company name is the only required field. Other company fields can be sent with a value
isPublished A value of 0 or 1

Response

If PUT, the expected response code is 200 if the company was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Company.

Delete Company

<?php

$company = $companyApi->delete($id);

Delete a company.

HTTP Request

DELETE /companies/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Company.

Add Contact to a Company

<?php

//...
$response = $companyApi->addContact($contactId, $companyId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually add a contact to a specific company.

HTTP Request

POST /companies/COMPANY_ID/contact/add/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Remove Contact from a Company

<?php

//...
$response = $companyApi->removeContact($contactId, $companyId);
if (empty($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually remove a contact to a specific company.

HTTP Request

POST /companies/COMPANY_ID/contact/remove/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Contacts

Use this endpoint to manipulate and obtain details on Mautic’s contacts.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth   = new ApiAuth();
$auth       = $initAuth->newAuth($settings);
$apiUrl     = "https://your-mautic.com";
$api        = new MauticApi();
$contactApi = $api->newApi("contacts", $auth, $apiUrl);

Get Contact

<?php

//...
$contact = $contactApi->get($id);
    "contact": {
        "id": 47,
        "dateAdded": "2015-07-21T12:27:12-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-21T14:12:03-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "owner": {
            "id": 1,
            "username": "joesmith",
            "firstName": "Joe",
            "lastName": "Smith"
        },
        "points": 10,
        "lastActive": "2015-07-21T14:19:37-05:00",
        "dateIdentified": "2015-07-21T12:27:12-05:00",
        "color": "ab5959",
        "ipAddresses": {
            "111.111.111.111": {
                "ipAddress": "111.111.111.111",
                "ipDetails": {
                    "city": "",
                    "region": "",
                    "country": "",
                    "latitude": "",
                    "longitude": "",
                    "isp": "",
                    "organization": "",
                    "timezone": ""
                }
            }
        },
        "fields": {
            "core": {
                "title": {
                    "id": "1",
                    "label": "Title",
                    "alias": "title",
                    "type": "lookup",
                    "group": "core",
                    "value": "Mr"
                },
                "firstname": {
                    "id": "2",
                    "label": "First Name",
                    "alias": "firstname",
                    "type": "text",
                    "group": "core",
                    "value": "Jim"
                },

                "...": {
                    "..." : "..."
                }

            },
            "social": {
                "twitter": {
                    "id": "17",
                    "label": "Twitter",
                    "alias": "twitter",
                    "type": "text",
                    "group": "social",
                    "value": "jimcontact"
                },

                "...": {
                    "..." : "..."
                }

            },
            "personal": [],
            "professional": [],
            "all": {
                "title": "Mr",
                "firstname": "Jim",
                "twitter": "jimcontact",

                "...": "..."
            }
        }
    }

Get an individual contact by ID.

HTTP Request

GET /contacts/ID

Response

Expected Response Code: 200

See JSON code example.

** Contact Properties **

Name Type Description
id int ID of the contact
dateAdded datetime Date/time contact was created
createdBy int ID of the user that created the contact
createdByUser string Name of the user that created the contact
dateModified datetime/null Date/time contact was last modified
modifiedBy int ID of the user that last modified the contact
modifiedByUser string Name of the user that last modified the contact
owner object User object that owns the contact.
points int Contact’s current number of points
lastActive datetime/null Date/time for when the contact was last recorded as active
dateIdentified datetime/null Date/time when the contact identified themselves
color string Hex value given to contact from Point Trigger definitions based on the number of points the contact has been awarded
ipAddresses array Array of IPs currently associated with this contact
fields array Array of all contact fields with data grouped by field group. See JSON code example for format. This array includes an “all” key that includes an single level array of fieldAlias => contactValue pairs.

List Contacts

<?php

//...
$contacts = $contactApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": "1",
    "contacts": [
        {
            "id": 47,
            "isPublished": true,
            "dateAdded": "2015-07-21T12:27:12-05:00",
            "createdBy": 1,
            "createdByUser": "Joe Smith",
            "dateModified": "2015-07-21T14:12:03-05:00",
            "modifiedBy": 1,
            "modifiedByUser": "Joe Smith",
            "owner": {
                "id": 1,
                "username": "joesmith",
                "firstName": "Joe",
                "lastName": "Smith"
            },
            "points": 10,
            "lastActive": "2015-07-21T14:19:37-05:00",
            "dateIdentified": "2015-07-21T12:27:12-05:00",
            "color": "ab5959",
            "ipAddresses": {
                "111.111.111.111": {
                    "ipAddress": "111.111.111.111",
                    "ipDetails": {
                        "city": "",
                        "region": "",
                        "country": "",
                        "latitude": "",
                        "longitude": "",
                        "isp": "",
                        "organization": "",
                        "timezone": ""
                    }
                }
            },
            "fields": {
                "core": {
                    "title": {
                        "id": "1",
                        "label": "Title",
                        "alias": "title",
                        "type": "lookup",
                        "group": "core",
                        "value": "Mr"
                    },
                    "firstname": {
                        "id": "2",
                        "label": "First Name",
                        "alias": "firstname",
                        "type": "text",
                        "group": "core",
                        "value": "Jim"
                    },

                    "...": {
                        "..." : "..."
                    }
                },
                "social": {
                    "twitter": {
                        "id": "17",
                        "label": "Twitter",
                        "alias": "twitter",
                        "type": "text",
                        "group": "social",
                        "value": "jimcontact"
                    },

                    "...": {
                        "..." : "..."
                    }
                },
                "personal": [],
                "professional": [],
                "all": {
                    "title": "Mr",
                    "firstname": "Jim",
                    "twitter": "jimcontact",

                    "...": "..."    
                }
            }
        }
    ]
}

Get a list of contacts.

HTTP Request

GET /contacts

** Query Parameters **

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

** Properties **

Same as Get Contact.

Create Contact

<?php 

$data = array(
    'firstname' => 'Jim',
    'lastname'  => 'Contact',
    'email'     => 'jim@his-site.com',
    'ipAddress' => $_SERVER['REMOTE_ADDR']
);

$contact = $contactApi->create($data);

Create a new contact.

HTTP Request

POST /contacts/new

** Post Parameters **

Name Description
* Any contact field alias can be posted as a parameter. For example, firstname, lastname, email, etc.
ipAddress IP address to associate with the contact
lastActive Date/time in UTC; preferablly in the format of Y-m-d H:m:i but if that format fails, the string will be sent through PHP’s strtotime then formatted
owner ID of a Mautic user to assign this contact to

Response

Expected Response Code: 201

** Properties **

Same as Get Contact.

Edit Contact

<?php

$id   = 1;
$data = array(
    'email'     => 'jim-new-address@his-site.com',
    'ipAddress' => $_SERVER['REMOTE_ADDR']
);

// Create new a contact of ID 1 is not found?
$createIfNotFound = true;

$contact = $contactApi->edit($id, $data, $createIfNotFound);

Edit a new contact. Note that this supports PUT or PATCH depending on the desired behavior.

** PUT ** creates a contact if the given ID does not exist and clears all the contact information, adds the information from the request. PATCH fails if the contact with the given ID does not exist and updates the contact field values with the values form the request.

HTTP Request

To edit a contact and return a 404 if the contact is not found:

PATCH /contacts/ID/edit

To edit a contact and create a new one if the contact is not found:

PUT /contacts/ID/edit

** Post Parameters **

Name Description
* Any contact field alias can be posted as a parameter. For example, firstname, lastname, email, etc.
ipAddress IP address to associate with the contact
lastActive Date/time in UTC; preferably in the format of Y-m-d H:m:i but if that format fails, the string will be sent through PHP’s strtotime then formatted
owner ID of a Mautic user to assign this contact to

Response

If PUT, the expected response code is 200 if the contact was edited or 201 if created.

If PATCH, the expected response code is 200.

** Properties **

Same as Get Contact.

Delete Contact

<?php

$contact = $contactApi->delete($id);

Delete a contact.

HTTP Request

DELETE /contacts/ID/delete

Response

Expected Response Code: 200

** Properties **

Same as Get Contact.

Add Do Not Contact

<?php

$data = array(
     'eventName' => 'Score via api',
     'actionName' => 'Adding',
 );
$contactApi->addDnc($contactId, $channel, $reason, $channelId, $comments);

Add a contact to DNC list

HTTP Request

To add Do Not Contact entry to a contact:

PATCH /contacts/ID/dnc/add/CHANNEL

** Data Parameters (optional) **

Name Description
channel Channel of DNC. For example ‘email’, 'sms’… Default is email.
reason Int value of the reason. Use Contacts constants: Contacts::UNSUBSCRIBED, Contacts::BOUNCED, Contacts::MANUAL. Default is Manual
channelId ID of the entity which was the reason for unsubscription
comments A text describing details of DNC entry

Response

Same as Get Contact.

Remove from Do Not Contact

<?php
$contactApi->addDnc($contactId, $channel);

Remove a contact from DNC list

HTTP Request

To remove Do Not Contact entry from a contact:

PATCH /contacts/ID/dnc/remove/CHANNEL

** Data Parameters (optional) **

Name Description
channel Channel of DNC. For example 'email’, 'sms’… Default is email.

Response

Same as Get Contact.

Add Points

<?php

$data = array(
     'eventName' => 'Score via api',
     'actionName' => 'Adding',
 );
$contactApi->addPoints($contactId, $pointDelta, $data);

Add contact points

HTTP Request

To add points to a contact and return a 404 if the lead is not found:

POST /contacts/ID/points/plus/POINTS

** Data Parameters (optional) **

Name Description
eventName Name of the event
actionName Name of the action

Response

Expected Response Code: 200 json { "success": true }

Subtract Points

<?php

$data = array(
     'eventname' => 'Score via api',
     'actionname' => 'Subtracting',
 );
$contactApi->subtractPoints($contactId, $pointDelta, $data);

Subtract contact points

HTTP Request

To subtract points from a contact and return a 404 if the contact is not found:

POST /contacts/ID/points/minus/POINTS

** Data Parameters (optional) **

Name Description
eventname Name of the event
actionname Name of the action

Response

Expected Response Code: 200 json { "success": true }

List Available Owners

<?php

$owners = $contactApi->getOwners();
[
  {
    "id": 1,
    "firstName": "Joe",
    "lastName": "Smith"
  },
  {
    "id": 2,
    "firstName": "Jane",
    "lastName": "Smith"
  }
]

Get a list of owners that can be used to assign contacts to when creating/editing.

HTTP Request

GET /contacts/list/owners

Response

Expected Response Code: 200

** Owner Properties **

Name Type Description
id int ID of the Mautic user
firstName string First name of the Mautic user
lastName string Last name of the Mautic user

List Available Fields

<?php

$fields = $contactApi->getFieldList();
{
    "1": {
        "id": 1,
        "label": "Title",
        "alias": "title",
        "type": "lookup",
        "group": "core",
        "order": 1
    },
    "2": {
        "id": 2,
        "label": "First Name",
        "alias": "firstname",
        "type": "text",
        "group": "core",
        "order": 2
    },
    "3": {
        "id": 3,
        "label": "Last Name",
        "alias": "lastname",
        "type": "text",
        "group": "core",
        "order": 3
    },

    "...": {
        "..." : "..."
    }
}

Get a list of available contact fields including custom ones.

HTTP Request

GET /contacts/list/fields

Response

Expected Response Code: 200

** Field Properties **

Name Type Description
id int ID of the field
label string Field label
alias string Field alias used as the column name in the database
type string Type of field. I.e. text, lookup, etc
group string Group the field belongs to
order int Field order

List Contact Notes

<?php

$notes = $contactApi->getContactNotes($id, $searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "notes": [
        {
              "id": 1,
              "text": "<p>Jim is super cool!</p>",
              "type": "general",
              "dateTime": "2015-07-23T13:14:00-05:00"
        }
    ]
}

Get a list of notes for a specific contact.

HTTP Request

GET /contacts/ID/notes

** Query Parameters **

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.

Response

Expected response code: 200

** Note Properties **

Name Type Description
id int ID of the note
text string Body of the note
type string Type of note. Options are “general”, “email”, “call”, “meeting”
dateTime datetime Date/time string of when the note was created.

Get Segment Memberships

<?php

$segments = $contactApi->getContactSegments($id);
{
    "total": 1,
    "segments": {
        "3": {
            "id": 3,
            "name": "New Contacts",
            "alias": "newcontacts"
        }
    }
}

Get a list of contact segments the contact is a member of.

HTTP Request

GET /contacts/ID/segments

Response

Expected response code: 200

** List Properties **

Name Type Description
id int ID of the list
name string Name of the list
alias string Alias of the list used with search commands.
dateAdded datetime Date/time string for when the contact was added to the list
manuallyAdded bool True if the contact was manually added to the list versus being added by a filter
manuallyRemoved bool True if the contact was manually removed from the list even though the list’s filter is a match

Change List Memberships

See Segements.

Get Campaign Memberships

<?php

$campaigns = $contactApi->getContactCampaigns($id);
{
    "total": 1,
    "campaigns": {
        "1": {
            "id": 1,
            "name": "Welcome Campaign",
            "dateAdded": "2015-07-21T14:11:47-05:00",
            "manuallyRemoved": false,
            "manuallyAdded": false,
            "list_membership": [
                3
            ]            
        }
    }
}

Get a list of campaigns the contact is a member of.

HTTP Request

GET /contacts/ID/campaigns

Response

Expected response code: 200

** List Properties **

Name Type Description
id int ID of the campaign
name string Name of the campaign
dateAdded datetime Date/time string for when the contact was added to the campaign
manuallyAdded bool True if the contact was manually added to the campaign versus being added by a contact list
manuallyRemoved bool True if the contact was manually removed from the campaign when the contact’s list is assigned to the campaign
listMembership array Array of contact list IDs this contact belongs to that is also associated with this campaign

Change Campaign Memberships

See Campaigns.

Get Contact’s Events

<?php

$events = $contactApi->getEvents($id, $search, $includeEvents, $excludeEvents, $orderBy, $orderByDir, $page);

** Query Parameters **

Name Description
filters[search] String or search command to filter events by.
filters[includeEvents][] Array of event types to include.
filters[excludeEvents][] Array of event types to exclude.
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
page What page number to load
{  
  "events":[  
    {  
      "event":"lead.identified",
      "icon":"fa-user",
      "eventType":"Contact identified",
      "eventPriority":-4,
      "timestamp":"2016-06-09T21:39:08+00:00",
      "featured":true
    }
  ],
  "filters":{  
    "search":"",
    "includeEvents":[  
      "lead.identified"
    ],
    "excludeEvents":[]
  },
  "order":[  
    "",
    "ASC"
  ],
  "types":{  
    "lead.ipadded":"Accessed from IP",
    "asset.download":"Asset downloaded",
    "campaign.event":"Campaign action triggered",
    "lead.create":"Contact created",
    "lead.identified":"Contact identified",
    "lead.donotcontact":"Do not contact",
    "email.read":"Email read",
    "email.sent":"Email sent",
    "email.failed":"Failed",
    "form.submitted":"Form submitted",
    "page.hit":"Page hit",
    "point.gained":"Point gained",
    "stage.changed":"Stage changed",
    "lead.utmtagsadded":"UTM tags recorded",
    "page.videohit":"Video View Event"
  },
  "total":1,
  "page":1,
  "limit":25,
  "maxPages":1
}

Get a list of contact events the contact created.

HTTP Request

GET /contacts/ID/events

Response

Expected response code: 200

** List Properties **

Name Type Description
events array List of events
event string ID of the event type
icon string Icon class from FontAwesome
eventType string Human name of the event
eventPriority string Priority of the event
timestamp timestamp Date and time when the event was created
featured bool Flag whether the event is featured
filters array Filters used in the query
order array Ordering used in the query
types array Array of available event types
total int Total number of events in the request
page int Current page number
limit int Limit of events per page
maxPages int How many pages of events are there

Get Contact’s Companies

<?php

$companies = $contactApi->getContactCompanies($contactId);

```json
{  
  "total":1,
  "companies":[  
    {  
      "company_id":"420",
      "date_associated":"2016-12-27 15:03:43",
      "is_primary":"0",
      "companyname":"test",
      "companyemail":"test@company.com",
      "companycity":"Raleigh",
      "score":"0",
      "date_added":"2016-12-27 15:03:42"
    }
  ]
}

Get a list of contact’s companies the contact belongs to.

HTTP Request

GET /contacts/ID/companies

Response

Expected response code: 200

List Properties

Name Type Description
company_id int Company ID
date_associated datetime Date and time when the contact was associated to the company
date_added datetime Date and time when the company was created
is_primary bool Flag whether the company association is primary (current)
companyname string Name of the company
companyemail string Email of the company
companycity string City of the company
score int Score of the company

Get Contact’s Devices

<?php

$devices = $contactApi->getContactDevices($contactId);

```json
{  
  "total":1,
  "devices":[  
    {  
      "id":60,
      "lead":[],
      "clientInfo":[],
      "device":"desktop",
      "deviceOsName":"Ubuntu",
      "deviceOsShortName":"UBT",
      "deviceOsPlatform":"x64"
    }
  ]
}

Get a list of contact’s devices the contact has used.

HTTP Request

GET /contacts/ID/devices

Response

Expected response code: 200

List Properties

Name Type Description
id int Device ID
clientInfo array Array with various information about the client (browser)
device string Device type; desktop, mobile..
deviceOsName string Full device OS name
deviceOsShortName string Short device OS name
deviceOsPlatform string OS platform

Data - Dashboard widget data

Use this endpoint to obtain details on Mautic’s dashboard statistical data.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth   = new ApiAuth();
$auth       = $initAuth->newAuth($settings);
$apiUrl     = "https://your-mautic.com";
$api        = new MauticApi();
$contactApi = $api->newApi("data", $auth, $apiUrl);

Get list of available widget types

<?php
$data = $dataApi->getList();

HTTP Request

GET /data

Expected Response Code: 200

{
    "success":1,
    "types":{
        "Core Widgets":{
            "recent.activity":"Recent Activity"
        },
        "Contact Widgets":{
            "created.leads.in.time":"Created contacts in time",
            "anonymous.vs.identified.leads":"Anonymous vs identified contacts",
            "map.of.leads":"Map",
            "top.lists":"Top segments",
            "top.creators":"Top contact creators",
            "top.owners":"Top contact owners",
            "created.leads":"Created contacts"
        },
        "Page Widgets":{
            "page.hits.in.time":"Page visits in time",
            "unique.vs.returning.leads":"Unique vs returning visitors",
            "dwell.times":"Dwell times",
            "popular.pages":"Popular landing pages",
            "created.pages":"Created Landing pages"
        },
        "Point Widgets":{
            "points.in.time":"Points in time"
        },
        "Form Widgets":{
            "submissions.in.time":"Submissions in time",
            "top.submission.referrers":"Top submission referrers",
            "top.submitters":"Top submitters",
            "created.forms":"Created forms"
        },
        "Email Widgets":{
            "emails.in.time":"Emails in time",
            "ignored.vs.read.emails":"Ignored vs read",
            "upcoming.emails":"Upcoming emails",
            "most.sent.emails":"Most sent emails",
            "most.read.emails":"Most read emails",
            "created.emails":"Created emails"
        },
        "Asset Widgets":{
            "asset.downloads.in.time":"Downloads in time",
            "unique.vs.repetitive.downloads":"Unique vs repetitive downloads",
            "popular.assets":"Popular assets",
            "created.assets":"Created assets"
        },
        "Campaign Widgets":{
            "events.in.time":"Events triggered in time",
            "leads.added.in.time":"Leads added in time"
        }
    }
}

Get an individual widget data by type.

<?php
$data = $dataApi->get($type, $options);

HTTP Request

GET /data/{type}?dateFrom={YYYY-mm-dd}&dateTo={YYYY-mm-dd}&timeUnit={m}

Returns response which can be directly visualized byt the chartJS library.

Response

Expected Response Code: 200

{
    "success":1,
    "cached":false,
    "execution_time":0.043900966644287,
    "data":{
        "chartType":"line",
        "chartHeight":220,
        "chartData":{
            "labels":[
                "Jan 2016",
                "Feb 2016",
                "Mar 2016",
                "Apr 2016",
                "May 2016"
            ],
            "datasets":[{
                "label":"Submission Count",
                "data":[
                    12,
                    6,
                    0,
                    0,
                    0
                ],
                "fillColor":"rgba(78,93,157,0.1)",
                "strokeColor":"rgba(78,93,157,0.8)",
                "pointColor":"rgba(78,93,157,0.75)",
                "pointHighlightStroke":"rgba(78,93,157,1)"
            }]
        }
    }
}

HTTP Request

GET /data/{type}?dateFrom={YYYY-mm-dd}&dateTo={YYYY-mm-dd}&timeUnit={m}&dataFormat={raw}

Returns raw format which can be more easily processed.

Response

Expected Response Code: 200

{
    "success":1,
    "cached":false,
    "execution_time":0.039958000183105,
    "data":{
        "Submission Count":{
            "Jan 2016":12,
            "Feb 2016":6,
            "Mar 2016":0,
            "Apr 2016":0,
            "May 2016":0
        }
    }
}

Available data URL query params

Name|Type|Example|Description —-|—-|———– timezone|string|America/New_York|PHP timezone dateFrom|string|2016-28-03|Date from in the YYYY-mm-dd HH:ii:ss format dateTo|string|2016-28-04|Date to in the YYYY-mm-dd HH:ii:ss format timeUnit|string|m|Date/Time unit. Available options: Y, m, W, d, H limit|int|10|Limit of the table widget items filter|array|[lead_id => 23]|filters which should be applied to the SQL query

Dynamic Content

Use this endpoint to obtain details on Mautic’s web dynamic content.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth         = new ApiAuth();
$auth             = $initAuth->newAuth($settings);
$apiUrl           = "https://your-mautic.com";
$api              = new MauticApi();
$dynamicConentApi = $api->newApi("dynamicConents", $auth, $apiUrl);

Get Dynamic Conent

<?php

//...
$dynamicConent = $dynamicConentApi->get($id);
{  
    "dynamicContent":{  
        "isPublished":true,
        "dateAdded":"2016-06-20T11:26:51+00:00",
        "createdBy":1,
        "createdByUser":"John Doe",
        "dateModified":"2016-08-08T16:36:27+00:00",
        "modifiedBy":1,
        "modifiedByUser":"John Doe",
        "id":1,
        "name":"DC13",
        "category":null,
        "publishUp":null,
        "publishDown":null,
        "sentCount":0,
        "variantParent":null,
        "variantChildren":[]
    }
}

Get an individual dynamicConent by ID.

HTTP Request

GET /dynamicconents/ID

Response

Expected Response Code: 200

See JSON code example.

Dynamic Conent Properties

Name Type Description
id int ID of the dynamic conent
name string Name of the dynamic conent
description string/null Description of the dynamic conent
isPublished bool Published state
publishUp datetime/null Date/time when the dynamic conent should be published
publishDown datetime/null Date/time the dynamic conent should be un published
dateAdded datetime Date/time dynamic conent was created
createdBy int ID of the user that created the dynamic conent
createdByUser string Name of the user that created the dynamic conent
dateModified datetime/null Date/time dynamic conent was last modified
modifiedBy int ID of the user that last modified the dynamic conent
modifiedByUser string Name of the user that last modified the dynamic conent
variantChildren array Array of Dynamic Conent entities for variants of this landing dynamic conent
variantParent object The parent/main dynamic conent if this is a variant (A/B test)
sentCount int Count of how many times the dynamic content was sent

List Dynamic Conents

<?php
// ...

$dynamicConents = $dynamicConentApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
    "total":30,
    "dynamicContents":[  
        {  
            "isPublished":true,
            "dateAdded":"2016-06-20T11:27:09+00:00",
            "createdBy":1,
            "createdByUser":"John Doe",
            "dateModified":"2016-08-22T17:14:01+00:00",
            "modifiedBy":1,
            "modifiedByUser":"John Doe",
            "id":2,
            "name":"CD2",
            "category":null,
            "publishUp":null,
            "publishDown":null,
            "sentCount":0,
            "variantParent":null,
            "variantChildren":[]
        }
    ]
}

HTTP Request

GET /dynamicconents

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Dynamic Conent.

Create Dynamic Conent

<?php 

$data = array(
    'name'        => 'Dynamic Conent A',
    'isPublished' => 1
);

$dynamicConent = $dynamicConentApi->create($data);

Create a new dynamicConent.

HTTP Request

POST /dynamicconents/new

Post Parameters

Name Type Description
id int ID of the dynamic conent
name string Name of the dynamic conent
description string/null Description of the dynamic conent
isPublished bool Published state
publishUp datetime/null Date/time when the dynamic conent should be published
publishDown datetime/null Date/time the dynamic conent should be un published
dateAdded datetime Date/time dynamic conent was created
createdBy int ID of the user that created the dynamic conent
createdByUser string Name of the user that created the dynamic conent
dateModified datetime/null Date/time dynamic conent was last modified
modifiedBy int ID of the user that last modified the dynamic conent
modifiedByUser string Name of the user that last modified the dynamic conent
variantChildren array Array of Dynamic Conent entities for variants of this landing dynamic conent
variantParent object The parent/main dynamic conent if this is a variant (A/B test)
sentCount int Count of how many times the dynamic content was sent

Response

Expected Response Code: 201

Properties

Same as Get Dynamic Conent.

Edit Dynamic Conent

<?php

$id   = 1;
$data = array(
    'name'        => 'New dynamicConent name',
    'isPublished' => 0
);

// Create new a dynamicConent of ID 1 is not found?
$createIfNotFound = true;

$dynamicConent = $dynamicConentApi->edit($id, $data, $createIfNotFound);

Edit a new dynamicConent. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a dynamicConent if the given ID does not exist and clears all the dynamic conent information, adds the information from the request. PATCH fails if the dynamic conent with the given ID does not exist and updates the dynamic conent field values with the values form the request.

HTTP Request

To edit a dynamicConent and return a 404 if the dynamic conent is not found:

PATCH /dynamicconents/ID/edit

To edit a dynamicConent and create a new one if the dynamic conent is not found:

PUT /dynamicconents/ID/edit

Post Parameters

Name Type Description
id int ID of the dynamic conent
name string Name of the dynamic conent
description string/null Description of the dynamic conent
isPublished bool Published state
publishUp datetime/null Date/time when the dynamic conent should be published
publishDown datetime/null Date/time the dynamic conent should be un published
dateAdded datetime Date/time dynamic conent was created
createdBy int ID of the user that created the dynamic conent
createdByUser string Name of the user that created the dynamic conent
dateModified datetime/null Date/time dynamic conent was last modified
modifiedBy int ID of the user that last modified the dynamic conent
modifiedByUser string Name of the user that last modified the dynamic conent
variantChildren array Array of Dynamic Conent entities for variants of this landing dynamic conent
variantParent object The parent/main dynamic conent if this is a variant (A/B test)
sentCount int Count of how many times the dynamic content was sent

Response

If PUT, the expected response code is 200 if the dynamic conent was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Dynamic Conent.

Delete Dynamic Conent

<?php

$dynamicConent = $dynamicConentApi->delete($id);

Delete a dynamicConent.

HTTP Request

DELETE /dynamicconents/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Dynamic Conent.

Emails

Use this endpoint to obtain details, create, update or delete Mautic’s emails.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$emailApi = $api->newApi("emails", $auth, $apiUrl);

Get Email

<?php

//...
$email = $emailApi->get($id);
{  
  "email":{  
    "isPublished":true,
    "dateAdded":"2016-10-25T18:51:17+00:00",
    "createdBy":1,
    "createdByUser":"John Doe",
    "dateModified":null,
    "modifiedBy":null,
    "modifiedByUser":null,
    "id":560,
    "name":"test",
    "subject":"API test email",
    "language":"en",
    "category":null,
    "fromAddress":null,
    "fromName":null,
    "replyToAddress":null,
    "bccAddress":null,
    "customHtml":"<h1>Hi there!<\/h1>",
    "plainText":null,
    "template":null,
    "emailType":"list",
    "publishUp":null,
    "publishDown":null,
    "readCount":0,
    "sentCount":0,
    "revision":1,
    "assetAttachments":[],
    "variantStartDate":null,
    "variantSentCount":0,
    "variantReadCount":0,
    "variantParent":null,
    "variantChildren":[],
    "translationParent":null,
    "translationChildren":[],
    "unsubscribeForm":null,
    "dynamicContent":[  
      {  
        "tokenName":null,
        "content":null,
        "filters":[  
          {  
            "content":null,
            "filters":[  
              {  
                "glue":null,
                "field":null,
                "object":null,
                "type":null,
                "operator":null,
                "display":null,
                "filter":null
              }
            ]
          }
        ]
      }
    ],
    "lists":[  
      {  
        "createdByUser":"John Doe",
        "modifiedByUser":null,
        "id":256,
        "name":"test",
        "alias":"test29",
        "description":null
      }
    ]
  }
}

Get an individual email by ID.

HTTP Request

GET /emails/ID

Response

Expected Response Code: 200

See JSON code example.

Email Properties

Name Type Description
id int ID of the email
name string Internal name of the email
subject stringl Subject of the email
fromAddress string The from email address if it’s different than the one in the Mautic configuration
fromName string The from name if it’s different than the one in the Mautic configuration
replyToAddress string The reply to email address if it’s different than the one in the Mautic configuration
bccAddress string The BCC email address if it’s different than the one in the Mautic configuration
isPublished bool Published state
publishUp datetime/null Date/time when the email should be published
publishDown datetime/null Date/time the email should be un published
dateAdded datetime Date/time email was created
createdBy int ID of the user that created the email
createdByUser string Name of the user that created the email
dateModified datetime/null Date/time email was last modified
modifiedBy int ID of the user that last modified the email
modifiedByUser string Name of the user that last modified the email
language string Language locale of the email
readCount int Total email read count
sentCount int Total email sent count
revision int Email revision
customHtml string The HTML content of the email
plainText string The plain text content of the email
template string The name of the template used as the base for the email
emailType string If it is a segment (former list) email or template email. Possible values are ‘list’ and 'template’
translationChildren array Array of Page entities for translations of this landing page
translationParent object The parent/main page if this is a translation
variantSentCount int Sent count since variantStartDate
variantReadCount int Read count since variantStartDate
variantChildren array Array of Email entities for variants of this landing email
variantParent object The parent/main email if this is a variant (A/B test)
variantSettings array The properties of the A/B test
variantStartDate datetime/null The date/time the A/B test began
category object/null Category information
unsubscribeForm int Id of the form displayed in the unsubscribe page
dynamicContent object Dynamic content configuration
lists array Array of segment IDs which should be added to the segment email

List Emails

<?php
// ...

$emails = $emailApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "emails": [
        {  
            "isPublished":true,
            "dateAdded":"2016-10-25T18:51:17+00:00",
            "createdBy":1,
            "createdByUser":"John Doe",
            "dateModified":null,
            "modifiedBy":null,
            "modifiedByUser":null,
            "id":560,
            "name":"test",
            "subject":"API test email",
            "language":"en",
            "category":null,
            "fromAddress":null,
            "fromName":null,
            "replyToAddress":null,
            "bccAddress":null,
            "customHtml":"<h1>Hi there!<\/h1>",
            "plainText":null,
            "template":null,
            "emailType":"list",
            "publishUp":null,
            "publishDown":null,
            "readCount":0,
            "sentCount":0,
            "revision":1,
            "assetAttachments":[],
            "variantStartDate":null,
            "variantSentCount":0,
            "variantReadCount":0,
            "variantParent":null,
            "variantChildren":[],
            "translationParent":null,
            "translationChildren":[],
            "unsubscribeForm":null,
            "dynamicContent":[  
              {  
                "tokenName":null,
                "content":null,
                "filters":[  
                  {  
                    "content":null,
                    "filters":[  
                      {  
                        "glue":null,
                        "field":null,
                        "object":null,
                        "type":null,
                        "operator":null,
                        "display":null,
                        "filter":null
                      }
                    ]
                  }
                ]
              }
            ],
            "lists":[  
              {  
                "createdByUser":"John Doe",
                "modifiedByUser":null,
                "id":256,
                "name":"test",
                "alias":"test29",
                "description":null
              }
            ]
          }
    ]
}

HTTP Request

GET /emails

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Email.

Create Email

<?php 

$data = array(
    'title'        => 'Email A',
    'description' => 'This is my first email created via API.',
    'isPublished' => 1
);

$email = $emailApi->create($data);

Create a new email.

HTTP Request

POST /emails/new

Post Parameters

Name Type Description
id int ID of the email
name string Internal name of the email
subject stringl Subject of the email
fromAddress string The from email address if it’s different than the one in the Mautic configuration
fromName string The from name if it’s different than the one in the Mautic configuration
replyToAddress string The reply to email address if it’s different than the one in the Mautic configuration
bccAddress string The BCC email address if it’s different than the one in the Mautic configuration
isPublished bool Published state
publishUp datetime/null Date/time when the email should be published
publishDown datetime/null Date/time the email should be un published
language string Language locale of the email
readCount int Total email read count
sentCount int Total email sent count
revision int Email revision
customHtml string The HTML content of the email
plainText string The plain text content of the email
template string The name of the template used as the base for the email
emailType string If it is a segment (former list) email or template email. Possible values are 'list’ and 'template’
translationChildren array Array of Page entities for translations of this landing page
translationParent object The parent/main page if this is a translation
variantSentCount int Sent count since variantStartDate
variantReadCount int Read count since variantStartDate
variantChildren array Array of Email entities for variants of this landing email
variantParent object The parent/main email if this is a variant (A/B test)
variantSettings array The properties of the A/B test
variantStartDate datetime/null The date/time the A/B test began
category object/null Category information
unsubscribeForm int Id of the form displayed in the unsubscribe page
dynamicContent object Dynamic content configuration
lists array Array of segment IDs which should be added to the segment email

Response

Expected Response Code: 201

Properties

Same as Get Email.

Edit Email

<?php

$id   = 1;
$data = array(
    'title'        => 'New email title',
    'isPublished' => 0
);

// Create new a email of ID 1 is not found?
$createIfNotFound = true;

$email = $emailApi->edit($id, $data, $createIfNotFound);

Edit a new email. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a email if the given ID does not exist and clears all the email information, adds the information from the request. PATCH fails if the email with the given ID does not exist and updates the email field values with the values form the request.

HTTP Request

To edit a email and return a 404 if the email is not found:

PATCH /emails/ID/edit

To edit a email and create a new one if the email is not found:

PUT /emails/ID/edit

Post Parameters

Name Type Description
id int ID of the email
name string Internal name of the email
subject stringl Subject of the email
fromAddress string The from email address if it’s different than the one in the Mautic configuration
fromName string The from name if it’s different than the one in the Mautic configuration
replyToAddress string The reply to email address if it’s different than the one in the Mautic configuration
bccAddress string The BCC email address if it’s different than the one in the Mautic configuration
isPublished bool Published state
publishUp datetime/null Date/time when the email should be published
publishDown datetime/null Date/time the email should be un published
language string Language locale of the email
readCount int Total email read count
sentCount int Total email sent count
revision int Email revision
customHtml string The HTML content of the email
plainText string The plain text content of the email
template string The name of the template used as the base for the email
emailType string If it is a segment (former list) email or template email. Possible values are 'list’ and 'template’
translationChildren array Array of Page entities for translations of this landing page
translationParent object The parent/main page if this is a translation
variantSentCount int Sent count since variantStartDate
variantReadCount int Read count since variantStartDate
variantChildren array Array of Email entities for variants of this landing email
variantParent object The parent/main email if this is a variant (A/B test)
variantSettings array The properties of the A/B test
variantStartDate datetime/null The date/time the A/B test began
category object/null Category information
unsubscribeForm int Id of the form displayed in the unsubscribe page
dynamicContent object Dynamic content configuration
lists array Array of segment IDs which should be added to the segment email

Response

If PUT, the expected response code is 200 if the email was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Email.

Delete Email

<?php

$email = $emailApi->delete($id);

Delete a email.

HTTP Request

DELETE /emails/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Email.

Send Email to Contact

<?php

$email = $emailApi->sendToContact($emailId, $contactId);

Send a predefined email to existing contact.

HTTP Request

POST /emails/ID/contact/CONTACT_ID/send

Post Parameters

Name Type Description
tokens array Array of tokens in email

Response

Expected Response Code: 200

Properties json { "success": 1 }

Send Email to Segment

<?php

$email = $emailApi->send($id);

Send a segment email to linked segment(s).

HTTP Request

POST /emails/ID/send

Response

Expected Response Code: 200

Properties json { "success": 1, "sentCount": 1, "failedCount": 0 }

Fields

Use this endpoint to work with Mautic’s contact/company fields.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$assetApi = $api->newApi("assets", $auth, $apiUrl);

// Get contact field context:
$fieldApi = $api->newApi("contactFields", $auth, $apiUrl);

// Or use 'companyFields' for company fields:
$fieldApi = $api->newApi("companyFields", $auth, $apiUrl);

Get Field

<?php

//...
$field = $fieldApi->get($id);
{  
  "field":{  
    "isPublished":true,
    "dateAdded":"2016-11-10T13:02:52+00:00",
    "createdBy":1,
    "createdByUser":"John Doe",
    "dateModified":null,
    "modifiedBy":null,
    "modifiedByUser":null,
    "id":165,
    "label":"API test field",
    "alias":"api_test_field11",
    "type":"text",
    "group":null,
    "order":36,
    "object":"lead",
    "defaultValue":null,
    "isRequired":false,
    "isPubliclyUpdatable":false,
    "isUniqueIdentifier":0,
    "properties":[]
  }
}

Get an individual field by ID.

HTTP Request

GET /fields/contact/ID or GET /fields/company/ID

Response

Expected Response Code: 200

See JSON code example.

Field Properties

Name Type Description
id int ID of the field
isPublished bool Published state
publishUp datetime/null Date/time when the field should be published
publishDown datetime/null Date/time the field should be un published
dateAdded datetime Date/time field was created
createdBy int ID of the user that created the field
createdByUser string Name of the user that created the field
dateModified datetime/null Date/time field was last modified
modifiedBy int ID of the user that last modified the field
modifiedByUser string Name of the user that last modified the field
label string Name of the field
alias string Unique alias of the field used in the form field name attributes
description string/null Description of the field
type string Field type
group string Groupd of the fields where the field belongs
order int Order number of the field
object string Which object use the field. Contact (lead) or Company.
defaultValue string Default value of the field.
isRequired boolean True if the field is required.
isPubliclyUpdatable boolean True if the field value can be changed from public requests. The tracking pixel query for example.
isUniqueIdentifier boolean True if the field is unique identifier and so the contacts should merge if the value of this field is the same.
properties array Field options if the field type needs some.

List Contact Fields

<?php

//...
$fields = $fieldApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
  "total":71,
  "fields":[  
    {  
      "isPublished":true,
      "dateAdded":"2016-10-12T11:31:13+00:00",
      "createdBy":1,
      "createdByUser":"John Doe",
      "dateModified":"2016-10-12T11:31:30+00:00",
      "modifiedBy":1,
      "modifiedByUser":"John Doe",
      "id":100,
      "label":"Multiselect test",
      "alias":"multiselect_test",
      "type":"multiselect",
      "group":"core",
      "order":3,
      "object":"lead",
      "defaultValue":null,
      "isRequired":false,
      "isPubliclyUpdatable":false,
      "isUniqueIdentifier":false,
      "properties":{  
        "list":[  
          {  
            "label":"PHP",
            "value":"php"
          },
          {  
            "label":"JS",
            "value":"js"
          },
          {  
            "label":"English",
            "value":"en"
          }
        ]
      }
    },
    [...]
  ]
}

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

HTTP Request

GET /fields/contact or GET /fields/company

Response

Expected Response Code: 200

See JSON code example.

Field Properties

Name Type Description
id int ID of the field
isPublished bool Published state
publishUp datetime/null Date/time when the field should be published
publishDown datetime/null Date/time the field should be un published
dateAdded datetime Date/time field was created
createdBy int ID of the user that created the field
createdByUser string Name of the user that created the field
dateModified datetime/null Date/time field was last modified
modifiedBy int ID of the user that last modified the field
modifiedByUser string Name of the user that last modified the field
label string Name of the field
alias string Unique alias of the field used in the form field name attributes
description string/null Description of the field
type string Field type
group string Groupd of the fields where the field belongs
order int Order number of the field
object string Which object use the field. Contact (lead) or Company.
defaultValue string Default value of the field.
isRequired boolean True if the field is required.
isPubliclyUpdatable boolean True if the field value can be changed from public requests. The tracking pixel query for example.
isUniqueIdentifier boolean True if the field is unique identifier and so the contacts should merge if the value of this field is the same.
properties array Field options if the field type needs some.

Create Field

<?php 

$data = array(
    'label' => 'API test field',
    'type' => 'text',
);

$field = $fieldApi->create($data);

Create a new field.

HTTP Request

POST /fields/contact/new or POST /fields/company/new

Post Parameters

Name Description
label string
alias string
description string/null
type string
group string
order int
object string
defaultValue string
isRequired boolean
isPubliclyUpdatable boolean
isUniqueIdentifier boolean
properties array

Response

Expected Response Code: 201

Properties

Same as Get Field.

Edit Field

<?php

$id   = 1;
$data = array(
    'label' => 'API test field',
    'type' => 'text',
);

// Create new a field of ID 1 is not found?
$createIfNotFound = true;

$field = $fieldApi->edit($id, $data, $createIfNotFound);

Edit a new field. Field that this supports PUT or PATCH depending on the desired behavior.

PUT creates a field if the given ID does not exist and clears all the field infieldation, adds the infieldation from the request. PATCH fails if the field with the given ID does not exist and updates the field field values with the values field the request.

HTTP Request

To edit a field and return a 404 if the field is not found:

PATCH /fields/contact/ID/edit or PATCH /fields/company/ID/edit

To edit a field and create a new one if the field is not found:

PUT /fields/contact/ID/edit or PUT /fields/company/ID/edit

Post Parameters

Name Description
label string
alias string
description string/null
type string
group string
order int
object string
defaultValue string
isRequired boolean
isPubliclyUpdatable boolean
isUniqueIdentifier boolean
properties array

Response

If PUT, the expected response code is 200 if the field was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Field.

Delete Field

<?php

$field = $fieldApi->delete($id);

Delete a field.

HTTP Request

DELETE /fields/contact/ID/delete or DELETE /fields/company/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Field.

Files

This endpoint is useful for working with files of images and assets.

Note: Assets doesn’t have nor support subdirectories.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$filesApi = $api->newApi("files", $auth, $apiUrl);

Get List of files

<?php

// Get list of root media/images directory:
$files = $filesApi->getList();

// Get list of some sub-directory (flags in this case) of media/images:
$filesApi->setFolder('images/flags');
$files = $filesApi->getList();

// Get list of root media/files directory where the asset files are stored:
$files = $filesApi->setFolder('assets');
$files = $filesApi->getList();
{  
  "files":{  
    "3":"0b0f20185251d1c0cd5ff17950213fc9.png",
    "4":"0f530efdf837d3005bd2ab81cc30e878.jpeg",
    "5":"162a694f4101cb06c27c0a0699bd87c4.png",
    "6":"16ada2e2ecfa3f1d8cbb5d633f0bd8c6.png",
    ...
  }
}

HTTP Request

GET /files/images to get root images directory GET /files/images?subdir=flags to get images/flags directory GET /files/assets to get root assets directory

Response

Expected Response Code: 200

See JSON code example.

Response Properties

Name Type Description
files array List of requested files and directories

Create File

<?php
$data = array(
    'file' => dirname(__DIR__).'/'.'mauticlogo.png' // Must be a path to an existing file
);

// Create a file in root media/images directory:
$response = $fileApi->create($data);

// Create a file in some sub-directory (flags in this case) of media/images:
$filesApi->setFolder('images/flags');
$response = $fileApi->create($data);

// Create a file in media/files directory where the asset files are stored:
$files = $filesApi->setFolder('assets');
$response = $fileApi->create($data);

Creates a file. The file is sent via regular POST files array like a browser sends it during file upload.

HTTP Request

POST /files/DIR/new

Response

Expected Response Code: 200 json { "file":{ "link":"http:\/\/yourmautic\/media\/images\/2b912b934dd2a4da49a226d0bf68bfea.png", "name":"2b912b934dd2a4da49a226d0bf68bfea.png" } }

Response Properties

Name Type Description
link string Appears only for files in image directory, not for assets
name string File name of newly created file

Delete File

<?php
// Delete a file from root media/images directory:
$response = $fileApi->delete($fileName);

// Delete a file from some sub-directory (flags in this case) of media/images:
$filesApi->setFolder('images/flags');
$response = $fileApi->delete($fileName);

// Delete a file from media/files directory where the asset files are stored:
$files = $filesApi->setFolder('assets');
$response = $fileApi->delete($fileName);

Delete a file.

HTTP Request

DELETE /files/DIR/FILE/delete

Response

Expected Response Code: 200 json { "success": true }

Forms

Use this endpoint to obtain details on Mautic’s forms.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$formApi  = $api->newApi("forms", $auth, $apiUrl);

Get Form

<?php

//...
$form = $formApi->get($id);
{
    "form": {
        "id": 3,
        "name": "Newlsetter",
        "alias": "newsletter",
        "description": null,
        "isPublished": true,
        "publishUp": null,
        "publishDown": null,
        "dateAdded": "2015-07-15T15:06:02-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-20T13:11:56-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "category": null,
        "cachedHtml": "\n\n<script...",
        "template": null,
        "submissionCount": 10,
        "fields": {
            "26": {
                "id": 26,
                "label": "Email",
                "showLabel": false,
                "alias": "email",
                "type": "text",
                "defaultValue": null,
                "isRequired": true,
                "validationMessage": "Email is required",
                "helpMessage": null,
                "order": 1,
                "properties": {
                    "placeholder": "Email address"
                },
                "labelAttributes": null,
                "inputAttributes": null,
                "containerAttributes": null
            },
            "27": {
                "id": 27,
                "label": "Submit",
                "showLabel": true,
                "alias": "submit",
                "type": "button",
                "defaultValue": null,
                "isRequired": false,
                "validationMessage": null,
                "helpMessage": null,
                "order": 4,
                "properties": [],
                "labelAttributes": null,
                "inputAttributes": null,
                "containerAttributes": null
            }
        },
        "actions": {
            "4": {
                "id": 4,
                "type": "email.send.lead",
                "name": "Send thank you email",
                "description": null,
                "order": 1,
                "properties": {
                    "email": 21
                }
            }
        }
    }
}

Get an individual form by ID.

HTTP Request

GET /forms/ID

Response

Expected Response Code: 200

See JSON code example.

Form Properties

Name Type Description
id int ID of the form
name string Name of the form
description string/null Description of the form
alias string Used to generate the URL for the form
isPublished bool Published state
publishUp datetime/null Date/time when the form should be published
publishDown datetime/null Date/time the form should be un published
dateAdded datetime Date/time form was created
createdBy int ID of the user that created the form
createdByUser string Name of the user that created the form
dateModified datetime/null Date/time form was last modified
modifiedBy int ID of the user that last modified the form
modifiedByUser string Name of the user that last modified the form
cachedHtml string Cached HTML for the form
template string/null Name of the template used to generate the HTML
submissionCount Number of times the form has been submitted
fields array Array of Field entities for the form. See below.
actions array Array of Action entities for the form. See below.

Field Properties

Name Type Description
id int ID of the field
label string Label of the field
showLabel bool Display the label of the field
alias string Alias of the field (used as the database column)
type string Field type
defaultValue string Default value
isRequired bool Field is required
validationMessage string Validation message if required field is left empty
helpMessage string Help message for the field
order int Order of the field
properties array Configured properties for the field
labelAttributes string/null Custom HTML attributes for the label
inputAttributes Custom HTML attributes for the input
containerAttributes Custom HTML attributes for the container

Action Properties

Name Type Description
id int ID of the action
type string Action type
name string Name of the action
description string/null Description of the action
order int Action order
properties array Configured properties for the action

List Forms

<?php
// ...

$forms = $formApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "forms": [
        {
            "id": 3,
            "name": "Newlsetter",
            "alias": "newsletter",
            "description": null,
            "isPublished": true,
            "publishUp": null,
            "publishDown": null,
            "dateAdded": "2015-07-15T15:06:02-05:00",
            "createdBy": 1,
            "createdByUser": "Joe Smith",
            "dateModified": "2015-07-20T13:11:56-05:00",
            "modifiedBy": 1,
            "modifiedByUser": "Joe Smith",
            "category": null,
            "cachedHtml": "\n\n<script...",
            "template": null,
            "submissionCount": 10,
            "fields": {
                "26": {
                    "id": 26,
                    "label": "Email",
                    "showLabel": false,
                    "alias": "email",
                    "type": "text",
                    "defaultValue": null,
                    "isRequired": true,
                    "validationMessage": "Email is required",
                    "helpMessage": null,
                    "order": 1,
                    "properties": {
                        "placeholder": "Email address"
                    },
                    "labelAttributes": null,
                    "inputAttributes": null,
                    "containerAttributes": null
                },
                "27": {
                    "id": 27,
                    "label": "Submit",
                    "showLabel": true,
                    "alias": "submit",
                    "type": "button",
                    "defaultValue": null,
                    "isRequired": false,
                    "validationMessage": null,
                    "helpMessage": null,
                    "order": 4,
                    "properties": [],
                    "labelAttributes": null,
                    "inputAttributes": null,
                    "containerAttributes": null
                }
            },
            "actions": {
                "4": {
                    "id": 4,
                    "type": "email.send.lead",
                    "name": "Send thank you email",
                    "description": null,
                    "order": 1,
                    "properties": {
                        "email": 21
                    }
                }
            }
        }
    ]
}

HTTP Request

GET /forms

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Form.

Create Form

<?php 

$data = array(
    'name' => 'test',
    'formType' => 'standalone',
    'description' => 'API test',
    'fields' => array(
        array(
            'label' => 'field name',
            'type' => 'text'
        )
    ),
    'actions' => array(
        array(
            'name' => 'action name',
            'description' => 'action desc',
            'type' => 'lead.pointschange',
            'properties' => array(
                'operator' => 'plus',
                'points' => 2
            )
        )
    )
);

$form = $formApi->create($data);

Create a new form.

HTTP Request

POST /forms/new

Post Parameters

Same as Get Form. Form fields and actions can be created/edited via the forms/actions arrays in the form array.

Response

Expected Response Code: 201

Properties

Same as Get Form.

Edit Form

<?php

$id   = 1;
$data = array(
    'name' => 'test',
    'formType' => 'standalone',
    'description' => 'API test',
    'fields' => array(
        array(
            'label' => 'field name',
            'type' => 'text'
        )
    ),
    'actions' => array(
        array(
            'name' => 'action name',
            'description' => 'action desc',
            'type' => 'lead.pointschange',
            'properties' => array(
                'operator' => 'plus',
                'points' => 2
            )
        )
    )
);

// Create new a form of ID 1 is not found?
$createIfNotFound = true;

$form = $formApi->edit($id, $data, $createIfNotFound);

Edit a new form. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a form if the given ID does not exist and clears all the form information, adds the information from the request. Form fields and actions will be also deleted if not present in the request. PATCH fails if the form with the given ID does not exist and updates the form field values with the values form the request.

HTTP Request

To edit a form and return a 404 if the form is not found:

PATCH /forms/ID/edit

To edit a form and create a new one if the form is not found:

PUT /forms/ID/edit

Post Parameters

Same as Get Form. Form fields and actions can be created/edited via the forms/actions arrays in the form array.

Response

If PUT, the expected response code is 200 if the form was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Form.

Delete Form

<?php

$form = $formApi->delete($id);

Delete a form.

HTTP Request

DELETE /forms/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Form.

Delete Form Fields

The following examples will show how to delete fields with ID 56 and 59.

<?php

$form = $formApi->deleteFields($formId, array(56, 59));

Delete a form fields.

HTTP Request

DELETE /forms/ID/fields/delete?fields[]=56&fields[]=59

Response

Expected Response Code: 200

Properties

Same as Get Form.

Delete Form Actions

The following examples will show how to delete actions with ID 56 and 59.

<?php

$form = $formApi->deleteActions($formId, array(56, 59));

Delete a form actions.

HTTP Request

DELETE /forms/ID/actions/delete?actions[]=56&actions[]=59

Response

Expected Response Code: 200

Properties

Same as Get Form.

Marketing Messages

Use this endpoint to obtain details, create, update or delete Mautic’s messages.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$messageApi = $api->newApi("messages", $auth, $apiUrl);

Get Marketing Message

<?php

//...
$message = $messageApi->get($id);
{
    "message": {
        "isPublished": true,
        "dateAdded": "2017-02-08T15:00:34+01:00",
        "dateModified": "2017-02-08T15:00:35+01:00",
        "createdBy": 1,
        "createdByUser": "John Doe",
        "modifiedBy": 1,
        "modifiedByUser": "John Doe",
        "id": 26,
        "name": "Thanks for the feedback!",
        "description": "",
        "publishUp": null,
        "publishDown": null,
        "channels": [
            {
                "id": 55,
                "channel": "email",
                "channelId": 1197,
                "channelName": "Email A",
                "isEnabled": true
            },
            {
                "id": 57,
                "channel": "notification",
                "channelId": null,
                "channelName": null,
                "isEnabled": false
            },
            {
                "id": 56,
                "channel": "sms",
                "channelId": 103,
                "channelName": "SMS A",
                "isEnabled": false
            },
            {
                "id": 91,
                "channel": "tweet",
                "channelId": null,
                "channelName": null,
                "isEnabled": false
            }
        ]
    }
}

Get an individual marketing message by ID.

HTTP Request

GET /messages/ID

Response

Expected Response Code: 200

See JSON code example.

Marketing Message Properties

Name Type Description
id int ID of the message
name string Internal name of the message
isPublished bool Published state
publishUp datetime/null Date/time when the message should be published
publishDown datetime/null Date/time the message should be un published
dateAdded datetime Date/time message was created
createdBy int ID of the user that created the message
createdByUser string Name of the user that created the message
dateModified datetime/null Date/time message was last modified
modifiedBy int ID of the user that last modified the message
modifiedByUser string Name of the user that last modified the message
channels array Array of channels configured for the marketing message

List Marketing Messages

<?php
// ...

$messages = $messageApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "messages": {
        "1": {
            "isPublished": true,
            "dateAdded": "2017-02-03T16:51:58+00:00",
            "dateModified": "2017-02-03T19:11:41+00:00",
            "createdBy": 1,
            "createdByUser": "John Doe",
            "modifiedBy": 1,
            "modifiedByUser": "John Doe",
            "id": 1,
            "name": "Live long and prosper",
            "description": null,
            "publishUp": null,
            "publishDown": null,
            "channels": [
                {
                    "id": 1,
                    "channel": "email",
                    "channelId": 44,
                    "channelName": "Email A",
                    "isEnabled": true
                },
                {
                    "id": 2,
                    "channel": "sms",
                    "channelId": 1,
                    "channelName": "SMS A",
                    "isEnabled": true
                },
                {
                    "id": 3,
                    "channel": "notification",
                    "channelId": 75,
                    "channelName": null,
                    "isEnabled": false
                }
            ]
        }
    }
}

HTTP Request

GET /messages

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Marketing Message.

Create Marketing Message

<?php 

$data = array(
    'name'        => 'Marketing Message A',
    'description' => 'This is my first message created via API.',
    'isPublished' => 1,
    'channels' => array(
        'email' => array(
            'channel' => 'email',
            'channelId' => 44,
            'isEnabled' => true,
        ),
        'sms' => array(
            'channel' => 'sms',
            'channelId' => 1,
            'isEnabled' => true,
        ),
        'notification' => array(
            'channel' => 'notification',
            'channelId' => 75,
            'isEnabled' => false,
        )
    )
);

$message = $messageApi->create($data);

Create a new message.

HTTP Request

POST /messages/new

Post Parameters

Name Type Description
id int ID of the message
name string Internal name of the message
isPublished bool Published state
publishUp datetime/null Date/time when the message should be published
publishDown datetime/null Date/time the message should be un published
channels array Array of channels

Response

Expected Response Code: 201

Properties

Same as Get Marketing Message.

Edit Marketing Message

<?php

$id   = 1;
$data = array(
    'name'        => 'New message title',
    'isPublished' => 0
);

// Create new a message of ID 1 is not found?
$createIfNotFound = true;

$message = $messageApi->edit($id, $data, $createIfNotFound);

Edit a new message. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a message if the given ID does not exist and clears all the message information, adds the information from the request. PATCH fails if the message with the given ID does not exist and updates the message field values with the values form the request.

HTTP Request

To edit a message and return a 404 if the message is not found:

PATCH /messages/ID/edit

To edit a message and create a new one if the message is not found:

PUT /messages/ID/edit

Post Parameters

Name Type Description
id int ID of the message
name string Internal name of the message
isPublished bool Published state
publishUp datetime/null Date/time when the message should be published
publishDown datetime/null Date/time the message should be un published
channels array Array of channels

Response

If PUT, the expected response code is 200 if the message was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Marketing Message.

Delete Marketing Message

<?php

$message = $messageApi->delete($id);

Delete a message.

HTTP Request

DELETE /messages/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Marketing Message.

Notes

Use this endpoint to obtain details on Mautic’s contact notes.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$noteApi  = $api->newApi("notes", $auth, $apiUrl);

Get Note

<?php

//...
$note = $noteApi->get($id);
{  
  "note":{  
    "id":39,
    "text":"Contact note created via API request",
    "type":"general",
    "dateTime":null,
    "lead":{  
      "id":1405,
      "points":0,
      "color":null,
      "fields":{  
        "core":{  
          "firstname":{  
            "id":"2",
            "label":"First Name",
            "alias":"firstname",
            "type":"text",
            "group":"core",
            "field_order":"42",
            "object":"lead",
            "value":"Note API test"
          },
          "lastname":{  
            "id":"3",
            "label":"Last Name",
            "alias":"lastname",
            "type":"text",
            "group":"core",
            "field_order":"44",
            "object":"lead",
            "value":null
          },
          [...]
        },
      }
    }
  }
}

Get an individual note by ID.

HTTP Request

GET /notes/ID

Response

Expected Response Code: 200

See JSON code example.

Note Properties

Name Type Description
id int ID of the note
lead array data of the contact
text string Note text
type string Note type
datetime datetime Date and time related to the note.

List Contact Notes

<?php

//...
$notes = $noteApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
  "total":24,
  "notes":[  
    {  
      "id":1,
      "text":"A test note",
      "type":"general",
      "dateTime":"2016-06-14T18:07:00+00:00",
      "lead":{  
        "id":1,
        "points":0,
        "color":null,
        "fields":[]
      }
    },
    [...]
  ]
}

HTTP Request

GET /notes

Response

Expected Response Code: 200

See JSON code example.

Note Properties

Name Type Description
id int ID of the note
lead array data of the contact
text string Note text
type string Note type
datetime datetime Date and time related to the note.

Create Note

<?php 

$data = array(
    'text' => 'Note A',
    'type' => 'general',
);

$note = $noteApi->create($data);

Create a new note.

HTTP Request

POST /notes/new

Post Parameters

Name Description
text string
type string
datetime datetime

Response

Expected Response Code: 201

Properties

Same as Get Note.

Edit Note

<?php

$id   = 1;
$data = array(
    'text' => 'Note B',
    'type' => 'general',
);

// Create new a note of ID 1 is not found?
$createIfNotFound = true;

$note = $noteApi->edit($id, $data, $createIfNotFound);

Edit a new note. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a note if the given ID does not exist and clears all the note information, adds the information from the request. PATCH fails if the note with the given ID does not exist and updates the note field values with the values form the request.

HTTP Request

To edit a note and return a 404 if the note is not found:

PATCH /notes/ID/edit

To edit a note and create a new one if the note is not found:

PUT /notes/ID/edit

Post Parameters

Name Description
text string
type string
datetime datetime

Response

If PUT, the expected response code is 200 if the note was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Note.

Delete Note

<?php

$note = $noteApi->delete($id);

Delete a note.

HTTP Request

DELETE /notes/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Note.

Notifications

Use this endpoint to obtain details on Mautic’s notifications.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth        = new ApiAuth();
$auth            = $initAuth->newAuth($settings);
$apiUrl          = "https://your-mautic.com";
$api             = new MauticApi();
$notificationApi = $api->newApi("notifications", $auth, $apiUrl);

Get Notification

<?php

//...
$notification = $notificationApi->get($id);
{  
    "notification":{  
        "isPublished":true,
        "dateAdded":"2016-09-14T14:03:05+00:00",
        "createdBy":1,
        "createdByUser":"John Doe",
        "dateModified":"2016-09-15T08:40:46+00:00",
        "modifiedBy":1,
        "modifiedByUser":"John Doe",
        "id":1,
        "name":"The first notification",
        "heading":"The first notification Heading",
        "message":"The first notification Message",
        "url":"http:\/\/mautic.org",
        "language":"en",
        "category":null,
        "publishUp":null,
        "publishDown":null,
        "readCount":0,
        "sentCount":0
    }
}

Get an individual notification by ID.

HTTP Request

GET /notifications/ID

Response

Expected Response Code: 200

See JSON code example.

Notification Properties

Name Type Description
id int ID of the notification
name string Title of the notification
heading string Heading of the notification
message string Message of the notification
url string URL to go to when the notification is clicked
isPublished bool Published state
publishUp datetime/null Date/time when the notification should be published
publishDown datetime/null Date/time the notification should be un published
dateAdded datetime Date/time notification was created
createdBy int ID of the user that created the notification
createdByUser string Name of the user that created the notification
dateModified datetime/null Date/time notification was last modified
modifiedBy int ID of the user that last modified the notification
modifiedByUser string Name of the user that last modified the notification
language string Language locale of the notification
readCount int Total notification read count
sentCount int Unique notification sent count
category null/object Category

List Notifications

<?php
// ...

$notifications = $notificationApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
    "total":1,
    "notifications":[  
        {  
            "isPublished":true,
            "dateAdded":"2016-09-14T14:03:05+00:00",
            "createdBy":1,
            "createdByUser":"John Doe",
            "dateModified":"2016-09-15T08:40:46+00:00",
            "modifiedBy":1,
            "modifiedByUser":"John Doe",
            "id":1,
            "name":"The first notification",
            "heading":"The first notification Heading",
            "message":"The first notification Message",
            "url":"http:\/\/mautic.org",
            "language":"en",
            "category":null,
            "publishUp":null,
            "publishDown":null,
            "readCount":0,
            "sentCount":0
        }
    ]
}

HTTP Request

GET /notifications

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Notification.

Create Notification

<?php 

$data = array(
    'name'    => 'Notification A',
    'heading' => 'Hello World!'
    'message' => 'This is my first notification created via API.',
);

$notification = $notificationApi->create($data);

Create a new notification.

HTTP Request

POST /notifications/new

Post Parameters

Name Type Description
id int ID of the notification
name string Title of the notification
heading string Heading of the notification
message string Message of the notification
url string URL to go to when the notification is clicked
isPublished bool Published state
publishUp datetime/null Date/time when the notification should be published
publishDown datetime/null Date/time the notification should be un published
language string Language locale of the notification

Response

Expected Response Code: 201

Properties

Same as Get Notification.

Edit Notification

<?php

$id   = 1;
$data = array(
    'name'    => 'Notification A',
    'heading' => 'Hello World!'
    'message' => 'This is my first notification created via API.',
);

// Create new a notification of ID 1 is not found?
$createIfNotFound = true;

$notification = $notificationApi->edit($id, $data, $createIfNotFound);

Edit a new notification. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a notification if the given ID does not exist and clears all the notification information, adds the information from the request. PATCH fails if the notification with the given ID does not exist and updates the notification field values with the values form the request.

HTTP Request

To edit a notification and return a 404 if the notification is not found:

PATCH /notifications/ID/edit

To edit a notification and create a new one if the notification is not found:

PUT /notifications/ID/edit

Post Parameters

Name Type Description
id int ID of the notification
name string Title of the notification
heading string Heading of the notification
message string Message of the notification
url string URL to go to when the notification is clicked
isPublished bool Published state
publishUp datetime/null Date/time when the notification should be published
publishDown datetime/null Date/time the notification should be un published
language string Language locale of the notification

Response

If PUT, the expected response code is 200 if the notification was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Notification.

Delete Notification

<?php

$notification = $notificationApi->delete($id);

Delete a notification.

HTTP Request

DELETE /notifications/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Notification.

Pages

Use this endpoint to obtain details on Mautic’s landing pages.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$pageApi  = $api->newApi("pages", $auth, $apiUrl);

Get Page

<?php

//...
$page = $pageApi->get($id);
{
    "page": {
        "id": 3,
        "title": "Webinar Landing Page",
        "description": null,
        "isPublished": true,
        "publishUp": null,
        "publishDown": null,
        "dateAdded": "2015-07-15T15:06:02-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-20T13:11:56-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "category": "Events",
        "language": "en",
        "hits": 0,
        "uniqueHits": 0,
        "variantHits": 0,
        "revision": 1,
        "metaDescription": null,
        "redirectType": null,
        "redirectUrl": null,
        "translationChildren": [],
        "translationParent": null,
        "variantChildren": [],
        "variantParent": null,
        "variantSettings": [],
        "variantStartDate": null
    }
}

Get an individual page by ID.

HTTP Request

GET /pages/ID

Response

Expected Response Code: 200

See JSON code example.

Page Properties

Name Type Description
id int ID of the page
title string Title of the page
description string/null Description of the page
alias string Used to generate the URL for the page
isPublished bool Published state
publishUp datetime/null Date/time when the page should be published
publishDown datetime/null Date/time the page should be un published
dateAdded datetime Date/time page was created
createdBy int ID of the user that created the page
createdByUser string Name of the user that created the page
dateModified datetime/null Date/time page was last modified
modifiedBy int ID of the user that last modified the page
modifiedByUser string Name of the user that last modified the page
language string Language locale of the page
hits int Total page hit count
uniqueHits int Unique page hit count
revision int Page revision
metaDescription Meta description for the page’s
redirectType int If unpublished, redirect with 301 or 302
redirectUrl string If unpublished, the URL to redirect to if redirectType is set
translationChildren array Array of Page entities for translations of this landing page
translationParent object The parent/main page if this is a translation
variantHits Hit count since variantStartDate
variantChildren array Array of Page entities for variants of this landing page
variantParent object The parent/main page if this is a variant (A/B test)
variantSettings array The properties of the A/B test
variantStartDate datetime/null The date/time the A/B test began

List Pages

<?php
// ...

$pages = $pageApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "pages": [
        {
            "id": 3,
            "title": "Webinar Landing Page",
            "description": null,
            "isPublished": true,
            "publishUp": null,
            "publishDown": null,
            "dateAdded": "2015-07-15T15:06:02-05:00",
            "createdBy": 1,
            "createdByUser": "Joe Smith",
            "dateModified": "2015-07-20T13:11:56-05:00",
            "modifiedBy": 1,
            "modifiedByUser": "Joe Smith",
            "category": "Events",
            "language": "en",
            "hits": 0,
            "uniqueHits": 0,
            "variantHits": 0,
            "revision": 1,
            "metaDescription": null,
            "redirectType": null,
            "redirectUrl": null,
            "translationChildren": [],
            "translationParent": null,
            "variantChildren": [],
            "variantParent": null,
            "variantSettings": [],
            "variantStartDate": null
        }
    ]
}

HTTP Request

GET /pages

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Page.

Create Page

<?php 

$data = array(
    'title'        => 'Page A',
    'description' => 'This is my first page created via API.',
    'isPublished' => 1
);

$page = $pageApi->create($data);

Create a new page.

HTTP Request

POST /pages/new

Post Parameters

Name Description
title Page title is the only required field
alias string
description A description of the page.
isPublished A value of 0 or 1
language string
metaDescription Meta description for the page’s
redirectType int
redirectUrl string

Response

Expected Response Code: 201

Properties

Same as Get Page.

Edit Page

<?php

$id   = 1;
$data = array(
    'title'        => 'New page title',
    'isPublished' => 0
);

// Create new a page of ID 1 is not found?
$createIfNotFound = true;

$page = $pageApi->edit($id, $data, $createIfNotFound);

Edit a new page. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a page if the given ID does not exist and clears all the page information, adds the information from the request. PATCH fails if the page with the given ID does not exist and updates the page field values with the values form the request.

HTTP Request

To edit a page and return a 404 if the page is not found:

PATCH /pages/ID/edit

To edit a page and create a new one if the page is not found:

PUT /pages/ID/edit

Post Parameters

Name Description
title Page title is the only required field
alias Name alias generated automatically if not set
description A description of the page.
isPublished A value of 0 or 1
language string
metaDescription Meta description for the page’s
redirectType int
redirectUrl string

Response

If PUT, the expected response code is 200 if the page was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Page.

Delete Page

<?php

$page = $pageApi->delete($id);

Delete a page.

HTTP Request

DELETE /pages/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Page.

Point Actions

Use this endpoint to obtain details on Mautic’s point actions.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$pointApi = $api->newApi("points", $auth, $apiUrl);

Get Point Action

<?php

//...
$point = $pointApi->get($id);
{
    "point": {
        "id": 1,
        "name": "Opens Email",
        "description": null,
        "type": "email.send",
        "isPublished": true,
        "publishUp": null,
        "publishDown": null,
        "dateAdded": "2015-07-19T00:34:11-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-19T00:41:44-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "delta": 10,
        "properties": {
            "emails": [
                35
            ]
        },
        "category": null
    }
}

Get an individual point action by ID.

HTTP Request

GET /points/ID

Response

Expected Response Code: 200

See JSON code example.

Point Action Properties

Name Type Description
id int ID of the point
name string Name of the point
description string/null Description of the point
category string Category name
type string Point action type
isPublished bool Published state
publishUp datetime/null Date/time when the point should be published
publishDown datetime/null Date/time the point should be un published
dateAdded datetime Date/time point was created
createdBy int ID of the user that created the point
createdByUser string Name of the user that created the point
dateModified datetime/null Date/time point was last modified
modifiedBy int ID of the user that last modified the point
modifiedByUser string Name of the user that last modified the point
delta int The number of points to give the lead when executing this action
properties array Configured properties for this point action

List Point Actions

<?php
// ...

$points = $pointApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "points": [
        {
            "id": 1,
            "name": "Opens Email",
            "description": null,
            "category": null
            "type": "email.send",
            "isPublished": true,
            "publishUp": null,
            "publishDown": null,
            "dateAdded": "2015-07-19T00:34:11-05:00",
            "createdBy": 1,
            "createdByUser": "Joe Smith",
            "dateModified": "2015-07-19T00:41:44-05:00",
            "modifiedBy": 1,
            "modifiedByUser": "Joe Smith",
            "delta": 10,
            "properties": {
                "emails": [
                    35
                ]
            }
        }
    ]
}

HTTP Request

GET /points

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Point Action.

Create Point Action

<?php 

$data = array(
    'name' => 'test',
    'delta' => 5,
    'type' => 'page.hit',
    'description' => 'created as a API test'
);

$point = $pointApi->create($data);

Create a new point action.

HTTP Request

POST /points/new

Post Parameters

Same as Get Point Action. Point Action fields and actions can be created/edited via the point actions/actions arrays in the point action array.

Response

Expected Response Code: 201

Properties

Same as Get Point Action.

Edit Point Action

<?php

$id   = 1;
$data = array(
    'name' => 'test',
    'delta' => 5,
    'type' => 'page.hit',
    'description' => 'created as a API test'
);

// Create new a point action of ID 1 is not found?
$createIfNotFound = true;

$point = $pointApi->edit($id, $data, $createIfNotFound);

Edit a new point action. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a point action if the given ID does not exist and clears all the point action inpoint actionation, adds the inpoint actionation from the request. Point Action fields and actions will be also deleted if not present in the request. PATCH fails if the point action with the given ID does not exist and updates the point action field values with the values point action the request.

HTTP Request

To edit a point action and return a 404 if the point action is not found:

PATCH /points/ID/edit

To edit a point action and create a new one if the point action is not found:

PUT /points/ID/edit

Post Parameters

Same as Get Point Action. Point Action fields and actions can be created/edited via the point actions/actions arrays in the point action array.

Response

If PUT, the expected response code is 200 if the point action was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Point Action.

Delete Point Action

<?php

$point = $pointApi->delete($id);

Delete a point action.

HTTP Request

DELETE /points/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Point Action.

Get Point Action Types

<?php

$point = $pointApi->getPointActionTypes();

Get array of available Point Action Types

HTTP Request

GET /points/actions/types

Response

Expected Response Code: 200

{  
    "pointActionTypes":{  
        "asset.download":"Downloads an asset",
        "email.send":"Is sent an email",
        "email.open":"Opens an email",
        "form.submit":"Submits a form",
        "page.hit":"Visits a landing page",
        "url.hit":"Visits specific URL"
    }
}

See JSON code example.

Point Triggers

Use this endpoint to obtain details on Mautic’s point triggers.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth   = new ApiAuth();
$auth       = $initAuth->newAuth($settings);
$apiUrl     = "https://your-mautic.com";
$api        = new MauticApi();
$triggerApi = $api->newApi("pointTriggers", $auth, $apiUrl);

Get Point Trigger

<?php

//...
$trigger = $triggerApi->get($id);
{
    "trigger": {
         "id": 1,
         "name": "Trigger test",
         "description": null,
         "category": null,      
         "isPublished": true,      
         "publishUp": null,
         "publishDown": null,
         "dateAdded": "2015-07-23T03:20:42-05:00",
         "createdBy": 1,
         "createdByUser": "Joe Smith",
         "dateModified": null,
         "modifiedBy": null,
         "modifiedByUser": null,l,
         "points": 10,
         "color": "ab5959",
         "events": {
             "1": {
                 "id": 1,
                 "type": "email.send",
                 "name": "Send email",
                 "description": null,
                 "order": 1,
                 "properties": {
                    "email": 21
                 }
             }
         }
     }
}

Get an individual point trigger by ID.

HTTP Request

GET /points/triggers/ID

Response

Expected Response Code: 200

See JSON code example.

Point Trigger Properties

Name Type Description
id int ID of the point
name string Name of the point
description string/null Description of the point
category string Category name
isPublished bool Published state
publishUp datetime/null Date/time when the point should be published
publishDown datetime/null Date/time the point should be un published
dateAdded datetime Date/time point was created
createdBy int ID of the user that created the point
createdByUser string Name of the user that created the point
dateModified datetime/null Date/time point was last modified
modifiedBy int ID of the user that last modified the point
modifiedByUser string Name of the user that last modified the point
points int The minimum number of points before the trigger events are executed
color string Color hex to highlight the lead with. This value does NOT include the pound sign (#)
events array Array of TriggerEvent entities for this trigger. See below.

Trigger Event Properties

Name Type Description
id int ID of the event
type string Event type
name string Name of the event
description string Description of the event
order int Event order
properties array Configured properties for the event

List Point Triggers

<?php
// ...

$triggers = $triggerApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": 1,
    "triggers": [
        {
            "id": 1,
            "name": "Trigger test",
            "description": null,
            "category": null,      
            "isPublished": true,      
            "publishUp": null,
            "publishDown": null,
            "dateAdded": "2015-07-23T03:20:42-05:00",
            "createdBy": 1,
            "createdByUser": "Joe Smith",
            "dateModified": null,
            "modifiedBy": null,
            "modifiedByUser": null,l,
            "points": 10,
            "color": "ab5959",
            "events": {
                "1": {
                    "id": 1,
                    "type": "email.send",
                    "name": "Send email",
                    "description": null,
                    "order": 1,
                    "properties": {
                        "email": 21
                    }
                }
            }
        }
    ]
}

HTTP Request

GET /points/triggers

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Point Trigger.

Create Point Trigger

<?php 

$data = array(
    'name' => 'test',
    'description' => 'created as a API test',
    'points' => 5,
    'color' => '4e5d9d',
    'trigger_existing_leads' => false,
    'events' => array(
        array(
            'name' => 'tag test event',
            'description' => 'created as a API test',
            'type' => 'lead.changetags',
            'order' => 1,
            'properties' => array(
                'add_tags' => array('tag-a'),
                'remove_tags' => array()
            )
        ),
        array(
            'name' => 'send email test event',
            'description' => 'created as a API test',
            'type' => 'email.send',
            'order' => 2,
            'properties' => array(
                'email' => 1
            )
        )
    )
);

$trigger = $triggerApi->create($data);

Create a new point trigger.

HTTP Request

POST /points/triggers/new

Post Parameters

Same as Get Point Trigger. Point Trigger events can be created/edited via the point trigger event arrays placed in the point trigger array.

Response

Expected Response Code: 201

Properties

Same as Get Point Trigger.

Edit Point Trigger

<?php

$id   = 1;
$data = array(
    'name' => 'test',
    'description' => 'created as a API test',
    'points' => 5,
    'color' => '4e5d9d',
    'trigger_existing_leads' => false,
    'events' => array(
        array(
            'name' => 'tag test event',
            'description' => 'created as a API test',
            'type' => 'lead.changetags',
            'order' => 1,
            'properties' => array(
                'add_tags' => array('tag-a'),
                'remove_tags' => array()
            )
        ),
        array(
            'name' => 'send email test event',
            'description' => 'created as a API test',
            'type' => 'email.send',
            'order' => 2,
            'properties' => array(
                'email' => 1
            )
        )
    )
);

// Create new a point trigger of ID 1 is not found?
$createIfNotFound = true;

$trigger = $triggerApi->edit($id, $data, $createIfNotFound);

Edit a new point trigger. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a point trigger if the given ID does not exist and clears all the point trigger information, adds the information from the request. Point Trigger events will be also deleted if not present in the request. PATCH fails if the point trigger with the given ID does not exist and updates the point trigger field values with the values point trigger the request.

HTTP Request

To edit a point trigger and return a 404 if the point trigger is not found:

PATCH /points/triggers/ID/edit

To edit a point trigger and create a new one if the point trigger is not found:

PUT /points/triggers/ID/edit

Post Parameters

Same as Get Point Trigger. Point Trigger events can be created/edited via the point triggers event arrays placed in the point trigger array.

Response

If PUT, the expected response code is 200 if the point trigger was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Point Trigger.

Delete Point Trigger

<?php

$trigger = $triggerApi->delete($id);

Delete a point trigger.

HTTP Request

DELETE /points/triggers/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Point Trigger.

Delete Point Trigger Events

The following examples will show how to delete events with ID 56 and 59.

<?php

$trigger = $triggerApi->deleteFields($triggerId, array(56, 59));

Delete a point trigger events.

HTTP Request

DELETE /points/triggers/ID/events/delete?events[]=56&events[]=59

Response

Expected Response Code: 200

Properties

Same as Get Point Trigger.

Get Point Trigger Event Types

<?php

$point = $pointApi->getEventTypes();

Get array of available Point Trigger Event Types

HTTP Request

GET /points/triggers/events/types

Response

Expected Response Code: 200

{  
    "eventTypes":{  
        "campaign.changecampaign":"Modify contact's campaigns",
        "lead.changelists":"Modify contact's segments",
        "lead.changetags":"Modify contact's tags",
        "plugin.leadpush":"Push contact to integration",
        "email.send":"Send an email"
    }
}

Roles

Use this endpoint to obtain details on Mautic’s roles (administrators).

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$roleApi  = $api->newApi("roles", $auth, $apiUrl);

Get Role

<?php

//...
$role = $roleApi->get($id);
{  
  "role":{  
    "isPublished":true,
    "dateAdded":"2016-11-09T15:24:32+00:00",
    "createdBy":1,
    "createdByUser":"John Doe",
    "dateModified":null,
    "modifiedBy":null,
    "modifiedByUser":null,
    "id":13,
    "name":"API test role",
    "description":"created via AIP",
    "isAdmin":false,
    "rawPermissions":{  
      "email:emails":[  
        "viewown",
        "viewother"
      ]
    }
  }
}

Get an individual role by ID.

HTTP Request

GET /roles/ID

Response

Expected Response Code: 200

See JSON code example.

Role Properties

Name Type Description
id int ID of the contact
dateAdded datetime Date/time contact was created
createdBy int ID of the role that created the contact
createdByRole string Name of the role that created the contact
dateModified datetime/null Date/time contact was last modified
modifiedBy int ID of the role that last modified the contact
modifiedByRole string Name of the role that last modified the contact
name string Name of the role
description string Description of the role
isAdmin boolean Whether the role has full access or only some
rawPermissions array List of roles

List Contact Roles

<?php

//...
$roles = $roleApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
  "total":9,
  "roles":[  
    {  
      "isPublished":true,
      "dateAdded":"2016-08-01T11:51:32+00:00",
      "createdBy":1,
      "createdByUser":"John Doe",
      "dateModified":null,
      "modifiedBy":null,
      "modifiedByUser":null,
      "id":2,
      "name":"view email",
      "description":null,
      "isAdmin":false,
      "rawPermissions":{  
        "email:emails":[  
          "viewown",
          "viewother"
        ]
      }
    },
    [...]
  ]
}

HTTP Request

GET /roles

Response

Expected Response Code: 200

See JSON code example.

Role Properties

Name Type Description
id int ID of the contact
dateAdded datetime Date/time contact was created
createdBy int ID of the role that created the contact
createdByRole string Name of the role that created the contact
dateModified datetime/null Date/time contact was last modified
modifiedBy int ID of the role that last modified the contact
modifiedByRole string Name of the role that last modified the contact
name string Name of the role
description string Description of the role
isAdmin boolean Whether the role has full access or only some
rawPermissions array List of roles

Create Role

<?php 

$data = array(
    'name' => 'API test role',
    'description' => 'created via AIP',
    'rawPermissions' => array (
        'email:emails' => 
        array (
            'viewown',
            'viewother',
        ),
    )
);

$role = $roleApi->create($data);

Create a new role.

HTTP Request

POST /roles/new

Post Parameters

Name Description
name string
description string
isAdmin boolean
rawPermissions array

Response

Expected Response Code: 201

Properties

Same as Get Role.

Edit Role

<?php

$id   = 1;
$data = array(
    'name' => 'API test role',
    'description' => 'created via AIP',
    'rawPermissions' => array (
        'email:emails' => 
        array (
            'editown',
            'editother',
        ),
    )
);

// Create new a role of ID 1 is not found?
$createIfNotFound = true;

$role = $roleApi->edit($id, $data, $createIfNotFound);

Edit a new role. Role that this supports PUT or PATCH depending on the desired behavior.

PUT creates a role if the given ID does not exist and clears all the role information, adds the information from the request. PATCH fails if the role with the given ID does not exist and updates the role field values with the values form the request.

HTTP Request

To edit a role and return a 404 if the role is not found:

PATCH /roles/ID/edit

To edit a role and create a new one if the role is not found:

PUT /roles/ID/edit

Post Parameters

Name Description
name string
description string
isAdmin boolean
rawPermissions array

Response

If PUT, the expected response code is 200 if the role was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Role.

Delete Role

<?php

$role = $roleApi->delete($id);

Delete a role.

HTTP Request

DELETE /roles/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Role.

Segments

Use this endpoint to obtain details on Mautic’s contact segments or to manipulate contact memberships.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth   = new ApiAuth();
$auth       = $initAuth->newAuth($settings);
$apiUrl     = "https://your-mautic.com";
$api        = new MauticApi();
$segmentApi = $api->newApi("segments", $auth, $apiUrl);

Get Segment

<?php

//...
$segment = $segmentApi->get($id);
    "list": {
        "id": 47,
        "isPublished": 1,
        "dateAdded": "2015-07-21T12:27:12-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-21T14:12:03-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "name": "Segment A",
        "alias": "segment-a",
        "description": "This is my first segment created via API.",
        "filters": [
          "glue": "and",
          "field": "city",
          "type": "text",
          "filter": "Prague",
          "display": null,
          "operator": "=",
        ],
        "isGlobal": true
    }

Get an individual segment by ID.

HTTP Request

GET /segments/ID

Response

Expected Response Code: 200

See JSON code example.

Segment Properties

Name Type Description
id int ID of the segment
isPublished boolean Whether the segment is published
dateAdded datetime Date/time segment was created
createdBy int ID of the user that created the segment
createdByUser string Name of the user that created the segment
dateModified datetime/null Date/time segment was last modified
modifiedBy int ID of the user that last modified the segment
modifiedByUser string Name of the user that last modified the segment
name string Segment name
alias string Segment alias
description string Segment description
filters array Smart filters for the segment
isGlobal boolean Whether the segment is global. 0 means only the author will see it.

List Contact Segments

<?php

//...
$segments = $segmentApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
  "total": 13,
  "lists": [
    {
        "id": 47,
        "isPublished": 1,
        "dateAdded": "2015-07-21T12:27:12-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-21T14:12:03-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "name": "Segment A",
        "alias": "segment-a",
        "description": "This is my first segment created via API.",
        "filters": [
          "glue": "and",
          "field": "city",
          "type": "text",
          "filter": "Prague",
          "display": null,
          "operator": "=",
        ],
        "isGlobal": true
    },
    ...
  ]
}

Returns a list of contact segments available to the user. This list is not filterable.

HTTP Request

GET /segments

Response

Expected Response Code: 200

See JSON code example.

Segment Properties

Name Type Description
total int Count of all segments
id int ID of the segment
isPublished boolean Whether the segment is published
dateAdded datetime Date/time segment was created
createdBy int ID of the user that created the segment
createdByUser string Name of the user that created the segment
dateModified datetime/null Date/time segment was last modified
modifiedBy int ID of the user that last modified the segment
modifiedByUser string Name of the user that last modified the segment
name string Segment name
alias string Segment alias
description string Segment description
filters array Smart filters for the segment
isGlobal boolean Whether the segment is global. 0 means only the author will see it.

Create Segment

<?php 

$data = array(
    'name'        => 'Segment A',
    'alias'       => 'segment-a',
    'description' => 'This is my first segment created via API.',
    'isPublished' => 1
);

$segment = $segmentApi->create($data);

Create a new segment.

HTTP Request

POST /segments/new

Post Parameters

Name Description
name Segment name is the only required field
alias Name alias generated automatically if not set
description A description of the segment.
isPublished A value of 0 or 1
isGlobal boolean

Response

Expected Response Code: 201

Properties

Same as Get Segment.

Edit Segment

<?php

$id   = 1;
$data = array(
    'name'        => 'New segment name',
    'isPublished' => 0
);

// Create new a segment of ID 1 is not found?
$createIfNotFound = true;

$segment = $segmentApi->edit($id, $data, $createIfNotFound);

Edit a new segment. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a segment if the given ID does not exist and clears all the segment information, adds the information from the request. PATCH fails if the segment with the given ID does not exist and updates the segment field values with the values form the request.

HTTP Request

To edit a segment and return a 404 if the segment is not found:

PATCH /segments/ID/edit

To edit a segment and create a new one if the segment is not found:

PUT /segments/ID/edit

Post Parameters

Name Description
name Segment name is the only required field
alias Name alias generated automatically if not set
description A description of the segment.
isPublished A value of 0 or 1
isGlobal boolean

Response

If PUT, the expected response code is 200 if the segment was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Segment.

Delete Segment

<?php

$segment = $segmentApi->delete($id);

Delete a segment.

HTTP Request

DELETE /segments/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Segment.

Add Contact to a Segment

<?php

//...
$response = $segmentApi->addContact($segmentId, $contactId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually add a contact to a specific segment.

HTTP Request

POST /segments/SEGMENT_ID/contact/add/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Remove Contact from a Segment

<?php

//...
$response = $segmentApi->removeContact($segmentId, $contactId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually remove a contact to a specific segment.

HTTP Request

POST /segments/SEGMENT_ID/contact/remove/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Text messages

Use this endpoint to obtain details on Mautic’s Text Messages (SMSes).

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$smsApi   = $api->newApi("smses", $auth, $apiUrl);

Get Text message

<?php

//...
$sms = $smsApi->get($id);
{  
    "sms":{  
        "isPublished":true,
        "dateAdded":"2016-09-14T12:14:45+00:00",
        "createdBy":1,
        "createdByUser":"John Doe",
        "dateModified":null,
        "modifiedBy":null,
        "modifiedByUser":null,
        "id":1,
        "name":"Message A",
        "message":"Hello",
        "language":"en",
        "category":null,
        "publishUp":null,
        "publishDown":null,
        "sentCount":0
    }
}

Get an individual sms by ID.

HTTP Request

GET /smses/ID

Response

Expected Response Code: 200

See JSON code example.

Text message Properties

Name Type Description
id int ID of the sms
name string Title of the sms
message string Message of the sms
isPublished bool Published state
publishUp datetime/null Date/time when the sms should be published
publishDown datetime/null Date/time the sms should be un published
dateAdded datetime Date/time sms was created
createdBy int ID of the user that created the sms
createdByUser string Name of the user that created the sms
dateModified datetime/null Date/time sms was last modified
modifiedBy int ID of the user that last modified the sms
modifiedByUser string Name of the user that last modified the sms
language string Language locale of the sms
sentCount int How many times the SMS was sent

List Text messages

<?php
// ...

$smses = $smsApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
    "total":1,
    "smses":[  
        {  
            "isPublished":true,
            "dateAdded":"2016-09-14T12:14:45+00:00",
            "createdBy":1,
            "createdByUser":"John Doe",
            "dateModified":null,
            "modifiedBy":null,
            "modifiedByUser":null,
            "id":1,
            "name":"Message A",
            "message":"Hello",
            "language":"en",
            "category":null,
            "publishUp":null,
            "publishDown":null,
            "sentCount":0
        }
    ]
}

HTTP Request

GET /smses

Query Parameters

Name Description
search String or search command to filter entities by.
start Starting row for the entities returned. Defaults to 0.
limit Limit number of entities to return. Defaults to the system configuration for pagination (30).
orderBy Column to sort by. Can use any column listed in the response.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.

Response

Expected Response Code: 200

See JSON code example.

Properties

Same as Get Text message.

Create Text message

<?php 

$data = array(
    'name'        => 'Text message A',
    'message' => 'This is my first sms created via API.',
    'isPublished' => 1
);

$sms = $smsApi->create($data);

Create a new sms.

HTTP Request

POST /smses/new

Post Parameters

Name Type Description
id int ID of the sms
name string Title of the sms
message string Message of the sms
isPublished bool Published state
publishUp datetime/null Date/time when the sms should be published
publishDown datetime/null Date/time the sms should be un published
dateAdded datetime Date/time sms was created
createdBy int ID of the user that created the sms
createdByUser string Name of the user that created the sms
dateModified datetime/null Date/time sms was last modified
modifiedBy int ID of the user that last modified the sms
modifiedByUser string Name of the user that last modified the sms
language string Language locale of the sms
sentCount int How many times the SMS was sent

Response

Expected Response Code: 201

Properties

Same as Get Text message.

Edit Text message

<?php

$id   = 1;
$data = array(
    'name'        => 'New sms name',
    'isPublished' => 0
);

// Create new a sms of ID 1 is not found?
$createIfNotFound = true;

$sms = $smsApi->edit($id, $data, $createIfNotFound);

Edit a new sms. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a sms if the given ID does not exist and clears all the sms information, adds the information from the request. PATCH fails if the sms with the given ID does not exist and updates the sms field values with the values form the request.

HTTP Request

To edit a sms and return a 404 if the sms is not found:

PATCH /smses/ID/edit

To edit a sms and create a new one if the sms is not found:

PUT /smses/ID/edit

Post Parameters

Name Type Description
id int ID of the sms
name string Title of the sms
message string Message of the sms
isPublished bool Published state
publishUp datetime/null Date/time when the sms should be published
publishDown datetime/null Date/time the sms should be un published
language string Language locale of the sms

Response

If PUT, the expected response code is 200 if the sms was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Text message.

Delete Text message

<?php

$sms = $smsApi->delete($id);

Delete a sms.

HTTP Request

DELETE /smses/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Text message.

Stages

Use this endpoint to obtain details on Mautic’s contact stages.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$stageApi = $api->newApi("stages", $auth, $apiUrl);

Get Stage

<?php

//...
$stage = $stageApi->get($id);
    "stage": {
        "id": 47,
        "isPublished": 1,
        "dateAdded": "2015-07-21T12:27:12-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-21T14:12:03-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "name": "Stage A",
        "category": null,
        "description": "This is my first stage created via API.",
        "weight": 0,
        "publishUp": "2015-07-21T14:12:03-05:00",
        "publishDown": "2015-07-21T14:12:03-05:00"
    }

Get an individual stage by ID.

HTTP Request

GET /stages/ID

Response

Expected Response Code: 200

See JSON code example.

Stage Properties

Name Type Description
id int ID of the stage
isPublished boolean Whether the stage is published
dateAdded datetime Date/time stage was created
createdBy int ID of the user that created the stage
createdByUser string Name of the user that created the stage
dateModified datetime/null Date/time stage was last modified
modifiedBy int ID of the user that last modified the stage
modifiedByUser string Name of the user that last modified the stage
name string Stage name
category int Stage category ID
description string Stage description
weight int Stage’s weight
publishUp datetime Date/time stage should be published
publishDown datetime Date/time stage should be unpublished

List Contact Stages

<?php

//...
$stages = $stageApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
  "total": 4,
  "stages": [
    {
        "id": 47,
        "isPublished": 1,
        "dateAdded": "2015-07-21T12:27:12-05:00",
        "createdBy": 1,
        "createdByUser": "Joe Smith",
        "dateModified": "2015-07-21T14:12:03-05:00",
        "modifiedBy": 1,
        "modifiedByUser": "Joe Smith",
        "name": "Stage A",
        "category": null,
        "description": "This is my first stage created via API.",
        "weight": 0,
        "publishUp": "2015-07-21T14:12:03-05:00",
        "publishDown": "2015-07-21T14:12:03-05:00"
    },
    ...
  ]
}

HTTP Request

GET /stages

Response

Expected Response Code: 200

See JSON code example.

Stage Properties

Name Type Description
total int Count of all stages
id int ID of the stage
isPublished boolean Whether the stage is published
dateAdded datetime Date/time stage was created
createdBy int ID of the user that created the stage
createdByUser string Name of the user that created the stage
dateModified datetime/null Date/time stage was last modified
modifiedBy int ID of the user that last modified the stage
modifiedByUser string Name of the user that last modified the stage
name string Stage name
category int Stage category ID
description string Stage description
weight int Stage’s weight
publishUp datetime Date/time stage should be published
publishDown datetime Date/time stage should be unpublished

Create Stage

<?php 

$data = array(
    'name'        => 'Stage A',
    'weight'      => 5,
    'description' => 'This is my first stage created via API.',
    'isPublished' => 1
);

$stage = $stageApi->create($data);

Create a new stage.

HTTP Request

POST /stages/new

Post Parameters

Name Description
name Stage name is the only required field
weight int
description A description of the stage.
isPublished A value of 0 or 1

Response

Expected Response Code: 201

Properties

Same as Get Stage.

Edit Stage

<?php

$id   = 1;
$data = array(
    'name'        => 'New stage name',
    'isPublished' => 0
);

// Create new a stage of ID 1 is not found?
$createIfNotFound = true;

$stage = $stageApi->edit($id, $data, $createIfNotFound);

Edit a new stage. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a stage if the given ID does not exist and clears all the stage information, adds the information from the request. PATCH fails if the stage with the given ID does not exist and updates the stage field values with the values form the request.

HTTP Request

To edit a stage and return a 404 if the stage is not found:

PATCH /stages/ID/edit

To edit a stage and create a new one if the stage is not found:

PUT /stages/ID/edit

Post Parameters

Name Description
name Stage name is the only required field
alias Name alias generated automatically if not set
description A description of the stage.
isPublished A value of 0 or 1
weight int

Response

If PUT, the expected response code is 200 if the stage was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Stage.

Delete Stage

<?php

$stage = $stageApi->delete($id);

Delete a stage.

HTTP Request

DELETE /stages/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Stage.

Add Contact to a Stage

<?php

//...
$response = $stageApi->addContact($contactId, $stageId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually add a contact to a specific stage.

HTTP Request

POST /stages/STAGE_ID/contact/add/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Remove Contact from a Stage

<?php

//...
$response = $stageApi->removeContact($contactId, $stageId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually remove a contact from a specific stage.

HTTP Request

POST /stages/STAGE_ID/contact/remove/CONTACT_ID

Response

Expected Response Code: 200

See JSON code example.

Stats

This endpoint is useful for downloading a full statistical table.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$statsApi = $api->newApi("stats", $auth, $apiUrl);

Get Available Stat Tables

<?php

//...
$tables = $statsApi->get();
{
    "availableTables": [
        "asset_downloads",
        "audit_log",
        "campaign_lead_event_log",
        "campaign_leads",
        "channel_url_trackables",
        "companies_leads",
        "dynamic_content_lead_data",
        "dynamic_content_stats",
        "email_stats",
        "email_stats_devices",
        "focus_stats",
        "form_submissions",
        "ip_addresses",
        "lead_categories",
        "lead_companies_change_log",
        "lead_devices",
        "lead_donotcontact",
        "lead_frequencyrules",
        "lead_lists_leads",
        "lead_points_change_log",
        "lead_stages_change_log",
        "lead_utmtags",
        "page_hits",
        "page_redirects",
        "point_lead_action_log",
        "point_lead_event_log",
        "push_notification_stats",
        "sms_message_stats",
        "stage_lead_action_log",
        "tweet_stats",
        "video_hits",
        "webhook_logs"
    ],
    "tableColumns": {
        "asset_downloads": [
            "asset_id",
            "code",
            "date_download",
            "email_id",
            "id",
            "ip_id",
            "lead_id",
            "referer",
            "source",
            "source_id",
            "tracking_id"
        ],
        "audit_log": [
            "action",
            "bundle",
            "date_added",
            "details",
            "id",
            "ip_address",
            "object",
            "object_id",
            "user_id",
            "user_name"
        ],
        "campaign_lead_event_log": [
            "campaign_id",
            "channel",
            "channel_id",
            "date_triggered",
            "event_id",
            "id",
            "ip_id",
            "is_scheduled",
            "lead_id",
            "metadata",
            "non_action_path_taken",
            "rotation",
            "system_triggered",
            "trigger_date"
        ],
        "campaign_leads": [
            "campaign_id",
            "date_added",
            "date_last_exited",
            "lead_id",
            "manually_added",
            "manually_removed",
            "rotation"
        ],
        "channel_url_trackables": [
            "channel",
            "channel_id",
            "hits",
            "redirect_id",
            "unique_hits"
        ],
        "companies_leads": [
            "company_id",
            "date_added",
            "is_primary",
            "lead_id",
            "manually_added",
            "manually_removed"
        ],
        "dynamic_content_lead_data": [
            "date_added",
            "dynamic_content_id",
            "id",
            "lead_id",
            "slot"
        ],
        "dynamic_content_stats": [
            "date_sent",
            "dynamic_content_id",
            "id",
            "last_sent",
            "lead_id",
            "sent_count",
            "sent_details",
            "source",
            "source_id",
            "tokens"
        ],
        "email_stats": [
            "copy_id",
            "date_read",
            "date_sent",
            "email_address",
            "email_id",
            "id",
            "ip_id",
            "is_failed",
            "is_read",
            "last_opened",
            "lead_id",
            "list_id",
            "open_count",
            "open_details",
            "retry_count",
            "source",
            "source_id",
            "tokens",
            "tracking_hash",
            "viewed_in_browser"
        ],
        "email_stats_devices": [
            "date_opened",
            "device_id",
            "id",
            "ip_id",
            "stat_id"
        ],
        "focus_stats": [
            "date_added",
            "focus_id",
            "id",
            "lead_id",
            "type",
            "type_id"
        ],
        "form_submissions": [
            "date_submitted",
            "form_id",
            "id",
            "ip_id",
            "lead_id",
            "page_id",
            "referer",
            "tracking_id"
        ],
        "ip_addresses": [
            "id",
            "ip_address",
            "ip_details"
        ],
        "lead_categories": [
            "category_id",
            "date_added",
            "id",
            "lead_id",
            "manually_added",
            "manually_removed"
        ],
        "lead_companies_change_log": [
            "action_name",
            "company_id",
            "date_added",
            "event_name",
            "id",
            "lead_id",
            "type"
        ],
        "lead_devices": [
            "client_info",
            "date_added",
            "device",
            "device_brand",
            "device_fingerprint",
            "device_model",
            "device_os_name",
            "device_os_platform",
            "device_os_shortname",
            "device_os_version",
            "id",
            "lead_id"
        ],
        "lead_donotcontact": [
            "channel",
            "channel_id",
            "comments",
            "date_added",
            "id",
            "lead_id",
            "reason"
        ],
        "lead_frequencyrules": [
            "channel",
            "date_added",
            "frequency_number",
            "frequency_time",
            "id",
            "lead_id",
            "pause_from_date",
            "pause_to_date",
            "preferred_channel"
        ],
        "lead_lists_leads": [
            "date_added",
            "leadlist_id",
            "lead_id",
            "manually_added",
            "manually_removed"
        ],
        "lead_points_change_log": [
            "action_name",
            "date_added",
            "delta",
            "event_name",
            "id",
            "ip_id",
            "lead_id",
            "type"
        ],
        "lead_stages_change_log": [
            "action_name",
            "date_added",
            "event_name",
            "id",
            "lead_id",
            "stage_id"
        ],
        "lead_utmtags": [
            "date_added",
            "id",
            "lead_id",
            "query",
            "referer",
            "remote_host",
            "url",
            "user_agent",
            "utm_campaign",
            "utm_content",
            "utm_medium",
            "utm_source",
            "utm_term"
        ],
        "page_hits": [
            "browser_languages",
            "city",
            "code",
            "country",
            "date_hit",
            "date_left",
            "device_id",
            "email_id",
            "id",
            "ip_id",
            "isp",
            "lead_id",
            "organization",
            "page_id",
            "page_language",
            "query",
            "redirect_id",
            "referer",
            "region",
            "remote_host",
            "source",
            "source_id",
            "tracking_id",
            "url",
            "url_title",
            "user_agent"
        ],
        "page_redirects": [
            "checked_out",
            "checked_out_by",
            "checked_out_by_user",
            "created_by",
            "created_by_user",
            "date_added",
            "date_modified",
            "hits",
            "id",
            "is_published",
            "modified_by",
            "modified_by_user",
            "redirect_id",
            "unique_hits",
            "url"
        ],
        "point_lead_action_log": [
            "date_fired",
            "ip_id",
            "lead_id",
            "point_id"
        ],
        "point_lead_event_log": [
            "date_fired",
            "event_id",
            "ip_id",
            "lead_id"
        ],
        "push_notification_stats": [
            "click_count",
            "click_details",
            "date_clicked",
            "date_sent",
            "id",
            "ip_id",
            "is_clicked",
            "last_clicked",
            "lead_id",
            "list_id",
            "notification_id",
            "retry_count",
            "source",
            "source_id",
            "tokens",
            "tracking_hash"
        ],
        "sms_message_stats": [
            "date_sent",
            "id",
            "ip_id",
            "lead_id",
            "list_id",
            "sms_id",
            "source",
            "source_id",
            "tokens",
            "tracking_hash"
        ],
        "stage_lead_action_log": [
            "date_fired",
            "ip_id",
            "lead_id",
            "stage_id"
        ],
        "tweet_stats": [
            "date_sent",
            "favorite_count",
            "handle",
            "id",
            "is_failed",
            "lead_id",
            "response_details",
            "retry_count",
            "retweet_count",
            "source",
            "source_id",
            "tweet_id",
            "twitter_tweet_id"
        ],
        "video_hits": [
            "browser_languages",
            "channel",
            "channel_id",
            "city",
            "code",
            "country",
            "date_hit",
            "date_left",
            "duration",
            "guid",
            "id",
            "ip_id",
            "isp",
            "lead_id",
            "organization",
            "page_language",
            "query",
            "referer",
            "region",
            "remote_host",
            "time_wat