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 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 all about contributing to Mautic as a Developer.

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. 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. You can also execute the install process from command line: * Add a local.php file in app/config * Edit the local.php file using the following template (adapt to your own settings): php <?php $parameters = array( 'db_driver' => 'pdo_mysql', 'db_host' => 'localhost', 'db_table_prefix' => null, 'db_port' => '3306', 'db_name' => 'mautic', 'db_user' => 'root', 'db_password' => 'root_password', 'db_backup_tables' => true, 'db_backup_prefix' => 'bak_', ); * Execute the following command and add your own options: php bin/console mautic:install http://your.mautic.instance

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. Note: If you’re using PHP-FPM (e.g. when using Docker/DDEV), you need to have cgi.fix_pathinfo = 1 in your PHP configuration, otherwise /index_dev.php/* might not work. Read more about the implications of this setting here.

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.

Cache Bundle

Enables PSR-6 and PSR-16 caching. Check: Symfony Cache Component

Namespace versus tag

This bundle introduces tags to cache. All its adapters are fully tag aware which makes the use of namespace obsolete for daily use.

Previously if you wanted to keep control on cache section and did not want to hold the index of all keys to clear you would have to use namespace.

Disadvantage of this approach is a new adapter being created for each namespace.

Symfony 3.4 Cache uses tag-aware adapters. If you want to clear all records related to your bundle or component you just need to tag them.

    /** @var CacheProvider $cache */
    $cache = $this->get('mautic.cache.provider');

    /** @var CacheItemInterface $item */
    $item = $cache->getItem('test_tagged_Item');
    $item->set('yesa!!!');
    $item->tag(['firstTag', 'secondTag']);
    $item->expiresAfter(20000);

All you need to do now is to clear all tagged items:

$cache->invalidateTags(['firstTag']);

Pools clearing

Removing Cache ItemsĀ¶

Cache Pools include methods to delete a cache item, some of them or all of them. The most common is Psr\\Cache\\CacheItemPoolInterface::deleteItem, which deletes the cache item identified by the given key.

$isDeleted = $cache->deleteItem('user_'.$userId);

Use the Psr\Cache\CacheItemPoolInterface::deleteItems method to delete several cache items simultaneously (it returns true only if all the items have been deleted, even when any or some of them don’t exist):

Configuration

Plugin comes preconfigured to utilize filesystem caching.

These are the default settings: php 'cache_adapter' => 'mautic.cache.adapter.filesystem', 'cache_prefix' => 'app', 'cache_lifetime' => 86400

They can be overridden in local.php like this:

'cache_adapter'  => 'mautic.cache.adapter.redis',
'cache_prefix'   => 'app_cache',
'cache_lifetime' => 86400,

Delivered adapters

In order to use another adapter just set it up as a service

Clearing the cache

The cache is cleared when cache:clear command is run. The cache can be cleared by running

bin/console mautic:cache:clear

Features auto pruning on adapter initialization

Usage

PSR-6

    /** @var CacheProvider $cache */
    $cache = $this->get('mautic.cache.provider');

    /** @var CacheItemInterface $item */
    $item = $cache->getItem('test_tagged_Item');
    $item->set('yesa!!!');
    $item->tag(['firstTag', 'secondTag']);
    $item->expiresAfter(20000);

    $cache->save($item);

    $item = $cache->getItem('test_nottagged_Item2');
    $item->tag(['firstTag']);
    $cache->save($item);

    $item = $cache->getItem('test_nottagged_Item3');
    $item->tag(['secondTag']);
    $cache->save($item);

    $cache->commit();

    var_dump($cache->getItem('test_nottagged_Item2')->isHit());
    var_dump($cache->getItem('test_nottagged_Item3')->isHit());

    $cache->invalidateTags(['firstTag']);

    var_dump($cache->getItem('test_nottagged_Item2')->isHit());
    var_dump($cache->getItem('test_nottagged_Item3')->isHit());

    $cache->commit();

PSR-16

        /** @var CacheProvider $cache */
        $cache = $this->get('mautic.cache.provider');


        $simpleCache = $cache->getSimpleCache();
        $test = $simpleCache->get('test_value');

        var_dump($test);

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.

3.0.0

See UPGRADE-3.0.md

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

THIS IS NOW DEPRECATED AND DISCOURAGED FROM USE. USE THE INTEGRATION FRAMEWORK’S MIGRATION INSTEAD.

<?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 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.dynamic_content="Dynamic Content"
mautic.core.dynamic_content.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.dynamic_content");
// outputs "Dynamic Content"

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

Mautic.translate("mautic.core.dynamic_content.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.

Description of tables

This is not yet a comprehensive list of all database tables. Contributions are very welcome.

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.

Integration Framework

The IntegrationsBundle is meant to be a drop in replacement for Mautic’s PluginBundle’s AbstractIntegration class. It provides cleaner interfaces for configuring, authenticating and syncing contacts/companies with 3rd party integrations.

An example HelloWorld plugin is available here.

Using the Integration Framework

If the integration requires authentication with the 3rd party service

  1. Register the integration as an integration that requires configuration options.
  2. Create a custom Symfony form type for the required credentials and return it as part of the config interface.
  3. Create a custom service that builds and configures the Guzzle client required to authenticate and communicate with the 3rd party service. Use an existing supported factory or create a new one.

If the integration has extra configuration settings for features unique to it

  1. Register the integration as an integration that requires configuration options.
  2. Create a custom Symfony form type for the features and return it as part of the config interface.

If the integration syncs with Mautic’s contacts and/or companies

  1. Read about the sync engine.

If the integration includes a builder integration (email or landing page)

  1. Register the integration as an integration that provides a custom builder.
  2. Configure what featured builders the integration supports (Mautic currently supports “email” and “page” builders).

Basics

Each integration provides its unique name as registered with Mautic, icon, and display name. When an integration is registered, the integration helper classes will manage the \Mautic\PluginBundle\Entity\Integration object through \Mautic\IntegrationsBundle\Integration\Interfaces\IntegrationInterface. It handles decryption and encryption of the integration’s API keys so the implementing code never has to.

Registering the Integration

All integrations whether it uses the config, auth or sync interfaces must have a class that registers itself with Mautic. The integration will be listed no the /s/plugins page.

In the plugin’s Config/config.php, register the integration using the tag mautic.basic_integration.

<?php
return [
    // ...
    'services' => [
        // ...
        'integrations' => [
            'helloworld.integration' => [
                'class' => \MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration::class,
                'tags'  => [
                    'mautic.basic_integration',
                ],
            ],
            // ...
        ],
        // ...
    ],
    // ...
];

The HelloWorldIntegration will need to implement \Mautic\IntegrationsBundle\Integration\Interfaces\IntegrationInterface and \Mautic\IntegrationsBundle\Integration\Interfaces\BasicInterface interfaces. Most use cases can simply extend the \Mautic\IntegrationsBundle\Integration\BasicIntegration abstract class then define the getName(), getDisplayName() and getIcon() methods.

<?php
namespace MauticPlugin\HelloWorldBundle\Integration;

use MauticPlugin\IntegrationsBundle\Integration\BasicIntegration;
use MauticPlugin\IntegrationsBundle\Integration\Interfaces\BasicInterface;
use MauticPlugin\IntegrationsBundle\Integration\Interfaces\IntegrationInterface;

class HelloWorldIntegration extends BasicIntegration
{
    const NAME = 'HelloWorld';

    public function getName(): string
    {
        return self::NAME;
    }

    public function getDisplayName(): string
    {
        return 'Hello World';
    }

    public function getIcon(): string
    {
        return 'plugins/HelloWorldBundle/Assets/img/helloworld.png';
    }
}

Plugin Schema

The integration framework provides a means for plugins to better manage their schema. Queries are in migration files that match the plugin’s versions number in it’s config. When the a plugin is installed or upgraded, it will loop over the migration files up to the latest version.

AbstractPluginBundle

The plugin’s root bundle class should extend MauticPlugin\IntegrationsBundle\Bundle\AbstractPluginBundle.

<?php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle;

use MauticPlugin\IntegrationsBundle\Bundle\AbstractPluginBundle;

class HelloWorldBundle extends AbstractPluginBundle
{
}

Plugin Migrations

Each migration file should be stored in the plugin’s Migration folder with a name that matches Version_X_Y_Z.php where X_Y_Z matches the semantic versioning of the plugin. Each file should contain the incremental schema changes for the plugin up to the latest version which should match the version in the plugin’s Config/config.php file.

There are two methods. isApplicable should return true/false if the migration should be ran. up should register the SQL to execute.

<?php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Mautic\IntegrationsBundle\Migration\AbstractMigration;

class Version_1_0_1 extends AbstractMigration
{
    private $table = 'hello_world';

    protected function isApplicable(Schema $schema): bool
    {
        try {
            return !$schema->getTable($this->concatPrefix($this->table))->hasColumn('is_enabled');
        } catch (SchemaException $e) {
            return false;
        }
    }

    protected function up(): void
    {
        $this->addSql("ALTER TABLE `{$this->concatPrefix($this->table)}` ADD `is_enabled` tinyint(1) 0");

        $this->addSql("CREATE INDEX {$this->concatPrefix('is_enabled')} ON {$this->concatPrefix($this->table)}(is_enabled);");
    }
}

Integration Authentication

The integrations bundle provides factories and helpers to create Guzzle Client classes for common authentication protocols.

Registering the Integration for Authentication

If the integration requires the user to authenticate through the web (OAuth2 three legged), the integration needs to tag a service with mautic.auth_integration to handle the authentication process (redirecting to login, request the access token, etc). This service will need to implement \Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface.

<?php
return [
    // ...
    'services' => [
        // ...
        'integrations' => [
            // ...
            'helloworld.integration.authentication' => [
                'class' => \MauticPlugin\HelloWorldBundle\Integration\Support\AuthSupport::class,
                'tags'  => [
                    'mautic.auth_integration',
                ],
            ],
            // ...
        ],
        // ...
    ],
    // ...
];

The AuthSupport class must implement \Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface.

<?php
namespace MauticPlugin\HelloWorldBundle\Integration\Support;

use MauticPlugin\HelloWorldBundle\Connection\Client;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Integration\ConfigurationTrait;
use MauticPlugin\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface;
use Symfony\Component\HttpFoundation\Request;

class AuthSupport implements AuthenticationInterface
{
    use ConfigurationTrait;

    /**
     * @var Client
     */
    private $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    public function getName(): string
    {
        return HelloWorldIntegration::NAME;
    }

    public function getDisplayName(): string
    {
        return 'Hello World';
    }

    /**
     * Returns true if the integration has already been authorized with the 3rd party service.
     *
     * @return bool
     */
    public function isAuthenticated(): bool
    {
        $apiKeys = $this->getIntegrationConfiguration()->getApiKeys();

        return !empty($apiKeys['access_token']) && !empty($apiKeys['refresh_token']);
    }

    /**
     * Authenticate and obtain the access token
     *
     * @param Request $request
     *
     * @return string
     */
    public function authenticateIntegration(Request $request): string
    {
        $code = $request->query->get('code');

        $this->client->authenticate($code);

        return 'Success!';
    }
}

Authentication Providers

The integrations bundle comes with a number of popular authentication protocols available to use as Guzzle clients. New ones can be created by implementing \Mautic\IntegrationsBundle\Auth\Provider\AuthProviderInterface.

The examples below use anonymous classes. Of course, use OOP with services and factories to generate credential, configuration, and client classes. The best way to get configuration values such as username, password, consumer key, consumer secret, etc is by using the mautic.integrations.helper (\Mautic\IntegrationsBundle\Helper\IntegrationsHelper) service in order to leverage the configuration stored in the Integration entity’s api keys.

<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

$configuration = $integration->getIntegrationConfiguration();
$apiKeys       = $configuration->getApiKeys();

$username = $apiKeys['username'] ?? null;
$password = $apiKeys['password'] ?? null;

//...
Api Key

Use the mautic.integrations.auth_provider.api_key service (\Mautic\IntegrationsBundle\Auth\Provider\ApiKey\HttpFactory) to obtain a GuzzleHttp\ClientInterface that uses an API key for all requests. Out of the box, the factory supports a parameter API key or a header API key.

Parameter Based API Key

To use the parameter based API key, create a credentials class that implements \Mautic\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\ParameterCredentialsInterface.

<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\ParameterCredentialsInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\ApiKey\HttpFactory;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

$apiKeys = $integration->getIntegrationConfiguration()->getApiKeys();

$credentials = new class($apiKeys['api_key']) implements ParameterCredentialsInterface {
    private $key;

    public function __construct(string $key)
    {
        $this->key = $key;
    }

    public function getKeyName(): string
    {
        return 'apikey';
    }

    public function getApiKey(): string
    {
        return $this->key;
    }
};

/** @var $factory HttpFactory */
$client   = $factory->getClient($credentials);
$response = $client->get('https://api-url.com/fetch');
Header Based API Key
<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\HeaderCredentialsInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\ApiKey\HttpFactory;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

$apiKeys = $integration->getIntegrationConfiguration()->getApiKeys();

$credentials = new class($apiKeys['api_key']) implements HeaderCredentialsInterface {
    private $key;

    public function __construct(string $key)
    {
        $this->key = $key;
    }

    public function getKeyName(): string
    {
        return 'X-API-KEY';
    }

    public function getApiKey(): string
    {
        return $this->key;
    }
};

/** @var $factory HttpFactory */
$client   = $factory->getClient($credentials);
$response = $client->get('https://api-url.com/fetch');
Basic Auth

Use the mautic.integrations.auth_provider.basic_auth service (\Mautic\IntegrationsBundle\Auth\Provider\BasicAuth\HttpFactory) to obtain a GuzzleHttp\ClientInterface that uses basic auth for all requests.

<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;
use MauticPlugin\IntegrationsBundle\Auth\Provider\BasicAuth\HttpFactory;
use MauticPlugin\IntegrationsBundle\Auth\Provider\BasicAuth\CredentialsInterface;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

$configuration = $integration->getIntegrationConfiguration();
$apiKeys       = $configuration->getApiKeys();

$credentials = new class($apiKeys['username'], $apiKeys['password']) implements CredentialsInterface {
    private $username;
    private $password;

    public function __construct(string $username, string $password)
    {
        $this->username = $username;
        $this->password = $password;
    }

    public function getUsername(): string
    {
        return $this->username;
    }

    public function getPassword(): string
    {
        return $this->password;
    }
};

/** @var $factory HttpFactory */
$client   = $factory->getClient($credentials);
$response = $client->get('https://api-url.com/fetch');
OAuth1a
OAuth1a Three Legged

This has not been implemented yet.

OAuth1a Two Legged

OAuth1A two legged does not require a user to login as would three legged.

<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;
use MauticPlugin\IntegrationsBundle\Auth\Provider\OAuth1aTwoLegged\HttpFactory;
use MauticPlugin\IntegrationsBundle\Auth\Provider\OAuth1aTwoLegged\CredentialsInterface;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

$configuration = $integration->getIntegrationConfiguration();
$apiKeys       = $configuration->getApiKeys();

$credentials = new class(
    'https://api-url.com/oauth/token', 
    $apiKeys['consumer_key'], 
    $apiKeys['consumer_secret']
) implements CredentialsInterface {
    private $authUrl;
    private $consumerKey;
    private $consumerSecret;

    public function __construct(string $authUrl, string $consumerKey, string $consumerSecret)
    {
        $this->authUrl        = $authUrl;
        $this->consumerKey    = $consumerKey;
        $this->consumerSecret = $consumerSecret;
    }

    public function getAuthUrl(): string
    {
        return $this->authUrl;
    }

    public function getConsumerKey(): ?string
    {
        return $this->consumerKey;
    }

    public function getConsumerSecret(): ?string
    {
        return $this->consumerSecret;
    }

    /**
     * Not used in this example. Tsk tsk for breaking the interface segregation principle
     *
     * @return string|null
     */
    public function getToken(): ?string
    {
        return null;
    }

    /**
     * Not used in this example. Tsk tsk for breaking the interface segregation principle
     *
     * @return string|null
     */
    public function getTokenSecret(): ?string
    {
        return null;
    }
};

/** @var $factory HttpFactory */
$client   = $factory->getClient($credentials);
$response = $client->get('https://api-url.com/fetch');
OAuth2

Use the OAuth2 factory according to the grant type required. \Mautic\IntegrationsBundle\Auth\Provider\Oauth2ThreeLegged\HttpFactory supports code and refresh_token grant types. \Mautic\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\HttpFactory supports client_credentials and password.

The OAuth2 factories leverages [https://github.com/kamermans/guzzle-oauth2-subscriber] as a middleware.

Client Configuration

Both OAuth2 factories leverage \Mautic\IntegrationsBundle\Auth\Provider\AuthConfigInterface object to configure things such as configuring the signer (basic auth, post form data, custom), token factory, token persistence, and token signer (bearer auth, basic auth, query string, custom). Use the appropriate interfaces as required for the use case (see the interfaces in plugins/IntegrationsBundle/Auth/Support/Oauth2/ConfigAccess).

See [https://github.com/kamermans/guzzle-oauth2-subscriber] for additional details on configuring the credentials and token signers or creating custom token persistence and factories.

Integration Token Persistence

For most use cases, a token persistence service to fetch and store the access tokens generated by using refresh tokens, etc will be required. The integrations bundle provides one that natively uses the \Mautic\PluginBundle\Entity\Integration entity’s api keys. Anything stored through the service is automatically encrypted.

Use the mautic.integrations.auth_provider.token_persistence_factory service (\Mautic\IntegrationsBundle\Auth\Support\Oauth2\Token\TokenPersistenceFactory) to generate a TokenFactoryInterface to be returned by the \Mautic\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenFactoryInterface interface.

<?php
use kamermans\OAuth2\Persistence\TokenPersistenceInterface;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenPersistenceInterface;
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\Token\TokenPersistenceFactory;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

/** @var $tokenPersistenceFactory TokenPersistenceFactory */
$tokenPersistence = $tokenPersistenceFactory->create($integration);

$config = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
    private $tokenPersistence;

    public function __construct(TokenPersistenceInterface$tokenPersistence)
    {
        $this->tokenPersistence = $tokenPersistence;
    }

    public function getTokenPersistence(): TokenPersistenceInterface
    {
        return $this->tokenPersistence;
    }
};

The token persistence service will automatically manage access_token, refresh_token, and expires_at from the authentication process which are stored in the Integration entity’s api keys array.

Token Factory

In some cases, the 3rd party service may return additional values that are not traditionally part of the oauth2 spec and these values are required for further communication with the api service. In this case, the integration bundle’s \Mautic\IntegrationsBundle\Auth\Support\Oauth2\Token\IntegrationTokenFactory can be used to capture those extra values and store them in the Integration entity’s api keys array.

The IntegrationTokenFactory can then be returned in a \Mautic\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenFactoryInterface when configuring the Client.

<?php
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenFactoryInterface;
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\Token\IntegrationTokenFactory;
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\Token\TokenFactoryInterface;

$tokenFactory = new IntegrationTokenFactory(['something_extra']);

$config = new class($tokenFactory) implements ConfigTokenFactoryInterface {
    private $tokenFactory;

    public function __construct(TokenFactoryInterface $tokenFactory)
    {
        $this->tokenFactory = $tokenFactory;
    }

    public function getTokenFactory(): TokenFactoryInterface
    {
        return $this->tokenFactory;
    }
};
OAuth2 Two Legged
Password Grant

Below is an example of the password grant for a service that uses a scope (optional interface). The use of the token persistence is assuming the access token is valid for a period of time (i.e. an hour).

<?php
use kamermans\OAuth2\Persistence\TokenPersistenceInterface;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\PasswordCredentialsGrantInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\ScopeInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\HttpFactory;
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenPersistenceInterface;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

$configuration = $integration->getIntegrationConfiguration();
$apiKeys       = $configuration->getApiKeys();

$credentials = new class(
    'https://api-url.com/oauth/token',
    'scope1,scope2',
    $apiKeys['client_id'],
    $apiKeys['client_secret'],
    $apiKeys['username'],
    $apiKeys['password']
) implements PasswordCredentialsGrantInterface, ScopeInterface {
    private $authorizeUrl;
    private $scope;
    private $clientId;
    private $clientSecret;
    private $username;
    private $password;

    public function getAuthorizationUrl(): string
    {
        return $this->authorizeUrl;
    }

    public function getClientId(): ?string
    {
        return $this->clientId;
    }

    public function getClientSecret(): ?string
    {
        return $this->clientSecret;
    }

    public function getPassword(): ?string
    {
        return $this->password;
    }

    public function getUsername(): ?string
    {
        return $this->username;
    }

    public function getScope(): ?string
    {
        return $this->scope;
    }
};

/** @var $tokenPersistenceFactory TokenPersistenceFactory */
$tokenPersistence = $tokenPersistenceFactory->create($integration);
$config           = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
    private $tokenPersistence;

    public function __construct(TokenPersistenceInterface$tokenPersistence)
    {
        $this->tokenPersistence = $tokenPersistence;
    }

    public function getTokenPersistence(): TokenPersistenceInterface
    {
        return $this->tokenPersistence;
    }
};

/** @var $factory HttpFactory */
$client   = $factory->getClient($credentials, $config);
$response = $client->get('https://api-url.com/fetch');
Client Credentials Grant

Below is an example of the client credentials grant for a service that uses a scope (optional interface). The use of the token persistence is assuming the access token is valid for a period of time (i.e. an hour).

<?php
use kamermans\OAuth2\Persistence\TokenPersistenceInterface;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\ClientCredentialsGrantInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\ScopeInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\HttpFactory;
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenPersistenceInterface;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

$configuration = $integration->getIntegrationConfiguration();
$apiKeys       = $configuration->getApiKeys();

$credentials = new class(
    'https://api-url.com/oauth/token',
    'scope1,scope2',
    $apiKeys['client_id'],
    $apiKeys['client_secret']
) implements ClientCredentialsGrantInterface, ScopeInterface {
    private $authorizeUrl;
    private $scope;
    private $clientId;
    private $clientSecret;

    public function getAuthorizationUrl(): string
    {
        return $this->authorizeUrl;
    }

    public function getClientId(): ?string
    {
        return $this->clientId;
    }

    public function getClientSecret(): ?string
    {
        return $this->clientSecret;
    }

    public function getScope(): ?string
    {
        return $this->scope;
    }
};

/** @var $tokenPersistenceFactory TokenPersistenceFactory */
$tokenPersistence = $tokenPersistenceFactory->create($integration);
$config           = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
    private $tokenPersistence;

    public function __construct(TokenPersistenceInterface$tokenPersistence)
    {
        $this->tokenPersistence = $tokenPersistence;
    }

    public function getTokenPersistence(): TokenPersistenceInterface
    {
        return $this->tokenPersistence;
    }
};

/** @var $factory HttpFactory */
$client   = $factory->getClient($credentials, $config);
$response = $client->get('https://api-url.com/fetch');
OAuth2 Three Legged

Three legged OAuth2 with the code grant is the most complex to implement because it involves redirecting the user to the 3rd party service to authenticate then sent back to Mautic to initiate the access token process using a code returned in the request.

The first step is to register the integration as a \Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface. The authenticateIntegration() method will be used to initiate the access token process using the code returned in the request after the user logs into the 3rd party service. The integrations bundle provides a route that can be used as the redirect or callback URIs through the named route mautic_integration_public_callback that requires a integration parameter. This redirect URI can be displayed in the UI by using ConfigFormCallbackInterface. This route will find the integration by name from the AuthIntegrationsHelper then execute its authenticateIntegration().

<?php
namespace MauticPlugin\HelloWorldBundle\Integration\Support;

use GuzzleHttp\ClientInterface;
use MauticPlugin\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class AuthSupport implements AuthenticationInterface {
    /**
     * @var ClientInterface
     */
    private $client;

    // ...

    public function authenticateIntegration(Request $request): Response
    {
        $code = $request->query->get('code');

        $this->client->authenticate($code);

        return new Response('OK!');
    }
}

The trick here is that the Client’s authenticate method will configure a ClientInterface then make a call to any valid API url (this is required). By making a call, the middleware will initiate the access token process and store it in the Integration entity’s api keys through the TokenPersistenceFactory. The URL is recommended to be something simple like a version check or fetching info for the authenticated user.

Here is an example of a client, assuming that the user has already logged in and the code is in the request.

<?php
use kamermans\OAuth2\Persistence\TokenPersistenceInterface;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2ThreeLegged\Credentials\CodeInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2ThreeLegged\Credentials\CredentialsInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2ThreeLegged\Credentials\RedirectUriInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\ScopeInterface;
use MauticPlugin\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\HttpFactory;
use MauticPlugin\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenPersistenceInterface;
use MauticPlugin\IntegrationsBundle\Helper\IntegrationsHelper;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Router;

/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);

/** @var Router $router */
$redirectUrl = $router->generate('mautic_integration_public_callback', ['integration' => HelloWorldIntegration::NAME]);

$configuration = $integration->getIntegrationConfiguration();
$apiKeys       = $configuration->getApiKeys();

/** @var Request $request */
$code = $request->get('code');

$credentials = new class(
    'https://api-url.com/oauth/authorize',
    'https://api-url.com/oauth/token',
    $redirectUrl,
    'scope1,scope2',
    $apiKeys['client_id'],
    $apiKeys['client_secret'],
    $code
) implements CredentialsInterface, RedirectUriInterface, ScopeInterface, CodeInterface {
    private $authorizeUrl;
    private $tokenUrl;
    private $redirectUrl;
    private $scope;
    private $clientId;
    private $clientSecret;
    private $code;

    public function __construct(string $authorizeUrl, string $tokenUrl, string $redirectUrl, string $scope, string $clientId, string $clientSecret, ?string $code)
    {
        $this->authorizeUrl = $authorizeUrl;
        $this->tokenUrl     = $tokenUrl;
        $this->redirectUrl  = $redirectUrl;
        $this->scope        = $scope;
        $this->clientId     = $clientId;
        $this->clientSecret = $clientSecret;
        $this->code         = $code;
    }

    public function getAuthorizationUrl(): string
    {
        return $this->authorizeUrl;
    }

    public function getTokenUrl(): string
    {
        return $this->tokenUrl;
    }

    public function getRedirectUri(): string
    {
        return $this->redirectUrl;
    }

    public function getClientId(): ?string
    {
        return $this->clientId;
    }

    public function getClientSecret(): ?string
    {
        return $this->clientSecret;
    }

    public function getScope(): ?string
    {
        return $this->scope;
    }

    public function getCode(): ?string
    {
        return $this->code;
    }
};

/** @var $tokenPersistenceFactory TokenPersistenceFactory */
$tokenPersistence = $tokenPersistenceFactory->create($integration);
$config           = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
    private $tokenPersistence;

    public function __construct(TokenPersistenceInterface$tokenPersistence)
    {
        $this->tokenPersistence = $tokenPersistence;
    }

    public function getTokenPersistence(): TokenPersistenceInterface
    {
        return $this->tokenPersistence;
    }
};

/** @var $factory HttpFactory */
$client   = $factory->getClient($credentials, $config);
$response = $client->get('https://api-url.com/fetch');

Integration Configuration

The integration plugin provides interfaces to display and store configuration options that can be accessed through the \Mautic\PluginBundle\Entity\Integration object.

Registering the Integration for Configuration

To tell the IntegrationsBundle that this integration has configuration options, tag the integration or support class with mautic.config_integration in the plugin’s app/config.php.

<?php
return [
    // ...
    'services' => [
        // ...
        'integrations' => [
            // ...
            'helloworld.integration.configuration' => [
                'class' => \MauticPlugin\HelloWorldBundle\Integration\Support\ConfigSupport::class,
                'tags'  => [
                    'mautic.config_integration',
                ],
            ],
            // ...
        ],
        // ...
    ],
    // ...
];

The ConfigSupport class must implement \Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormInterface.

<?php
namespace MauticPlugin\HelloBundle\Integration\Support;

use MauticPlugin\HelloWorldBundle\Form\Type\ConfigAuthType;
use MauticPlugin\IntegrationsBundle\Integration\DefaultConfigFormTrait;
use MauticPlugin\IntegrationsBundle\Integration\Interfaces\ConfigFormInterface;
use MauticPlugin\IntegrationsBundle\Integration\Interfaces\ConfigFormAuthInterface;

class ConfigSupport implements ConfigFormInterface, ConfigFormAuthInterface
{
    use DefaultConfigFormTrait;

    public function getDisplayName(): string
    {
        return 'Hello World';
    }

    /**
     * Return a custom Symfony form field type class that will be used on the Enabled/Auth tab.
     * This should include things like API credentials, URLs, etc. All values from this form fields
     * will be encrypted before being persisted.
     *
     * @link https://symfony.com/doc/2.8/form/create_custom_field_type.html#defining-the-field-type
     *
     * @return string
     */
    public function getAuthConfigFormName(): string
    {
        return ConfigAuthType::class;
    }
}

Interfaces

There are multiple interfaces that can be used to add form fields options to the provided configuration tabs.

Enabled/Auth Tab

These interfaces provide the configuration options for authenticating with the 3rd party service. Read more about how to use integrations bundle’s auth providers here.

\Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormAuthInterface

Used in the example above. This interface provides the Symfony form type class that defines the fields to be stored as the api keys.

<?php
$apiKeys  = $integrationHelper->get(HelloWorldIntegration::NAME)->getIntegrationConfiguration()->getApiKeys();
$username = $apiKeys['username'];
\Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormCallbackInterface

If the integration leverages an auth provider that requires a callback URL or something similar, this interface provides a means to return a translation string to display in the UI. For example, OAuth2 requires a redirect URI. If the admin has to configure the OAuth credentials in the 3rd party service and needs to know what URL to use in Mautic as the return URI, or callback URL, use the getCallbackHelpMessageTranslationKey() method.

Feature Interfaces

\Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormFeatureSettingsInterface

This interface provides the Symfony form type class that defines the fields to be displayed on the Features tab. These values are not encrypted.

<?php
$featureSettings  = $integrationHelper->get(HelloWorldIntegration::NAME)->getIntegrationConfiguration()->getFeatureSettings();
$doSomething      = $featureSettings['doSomething'];

\Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormFeaturesInterface

Currently the integrations bundle provides default features. To use these features, implement this interface. getSupportedFeatures will return an array of supported features. For example, if the integration syncs with Mautic contacts, getSupportedFeatures() could return [ConfigFormFeaturesInterface::FEATURE_SYNC];.

Contact/Company Syncing Interfaces

The integrations bundle provides a sync framework for 3rd party services to sync with Mautic’s contacts and companies. The \Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormSyncInterface determines the configuration options for this sync feature. Refer to the method docblocks in the interface for more details.

Read more about how to leverage the sync framework.

Integration Sync Engine

The sync engine supports bidirectional syncing between Mautic’s contact and companies with 3rd party objects. The engine generates a “sync report” from Mautic that it converts to a “sync order” for the integration to process. It then asks for a “sync report” from the integration which it converts to a “sync order” for Mautic to process.

When building the report, Mautic or the integration will fetch the objects that have been modified or created within the specified timeframe. If the integration supports changes at the field level, it should tell the report on a per field basis when the field was last updated. Otherwise, it should tell the report when the object itself was last modified. These dates are used by the “sync judge” to determine which value should be used in a bi-directional sync.

The sync is initiated using the mautic:integrations:sync command. For example: php app/console mautic:integrations:sync HelloWorld --start-datetime="2020-01-01 00:00:00" --end-datetime="2020-01-02 00:00:00". (Use php bin/console for Mautic 3).

Registering the Integration for the Sync Engine

To tell the IntegrationsBundle that this integration provides a syncing feature, tag the integration or support class with mautic.sync_integration in the plugin’s app/config.php.

<?php
return [
    // ...
    'services' => [
        // ...
        'integrations' => [
            // ...
            'helloworld.integration.sync' => [
                'class' => \MauticPlugin\HelloWorldBundle\Integration\Support\SyncSupport::class,
                'tags'  => [
                    'mautic.sync_integration',
                ],
            ],
            // ...
        ],
        // ...
    ],
    // ...
];

The SyncSupport class must implement \Mautic\IntegrationsBundle\Integration\Interfaces\SyncInterface.

Syncing

The Mapping Manual

The mapping manual tells the sync engine which integration should be synced with which Mautic object (contact or company), the integration fields that were mapped to Mautic fields, and the direction the data is supposed to flow.

See https://github.com/mautic/plugin-helloworld/blob/mautic-2/Sync/Mapping/Manual/MappingManualFactory.php

The Sync Data Exchange

This is where the sync takes place and is executed by the mautic:integrations:sync command. Mautic and the integration will build their respective reports of new or modified objects then execute the order from the other side.

See https://github.com/mautic/plugin-helloworld/blob/mautic-2/Sync/DataExchange/SyncDataExchange.php

Building Sync Report

The sync report tells the sync engine what objects are new and/or modified between the two timestamps given by the engine (or up to the integration discretion if it is a first time sync). Objects should be processed in batches which can be done using the RequestDAO::getSyncIteration(). The sync engine will execute SyncDataExchangeInterface::getSyncReport() until a report comes back with no objects.

If the integration supports field level change tracking, it should tell the report so that the sync engine can merge the two data sets more accurately.

See https://github.com/mautic/plugin-helloworld/blob/mautic-2/Sync/DataExchange/ReportBuilder.php

Executing the Sync Order

The sync order contains all the changes the sync engine has determined should be written to the integration. The integration should communicate back the ID of any objects created or adjust objects as needed such as if they were converted from one to another or deleted.

See https://github.com/mautic/plugin-helloworld/blob/mautic-2/Sync/DataExchange/OrderExecutioner.php

Integration Builders

Builders can register itself as a “builder” for email and/or landing pages.

Registering the Integration as a Builder

To tell the IntegrationsBundle that this integration has configuration options, tag the integration or support class with mautic.config_integration in the plugin’s app/config.php.

<?php
return [
    // ...
    'services' => [
        // ...
        'integrations' => [
            // ...
            'helloworld.integration.builder' => [
                'class' => \MauticPlugin\HelloWorldBundle\Integration\Support\BuilderSupport::class,
                'tags'  => [
                    'mautic.builder_integration',
                ],
            ],
            // ...
        ],
        // ...
    ],
    // ...
];

The BuilderSupport class must implement \Mautic\IntegrationsBundle\Integration\Interfaces\BuilderInterface.

The only method currently defined for the interface is isSupported which should return a boolean if it supports the given feature. Currently, Mautic supports email and page (landing pages). This will determine what themes should be displayed as an option for the given builder/feature.

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, $uniqueLeadFields);
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->addDecision($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),
            FormEvents::ON_FORM_VALIDATE  => ['onFormValidate', 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
                'formType' => \MauticPlugin\HelloWorldBundle\Form\Type\HelloWorldType::class // Optional - otherwise just default required option should be generated to validation tab 

            ]
        );

        // 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'
            ]
        );
    }


    /**
     * @param Events\ValidationEvent $event
     */
    public function onFormValidate(Events\ValidationEvent $event)
    {
        $field = $event->getField();
        if ($field->getType() === 'helloworld.customfield' && !empty($field->getValidation()['c_enable'])) {
            if (empty($field->getValidation()['helloworld_customfield_enable_validationmsg'])) {
                $event->failedValidation($field->getValidation()['helloworld_customfield_enable_validationmsg']);
            } else {
                $event->failedValidation('plugin.helloworld.formfield.customfield.invalid');
            }
        }
    }
}

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.
formType optional string Form type class to generate additional fields to validator tab

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":"[email protected]"},"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 Symfony\Component\EventDispatcher\Event;
use MauticPlugin\HelloWorldBundle\Entity\World;

class ArmageddonEvent extends Event
{
    /** @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)
);

Marketplace

Mautic 4 comes with a Marketplace directly in the Mautic administration user interface and command line interface as well.

Marketplace under the hood

The Marketplace use Packagist and Composer v2 under the hood. Packagist API is used to list the Mautic plugins and find the plugin details. Composer v2 is used to install and update the plugins. Composer will take care of the dependencies of your plugin and also compatibility with different Mautic, PHP and other dependecies versions.

How to list a plugin in the Mautic Marketplace

Please check the resources on the WIP new Developer Documentation

How to get included in the allow-list

Please check the resources on the WIP new Developer Documentation

Best Practices

Please check the best practices on the WIP new Developer Documentation

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="1".

Example:

<div data-section-wrapper="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.

By default, the refresh token is valid for 14 days. - If your application requests a new access token using the refresh token within 14 days, no user interaction is needed. Your application gets both a new access token and a new refresh token (which is valid for another 14 days after it’s issued); - If your application does not request a new token using the refresh token within 14 days, user interaction is required in order to get new tokens.

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"
    }

Client Credentials

The Client Credentials grant is used when applications request an access token to access their own resources, not on behalf of a user.

To obtain a new access token, a POST should be made to the access token’s endpoint oauth/v2/token using the client_credentials grant type.

POST /oauth/v2/token
    
client_id=CLIENT_ID
    &client_secret=CLIENT_SECRET
    &grant_type=client_credentials

(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: ""
    }

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==

API Rate limiter

You can configure rate limiter cache in local.php By default, filesystem is used as: php api_rate_limiter_cache => [ 'type' => 'file_system', ],

You can configure memcached server: php 'api_rate_limiter_cache' => [ 'memcached' => [ 'servers' => [ [ 'host' => 'localhost', 'port' => 11211 ] ] ] ],

Or whatever cache you want described in Symfony cache documentation.

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": {
            "createdByUser": "John Doe",
            "modifiedByUser": "John Doe",
            "id": 19,
            "title": "test",
            "alias": "test",
            "description": null,
            "color": null,
            "bundle": "asset"
        },
        "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 object/null Object with the category details
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": null,
            "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": {
        "3": {
            "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.
published 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.

Clone A Campaign

<?php

$camnpaignId = 12;

$campaign = $campaignApi->cloneCampaign($campaignId);

Clone an existing campaign. To see more advanced example with campaign events and so on, see the unit tests.

HTTP Request

POST /campaigns/clone/CAMPAIGN_ID

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($campaignId, $contactId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually add a contact to a specific campaign.

HTTP Request

POST /campaigns/CAMPAIGN_ID/contact/CONTACT_ID/add

Response

Expected Response Code: 200

See JSON code example.

Remove Contact from a Campaign

<?php

//...
$response = $listApi->removeContact($campaignId, $contactId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually remove a contact from a specific campaign.

HTTP Request

POST /campaigns/CAMPAIGN_ID/contact/CONTACT_ID/remove

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' => '[email protected]',
    '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.

Assign a Category

To assign a category to an entity simply set category = [ID] to the payload. For example this is how a category 123 can be asssigned to a new Asset:

$data = array(
    'title' => 'PDF sent as a API request',
    'storageLocation' => 'remote',
    'file' => 'https://www.mautic.org/media/logos/logo/Mautic_Logo_DB.pdf'
    'category' => 123
);

$asset = $assetApi->create($data);

The category must exist in the Mautic instance and the entity must support categories,

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":"[email protected]",
                "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": {
    "176": {  
      "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":"[email protected]",
            "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

** 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

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' => '[email protected]',
    'companycity' => 'Raleigh',
    'overwriteWithBlank' => true
);

$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
overwriteWithBlank If true, then empty values are set to fields. Otherwise empty values are skipped

Response

Expected Response Code: 201

Properties

Same as Get Company.

Edit Company

<?php

$id   = 1;
$data = array(
    'companyname' => 'test',
    'companyemail' => '[email protected]',
    '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
overwriteWithBlank If true, then empty values are set to fields. Otherwise empty values are skipped

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($companyId, $contactId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually add a contact to a specific company.

HTTP Request

POST /companies/COMPANY_ID/contact/CONTACT_ID/add

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/CONTACT_ID/remove

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
isPublished Boolean True if the contact is published
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.
tags array Array of tags associated with this contact. See JSON code example for format.
utmtags array Array of UTM Tags associated with this contact. See JSON code example for format.
doNotContact array Array of Do Not Contact objects. See JSON code example for format.

List Contacts

<?php

//...
$contacts = $contactApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
    "total": "1",
    "contacts": {
        "47": {
            "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",

                    "...": "..."
                }
            },
            "tags": [{
              "tag": "aTag"
            },
            {
              "tag": "bTag"
            }],
            "utmtags" : [{
              "id": 1,
              "query": {
                "page": "asd",
                "cid": "fb1"
              },
              "referer": "https://example.com/",
              "remoteHost": "example.com",
              "userAgent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0",
              "utmCampaign": "abcampaign",
              "utmContent": "page",
              "utmMedium": "social",
              "utmSource": "fb",
              "utmTerm": "test1"
            }],
            "doNotContact": [{
                "id": 2,
                "reason": 2,
                "comments": "",
                "channel": "email",
                "channelId": null
            }]
        }
    }
}

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. However, all properties in the response that are written in camelCase need to be changed a bit. Before every capital add an underscore _ and then change the capital letters to non-capital letters. So dateIdentified becomes date_identified, modifiedByUser becomes modified_by_user etc.
orderByDir Sort direction: asc or desc.
publishedOnly Only return currently published entities.
minimal Return only array of entities without additional lists in it.
where An array of advanced where conditions
order An array of advanced order statements

Advanced filtering

In some cases you may want to filter by specific value(s). Use URL params like this:

In PHP: php $where = [ [ 'col' => 'phone', 'expr' => 'in', 'val' => '444444444,888888888', ] ]; This design allows to add multiple conditions in the same request.

If you are not using PHP, here is URL-encoded example of the above where array: GET https://[your_mauitc_domain]/api/contacts?where%5B0%5D%5Bcol%5D=phone&where%5B0%5D%5Bexpr%5D=in&where%5B0%5D%5Bval%5D=444444444,888888888

List of available expressions

Response

Expected Response Code: 200

See JSON code example.

** Properties **

Same as Get Contact.

Create Contact

<?php

$data = array(
    'firstname' => 'Jim',
    'lastname'  => 'Contact',
    'email'     => '[email protected]',
    'ipAddress' => $_SERVER['REMOTE_ADDR'],
    'overwriteWithBlank' => true,
);

$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
overwriteWithBlank If true, then empty values are set to fields. Otherwise empty values are skipped

Response

Expected Response Code: 201

** Properties **

Same as Get Contact.

Create Batch Contact

<?php

$data = array(
    array(
    'firstname' => 'Jim',
    'lastname'  => 'Contact',
    'email'     => '[email protected]',
    'ipAddress' => $_SERVER['REMOTE_ADDR']
    ),
    array(
        'firstname' => 'John',
    'lastname'  => 'Doe',
    'email'     => '[email protected]',
    'ipAddress' => $_SERVER['REMOTE_ADDR']
    )
);
$contact = $contactApi->createBatch($data);

Create a batch of new contacts.

HTTP Request

POST /contacts/batch/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 **

Array of contacts. Record is the same as Get Contact.

Edit Contact

<?php

$id   = 1;
$data = array(
    'email'     => '[email protected]',
    '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
overwriteWithBlank If true, then empty values are set to fields. Otherwise empty values are skipped

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.

Note: In order to remove tag from contact add minus - before it. For example: tags: ['one', '-two'] - sending this in request body will add tag one and remove tag two from contact.

Edit Batch Contact

<?php

$data = array(
    array(
        'id'        => 1,
        'firstname' => 'Jim',
        'lastname'  => 'Contact',
        'email'     => '[email protected]',
        'ipAddress' => $_SERVER['REMOTE_ADDR']
    ),
    array(
        'id'        => 1,
        'firstname' => 'John',
        'lastname'  => 'Doe',
        'email'     => '[email protected]',
        'ipAddress' => $_SERVER['REMOTE_ADDR']
    )
);

$contact = $contactApi->editBatch($data);

Edit several contacts in one request. 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/batch/edit

To edit a contact and create a new one if the contact is not found:

PUT /contacts/batch/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
overwriteWithBlank If true, then empty values are set to fields. Otherwise empty values are skipped

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

Contacts array. Record same as Get Contact.

Note: In order to remove tag from contact add minus - before it. For example: tags: ['one', '-two'] - sending this in request body will add tag one and remove tag two from 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.

Delete Batch Contact

<?php
$data = array(1, 2);
$contact = $contactApi->deleteBatch($data);

Delete contacts.

HTTP Request

DELETE /contacts/batch/delete

If you are not using PHP, here is a URL example:

DELETE https://[your_mautic_domain]/api/contacts/batch/delete?ids=1,2

Response

Expected Response Code: 200

Properties

Contacts array. Record same as Get Contact.

Add Do Not Contact

<?php

$contactApi->addDnc($contactId, $channel, $reason, $channelId, $comments);
{
  "channelId": "26",
  "reason": "Integration issued DNC",
  "comments": "Unsubscribed via API"
}

Add a contact to DNC list

HTTP Request

To add Do Not Contact entry to a contact:

POST /contacts/ID/dnc/CHANNEL/add

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 (1), Contacts::BOUNCED (2), Contacts::MANUAL (3). 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->removeDnc($contactId, $channel);

Remove a contact from DNC list

HTTP Request

To remove Do Not Contact entry from a contact:

POST /contacts/ID/dnc/CHANNEL/remove

Data Parameters (optional)

Name Description
channel Channel of DNC. For example 'email’, 'sms’… Default is email.

Response

Same as Get Contact.

Add UTM Tags

<?php

$data = array(
    'utm_campaign' => 'apicampaign',
    'utm_source'   => 'fb',
    'utm_medium'   => 'social',
    'utm_content'  => 'fbad',
    'utm_term'     => 'mautic api',
    'useragent'    => 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0',
    'url'          => '/product/fbad01/',
    'referer'      => 'https://google.com/q=mautic+api',
    'query'        => ['cid'=>'abc','cond'=>'new'], // or as string with "cid=abc&cond=new"
    'remotehost'   => 'example.com',
    'lastActive'   => '2017-01-17T00:30:08+00:00'
 );
$contactApi->addUtm($contactId, $data);

Add UTM tags to a contact

HTTP Request

To add UTM tag entry to a contact:

POST /contacts/ID/utm/add

UTM Parameters (required)

While the parameter array is required, each utm tag entry is optional.

Name Description
utm_campaign The UTM Campaign parameter
utm_source The UTM Source parameter
utm_medium The UTM Medium parameter
utm_content The UTM Content parameter
utm_term The UTM Term parameter
useragent The browsers UserAgent. If provided a new Device entry will be created if necessary.
url The page url
referer The URL of the referer,
query Any extra query parameters you wish to include. Array or http query string
remotehost The Host name
lastActive The date that the action occured. Contacts lastActive date will be updated if included. Date format required 2017-01-17T00:30:08+00:00.

Response

Same as Get Contact with the added UTM Tags.

Remove UTM Tags from a contact

<?php
$contactApi->removeUtm($contactId, $utmId);

Remove a set of UTM Tags from a contact

HTTP Request

To remove UTM Tags from a contact:

POST /contacts/ID/utm/UTMID/remove

Data Parameters

None required.

Response

Same as Get Contact without the removed UTM Tags.

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);

Warning: Deprecated. Use getActivityForContact instead.

** Query Parameters **

Name Description
id Contact ID
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.
order Array of Column and Direction [COLUMN, DIRECTION].
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

Warning: Deprecated. Use GET /contacts/ID/activity instead.

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 activity events for specific contact

<?php

$events = $contactApi->getActivityForContact($id, $search, $includeEvents, $excludeEvents, $orderBy, $orderByDir, $page, $dateFrom, $dateTo);

** Query Parameters **

Name Description
id Contact ID
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.
filters[dateFrom] Date from filter. Must be type of \DateTime for the PHP API libary and in format Y-m-d H:i:s for HTTP param
filters[dateTo] Date to filter. Must be type of \DateTime for the PHP API libary and in format Y-m-d H:i:s for HTTP param
order Array of Column and Direction [COLUMN, DIRECTION].
page What page number to load
limit Limit of events per page
{
  "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":{
    "asset.download": "Asset downloaded",
    "campaign.event": "Campaign action triggered",
    "campaign.event.scheduled": "Campaign event scheduled",
    "lead.donotcontact": "Do not contact",
    "email.failed": "Email failed",
    "email.read": "Email read",
    "email.sent": "Email sent",
    "form.submitted": "Form submitted",
    "lead.imported": "Imported",
    "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 had created.

HTTP Request

GET /contacts/ID/activity

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 Activity events for all contacts

<?php

$events = $contactApi->getActivity($search, $includeEvents, $excludeEvents, $orderBy, $orderByDir, $page, $dateFrom, $dateTo);

** 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.
filters[dateFrom] Date from filter. Must be type of \DateTime for the PHP API libary and in format Y-m-d H:i:s for HTTP param
filters[dateTo] Date to filter. Must be type of \DateTime for the PHP API libary and in format Y-m-d H:i:s for HTTP param
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": "meeting.attended",
      "eventId": "meeting.attended65",
      "eventLabel": "Attended meeting - Mautic instance",
      "eventType": "Meeting attendance",
      "timestamp": "2017-08-03T21:03:04+00:00",
      "contactId": "12180",
      "details": {
        "eventName": "mautic-instance",
        "eventId": "371343405",
        "eventDesc": "Mautic instance",
        "joinUrl": ""
      }
    },
    {
      "event": "webinar.attended",
      "eventId": "webinar.attended67",
      "eventLabel": "Attended webinar - Mautic",
      "eventType": "Webinar attendance",
      "timestamp": "2017-08-03T21:03:04+00:00",
      "contactId": "12180",
      "details": {
        "eventName": "mautic",
        "eventId": "530287395",
        "eventDesc": "Mautic",
        "joinUrl": ""
      }
    },
    {
      "event": "webinar.registered",
      "eventId": "webinar.registered66",
      "eventLabel": "Registered for webinar - Mautic",
      "eventType": "Webinar registered for",
      "timestamp": "2017-08-03T21:03:04+00:00",
      "contactId": "12180",
      "details": {
        "eventName": "mautic",
        "eventId": "530287395",
        "eventDesc": "Mautic",
        "joinUrl": "https://global.gotowebinar.com/join/xxx/xxx"
      }
    },
    {
      "event": "campaign.event",
      "eventId": "campaign.event892",
      "eventLabel": {
        "label": "Contact field value \/ Campaign Date",
        "href": "\/s\/campaigns\/view\/498"
      },
      "eventType": "Campaign action triggered",
      "timestamp": "2017-08-03T00:58:25+00:00",
      "contactId": "12281",
      "details": {
        "log": {
          "dateTriggered": "2017-08-03T00:58:25+00:00",
          "metadata": [],
          "type": "lead.field_value",
          "isScheduled": "0",
          "logId": "892",
          "eventId": "1457",
          "campaignId": "498",
          "eventName": "Contact field value",
          "campaignName": "Campaign Date"
        }
      }
    },
    {
      "event": "email.sent",
      "eventId": "email.sent796",
      "eventLabel": {
        "label": "2017-05-23 - Email - Leads - Nurture Flow (Monica) 1",
        "href": "http:\/\/mautic.dev\/email\/view\/597a116ae69ca",
        "isExternal": true
      },
      "eventType": "Email sent",
      "timestamp": "2017-07-27T16:14:34+00:00",
      "contactId": "16419",
      "details": {
        "stat": {
          "id": "796",
          "dateSent": "2017-07-27T16:14:34+00:00",
          "subject": "How to make the case for digital",
          "isRead": "0",
          "isFailed": "0",
          "viewedInBrowser": "0",
          "retryCount": "0",
          "idHash": "597a116ae69ca",
          "openDetails": [],
          "storedSubject": "How to make the case for digital",
          "timeToRead": false,
          "emailId": "78",
          "emailName": "2017-05-23 - Email - Leads - Nurture Flow (Monica) 1"
        },
        "type": "sent"
      }
    },
    {
      "event": "email.read",
      "eventId": "email.read769",
      "eventLabel": {
        "label": "Custom Email: device test",
        "href": "http:\/\/mautic.dev\/email\/view\/5966b0cd571f4",
        "isExternal": true
      },
      "eventType": "Email read",
      "timestamp": "2017-07-12T23:30:56+00:00",
      "contactId": "13930",
      "details": {
        "stat": {
          "id": "769",
          "dateRead": "2017-07-12T23:30:56+00:00",
          "dateSent": "2017-07-12T23:29:17+00:00",
          "isRead": "1",
          "isFailed": "0",
          "viewedInBrowser": "0",
          "retryCount": "0",
          "idHash": "5966b0cd571f4",
          "openDetails": [
            {
              "datetime": "2017-07-12 23:30:56",
              "useragent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/59.0.3071.115 Safari\/537.36",
              "inBrowser": false
            },
            {
              "datetime": "2017-07-13 02:18:51",
              "useragent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/59.0.3071.115 Safari\/537.36",
              "inBrowser": false
            }
          ],
          "storedSubject": "device test",
          "timeToRead": "PT1M39S"
        },
        "type": "read"
      }
    },
    {
      "event": "lead.ipadded",
      "eventId": "lead.ipadded3263",
      "eventLabel": "127.0.0.1",
      "eventType": "Accessed from IP",
      "timestamp": "2017-07-27T03:09:09+00:00",
      "contactId": "3263",
      "details": []
    },
    {
      "event": "form.submitted",
      "eventId": "form.submitted503",
      "eventLabel": {
        "label": "3586 Test",
        "href": "\/s\/forms\/view\/143"
      },
      "eventType": "Form submitted",
      "timestamp": "2017-07-27T03:09:07+00:00",
      "contactId": "16417",
      "details": {
        "submission": {
          "id": 503,
          "ipAddress": {
            "ip": "127.0.0.1"
          },
          "form": {
            "id": 143,
            "name": "3586 Test",
            "alias": "3586_test"
          },
          "dateSubmitted": "2017-07-27T03:09:07+00:00",
          "referer": "http:\/\/mautic.dev\/form\/143",
          "results": {
            "form_id": "143",
            "email": "[email protected]",
            "f_name": ""
          }
        },
        "form": {
          "id": 143,
          "name": "3586 Test",
          "alias": "3586_test"
        },
        "page": {}
      }
    },
    {
      "event": "page.hit",
      "eventLabel": {
        "label": "Test",
        "href": "\/s\/pages\/view\/8"
      },
      "eventType": "Page hit",
      "timestamp": "2017-07-21T20:36:49+00:00",
      "contactId": "16380",
      "details": {
        "hit": {
          "userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/59.0.3071.115 Safari\/537.36",
          "dateHit": "2017-07-21T20:36:49+00:00",
          "url": "http:\/\/mautic.dev\/uncategorized\/translation-test1",
          "query": {
            "pageUrl": "http:\/\/mautic.dev\/uncategorized\/translation-test1"
          },
          "clientInfo": "a:6:{s:4:\"type\";s:7:\"browser\";s:4:\"name\";s:6:\"Chrome\";s:10:\"short_name\";s:2:\"CH\";s:7:\"version\";s:4:\"59.0\";s:6:\"engine\";s:5:\"Blink\";s:14:\"engine_version\";s:0:\"\";}",
          "device": "desktop",
          "deviceOsName": "Mac",
          "deviceBrand": "",
          "deviceModel": "",
          "pageId": "8"
        }
      }
    },
    {
      "event": "point.gained",
      "eventLabel": "2: Page Hit Test \/ 20",
      "eventType": "Point gained",
      "timestamp": "2017-07-20T22:38:28+00:00",
      "contactId": "16379",
      "details": {
        "log": {
          "eventName": "2: Page Hit Test",
          "actionName": "hit",
          "dateAdded": "2017-07-20T22:38:28+00:00",
          "type": "url",
          "delta": "20",
          "id": "2"
        }
      }
    },
    {
      "event": "lead.imported",
      "eventId": "lead.imported6324",
      "eventType": "Imported",
      "eventLabel": {
        "label": "Contact import failed from FakeNameGenerator.com_20d05d9c.csv",
        "href": "\/s\/contacts\/import\/view\/4"
      },
      "timestamp": "2017-07-17T21:42:35+00:00",
      "details": {
        "id": "6324",
        "bundle": "lead",
        "object": "import",
        "action": "failed",
        "properties": {
          "line": 2001,
          "file": "FakeNameGenerator.com_20d05d9c.csv",
          "error": "No data found"
        },
        "userId": "2",
        "userName": "Bob Smith",
        "objectId": "4",
        "dateAdded": "2017-07-17T21:42:35+00:00"
      }
    },
    {
      "event": "asset.download",
      "eventId": "asset.download11",
      "eventLabel": {
        "label": "Download Mautic",
        "href": "\/s\/assets\/view\/1"
      },
      "eventType": "Asset downloaded",
      "timestamp": "2017-04-04T01:49:13+00:00",
      "details": {
        "asset": {
          "id": 1,
          "title": "Download Mautic",
          "alias": "download-mautic",
          "description": "test"
        },
        "assetDownloadUrl": "http:\/\/mautic.dev\/asset\/1:download-mautic"
      }
    },
  ],
  "filters": {
    "search": "",
    "includeEvents": [],
    "excludeEvents": []
  },
  "order": [
    "timestamp",
    "DESC"
  ],
  "types": {
    "lead.ipadded": "Accessed from IP",
    "asset.download": "Asset downloaded",
    "meeting.attended": "Attended meeting",
    "webinar.attended": "Attended webinar",
    "campaign.event": "Campaign action triggered",
    "campaign.event.scheduled": "Campaign event scheduled",
    "lead.donotcontact": "Do not contact",
    "email.failed": "Email failed",
    "email.read": "Email read",
    "email.sent": "Email sent",
    "form.submitted": "Form submitted",
    "lead.imported": "Imported",
    "page.hit": "Page hit",
    "point.gained": "Point gained",
    "meeting.registered": "Registered for meeting",
    "webinar.registered": "Registration to Webinar",
    "stage.changed": "Stage changed",
    "lead.utmtagsadded": "UTM tags recorded",
    "page.videohit": "Video view event"
  },
  "total": 12,
  "page": 1,
  "limit": 25,
  "maxPages": 1
}

HTTP Request

GET /contacts/activity

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
contactId ID of the contact who created 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":"[email protected]",
      "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",
            "sent.email.to.contacts":"Sent email to contacts",
            "most.hit.email.redirects":"Most hit email redirects",
            "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 by 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
        }
    }
}

“Emails in time” widget

Filter parameters

filtercompanyId Filter only emails from contacts assigned to provided company.

filter[campaignId (int) Filter only emails from contacts that were sent as part of provided campaign.

filtersegmentId Filter only emails from contacts assigned to provided segment.

Dataset parameter

dataset (array) - sent - opened - unsubscribed - clicked - bounced - failed Provide more datasets in response based on request.

HTTP Request:

GET /api/data/emails.in.time?dateFrom={YYYY-mm-dd}&dateTo={YYYY-mm-dd}&timeUnit={m}&filter[campaignId]={int}&filter[companyId]={int}&filter[segmentId]={int}&withCounts&dataset[]=sent&dataset[]=opened&dataset[]=unsubscribed&dataset[]=clicked

“Sent email to contacts” widget

Filter parameters

filtercompanyId Filter only emails from contacts assigned to provided company.

filter[campaignId (int) Filter only emails from contacts that were sent as part of provided campaign.

filtersegmentId Filter only emails from contacts assigned to provided segment.

HTTP Request:

GET /api/data/sent.email.to.contacts?dateFrom={YYYY-mm-dd}&dateTo={YYYY-mm-dd}&timeUnit={m}&filter[campaignId]={int}&filter[companyId]={int}&filter[segmentId]={int}&limit=10&offset=0

“Most hit email redirects” widgets

Filter parameters

filtercompanyId Filter only emails from contacts assigned to provided company.

filter[campaignId (int) Filter only emails from contacts that were sent as part of provided campaign.

filtersegmentId Filter only emails from contacts assigned to provided segment.

HTTP Request:

GET /api/data/most.hit.email.redirects?dateFrom={YYYY-mm-dd}&dateTo={YYYY-mm-dd}&timeUnit={m}&filter[campaignId]={int}&filter[companyId]={int}&filter[segmentId]={int}&limit=10&offset=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();
$dynamicContentApi = $api->newApi("dynamicContents", $auth, $apiUrl);

Get Dynamic Content

<?php

//...
$dynamicContent = $dynamicContentApi->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 dynamicContent by ID.

HTTP Request

GET /dynamiccontents/ID

Response

Expected Response Code: 200

See JSON code example.

Dynamic Content Properties

Name Type Description
id int ID of the dynamic content
name string Name of the dynamic content
description string/null Description of the dynamic content
isPublished bool Published state
publishUp datetime/null Date/time when the dynamic content should be published
publishDown datetime/null Date/time the dynamic content should be un published
dateAdded datetime Date/time dynamic content was created
createdBy int ID of the user that created the dynamic content
createdByUser string Name of the user that created the dynamic content
dateModified datetime/null Date/time dynamic content was last modified
modifiedBy int ID of the user that last modified the dynamic content
modifiedByUser string Name of the user that last modified the dynamic content
variantChildren array Array of Dynamic Content entities for variants of this landing dynamic content
variantParent object The parent/main dynamic content if this is a variant (A/B test)
sentCount int Count of how many times the dynamic content was sent

List Dynamic Contents

<?php
// ...

$dynamicContents = $dynamicContentApi->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 /dynamiccontents

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 Content.

Create Dynamic Content

<?php

$data = array(
    'name'        => 'Dynamic Content A',
    'isPublished' => 1
);

$dynamicContent = $dynamicContentApi->create($data);

Create a new dynamicContent.

HTTP Request

POST /dynamiccontents/new

Post Parameters

Name Type Description
id int ID of the dynamic content
name string Name of the dynamic content
description string/null Description of the dynamic content
isPublished bool Published state
publishUp datetime/null Date/time when the dynamic content should be published
publishDown datetime/null Date/time the dynamic content should be un published
dateAdded datetime Date/time dynamic content was created
createdBy int ID of the user that created the dynamic content
createdByUser string Name of the user that created the dynamic content
dateModified datetime/null Date/time dynamic content was last modified
modifiedBy int ID of the user that last modified the dynamic content
modifiedByUser string Name of the user that last modified the dynamic content
variantChildren array Array of Dynamic Content entities for variants of this landing dynamic content
variantParent object The parent/main dynamic content 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 Content.

Edit Dynamic Content

<?php

$id   = 1;
$data = array(
    'name'        => 'New dynamicContent name',
    'isPublished' => 0
);

// Create new a dynamicContent of ID 1 is not found?
$createIfNotFound = true;

$dynamicContent = $dynamicContentApi->edit($id, $data, $createIfNotFound);

Edit a new dynamicContent. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a dynamicContent if the given ID does not exist and clears all the dynamic content information, adds the information from the request. PATCH fails if the dynamic content with the given ID does not exist and updates the dynamic content field values with the values form the request.

HTTP Request

To edit a dynamicContent and return a 404 if the dynamic content is not found:

PATCH /dynamiccontents/ID/edit

To edit a dynamicContent and create a new one if the dynamic content is not found:

PUT /dynamiccontents/ID/edit

Post Parameters

Name Type Description
id int ID of the dynamic content
name string Name of the dynamic content
description string/null Description of the dynamic content
isPublished bool Published state
publishUp datetime/null Date/time when the dynamic content should be published
publishDown datetime/null Date/time the dynamic content should be un published
dateAdded datetime Date/time dynamic content was created
createdBy int ID of the user that created the dynamic content
createdByUser string Name of the user that created the dynamic content
dateModified datetime/null Date/time dynamic content was last modified
modifiedBy int ID of the user that last modified the dynamic content
modifiedByUser string Name of the user that last modified the dynamic content
variantChildren array Array of Dynamic Content entities for variants of this landing dynamic content
variantParent object The parent/main dynamic content 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 content was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Dynamic Content.

Delete Dynamic Content

<?php

$dynamicContent = $dynamicContentApi->delete($id);

Delete a dynamicContent.

HTTP Request

DELETE /dynamiccontents/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Dynamic Content.

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
assetAttachments array asset IDs Array for email attachment

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.

Assets can be referenced for attaching documents (either ids of existing assets or ids returned by the Create Asset).

HTTP Request

POST /emails/ID/contact/CONTACT_ID/send

Post Parameters

Name Type Description
tokens array Array of tokens in email
assetAttachments array Array of asset ids

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 }

Create a reply to a send email send row

This endpoint can create a record that a specific email stat row received a reply. It will also mark an email send stat as read.

HTTP Request

POST /emails/reply/TRACKING_HASH

Tracking hash is created as unique hash for each email send stat record.

Response

Expected Response Code: 200

Properties json { "success": 1, }

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);

Multiselect Field “`php <?php

$data = array( ‘label’ => 'API test field’, 'type’ => 'multiselect’, 'isPubliclyUpdatable’ => true, 'properties’ => array( 'list’ => array( array( 'label’ => 'label 1’, 'value’ => 'value 1’ ), array( 'label’ => 'label 2’, 'value’ => 'value 2’ ) ) ) );

$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,
        "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
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,
            "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' => 'A field that will be added',
            'type' => 'text'
        ),
        array(
            'id' => 1,
            'label' => 'A field that will be edited',
            '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.

Make sure that whenever you want to edit a form field that you include the form field id in the request. Fields without an id are assumed to be new fields.

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.

List Form Submissions

<?php

$submissions = $formApi->getSubmissions($formId, $searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
  "total": "1",
  "submissions": [
    {
      "id": 1,
      "ipAddress": {
        "ip": "127.0.0.1"
      },
      "form": {
        "id": 25,
        "name": "test",
        "alias": "test",
        "category": null
      },
      "lead": {
        "id": 2183,
        "points": 0,
        "color": null,
        "title": null,
        "firstname": null,
        "lastname": null,
        "company": null,
        "position": null,
        "email": "[email protected]",
        "phone": null,
        "mobile": null,
        "address1": null,
        "address2": null,
        "city": null,
        "state": null,
        "zipcode": null,
        "timezone": null,
        "country": null
      },
      "trackingId": null,
      "dateSubmitted": "2017-07-17T09:52:29+00:00",
      "referer": "http:\/\/mautic.dev\/s\/forms\/preview\/25",
      "page": null,
      "results": {
        "email": "[email protected]"
      }
    }
  ]
}

HTTP Request

GET /forms/FORM_ID/submissions

Query Parameters

Name Description
formId ID of the form you want to get submissions for
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, also can use column of joined table with prefix. Sort by submitted date is s.date_submitted
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

Name Type Description
id int ID of the submission
ipAddress array Associative array containing IP address of the client who made the submission
form array Simplified associative array of the form containing id, name, alias and category
lead array Associative array of the lead containing the core values as well as custom fields
dateSubmitted string Date time string holding the UTC date and time when the submission was made
referer string HTTP referer info
results array Associative array of the form fields as the keys and submission values

List Form Submissions for a contact

<?php

$submissions = $formApi->getSubmissionsForContact($formId, $contactId, $searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);

HTTP Request

GET /forms/FORM_ID/submissions/contact/CONTACT_ID

Response and properties same as Get Form Submissions. Parameters too except the ContactId was added.

Get Form Submission

<?php

//...
$form = $formApi->getSubmission($formId, $submissionId);
{
  "submission": {
    "id": 1,
    "ipAddress": {
      "ip": "127.0.0.1"
    },
    "form": {
      "id": 25,
      "name": "test",
      "alias": "test",
      "category": null
    },
    "lead": {
      "id": 2183,
      "points": 0,
      "color": null,
      "title": null,
      "firstname": null,
      "lastname": null,
      "company": null,
      "position": null,
      "email": "[email protected]",
      "phone": null,
      "mobile": null,
      "address1": null,
      "address2": null,
      "city": null,
      "state": null,
      "zipcode": null,
      "timezone": null,
      "country": null
    },
    "trackingId": null,
    "dateSubmitted": "2017-07-17T09:52:29+00:00",
    "referer": "http:\/\/mautic.dev\/s\/forms\/preview\/25",
    "page": null,
    "results": {
      "form_id": "25",
      "email": "[email protected]"
    }
  }
}

Get an individual form submission by ID.

HTTP Request

GET /forms/FORM_ID/submissions/SUBMISSION_ID

Response

Expected Response Code: 200

See JSON code example.

Form Properties

Same as Get Form Submissions.

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 

$contactID = 1;

$data = array(
    'lead' => $contactID,
    '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",
        "template": "blank",
        "customHtml": "<!DOCTYPE ...",
        "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
template string Template of the page
customHtml string Static HTML 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",
            "template": "blank",
            "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"
    }
}

Reports

Use this endpoint to obtain details on Mautic’s reports.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth  = new ApiAuth();
$auth      = $initAuth->newAuth($settings);
$apiUrl    = "https://your-mautic.com";
$api       = new MauticApi();
$reportApi = $api->newApi("reports", $auth, $apiUrl);

Get Report

<?php

//...
// Simple get all with default options:
$report = $reportApi->get($id);

// Or define exactly what rows you want:
$limit    = 100;
$page     = 2;
$dateFrom = \DateTime('1 week ago');
$dateTo   = \DateTime('now');
$report   = $reportApi->get($id, $limit, $page, $dateFrom, $dateTo);
{
  "totalResults": 3990,
  "data": [
    {
      "id2": "12",
      "email1": "[email protected]",
      "firstname1": "",
      "lastname1": ""
    },
    {
      "id2": "23",
      "email1": "[email protected]",
      "firstname1": "",
      "lastname1": ""
    },
    {
      "id2": "24",
      "email1": "[email protected]",
      "firstname1": "",
      "lastname1": ""
    },
    {
      "id2": "25",
      "email1": "[email protected]",
      "firstname1": "",
      "lastname1": ""
    },
    {
      "id2": "26",
      "email1": "[email protected]",
      "firstname1": "",
      "lastname1": ""
    }
  ],
  "dataColumns": {
    "address11": "l.address1",
    "address21": "l.address2",
    "attribution1": "l.attribution",
    "attribution_date1": "l.attribution_date",
    "city1": "l.city",
    "company1": "l.company",
    "companyaddress11": "comp.companyaddress1",
    "companyaddress21": "comp.companyaddress2",
    "companycity1": "comp.companycity",
    "companyemail1": "comp.companyemail",
    "companyname1": "comp.companyname",
    "companycountry1": "comp.companycountry",
    "companydescription1": "comp.companydescription",
    "companyfax1": "comp.companyfax",
    "id1": "comp.id",
    "companyphone1": "comp.companyphone",
    "companystate1": "comp.companystate",
    "companywebsite1": "comp.companywebsite",
    "companyzipcode1": "comp.companyzipcode",
    "id2": "l.id",
    "country1": "l.country",
    "custom_select1": "l.custom_select",
    "date_identified1": "l.date_identified",
    "email1": "l.email",
    "facebook1": "l.facebook",
    "fax1": "l.fax",
    "firstname1": "l.firstname",
    "foursquare1": "l.foursquare",
    "gender1": "l.gender",
    "googleplus1": "l.googleplus",
    "ip_address1": "i.ip_address",
    "instagram1": "l.instagram",
    "is_primary1": "companies_lead.is_primary",
    "lastname1": "l.lastname",
    "linkedin1": "l.linkedin",
    "mobile1": "l.mobile",
    "multiline1": "l.multiline",
    "multiselect1": "l.multiselect",
    "owner_id1": "l.owner_id",
    "first_name1": "u.first_name",
    "last_name1": "u.last_name",
    "phone1": "l.phone",
    "points1": "l.points",
    "position1": "l.position",
    "preferred_locale1": "l.preferred_locale",
    "timezone1": "l.timezone",
    "skype1": "l.skype",
    "state1": "l.state",
    "title1": "l.title",
    "twitter1": "l.twitter",
    "website1": "l.website",
    "zipcode1": "l.zipcode",
  },
  "limit": 5,
  "page": 3,
  "dateFrom": "2017-01-01T00:00:00+00:00",
  "dateTo": "2018-10-24T11:55:29+00:00",
}

Get an individual report by ID.

HTTP Request

GET /reports/ID

Or define query params like this:

GET /reports/3?dateFrom=2017-01-01&dateTo=2018-01-01&limit=5&page=3

Response

Expected Response Code: 200

See JSON code example.

Report Properties

Name Type Description
totalResults int Amount of results in the defined date range. Default date range is from 30 days ago to now
data array Holds rows of the report specific to each report data type and selected columns
dataColumns array Array of supported column names for the report data type
limit int Currently applied limit
page int Currently applied page
dateFrom datetime Currently applied date from filter
dateTo datetime Currently applied date to filter

List Reports

<?php

//...
$reports = $reportApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
  "total":8,
  "reports":[  
    {  
      "id":1,
      "name":"Contacts",
      "descriptionn":"lists all contacts",
      "system": false,
      "isScheduled": false,
      "source": "leads",
      "columns": [
        "l.id",
        "l.email",
        "l.firstname",
        "l.lastname"
      ],
      "filters": [],
      "tableOrder": [],
      "graphs": [],
      "groupBy": [],
      "settings": {
        "showGraphsAboveTable": 0,
        "showDynamicFilters": 0,
        "hideDateRangeFilter": 0
      },
      "aggregators": [],
      "scheduleUnit": null,
      "toAddress": null,
      "scheduleDay": null,
      "scheduleMonthFrequency": null
    },
    [...]
  ]
}

Returns a list of contact reports available to the user. This list is not filterable.

HTTP Request

GET /reports

Response

Expected Response Code: 200

See JSON code example.

Report Properties

Name Type Description
id int ID of the report
name string The report name
description string The report description
system boolean If true then the report is visible to all users. If false then only creator can see this report
isScheduled boolean Scheduled reports send report emails as the user defines
source string Report data source type
columns array List of selected columns for this particular report
filters array Filters applied on this report
tableOrder array Ordering applied on this report
graphs array Graphs defined for this report. API won’t return graphs
groupBy array Group by rules applied for this report
settings array Additional settings for the UI layout
aggregators array Aggregation rules applied on this report
scheduleUnit string or null Unit for the scheduler
toAddress string or null Email address for the scheduler
scheduleDay string or null Day for the scheduler
scheduleMonthFrequency string or null Frequency for the scheduler

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",
        "category": null,
        "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
category object/null Object with the category details
name string Segment name
alias string Segment alias
description string Segment description
filters array Smart filters for the segment. See filter properties bellow
isGlobal boolean Whether the segment is global. 0 means only the author will see it.

Segment Filter Properties

Name Type Description
glue string How to glue the filters to others. Possible values: and, or
field string Alias of the contact or company field to based the filter on
object string Object which have the field. Possible values: ‘lead’ (for contacts), company
type string Type of the field. Possible values: 'boolean’, date (format Y-m-d), datetime (format Y-m-d H:i:s), email, country, locale, lookup, number, tel, region, select, multiselect, text, textarea, time, timezone, url
operator string Operator used for matching the values. Possible values: ’=’, !=, empty, !empty, like, !like, regexp, !regexp, startsWith, endsWith, contains

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",
        "category": null,
        "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

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.

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
category object/null Object with the category details
name string Segment name
alias string Segment alias
description string Segment description
filters array Smart filters for the segment. See filter properties bellow
isGlobal boolean Whether the segment is global. 0 means only the author will see it.

Segment Filter Properties

Name Type Description
glue string How to glue the filters to others. Possible values: and, or
field string Alias of the contact or company field to based the filter on
object string Object which have the field. Possible values: 'lead’ (for contacts), company
type string Type of the field. Possible values: 'boolean’, date (format Y-m-d), datetime (format Y-m-d H:i:s), email, country, locale, lookup, number, tel, region, select, multiselect, text, textarea, time, timezone, url
operator string Operator used for matching the values. Possible values: ’=’, !=, empty, !empty, like, !like, regexp, !regexp, startsWith, endsWith, contains

Create Segment

<?php

$data = array(
    'name'        => 'Segment A',
    'alias'       => 'segment-a',
    'description' => 'This is my first segment created via API.',
    'isPublished' => 1,
    'filters' => array(
        array(
            'glue' => 'and',
            'field' => 'email',
            'object' => 'lead',
            'type' => 'email',
            'filter' => '*@gmail.com',
            'operator' => 'like',
        ),
    ),
);

$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
filters array

Segment Filter Properties

Name Type Description
glue string How to glue the filters to others. Possible values: and, or
field string Alias of the contact or company field to based the filter on
object string Object which have the field. Possible values: 'lead’ (for contacts), company
type string Type of the field. Possible values: 'boolean’, date (format Y-m-d), datetime (format Y-m-d H:i:s), email, country, locale, lookup, number, tel, region, select, multiselect, text, textarea, time, timezone, url
operator string Operator used for matching the values. Possible values: ’=’, !=, empty, !empty, like, !like, regexp, !regexp, startsWith, endsWith, contains

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
filters array

Segment Filter Properties

Name Type Description
glue string How to glue the filters to others. Possible values: and, or
field string Alias of the contact or company field to based the filter on
object string Object which have the field. Possible values: 'lead’ (for contacts), company
type string Type of the field. Possible values: 'boolean’, date (format Y-m-d), datetime (format Y-m-d H:i:s), email, country, locale, lookup, number, tel, region, select, multiselect, text, textarea, time, timezone, url
operator string Operator used for matching the values. Possible values: ’=’, !=, empty, !empty, like, !like, regexp, !regexp, startsWith, endsWith, contains

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/CONTACT_ID/add

Response

Expected Response Code: 200

See JSON code example.

Add Contacts to a Segment

<?php

//...
$contactIds = ['ids'=>[ 1, 45, 39]];
$response = $segmentApi->addContact($segmentId, $contactIds);
if (!isset($response['success'])) {
    // handle error
}
{
     "success":true,
     "details":{
        "1" :{"success":true},
        "45":{"success":true},
        "39":{"success":false}
     }
}

Manually add contacts to a specific segment.

HTTP Request

POST /segments/SEGMENT_ID/contacts/add

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/CONTACT_ID/remove

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.

Send SMS to Contact

<?php

$sms = $smsApi->sendToContact($smsId, $contactId);

Send a predefined sms to existing contact.

HTTP Request

GET /smses/ID/contact/CONTACT_ID/send

Response

Expected Response Code: 200

Properties json { "success": 1, "status": "Delivered" }

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($stageId, $contactId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually add a contact to a specific stage.

HTTP Request

POST /stages/STAGE_ID/contact/CONTACT_ID/add

Response

Expected Response Code: 200

See JSON code example.

Remove Contact from a Stage

<?php

//...
$response = $stageApi->removeContact($stageId, $contactId);
if (!isset($response['success'])) {
    // handle error
}
{
    "success": true
}

Manually remove a contact from a specific stage.

HTTP Request

POST /stages/STAGE_ID/contact/CONTACT_ID/remove

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_stat_replies",
    "email_stats",
    "email_stats_devices",
    "focus_stats",
    "form_submissions",
    "ip_addresses",
    "lead_categories",
    "lead_companies_change_log",
    "lead_devices",
    "lead_donotcontact",
    "lead_event_log",
    "lead_frequencyrules",
    "lead_lists_leads",
    "lead_points_change_log",
    "lead_stages_change_log",
    "lead_utmtags",
    "page_hits",
    "page_redirects",
    "plugin_citrix_events",
    "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_stat_replies": [
      "date_replied",
      "id",
      "message_id",
      "stat_id"
    ],
    "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",
      "tracking_id"
    ],
    "lead_donotcontact": [
      "channel",
      "channel_id",
      "comments",
      "date_added",
      "id",
      "lead_id",
      "reason"
    ],
    "lead_event_log": [
      "action",
      "bundle",
      "date_added",
      "id",
      "lead_id",
      "object",
      "object_id",
      "properties",
      "user_id",
      "user_name"
    ],
    "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"
    ],
    "plugin_citrix_events": [
      "email",
      "event_date",
      "event_desc",
      "event_name",
      "event_type",
      "id",
      "lead_id",
      "product"
    ],
    "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_read",
      "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_watched",
      "url",
      "user_agent"
    ],
    "webhook_logs": [
      "date_added",
      "id",
      "note",
      "runtime",
      "status_code",
      "webhook_id"
    ]
  }
}

HTTP Request

GET /stats

Response

Expected Response Code: 200

See JSON code example.

Stats Properties

Name Type Description
availableTables array List of available tables which can be used in this endpoint
stats array An empty array of stats, because no table was defined

Get Stats from a table

<?php
// Example setup variables:
$table = 'asset_downloads';
$start = 0;
$limit = 50;
$order = array(
    array(
      'col' => 'id',
      'dir' => 'asc'
    )
);
$where = array(
    array(
      'col' => 'id',
      'expr' => 'gt',
      'val' => 3,
    )
);

$stats = $statsApi->get($table, $start, $limit, $order, $where);
{
  "stats":[
    {
      "id":"1",
      "asset_id":"1",
      "ip_id":"1",
      "lead_id":"31",
      "date_download":"2016-06-30 08:51:22",
      "code":"200",
      "tracking_id":"b3259e7709f35b7428b7bffcbb3d1d713ac1526c"
    },
    [...]
  ]
}

HTTP Request

GET /stats/TABLE

Request Properties

Name Type Description
start int Which row to start on
limit int How many rows to return
order array An array of arrays which contain ordering (example above)
where array An array of arrays which contain where conditions (example above). As the expr param can be used most of the methods from DBAL Doctrine where methods.

If using cURL, a query parameter may look something like where%5B0%5D%5Bcol%5D=id&where%5B0%5D%5Bexpr%5D=eq&where%5B0%5D%5Bval%5D=3 which is the equivalent to the following:

array(
    array(
        'col'  => 'id',
        'expr' => 'eq',
        'val'  => 3,
    )
);

Response

Expected Response Code: 200

See JSON code example.

Stats Properties

Different for every table. It simply returns rows or requested table.

Tags

Use this endpoint to obtain details on Mautic’s tags. Implemented in Mautic 2.12.0.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth        = new ApiAuth();
$auth            = $initAuth->newAuth($settings);
$apiUrl          = "https://your-mautic.com";
$api             = new MauticApi();
$tagApi = $api->newApi("tags", $auth, $apiUrl);

Get Tag

<?php

//...
$tag = $tagApi->get($id);
{  
    "tag": {
        "id": 34,
        "tag": "tagA",
    }
}

Get an individual tag by ID.

HTTP Request

GET /tags/ID

Response

Expected Response Code: 200

See JSON code example.

Tag Properties

Name Type Description
id int ID of the tag
tag string Title of the tag

List Tags

<?php
// ...

$tags = $tagApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
    "total":1,
    "tags":[  
        {
            "id": 34,
            "tag": "tagA",
        }
    ]
}

HTTP Request

GET /tags

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 Tag.

Create Tag

<?php

$data = array(
    'tag' => 'Tag A',
);

$tag = $tagApi->create($data);

Create a new tag.

HTTP Request

POST /tags/new

Post Parameters

Name Type Description
id int ID of the tag
tag string Title of the tag

Response

Expected Response Code: 201

Properties

Same as Get Tag.

Edit Tag

<?php

$id   = 1;
$data = array(
    'tag' => 'Tag B',
);

// Create new a tag of ID 1 is not found?
$createIfNotFound = true;

$tag = $tagApi->edit($id, $data, $createIfNotFound);

Edit a new tag. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a tag if the given ID does not exist and clears all the tag information, adds the information from the request. PATCH fails if the tag with the given ID does not exist and updates the tag field values with the values form the request.

HTTP Request

To edit a tag and return a 404 if the tag is not found:

PATCH /tags/ID/edit

To edit a tag and create a new one if the tag is not found:

PUT /tags/ID/edit

Post Parameters

Name Type Description
id int ID of the tag
tag string Title of the tag

Response

If PUT, the expected response code is 200 if the tag was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Tag.

Delete Tag

<?php

$tag = $tagApi->delete($id);

Delete a tag.

HTTP Request

DELETE /tags/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Tag.

Themes

This endpoint is useful for working with Mautic themes.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$themesApi = $api->newApi("themes", $auth, $apiUrl);

Get theme

Returns directly the zip file in the response with the application/zip header on success or a JSON response body with error messages on fail. The PHP API library will save the zip file to the system temporary directory and provides you with the path.

<?php
$response = $themesApi->get($themeName);
{
    "file": "/absolute/path/to/the/system/temp/dir/with/the/theme/zip/file"
}

HTTP Request

GET /themes/THEME_NAME

Response

Expected Response Code: 200

Set Temporary File Path

Changes the default temporary directory where the zip file is created. The directory is created if it does not exist.

<?php
$themesApi->setTemporaryFilePath("/absolute/path/to/a/different/temp/dir");
$response = $themesApi->get($themeName);
{
    "file": "/absolute/path/to/a/different/temp/dir/zipfile"
}

Get List of themes

Lists all installed themes with the detailes stored in their config.json files.

<?php
$response = $themesApi->getList();
{
    "themes": {
        "blank": {
            "name": "Blank",
            "key": "blank",
            "config": {
                "name": "Blank",
                "author": "Mautic team",
                "authorUrl": "https:\/\/mautic.org",
                "features": [
                    "page",
                    "email",
                    "form"
                ]
            }
        },
        ...
    }
}

HTTP Request

GET /themes

Response

Expected Response Code: 200

See JSON code example.

Response Properties

Name Type Description
themes array List of installed themes and their configs

Create Theme

Creates a new theme or updates an existing one (based on the file name) from provided zip file.

<?php
$data = array(
    'file' => dirname(__DIR__).'/'.'mytheme.zip' // Must be a path to an existing file
);

$response = $themeApi->create($data);

The file is sent via regular POST files array like a browser sends it during file upload.

HTTP Request

POST /themes/new

Response

Expected Response Code: 200 json { "success": true }

Delete File

<?php
$response = $themeApi->delete($themeName);

Delete a theme. The stock themes cannot be deleted

HTTP Request

DELETE /themes/THEME_NAME/delete

Response

Expected Response Code: 200 json { "success": true }

Tweets

Use this endpoint to obtain details on Mautic’s tweets. Implemented in Mautic 2.8.0.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth        = new ApiAuth();
$auth            = $initAuth->newAuth($settings);
$apiUrl          = "https://your-mautic.com";
$api             = new MauticApi();
$tweetApi = $api->newApi("tweets", $auth, $apiUrl);

Get Tweet

<?php

//...
$tweet = $tweetApi->get($id);
{  
    "tweet": {
        "isPublished": true,
        "dateAdded": "2017-02-03T17:51:58+01:00",
        "dateModified": "2017-03-28T11:03:03+02:00",
        "createdBy": 1,
        "createdByUser": "John Doe",
        "modifiedBy": 1,
        "modifiedByUser": "John Doe",
        "id": 1,
        "name": "Thank you tweet",
        "text": "Hi {twitter_handle}\n\nThanks for ...",
        "language": "en",
        "category": {
            "createdByUser": "John Doe",
            "modifiedByUser": null,
            "id": 185,
            "title": "Thank you tweets",
            "alias": "thank-you-tweets",
            "description": null,
            "color": "244bc9",
            "bundle": "global"
        },
        "tweetId": null,
        "mediaId": null,
        "mediaPath": null,
        "sentCount": 3,
        "favoriteCount": 0,
        "retweetCount": 0,
        "description": "Used in the Product A campaign 1"
    }
}

Get an individual tweet by ID.

HTTP Request

GET /tweets/ID

Response

Expected Response Code: 200

See JSON code example.

Tweet Properties

Name Type Description
id int ID of the tweet
name string Title of the tweet
text string Message of the tweet
isPublished bool Published state
publishUp datetime/null Date/time when the tweet should be published
publishDown datetime/null Date/time the tweet should be un published
dateAdded datetime Date/time tweet was created
createdBy int ID of the user that created the tweet
createdByUser string Name of the user that created the tweet
dateModified datetime/null Date/time tweet was last modified
modifiedBy int ID of the user that last modified the tweet
modifiedByUser string Name of the user that last modified the tweet
language string Language locale of the tweet
category null/object Category

List Tweets

<?php
// ...

$tweets = $tweetApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
    "total":1,
    "tweets":[  
        {
            "isPublished": true,
            "dateAdded": "2017-02-03T17:51:58+01:00",
            "dateModified": "2017-03-28T11:03:03+02:00",
            "createdBy": 1,
            "createdByUser": "John Doe",
            "modifiedBy": 1,
            "modifiedByUser": "John Doe",
            "id": 1,
            "name": "Thank you tweet",
            "text": "Hi {twitter_handle}\n\nThanks for ...",
            "language": "en",
            "category": null,
            "tweetId": null,
            "mediaId": null,
            "mediaPath": null,
            "favoriteCount": 0,
            "retweetCount": 0,
            "description": "Used in the Product A campaign 1"
        }
    ]
}

HTTP Request

GET /tweets

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 Tweet.

Create Tweet

<?php 

$data = array(
    'name'    => 'Tweet A',
    'heading' => 'Hello World!'
    'message' => 'This is my first tweet created via API.',
);

$tweet = $tweetApi->create($data);

Create a new tweet.

HTTP Request

POST /tweets/new

Post Parameters

Name Type Description
id int ID of the tweet
name string Title of the tweet
text string Message of the tweet
url string URL to go to when the tweet is clicked
isPublished bool Published state
publishUp datetime/null Date/time when the tweet should be published
publishDown datetime/null Date/time the tweet should be un published
language string Language locale of the tweet

Response

Expected Response Code: 201

Properties

Same as Get Tweet.

Edit Tweet

<?php

$id   = 1;
$data = array(
    'name' => 'Tweet A',
    'text' => 'This is my first tweet created via API.',
);

// Create new a tweet of ID 1 is not found?
$createIfNotFound = true;

$tweet = $tweetApi->edit($id, $data, $createIfNotFound);

Edit a new tweet. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a tweet if the given ID does not exist and clears all the tweet information, adds the information from the request. PATCH fails if the tweet with the given ID does not exist and updates the tweet field values with the values form the request.

HTTP Request

To edit a tweet and return a 404 if the tweet is not found:

PATCH /tweets/ID/edit

To edit a tweet and create a new one if the tweet is not found:

PUT /tweets/ID/edit

Post Parameters

Name Type Description
id int ID of the tweet
name string Title of the tweet
text string Message of the tweet
url string URL to go to when the tweet is clicked
isPublished bool Published state
publishUp datetime/null Date/time when the tweet should be published
publishDown datetime/null Date/time the tweet should be un published
language string Language locale of the tweet

Response

If PUT, the expected response code is 200 if the tweet was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Tweet.

Delete Tweet

<?php

$tweet = $tweetApi->delete($id);

Delete a tweet.

HTTP Request

DELETE /tweets/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get Tweet.

Users

Use this endpoint to obtain details on Mautic’s users (administrators).

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth = new ApiAuth();
$auth     = $initAuth->newAuth($settings);
$apiUrl   = "https://your-mautic.com";
$api      = new MauticApi();
$userApi  = $api->newApi("users", $auth, $apiUrl);

Get User

<?php

//...
$user = $userApi->get($id);
{  
  "user":{  
    "isPublished":true,
    "dateAdded":"2016-11-09T14:23:44+00:00",
    "createdBy":1,
    "createdByUser":"John Doe",
    "dateModified":null,
    "modifiedBy":null,
    "modifiedByUser":null,
    "id":6,
    "username":"apitest",
    "firstName":"John",
    "lastName":"Doe",
    "email":"[email protected]",
    "position":null,
    "role":{  
      "createdByUser":null,
      "modifiedByUser":null,
      "id":1,
      "name":"Administrator",
      "description":"Full system access",
      "isAdmin":true,
      "rawPermissions":null
    },
    "timezone":null,
    "locale":null,
    "lastLogin":null,
    "lastActive":null,
    "onlineStatus":"offline",
    "signature":null
  }
}

Get an individual user by ID.

HTTP Request

GET /users/ID

Response

Expected Response Code: 200

See JSON code example.

User 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
lastActive datetime/null Date/time when the user last active
modifiedBy int ID of the user that last modified the contact
modifiedByUser string Name of the user that last modified the contact
username string Username which can be used for log in to Mautic.
firstName string First Name of the user
lastName string Last Name of the user
email string Email of the user
position string User’s position title
role object Role details
timezone string Timezone of the user
onlineStatus string Online status of the user
signature string Signature of the user which can be used in the emails

List Contact Users

<?php

//...
$users = $userApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{  
  "total":2,
  "users":[  
    {  
      "isPublished":true,
      "dateAdded":"2016-08-01T11:52:15+00:00",
      "createdBy":null,
      "createdByUser":" ",
      "dateModified":"2016-09-26T15:02:32+00:00",
      "modifiedBy":null,
      "modifiedByUser":" ",
      "id":2,
      "username":"test",
      "firstName":"John",
      "lastName":"Doe",
      "email":"[email protected]",
      "position":null,
      "role":{  
        "createdByUser":"John Doe",
        "modifiedByUser":null,
        "id":4,
        "name":"edit own contacts",
        "description":null,
        "isAdmin":false,
        "rawPermissions":{  
          "lead:leads":[  
            "viewown",
            "editown",
            "create",
            "deleteown"
          ],
          "lead:lists":[  
            "viewother"
          ]
        }
      },
      "timezone":null,
      "locale":null,
      "lastLogin":"2016-09-26T15:03:25+00:00",
      "lastActive":"2016-09-26T15:19:15+00:00",
      "onlineStatus":"offline",
      "signature":"Best regards,&#10;Yours&#10;|FROM_NAME|"
    },
    [...]
  ]
}

HTTP Request

GET /users

Response

Expected Response Code: 200

See JSON code example.

User 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
lastActive datetime/null Date/time when the user last active
modifiedBy int ID of the user that last modified the contact
modifiedByUser string Name of the user that last modified the contact
username string Username which can be used for log in to Mautic.
firstName string First Name of the user
lastName string Last Name of the user
email string Email of the user
position string User’s position title
role array List of roles of the user
timezone string Timezone of the user
onlineStatus string Online status of the user
signature string Signature of the user which can be used in the emails

Create User

<?php 

$data = array(
    'username' => 'apitest',
    'firstName' => 'John',
    'lastName' => 'Doe',
    'email' => '[email protected]',
    'plainPassword' => array(
        'password' => 'topSecret007',
        'confirm' => 'topSecret007',
    ),
    'role' => 1,
);

$user = $userApi->create($data);

Create a new user.

HTTP Request

POST /users/new

Post Parameters

Name Description
username string
firstName string
lastName string
email string
position string
role int
timezone string
onlineStatus string
signature string
plainPassword array

Response

Expected Response Code: 201

Properties

Same as Get User.

Edit User

<?php

$id   = 1;
$data = array(
    'lastName' => 'Doeboe',
);

// Create new a user of ID 1 is not found?
$createIfNotFound = true;

$user = $userApi->edit($id, $data, $createIfNotFound);

Edit a new user. User that this supports PUT or PATCH depending on the desired behavior.

PUT creates a user if the given ID does not exist and clears all the user information, adds the information from the request. PATCH fails if the user with the given ID does not exist and updates the user field values with the values form the request.

HTTP Request

To edit a user and return a 404 if the user is not found:

PATCH /users/ID/edit

To edit a user and create a new one if the user is not found:

PUT /users/ID/edit

Post Parameters

Name Description
username string
firstName string
lastName string
email string
position string
role int
timezone string
signature string

Response

If PUT, the expected response code is 200 if the user was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get User.

Delete User

<?php

$user = $userApi->delete($id);

Delete a user.

HTTP Request

DELETE /users/ID/delete

Response

Expected Response Code: 200

Properties

Same as Get User.

Get Self User

<?php

$user = $userApi->getSelf();

Get a self user.

HTTP Request

GET /users/self

Response

Expected Response Code: 200

Properties

Same as Get User.

Check User Permissions

<?php
$permission = array('user:users:create', 'user:users:edit');
$user = $userApi->checkPermission($id, $permission);

Get a self user.

HTTP Request

GET /users/ID/permissioncheck

Response

Expected Response Code: 200 json { "user:users:create":true, "user:users:edit":true }

Properties

array of requested permissions of string in case of just one

Webhooks

Use this endpoint to obtain details on Mautic’s webhooks. Implemented in Mautic 2.8.0.

<?php
use Mautic\MauticApi;
use Mautic\Auth\ApiAuth;

// ...
$initAuth        = new ApiAuth();
$auth            = $initAuth->newAuth($settings);
$apiUrl          = "https://your-mautic.com";
$api             = new MauticApi();
$webhookApi      = $api->newApi("webhooks", $auth, $apiUrl);

Get Webhook

<?php

//...
$webhook = $webhookApi->get($id);
{
  "hook": {
    "isPublished": false,
    "dateAdded": "2017-06-07T08:54:46+00:00",
    "dateModified": "2017-06-09T07:16:23+00:00",
    "createdBy": 1,
    "createdByUser": "John Doe",
    "modifiedBy": null,
    "modifiedByUser": " ",
    "id": 31,
    "name": "test",
    "description": "Created via API",
    "webhookUrl": "https:\/\/johndoe.com",
    "secret": "webhookSecretKey",
    "eventsOrderbyDir": "DESC",
    "category": {
      "createdByUser": "John Doe",
      "modifiedByUser": "John Doe",
      "id": 1,
      "title": "Important",
      "alias": "important",
      "description": null,
      "color": null,
      "bundle": "Webhook"
    },
    "triggers": [
      "mautic.lead_post_delete",
      "mautic.lead_points_change",
      "mautic.lead_post_save_new",
      "mautic.lead_post_save_update"
    ]
  }
}

Get an individual webhook by ID.

HTTP Request

GET /hooks/ID

Response

Expected Response Code: 200

See JSON code example.

Webhook Properties

Name Type Description
id int ID of the webhook
name string Title of the webhook
description string Description of the webhook
webhookUrl string Url to send the webhook payload to
secret string Secret key used for authenticity verification
eventsOrderbyDir Order direction for queued events in one webhook. Can be “DESC” or “ASC”
isPublished bool Published state
publishUp datetime/null Date/time when the webhook should be published
publishDown datetime/null Date/time the webhook should be un published
dateAdded datetime Date/time webhook was created
createdBy int ID of the user that created the webhook
createdByUser string Name of the user that created the webhook
dateModified datetime/null Date/time webhook was last modified
modifiedBy int ID of the user that last modified the webhook
modifiedByUser string Name of the user that last modified the webhook
category null/object Category
triggers array List of triggers available in Mautic

List Webhooks

<?php
// ...

$webhooks = $webhookApi->getList($searchFilter, $start, $limit, $orderBy, $orderByDir, $publishedOnly, $minimal);
{
  "total": 1,
  "hooks": {
    "31": {
      "isPublished": false,
      "dateAdded": "2017-06-07T08:54:46+00:00",
      "dateModified": "2017-06-09T07:16:23+00:00",
      "createdBy": 1,
      "createdByUser": "John Doe",
      "modifiedBy": null,
      "modifiedByUser": " ",
      "id": 31,
      "name": "Deleted contact",
      "description": "Notify me when a contact is deleted",
      "webhookUrl": "https:\/\/johndoe.com",
      "secret": "webhookSecretKey",
      "eventsOrderbyDir": "DESC",
      "category": null,
      "triggers": [
        "mautic.lead_post_delete",
      ]
    }
  }
}

HTTP Request

GET /hooks

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 Webhook.

Create Webhook

<?php

$data = array(
    'name' => 'test',
    'description' => 'Created via API',
    'webhookUrl' => 'http://some.url',
    'secret': 'webhookSecretKey',
    'eventsOrderbyDir' => "ASC",
    'triggers' => array(
        'mautic.lead_post_save_update',
        'mautic.lead_post_save_new',
    )
);

$webhook = $webhookApi->create($data);

Create a new webhook.

HTTP Request

POST /hooks/new

Post Parameters

Name Type Description
id int ID of the webhook
name string Title of the webhook
description string Description of the webhook
webhookUrl string URL to send the webhook payload to
secret string (Optional) Secret key used for authenticity verification
eventsOrderbyDir Order direction for queued events in one webhook. Can be “DESC” or “ASC”
isPublished bool Published state

Response

Expected Response Code: 201

Properties

Same as Get Webhook.

Edit Webhook

<?php

$id   = 1;
$data = array(
    'name' => 'Rename webhook 1 to this',
);

// Create new a webhook of ID 1 is not found?
$createIfNotFound = true;

$webhook = $webhookApi->edit($id, $data, $createIfNotFound);

Edit a new webhook. Note that this supports PUT or PATCH depending on the desired behavior.

PUT creates a webhook if the given ID does not exist and clears all the webhook information, adds the information from the request. PATCH fails if the webhook with the given ID does not exist and updates the webhook field values with the values form the request.

HTTP Request

To edit a webhook and return a 404 if the webhook is not found:

PATCH /hooks/ID/edit

To edit a webhook and create a new one if the webhook is not found:

PUT /hooks/ID/edit

Post Parameters

Name Type Description
id int ID of the webhook
name string Title of the webhook
description string Description of the webhook
webhookUrl string Url to send the webhook payload to
secret string Secret key used for authenticity verification
eventsOrderbyDir Order direction for queued events in one webhook. Can be “DESC” or “ASC”
isPublished bool Published state

Response

If PUT, the expected response code is 200 if the webhook was edited or 201 if created.

If PATCH, the expected response code is 200.

Properties

Same as Get Webhook.

Delete Webhook

<?php

$webhook = $webhookApi->delete($id);

Delete a webhook.

HTTP Request

DELETE /hooks/ID/delete

Response

Expected Response Code: 200

Same as Get Webhook.

List available webhook triggers

<?php

$webhook = $webhookApi->getTriggers();

List webhook triggers

HTTP Request

GET /hooks/triggers

Response

Expected Response Code: 200

{
  "triggers": {
    "mautic.lead_post_delete": {
      "label": "Contact Delete Event",
      "description": "mautic.lead.webhook.event.lead.deleted_desc"
    },
    "mautic.lead_points_change": {
      "label": "Contact Point Change (Increase \/ Decrease) Event",
      "description": "mautic.lead.webhook.event.lead.points_desc"
    },
    "mautic.lead_post_save_update": {
      "label": "Contact Updated Event",
      "description": "mautic.lead.webhook.event.lead.update_desc"
    },
    "mautic.email_on_open": {
      "label": "Email Open Event",
      "description": "mautic.email.webhook.event.open_desc"
    },
    "mautic.form_on_submit": {
      "label": "Form Submit Event",
      "description": "mautic.form.webhook.event.form.submit_desc"
    },
    "mautic.lead_post_save_new": {
      "label": "New Contact Event",
      "description": "mautic.lead.webhook.event.lead.new_desc"
    },
    "mautic.page_on_hit": {
      "label": "Page Hit Event",
      "description": "mautic.page.webhook.event.hit_desc"
    }
  }
}

Webhooks

Webhook is a universal way how to send data about leads, pages, forms and events. The data is sent in real-time when an action occurs so the system which listens form Mautic webhook data can process them without the need for a periodic scanning if Mautic has some new data.

Available webhook actions

Mautic can send webhook payload on these actions:

The webhook workflow

The example workflow below describes a real-life workflow to get an idea how the webhooks can be used. Let’s imagine we have a project management system (PMS) and we want to create a new issue when a lead submits a form.

  1. A lead submits a Mautic form.
  2. Mautic saves the form.
  3. Mautic checks if there is a webhook with the Form submit event. If there is, Mautic sends the data to the URL address defined in the webhook.
  4. PMS receives the data about the form submission and creates a new issue from it.

Create a webhook

It is possible to create multiple different webhooks. That will allow you to send different information to different apps/scripts.

  1. Open the right hand side menu (click the cog icon in the top right corner) and select Webhooks.
  2. Create a new webhook.
  3. Fill in a Name, Webhook POST Url (see the next paragraph if you don’t have one) and select which Webhook Events should trigger this webhook.

Test a webhook

The easiest way how to test a webhook payload is to use a service like RequestBin. RequestBin lets you create a URL which you can set as the Webhook POST Url in Mautic. Then click the Apply button to save it and then click the Send Test Payload button. That will send a test payload data to RequestBin and you will be able to see it at your RequestBin.

When you have created your testing webhook, you can test the real data it sends when a defined action is triggered.

Immediate or queued webhooks

There is an option to queue webhooks for background execution. The reason behind it is that every time an action like contact update happens which has the webhook listener attached to it, the action has to wait for the webhook response until the webhook response returns or when it times out after 10 seconds. So it is up to the webhook receiver how fast the contact update is.

This lag can be more visible when you do a CSV import. It may be slow when it is waiting a second or two for webhook response for every imported contact.

If you want to avoid this lag in the user interface, configure the Webhook queue in the configuration and add this command to your cron tab: bin/console mautic:webhooks:process. This way every time the Webhook is triggered, the information is only stored as a new row into database, so it is much faster than sending the information to another service via HTTP. Then the command (cron job) will read the records from the database and make the requests which may take some time, but it is not slowing down the Users or Contacts. The command will also send 10 events in 1 Webhook request so this communication needs 10 times less HTTP requests. The caveat to this optimization is that the Webhooks are not fired every time the action happens, but every time the command (cron job) runs.

Queued event order

Mautic will send several events in one webhook if they happen before the queue trigger command runs. Mautic’s default order of those events is chronological. But Zapier integration which use webhooks heavily requires reverse chronological order. Therefore the new option to configure the order was added to webhooks as well as to the global configuration. Webhook configuration overwrites the global configuration, but if not set, the global configuration order value is applied.

Authenticity verification

During webhook creation you can provide a secret key, if no secret key is provided it will be automatically generated. This secret has to be shared with third-party application which will receive webhooks from mautic. Indeed, in order to verify authenticity of the data provided in a webhook, Mautic add an header Webhook-Signature on every webhook call. A third-party application can compute a base64 encoded HMAC-SHA256 signature with the webhook secret on the (raw) payload body to verify this signature and prove authenticity of the webhook data.

Examples webhook script

If you need an idea about how to receive Mautic webhook data in your app, this script can be used as a starting point. The script will log the request and return the payload. Place this script on a publicly accessible URL (i.e. `http://yourwebsite.com/webhookTest.php), then fill in the Mautic Webhook POST Url to this script.

<?php
// webhookTest.php

/**
 * A helper class to log and get the Mautic webhook request
 */
class webhookTest {
    /**
     * Log a message to a file
     *
     * @param  mixed $message
     * @param  string $type [info|error|request]
     */
    public function log($message, $type = 'info')
    {
        $prefix = 'webhookLog_';
        $file = $type . '.log';
        $date = new DateTime();
        error_log($date->format('Y-m-d H:i:s') . ' ' . $message . "\n\n", 3, $prefix . $file);
    }
    /**
     * Get the request JSON object and log the request
     *
     * @return object
     */
    public function getRequest()
    {
        $rawData = file_get_contents("php://input");
        $this->log($rawData, 'request');
        return $rawData;
    }
}

$secret = 'mySecret';
$webhook = new webhookTest;
$rawData = $webhook->getRequest();

// optional signature verification
$headers = getallheaders();
$receivedSignature = $headers['Webhook-Signature'];
$computedSignature = base64_encode(hash_hmac('sha256', $rawData, $secret, true));

if ($receivedSignature === $computedSignature) {
    $webhook->log('Webhook authenticity verification OK', 'request');
} else {
    $webhook->log('Webhook not authentic!', 'request');
}

// @todo Process the $requestData as needed
$requestData = json_decode($rawData);

Additionally here are another example in NodeJS (with express): “`javascript ‘use strict’;

const express = require('express’); const crypto = require('crypto’); const app = express(); const port = 3000; const SECRET = 'mySecret’;

// save raw body app.use ((req, res, next) => { let data = ”; req.setEncoding('utf8’);

req.on('data’, chunk => data += chunk); req.on('end’, () => { req.body = data; return next(); }); });

app.post(’/webhook’, (req, res) => {

// optional signature verification const receivedSignature = req.headers['webhook-signature’]; console.log('Received signature (in header):’, receivedSignature);

const computedSignature = crypto.createHmac('sha256’, SECRET).update(req.body).digest('base64’); console.log('Computed signature (from body):’, computedSignature);

if (receivedSignature === computedSignature) { console.log('Webhook authenticity verification OK’); } else { console.log('Webhook not authentic!’); }

// TODO: process body const body = JSON.parse(req.body);

res.send(); });

app.listen(port, () => console.log(App listening on port ${port}!)); “` If you’d like to extend the webhook functionality with your plugin, read more in the plugin docs.

MauticJS API

Mautic provides a means for plugins to inject custom JavaScript into mtc.js, the PHP generated script that manages Mautic’s tracking pixel, dynamic web content, etc. mtc.js is embedded in 3rd party websites to manage communication between those and Mautic.

mtc.js

<?php

namespace Mautic\PageBundle\EventListener;

use Mautic\CoreBundle\CoreEvents;
use Mautic\CoreBundle\Event\BuildJsEvent;
use Mautic\PageBundle\Event\TrackingEvent;
use Mautic\PageBundle\PageEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class TrackingSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            CoreEvents::BUILD_MAUTIC_JS    => ['onBuildJs', 0],
            PageEvents::ON_CONTACT_TRACKED => ['onContactTracked', 0],
        ];
    }

    public function onBuildJs(BuildJsEvent $event)
    {
        $event->appendJs(
            <<<JS

        document.addEventListener('mauticPageEventDelivered', function(e) {
            var detail   = e.detail;
            if (detail.response && detail.response.events && detail.response.events.tracked) {
                console.log(detail.response.events.tracked);
            }
      });

JS
        );
    }

    public function onContactTracked(TrackingEvent $event)
    {
        $contact  = $event->getContact();
        $response = $event->getResponse();

        $response->set(
            'tracked',
            [
                'email' => $contact->getEmail()
            ]
        );
    }
}

To inject custom Javascript into mtc.js, use an event listener for the CoreEvents::BUILD_MAUTIC_JS event. This event receives a Mautic\CoreBundle\Event\BuildJsEvent object where $event->appendJs($js, $sectionName); can be used to inject the script’s code.

Hooking into the Tracking Process and Returning Custom Responses

If it’s desired to do something during the request to track the contact (/mtc/event) or append to the payload returned to the tracking code which can be leveraged by custom javascript injected through CoreEvents::BUILD_MAUTIC_JS, subscribe to the PageEvents::ON_CONTACT_TRACKED event. The listener can inject a custom payload through the Mautic\PageBundle\Event\TrackingEvent::set method. This will expose the payload to the tracking code’s mauticPageEventDelivered event in the detail.response.events object. See the PHP code example.

JS Form Processing Hooks

if (typeof MauticFormCallback == 'undefined') {
    var MauticFormCallback = {};
}
MauticFormCallback['replaceWithFormName'] = {
    onValidateEnd: function (formValid) {
         // before form submit
    },
    onResponse: function (response) { 
         // after form submit
    }
};

If you wish execute additional code to execute as form is being processed create a MauticFormCallback object. In the example code replace replaceWithFormName with the name of your form.

onValidateEnd and onResponse are actions called by Form.customCallbackHandler.

onValidate()

MauticFormCallback['replaceWithFormName'] = {
    onValidate: function () {
        // before form validation
        var formIsGood = True;
        var dontUpdate = False;
        if(dontUpdate){
            return null;
        }else if(formIsGood){
            return True;
        }else if(!formIsGood){
            return False;
        }
    },
};

Called before default form validation and can be used to override default form validation.

Return True to skip the default form validation continue with form processing. Return False to skip the default form validation and prevent the form submission. Return null to execute default form validation.

onValidateStart()

MauticFormCallback['replaceWithFormName'] = {
    onValidateStart: function () {
         // before default validation
    },
};

Called at the beginning of the default form validation. Receives no values and return value is not required and not processed.

onValidateEnd(formValid)

MauticFormCallback['replaceWithFormName'] = {
    onValidateEnd: function (formValid) {
         // before form submit
    },
};

Called after all form validation is complete (default and/or onValidate callback) and before the form is submitted. Receives formValid to determine if form is valid. Return value is not required and not processed.

onErrorMark(callbackData)

var callbackData = {
    containerId: containerId,
    valid: valid,
    validationMessage: callbackValidationMessage
};

MauticFormCallback['replaceWithFormName'] = {
    onErrorMark: function (callbackData) {
         // called during error marking
    },
};

Called during error marking. Receives a callbackData object. Return True to skip the default error marking.

onErrorClear(containerId)

MauticFormCallback['replaceWithFormName'] = {
    onErrorClear: function (containerId) {
         // called to clear an existing error
    },
};

Called to clear an existing error. Receives containerId with the id of the element containing the error. Return True to skip the default error clearing.

onResponse(response)

MauticFormCallback['replaceWithFormName'] = {
    onResponse: function (response) {
         // called to process the response to the form submission
    },
};

Called prior to default form submission response processing. Receives response containing the form submission response. Return True to skip the default form submission response processing.

onResponseStart(response)

MauticFormCallback['replaceWithFormName'] = {
    onResponseStart: function (response) {
         // called to process the response to the form submission
    },
};

Called at the beginning default form submission response processing. Receives response containing the form submission response. Return value is not required and not processed.

onResponseEnd(response)

MauticFormCallback['replaceWithFormName'] = {
    onResponseEnd: function (response) {
         // called to process the response to the form submission
    },
};

Called at the end default form submission response processing. Receives response containing the form submission response. Return value is not required and not processed.

onMessageSet(messageObject)

var messageObject = {
    message: message,
    type: type
};

MauticFormCallback['replaceWithFormName'] = {
    onErrorMark: function (messageObject) {
         // called prior to default message insertion
    },
};

Called prior to default message insertion. Receives a messageObject containing the message and message type. Return True to skip the default message insertion.

onSubmitButtonDisable(messageObject)

MauticFormCallback['replaceWithFormName'] = {
    onErrorMark: function (messageObject) {
         // called prior to default message insertion
    },
};

Called prior to default disabling of the submit button. Receives no values. Return True to skip the default disabling of the submit button.

onSubmitButtonEnable()

MauticFormCallback['replaceWithFormName'] = {
    onErrorMark: function (messageObject) {
         // called prior to default message insertion
    },
};

Called prior to default enabling of the submit button. Receives no values. Return True to skip the default enabling of the submit button.

onShowNextPage()

MauticFormCallback['replaceWithFormName'] = {
    onShowNextPage: function (pageNumber) {
         // called prior to going to the next page
    },
};

Called prior to going to the next page in the form. Useful to adjust the DOM prior to making the page visible.

onShowPreviousPage()

MauticFormCallback['replaceWithFormName'] = {
    onShowPreviousPage: function (pageNumber) {
         // called prior to going back to previous page
    },
};

Called prior to going back to a previous page in the form. Useful to adjust the DOM prior to making the page visible.

MauticJS API Functions

MauticJS.serialize(object)

This method will transform an object properties into a key=value string, concatenating them with an ampersand. It is used when submitting data via MauticJS.makeCORSRequest.

var obj = {firstname: "John", lastname: "Doe"};

var serialized = MauticJS.serialize(obj);

alert(serialized); // Shows "firstname=John&lastname=Doe"

MauticJS.documentReady(functionName|function)

This method will check if the document has finished rendering, then execute the given function. The function argument can be the name of a function or an anonymous function.

function test() {
    alert('test');
}

MauticJS.documentReady(test);

MauticJS.iterateCollection(collection)(functionName|function)

This method will iterate over the provided collection (array, object, HTMLCollection, etc) using the provided function argument. The function argument can be the name of a function or an anonymous function. The function will receive the collection node and the iteration number as arguments.

var videos = document.getElementsByTagName('video');

// Add a custom data attribute to all videos
MauticJS.iterateCollection(videos)(function(node, i) {
    node.dataset.customAttribute = 'test';
});

MauticJS.log(arguments)

This method is a lightweight wrapper around console.log. It exists because some browsers do not provide this functionality. It takes any number of arguments, logs them, then passes those same arguments to the console.log method if it exists.

MauticJS.log('Something happened');

MauticJS.createCORSRequest(method, url)

This method creates an XMLHttpRequest, then checks to see if it supports the withCredentials property. If not, we are probably on windows, so it then checks for the existence of XDomainRequest, then creates it if found. Finally, it opens then returns the xhr. It can be used to send cross domain requests that include the cookies for the domain. It is used internally within the MauticJS.makeCORSRequest method.

MauticJS.createCORSRequest('GET', 'https://mymautic.com/dwc/slot1');

MauticJS.makeCORSRequest(method, url, data, callbackSuccess, callbackError)

This method uses MauticJS.createCORSRequest to open a cross domain request to the specified URL, then sets the callbackSuccess and callbackError values appropriately. You may omit either of the callbacks. If you do, the callbacks are replaced with a basic function that uses MauticJS.log(response) to log the response from the request. The callback methods receive the server response and the xhr object as arguments. If the response is determined to be a JSON string, it is automatically parsed to a JSON object. The data argument will be serialized using MauticJS.serialize(data), then sent with the request to the server. All requests made this way have the X-Requested-With header set to XMLHttpRequest.

MauticJS.makeCORSRequest('GET', 'https://mymautic.com/dwc/slot1', [], function (response, xhr) {
    if (response.success) {
        document.getElementById('slot1').innerHTML = response.content;
    }
});

MauticJS.parseTextToJSON(maybeJSON)

This method will take a text string and check to see if it is a valid JSON string. If so, it parses it into a JSON object and returns. If not, then it will simply return the argument passed to it.

var text = '{"firstname": "John", "lastname": "Doe"}';

var json = MauticJS.parseTextToJSON(text);

alert(json); // Will show [object Object]

var text = 'not valid json';

var json = MauticJS.parseTextToJSON(text);

alert(json); // Will show 'not valid json'

MauticJS.insertScript(scriptUrl)

This method will insert a script tag with the provided URL in the head of your document, before other scripts.

MauticJS.insertScript('http://google.com/ga.js');