diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 42dc2ab..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-tests/bin/
-tests/composer.lock
-tests/vendor/
-tests/behat.yml
-*.patch
-
diff --git a/README.md b/README.md
index 5c796ed..55a8a5b 100644
--- a/README.md
+++ b/README.md
@@ -4,20 +4,25 @@ This module adds Marketo tracking capability to your Drupal site.
 
 ## Requirements
 
-- Drupal 7.x
-- An active Marketo account http://www.marketo.com/
+- Drupal 8.x
+- Composer <https://getcomposer.org/>
+- An active Marketo account <http://www.marketo.com/>
 
 ## Installation
 
-1. Copy the entire marketo_ma directory the Drupal sites/all/modules directory.
+1. Copy the entire `marketo_ma` directory the Drupal `modules` directory. You
+   can optionally user drush to download with `drush dl marketo_ma`.
 
-2. Login as an administrator. Enable the Marketo MA module in
+2. Add the SOAP API library to drupal using `composer require
+   dchesterton/marketo-rest-api:dev-master`.
+
+3. Login as an administrator. Enable the Marketo MA module in
    "Administer" -> "Modules"
 
-3. (Optional) Enable the Marketo MA User module in
+4. (Optional) Enable the Marketo MA User module in
    "Administer" -> "Modules"
 
-4. (Optional) Enable the Marketo MA Webform module in
+5. (Optional) Enable the Marketo MA Webform module in
    "Administer" -> "Modules"
 
 
@@ -103,7 +108,7 @@ and [Export a List of All Marketo API Field Names](http://community.marketo.com/
 **Marketo Fields**
 This section should contain a pipe "|" delimited list of the fields in the format
 "[Field API Name]|[Friendly Label]". Example:
-    
+
     FirstName|First Name
     LastName|Last Name
     Email|Email Address
@@ -176,13 +181,13 @@ attached to it and provides an overview of each item's current state.
 
 - **Title**
   The title of the webform.
-  
+
 - **Tracking Enabled**
   A Yes/No value indicating if tracking is currently enabled for this webform.
-  
+
 - **Components Mapped**
   The number of form components that have been mapped to a Marketo field.
-  
+
 - **Manage**
   A direct link to the Marketo component mapping page for this webform.
 
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..4c65ce7
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,23 @@
+{
+  "name": "drupal/marketo_ma",
+  "type": "drupal-module",
+  "description": "Integration with Marketo marketing automation software.",
+  "keywords": ["Drupal"],
+  "license": "GPL-2.0+",
+  "homepage": "https://www.drupal.org/project/marketo_ma",
+  "minimum-stability": "dev",
+  "support": {
+    "issues": "https://www.drupal.org/project/issues/marketo_ma",
+    "source": "http://cgit.drupalcode.org/marketo_ma"
+  },
+  "repositories": [
+    {
+      "type": "vcs",
+      "url": "https://github.com/Jaesin/marketo-rest-api"
+    }
+  ],
+  "require": {
+    "php": ">=5.5.9",
+    "dchesterton/marketo-rest-api" : "dev-master"
+  }
+}
diff --git a/config/install/marketo_ma.settings.yml b/config/install/marketo_ma.settings.yml
new file mode 100644
index 0000000..133dd0c
--- /dev/null
+++ b/config/install/marketo_ma.settings.yml
@@ -0,0 +1,25 @@
+instance_host: null
+logging: 0
+tracking_method: api_client
+munchkin:
+  javascript_library: '//munchkin.marketo.net/munchkin.js'
+  api_private_key: null
+  partition: Default
+  lead_source: null
+  account_id: null
+rest:
+  batch_requests: 0
+  client_id: ''
+  client_secret: ''
+field:
+  defined_fields:
+    FirstName: 'First Name'
+    LastName: 'Last Name'
+    Email: 'Email Address'
+tracking:
+  request_path:
+    negate: 1
+    pages: "admin\nadmin/*\nbatch\nnode/add*\nnode/*/*\nuser/*/*"
+  roles:
+    authenticated: authenticated
+    anonymous: anonymous
diff --git a/config/schema/marketo_ma.settings.schema.yml b/config/schema/marketo_ma.settings.schema.yml
new file mode 100644
index 0000000..5ba17b6
--- /dev/null
+++ b/config/schema/marketo_ma.settings.schema.yml
@@ -0,0 +1,65 @@
+# Schema for the marketo_ma.settigns configuration.
+marketo_ma.settings:
+  type: config_object
+  label: 'Marketo MA configuration'
+  mapping:
+    instance_host:
+      type: string
+      label: 'Marketo Instance Host'
+    logging:
+      type: integer
+    tracking_method:
+      type: string
+    munchkin:
+      type: config_object
+      label: 'Munchkin Javascript API'
+      mapping:
+        javascript_library:
+          type: string
+        api_private_key:
+          type: string
+        partition:
+          type: string
+        lead_source:
+          type: string
+        account_id:
+          type: string
+    rest:
+      type: config_object
+      label: 'REST API config'
+      mapping:
+        batch_requests:
+          type: integer
+        client_id:
+          type: string
+        client_secret:
+          type: string
+    field:
+      type: config_object
+      label: 'Field Definition config'
+      mapping:
+        defined_fields:
+          type: sequence
+          label: 'Marketo defined fields'
+          sequence:
+            type: string
+            label: 'Field Value'
+    tracking:
+      type: config_object
+      label: 'Field Definition config'
+      mapping:
+        request_path:
+          type: config_object
+          mapping:
+            negate:
+              type: integer
+              label: 'Negate page paths'
+            pages:
+              type: string
+              label: 'Pages'
+        roles:
+          type: sequence
+          label: 'Roles'
+          sequence:
+            type: string
+            label: 'Role ID'
diff --git a/includes/marketo_ma.admin.inc b/includes/marketo_ma.admin.inc
deleted file mode 100644
index fe8fe4e..0000000
--- a/includes/marketo_ma.admin.inc
+++ /dev/null
@@ -1,311 +0,0 @@
-<?php
-
-/**
- * @file
- * Administrative page callbacks for the Marketo MA module.
- */
-
-/**
- * Builds and returns the Marketo settings form.
- */
-function marketo_ma_admin_settings_form($form, &$form_state) {
-  $form['marketo_ma_basic'] = array(
-    '#title' => t('Basic Settings'),
-    '#type' => 'fieldset',
-  );
-
-  $form['marketo_ma_basic']['marketo_ma_munchkin_account_id'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Account ID'),
-    '#default_value' => variable_get('marketo_ma_munchkin_account_id', ''),
-    '#required' => TRUE,
-  );
-  $form['marketo_ma_basic']['marketo_ma_munchkin_javascript_library'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Munchkin Javascript Library'),
-    '#default_value' => variable_get('marketo_ma_munchkin_javascript_library', '//munchkin.marketo.net/munchkin.js'),
-    '#required' => TRUE,
-    '#description' => t('Typically this does not need to be changed and should use the default value //munchkin.marketo.net/munchkin.js'),
-  );
-  $form['marketo_ma_basic']['marketo_ma_instance_host'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Marketo Instance Host'),
-    '#default_value' => variable_get('marketo_ma_instance_host'),
-    '#required' => FALSE,
-    '#description' => t('Host for your Marketo instance. Example: app-sjqe.marketo.com'),
-  );
-  $form['marketo_ma_basic']['marketo_ma_munchkin_lead_source'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Default Lead Source'),
-    '#default_value' => variable_get('marketo_ma_munchkin_lead_source'),
-    '#description' => t('If set, LeadSource will be set to this value unless specifically overridden during data collection.'),
-  );
-  $form['marketo_ma_basic']['marketo_ma_logging'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Verbose Logging'),
-    '#default_value' => variable_get('marketo_ma_logging', FALSE),
-    '#description' => t('If checked, additional data will be added to watchdog.'),
-  );
-
-  $form['marketo_ma_tabs'] = array(
-    '#type' => 'vertical_tabs',
-  );
-
-  $form['marketo_ma_tabs']['marketo_ma_api'] = array(
-    '#title' => t('API Configuration'),
-    '#type' => 'fieldset',
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_tracking_method'] = array(
-    '#title' => t('Tracking Method'),
-    '#type' => 'radios',
-    '#options' => array(
-      'munchkin' => t('Munchkin Javascript API'),
-      'soap' => t('SOAP API (Synchronous)'),
-      'soap_async' => t('SOAP API (Asynchronous)'),
-    ),
-    '#default_value' => variable_get('marketo_ma_tracking_method', MARKETO_MA_TRACKING_METHOD_DEFAULT),
-    '#required' => TRUE,
-    '#descritpion' => t('Asynchronous will queue requests and send data when cron runs.'),
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_munchkin'] = array(
-    '#title' => t('Munchkin Javascript API'),
-    '#type' => 'fieldset',
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_munchkin']['marketo_ma_munchkin_api_private_key'] = array(
-    '#type' => 'textfield',
-    '#title' => t('API Private Key'),
-    '#default_value' => variable_get('marketo_ma_munchkin_api_private_key', ''),
-    '#required' => TRUE,
-    '#description' => t('Value can be found on the Munchkin Admin page at Admin > Integration > Munchkin'),
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_munchkin']['marketo_ma_munchkin_partition'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Workspace (Partition)'),
-    '#default_value' => variable_get('marketo_ma_munchkin_partition', ''),
-    '#required' => FALSE,
-    '#description' => t('Value can be found on the Munchkin Admin page at Admin > Integration > Munchkin'),
-  );
-
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap'] = array(
-    '#title' => t('SOAP API'),
-    '#type' => 'fieldset',
-    '#description' => t('Values can be found on the SOAP API Admin page at Admin > Integration > SOAP API'),
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_endpoint'] = array(
-    '#type' => 'textfield',
-    '#title' => t('SOAP endpoint'),
-    '#default_value' => variable_get('marketo_ma_soap_endpoint', ''),
-    '#required' => FALSE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_user_id'] = array(
-    '#type' => 'textfield',
-    '#title' => t('User ID'),
-    '#default_value' => variable_get('marketo_ma_soap_user_id', ''),
-    '#required' => FALSE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_encryption_key'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Encryption Key'),
-    '#default_value' => variable_get('marketo_ma_soap_encryption_key', ''),
-    '#required' => FALSE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_proxy'] = array(
-    '#title' => t('SoapClient Proxy Settings'),
-    '#type' => 'fieldset',
-    '#description' => t('For making an HTTP connection through a proxy server, the options proxy_host, proxy_port, proxy_login and proxy_password are also available.'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_proxy']['marketo_ma_soap_proxy_host'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Proxy Host'),
-    '#default_value' => variable_get('marketo_ma_soap_proxy_host', ''),
-    '#required' => FALSE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_proxy']['marketo_ma_soap_proxy_port'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Proxy Port'),
-    '#default_value' => variable_get('marketo_ma_soap_proxy_port', ''),
-    '#required' => FALSE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_proxy']['marketo_ma_soap_proxy_login'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Proxy Login'),
-    '#default_value' => variable_get('marketo_ma_soap_proxy_login', ''),
-    '#required' => FALSE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_api']['marketo_ma_soap']['marketo_ma_soap_proxy']['marketo_ma_soap_proxy_password'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Proxy Password'),
-    '#default_value' => variable_get('marketo_ma_soap_proxy_password', ''),
-    '#required' => FALSE,
-  );
-
-  $form['marketo_ma_tabs']['marketo_ma_fields'] = array(
-    '#title' => t('Field Definition'),
-    '#type' => 'fieldset',
-    '#description' => t('The fields defined here will be available for mapping to Webform and User Profile fields'),
-  );
-  $form['marketo_ma_tabs']['marketo_ma_fields']['marketo_ma_webform_fields'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Marketo fields'),
-    '#description' => t('Pipe "|" delimited strings of [API Name]|[Friendly Label]. Enter one field per line. This information can be found in the Marketo admin page at Admin > Field Management > Export Field Names.<p>Once SOAP API settings have been configured, these fields can be automatically obtained from Marketo using the button below</p>'),
-    '#rows' => 10,
-    '#prefix' => '<div id="marketo-webform-fields-wrapper">',
-    '#suffix' => '</div>',
-  );
-
-  if (isset($form_state['triggering_element']['#array_parents']) && in_array('marketo_ma_webform_fields_soap', $form_state['triggering_element']['#array_parents'])) {
-    $data = _marketo_ma_get_fields();
-    $mwf_default = "";
-    foreach ($data as $key => $value) {
-      $mwf_default .= $key . "|" . $value['displayName'] . "\n";
-    }
-    $form['marketo_ma_tabs']['marketo_ma_fields']['marketo_ma_webform_fields']['#value'] = $mwf_default;
-  }
-  else {
-    $form['marketo_ma_tabs']['marketo_ma_fields']['marketo_ma_webform_fields']['#default_value'] = variable_get('marketo_ma_webform_fields', MARKETO_MA_WEBFORM_FIELD_DEFAULTS);
-  }
-
-  $form['marketo_ma_tabs']['marketo_ma_fields']['marketo_ma_webform_fields_soap'] = array(
-    '#type' => 'button',
-    '#value' => t('Retrieve from Marketo'),
-    '#ajax' => array(
-      'callback' => '_marketo_ma_webform_fields_soap_callback',
-      'wrapper' => 'marketo-webform-fields-wrapper',
-    ),
-  );
-  // If SOAP settings have not been configured, the button should be disabled.
-  if (!_marketo_ma_soap_is_configured()) {
-    $form['marketo_ma_tabs']['marketo_ma_fields']['marketo_ma_webform_fields_soap']['#disabled'] = TRUE;
-  }
-
-  $form['marketo_ma_tabs']['marketo_ma_page_vis'] = array(
-    '#title' => t('Page Visibility'),
-    '#type' => 'fieldset',
-  );
-  $form['marketo_ma_tabs']['marketo_ma_page_vis']['marketo_ma_visibility_pages'] = array(
-    '#title' => t('Add tracking to specific pages'),
-    '#type' => 'radios',
-    '#options' => array(
-      t('All pages except those listed below'),
-      t('Only the pages listed below'),
-    ),
-    '#default_value' => variable_get('marketo_ma_visibility_pages', 0),
-    '#required' => TRUE,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_page_vis']['marketo_ma_pages'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Pages'),
-    '#title_display' => 'invisible',
-    '#default_value' => variable_get('marketo_ma_pages', MARKETO_MA_PAGES),
-    '#description' => t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array(
-      '%blog' => 'blog',
-      '%blog-wildcard' => 'blog/*',
-      '%front' => '<front>')
-    ),
-    '#rows' => 10,
-  );
-
-  $form['marketo_ma_tabs']['marketo_ma_role_vis'] = array(
-    '#title' => t('Role Visibility'),
-    '#type' => 'fieldset',
-  );
-  $form['marketo_ma_tabs']['marketo_ma_role_vis']['marketo_ma_visibility_roles'] = array(
-    '#title' => t('Add tracking to specific roles'),
-    '#type' => 'radios',
-    '#options' => array(
-      1 => t('All roles except those selected below'),
-      0 => t('Only the roles selected below'),
-    ),
-    '#default_value' => variable_get('marketo_ma_visibility_roles', 1),
-    '#required' => TRUE,
-  );
-  $role_options = array_map('check_plain', user_roles());
-  $form['marketo_ma_tabs']['marketo_ma_role_vis']['marketo_ma_roles'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Roles'),
-    '#default_value' => variable_get('marketo_ma_roles', array()),
-    '#options' => $role_options,
-  );
-
-  return system_settings_form($form);
-}
-
-/**
- * Implements hook_form_validate().
- */
-function marketo_ma_admin_settings_form_validate($form, &$form_state) {
-  // If using a soap option attempt validation.
-  if (
-    $form_state['values']['marketo_ma_tracking_method'] == 'soap' ||
-    $form_state['values']['marketo_ma_tracking_method'] == 'soap_async'
-  ) {
-    $form_state['values']['marketo_ma_soap_endpoint'] = trim($form_state['values']['marketo_ma_soap_endpoint']);
-    $form_state['values']['marketo_ma_soap_user_id'] = trim($form_state['values']['marketo_ma_soap_user_id']);
-    $form_state['values']['marketo_ma_soap_encryption_key'] = trim($form_state['values']['marketo_ma_soap_encryption_key']);
-
-    $proxy = array();
-    $proxy['proxy_host'] = trim($form_state['values']['marketo_ma_soap_proxy_host']);
-    $proxy['proxy_port'] = trim($form_state['values']['marketo_ma_soap_proxy_port']);
-    $proxy['proxy_login'] = trim($form_state['values']['marketo_ma_soap_proxy_login']);
-    $proxy['proxy_password'] = trim($form_state['values']['marketo_ma_soap_proxy_password']);
-
-    if (empty($proxy['proxy_host'])) {
-      unset($proxy['proxy_host']);
-    }
-    if (empty($proxy['proxy_port'])) {
-      unset($proxy['proxy_port']);
-    }
-    if (empty($proxy['proxy_login'])) {
-      unset($proxy['proxy_login']);
-    }
-    if (empty($proxy['proxy_password'])) {
-      unset($proxy['proxy_password']);
-    }
-
-    $soap_is_valid = _marketo_ma_validate_soap_configuration(
-      $form_state['values']['marketo_ma_soap_user_id'], $form_state['values']['marketo_ma_soap_encryption_key'], $form_state['values']['marketo_ma_soap_endpoint'], $proxy
-    );
-    if (!$soap_is_valid) {
-      form_set_error('marketo_ma_soap_endpoint', 'Unable to validate SOAP API settings.');
-      form_set_error('marketo_ma_soap_user_id');
-      form_set_error('marketo_ma_soap_encryption_key');
-    }
-  }
-}
-
-/**
- * Performs validation for Marketo SOAP API settings.
- * 
- * @param string $access_key
- *   Access Key
- * @param string $secret_key
- *   Secret Key
- * @param string $soap_endpoint
- *   Endpoint
- * 
- * @return bool
- *   Returns TRUE if SOAP settings are validated successfully
- */
-function _marketo_ma_validate_soap_configuration($access_key, $secret_key, $soap_endpoint, $proxy) {
-  module_load_include('inc', 'marketo_ma', 'includes/marketo_ma.soap');
-  try {
-    $client = new MarketoClient($access_key, $secret_key, $soap_endpoint, $proxy);
-    $result = $client->getFields();
-    if (!$result) {
-      $result = FALSE;
-    }
-  }
-  catch (Exception $e) {
-    $result = FALSE;
-  }
-  return $result;
-}
-
-/**
- * Callback for updating webform fields.
- */
-function _marketo_ma_webform_fields_soap_callback($form, &$form_state) {
-  return $form['marketo_ma_tabs']['marketo_ma_fields']['marketo_ma_webform_fields'];
-}
diff --git a/includes/marketo_ma.soap.inc b/includes/marketo_ma.soap.inc
deleted file mode 100644
index 6d6c9e6..0000000
--- a/includes/marketo_ma.soap.inc
+++ /dev/null
@@ -1,425 +0,0 @@
-<?php
-
-/**
- * @file
- * Class and methods for interacting with the Marketo SOAP API.
- */
-
-/**
- * Marketo Interface
- */
-interface MarketoInterface {
-
-  /**
-   * Retrieves list of defined fields.
-   */
-  public function getFields();
-
-  /**
-   * Retrieves lead information.
-   * 
-   * @param string $key
-   *   Lead Key, typically email address
-   * @param string $type
-   *   Lead key type, auto-detection attempted if not supplied
-   */
-  public function getLead($key, $type);
-
-  /**
-   * Retrieves lead activity information.
-   * 
-   * @param string $key
-   *   Lead Key, typically email address
-   * @param string $type
-   *   Lead key type, auto-detection attempted if not supplied
-   */
-  public function getLeadActivity($key, $type);
-
-  /**
-   * Inserts or updates a lead.
-   * 
-   * @param array $lead
-   *   Key value pairs to be associated with a lead
-   * @param string $key
-   *   Lead Key, typically email address
-   * @param object $cookie
-   *   Marketo cookie information
-   * @param array $options
-   *   Array of additional options to configure lead syncing
-   */
-  public function syncLead($lead, $key, $cookie, $options);
-}
-
-/**
- * Marketo Client
- */
-class MarketoClient implements MarketoInterface {
-
-  // http://app.marketo.com/soap/mktows/2_0?WSDL
-  protected $userId;
-  protected $encryptionKey;
-  protected $endpoint;
-
-  /**
-   * Constructor for MarketoClient class.
-   * 
-   * @param string $user_id
-   *   A User ID (aka Access Key) defined in Marketo admin
-   * @param string $encryption_key
-   *   A shared secret-key (aka Encryption Key) defined in Marketo admin
-   * @param string $endpoint
-   *   SOAP endpoint URL
-   * @param array $options
-   *   Optional array of SoapClient options 
-   */
-  public function __construct($user_id, $encryption_key, $endpoint, $options = array()) {
-    $this->userId = $user_id;
-    $this->encryptionKey = $encryption_key;
-    $this->endpoint = $endpoint;
-
-    $wsdl = $this->endpoint . '?WSDL';
-
-    $default_options = array(
-      "connection_timeout" => 20,
-      "location" => $this->endpoint,
-    );
-    $soap_options = array_merge($default_options, $options);
-
-    $this->client = new SoapClient($wsdl, $soap_options);
-  }
-
-  /**
-   * Builds Marketo authentication header.
-   * 
-   * @return \SoapHeader
-   *   Marketo authentication header
-   */
-  protected function authenticationHeader() {
-    $timestamp = date("c");
-    $encrypt_string = $timestamp . $this->userId;
-    $signature = hash_hmac('sha1', $encrypt_string, $this->encryptionKey);
-
-    $data = new stdClass();
-    $data->mktowsUserId = $this->userId;
-    $data->requestSignature = $signature;
-    $data->requestTimestamp = $timestamp;
-
-    $header = new SoapHeader('http://www.marketo.com/mktows/', 'AuthenticationHeader', $data);
-
-    return $header;
-  }
-
-  /**
-   * Determines lead key type for a given key.
-   * 
-   * @param string $key
-   *   The key to examine
-   * 
-   * @return string
-   *   Lead key type
-   */
-  protected function keyType($key) {
-    if (filter_var($key, FILTER_VALIDATE_EMAIL)) {
-      $type = 'EMAIL';
-    }
-    elseif (is_int($key) || (is_string($key) && ctype_digit($key))) {
-      $type = 'IDNUM';
-    }
-    elseif (filter_var($key, FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => '/^id:.*&token:.*/')))) {
-      $type = 'COOKIE';
-    }
-    else {
-      $type = 'UNKNOWN';
-    }
-
-    return $type;
-  }
-
-  /**
-   * Sends request to Marketo.
-   * 
-   * @param string $operation
-   *   The operation to execute
-   * @param array $params
-   *   Parameters to be sent with the request
-   * 
-   * @return object
-   *   Response object
-   */
-  protected function request($operation, $params) {
-    return $this->client->__soapCall(
-        $operation, array($params), array(), $this->authenticationHeader()
-    );
-  }
-
-  /**
-   * Returns the XML sent in the last SOAP request.
-   * 
-   * @return string
-   */
-  public function __getLastRequest() {
-    return $this->client->__getLastRequest();
-  }
-
-  /**
-   * Returns the XML received in the last SOAP response.
-   *
-   * @return string
-   */
-  public function __getLastResponse() {
-    return $this->client->__getLastResponse();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getLead($key, $type = NULL) {
-    $lead = new stdClass();
-    $lead->leadKey = new stdClass();
-    $lead->leadKey->keyType = (is_null($type)) ? $this->keyType($key) : strtoupper($type);
-    $lead->leadKey->keyValue = $key;
-
-    try {
-      $result = $this->request('getLead', $lead);
-      $leads = $this->prepareLeadResults($result);
-    }
-    catch (Exception $e) {
-
-      if (isset($e->detail->serviceException->code) && $e->detail->serviceException->code == MarketoError::ERR_LEAD_NOT_FOUND) {
-        // No leads were found.
-        $leads = array();
-      }
-      else {
-        throw new Exception($e);
-      }
-    }
-
-    return $leads;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getLeadActivity($key, $type = NULL) {
-    $lead = new stdClass();
-    $lead->leadKey = new stdClass();
-    $lead->leadKey->keyType = (is_null($type)) ? $this->keyType($key) : strtoupper($type);
-    $lead->leadKey->keyValue = $key;
-    $lead->activityFilter = new stdClass();
-    $lead->startPosition = new stdClass();
-    $lead->batchSize = 100;
-
-    try {
-      $result = $this->request('getLeadActivity', $lead);
-      $activity = $this->prepareLeadActivityResults($result);
-    }
-    catch (Exception $e) {
-
-      if (isset($e->detail->serviceException->code) && $e->detail->serviceException->code == MarketoError::ERR_LEAD_NOT_FOUND) {
-        // No leads were found.
-        $activity = array();
-      }
-      else {
-        throw new Exception($e);
-      }
-    }
-
-    return $activity;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFields() {
-    $params = array(
-      'objectName' => 'LeadRecord',
-    );
-    try {
-      $result = $this->request('describeMObject', $params);
-      $fields = $this->prepareFieldResults($result);
-    }
-    catch (Exception $e) {
-      $fields = array();
-    }
-
-    return $fields;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function syncLead($lead, $key = NULL, $cookie = NULL, $options = array()) {
-    // Merge default options.
-    $options += array(
-      'returnLead' => FALSE,
-      'extendedResponse' => FALSE,
-    );
-
-    $params = new stdClass();
-    $params->marketoCookie = $cookie;
-    $params->returnLead = $options['returnLead'];
-    $params->leadRecord = $this->leadRecord($lead, $key);
-    $ret = array(
-      'success' => FALSE,
-      'result' => NULL,
-    );
-
-    try {
-      $ret['result'] = $this->request('syncLead', $params);
-      $ret['success'] = TRUE;
-    }
-    catch (Exception $e) {
-      $ret['result'] = $e->getMessage();
-      $ret['success'] = FALSE;
-    }
-    return ($options['extendedResponse']) ? $ret : $ret['result'];
-  }
-
-  /**
-   * Converts response into a more useful structure.
-   * 
-   * @param object $data
-   *   LeadRecord object definition
-   * 
-   * @return array
-   *   Key value pairs of fields
-   */
-  protected function prepareFieldResults($data) {
-    $fields = array();
-
-    foreach ($data->result->metadata->fieldList->field as $field) {
-      $fields[$field->name] = (array) $field;
-    }
-
-    return $fields;
-  }
-
-  /**
-   * Creates proper structure for submitting a lead.
-   * 
-   * @param array $attributes
-   *   Key value pairs to be associated withi this lead
-   * @param string $key
-   *   Lead Key
-   * 
-   * @return \stdClass
-   *   Lead Record
-   */
-  protected function leadRecord($attributes, $key = NULL) {
-    $record = new stdClass();
-
-    if ($key) {
-      if (is_numeric($key)) {
-        $record->Id = $key;
-      }
-      else {
-        $record->Email = $key;
-      }
-    }
-
-    $record->leadAttributeList = new stdClass();
-    $record->leadAttributeList->attribute = array();
-
-    foreach ($attributes as $attribute => $value) {
-      $lead_attribute = new stdClass();
-      $lead_attribute->attrName = $attribute;
-      $lead_attribute->attrValue = $value;
-
-      array_push($record->leadAttributeList->attribute, $lead_attribute);
-    }
-
-    return $record;
-  }
-
-  /**
-   * Parses lead results into a more useful format.
-   * 
-   * @param object $marketo_result
-   *   SOAP response
-   * 
-   * @return array
-   *   An array of objects defining lead data
-   */
-  protected function prepareLeadResults($marketo_result) {
-    if ($marketo_result->result->count == 1) {
-      $leads[] = $marketo_result->result->leadRecordList->leadRecord;
-    }
-    elseif ($marketo_result->result->count > 1) {
-      $leads = $marketo_result->result->leadRecordList->leadRecord;
-    }
-    else {
-      $leads = array();
-    }
-
-    foreach ($leads as &$lead) {
-      $lead->attributes = array();
-      foreach ($lead->leadAttributeList->attribute as $attribute) {
-        $lead->attributes[$attribute->attrName] = $attribute->attrValue;
-      }
-      unset($lead->leadAttributeList);
-    }
-
-    return $leads;
-  }
-
-  /**
-   * Parses lead activity results into a more useful format.
-   * 
-   * @param object $marketo_result
-   *   SOAP response
-   * 
-   * @return array
-   *   An array of objects defining lead activity data
-   */
-  protected function prepareLeadActivityResults($marketo_result) {
-    if ($marketo_result->leadActivityList->returnCount > 1) {
-      $activity = $marketo_result->leadActivityList->activityRecordList->activityRecord;
-    }
-    elseif ($marketo_result->leadActivityList->returnCount == 1) {
-      $activity[] = $marketo_result->leadActivityList->activityRecordList->activityRecord;
-    }
-    else {
-      $activity = array();
-    }
-
-    foreach ($activity as &$event) {
-      $event->attributes = array();
-      foreach ($event->activityAttributes->attribute as $attribute) {
-        $event->attributes[$attribute->attrName] = $attribute->attrValue;
-      }
-      unset($event->activityAttributes);
-    }
-
-    return $activity;
-  }
-
-}
-
-class MarketoError {
-
-  const ERR_SEVERE_INTERNAL_ERROR = 10001;
-  const ERR_INTERNAL_ERROR = 20011;
-  const ERR_REQUEST_NOT_UNDERSTOOD = 20012;
-  const ERR_ACCESS_DENIED = 20013;
-  const ERR_AUTH_FAILED = 20014;
-  const ERR_REQUEST_LIMIT_EXCEEDED = 20015;
-  const ERR_REQ_EXPIRED = 20016;
-  const ERR_INVALID_REQ = 20017;
-  const ERR_BAD_ENCODING = 20018;
-  const ERR_UNSUPPORTED_OP = 20019;
-  const ERR_LEAD_KEY_REQ = 20101;
-  const ERR_LEAD_KEY_BAD = 20102;
-  const ERR_LEAD_NOT_FOUND = 20103;
-  const ERR_LEAD_DETAIL_REQ = 20104;
-  const ERR_LEAD_ATTRIB_BAD = 20105;
-  const ERR_LEAD_SYNC_FAILED = 20106;
-  const ERR_ACTIVITY_KEY_BAD = 20107;
-  const ERR_PARAMETER_REQ = 20109;
-  const ERR_PARAMETER_BAD = 20110;
-  const ERR_LIST_NOT_FOUND = 20111;
-  const ERR_CAMP_NOT_FOUND = 20113;
-  const ERR_BAD_PARAMETER = 20114;
-  const ERR_BAD_STREAM_POS = 20122;
-  const ERR_STREAM_AT_END = 20123;
-
-}
diff --git a/js/marketo_ma.js b/js/marketo_ma.js
index 055cd8b..6c76e20 100644
--- a/js/marketo_ma.js
+++ b/js/marketo_ma.js
@@ -13,12 +13,14 @@
                 Drupal.behaviors.marketo_ma.marketoMunchkinFunction(this.action, this.data, this.hash);
               });
             }
+            // Set the marketo status to completed.
+            settings.marketo_ma.action_status = 'completed';
           }
         });
       }
     },
-    marketoMunchkinFunction: function(leadType, data, hash) {
-      mktoMunchkinFunction(leadType, data, hash);
+    marketoMunchkinFunction: function(actionType, data, hash) {
+      Munchkin.munchkinFunction(actionType, data, hash);
     }
   };
 
diff --git a/marketo_ma.api.php b/marketo_ma.api.php
index d5101cd..af96a6c 100644
--- a/marketo_ma.api.php
+++ b/marketo_ma.api.php
@@ -2,6 +2,8 @@
 /**
  * @file
  * Hooks provided by Marketo MA.
+ *
+ * @todo: Implement these alter hooks.
  */
 
 /**
diff --git a/marketo_ma.drush.inc b/marketo_ma.drush.inc
deleted file mode 100644
index f3b462d..0000000
--- a/marketo_ma.drush.inc
+++ /dev/null
@@ -1,261 +0,0 @@
-<?php
-
-/**
- * @file
- * Drush integration for Marketo MA
- */
-
-/**
- * Implements hook_drush_command().
- */
-function marketo_ma_drush_command() {
-  $items = array();
-  $items['mma-get-lead'] = array(
-    'description' => dt('Get Marketo lead and activity information for the given search criteria.'),
-    'arguments' => array(
-      'key' => dt('An email address, Marketo lead ID, Marketo _mkto_trk cookie, Drupal uid, or Drupal name'),
-    ),
-    'examples' => array(
-      'drush mma-get-lead john@example.com' => dt('Get leads with email address john@example.com.'),
-      'drush mmal john@example.com --activity' => dt('Get leads with email address john@example.com and include activity records.'),
-      'drush mmal john@example.com --activity --info=0' => dt('Show only activity for john@example.com'),
-      'drush mmal john@example.com --local' => dt('Get leads with email address john@example.com that also have a Drupal user account.'),
-      'drush mma-get-lead 14' => dt('Get leads for Marketo lead ID 14 or Drupal uid 14 and return merged results.'),
-      'drush mmal 14 --local' => dt('Get leads for Drupal uid 14 and ignores Marketo Lead ID 14.'),
-      'drush mmal 14 --remote' => dt('Get lead for Marketo lead ID 14 and ignores Drupal uid 14'),
-      'drush mmal "id:222-AAA-222&token:_mch-example.com-1384222270733-22222"' => dt('Get lead by _mkto_trk value.'),
-    ),
-    'options' => array(
-      'activity' => dt('Show lead activity (OFF by default).'),
-      'info' => dt('Show lead information (ON by default). Hide with --info=0'),
-      'local' => dt('Search only Drupal keys (mail, uid, name).'),
-      'remote' => dt('Search only Marketo keys (mail, lead ID, cookie)'),
-    ),
-    'aliases' => array('mmal'),
-  );
-  $items['mma-fields'] = array(
-    'description' => dt('Get Marketo fields.'),
-    'aliases' => array('mmaf'),
-    'outputformat' => array(
-      'default' => 'table',
-      'pipe-format' => 'list',
-      'field-labels' => array(
-        'name' => 'Name',
-        'description' => 'Description',
-        'displayName' => 'Label',
-        'sourceObject' => 'sourceObject',
-        'dataType' => 'dataType',
-        'size' => 'size',
-        'isReadonly' => 'isReadonly',
-        'isUpdateBlocked' => 'isUpdateBlocked',
-        'isName' => 'isName',
-        'isPrimaryKey' => 'isPrimaryKey',
-        'isCustom' => 'isCustom',
-        'isDynamic' => 'isDynamic',
-        'dynamicFieldRef' => 'dynamicFieldRef',
-        'updatedAt' => 'updatedAt',
-      ),
-      'fields-default' => array('name', 'displayName'),
-      'output-data-type' => 'format-table',
-    ),
-  );
-  $items['mma-verify'] = array(
-    'description' => dt('Verify this site can connect to Marketo.'),
-    'aliases' => array('mmav'),
-  );
-  return $items;
-}
-
-/**
- * Callback function for mma-get-lead.
- *
- * @param string $key
- */
-function drush_marketo_ma_mma_get_lead($key = NULL) {
-  if (is_null($key)) {
-    drush_log('No key provided', 'error');
-    return;
-  }
-
-  $local = (bool) drush_get_option('local', FALSE);
-  $remote = (bool) drush_get_option('remote', FALSE);
-  if ($local && $remote) {
-    drush_log('--local and --remote cannot be used together', 'error');
-    return;
-  }
-
-  if ($remote) {
-    $scope = 'marketo';
-  }
-  elseif ($local) {
-    $scope = 'drupal';
-  }
-  else {
-    $scope = strtolower(drush_get_option('scope', 'all'));
-  }
-
-  $info = (bool) drush_get_option('info', TRUE);
-  $activity = (bool) drush_get_option('activity', FALSE);
-
-  $finds = array();
-
-  switch ($scope) {
-    case 'all':
-      $finds = _marketo_ma_search_drupal_lead($key, $activity) + _marketo_ma_search_marketo_lead($key, $activity);
-      break;
-
-    case 'drupal':
-      $finds = _marketo_ma_search_drupal_lead($key, $activity);
-      break;
-
-    case 'marketo':
-      $finds = _marketo_ma_search_marketo_lead($key, $activity);
-      break;
-
-    default:
-      drush_log('Valid scope options are: all, drupal, marketo', 'error');
-      return;
-  }
-
-  switch (count($finds)) {
-    case 0:
-      drush_log("No leads found for $key", 'warning');
-      break;
-
-    case 1:
-      drush_log("Lead found for $key: [" . implode(",", array_keys($finds)) . "]", 'success');
-      break;
-
-    default:
-      drush_log("Multiple leads found for $key: [" . implode(",", array_keys($finds)) . "]", 'success');
-      break;
-  }
-
-  foreach ($finds as $leadid => $find) {
-    if ($info) {
-      drush_print('Marketo lead ID ' . $find['marketo']->Id);
-      $rows = array();
-      $rows[] = array(dt('Group'), dt('Key'), dt('Value'));
-      if ($find['drupal']->uid) {
-        $rows[] = array(dt('Account'), 'uid', $find['drupal']->uid);
-        $rows[] = array(dt('Account'), 'name', $find['drupal']->name);
-        $rows[] = array(dt('Account'), 'mail', $find['drupal']->mail);
-      }
-      $rows[] = array(dt('Marketo'), 'Id', $find['marketo']->Id);
-      $rows[] = array(dt('Marketo'), 'Email', $find['marketo']->Email);
-      // Process Marketo attributes.
-      foreach ($find['marketo']->attributes as $attr => $value) {
-        $rows[] = array(dt('Marketo'), $attr, $value);
-      }
-      drush_print_table($rows, TRUE);
-    }
-
-    if ($activity && array_key_exists('activity', $find)) {
-      if (count($find['activity'])) {
-        drush_print('Activity for lead ID ' . $find['marketo']->Id);
-        _marketo_ma_print_activity_($find['activity']);
-      }
-      else {
-        drush_print('No activity found for lead ID ' . $find['marketo']->Id);
-      }
-    }
-  }
-
-  return;
-}
-
-/**
- * Callback function for mma-fields.
- */
-function drush_marketo_ma_mma_fields() {
-  $fields = _marketo_ma_get_fields();
-  foreach ($fields as $key => $value) {
-    $rows[$value['name']] = $value;
-  }
-  return $rows;
-}
-
-/**
- * Callback function for mma-verify.
- */
-function drush_marketo_ma_mma_verify() {
-  $fields = _marketo_ma_get_fields();
-  if (count($fields) > 0) {
-    drush_log('Successfully connected to Marketo', 'success');
-  }
-  else {
-    drush_log('Unable to connect to Marketo', 'error');
-  }
-}
-
-/**
- * Get uid(s) from a uid, user name, or email address.
- * 
- * @param string $search
- *   A uid, user name, or email address
- * 
- * @return array
- *   Associative array or records found keyed on uid
- */
-function _marketo_ma_get_uid($search) {
-  // We use a DB query while looking for the uid to keep things speedy.
-  $uids = array();
-  if (is_numeric($search)) {
-    $query = db_query("SELECT uid, name, mail FROM {users} WHERE uid = :uid OR name = :name", array(':uid' => $search, ':name' => $search));
-  }
-  else {
-    $query = db_query("SELECT uid, name, mail FROM {users} WHERE mail = :mail OR name = :name", array(':mail' => $search, ':name' => $search));
-  }
-  return $query->fetchAllAssoc('uid');
-}
-
-function _marketo_ma_search_marketo_lead($key, $activity = FALSE) {
-  $result = array();
-  $marketo = marketo_ma_get_lead($key); // Can return multiple.
-  foreach ($marketo as $lead) {
-    $account = user_load_by_mail($lead->Email); // Not sure about multiple.
-    $result[$lead->Id] = array(
-      'marketo' => $lead,
-      'drupal' => $account,
-    );
-    if ($activity) {
-      $result[$lead->Id]['activity'] = marketo_ma_get_lead_activity($lead->Id);
-    }
-  }
-
-  return $result;
-}
-
-function _marketo_ma_search_drupal_lead($key, $activity = FALSE) {
-  $result = array();
-  $uids = _marketo_ma_get_uid($key); // Can return multiple.
-  foreach ($uids as $uid => $account) {
-    $marketo = marketo_ma_get_lead($account->mail);
-    foreach ($marketo as $lead) {
-      $result[$lead->Id] = array(
-        'marketo' => $lead,
-        'drupal' => $account,
-      );
-      if ($account) {
-        $result[$lead->Id]['activity'] = marketo_ma_get_lead_activity($lead->Id);
-      }
-    }
-  }
-
-  return $result;
-}
-
-function _marketo_ma_print_activity_($activity) {
-  $fields = array('id', 'activityDateTime', 'activityType', 'mktgAssetName');
-  $rows = array();
-  $rows[] = array(dt('ID'), dt('Date/Time'), dt('Activity Type'), dt('Asset Name'));
-  foreach ($activity as $event) {
-    $row = array();
-    foreach ($fields as $field) {
-      $row[] = $event->$field;
-    }
-    $rows[] = $row;
-  }
-
-  drush_print_table($rows, TRUE);
-}
diff --git a/marketo_ma.info b/marketo_ma.info
deleted file mode 100644
index 22d7767..0000000
--- a/marketo_ma.info
+++ /dev/null
@@ -1,8 +0,0 @@
-name = Marketo MA
-description = Integration with Marketo marketing automation software.
-core = 7.x
-package = Marketo
-configure = admin/config/search/marketo_ma
-
-files[] = includes/marketo_ma.soap.inc
-scripts[] = js/marketo_ma.js
diff --git a/marketo_ma.info.yml b/marketo_ma.info.yml
new file mode 100644
index 0000000..30c1a00
--- /dev/null
+++ b/marketo_ma.info.yml
@@ -0,0 +1,9 @@
+name: Marketo Ma
+type: module
+description: Integration with Marketo marketing automation software.
+core: 8.x
+package: Marketo
+configure: marketo_ma.settings
+dependencies:
+ - user
+ - encryption
diff --git a/marketo_ma.install b/marketo_ma.install
index 2fcd144..eee3d94 100644
--- a/marketo_ma.install
+++ b/marketo_ma.install
@@ -5,41 +5,84 @@
  * Install hooks for Marketo MA module.
  */
 
-/**
- * Implements hook_uninstall().
- */
-function marketo_ma_uninstall() {
-  variable_del('marketo_ma_instance_host');
-  variable_del('marketo_ma_logging');
-  variable_del('marketo_ma_munchkin_account_id');
-  variable_del('marketo_ma_munchkin_api_private_key');
-  variable_del('marketo_ma_munchkin_javascript_library');
-  variable_del('marketo_ma_munchkin_lead_source');
-  variable_del('marketo_ma_munchkin_partition');
-  variable_del('marketo_ma_munchkin_tracking_code_type');
-  variable_del('marketo_ma_pages');
-  variable_del('marketo_ma_roles');
-  variable_del('marketo_ma_soap_encryption_key');
-  variable_del('marketo_ma_soap_endpoint');
-  variable_del('marketo_ma_soap_proxy_host');
-  variable_del('marketo_ma_soap_proxy_login');
-  variable_del('marketo_ma_soap_proxy_password');
-  variable_del('marketo_ma_soap_proxy_port');
-  variable_del('marketo_ma_soap_user_id');
-  variable_del('marketo_ma_tabs__active_tab');
-  variable_del('marketo_ma_tracking_method');
-  variable_del('marketo_ma_user_fieldmap');
-  variable_del('marketo_ma_user_triggers');
-  variable_del('marketo_ma_visibility_pages');
-  variable_del('marketo_ma_visibility_roles');
-  variable_del('marketo_ma_webform_fields');
-  variable_del('marketo_ma_webform_fields_soap');
-}
+use Drupal\Core\Link;
 
 /**
- * Implements hook_disable().
+ * Implements hook_requirements().
+ *
+ * - Checks the ability to connect to the marketo REST API.
  */
-function marketo_ma_disable() {
-  $queue = DrupalQueue::get('marketo_ma_lead', TRUE);
-  $queue->deleteQueue();
+function marketo_ma_requirements($phase) {
+
+  if ($phase == 'runtime') {
+
+    // Get the Marketo settings.
+    $config = \Drupal::config('marketo_ma.settings');
+
+    // Run Marketo REST tests.
+    if ($config->get('tracking_method') == 'api_client') {
+
+      /** @var \Drupal\marketo_ma\MarketoMaApiClientInterface $marketo_client */
+      $marketo_client = \Drupal::service('marketo_ma.client');
+
+      if (!class_exists('\CSD\Marketo\Client')) {
+        // The composer library is missing.
+        return [
+          'marketo_ma_rest_api_client' => [
+            'description' => t("The Marketo REST api client is required for the Marketo MA module. Please read the module's readme for installation instructions."),
+            'title' => t('Marketo REST client'),
+            'severity' => REQUIREMENT_ERROR,
+            'value' => t('Not found'),
+          ],
+        ];
+      }
+      elseif (!$marketo_client->canConnect()) {
+        // Some required configuration is missing for the Marketo API.
+        return [
+          'marketo_ma_rest_configuration' => [
+            'description' => t('The Marketo REST API was chosen for connection method but some !link seems to be missing.', [
+              '!link' => Link::createFromRoute('configuration', 'marketo_ma.settings')->toString(),
+            ]),
+            'title' => t('Marketo MA configuration.'),
+            'severity' => REQUIREMENT_ERROR,
+            'value' => t('Configuration incomplete.'),
+          ],
+        ];
+      }
+      else {
+        // Get available lead fields from marketo.
+        // @todo: Consider moving this check to the configuration form.
+        try {
+          $fields = $marketo_client->getFields();
+        }
+        catch (\Exception $e) {
+          return [
+            'marketo_ma_rest_connection' => [
+              'description' => t('There was an error connecting to the Marketo REST API. check your api credentials on the !link page: %message', [
+                '%message' => $e->getMessage(),
+                '!link' => Link::createFromRoute('Marketo MA configuration', 'marketo_ma.settings')->toString(),
+              ]),
+              'title' => t('Marketo REST Connection'),
+              'severity' => REQUIREMENT_ERROR,
+              'value' => t('Connection Error'),
+            ],
+          ];
+        }
+        if (!isset($fields)) {
+          // Unable to get a valid response from Marketo for some reason.
+          return [
+            'marketo_ma_rest_connection' => [
+              'description' => t('There was an error connecting to the Marketo REST API. Check your api credentials on the !link page.', [
+                '!link' => Link::createFromRoute('Marketo MA configuration', 'marketo_ma.settings')->toString(),
+              ]),
+              'title' => t('Marketo REST Connection'),
+              'severity' => REQUIREMENT_ERROR,
+              'value' => t('Connection Error'),
+            ],
+          ];
+        }
+      }
+    }
+  }
+  return [];
 }
diff --git a/marketo_ma.libraries.yml b/marketo_ma.libraries.yml
new file mode 100644
index 0000000..5819c8d
--- /dev/null
+++ b/marketo_ma.libraries.yml
@@ -0,0 +1,8 @@
+marketo-ma:
+  version: 1.x
+  js:
+    js/marketo_ma.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
diff --git a/marketo_ma.links.menu.yml b/marketo_ma.links.menu.yml
new file mode 100644
index 0000000..713768f
--- /dev/null
+++ b/marketo_ma.links.menu.yml
@@ -0,0 +1,6 @@
+marketo_ma.settings:
+  title: 'Marketo MA'
+  route_name: marketo_ma.settings
+  description: 'Marketo MA configuration.'
+  parent: system.admin_config_services
+  weight: 99
diff --git a/marketo_ma.module b/marketo_ma.module
index 64d54ba..a6c6ad3 100644
--- a/marketo_ma.module
+++ b/marketo_ma.module
@@ -1,701 +1,33 @@
 <?php
-
 /**
  * @file
- * Module functions for Marketo MA.
- */
-
-define('MARKETO_MA_PAGES', "admin\nadmin/*\nbatch\nnode/add*\nnode/*/*\nuser/*/*");
-define('MARKETO_MA_TRACKING_METHOD_DEFAULT', 'munchkin');
-define('MARKETO_MA_WEBFORM_FIELD_DEFAULTS', "FirstName|First Name\nLastName|Last Name\nEmail|Email Address");
-
-define('MARKETO_MA_SCHEMA_WEBFORM', 'marketo_ma_webform');
-define('MARKETO_MA_WEBFORM_FIELD_ACTIVE', 'is_active');
-define('MARKETO_MA_WEBFORM_OPTIONS', 'options');
-
-define('MARKETO_MA_SCHEMA_WEBFORM_COMPONENT', 'marketo_ma_webform_component');
-define('MARKETO_MA_WEBFORM_COMPONENT_KEY', 'marketo_ma_key');
-define('MARKETO_MA_WEBFORM_COMPONENT_NONE', 'none');
-
-/**
- * Implements hook_menu().
- */
-function marketo_ma_menu() {
-  // Module settings.
-  $items['admin/config/search/marketo_ma'] = array(
-    'title' => 'Marketo MA',
-    'description' => 'Marketo MA configuration',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('marketo_ma_admin_settings_form'),
-    'access arguments' => array('administer marketo'),
-    'file' => 'includes/marketo_ma.admin.inc',
-  );
-  return $items;
-}
-
-/**
- * Implements hook_permission().
- */
-function marketo_ma_permission() {
-  return array(
-    'administer marketo' => array(
-      'title' => t('Administer Marketo MA'),
-      'description' => t('Configure Marketo settings such as page visibility, Munchkin keys, and SOAP API endpoint'),
-    ),
-  );
-}
-
-/**
- * Implements hook_cron_queue_info().
- */
-function marketo_ma_cron_queue_info() {
-  $queues = array();
-  $queues['marketo_ma_lead'] = array(
-    'worker callback' => '_marketo_ma_associate_lead_soap',
-    'time' => 30,
-  );
-  return $queues;
-}
-
-/**
- * Adds lead information to be captured.
- * 
- * @param string $email
- *   A valid email address for this lead
- * @param array $data
- *   Key value pairs of data to be associated with this lead
- * @param bool $merge
- *   If TRUE, $data will be merged if $email already exists else $data will
- *   be replaced
- * @param array $options
- *   Array of additional options to modify the behavior of lead synchronization
- * 
- * @return bool
- *   Returns TRUE upon completion
- */
-function marketo_ma_add_lead($email, $data = array(), $merge = FALSE, $options = array()) {
-  $options += array(
-    'tracking_method' => 'default',
-  );
-
-  $marketo_ma_data = array();
-  if (isset($_SESSION['marketo_ma_data'])) {
-    $marketo_ma_data = $_SESSION['marketo_ma_data'];
-  }
-
-  if ($merge && array_key_exists($email, $marketo_ma_data)) {
-    $marketo_ma_data[$email]['data'] = array_merge($marketo_ma_data[$email]['data'], $data);
-  }
-  else {
-    $marketo_ma_data[$email]['data'] = $data;
-  }
-  $marketo_ma_data[$email]['email'] = $email;
-  $marketo_ma_data[$email]['options'] = $options;
-
-  // Check to see if there is a default lead source we should apply.
-  $source = variable_get('marketo_ma_munchkin_lead_source');
-  if (!array_key_exists('LeadSource', $marketo_ma_data[$email]['data']) && $source !== '') {
-    $marketo_ma_data[$email]['data']['LeadSource'] = $source;
-  }
-
-  // If the user executing this method is anonymous or is actually the lead
-  // being modified we will pass along the _mkto_trk cookie value so any
-  // activity associated with the cookie gets merged to the lead.
-  global $user;
-  if (($user->uid == 0 || $user->mail == $email) && isset($_COOKIE['_mkto_trk'])) {
-    $marketo_ma_data[$email]['marketoCookie'] = $_COOKIE['_mkto_trk'];
-  }
-
-  // Provide modules an opportunity to modify data.
-  // hook_marketo_ma_lead_alter(&$data)
-  drupal_alter('marketo_ma_lead', $marketo_ma_data[$email]);
-  foreach ($marketo_ma_data[$email]['data'] as $key => $value) {
-    // hook_marketo_ma_lead_firstname_alter(&$data)
-    drupal_alter('marketo_ma_lead_' . $key, $marketo_ma_data[$email]['data'][$key]);
-  }
-
-  $_SESSION['marketo_ma_data'] = $marketo_ma_data;
-  return TRUE;
-}
-
-/**
- * Retrieves queued data.
- */
-function marketo_ma_get_queue() {
-  // Data looks like $queue[email] = array(key => value).
-  return (isset($_SESSION['marketo_ma_data'])) ? $_SESSION['marketo_ma_data'] : array();
-}
-
-/**
- * Cleanup any mess we made or data we don't want to hold on to.
- */
-function _marketo_ma_cleanup() {
-  if (isset($_SESSION['marketo_ma_data'])) {
-    unset($_SESSION['marketo_ma_data']);
-  }
-}
-
-/**
- * Implements hook_page_alter().
+ * Contains marketo_ma.module..
  */
-function marketo_ma_page_alter(&$page) {
-  global $user;
-  $marketo_ma_data = marketo_ma_get_queue();
 
-  if ((_marketo_ma_visibility_pages() && _marketo_ma_visibility_roles($user)) || count($marketo_ma_data) > 0) {
-
-    /*
-     * @todo handle case where visibility = false, count > 0, and
-     * tracking type != munchkin.. we don't need any tracking in this case
-     */
-    // Basic Munchkin tracking.
-    _marketo_ma_output_tracking_code();
-
-    foreach ($marketo_ma_data as $lead) {
-      if (array_key_exists('email', $lead)) {
-        _marketo_ma_associate_lead($lead);
-      }
-    }
-
-    _marketo_ma_cleanup();
-  }
-}
+use Drupal\Core\Routing\RouteMatchInterface;
 
 /**
- * Adds basic tracking code to page.
+ * Implements hook_help().
  */
-function _marketo_ma_output_tracking_code() {
-  if (_marketo_ma_munchkin_is_configured()) {
-    $marketo_ma_munchkin_account_id = variable_get('marketo_ma_munchkin_account_id');
-    $marketo_ma_munchkin_javascript_library = '//' . str_replace(array(
-        'http://',
-        'https://',
-        '//'), '', variable_get('marketo_ma_munchkin_javascript_library'));
-
-    drupal_add_js(array(
-      'marketo_ma' => array(
-        'track' => TRUE,
-        'key' => $marketo_ma_munchkin_account_id,
-        'library' => $marketo_ma_munchkin_javascript_library),
-      ), 'setting');
-  }
-}
+function marketo_ma_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    // Main module help for the marketo_ma module.
+    case 'help.page.marketo_ma':
+      return t('<h3>:about</h3><p>:description</p>', [
+        ':about' => 'About',
+        ':description' => 'Integration with Marketo marketing automation software.',
+      ]);
 
-/**
- * Determines which tracking method should be used.
- * 
- * @param array $lead
- *   An associative array containing lead data
- *   - email: The email address of this lead
- *   - data: An associative array containing marketo fields and their values
- *     - FirstName
- *     - LastName
- *   - marketoCookie: NULL or the value of $_COOKIE['_mkto_trk']
- *   - options: Array of options to modify the behavior of lead synchronization
- *     - tracking_method: 'default' or a string indicating which method to use
- *     - formid: Required when tracking_method is 'forms2'
- */
-function _marketo_ma_associate_lead($lead) {
-  $track = variable_get('marketo_ma_tracking_method');
-  if (isset($lead['options']['tracking_method']) && $lead['options']['tracking_method'] != 'default') {
-    $track = $lead['options']['tracking_method'];
-  }
-  switch ($track) {
-    case 'munchkin':
-      _marketo_ma_associate_lead_munchkin($lead);
-      break;
-
-    case 'forms2':
-      if (isset($lead['options']['formid'])) {
-        _marketo_ma_associate_lead_forms2($lead, $lead['options']['formid']);
-      }
-      break;
-
-    case 'soap_async':
-      _marketo_ma_queue_lead($lead);
-      break;
-
-    case 'soap':
     default:
-      _marketo_ma_associate_lead_soap($lead);
-      break;
   }
 }
 
 /**
- * Adds javascript required to track a lead.
- * 
- * @param array $lead
- *   Lead data
- */
-function _marketo_ma_associate_lead_munchkin($lead) {
-  if (_marketo_ma_munchkin_is_configured()) {
-    $key = variable_get('marketo_ma_munchkin_api_private_key', NULL);
-
-    if (variable_get('marketo_ma_logging', FALSE)) {
-      watchdog('marketo', 'Associating lead !email [@method] <pre>@data</pre>', array(
-        '!email' => $lead['email'],
-        '@method' => 'munchkin',
-        '@data' => print_r($lead['data'], TRUE)), WATCHDOG_INFO);
-    }
-    /*
-     * Using sha1 here rather than sha256 recommended by
-     * http://drupal.org/node/845876 as this is a Marketo requirement.
-     */
-    $hash = hash('sha1', $key . $lead['email']);
-    $action = array(
-      'action' => 'associateLead',
-      'data' => $lead['data'],
-      'hash' => $hash,
-    );
-    drupal_add_js(array('marketo_ma' => array('actions' => array($action))), 'setting');
-  }
-}
-
-/**
- * Captures lead data using the Forms 2.0 endpoint.
- * 
- * Reference: http://developers.marketo.com/documentation/websites/forms-2-0/
+ * Implements hook_page_attachments().
  *
- * @param array $lead
- *   Lead data
- */
-function _marketo_ma_associate_lead_forms2($lead, $formid, $scheme = 'https', $path = '/index.php/leadCapture/save2') {
-  if (_marketo_ma_forms2_is_configured()) {
-    $url = $scheme . '://' . variable_get('marketo_ma_instance_host') . $path;
-
-    $forms2 = array(
-      'formid' => $formid,
-      'formVid' => $formid,
-      'munchkinId' => variable_get('marketo_ma_munchkin_account_id'),
-    );
-    $options = array(
-      'method' => 'POST',
-      'headers' => array('Content-Type' => 'application/x-www-form-urlencoded'),
-      'data' => drupal_http_build_query(array_merge($lead['data'], $forms2)),
-    );
-
-    $response = drupal_http_request($url, $options);
-
-    if ($response->code == 200) {
-      if (variable_get('marketo_ma_logging', FALSE)) {
-        watchdog('marketo', 'Associating lead !email [forms2] <pre>@data</pre>', array(
-          '!email' => $lead['email'],
-          '@data' => print_r($lead['data'], TRUE)), WATCHDOG_INFO);
-      }
-    }
-    else {
-      watchdog('marketo', 'Unable to sync lead !email [forms2] <pre>@error</pre> Request: <pre>@request</pre> Response: <pre>@response</pre>', array(
-          '!email' => $lead['email'],
-          '@error' => $response->error,
-          '@request' => $response->request,
-          '@response' => $response->code . ' ' . $response->status_message,
-          ), WATCHDOG_ERROR);
-    }
-  }
-}
-
-/**
- * Sends lead data using SOAP API.
- * 
- * @param array $lead
- *   Lead data
- * 
- * @throws Exception
+ * Adds the marketo ma javascript to all pages.
  */
-function _marketo_ma_associate_lead_soap($lead) {
-  if (_marketo_ma_soap_is_configured()) {
-    module_load_include('inc', 'marketo_ma', 'includes/marketo_ma.soap');
-
-    $secret_key = variable_get('marketo_ma_soap_encryption_key');
-    $access_key = variable_get('marketo_ma_soap_user_id');
-    $soap_endpoint = variable_get('marketo_ma_soap_endpoint');
-
-    if (array_key_exists('marketoCookie', $lead)) {
-      $mkto_trk = $lead['marketoCookie'];
-    }
-    else {
-      $mkto_trk = NULL;
-    }
-
-    try {
-      $soap_settings = _marketo_ma_soap_proxy_settings();
-      $soap_settings['trace'] = variable_get('marketo_ma_logging', FALSE);
-      $client = new MarketoClient($access_key, $secret_key, $soap_endpoint, $soap_settings);
-      $sync = $client->syncLead($lead['data'], $lead['email'], $mkto_trk, array('extendedResponse' => TRUE));
-      if ($sync['success']) {
-        if (variable_get('marketo_ma_logging', FALSE)) {
-          watchdog('marketo', 'Associating lead !email [soap] <pre>@data @result</pre>', array(
-            '!email' => $lead['email'],
-            '@data' => print_r($lead['data'], TRUE),
-            '@result' => print_r($sync['result'], TRUE),
-            ), WATCHDOG_INFO);
-        }
-      }
-      else {
-        watchdog('marketo', 'Unable to sync lead !email [soap] <pre>@error</pre> Request XML: <pre>@request</pre> Response XML: <pre>@response</pre>', array(
-          '!email' => $lead['email'],
-          '@error' => $sync['result'],
-          '@request' => $client->__getLastRequest(),
-          '@response' => $client->__getLastResponse(),
-          ), WATCHDOG_ERROR);
-      }
-    }
-    catch (Exception $e) {
-      watchdog('marketo', 'Unable to sync lead !email [soap] <pre>@error</pre>', array('!email' => $lead['email'], '@error' => $e->getMessage()), WATCHDOG_ERROR);
-    }
-  }
-}
-
-/**
- * Add a lead to the queue for submission later.
- * 
- * @param array $lead
- *   Lead data
- * 
- * @return bool
- *   Returns TRUE if lead was successfully added to queue
- */
-function _marketo_ma_queue_lead($lead) {
-  if (variable_get('marketo_ma_logging', FALSE)) {
-    watchdog('marketo', 'Queing lead !email [@method] <pre>@data</pre>', array(
-      '!email' => $lead['email'],
-      '@method' => 'queued',
-      '@data' => print_r($lead['data'], TRUE)), WATCHDOG_INFO);
-  }
-  $queue = DrupalQueue::get('marketo_ma_lead', TRUE);
-  $success = $queue->createItem($lead);
-  return $success;
-}
-
-/**
- * Returns defined fields as an associative array useful for select options.
- * 
- * @return array
- *   Key/value pairs of defined Marketo fields
- */
-function _marketo_ma_get_field_options($labels = TRUE, $sort = TRUE) {
-  /*
-   * marketo_ma_webform_fields is multi-line, pipe "|" delimited data
-   * which needs to be parsed. First split it into rows of data.
-   */
-  $raw_options = preg_split('/(\r\n?|\n)/', trim(variable_get('marketo_ma_webform_fields', MARKETO_MA_WEBFORM_FIELD_DEFAULTS)));
-  $options = array();
-
-  // Loop over all the rows getting the keys and values for the fields.
-  foreach ($raw_options as $row) {
-    $field = explode('|', trim($row));
-    if ($labels) {
-      $options[trim($field[0])] = trim($field[1]) . ' (' . trim($field[0]) . ')';
-    }
-    else {
-      $options[trim($field[0])] = trim($field[1]);
-    }
-  }
-  if ($sort) {
-    asort($options);
-  }
-
-  return $options;
-}
-
-/**
- * Retrieves fields from Marketo using SOAP API.
- * 
- * @return array
- *   Marketo fields
- */
-function _marketo_ma_get_fields() {
-  $result = array();
-  if (_marketo_ma_soap_is_configured()) {
-    module_load_include('inc', 'marketo_ma', 'includes/marketo_ma.soap');
-
-    $secret_key = variable_get('marketo_ma_soap_encryption_key');
-    $access_key = variable_get('marketo_ma_soap_user_id');
-    $soap_endpoint = variable_get('marketo_ma_soap_endpoint');
-
-    try {
-      $client = new MarketoClient($access_key, $secret_key, $soap_endpoint, _marketo_ma_soap_proxy_settings());
-      $result = $client->getFields();
-      if (!$result) {
-        watchdog('marketo', 'Unable to retrieve field information. Please check SOAP API configuration', WATCHDOG_ERROR);
-      }
-    }
-    catch (Exception $e) {
-      watchdog('marketo', 'Unable to retrieve field information. <pre>@error</pre>', array('error' => $e->getMessage()), WATCHDOG_ERROR);
-    }
-  }
-
-  return $result;
-}
-
-/**
- * Retrieves a lead from Marketo using SOAP API.
- *
- * @return array
- *   Marketo lead
- */
-function marketo_ma_get_lead($key) {
-  $result = array();
-  if (_marketo_ma_soap_is_configured()) {
-    module_load_include('inc', 'marketo_ma', 'includes/marketo_ma.soap');
-
-    $secret_key = variable_get('marketo_ma_soap_encryption_key');
-    $access_key = variable_get('marketo_ma_soap_user_id');
-    $soap_endpoint = variable_get('marketo_ma_soap_endpoint');
-
-    try {
-      $client = new MarketoClient($access_key, $secret_key, $soap_endpoint, _marketo_ma_soap_proxy_settings());
-      $result = $client->getLead($key);
-      if (!$result && variable_get('marketo_ma_logging', FALSE)) {
-        watchdog('marketo', 'Lead data not found for !key', array('!key' => $key), WATCHDOG_INFO);
-      }
-    }
-    catch (Exception $e) {
-      watchdog('marketo', 'Unable to retrieve lead information. <pre>@error</pre>', array('error' => $e->getMessage()), WATCHDOG_ERROR);
-    }
-  }
-
-  return $result;
-}
-
-/**
- * Retrieves lead activity from Marketo.
- *
- * @return array
- *   Marketo lead
- */
-function marketo_ma_get_lead_activity($key) {
-  $result = array();
-  if (_marketo_ma_soap_is_configured()) {
-    module_load_include('inc', 'marketo_ma', 'includes/marketo_ma.soap');
-
-    $secret_key = variable_get('marketo_ma_soap_encryption_key');
-    $access_key = variable_get('marketo_ma_soap_user_id');
-    $soap_endpoint = variable_get('marketo_ma_soap_endpoint');
-
-    try {
-      $client = new MarketoClient($access_key, $secret_key, $soap_endpoint, _marketo_ma_soap_proxy_settings());
-      $result = $client->getLeadActivity($key);
-      if (!$result && variable_get('marketo_ma_logging', FALSE)) {
-        watchdog('marketo', 'Lead data not found for !key', array('!key' => $key), WATCHDOG_INFO);
-      }
-    }
-    catch (Exception $e) {
-      watchdog('marketo', 'Unable to retrieve lead activity. <pre>@error</pre>', array('error' => $e->getMessage()), WATCHDOG_ERROR);
-    }
-  }
-
-  return $result;
-}
-
-/**
- * Tests to see if Marketo MA has a valid configuration.
- * 
- * @staticvar bool $configured
- * @return bool
- *   Returns TRUE if Munchkin settings are configured
- */
-function _marketo_ma_is_configured() {
-  static $configured;
-
-  if (!isset($configured)) {
-    $track = variable_get('marketo_ma_tracking_method');
-    if (
-      ($track == 'munchkin' & _marketo_ma_munchkin_is_configured()) ||
-      ($track == 'soap' & _marketo_ma_soap_is_configured()) ||
-      ($track == 'soap_async' & _marketo_ma_soap_is_configured())
-    ) {
-      $configured = TRUE;
-    }
-    else {
-      $configured = FALSE;
-    }
-  }
-
-  return $configured;
-}
-
-/**
- * Tests to see if munchkin is configured.
- * 
- * @staticvar bool $configured
- * @return bool
- *   Returns TRUE if Munchkin settings are configured
- */
-function _marketo_ma_munchkin_is_configured() {
-  static $configured;
-
-  if (!isset($configured)) {
-    if (
-      variable_get('marketo_ma_munchkin_account_id', FALSE) &&
-      variable_get('marketo_ma_munchkin_api_private_key', FALSE) &&
-      variable_get('marketo_ma_munchkin_javascript_library', FALSE)
-    ) {
-      $configured = TRUE;
-    }
-    else {
-      $configured = FALSE;
-    }
-  }
-  return $configured;
-}
-
-/**
- * Tests to see if forms2 is configured.
- *
- * @staticvar bool $configured
- * @return bool
- *   Returns TRUE if forms2 settings are configured
- */
-function _marketo_ma_forms2_is_configured() {
-  static $configured;
-
-  if (!isset($configured)) {
-    if (
-      variable_get('marketo_ma_munchkin_account_id', FALSE) &&
-      variable_get('marketo_ma_instance_host', FALSE)
-    ) {
-      $configured = TRUE;
-    }
-    else {
-      $configured = FALSE;
-    }
-  }
-  return $configured;
-}
-
-/**
- * Tests to see if soap is configured.
- * 
- * @staticvar bool $configured
- * @return bool
- *   Returns TRUE is SOAP API settings are configured
- */
-function _marketo_ma_soap_is_configured() {
-  static $configured;
-
-  if (!isset($configured)) {
-    if (
-      variable_get('marketo_ma_soap_encryption_key', FALSE) &&
-      variable_get('marketo_ma_soap_endpoint', FALSE) &&
-      variable_get('marketo_ma_soap_user_id', FALSE)
-    ) {
-      $configured = TRUE;
-    }
-    else {
-      $configured = FALSE;
-    }
-  }
-  return $configured;
-}
-
-/**
- * Based on page visibility setting determines if marketo should be included.
- */
-function _marketo_ma_visibility_pages() {
-  static $page_match;
-
-  // Cache visibility result if function is called more than once.
-  if (!isset($page_match)) {
-    $visibility = variable_get('marketo_ma_visibility_pages', 0);
-    $setting_pages = variable_get('marketo_ma_pages', MARKETO_MA_PAGES);
-
-    // Match path if necessary.
-    if (!empty($setting_pages)) {
-      // Convert path to lowercase. This allows comparison of the same path
-      // with different case. Ex: /Page, /page, /PAGE.
-      $pages = drupal_strtolower($setting_pages);
-      if ($visibility < 2) {
-        // Convert the Drupal path to lowercase.
-        $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
-        // Compare the lowercase internal and lowercase path alias (if any).
-        $page_match = drupal_match_path($path, $pages);
-        if ($path != $_GET['q']) {
-          $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
-        }
-        // When $visibility has a value of 0, the tracking code is displayed on
-        // all pages except those listed in $pages. When set to 1, it
-        // is displayed only on those pages listed in $pages.
-        $page_match = !($visibility xor $page_match);
-      }
-      elseif (module_exists('php')) {
-        $page_match = php_eval($setting_pages);
-      }
-      else {
-        $page_match = FALSE;
-      }
-    }
-    else {
-      $page_match = TRUE;
-    }
-  }
-  return $page_match;
-}
-
-/**
- * Based on role visibility setting determines if marketo should be included.
- */
-function _marketo_ma_visibility_roles($account) {
-  static $role_match;
-
-  // Cache visibility result if function is called more than once.
-  if (!isset($role_match)) {
-    $visibility = variable_get('marketo_ma_visibility_roles', 0);
-    $enabled = $visibility;
-    $roles = variable_get('marketo_ma_roles', array());
-
-    if (array_sum($roles) > 0) {
-      // One or more roles are selected.
-      foreach (array_keys($account->roles) as $rid) {
-        // Is the current user a member of one of these roles?
-        if (isset($roles[$rid]) && $rid == $roles[$rid]) {
-          // Current user is a member of a role that should be (in|ex)cluded.
-          $enabled = !$visibility;
-          break;
-        }
-      }
-    }
-    else {
-      /*
-       * No roles have been selected therefore we will return the visibility
-       * setting itself. "Only selected" will be FALSE and "All except" TRUE
-       */
-      $enabled = $visibility;
-    }
-  }
-  return $enabled;
-}
-
-/**
- * Returns SoapClient proxy settings if configured.
- * 
- * @return array
- *   Proxy settings
- */
-function _marketo_ma_soap_proxy_settings() {
-  static $proxy;
-
-  if (!isset($proxy)) {
-    $proxy = array();
-    $proxy['proxy_host'] = variable_get('marketo_ma_soap_proxy_host', '');
-    $proxy['proxy_port'] = variable_get('marketo_ma_soap_proxy_port', '');
-    $proxy['proxy_login'] = variable_get('marketo_ma_soap_proxy_login', '');
-    $proxy['proxy_password'] = variable_get('marketo_ma_soap_proxy_password', '');
-
-    if (empty($proxy['proxy_host'])) {
-      unset($proxy['proxy_host']);
-    }
-    if (empty($proxy['proxy_port'])) {
-      unset($proxy['proxy_port']);
-    }
-    if (empty($proxy['proxy_login'])) {
-      unset($proxy['proxy_login']);
-    }
-    if (empty($proxy['proxy_password'])) {
-      unset($proxy['proxy_password']);
-    }
-  }
-
-  return $proxy;
+function marketo_ma_page_attachments(array &$page) {
+  // Hand off to the marketo_ma service.
+  \Drupal::service('marketo_ma')->pageAttachments($page);
 }
diff --git a/marketo_ma.permissions.yml b/marketo_ma.permissions.yml
new file mode 100644
index 0000000..bd340d5
--- /dev/null
+++ b/marketo_ma.permissions.yml
@@ -0,0 +1,5 @@
+
+administer marketo:
+  title: 'Administer Marketo MA'
+  description: 'Configure Marketo settings such as page visibility, Munchkin keys, and SOAP API endpoint.'
+  restrict access: true
diff --git a/marketo_ma.routing.yml b/marketo_ma.routing.yml
new file mode 100644
index 0000000..5a20123
--- /dev/null
+++ b/marketo_ma.routing.yml
@@ -0,0 +1,10 @@
+
+marketo_ma.settings:
+  path: 'admin/config/services/marketo-ma'
+  defaults:
+    _form: '\Drupal\marketo_ma\Form\MarketoMASettings'
+    _title: 'Marketo MA configuration'
+  requirements:
+    _permission: 'administer marketo'
+  options:
+    _admin_route: TRUE
diff --git a/marketo_ma.rules.inc b/marketo_ma.rules.inc
deleted file mode 100644
index 4b4cc34..0000000
--- a/marketo_ma.rules.inc
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @file
- * Rules integration for Marketo MA
- */
-
-/**
- * Implements hook_rules_action_info().
- */
-function marketo_ma_rules_action_info() {
-  $actions = array(
-    'marketo_ma_rules_action_add_lead' => array(
-      'label' => t('Send lead data to Marketo'),
-      'group' => t('User'),
-      'parameter' => array(
-        'account' => array(
-          'type' => 'user',
-          'label' => t('User account to add as Marketo lead'),
-          'save' => FALSE,
-        ),
-        'data' => array(
-          'type' => 'list',
-          'label' => t('Marketo data should be a list of pipe delimited strings provided by a variable.'),
-        ),
-      ),
-    ),
-    'marketo_ma_rules_action_get_lead' => array(
-      'label' => t('Get lead data from Marketo'),
-      'group' => t('User'),
-      'parameter' => array(
-        'account' => array(
-          'type' => 'user',
-          'label' => t('User account to retrieve'),
-          'save' => FALSE,
-        ),
-      ),
-      'provides' => array(
-        'marketo_ma_lead' => array(
-          'type' => 'text',
-          'label' => t('Marketo lead data'),
-        ),
-      ),
-    ),
-  );
-  return $actions;
-}
-
-/**
- * Process data passed as a list from action firing and execute lead update.
- *
- * @param $account
- * @param $data
- */
-function marketo_ma_rules_action_add_lead($account, $data) {
-  if (!empty($account->mail)) {
-    $output = array();
-    foreach ($data as $row) {
-      $r = explode("|", $row);
-      $output[$r[0]] = $r[1];
-    }
-    marketo_ma_add_lead($account->mail, $output);
-  }
-}
-
-/**
- * Retrieves a lead from Marketo.
- *
- * @param $account
- * @return array
- */
-function marketo_ma_rules_action_get_lead($account) {
-  $lead = NULL;
-  if (!empty($account->mail)) {
-    $lead = marketo_ma_get_lead($account->mail);
-  }
-  return array(
-    'marketo_ma_lead' => json_encode($lead),
-  );
-}
diff --git a/marketo_ma.rules_defaults.inc b/marketo_ma.rules_defaults.inc
deleted file mode 100644
index 2961463..0000000
--- a/marketo_ma.rules_defaults.inc
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-
-/**
- * @file
- * Default rules for Marketo MA
- */
-
-/**
- * Implements hook_default_rules_configuration().
- */
-function marketo_ma_default_rules_configuration() {
-  $config = array();
-
-  $rules = array();
-  $rules['rules_update_marketo_lead_on_create'] = <<<EOD
-{ "rules_update_marketo_lead_on_create" : {
-    "LABEL" : "Update Marketo lead on user creation",
-    "PLUGIN" : "reaction rule",
-    "ACTIVE" : false,
-    "TAGS" : [ "Marketo" ],
-    "REQUIRES" : [ "rules", "marketo_ma" ],
-    "ON" : [ "user_insert" ],
-    "DO" : [
-      { "variable_add" : {
-          "USING" : { "type" : "list\u003Ctext\u003E" },
-          "PROVIDE" : { "variable_added" : { "marketoparams" : "Marketo Parameters" } }
-        }
-      },
-      { "list_add" : { "list" : [ "marketoparams" ], "item" : "Email|[account:mail]" } },
-      { "marketo_ma_rules_action_add_lead" : { "account" : [ "account" ], "data" : [ "marketoparams" ] } }
-    ]
-  }
-}
-EOD;
-
-  $rules['rules_update_marketo_lead_on_login'] = <<<EOD
-{ "rules_update_marketo_lead_on_login" : {
-    "LABEL" : "Update Marketo lead on login",
-    "PLUGIN" : "reaction rule",
-    "ACTIVE" : false,
-    "TAGS" : [ "Marketo" ],
-    "REQUIRES" : [ "rules", "marketo_ma" ],
-    "ON" : [ "user_login" ],
-    "DO" : [
-      { "variable_add" : {
-          "USING" : { "type" : "list\u003Ctext\u003E" },
-          "PROVIDE" : { "variable_added" : { "marketoparams" : "Marketo Parameters" } }
-        }
-      },
-      { "list_add" : { "list" : [ "marketoparams" ], "item" : "Email|[account:mail]" } },
-      { "marketo_ma_rules_action_add_lead" : { "account" : [ "site:current-user" ], "data" : [ "marketoparams" ] } }
-    ]
-  }
-}
-EOD;
-
-  $rules['rules_update_marketo_lead_on_user_update'] = <<<EOD
-{ "rules_update_marketo_lead_on_user_update" : {
-    "LABEL" : "Update Marketo lead on user update",
-    "PLUGIN" : "reaction rule",
-    "ACTIVE" : false,
-    "TAGS" : [ "Marketo" ],
-    "REQUIRES" : [ "rules", "marketo_ma" ],
-    "ON" : [ "user_update" ],
-    "DO" : [
-      { "variable_add" : {
-          "USING" : { "type" : "list\u003Ctext\u003E" },
-          "PROVIDE" : { "variable_added" : { "marketoparams" : "Marketo Parameters" } }
-        }
-      },
-      { "list_add" : { "list" : [ "marketoparams" ], "item" : "Email|[account:mail]" } },
-      { "marketo_ma_rules_action_add_lead" : { "account" : [ "account" ], "data" : [ "marketoparams" ] } }
-    ]
-  }
-}
-EOD;
-
-  foreach ($rules as $key => $value) {
-    $config[$key] = rules_import($value);
-  }
-
-  return $config;
-}
diff --git a/marketo_ma.services.yml b/marketo_ma.services.yml
new file mode 100644
index 0000000..15a339f
--- /dev/null
+++ b/marketo_ma.services.yml
@@ -0,0 +1,15 @@
+services:
+  # The Marketo MA default service (Worker/Core).
+  marketo_ma:
+    class: Drupal\marketo_ma\MarketoMaService
+    arguments: ['@config.factory', '@marketo_ma.client', '@current_user', '@current_route_match', '@path.matcher', '@marketo_ma.munchkin', '@queue', '@user.private_tempstore']
+  # The Marketo MA API Client service.
+  # @todo It would be nice to have some from of cached client, which caches for
+  #   example the field mapping along the way.
+  marketo_ma.client:
+    class: Drupal\marketo_ma\MarketoMaApiClient
+    arguments: ['@config.factory']
+  # The Marketo MA munchkin service
+  marketo_ma.munchkin:
+    class: Drupal\marketo_ma\MarketoMaMunchkin
+    arguments: ['@config.factory', '@current_user', '@current_route_match']
diff --git a/modules/mma_contact/config/schema/mma_contact.schema.yml b/modules/mma_contact/config/schema/mma_contact.schema.yml
new file mode 100644
index 0000000..e7abb35
--- /dev/null
+++ b/modules/mma_contact/config/schema/mma_contact.schema.yml
@@ -0,0 +1,22 @@
+contact.form.*.third_party.mma_contact:
+  type: mapping
+  label: 'MMA contact settings'
+  mapping:
+    mapping:
+      label: 'Contact fields to marketo fields mapping'
+      type: sequence
+      sequence:
+        type: string
+    enabled:
+      label: 'Whether Marketo tracking is enabled for the contact form.'
+      type: integer
+
+block.settings.mma_contact_block__additional_field_values:
+  type: block_settings
+  mapping:
+    contact_form:
+      type: string
+    fields:
+      type: sequence
+      sequence:
+        type: ignore
diff --git a/modules/mma_contact/mma_contact.info.yml b/modules/mma_contact/mma_contact.info.yml
new file mode 100644
index 0000000..74446e9
--- /dev/null
+++ b/modules/mma_contact/mma_contact.info.yml
@@ -0,0 +1,10 @@
+name: Marketo MA Contact
+type: module
+description: Integrates Marketo MA with Contact module forms.
+core: 8.x
+package: Marketo
+dependencies:
+  - marketo_ma
+  - contact
+  - contact_storage
+  - contact_block
diff --git a/modules/mma_contact/mma_contact.links.task.yml b/modules/mma_contact/mma_contact.links.task.yml
new file mode 100644
index 0000000..70f9487
--- /dev/null
+++ b/modules/mma_contact/mma_contact.links.task.yml
@@ -0,0 +1,5 @@
+mma_contact.contact.configuration:
+  route_name: 'mma_contact.contact.configuration'
+  title: 'Marketo configuration'
+  base_route: entity.contact_form.edit_form
+  weight: 30
diff --git a/modules/mma_contact/mma_contact.module b/modules/mma_contact/mma_contact.module
new file mode 100644
index 0000000..f49d77b
--- /dev/null
+++ b/modules/mma_contact/mma_contact.module
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains mma_contact.module..
+ */
+
+use Drupal\contact\ContactFormInterface;
+use Drupal\contact\MessageInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
+use Drupal\mma_contact\Hooks\ContactMessageInsert;
+
+/**
+ * Implements hook_help().
+ */
+function mma_contact_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    // Main module help for the mma_contact module.
+    case 'help.page.mma_contact':
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('Integrates Marketo MA with Contact module forms.') . '</p>';
+      return $output;
+
+    default:
+  }
+}
+
+/**
+ * Implements hook_entity_operation().
+ */
+function mma_contact_entity_operation(EntityInterface $entity) {
+  $operations = [];
+  if ($entity instanceof ContactFormInterface) {
+    $operations['mma_contact_mapping'] = [
+      'title' => t('Marketo mapping'),
+      'url' => Url::fromRoute('mma_contact.contact.configuration', ['contact_form' => $entity->id()]),
+      'weight' => 50,
+    ];
+  }
+
+  return $operations;
+}
+
+/**
+ * Implements hook_contact_message_insert().
+ */
+function mma_contact_contact_message_insert(MessageInterface $message) {
+  /** @var \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver */
+  $class_resolver = \Drupal::service('class_resolver');
+  $hook = $class_resolver->getInstanceFromDefinition(ContactMessageInsert::class);
+  $hook->contactMessageInsert($message);
+}
diff --git a/modules/mma_contact/mma_contact.routing.yml b/modules/mma_contact/mma_contact.routing.yml
new file mode 100644
index 0000000..eb09b92
--- /dev/null
+++ b/modules/mma_contact/mma_contact.routing.yml
@@ -0,0 +1,7 @@
+mma_contact.contact.configuration:
+  path: /admin/structure/contact/manage/{contact_form}/marketo
+  defaults:
+    _form: \Drupal\mma_contact\Form\MmaContactConfiguration
+    title: 'Marketo configuration'
+  requirements:
+    _permission: 'administer marketo'
diff --git a/modules/mma_contact/src/Form/MmaContactConfiguration.php b/modules/mma_contact/src/Form/MmaContactConfiguration.php
new file mode 100644
index 0000000..4fbf9c5
--- /dev/null
+++ b/modules/mma_contact/src/Form/MmaContactConfiguration.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace Drupal\mma_contact\Form;
+
+use Drupal\contact\ContactFormInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\marketo_ma\MarketoMaApiClientInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a configuration form per contact_form instance.
+ *
+ * This form adds:
+ *
+ * - A toggle to enable marketo support for this specific form
+ * - Provide a way to provide field mapping between contact fields and marketo
+ *   ones.
+ */
+class MmaContactConfiguration extends FormBase {
+
+  /**
+   * The contact form entity.
+   *
+   * @todo Could we also implement an entity form instead?
+   *
+   * @var \Drupal\contact\ContactFormInterface
+   */
+  protected $contactForm;
+
+  /** @var \Drupal\marketo_ma\MarketoMaApiClientInterface */
+  protected $marketoClient;
+
+  /** @var \Drupal\Core\Entity\EntityFieldManagerInterface */
+  protected $entityFieldManager;
+
+  /**
+   * Creates a new MmaContactConfiguration instance.
+   *
+   * @param \Drupal\marketo_ma\MarketoMaApiClientInterface $marketoClient
+   *   The marketo client.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
+   *   The entity field manager.
+   */
+  public function __construct(MarketoMaApiClientInterface $marketoClient, EntityFieldManagerInterface $entityFieldManager) {
+    $this->marketoClient = $marketoClient;
+    $this->entityFieldManager = $entityFieldManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('marketo_ma.client'),
+      $container->get('entity_field.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'mma_contact_configuration';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, ContactFormInterface $contact_form = NULL) {
+    $this->contactForm = $contact_form;
+
+    $mapping = $this->contactForm->getThirdPartySetting('mma_contact', 'mapping');
+
+    $form['enabled'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Enable tracking'),
+      '#description' => $this->t('Set to "Enable tracking" to turn on tracking for this contact form.'),
+      '#default_value' => $this->contactForm->getThirdPartySetting('mma_contact', 'enabled'),
+    ];
+
+    $form['mapping'] = [
+      '#type' => 'table',
+      '#header' => [
+        'title' => $this->t('Title'),
+        'tracking' => $this->t('Tracking enabled'),
+        'mapping' => $this->t('Components mapping'),
+      ],
+    ];
+
+    $marketo_field_options = ['' => $this->t('None')] + $this->getMarketoFields();
+    foreach ($this->getContactFields() as $field_name => $label) {
+      $form['mapping'][$field_name] = [
+        'title' => ['#markup' => $label],
+        'tracking' => ['#markup' => ''],
+        'mapping' => [
+          '#type' => 'select',
+          '#title' => $this->t('Select mapped component'),
+          '#title_display' => 'hidden',
+          '#options' => $marketo_field_options,
+          '#default_value' => isset($mapping[$field_name]) ? $mapping[$field_name] : FALSE,
+        ],
+      ];
+    }
+
+    $form['actions'] = [
+      '#type' => 'actions',
+    ];
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Save'),
+      '#button_type' => 'primary',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * Returns all available contact form field labels, keyed by machine name.
+   *
+   * @return string[]
+   */
+  protected function getContactFields() {
+    $fields = $this->entityFieldManager->getFieldDefinitions('contact_message', $this->contactForm->id());
+    return array_map(function (FieldDefinitionInterface $field_definition) {
+      return $field_definition->getLabel();
+    }, $fields);
+  }
+
+  /**
+   * Returns the available marketo field labels, keyed by machine name.
+   *
+   * @return string[]
+   */
+  protected function getMarketoFields() {
+    $fields = $this->marketoClient->getFields();
+    $keys = array_map(function ($field) {
+      return $field['default_name'];
+    }, $fields);
+    $labels = array_map(function ($field) {
+      return $field['displayName'];
+    }, $fields);
+    return array_combine($keys, $labels);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Set the enabled value.
+    $this->contactForm->setThirdPartySetting('mma_contact', 'enabled', $form_state->getValue('enabled'));
+
+    // Get the mapping values.
+    $mapping = array_map(function ($form_value) {
+      return $form_value['mapping'];
+    }, $form_state->getValue('mapping'));
+    // Remove any unassociated fields.
+    $mapping = array_filter($mapping);
+
+    // Set the third party settings for field mappings and save.
+    $this->contactForm->setThirdPartySetting('mma_contact', 'mapping', $mapping);
+    $this->contactForm->save();
+  }
+
+}
diff --git a/modules/mma_contact/src/Hooks/ContactMessageInsert.php b/modules/mma_contact/src/Hooks/ContactMessageInsert.php
new file mode 100644
index 0000000..a269edf
--- /dev/null
+++ b/modules/mma_contact/src/Hooks/ContactMessageInsert.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Drupal\mma_contact\Hooks;
+
+use Drupal\contact\MessageInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\marketo_ma\Lead;
+use Drupal\marketo_ma\MarketoMaApiClientInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class ContactMessageInsert implements ContainerInjectionInterface {
+
+  /**
+   * @var \Drupal\marketo_ma\MarketoMaApiClientInterface
+   */
+  protected $marketoClient;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Stores the loaded mapping configuration.
+   *
+   * @var array|NULL
+   */
+  protected $mappingConfiguration;
+
+  /**
+   * Creates a new ContactMessageInsert instance.
+   *
+   * @param \Drupal\marketo_ma\MarketoMaApiClientInterface $marketoClient
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   */
+  public function __construct(MarketoMaApiClientInterface $marketoClient, EntityTypeManagerInterface $entityTypeManager) {
+    $this->marketoClient = $marketoClient;
+    $this->entityTypeManager = $entityTypeManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('marketo_ma.client'),
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * Implements hook_contact_message_insert().
+   */
+  public function contactMessageInsert(MessageInterface $message) {
+    if ($tracking_enabled = $this->isTrackingEnabled($message->bundle())) {
+      $data = $this->determineMappedData($message);
+      $this->marketoClient->syncLead(new Lead($data));
+    }
+  }
+
+  /**
+   * Loads the mapping configuration for a specific contact form.
+   *
+   * @param string $contact_form_id
+   *   The contact form id.
+   *
+   * @return array
+   */
+  protected function loadMappingConfiguration($contact_form_id) {
+    if (!isset($this->mappingConfiguration)) {
+      /** @var \Drupal\contact\ContactFormInterface $contact_form */
+      $contact_form = $this->entityTypeManager->getStorage('contact_form')->load($contact_form_id);
+      $this->mappingConfiguration = $contact_form->getThirdPartySetting('mma_contact', 'mapping', []);
+    }
+    return $this->mappingConfiguration;
+  }
+
+  /**
+   * Determines whether some marketo tracking is enabled.
+   *
+   * @param string $contact_form_id
+   *   The contact form id.
+   *
+   * @return bool
+   *   TRUE if marketo tracking is enable.d
+   */
+  protected function isTrackingEnabled($contact_form_id) {
+    $contact_form = $this->entityTypeManager->getStorage('contact_form')->load($contact_form_id);
+    return ($contact_form->getThirdPartySetting('mma_contact', 'enabled', 0) === 1
+      && !empty($this->loadMappingConfiguration($contact_form_id)));
+  }
+
+  /**
+   * Determines data mapping from the contact form to marketo fields.
+   *
+   * @param \Drupal\contact\MessageInterface $message
+   *
+   * @return array
+   *  The mapping data, keyed by marketo field name.
+   */
+  protected function determineMappedData(MessageInterface $message) {
+    $mapping = $this->loadMappingConfiguration($message->bundle());
+    $data = [];
+
+    foreach ($mapping as $contact_field_name => $marketo_field_name) {
+      $field_item_list = $message->get($contact_field_name);
+      $field_data = $field_item_list->{$field_item_list->get(0)->mainPropertyName()};
+      $data[$marketo_field_name] = $field_data;
+    }
+    return $data;
+  }
+
+}
diff --git a/modules/mma_contact/src/Plugin/Block/AdditionalFieldsContactBlock.php b/modules/mma_contact/src/Plugin/Block/AdditionalFieldsContactBlock.php
new file mode 100644
index 0000000..aac623e
--- /dev/null
+++ b/modules/mma_contact/src/Plugin/Block/AdditionalFieldsContactBlock.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace Drupal\mma_contact\Plugin\Block;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\contact\Entity\Message;
+use Drupal\contact\MessageInterface;
+use Drupal\contact_block\Plugin\Block\ContactBlock;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides a 'ContactBlock' block with additional field values.
+ *
+ * @Block(
+ *  id = "mma_contact_block__additional_field_values",
+ *  admin_label = @Translation("Contact block (with additional values) "),
+ * )
+ *
+ * @todo config schema
+ * @todo tests
+ */
+class AdditionalFieldsContactBlock extends ContactBlock {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    $defaults = parent::defaultConfiguration();
+
+    $defaults['contact_form'] = '';
+    $defaults['fields'] = [];
+    return $defaults;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    $form['contact_form']['#ajax'] = [
+      'callback' => [$this, 'onContactFormChange'],
+      'wrapper' => 'mma_contact_block__additional_field_values',
+    ];
+
+    $form['fields'] = [
+      '#prefix' => '<div id="mma_contact_block__additional_field_values">',
+      '#type' => 'container',
+      '#suffix' => '</div>',
+    ];
+    if (($contact_form_id = $form_state->getValue(['settings', 'contact_form'])) || ($contact_form_id = $this->configuration['contact_form'])) {
+      $form['fields']['#type'] = 'details';
+      $form['fields']['#open'] = TRUE;
+
+      $contact_message = Message::create([
+        'contact_form' => $contact_form_id,
+      ]);
+      $this->applyFieldValuesToEntity($contact_message, $this->configuration['fields']);
+      $form_display = $this->getFormDisplay($contact_form_id);
+
+      // We need to setup the right paths, so $form_display::extractFormValues
+      // works in the submit function.
+      $form['fields']['#parents'] = ['settings', 'fields'];
+      $form_display->buildForm($contact_message, $form['fields'], $form_state);
+    }
+
+    return $form;
+  }
+
+  /**
+   * Gets the contact message form display for a specific contact form.
+   *
+   * @param string $contact_form_id
+   *   The contact form ID.
+   *
+   * @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface
+   *   The entity form display.
+   */
+  protected function getFormDisplay($contact_form_id) {
+    return entity_get_form_display('contact_message', $contact_form_id, 'block_form');
+  }
+
+  /**
+   * Applies the preconfigured field values to the contact message entity.
+   *
+   * @param \Drupal\contact\MessageInterface $contact_message
+   *   The contact message entity.
+   * @param array $field_values
+   *   The preconfigured field values.
+   */
+  protected function applyFieldValuesToEntity(MessageInterface $contact_message, array $field_values) {
+    foreach ($this->configuration['fields'] as $field_name => $field_values) {
+      $contact_message->get($field_name)->setValue($field_values);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit($form, FormStateInterface $form_state) {
+    parent::blockSubmit($form, $form_state);
+
+    if ($contact_form_id = $form_state->getValue(['contact_form'])) {
+      $contact_message = Message::create([
+        'contact_form' => $contact_form_id,
+      ]);
+
+      $form_display = $this->getFormDisplay($contact_form_id);
+      // We need to setup the right paths, so $form_display::extractFormValues
+      // works.
+      $entity_form = $form['settings']['fields'];
+      $entity_form['#parents'] = ['fields'];
+      $extracted_fields = $form_display->extractFormValues($contact_message, $entity_form, $form_state);
+
+      $pre_configured_field_values = [];
+      foreach ($extracted_fields as $extract_field) {
+        $field_value = $contact_message->{$extract_field}->getValue();
+        $pre_configured_field_values[$extract_field] = $field_value;
+      }
+
+      $this->configuration['fields'] = $pre_configured_field_values;
+    }
+  }
+
+  /**
+   * Ajax callback when changing the contact form.
+   *
+   * @param array $form
+   *   The build form.
+   *
+   * @return array
+   *   The subpart of the form which should be rendered.
+   */
+  public function onContactFormChange($form) {
+    return NestedArray::getValue($form, ['settings', 'fields']);
+  }
+
+  /**
+   * Creates the contact message entity without saving it.
+   *
+   * @return \Drupal\contact\Entity\Message|null
+   *   The contact message entity. NULL if the entity does not exist.
+   */
+  protected function createContactMessage() {
+    if ($contact_message = parent::createContactMessage()) {
+      $this->applyFieldValuesToEntity($contact_message, $this->configuration['fields']);
+    }
+    return $contact_message;
+  }
+
+}
diff --git a/modules/mma_contact/tests/modules/mma_contact_test/mma_contact_test.info.yml b/modules/mma_contact/tests/modules/mma_contact_test/mma_contact_test.info.yml
new file mode 100644
index 0000000..a30a28d
--- /dev/null
+++ b/modules/mma_contact/tests/modules/mma_contact_test/mma_contact_test.info.yml
@@ -0,0 +1,5 @@
+name: mma_contact_test
+type: module
+core: 8.x
+dependencies:
+  - mma_contact
diff --git a/modules/mma_contact/tests/modules/mma_contact_test/src/MmaContactTestServiceProvider.php b/modules/mma_contact/tests/modules/mma_contact_test/src/MmaContactTestServiceProvider.php
new file mode 100644
index 0000000..063c3a4
--- /dev/null
+++ b/modules/mma_contact/tests/modules/mma_contact_test/src/MmaContactTestServiceProvider.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\mma_contact_test;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceModifierInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Provides an alternative mma client service implementation.
+ */
+class MmaContactTestServiceProvider implements ServiceModifierInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alter(ContainerBuilder $container) {
+    $container->getDefinition('marketo_ma.client')
+      ->setClass(TestMarketoMaApiClient::class)
+      ->setArguments([new Reference('state')]);
+  }
+
+}
diff --git a/modules/mma_contact/tests/modules/mma_contact_test/src/TestMarketoMaApiClient.php b/modules/mma_contact/tests/modules/mma_contact_test/src/TestMarketoMaApiClient.php
new file mode 100644
index 0000000..5539eea
--- /dev/null
+++ b/modules/mma_contact/tests/modules/mma_contact_test/src/TestMarketoMaApiClient.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\mma_contact_test;
+
+use Drupal\Core\State\StateInterface;
+use Drupal\marketo_ma\MarketoMaApiClientInterface;
+
+/**
+ * Stub implementation of an marketo api client.
+ */
+class TestMarketoMaApiClient implements MarketoMaApiClientInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $syncedLeads = [];
+
+  /**
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  public function __construct(StateInterface $state) {
+    $this->state = $state;
+    $this->syncedLeads = $this->state->get(static::class, []);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function canConnect() {
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFields() {
+    $fields = [];
+    $fields[] = [
+      'id' => 1,
+      'displayName' => 'First name',
+      'default_name' => 'firstName',
+      'dataType' => 'string',
+      'length' => 255,
+    ];
+    $fields[] = [
+      'id' => 2,
+      'displayName' => 'Second name',
+      'default_name' => 'secondName',
+      'dataType' => 'string',
+      'length' => 255,
+    ];
+    $fields[] = [
+      'id' => 3,
+      'displayName' => 'mail',
+      'default_name' => 'email',
+      'dataType' => 'string',
+      'length' => 255,
+    ];
+
+    return $fields;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLead($key, $type) {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLeadActivity($key, $type) {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function syncLead($lead, $key = 'email', $cookie = null, $options = []) {
+    $this->syncedLeads[] = $lead;
+    $this->state->set(static::class, $this->syncedLeads);
+
+    return [];
+  }
+
+  public function getSyncedLeads() {
+    return $this->syncedLeads;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteLead($leads, $args = array()) {
+    return [];
+  }
+
+}
diff --git a/modules/mma_contact/tests/src/Functional/MmaContactSettingsTest.php b/modules/mma_contact/tests/src/Functional/MmaContactSettingsTest.php
new file mode 100644
index 0000000..de84511
--- /dev/null
+++ b/modules/mma_contact/tests/src/Functional/MmaContactSettingsTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\Tests\mma_contact\Functional;
+
+use Drupal\contact\Entity\ContactForm;
+
+/**
+ * Tests the mma_contact admin settings.
+ *
+ * @group mma_contact
+ *
+ * @see \Drupal\mma_contact_test\TestMarketoMaApiClient
+ */
+class MmaContactSettingsTest extends MmaContactTestBase {
+
+  public function testMarketoContactAdminSettingsUI() {
+    $contact_form_id = 'test_contact' . strtolower($this->randomMachineName());
+    $edit = [
+      'label' => 'test contact',
+      'id' => $contact_form_id,
+      'recipients' => 'foo@example.com',
+      'contact_storage_preview' => FALSE,
+    ];
+    $this->drupalPostForm('admin/structure/contact/add', $edit, 'Save');
+    $this->assertSession()->pageTextContains('has been added');
+
+    $edit = [
+      'enabled' => 1,
+      'mapping[name][mapping]' => 'firstName',
+      'mapping[mail][mapping]' => 'email',
+    ];
+    $this->drupalPostForm("admin/structure/contact/manage/{$contact_form_id}/marketo", $edit, 'Save');
+
+    $contact_form = ContactForm::load($contact_form_id);
+    $this->assertEquals([
+      'name' => 'firstName',
+      'mail' => 'email',
+    ], $contact_form->getThirdPartySetting('mma_contact', 'mapping'));
+  }
+
+}
diff --git a/modules/mma_contact/tests/src/Functional/MmaContactTestBase.php b/modules/mma_contact/tests/src/Functional/MmaContactTestBase.php
new file mode 100644
index 0000000..2e7a498
--- /dev/null
+++ b/modules/mma_contact/tests/src/Functional/MmaContactTestBase.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Drupal\Tests\mma_contact\Functional;
+
+use Drupal\contact\Entity\ContactForm;
+use Drupal\Tests\BrowserTestBase;
+
+abstract class MmaContactTestBase extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['contact', 'contact_storage', 'mma_contact', 'marketo_ma', 'mma_contact_test', 'user', 'contact_block'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    /** @var \Drupal\contact\ContactFormInterface $contact_form */
+    $contact_form = ContactForm::create([
+      'label' => 'test contact',
+      'id' => 'test_contact',
+      'recipients' => ['foo@example.com'],
+      'contact_storage_preview' => FALSE,
+    ]);
+
+    $contact_form->setThirdPartySetting('mma_contact', 'mapping', [
+      'name' => 'firstName',
+      'mail' => 'email',
+    ]);
+    // Enable marketo integration for the form.
+    $contact_form->setThirdPartySetting('mma_contact', 'enabled', 1);
+
+    $contact_form->save();
+
+    $account = $this->drupalCreateUser(['administer contact forms', 'administer marketo']);
+    $this->drupalLogin($account);
+  }
+
+}
diff --git a/modules/mma_contact/tests/src/Functional/Plugin/Block/AdditionalFieldsContactBlockTest.php b/modules/mma_contact/tests/src/Functional/Plugin/Block/AdditionalFieldsContactBlockTest.php
new file mode 100644
index 0000000..96ac7b2
--- /dev/null
+++ b/modules/mma_contact/tests/src/Functional/Plugin/Block/AdditionalFieldsContactBlockTest.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Drupal\Tests\mma_contact\Kernel\Plugin\Block;
+
+use Drupal\block\Entity\Block;
+use Drupal\contact\Entity\ContactForm;
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Entity\Entity\EntityFormMode;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\marketo_ma\Lead;
+use Drupal\mma_contact_test\TestMarketoMaApiClient;
+use Drupal\simpletest\BlockCreationTrait;
+use Drupal\Tests\mma_contact\Functional\MmaContactTestBase;
+
+/**
+ * @coversDefaultClass \Drupal\mma_contact\Plugin\Block\AdditionalFieldsContactBlock
+ * @group mma_contact
+ */
+class AdditionalFieldsContactBlockTest extends MmaContactTestBase {
+
+  use BlockCreationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['block', 'field'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    FieldStorageConfig::create([
+      'entity_type' => 'contact_message',
+      'type' => 'string',
+      'field_name' => 'field_test',
+    ])->save();
+    FieldConfig::create([
+      'entity_type' => 'contact_message',
+      'bundle' => 'test_contact',
+      'field_name' => 'field_test',
+    ])->save();
+
+    $contact_form = ContactForm::load('test_contact');
+    $contact_form->setThirdPartySetting('mma_contact', 'mapping', [
+      'name' => 'firstName',
+      'mail' => 'email',
+      'field_test' => 'fieldTest',
+    ]);
+
+    $contact_form->save();
+
+    EntityFormMode::create([
+      'id' => 'contact_message.block_form',
+      'targetEntityType' => 'contact_message',
+    ])->save();
+
+    $entity_form_display = EntityFormDisplay::create([
+      'targetEntityType' => 'contact_message',
+      'bundle' => 'test_contact',
+      'mode' => 'block_form',
+      'content' => [],
+    ]);
+    $entity_form_display->setComponent('field_test', [
+      'type' => 'string_textfield',
+    ]);
+    $entity_form_display->save();
+
+    $account = $this->drupalCreateUser(['administer contact forms', 'administer marketo', 'access site-wide contact form', 'administer blocks']);
+    $this->drupalLogin($account);
+  }
+
+  /**
+   * Tests the block UI.
+   */
+  public function testBlockSettings() {
+    $edit = [
+      'settings[contact_form]' => 'test_contact',
+      'id' => 'test_block',
+    ];
+
+    $this->drupalPostForm('admin/structure/block/add/mma_contact_block__additional_field_values/tibco', $edit, 'Save block');
+
+    $edit = [
+      'settings[fields][subject][0][value]' => 'test subject',
+      'settings[fields][message][0][value]' => 'test message',
+      'settings[fields][field_test][0][value]' => 'test value',
+    ];
+    $this->drupalPostForm('admin/structure/block/manage/test_block', $edit, 'Save block');
+
+
+    $block_settings = Block::load('test_block');
+    $this->assertEquals([['value' => 'test value']], $block_settings->getPlugin()->getConfiguration()['fields']['field_test']);
+  }
+
+  /**
+   * Tests that the preconfigured value is passed along to the lead data.
+   */
+  public function testBlockSubmission() {
+    $this->placeBlock('mma_contact_block__additional_field_values', [
+      'contact_form' => 'test_contact',
+      'fields' => [
+        'field_test' => [['value' => 'test_value']],
+      ],
+    ]);
+
+    $this->drupalPostForm('', [
+      'subject[0][value]' => 'test subject',
+      'message[0][value]' => 'test message',
+    ], 'Send message');
+
+    $lead_data = \Drupal::state()->get(TestMarketoMaApiClient::class);
+    $lead_data = array_filter($lead_data);
+    $expected_lead_data = new Lead([
+      'firstName' => $this->loggedInUser->getAccountName(),
+      'email' => $this->loggedInUser->getEmail(),
+      'fieldTest' => 'test_value',
+    ]);
+
+    $this->assertEquals($expected_lead_data, end($lead_data));
+  }
+
+}
diff --git a/modules/mma_contact/tests/src/Kernel/ContactSubmissionTest.php b/modules/mma_contact/tests/src/Kernel/ContactSubmissionTest.php
new file mode 100644
index 0000000..5b5d179
--- /dev/null
+++ b/modules/mma_contact/tests/src/Kernel/ContactSubmissionTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\Tests\mma_contact\Kernel;
+
+use Drupal\contact\Entity\Message;
+use Drupal\marketo_ma\Lead;
+
+/**
+ * @coversDefaultClass \Drupal\mma_contact\Hooks\ContactMessageInsert
+ * @group mma_contact
+ */
+class ContactSubmissionTest extends MmaContactTestBase {
+
+  public function testContactSubmission() {
+    $contact = Message::create([
+      'contact_form' => 'test_contact',
+      'name' => 'My name',
+      'mail' => 'example@example.com'
+    ]);
+    $contact->save();
+
+    // @todo Potentially one could use a mock instead.
+    $synced_leads = \Drupal::service('marketo_ma.client')->getSyncedLeads();
+    $this->assertEquals([new Lead([
+      'firstName' => 'My name',
+      'email' => 'example@example.com',
+    ])], $synced_leads);
+  }
+
+}
diff --git a/modules/mma_contact/tests/src/Kernel/MmaContactTestBase.php b/modules/mma_contact/tests/src/Kernel/MmaContactTestBase.php
new file mode 100644
index 0000000..d5e9bd2
--- /dev/null
+++ b/modules/mma_contact/tests/src/Kernel/MmaContactTestBase.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\Tests\mma_contact\Kernel;
+
+use Drupal\contact\Entity\ContactForm;
+use Drupal\KernelTests\KernelTestBase;
+
+abstract class MmaContactTestBase extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['contact', 'contact_storage', 'marketo_ma', 'mma_contact', 'mma_contact_test', 'user'];
+
+  protected function setUp() {
+    parent::setUp();
+
+    /** @var \Drupal\contact\ContactFormInterface $contact_form */
+    $contact_form = ContactForm::create([
+      'label' => 'test contact',
+      'id' => 'test_contact',
+      'recipients' => ['foo@example.com'],
+      'contact_storage_preview' => FALSE,
+    ]);
+
+    $contact_form->setThirdPartySetting('mma_contact', 'mapping', [
+      'name' => 'firstName',
+      'mail' => 'email',
+    ]);
+    $contact_form->setThirdPartySetting('mma_contact', 'enabled', 1);
+
+    $contact_form->save();
+
+    $this->installEntitySchema('contact_message');
+  }
+
+}
diff --git a/src/Form/MarketoMASettings.php b/src/Form/MarketoMASettings.php
new file mode 100644
index 0000000..5253b44
--- /dev/null
+++ b/src/Form/MarketoMASettings.php
@@ -0,0 +1,400 @@
+<?php
+
+namespace Drupal\marketo_ma\Form;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\encryption\EncryptionTrait;
+use Drupal\Core\Link;
+use Drupal\Core\Url;
+use Drupal\marketo_ma\MarketoMaApiClientInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class MarketoSettings.
+ *
+ * @package Drupal\marketo_ma\Form
+ */
+class MarketoMASettings extends ConfigFormBase {
+
+  use EncryptionTrait;
+
+  /**
+   * The Marketo MA API client.
+   *
+   * @var \Drupal\marketo_ma\MarketoMaApiClientInterface
+   */
+  protected $client;
+
+  /**
+   * Constructs a \Drupal\marketo_ma\Form\MarketoMASettings object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The factory for configuration objects.
+   * @param \Drupal\marketo_ma\MarketoMaApiClientInterface $marketo_ma_api_client
+   *   The Marketo MA API client.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, MarketoMaApiClientInterface $marketo_ma_api_client) {
+    parent::__construct($config_factory);
+    $this->client = $marketo_ma_api_client;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('marketo_ma.client')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return [
+      'marketo_ma.settings',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'settings';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    /** @var array $form */
+    $form = parent::buildForm($form, $form_state);
+
+    // Get the configuration.
+    $config = $this->config('marketo_ma.settings');
+
+    //<editor-fold desc="Form layout structure">
+    $form['marketo_ma_basic'] = [
+      '#title' => $this->t('Basic Settings'),
+      '#type' => 'fieldset',
+    ];
+    $form['marketo_ma_tabs'] = [
+      '#type' => 'vertical_tabs',
+      '#default_tab' => 'tab_api',
+    ];
+    $form['api_tab'] = [
+      '#title' => $this->t('API Configuration'),
+      '#type' => 'details',
+      '#group' => 'marketo_ma_tabs',
+    ];
+    $form['field_tab'] = [
+      '#title' => t('Field Definition'),
+      '#type' => 'details',
+      '#description' => $this->t('The fields defined here will be available for mapping.'),
+      '#group' => 'marketo_ma_tabs',
+    ];
+    $form['page_tracking_tab'] = [
+      '#title' => t('Page tracking'),
+      '#type' => 'details',
+      '#group' => 'marketo_ma_tabs',
+      '#description' => $this->t('On which pages should Marketo tracking take place.'),
+    ];
+    $form['role_tracking_tab'] = [
+      '#title' => t('Role tracking'),
+      '#type' => 'details',
+      '#group' => 'marketo_ma_tabs',
+    ];
+    //</editor-fold>
+
+    //<editor-fold desc="Basic settings">
+    $form['munchkin_account_id'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Account ID'),
+      // @see http://developers.marketo.com/blog/server-side-form-post/
+      '#description' => t('In Marketo, go to Admin > Munchkin and copy the Munchkin Account ID, which has the format of 000-AAA-000'),
+      '#maxlength' => 128,
+      '#size' => 64,
+      '#default_value' => $this->decrypt($config->get('munchkin.account_id')),
+      '#group' => 'marketo_ma_basic',
+    ];
+    $form['munchkin_javascript_library'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Munchkin Javascript Library'),
+      '#default_value' => $config->get('munchkin.javascript_library'),
+      '#required' => TRUE,
+      '#description' => $this->t('Typically this does not need to be changed and should use the default value //munchkin.marketo.net/munchkin.js'),
+      '#group' => 'marketo_ma_basic',
+    ];
+    $form['instance_host'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Marketo Instance Host'),
+      '#default_value' => $config->get('instance_host'),
+      '#required' => FALSE,
+      '#description' => $this->t('Host for your Marketo instance. Example: app-sjqe.marketo.com. Used for Forms 2.0 API.'),
+      '#group' => 'marketo_ma_basic',
+    ];
+    $form['munchkin_lead_source'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Default Lead Source'),
+      '#default_value' => $config->get('munchkin.lead_source'),
+      '#description' => $this->t('If set, LeadSource will be set to this value unless specifically overridden during data collection.'),
+      '#group' => 'marketo_ma_basic',
+    ];
+    $form['logging'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Verbose Logging'),
+      '#default_value' => $config->get('logging'),
+      '#description' => $this->t('If checked, additional data will be added to watchdog.'),
+      '#group' => 'marketo_ma_basic',
+    ];
+    //</editor-fold>
+
+    //<editor-fold desc="API configuration">
+    $form['api_tab']['tracking_method'] = [
+      '#type' => 'radios',
+      '#title' => $this->t('Tracking Method'),
+      '#description' => $this->t(':desc<br /><ol><li>:opt1</li><li>:opt2</li></ol>', [
+        ':desc' => 'Select how tracking should be handled.',
+        ':opt1' => 'Munchkin API | Client side JS.',
+        ':opt2' => 'REST API | Via the REST API on the server side.',
+      ]),
+      '#options' => [
+        'munchkin' => $this->t('Munchkin Javascript API'),
+        'api_client' => $this->t('REST API'),
+      ],
+      '#default_value' => $config->get('tracking_method'),
+      '#required' => TRUE,
+    ];
+
+    //<editor-fold desc="Munchkin Configuration">
+    $form['api_tab']['group_munchkin'] = [
+      '#title' => $this->t('Munchkin Javascript API'),
+      '#type' => 'fieldset',
+      '#states' => [
+        'visible' => [':input[name=tracking_method]' => ['value' => 'munchkin']],
+      ],
+    ];
+
+    $form['api_tab']['group_munchkin']['munchkin_api_private_key'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('API Private Key'),
+      '#default_value' => $this->decrypt($config->get('munchkin.api_private_key')),
+      '#description' => $this->t('Value can be found on the Munchkin Admin page at Admin > Integration > Munchkin'),
+      '#states' => [
+        'required' => [':input[name=tracking_method]' => ['value' => 'munchkin']],
+      ],
+    ];
+    $form['api_tab']['group_munchkin']['munchkin_partition'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Workspace (Partition)'),
+      '#default_value' => $config->get('munchkin.partition'),
+      '#required' => FALSE,
+      '#description' => $this->t('Value can be found on the Munchkin Admin page at Admin > Integration > Munchkin'),
+    ];
+    //</editor-fold>
+
+    //<editor-fold desc="REST configuration">
+    $form['api_tab']['group_rest'] = [
+      '#title' => $this->t('REST API config'),
+      '#description' => $this->t('You will need an api user and service configured for this application. See !link for details.', [
+        '!link' => Link::fromTextAndUrl('Quick Start Guide for Marketo REST API', Url::fromUri('http://developers.marketo.com/blog/quick-start-guide-for-marketo-rest-api/'))->toString(),
+      ]),
+      '#type' => 'fieldset',
+      '#states' => [
+        'visible' => [':input[name=tracking_method]' => ['value' => 'api_client']],
+      ],
+    ];
+    $form['api_tab']['group_rest']['rest_client_id'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Client Id'),
+      '#description' => $this->t('The client id for your rest api user.'),
+      '#default_value' => $this->decrypt($config->get('rest.client_id')),
+      '#states' => [
+        'required' => [':input[name=tracking_method]' => ['value' => 'api_client']],
+      ],
+    ];
+    $form['api_tab']['group_rest']['rest_client_secret'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Client Secret'),
+      '#description' => $this->t('The client secret for your rest api user.'),
+      '#default_value' => $this->decrypt($config->get('rest.client_secret')),
+      '#states' => [
+        'required' => [':input[name=tracking_method]' => ['value' => 'api_client']],
+      ],
+    ];
+    $form['api_tab']['group_rest']['rest_batch_requests'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Batch API transactions'),
+      '#description' => $this->t('Will queue activity and send data to Marketo when cron runs.'),
+      '#default_value' => $config->get('rest.batch_requests'),
+    ];
+    //</editor-fold>
+    //</editor-fold>
+
+    //<editor-fold desc="Field Definition config">
+    $form['field_tab']['field_defined_fields'] = [
+      '#type' => 'textarea',
+      '#title' => t('Marketo fields'),
+      '#description' => $this->t('Pipe "|" delimited strings of [API Name]|[Friendly Label]. Enter one field per line. This information can be found in the Marketo admin page at Admin > Field Management > Export Field Names.<p>Once API client settings have been configured, these fields can be automatically obtained from Marketo using the button below</p>'),
+      '#rows' => 10,
+      '#prefix' => '<div id="marketo-defined-fields-wrapper">',
+      '#suffix' => '</div>',
+      '#default_value' => $this->implodeFields($config->get('field.defined_fields')),
+    ];
+
+    // Add the ajax button that get's fields from the marketo API.
+    $form['field_tab']['field_api_retrieve_fields'] = [
+      '#type' => 'button',
+      '#value' => $this->t('Retrieve from Marketo'),
+      '#disabled' => !$this->client->canConnect(),
+      '#ajax' => [
+        'callback' => [$this, 'retrieveApiFields'],
+        'event' => 'mouseup',
+        'wrapper' => 'marketo-defined-fields-wrapper',
+        'progress' => array(
+          'type' => 'throbber',
+          'message' => $this->t('Retrieving fields from Marketo...'),
+        ),
+      ],
+    ];
+    //</editor-fold>
+
+    //<editor-fold desc="Page tracking config">
+    $form['page_tracking_tab']['tracking_request_path_pages'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Pages'),
+      '#default_value' => $config->get('tracking.request_path.pages'),
+      '#rows' => 10,
+      '#description' => $this->t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", [
+        '%blog' => '/blog',
+        '%blog-wildcard' => '/blog/*',
+        '%front' => '<front>',
+      ]),
+    ];
+    $form['page_tracking_tab']['tracking_request_path_negate'] = [
+      '#type' => 'radios',
+      '#options' => [
+        0 => $this->t('Track on the listed pages'),
+        1 => $this->t('Do not track on the listed pages'),
+      ],
+      '#default_value' => $config->get('tracking.request_path.negate'),
+      '#required' => TRUE,
+    ];
+    //</editor-fold>
+
+    //<editor-fold desc="Role tracking config">
+    // Get the user roles to use as options.
+    $options = \user_roles();
+    // We don't need the Role entity, just the label.
+    array_walk($options, function (&$item) {
+      $item = $item->label();
+    });
+    // Add the role tracking settings.
+    $form['role_tracking_tab']['tracking_roles'] = [
+      '#type' => 'checkboxes',
+      '#title' => t('Add tracking to specific roles'),
+      '#default_value' => $config->get('tracking.roles'),
+      '#options' => $options,
+      '#description' => $this->t("Specify roles to be tracked, Warning: %warning", [
+        '%warning' => 'If Anonymous user is unchecked, tracking history will not be available once the user logs in.',
+      ]),
+    ];
+    //</editor-fold>
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    parent::submitForm($form, $form_state);
+
+    $this->config('marketo_ma.settings')
+      ->set('tracking_method', $form_state->getValue('tracking_method'))
+      ->set('instance_host', $form_state->getValue('instance_host'))
+      ->set('logging', $form_state->getValue('logging'))
+      ->set('munchkin.lead_source', $form_state->getValue('munchkin_lead_source'))
+      ->set('munchkin.account_id', $this->encrypt($form_state->getValue('munchkin_account_id')))
+      ->set('munchkin.javascript_library', $form_state->getValue('munchkin_javascript_library'))
+      ->set('munchkin.partition', $form_state->getValue('munchkin_partition'))
+      ->set('munchkin.api_private_key', $this->encrypt($form_state->getValue('munchkin_api_private_key')))
+      ->set('rest.batch_requests', $form_state->getValue('rest_batch_requests'))
+      ->set('rest.client_id', $this->encrypt($form_state->getValue('rest_client_id')))
+      ->set('rest.client_secret', $this->encrypt($form_state->getValue('rest_client_secret')))
+      ->set('field.defined_fields', $this->explodeFields($form_state->getValue('field_defined_fields')))
+      ->set('tracking.request_path.pages', $form_state->getValue('tracking_request_path_pages'))
+      ->set('tracking.request_path.negate', $form_state->getValue('tracking_request_path_negate'))
+      ->set('tracking.roles', array_filter($form_state->getValue('tracking_roles')))
+      ->save();
+  }
+
+  /**
+   * Connects to Marketo and retrieves the API fields.
+   *
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *
+   * @return array
+   *   The form element to replace in the ajax wrapper setting.
+   */
+  public function retrieveApiFields(array &$form, FormStateInterface $form_state) {
+    // Get the fields from the api.
+    $api_fields = $this->client->getFields();
+    // Reset the defined fields value.
+    $form['field_tab']['field_defined_fields']['#value'] = '';
+    // Loop through results and get the default key/display names.
+    foreach ($api_fields as $api_field) {
+      $form['field_tab']['field_defined_fields']['#value'] .= "{$api_field['default_name']}|{$api_field['displayName']}\n";
+    }
+    // Return the form element that will bre replaced in the wrapper element.
+    return $form['field_tab']['field_defined_fields'];
+  }
+
+  /**
+   * Takes a pipe delimited input value converts it to a key/value config sequence.
+   *
+   * @param string $submitted_value
+   * @return array
+   */
+  protected function explodeFields($submitted_value) {
+    // Return an empty array for empty values.
+    if (empty($submitted_value)) {
+      return [];
+    }
+
+    // Buffer fields.
+    $fields = [];
+
+    // Split lines and delimited values into a fields array.
+    array_map(function ($line) use (&$fields) {
+      $key_value = explode('|', $line);
+      $fields[reset($key_value)] = array_pop($key_value);
+    }, array_filter(preg_split('/[\r\n]+/', $submitted_value)));
+
+    return $fields;
+  }
+
+  /**
+   * Converts a config array into a pipe delimited multi-line value.
+   *
+   * @param $config_array
+   * @return string
+   */
+  protected function implodeFields($config_array) {
+    return implode(PHP_EOL, array_map(function($k, $v){
+      return "{$k}|{$v}";
+    }, array_keys($config_array), $config_array)) . PHP_EOL;
+  }
+
+}
diff --git a/src/Lead.php b/src/Lead.php
new file mode 100644
index 0000000..03e171d
--- /dev/null
+++ b/src/Lead.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+/**
+ * Provides a lead object for the Marketo MA module.
+ *
+ * @package Drupal\marketo_ma
+ */
+class Lead implements LeadInterface, \Serializable {
+
+  /**
+   * The lead data.
+   *
+   * @var array
+   */
+  protected $data;
+
+  /**
+   * Constructs a \Drupal\marketo_ma\Lead object.
+   *
+   * @param array $lead_data
+   *   Initial lead data.
+   */
+  public function __construct($lead_data = []) {
+    // Set the lead data for this lead.
+    $this->data = $lead_data;
+    // Set the marketo tracking cookie if it is available.
+    if (!empty($_COOKIE['_mkto_trk'])) {
+      $this->setCookie($_COOKIE['_mkto_trk']);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEmail() {
+    return $this->get('email');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCookie() {
+    return $this->get('cookies');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setCookie($value) {
+    $this->set('cookies', $value);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function id() {
+    return $this->get('id');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($data_key) {
+    return !empty($this->data[$data_key]) ? $this->data[$data_key] : NULL;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($data_key, $value) {
+    $this->data[$data_key] = $value;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function data() {
+    return $this->data;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function serialize() {
+    return serialize($this->data());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function unserialize($data) {
+    $this->data = unserialize($data);
+  }
+}
diff --git a/src/LeadInterface.php b/src/LeadInterface.php
new file mode 100644
index 0000000..a432305
--- /dev/null
+++ b/src/LeadInterface.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+/**
+ * Interface for a marketo lead.
+ *
+ * @package Drupal\marketo_ma
+ */
+interface LeadInterface {
+
+  /**
+   * Get the Marketo MA lead's email address.
+   *
+   * @return string
+   *   The Lead's email.
+   */
+  public function id();
+
+  /**
+   * Get the Marketo MA lead's email address.
+   *
+   * @return string
+   *   The Lead's email.
+   */
+  public function getEmail();
+
+  /**
+   * Get the current session "_mkto_trk" cookie value.
+   *
+   * @return string
+   *   The Lead's email.
+   */
+  public function getCookie();
+
+  /**
+   * Sets the leads tracking cookie value.
+   *
+   * @param string $value
+   *   The value from the "_mkto_trk" cookie.
+   *
+   * @return $this
+   */
+  public function setCookie($value);
+
+  /**
+   * Get all the data stored in this lead.
+   *
+   * @return array
+   *   Lead data set.
+   */
+  public function data();
+
+  /**
+   * Get a specific lead value.
+   *
+   * @param string $data_key
+   *   The key used to store the data. i.e. "email".
+   * @return mixed
+   *   The requested value.
+   */
+  public function get($data_key);
+
+  /**
+   * Set a specific lead value.
+   *
+   * @param $data_key
+   *   The key used to store the data. i.e. "email".
+   * @param mixed $value
+   *
+   * @return $this
+   *   The lead object.
+   */
+  public function set($data_key, $value);
+
+}
diff --git a/src/MarketoMaApiClient.php b/src/MarketoMaApiClient.php
new file mode 100644
index 0000000..5b87d4c
--- /dev/null
+++ b/src/MarketoMaApiClient.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+use CSD\Marketo\Client;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\encryption\EncryptionTrait;
+
+/**
+ * This is a wrapper for the default API client library. It could be switched
+ * out by another module that supplies an alternate API client library.
+ */
+class MarketoMaApiClient implements MarketoMaApiClientInterface {
+
+
+  // Adds ability to encrypt/decrypt configuration.
+  use EncryptionTrait;
+
+  /**
+   * The config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The API client library. @see: https://github.com/dchesterton/marketo-rest-api.
+   *
+   * @var \CSD\Marketo\ClientInterface
+   */
+  private $client;
+
+  /**
+   * The config used to instantiate the REST client.
+   *
+   * @var array
+   */
+  private $client_config;
+
+  /**
+   * Creates the Marketo API client wrapper service.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   */
+  public function __construct(ConfigFactoryInterface $config_factory) {
+    $this->configFactory = $config_factory;
+
+    $config = $this->config();
+
+    // Build the config for the REST API Client.
+    $this->client_config = [
+      'client_id' => $this->decrypt($config->get('rest.client_id')),
+      'client_secret' => $this->decrypt($config->get('rest.client_secret')),
+      'munchkin_id' => $this->decrypt($config->get('munchkin.account_id')),
+    ];
+  }
+
+  /**
+   * Get's marketo_ma settings.
+   * @return \Drupal\Core\Config\ImmutableConfig
+   */
+  protected function config() {
+    return $this->configFactory->get('marketo_ma.settings');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFields() {
+    $fields_result = $this->getClient()->getFields()->getResult();
+
+    array_walk($fields_result, function (&$field_item) {
+      $field_item['default_name'] = $field_item['rest']['name'];
+    });
+
+    return $fields_result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function canConnect() {
+    return !empty($this->client_config['munchkin_id'])
+      && !empty($this->client_config['client_id'])
+      && !empty($this->client_config['munchkin_id']);
+  }
+
+  /**
+   * Instantiate the REST API client.
+   */
+  protected function getClient() {
+    if (!isset($this->client)) {
+      $this->client = Client::factory($this->client_config);
+    }
+    return $this->client;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLead($key, $type) {
+    $leads_result = $this->getClient()->getLeadByFilterType($key, $type)->getResult();
+    return !empty($leads_result[0]) ? new Lead(reset($leads_result)) : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLeadActivity($key, $type) {
+    // Get the lead by the filter type.
+    $lead = $this->getLead($key, $type);
+
+    /**
+     * @todo: Use configuration form to manage the default activity types.
+     *
+     * The other option is to get all activity type ids, and request activity in
+     * groups of 10 ids and aggregate the results.
+     */
+    // Use ids for common activities (max 10 activity ids pre request).
+    $activity_type_ids = '1,2,3';
+
+    // A paging token is required by the activities.json call.
+    $paging_token = $this->getClient()->getPagingToken(date('c'))->getNextPageToken();
+    // Calls get lead activities on the API client.
+    return $this->getClient()->getLeadActivity($paging_token, $lead['id'], $activity_type_ids)->getLeadActivity();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function syncLead($lead, $key = 'email', $options = []) {
+    // Add the create/update leads call to do the association.
+    $result = $this->getClient()->createOrUpdateLeads([$lead->data()], $key, $options)->getResult();
+    return $result;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteLead($leads, $args = array()) {
+    return $this->getClient()->deleteLead($leads)->getResult();
+  }
+
+}
diff --git a/src/MarketoMaApiClientInterface.php b/src/MarketoMaApiClientInterface.php
new file mode 100644
index 0000000..b666880
--- /dev/null
+++ b/src/MarketoMaApiClientInterface.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+/**
+ * Service interface for the `marketo_ma` API client.
+ *
+ * @package Drupal\marketo_ma
+ */
+interface MarketoMaApiClientInterface {
+
+
+  /**
+   * Checks whether the client has all the information necessary to attempt a
+   *  connection.
+   *
+   * @return bool
+   */
+  public function canConnect();
+
+  /**
+   * Gets the lead fields that are available for leads (AKA describe).
+   *
+   * @see: http://developers.marketo.com/documentation/rest/describe/
+   *
+   * @return array
+   *   All of the fields available for leads.
+   */
+  public function getFields();
+
+  /**
+   * Retrieves lead information.
+   *
+   * @param string $key
+   *   Lead Key, typically email address
+   * @param string $type
+   *   Lead key type, auto-detection attempted if not supplied
+   * @return \Drupal\marketo_ma\LeadInterface
+   *   The lead.
+   */
+  public function getLead($key, $type);
+
+  /**
+   * Retrieves lead activity information.
+   *
+   * @param string $key
+   *   Lead Key, typically email address
+   * @param string $type
+   *   Lead key type, auto-detection attempted if not supplied
+   */
+  public function getLeadActivity($key, $type);
+
+  /**
+   * Inserts or updates a lead.
+   *
+   * @param \Drupal\marketo_ma\LeadInterface $lead
+   *   The lead to be updated.
+   * @param string $key
+   *   Lead Key, typically email address
+   * @param array $options
+   *   Array of additional options to configure lead syncing
+   *
+   * @return array
+   *   An array of lead ids and status messages.
+   */
+  public function syncLead($lead, $key = 'email', $options = []);
+
+  /**
+   * Delete one or more leads.
+   *
+   * @param int|array $leads
+   *   Either a single lead ID or an array of lead IDs
+   * @param array     $args
+   *
+   * @return array
+   *   An array of response messages and ids (`$ret[n][status] === 'deleted'`).
+   */
+  public function deleteLead($leads, $args = array());
+
+}
diff --git a/src/MarketoMaMunchkin.php b/src/MarketoMaMunchkin.php
new file mode 100644
index 0000000..8ad382e
--- /dev/null
+++ b/src/MarketoMaMunchkin.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\encryption\EncryptionTrait;
+
+/**
+ * The marketo MA munchkin service (marketo_ma.munchkin).
+ */
+class MarketoMaMunchkin implements MarketoMaMunchkinInterface {
+
+  use EncryptionTrait;
+
+  /**
+   * The config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $config_factory;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  private $current_user;
+
+  /**
+   * The route match.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $route_match;
+
+  /**
+   * Creates the Marketo API client wrapper service.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, AccountInterface $current_user, RouteMatchInterface $route_match) {
+    $this->config_factory = $config_factory;
+    $this->current_user = $current_user;
+    $this->route_match = $route_match;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function config() {
+    // Use static caching.
+    static $config = NULL;
+    // Load config if not already loaded.
+    if (empty($config)) {
+      $config = $this->config_factory->get('marketo_ma.settings');
+    }
+
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAccountID() {
+    return $this->decrypt($this->config()->get('munchkin.account_id'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLibrary() {
+    return $this->config()->get('munchkin.javascript_library');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isConfigured() {
+    return (!empty($this->decrypt($this->config()->get('munchkin.api_private_key')))
+      && !empty($this->getAccountID())
+      && !empty($this->getLibrary())
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAction($action_type, LeadInterface $lead, $args = []) {
+    if ($action_type === MarketoMaMunchkinInterface::ACTION_ASSOCIATE_LEAD && !empty($lead->getEmail())) {
+      // The `associateLead` action requires the email and signing.
+      return [
+        'action' => $action_type,
+        'data' => $lead->data(),
+        'hash' => hash('sha1', $this->decrypt($this->config()->get('munchkin.api_private_key')) . $lead->getEmail()),
+      ];
+    } else {
+      // The cookie is used for identification. Only args are required.
+      return [
+        'action' => $action_type,
+        'data' => $args,
+      ];
+    }
+  }
+
+}
diff --git a/src/MarketoMaMunchkinInterface.php b/src/MarketoMaMunchkinInterface.php
new file mode 100644
index 0000000..1d54952
--- /dev/null
+++ b/src/MarketoMaMunchkinInterface.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+/**
+ * Service interface for the `marketo_ma` munchkin service (marketo_ma.munchkin).
+ *
+ * @package Drupal\marketo_ma
+ */
+interface MarketoMaMunchkinInterface {
+
+  /**
+   * Constants for munchkin javascript actions.
+   */
+  const ACTION_VISIT_PAGE = 'visitWebPage';
+  const ACTION_CLICK_LINK = 'clickLink';
+  const ACTION_ASSOCIATE_LEAD = 'associateLead';
+
+  /**
+   * Gets the marketo_ma config.
+   *
+   * @return \Drupal\Core\Config\ImmutableConfig|null
+   */
+  public function config();
+
+  /**
+   * Determines if all of the configuration is complete for munchkin integration.
+   *
+   * @return bool
+   */
+  public function isConfigured();
+
+  /**
+   * Gets the Munchkin account ID.
+   *
+   * @return string
+   */
+  public function getAccountID();
+
+  /**
+   * Gets the Munchkin account ID.
+   *
+   * @return string
+   */
+  public function getLibrary();
+
+  /**
+   * Get a munchkin action given the action type and lead information.
+   * @see: http://developers.marketo.com/documentation/websites/munchkin-api/
+   *
+   * @param $action_type
+   *   The type of action to be preformed. ('visitWebPage', 'clickLink', 'associateLead')
+   * @param LeadInterface $lead
+   *   The lead to be associated. Note: A Lead email is required the
+   *   `associateLead` action.
+   * @param array $args
+   *   Required args for 'visitWebPage' or 'clickLink' actions.
+   *
+   * @return array
+   *   The Drupal settings array required for the action.
+   */
+  public function getAction($action_type, LeadInterface $lead, $args = []);
+
+}
diff --git a/src/MarketoMaService.php b/src/MarketoMaService.php
new file mode 100644
index 0000000..d064439
--- /dev/null
+++ b/src/MarketoMaService.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Path\PathMatcherInterface;
+use Drupal\Core\Queue\QueueFactory;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\PrivateTempStoreFactory;
+
+/**
+ * The marketo MA worker service is responsible for most of the work the module
+ * performs.
+ */
+class MarketoMaService implements MarketoMaServiceInterface {
+
+  /**
+   * The config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $config_factory;
+
+  /**
+   * The marketo MA API client service.
+   *
+   * @var \Drupal\marketo_ma\MarketoMaApiClientInterface
+   */
+  private $api_client;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  private $current_user;
+
+  /**
+   * The route match.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $route_match;
+
+  /**
+   * The path matcher.
+   *
+   * @var \Drupal\Core\Path\PathMatcherInterface
+   */
+  protected $path_matcher;
+
+  /**
+   * The Marketo MA munchkin service.
+   *
+   * @var \Drupal\marketo_ma\MarketoMaMunchkinInterface
+   */
+  protected $munchkin;
+
+  /**
+   * The queue service.
+   *
+   * @var \Drupal\Core\Queue\QueueFactory
+   */
+  protected $queue_factory;
+
+  /**
+   * Stores the tempstore factory.
+   *
+   * @var \Drupal\user\PrivateTempStoreFactory
+   */
+  protected $temp_store_factory;
+
+  /**
+   * Creates the Marketo API client wrapper service.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   * @param \Drupal\marketo_ma\MarketoMaApiClientInterface $client
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   * @param \Drupal\Core\Path\PathMatcherInterface $path_matcher
+   *   The path matcher service.
+   * @param \Drupal\marketo_ma\MarketoMaMunchkinInterface $munchkin
+   *   The munchkin service.
+   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
+   *   The queue service.
+   * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
+   *   The factory for the temp store object.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, MarketoMaApiClientInterface $client, AccountInterface $current_user, RouteMatchInterface $route_match, PathMatcherInterface $path_matcher, MarketoMaMunchkinInterface $munchkin, QueueFactory $queue_factory, PrivateTempStoreFactory $temp_store_factory) {
+    $this->config_factory = $config_factory;
+    $this->api_client = $client;
+    $this->current_user = $current_user;
+    $this->route_match = $route_match;
+    $this->path_matcher = $path_matcher;
+    $this->munchkin = $munchkin;
+    $this->queue_factory = $queue_factory;
+    $this->temp_store_factory = $temp_store_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function trackingMethod() {
+    return $this->config()->get('tracking_method');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function pageAttachments(&$page) {
+    // Check whether we should track via the Munchkin.
+    if ($this->shouldTrackCurrentRequest()) {
+      // Add marketo ma to the page..
+      if (!empty($this->munchkin->getAccountID())) {
+        // Add the library and settings for tracking the page.
+        $page['#attached']['library'][] = 'marketo_ma/marketo-ma';
+        $page['#attached']['drupalSettings']['marketo_ma'] = [
+          'track' => TRUE,
+          'key' => $this->munchkin->getAccountID(),
+          'library' => $this->munchkin->getLibrary(),
+        ];
+      }
+
+      // Get the Lead data from temporary user storage.
+      $lead = new Lead($this->getUserData());
+      // Check for logged in user.
+      if (empty($lead->getEmail()) && !empty($this->current_user->getEmail())) {
+        $lead->set('email', $this->current_user->getEmail());
+      }
+      // Check for the munchkin option and that the munchkin api is configured.
+      if ($this->trackingMethod() == 'munchkin' && $this->munchkin->isConfigured() && !empty($lead->getEmail()) && $lead->get('associated') !== TRUE) {
+        // Set drupalSettings so JS will do the lead association.
+        $page['#attached']['drupalSettings']['marketo_ma']['actions'][] = $this->munchkin->getAction(MarketoMaMunchkinInterface::ACTION_ASSOCIATE_LEAD, $lead);
+        // Set the associated flag so we are not associating on every request.
+        $this->setUserData($lead->data() + ['associated' => TRUE]);
+      }
+      // Check for the api option and that the client can connect.
+      elseif ($this->trackingMethod() == 'api_client' && $this->api_client->canConnect() && !empty($lead->getEmail()) && $lead->get('associated') !== TRUE) {
+        // Use the API to associate the lead.
+        $this->updateLead($lead);
+        // Set the associated flag so we are not associating on every request.
+        $this->setUserData($lead->data() + ['associated' => TRUE]);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function config() {
+    // Use static caching.
+    static $config = NULL;
+    // Load config if not already loaded.
+    if (empty($config)) {
+      $config = $this->config_factory->get('marketo_ma.settings');
+    }
+
+    return $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function shouldTrackCurrentRequest() {
+    // Get track-able roles.
+    $trackable_roles = array_filter($this->config()->get('tracking.roles'));
+    // Get the current user's roles.
+    $user_roles = $this->current_user->getRoles();
+    // Checks if the current user has any trackable roles.
+    if (empty(array_intersect(array_keys($trackable_roles), $user_roles))) {
+      return FALSE;
+    }
+
+    // Get whether we are looking for a page match or a lack thereof.
+    $negate_page_match = $this->config()->get('tracking.request_path.negate');
+    // Get pages from config.
+    $pages = $this->config()->get('tracking.request_path.pages');
+
+    // Use the patch matcher service to test whether the current path matches.
+    $path_has_match = $this->path_matcher->matchPath($this->route_match->getRouteObject()->getPath(), $pages);
+
+    return (($path_has_match && !$negate_page_match) || (!$path_has_match && $negate_page_match));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function updateLead($lead) {
+    // Get the tracking method.
+    if ($this->trackingMethod() === 'api_client') {
+      // Do we need to batch the lead update?
+      if (!$this->config()->get('rest.batch_requests')) {
+        // Just sync the lead now.
+        $this->api_client->syncLead($lead);
+      } else {
+        // Queue up the lead sync.
+        $this->queue_factory->get('marketo_ma_lead')->createItem($lead);
+      }
+      // Save the lead data in the user data for future use.
+      if (!empty($this->current_user->getLastAccessedTime())) {
+        $this->setUserData(NestedArray::mergeDeep($lead->data(), !empty($this->getUserData()) ? $this->getUserData() : []));
+      }
+    } else {
+      // Save the data for the munchkin API.
+      $this->setUserData($lead->data());
+    }
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUserData($data) {
+    $this->temporaryStorage()->set('user_data', $data);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUserData() {
+    return $this->temporaryStorage()->get('user_data');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetUserData() {
+    $this->temporaryStorage()->delete('user_data');
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasUserData() {
+    return !empty($this->getUserData());
+  }
+
+  /**
+   * Gets the private temporary storage for the marketo_ma module.
+   *
+   * @return \Drupal\user\PrivateTempStore
+   */
+  protected function temporaryStorage() {
+    return $this->temp_store_factory->get('marketo_ma');
+  }
+}
diff --git a/src/MarketoMaServiceInterface.php b/src/MarketoMaServiceInterface.php
new file mode 100644
index 0000000..e6a26aa
--- /dev/null
+++ b/src/MarketoMaServiceInterface.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Drupal\marketo_ma;
+
+/**
+ * Service interface for the `marketo_ma` worker service.
+ *
+ * @todo Should this service has a syncLoad method as well, which would allow us
+ *   to send them in bulk via a queue? On top of that there might be more things
+ *   we could put into it, like the alter hook, see marketo_ma.api.php.
+ *
+ * @package Drupal\marketo_ma
+ */
+interface MarketoMaServiceInterface {
+
+
+  /**
+   * Gets the marketo_ma config.
+   *
+   * @return \Drupal\Core\Config\ImmutableConfig|null
+   */
+  public function config();
+
+  /**
+   * Handles `hook_page_attachments` for the marketo_ma module.
+   *
+   * @param $page
+   * @return null
+   */
+  public function pageAttachments(&$page);
+
+  /**
+   * Check configuration vs the current request to determine tracking requirement.
+   *
+   * @return bool
+   *   Whether the current request should be tracked.
+   */
+  public function shouldTrackCurrentRequest();
+
+  /**
+   * Gets the tracking method from settings.
+   *
+   * @return string
+   *   The tracking method.
+   */
+  public function trackingMethod();
+
+  /**
+   * Sets temporary user data for this session.
+   *
+   * @param $data
+   *   The marketo user data.
+   * @return $this
+   */
+  public function setUserData($data);
+
+  /**
+   * Gets temporary user data for the current session.
+   *
+   * @return array
+   *   The temporary user data.
+   */
+  public function getUserData();
+
+  /**
+   * Resets (deletes) the temporary user data for the current session.
+   *
+   * @return $this
+   */
+  public function resetUserData();
+
+  /**
+   * Determines whether the current session contains any temporary user data.
+   *
+   * @return bool
+   */
+  public function hasUserData();
+
+
+  /**
+   * Updates lead information respecting batch settings.
+   *
+   * @param \Drupal\marketo_ma\LeadInterface $lead
+   *   The Lead object.
+   *
+   * @return $this
+   */
+  public function updateLead($lead);
+
+}
diff --git a/src/Plugin/QueueWorker/MarketoMaLead.php b/src/Plugin/QueueWorker/MarketoMaLead.php
new file mode 100644
index 0000000..59328d6
--- /dev/null
+++ b/src/Plugin/QueueWorker/MarketoMaLead.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\marketo_ma\Plugin\QueueWorker;
+
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Queue\QueueWorkerBase;
+use Drupal\marketo_ma\MarketoMaApiClientInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Updates marketo lead.
+ *
+ * @QueueWorker(
+ *   id = "marketo_ma_lead",
+ *   title = @Translation("Marketo MA Lead"),
+ *   cron = {"time" = 60}
+ * )
+ */
+class MarketoMaLead extends QueueWorkerBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The Marketo MA API Client.
+   *
+   * @var \Drupal\marketo_ma\MarketoMaApiClientInterface
+   */
+  protected $api_client;
+
+  /**
+   * Constructs a Drupal\Component\Plugin\PluginBase object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\marketo_ma\MarketoMaApiClientInterface $api_client
+   *   The marketo API client.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MarketoMaApiClientInterface $api_client = NULL) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->api_client = $api_client;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('marketo_ma.client')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processItem($lead) {
+    // Use the API service to sync the lead.
+    $this->api_client->syncLead($lead);
+  }
+
+}
diff --git a/tests/README.md b/tests/README.md
deleted file mode 100644
index 26f9e62..0000000
--- a/tests/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-[![Build Status](https://travis-ci.org/MarketoMA/marketo_ma.svg?branch=7.x-1.x)](https://travis-ci.org/MarketoMA/marketo_ma)
-
-## Install required components
-The Marketo MA test suite uses Behat. Set it up in the regular way.
-
-```
-$ curl http://getcomposer.org/installer | php
-$ php composer.phar install
-```
-
-## Configure Marketo test settings
-The Marketo MA module comes with a behat.yml.dist file which must be copied and configured for your specific environment.
-
-```
-$ cd path/to/marketo_ma/tests
-$ cp behat.yml.dist behat.yml
-```
-
-Modify behat.yml populating valid information for:
-- marketo_settings
-- base_url
-- drupal_root
-- root
-
-## Execute tests
-To execute all tests
-
-```
-$ cd path/to/marketo_ma/tests
-$ bin/behat
-```
-
-Tags have been placed on most scenarios so the scope testing can be controlled
-
-```
-$ cd path/to/marketo_ma/tests
-$ bin/behat --tags='config&&live'
-```
\ No newline at end of file
diff --git a/tests/behat.yml.dist b/tests/behat.yml.dist
deleted file mode 100644
index 7db4779..0000000
--- a/tests/behat.yml.dist
+++ /dev/null
@@ -1,114 +0,0 @@
-default:
-  suites:
-    default:
-      contexts:
-        - FeatureContext:
-            parameters:
-              marketo_settings:
-                marketo_ma_instance_host: change_me_to_a_live_value
-                marketo_ma_munchkin_account_id: change_me_to_a_live_value
-                marketo_ma_munchkin_api_private_key: change_me_to_a_live_value
-                marketo_ma_soap_encryption_key: change_me_to_a_live_value
-                marketo_ma_soap_endpoint: change_me_to_a_live_value
-                marketo_ma_soap_user_id: change_me_to_a_live_value
-                marketo_ma_tracking_method: soap
-              
-              marketo_test_settings:
-                marketo_ma_instance_host: app-sjqe.marketo.com
-                marketo_ma_munchkin_account_id: 000-AAA-000
-                marketo_ma_munchkin_api_private_key: abcdefghijklmnopqrstuvwxyz
-                marketo_ma_tracking_method: munchkin
-                marketo_ma_user_fieldmap:
-                  uid: none
-                  name: none
-                  pass: none
-                  mail: none
-                  theme: none
-                  signature: none
-                  signature_format: none
-                  created: none
-                  access: none
-                  login: none
-                  status: none
-                  timezone: none
-                  language: none
-                  picture: none
-                  init: none
-                  data: none
-                
-              marketo_default_settings:
-                marketo_ma_instance_host: ''
-                marketo_ma_logging: 0
-                marketo_ma_munchkin_account_id: ''
-                marketo_ma_munchkin_api_private_key: ''
-                marketo_ma_munchkin_javascript_library: //munchkin.marketo.net/munchkin.js
-                marketo_ma_munchkin_lead_source: ''
-                marketo_ma_munchkin_partition: ''
-                marketo_ma_pages: "admin\r\nadmin/*\r\nbatch\r\nnode/add*\r\nnode/*/*\r\nuser/*/*"
-                marketo_ma_roles:
-                  1: 0
-                  2: 0
-                  3: 0
-                marketo_ma_soap_encryption_key: ''
-                marketo_ma_soap_endpoint: ''
-                marketo_ma_soap_proxy_host: ''
-                marketo_ma_soap_proxy_login: ''
-                marketo_ma_soap_proxy_password: ''
-                marketo_ma_soap_proxy_port: ''
-                marketo_ma_soap_user_id: ''
-                marketo_ma_tracking_method: munchkin
-                marketo_ma_user_fieldmap: []
-                marketo_ma_user_triggers:
-                  login: login
-                  insert: insert
-                  update: update
-                marketo_ma_visibility_pages: '0'
-                marketo_ma_visibility_roles: '1'
-                marketo_ma_webform_fields: "FirstName|First Name\r\nLastName|Last Name\r\nEmail|Email Address"
-                marketo_ma_webform_fields_soap: 'Retrieve from Marketo'
-
-              marketo_page_vis_only:
-                marketo_ma_munchkin_account_id: 000-AAA-000
-                marketo_ma_munchkin_api_private_key: abcdefghijklmnopqrstuvwxyz
-                marketo_ma_tracking_method: munchkin
-                marketo_ma_visibility_pages: '1'
-
-              marketo_role_vis_auth_exclude:
-                marketo_ma_munchkin_account_id: 000-AAA-000
-                marketo_ma_munchkin_api_private_key: abcdefghijklmnopqrstuvwxyz
-                marketo_ma_tracking_method: munchkin
-                marketo_ma_visibility_roles: '1'
-                marketo_ma_roles:
-                  1: 0
-                  2: '2'
-                  3: 0
-
-              marketo_role_vis_auth_include:
-                marketo_ma_munchkin_account_id: 000-AAA-000
-                marketo_ma_munchkin_api_private_key: abcdefghijklmnopqrstuvwxyz
-                marketo_ma_tracking_method: munchkin
-                marketo_ma_visibility_roles: '0'
-                marketo_ma_roles:
-                  1: 0
-                  2: '2'
-                  3: 0
-
-        - Drupal\DrupalExtension\Context\DrupalContext
-        - Drupal\DrupalExtension\Context\MinkContext
-        - Drupal\DrupalExtension\Context\MessageContext
-        - Drupal\DrupalExtension\Context\DrushContext
-  extensions:
-    Behat\MinkExtension:
-      goutte: ~
-      selenium2: ~
-      base_url: http://localhost
-    Drupal\DrupalExtension:
-      blackbox: ~
-      api_driver: 'drupal'
-      drush:
-        root: '/var/www'
-      drupal:
-        drupal_root: '/var/www'
-      region_map:
-        primary tabs: "ul.tabs.primary"
-        secondary tabs: "ul.tabs.secondary"
diff --git a/tests/composer.json b/tests/composer.json
deleted file mode 100644
index 2d59308..0000000
--- a/tests/composer.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "require": {
-    "drupal/drupal-extension": "~3.0"
-  }
-}
diff --git a/tests/features/admin/configure.feature b/tests/features/admin/configure.feature
deleted file mode 100644
index f0f1a10..0000000
--- a/tests/features/admin/configure.feature
+++ /dev/null
@@ -1,41 +0,0 @@
-@api
-Feature: Module configuration
-  In order to use the Marketo MA modules
-  As an administrator
-  I must configure the module settings
-
-  Background: Fresh module install
-    Given all Marketo MA modules are clean
-    
-  @config
-  Scenario: Configure module settings
-    When I am on the homepage
-    
-    Given I am logged in as an administrator
-    And I go to "/admin/config/search/marketo_ma"
-    When I press "Save configuration"
-    Then I should see "Account ID field is required."
-    And I should see "API Private Key field is required."
-    
-    Given I am logged in as an administrator
-    And I go to "/admin/config/search/marketo_ma"
-    And I fill in "marketo_ma_munchkin_account_id" with "bogus"
-    And I fill in "marketo_ma_munchkin_api_private_key" with "bogus"
-    When I press "Save configuration"
-    Then I should see "The configuration options have been saved."
-
-    Given I am logged in as an administrator
-    When I go to "/admin/config/search/marketo_ma"
-    And I fill in "marketo_ma_munchkin_account_id" with "bogus"
-    And I fill in "marketo_ma_munchkin_api_private_key" with "bogus"
-    And I select the radio button "SOAP API (Synchronous)" with the id "edit-marketo-ma-tracking-method-soap"
-    When I press "Save configuration"
-    Then I should see "Unable to validate SOAP API settings."
-  
-  @config @live
-  Scenario: Configure live module settings
-    Given I populate the Marketo MA config using "marketo_settings"
-    When I am logged in as an administrator
-    And I go to "/admin/config/search/marketo_ma"
-    When I press "Save configuration"
-    Then I should see "The configuration options have been saved."
\ No newline at end of file
diff --git a/tests/features/admin/drush.feature b/tests/features/admin/drush.feature
deleted file mode 100644
index 74ce18e..0000000
--- a/tests/features/admin/drush.feature
+++ /dev/null
@@ -1,32 +0,0 @@
-@api @marketo_ma_drush
-Feature: Marketo MA Drush features
-  In order to prove that drush funcitons are working properly
-  As a developer
-  I need all of these tests to run successfully
-
-  Background: Fresh module install
-    Given all Marketo MA modules are clean and using "marketo_test_settings"
-    
-  Scenario Outline: Ensure all expected drush commands are available and functioning
-    When I run drush "help" "<command>"
-    Then drush output should contain "<description>"
-    
-    When I run drush "help" "<alias>"
-    Then drush output should contain "<description>"
-    
-  Examples:
-    | command      | alias | description        |
-    | mma-fields   | mmaf  | Get Marketo fields |
-    | mma-get-lead | mmal  | Get Marketo lead   |
-    | mma-verify   | mmav  | Verify this site   |
-
-  @live
-  Scenario: Execute drush commands
-    Given I populate the Marketo MA config using "marketo_settings"
-    
-    When I run drush "mma-verify"
-    Then drush output should contain "Successfully connected to Marketo"
-    
-    When I run drush "mma-fields"
-    Then drush output should contain " Name "
-    And drush output should contain " Label "
\ No newline at end of file
diff --git a/tests/features/admin/install.feature b/tests/features/admin/install.feature
deleted file mode 100644
index 689f4fd..0000000
--- a/tests/features/admin/install.feature
+++ /dev/null
@@ -1,30 +0,0 @@
-@api
-Feature: Module setup
-  In order to prove that this module can be installed and uninstalled cleanly
-  As an administrator
-  I need to do the following
-
-  Background: Reset to a clean state
-    Given I reinstall all Marketo MA modules
-
-  @install
-  Scenario: Install all Marketo MA modules
-    Given I am logged in as an administrator
-    When I go to "/admin/config/search/marketo_ma"
-    Then I should see the heading "Marketo MA"
-    And I should see a "#marketo-ma-admin-settings-form" element
-
-  @uninstall
-  Scenario: Disable and uninstall all Marketo MA modules
-    Given I run drush "vset" "marketo_ma_bogus 'bogus'"
-
-    Given I am logged in as an administrator
-    And I go to "/admin/config/search/marketo_ma"
-    And I fill in "marketo_ma_munchkin_account_id" with "bogus"
-    And I fill in "marketo_ma_munchkin_api_private_key" with "bogus"
-    When I press "Save configuration"
-    Then I should see "The configuration options have been saved."
-
-    Given I uninstall all Marketo MA modules
-    And I run drush "vget" "marketo_ma --format=json"
-    Then drush output should contain '{"marketo_ma_bogus":"bogus"}'
diff --git a/tests/features/admin/marketo_ma_user.feature b/tests/features/admin/marketo_ma_user.feature
deleted file mode 100644
index 5d65ce1..0000000
--- a/tests/features/admin/marketo_ma_user.feature
+++ /dev/null
@@ -1,63 +0,0 @@
-@api
-Feature: Marketo MA User features
-  In order to prove that the marketo_ma_user module is working properly
-  As a developer
-  I need all of these tests to run successfully
-
-  Background: Modules are clean and users are ready to test
-    Given all Marketo MA modules are clean and using "marketo_test_settings"
-    And fields:
-      | bundle | entity | field_name         | field_type | widget_type |
-      | user   | user   | field_firstname123 | text       | text_field  |
-      | user   | user   | field_lastname123  | text       | text_field  |
-      | user   | user   | field_company123   | text       | text_field  |
-    And users:
-      | name     | mail                     | field_firstname123 | field_lastname123 | field_company123 | pass     |
-      | mmatest1 | mmatest1@mma.example.com | Mma1               | Test1             | MMA Test Co.     | password |
-      | mmatest2 | mmatest2@mma.example.com | Mma2               | Test2             | MMA Test Co.     | password |
-
-  @user_field_mapping
-  Scenario: Ensure core and custom user fields can be mapped
-    Given I am logged in as a user with the "administer marketo" permission
-
-    When I go to "/admin/config/search/marketo_ma"
-    Then I should see "[account:uid] (uid)"
-    And I should see "[account:name] (name)"
-    And I should see "field_firstname123 (field_firstname123)"
-    And I should see "field_lastname123 (field_lastname123)"
-    And I should see "field_company123 (field_company123)"
-    
-    When I select "LastName" from "[account:name] (name)"
-    And I select "LastName" from "field_company123 (field_company123)"
-    And I press "Save configuration"
-    Then I should see "The configuration options have been saved."
-    And the "[account:name] (name)" field should contain "LastName"
-    And the "field_company123 (field_company123)" field should contain "LastName"
-
-    When I go to "/user"
-    And I click "Edit"
-    And I enter "Example Co." for "field_company123"
-    And I press "Save"
-    Then I should see "The changes have been saved."
-    And the "field_company123" field should contain "Example Co."
-
-  @javascript
-  Scenario: Mapped user fields are sent to Marketo
-    Given I am logged in as a user with the "administer marketo" permission
-    When I go to "/admin/config/search/marketo_ma"
-    And I click "User Integration"
-    And I select "FirstName" from "field_firstname123 (field_firstname123)"
-    And I select "LastName" from "field_lastname123 (field_lastname123)"
-    And I press "Save configuration"
-    Then I should see "The configuration options have been saved."
-    And I go to "/user/logout"
-
-    When I go to "/user/login"
-    And I enter "mmatest1" for "edit-name"
-    And I enter "password" for "edit-pass"
-    And I press "Log in"
-    Then Munchkin associateLead action should send data
-      | field     | value                    |
-      | Email     | mmatest1@mma.example.com |
-      | FirstName | Mma1                     |
-      | LastName  | Test1                    |
diff --git a/tests/features/admin/marketo_ma_webform.feature b/tests/features/admin/marketo_ma_webform.feature
deleted file mode 100644
index f14865e..0000000
--- a/tests/features/admin/marketo_ma_webform.feature
+++ /dev/null
@@ -1,8 +0,0 @@
-@api
-Feature: Marketo MA Webform features
-  In order to prove that the marketo_ma_webform module is working properly
-  As a developer
-  I need all of these tests to run successfully
-
-  Background: Fresh module install
-    Given all Marketo MA modules are clean and using "marketo_test_settings"
diff --git a/tests/features/admin/permissions.feature b/tests/features/admin/permissions.feature
deleted file mode 100644
index d54a3f1..0000000
--- a/tests/features/admin/permissions.feature
+++ /dev/null
@@ -1,88 +0,0 @@
-@api @permissions
-Feature: Module permissions
-  In order to prove that module permissions are working properly
-  As a variety of user types
-  I need to attempt to accesss portions of the system ensuring expected results are returned
-
-  Background: Fresh module install
-    Given all Marketo MA modules are clean and using "marketo_test_settings"
-    
-  Scenario: Ensure core module permissions work as expected
-    Given I am an anonymous user
-    When I go to "/admin/config/search/marketo_ma"
-    Then the response status code should be 403
-    And I should see "Access denied"
-
-    Given I am logged in as a user with the "authenticated user" role
-    When I go to "/admin/config/search/marketo_ma"
-    Then the response status code should be 403
-    And I should see "Access denied"
-
-    Given I am logged in as an administrator
-    When I go to "/admin/config/search/marketo_ma"
-    Then the response status code should be 200
-    And I should see the heading "Marketo MA"
-    And I should see a "#marketo-ma-admin-settings-form" element
-
-    Given I am logged in as a user with the "administer marketo" permission
-    When I go to "/admin/config/search/marketo_ma"
-    Then the response status code should be 200
-    And I should see the heading "Marketo MA"
-    And I should see a "#marketo-ma-admin-settings-form" element
-
-  @marketo_ma_user
-  Scenario: Ensure Marketo MA User specific permissions work as expected
-    Given I am an anonymous user
-    When I go to "/user"
-    Then I should not see the link "Marketo" in the "primary tabs" region
-    When I go to "/user/1/marketo"
-    Then the response status code should be 403
-    
-    Given I am logged in as a user with the "authenticated user" role
-    When I go to "/user"
-    Then I should not see the link "Marketo" in the "primary tabs" region
-    When I go to "/user/1/marketo"
-    Then the response status code should be 403
-    
-    Given I am logged in as an administrator
-    When I go to "/user"
-    Then I should see the link "Marketo" in the "primary tabs" region
-    When I click "Marketo" in the "primary tabs" region
-    Then I should see the link "Lead" in the "secondary tabs" region
-    And I should see the link "Activity" in the "secondary tabs" region
-    
-    Given I am logged in as a user with the "access own marketo lead data" permissions
-    When I go to "/user"
-    Then I should see the link "Marketo" in the "primary tabs" region
-    When I click "Marketo" in the "primary tabs" region
-    Then I should see the link "Lead" in the "secondary tabs" region
-    And I should see the link "Activity" in the "secondary tabs" region
-    When I go to "/user/1/marketo"
-    Then the response status code should be 403
-    
-    Given I am logged in as a user with the "access all marketo lead data" permissions
-    When I go to "/user"
-    Then I should see the link "Marketo" in the "primary tabs" region
-    When I click "Marketo" in the "primary tabs" region
-    Then I should see the link "Lead" in the "secondary tabs" region
-    And I should see the link "Activity" in the "secondary tabs" region
-    When I go to "/user/1/marketo"
-    Then the response status code should be 200
-    Then I should see the link "Lead" in the "secondary tabs" region
-    And I should see the link "Activity" in the "secondary tabs" region
-
-  @marketo_ma_webform
-  Scenario: Ensure Marketo MA Webform specific permissions work as expected
-    Given I am an anonymous user
-    And I am accessing "/webform/marketo" belonging to a "Webform" with the title "Testorama"
-    Then the response status code should be 403
-    
-    Given I am logged in as a user with the "authenticated user" role
-    And I am accessing "/webform/marketo" belonging to a "Webform" with the title "Testorama"
-    Then the response status code should be 403
-    
-    Given I am logged in as a user with the "administrator" role
-    And I am accessing "/webform/marketo" belonging to a "Webform" with the title "Testorama"
-    Then the response status code should be 200
-    And I should see the link "Marketo" in the "secondary tabs" region
-    And I should see a "#marketo-ma-webform-settings-form" element
\ No newline at end of file
diff --git a/tests/features/bootstrap/FeatureContext.php b/tests/features/bootstrap/FeatureContext.php
deleted file mode 100644
index 1c4fa17..0000000
--- a/tests/features/bootstrap/FeatureContext.php
+++ /dev/null
@@ -1,324 +0,0 @@
-<?php
-
-use Behat\Behat\Tester\Exception\PendingException;
-use Drupal\DrupalExtension\Context\RawDrupalContext;
-use Drupal\DrupalExtension\Context\DrupalContext;
-use Drupal\DrupalExtension\Context\DrushContext;
-use Behat\Behat\Context\SnippetAcceptingContext;
-use Behat\Gherkin\Node\PyStringNode;
-use Behat\Gherkin\Node\TableNode;
-use Behat\Behat\Hook\Scope\BeforeScenarioScope;
-
-/**
- * Defines application features from the specific context.
- */
-class FeatureContext extends RawDrupalContext implements SnippetAcceptingContext {
-
-  private $params = array();
-
-  /** @var DrupalContext */
-  private $drupalContext;
-
-  /** @var DrushContext */
-  private $drushContext;
-
-  /**
-   * Keep track of fields so they can be cleaned up.
-   *
-   * @var array
-   */
-  protected $fields = array();
-
-  /** @BeforeScenario */
-  public function gatherContexts(BeforeScenarioScope $scope) {
-    $environment = $scope->getEnvironment();
-    $this->drupalContext = $environment->getContext('Drupal\DrupalExtension\Context\DrupalContext');
-    $this->drushContext = $environment->getContext('Drupal\DrupalExtension\Context\DrushContext');
-  }
-
-  /**
-   * Remove any created fields.
-   *
-   * @AfterScenario
-   */
-  public function cleanFields() {
-    // Remove any fields that were created.
-    foreach ($this->fields as $field) {
-      $this->drushContext->assertDrushCommandWithArgument("field-delete", "$field --y");
-    }
-    $this->fields = array();
-  }
-
-  /**
-   * Initializes context.
-   *
-   * Every scenario gets its own context instance.
-   * You can also pass arbitrary arguments to the
-   * context constructor through behat.yml.
-   */
-  public function __construct(array $parameters) {
-    $this->params = $parameters;
-  }
-
-  /**
-   * Resets all Marketo MA modules to their default enabled state.
-   *
-   * @Given all Marketo MA modules are clean
-   * @Given all Marketo MA modules are clean and using :config
-   */
-  public function allMarketoMaModulesClean($config = 'marketo_default_settings') {
-    $module_list = array('marketo_ma', 'marketo_ma_user', 'marketo_ma_webform');
-
-    foreach ($module_list as $module) {
-      if (!module_exists($module)) {
-        module_enable(array($module));
-      }
-    }
-
-    $this->iPopulateConfigFromBehatYml($config);
-    drupal_flush_all_caches();
-
-    foreach ($module_list as $module) {
-      if (!module_exists($module)) {
-        $message = sprintf('Module "%s" could not be enabled.', $module);
-        throw new \Exception($message);
-      }
-    }
-  }
-
-  /**
-   * Reinstalls Marketo MA modules.
-   *
-   * @Given I reinstall all Marketo MA modules
-   */
-  public function reinstallMarketoMaModules() {
-    $module_list = array('marketo_ma', 'marketo_ma_user', 'marketo_ma_webform');
-
-    $this->uninstallMarketoMaModules();
-    module_enable($module_list);
-    drupal_flush_all_caches();
-
-    foreach ($module_list as $module) {
-      if (!module_exists($module)) {
-        $message = sprintf('Module "%s" could not be enabled.', $module);
-        throw new \Exception($message);
-      }
-    }
-  }
-
-  /**
-   * Uninstalls all Marketo MA modules.
-   *
-   * @Given I uninstall all Marketo MA modules
-   */
-  public function uninstallMarketoMaModules() {
-    $module_list = array('marketo_ma', 'marketo_ma_user', 'marketo_ma_webform');
-
-    module_disable($module_list);
-    drupal_uninstall_modules($module_list);
-    drupal_flush_all_caches();
-
-    foreach ($module_list as $module) {
-      if (module_exists($module)) {
-        $message = sprintf('Module "%s" could not be uninstalled.', $module);
-        throw new \Exception($message);
-      }
-    }
-  }
-
-  /**
-   * Reinstalls the given modules and asserts that they are enabled.
-   *
-   * @Given the :modules module(s) is/are clean
-   */
-  public function assertModulesClean($modules) {
-    $this->assertModulesUninstalled($modules);
-    $this->assertModulesEnabled($modules);
-  }
-
-  /**
-   * Asserts that the given modules are enabled
-   *
-   * @Given the :modules module(s) is/are enabled
-   */
-  public function assertModulesEnabled($modules) {
-    $module_list = preg_split("/,\s*/", $modules);
-    module_enable($module_list, TRUE);
-    foreach ($module_list as $module) {
-      if (!module_exists($module)) {
-        $this->drushContext->assertDrushCommandWithArgument("pm-list", '--package="Marketo"');
-        echo $this->drushContext->readDrushOutput();
-        $message = sprintf('Module "%s" is not enabled.', $module);
-        throw new \Exception($message);
-      }
-    }
-  }
-
-  /**
-   * Asserts that the given modules are disabled
-   *
-   * @Given the :modules module(s) is/are disabled
-   */
-  public function assertModulesDisabled($modules) {
-    $module_list = preg_split("/,\s*/", $modules);
-    module_disable($module_list, TRUE);
-    foreach ($module_list as $module) {
-      if (module_exists($module)) {
-        $this->drushContext->assertDrushCommandWithArgument("pm-list", '--package="Marketo"');
-        echo $this->drushContext->readDrushOutput();
-        $message = sprintf('Module "%s" is not disabled.', $module);
-        throw new \Exception($message);
-      }
-    }
-  }
-
-  /**
-   * Asserts that the given modules are uninstalled
-   *
-   * @Given the :modules module(s) is/are uninstalled
-   */
-  public function assertModulesUninstalled($modules) {
-    $module_list = preg_split("/,\s*/", $modules);
-    $this->assertModulesDisabled($modules);
-    drupal_uninstall_modules($module_list, TRUE);
-    foreach ($module_list as $module) {
-      if (module_exists($module)) {
-        $this->drushContext->assertDrushCommandWithArgument("pm-list", '--package="Marketo"');
-        echo $this->drushContext->readDrushOutput();
-        $message = sprintf('Module "%s" could not be uninstalled.', $module);
-        throw new \Exception($message);
-      }
-    }
-  }
-
-  /**
-   * Creates content of the given type and navigates to a path belonging to it.
-   *
-   * @Given I am accessing :path belonging to a/an :type (content )with the title :title
-   */
-  public function accessNodePath($path, $type, $title) {
-    // @todo make this easily extensible.
-    $node = (object) array(
-          'title' => $title,
-          'type' => $type,
-          'body' => $this->getRandom()->string(255),
-    );
-    $saved = $this->nodeCreate($node);
-    // Set internal page on the new node.
-    $this->getSession()->visit($this->locatePath('/node/' . $saved->nid . $path));
-  }
-
-  /**
-   * @Given Marketo MA is configured using settings from :config
-   */
-  public function marketoMaIsConfiguredUsingSettingsFrom($config) {
-    $this->assertModulesClean("marketo_ma, marketo_ma_user, marketo_ma_webform");
-
-    $settings = array_merge($this->params['marketo_default_settings'], $this->params[$config]);
-    foreach ($settings as $key => $value) {
-      variable_set($key, $value);
-    }
-  }
-
-  /**
-   * @Given I populate the Marketo MA config using :config
-   */
-  public function iPopulateConfigFromBehatYml($config) {
-    $settings = array_merge($this->params['marketo_default_settings'], $this->params[$config]);
-    foreach ($settings as $key => $value) {
-      variable_set($key, $value);
-    }
-  }
-
-  /**
-   * Creates fields for the given entity type.
-   * | bundle | entity | field_name    | field_type | widget_type |
-   * | user   | user   | field_company | text       | text_field  |
-   * | ...    | ...    | ...           | ...        | ...         |
-   * 
-   * @Given fields:
-   */
-  public function createCustomUserFields(TableNode $fieldTable) {
-    foreach ($fieldTable->getHash() as $fieldHash) {
-      $field = (object) $fieldHash;
-      array_push($this->fields, $field->field_name);
-      $this->drushContext->assertDrushCommandWithArgument("field-create", "$field->bundle $field->field_name,$field->field_type,$field->widget_type --entity_type=$field->entity");
-    }
-  }
-
-  /**
-   * @Then Munchkin tracking should be enabled
-   */
-  public function assertMunchkinTrackingEnabled() {
-    $enabled = $this->getSession()->evaluateScript("return (Drupal.settings.marketo_ma === undefined) ? false : Drupal.settings.marketo_ma.track;");
-    if ($enabled !== TRUE) {
-      throw new Exception("Munchkin tracking is excpected to be ON but is currently OFF");
-    }
-  }
-
-  /**
-   * @Then Munchkin tracking should not be enabled
-   * @Then Munchkin tracking should be disabled
-   */
-  public function assertMunchkinTrackingNotEnabled() {
-    $enabled = $this->getSession()->evaluateScript("return (Drupal.settings.marketo_ma === undefined) ? false : Drupal.settings.marketo_ma.track;");
-    if ($enabled !== FALSE) {
-      throw new Exception("Munchkin tracking is expected to be OFF but is currently ON");
-    }
-  }
-
-  /**
-   * @Then Munchkin associateLead action should send data
-   */
-  public function assertMunchkinAssociateLeadSendData(TableNode $fields) {
-    $actions = $this->getSession()->evaluateScript("return Drupal.settings.marketo_ma.actions");
-    if ((isset($actions[0]['action']) && $actions[0]['action'] == 'associateLead') == FALSE) {
-      throw new \Exception("Munchkin associateLead did not fire as expected");
-    }
-    foreach ($fields->getHash() as $row) {
-      if ($actions[0]['data'][$row['field']] != $row['value']) {
-        $message = sprintf('Field "%s" was expected to be "%s" but was "%s".', $row['field'], $row['value'], $actions[0]['data'][$row['field']]);
-        throw new \Exception($message);
-      }
-    }
-  }
-
-  /**
-   * @Given I evaluate script:
-   */
-  public function iEvaluateScript(PyStringNode $script) {
-    $this->getSession()->evaluateScript($script->getRaw());
-  }
-
-  /**
-   * @Given I execute script:
-   */
-  public function iExecuteScript(PyStringNode $script) {
-    $this->getSession()->executeScript($script->getRaw());
-  }
-
-  /**
-   * @Given given javascript variable :variable equals :value
-   */
-  public function givenJavascriptVariableEquals($variable, $value) {
-    $result = $this->getSession()->evaluateScript("$variable == $value");
-    if ($result === FALSE) {
-      throw new \Exception(sprintf("The variable '%s' was expected to be '%s' but evaluated to %s", $variable, $value, $result));
-    }
-  }
-
-  /**
-   * @Given given javascript variable :variable does not equal :value
-   */
-  public function givenJavascriptVariableDoesNotEqual($variable, $value) {
-    throw new PendingException();
-  }
-
-  /**
-   * @Given I take a dump
-   */
-  public function iTakeADump() {
-    var_dump($this->params);
-  }
-
-}
diff --git a/tests/features/travis-ci.feature b/tests/features/travis-ci.feature
deleted file mode 100644
index c2a14ae..0000000
--- a/tests/features/travis-ci.feature
+++ /dev/null
@@ -1,26 +0,0 @@
-@travis
-Feature: Travis CI
-  In order to run tests for this module
-  As a developer
-  I must ensure that the Travis CI test environment has been setup successfully
-
-  Scenario: Site is accessiable
-    Given I am on the homepage
-    Then I should see the heading "Welcome to Site-Install"
-  
-  Scenario: Friendly URLs are working as expected
-    When I visit "/?q=user"
-    Then I should see a "#user-login" element
-    
-    When I visit "/user"
-    Then I should see a "#user-login" element
-  
-  @api
-  Scenario: DrupalExtension features are working as expected
-    Given I am logged in as an administrator
-    And I visit "/admin"
-    Then I should see the heading "Administration"
-    
-    When I run drush "status" "bootstrap"
-    Then drush output should contain "Successful"
-    
\ No newline at end of file
diff --git a/tests/features/visitor/browse.feature b/tests/features/visitor/browse.feature
deleted file mode 100644
index 80cad54..0000000
--- a/tests/features/visitor/browse.feature
+++ /dev/null
@@ -1,81 +0,0 @@
-@api @javascript @visitor
-Feature: Browser tests
-
-  Background: Fresh module install
-    Given all Marketo MA modules are clean and using "marketo_test_settings"
-
-  @page_visibility
-  Scenario: Page visibilty when using default "All pages except those listed"
-    And I am logged in as a user with the "administrator" role
-    
-    When I am on the homepage
-    Then Munchkin tracking should be enabled
-    
-    When I am viewing a "Article" with the title "Foo"
-    Then Munchkin tracking should be enabled
-    
-    When I visit "/admin"
-    Then Munchkin tracking should be disabled
-    
-    When I visit "/admin/config/search/marketo_ma"
-    Then Munchkin tracking should be disabled
-    
-    When I visit "/node/add"
-    Then Munchkin tracking should be disabled
-    
-    When I visit "/node/add/article"
-    Then Munchkin tracking should be disabled
-    
-  @page_visibility
-  Scenario: Page visibilty when using "Only the pages listed"
-    Given I populate the Marketo MA config using "marketo_page_vis_only"
-    And I am logged in as a user with the "administrator" role
-    
-    When I am on the homepage
-    Then Munchkin tracking should be disabled
-    
-    When I am viewing a "Article" with the title "Foo"
-    Then Munchkin tracking should be disabled
-    
-    When I visit "/admin"
-    Then Munchkin tracking should be enabled
-    
-    When I visit "/admin/config/search/marketo_ma"
-    Then Munchkin tracking should be enabled
-    
-    When I visit "/node/add"
-    Then Munchkin tracking should be enabled
-    
-    When I visit "/node/add/article"
-    Then Munchkin tracking should be enabled
-    
-  @role_visibility
-  Scenario: Role visibilty when using default "All roles except those selected"
-    
-    When I am an anonymous user
-    And I am on the homepage
-    Then Munchkin tracking should be enabled
-    
-    When I am logged in as a user with the "authenticated user" role
-    And I am on the homepage
-    Then Munchkin tracking should be enabled
-    
-    Given I populate the Marketo MA config using "marketo_role_vis_auth_exclude"
-    
-    When I am an anonymous user
-    And I am on the homepage
-    Then Munchkin tracking should be enabled
-    
-    When I am logged in as a user with the "authenticated user" role
-    And I am on the homepage
-    Then Munchkin tracking should be disabled
-
-    Given I populate the Marketo MA config using "marketo_role_vis_auth_include"
-    
-    When I am an anonymous user
-    And I am on the homepage
-    Then Munchkin tracking should be disabled
-    
-    When I am logged in as a user with the "authenticated user" role
-    And I am on the homepage
-    Then Munchkin tracking should be enabled
diff --git a/tests/marketo_ma_fields.yml b/tests/marketo_ma_fields.yml
deleted file mode 100644
index 2f1a2e8..0000000
--- a/tests/marketo_ma_fields.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-default:
-  context:
-    class: FeatureContext
-    parameters:
-      marketo_default_settings:
-        marketo_ma_instance_host: app-sjqe.marketo.com
-        marketo_ma_logging: 0
-        marketo_ma_munchkin_account_id: foo
-        marketo_ma_munchkin_api_private_key: bar
-        marketo_ma_munchkin_javascript_library: //munchkin.marketo.net/munchkin.js
-        marketo_ma_munchkin_lead_source: ''
-        marketo_ma_munchkin_partition: ''
-        marketo_ma_pages: "admin\r\nadmin/*\r\nbatch\r\nnode/add*\r\nnode/*/*\r\nuser/*/*"
-        marketo_ma_roles:
-          1: 0
-          2: 0
-          3: 0
-        marketo_ma_soap_encryption_key: ''
-        marketo_ma_soap_endpoint: ''
-        marketo_ma_soap_proxy_host: ''
-        marketo_ma_soap_proxy_login: ''
-        marketo_ma_soap_proxy_password: ''
-        marketo_ma_soap_proxy_port: ''
-        marketo_ma_soap_user_id: ''
-        marketo_ma_tracking_method: munchkin
-        marketo_ma_user_fieldmap:
-          field_first_name: none
-          field_last_name: none
-        marketo_ma_user_triggers:
-          login: login
-          insert: insert
-          update: update
-        marketo_ma_visibility_pages: '0'
-        marketo_ma_visibility_roles: '1'
-        marketo_ma_webform_fields: "FirstName|First Name\r\nLastName|Last Name\r\nEmail|Email Address"
-        marketo_ma_webform_fields_soap: 'Retrieve from Marketo'
-        
-      marketo_except_page_vis:
-        marketo_ma_visibility_pages: '1'
-        
-      marketo_role_vis_auth_exclude:
-        marketo_ma_visibility_roles: '1'
-        marketo_ma_roles:
-          1: 0
-          2: '2'
-          3: 0
-          
-      marketo_role_vis_auth_include:
-        marketo_ma_visibility_roles: '0'
-        marketo_ma_roles:
-          1: 0
-          2: '2'
-          3: 0
diff --git a/tests/src/FunctionalJavascript/MarketoMaApiClientTest.php b/tests/src/FunctionalJavascript/MarketoMaApiClientTest.php
new file mode 100644
index 0000000..2e2a7e5
--- /dev/null
+++ b/tests/src/FunctionalJavascript/MarketoMaApiClientTest.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace Drupal\Tests\marketo_ma\FunctionalJavascript;
+
+/**
+ * Tests the Marketo MA module in API Client mode.
+ *
+ * @group marketo_ma-js
+ */
+class MarketoMaApiClientTest extends MmaJavascriptTestBase {
+
+  /**
+   * @var \Drupal\marketo_ma\MarketoMaApiClientInterface The marketo_ma client service.
+   */
+  protected $client;
+
+  /**
+   * @var \Drupal\marketo_ma\MarketoMaServiceInterface The marketo_ma client service.
+   */
+  protected $service;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Get the encryption service.
+    $encryption_service = \Drupal::service('encryption');
+
+    // Get the API settings.
+    $config = \Drupal::configFactory()->getEditable('marketo_ma.settings');
+
+    // Set up required settings.
+    $config->set('tracking_method', 'api_client');
+    $config->set('munchkin.account_id', $encryption_service->encrypt(getenv('marketo_ma_munchkin_account_id')));
+    $config->set('rest.client_id', $encryption_service->encrypt(getenv('marketo_ma_rest_client_id')));
+    $config->set('rest.client_secret', $encryption_service->encrypt(getenv('marketo_ma_rest_client_secret')));
+    $config->save();
+
+    // Get the API client service.
+    $this->client = \Drupal::service('marketo_ma.client');
+    // Get the API client service.
+    $this->service = \Drupal::service('marketo_ma');
+
+  }
+
+  /**
+   * Tests if a lead is associated when the user logs in.
+   */
+  public function testMunchkinLeadAssociation() {
+    $marketo_user = $this->drupalCreateUser();
+
+    $this->drupalGet('<front>');
+    $page = $this->getSession()->getPage();
+
+    // Make sure there weren't any page errors.
+    self::assertEmpty($page->find('css', 'div[role=alert]'));
+
+    // Get drupal settings.
+    $drupal_settings = $this->getDrupalSettings();
+
+    // The marketo track settings should be there.
+    self::assertTrue(!empty($drupal_settings['marketo_ma']));
+    // But no actions.
+    self::assertTrue(empty($drupal_settings['marketo_ma']['actions']));
+
+    // Log into drupal.
+    $this->drupalLogin($marketo_user);
+    // Get drupal settings.
+    $drupal_settings = $this->getDrupalSettings();
+
+    // Load the lead via the API.
+    $lead = $this->client->getLead('email', $marketo_user->getEmail());
+
+    // Make sure A lead result was returned.
+    self::assertTrue(!empty($lead), 'A lead was associated');
+    // Makes sure the lead email is the same as the user.
+    self::assertEquals($lead->getEmail(), $marketo_user->getEmail(), 'Marketo client lead email matches.');
+    // make sure we have a marketo lead ID for the lead.
+    self::assertNotEmpty($lead->id(), 'Marketo client lead has a lead id.');
+
+    // Clean up and delete the lead.
+    $this->client->deleteLead([$lead->id()]);
+
+
+    // The marketo track settings should be there.
+    self::assertTrue(!empty($drupal_settings['marketo_ma']));
+    // But no actions.
+    self::assertTrue(empty($drupal_settings['marketo_ma']['actions']));
+
+    // Get a random cache busting page.
+    $this->drupalGet($this->randomMachineName());
+    // Get drupal settings.
+    $drupal_settings = $this->getDrupalSettings();
+
+    // The marketo track settings should be there.
+    self::assertTrue(!empty($drupal_settings['marketo_ma']));
+    // But no actions.
+    self::assertTrue(empty($drupal_settings['marketo_ma']['actions']));
+
+  }
+
+}
diff --git a/tests/src/FunctionalJavascript/MarketoMaMunchkinTest.php b/tests/src/FunctionalJavascript/MarketoMaMunchkinTest.php
new file mode 100644
index 0000000..19b36f3
--- /dev/null
+++ b/tests/src/FunctionalJavascript/MarketoMaMunchkinTest.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Drupal\Tests\marketo_ma\FunctionalJavascript;
+
+use Drupal\marketo_ma\MarketoMaMunchkinInterface;
+
+/**
+ * Tests the Marketo MA module in Munchkin mode.
+ *
+ * @group marketo_ma-js
+ */
+class MarketoMaMunchkinTest extends MmaJavascriptTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Get the encryption service.
+    $encryption_service = \Drupal::service('encryption');
+
+    // Get the API settings.
+    $config = \Drupal::configFactory()->getEditable('marketo_ma.settings');
+
+    // Set up required settings.
+    $config->set('tracking_method', 'munchkin');
+    $config->set('instance_host', $encryption_service->encrypt(getenv('marketo_ma_instance_host')));
+    $config->set('munchkin.account_id', $encryption_service->encrypt(getenv('marketo_ma_munchkin_account_id')));
+    $config->set('munchkin.api_private_key', $encryption_service->encrypt(getenv('marketo_ma_munchkin_api_private_key')));
+    $config->save();
+
+    // Write the encryption keto to settings.php
+//    $this->writeSettings([
+//      'config' => [
+//        'marketo_ma.settings' => (object) [
+//          'value' => [
+//            'tracking_method' => 'munchkin',
+//            'instance_host' => $encryption_service->encrypt(getenv('marketo_ma_instance_host')),
+//            'munchkin.account_id' => $encryption_service->encrypt(getenv('marketo_ma_munchkin_account_id')),
+//            'munchkin.api_private_key' => $encryption_service->encrypt(getenv('marketo_ma_munchkin_api_private_key')),
+//          ],
+//          'required' => TRUE,
+//        ],
+//      ],
+//    ]);
+  }
+
+  /**
+   * Tests if a lead is associated when the user logs in.
+   */
+  public function testMunchkinLeadAssociation() {
+    $marketo_user = $this->drupalCreateUser();
+
+    $this->drupalGet('<front>');
+    $page = $this->getSession()->getPage();
+    // Get the marketo cookie.
+    $marketo_cookie = $this->getSession()->getCookie('_mkto_trk');
+    // Get drupal settings.
+    $drupal_settings = $this->getDrupalSettings();
+
+    // Make sure there weren't any page errors.
+    self::assertEmpty($page->find('css', 'div[role=alert]'));
+    // The marketo track settings should be there.
+    self::assertTrue($drupal_settings['marketo_ma']['track'], 'The marketo track flag has been set.');
+    // But no actions.
+    self::assertTrue(empty($drupal_settings['marketo_ma']['actions']), 'There are no marketo actions for this request.');
+    // Make sure the marketo cookie is there.
+    self::assertNotEmpty($marketo_cookie, 'The marketo cookie has been set.');
+
+    // Log into drupal.
+    $this->drupalLogin($marketo_user);
+    // Get the marketo cookie.
+    $marketo_cookie = $this->getSession()->getCookie('_mkto_trk');
+    // Get drupal settings.
+    $drupal_settings = $this->getDrupalSettings();
+
+    // The marketo track settings should be there.
+    self::assertTrue(!empty($drupal_settings['marketo_ma']));
+    // There should be an "associateLead" action.
+    self::assertTrue(!empty($drupal_settings['marketo_ma']['actions'][0]['action']), 'A munchkin action exists');
+    self::assertTrue($drupal_settings['marketo_ma']['actions'][0]['action'] === MarketoMaMunchkinInterface::ACTION_ASSOCIATE_LEAD, 'The first action will associate the lead');
+    self::assertTrue(!empty($drupal_settings['marketo_ma']['actions'][0]['hash']), 'The munchkin hash exists.');
+    self::assertTrue(!empty($drupal_settings['marketo_ma']['actions'][0]['data']['email']), 'The user email exists.');
+    self::assertTrue($drupal_settings['marketo_ma']['actions'][0]['data']['email'] === $marketo_user->getEmail(), 'The user email exists.');
+    // Make sure the marketo cookie is there.
+    self::assertNotEmpty($marketo_cookie, 'The marketo cookie has been set.');
+
+    // Get a random cache busting page.
+    $this->drupalGet($this->randomMachineName());
+
+    $drupal_settings = $this->getDrupalSettings();
+    // Get the marketo cookie.
+    $marketo_cookie = $this->getSession()->getCookie('_mkto_trk');
+
+    // The marketo track settings should be there.
+    self::assertTrue(!empty($drupal_settings['marketo_ma']));
+    // But no actions.
+    self::assertTrue(empty($drupal_settings['marketo_ma']['actions']));
+    // Make sure the marketo cookie is there.
+    self::assertNotEmpty($marketo_cookie, 'The marketo cookie has been set.');
+
+  }
+
+}
diff --git a/tests/src/FunctionalJavascript/MmaJavascriptTestBase.php b/tests/src/FunctionalJavascript/MmaJavascriptTestBase.php
new file mode 100644
index 0000000..4b27b6c
--- /dev/null
+++ b/tests/src/FunctionalJavascript/MmaJavascriptTestBase.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\Tests\marketo_ma\FunctionalJavascript;
+
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+
+/**
+ * Base for Marketo MA functional javascript tests.
+ *
+ * @group marketo_ma-js
+ */
+abstract class MmaJavascriptTestBase extends JavascriptTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'user',
+    'encryption',
+    'marketo_ma',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    // Generate a random encryption key.
+    $settings['settings']['encryption_key'] = (object) array(
+      'value' => base64_encode(random_bytes(32)),
+      'required' => TRUE,
+    );
+    // Write the encryption keto to settings.php
+    $this->writeSettings($settings);
+
+  }
+
+  /**
+   * Gets drupal settings and parses into an php array.
+   *
+   * @return array
+   *   The drupal settings object.
+   */
+  public function getDrupalSettings() {
+    // Get the active drupal settings from the session.
+    return $this->getSession()->evaluateScript('function(){ if (drupalSettings !== \'undefined\') { return drupalSettings; }}()');
+  }
+
+}
diff --git a/tests/src/Kernel/MarketoMaApiClientTest.php b/tests/src/Kernel/MarketoMaApiClientTest.php
new file mode 100644
index 0000000..1bba69f
--- /dev/null
+++ b/tests/src/Kernel/MarketoMaApiClientTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Drupal\Tests\marketo_ma\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\marketo_ma\Lead;
+use \Drupal\marketo_ma\MarketoMaApiClientInterface;
+use Drupal\Core\Site\Settings;
+
+/**
+ * @group marketo_ma
+ */
+class MarketoMaApiClientTest extends KernelTestBase {
+
+  /**
+   * @var \Drupal\marketo_ma\MarketoMaApiClientInterface The marketo_ma client service.
+   */
+  protected $api_client;
+
+  protected $test_lead_email;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'user',
+    'encryption',
+    'marketo_ma',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Install config for this module.
+    $this->installConfig('marketo_ma');
+
+    // Get the settings object.
+    $settings = Settings::getAll();
+    // Add a randomly generated encryption key.
+    new Settings($settings + ['encryption_key' => base64_encode(random_bytes(32))]);
+
+    // Get the encryption service.
+    $encryption_service = \Drupal::service('encryption');
+
+    // Get the API settings.
+    $config = \Drupal::configFactory()->getEditable('marketo_ma.settings');
+
+    // Set up required settings.
+    $config->set('tracking_method', 'api_client')
+      ->set('munchkin.account_id', $encryption_service->encrypt(getenv('marketo_ma_munchkin_account_id')))
+      ->set('rest.client_id', $encryption_service->encrypt(getenv('marketo_ma_rest_client_id')))
+      ->set('rest.client_secret', $encryption_service->encrypt(getenv('marketo_ma_rest_client_secret')))
+      ->save();
+
+    // Get the API client service.
+    $this->api_client = \Drupal::service('marketo_ma.client');
+
+    // Set the test lead ID.
+    $this->test_lead_email = 'test_lead-'.$this->randomMachineName().'@marketo.com';
+  }
+
+
+  /**
+   * Tests the marketo_ma service.
+   */
+  public function testMarketoMaService() {
+    self::assertTrue($this->api_client instanceof MarketoMaApiClientInterface);
+    self::assertTrue($this->api_client->canConnect());
+  }
+
+
+  /**
+   * Tests the getFields call.
+   */
+  public function testGetFields() {
+    $fields = $this->api_client->getFields();
+
+    self::assertTrue(is_array($fields));
+    self::assertNotEmpty($fields);
+  }
+
+  /**
+   * Tests the creating and deleting a lead.
+   */
+  public function testCreateLead() {
+    // Create a new lead.
+    $result = $this->_sync_lead();
+
+    self::assertEquals('created', $result[0]['status']);
+    self::assertTrue(is_numeric($result[0]['id']));
+
+    // Delete the newly created lead.
+    $delete_result = $this->api_client->deleteLead($result[0]['id']);
+
+    // Check that the status is deleted.
+    self::assertEquals('deleted', $delete_result[0]['status']);
+    // Double check the lead ids match up.
+    self::assertEquals($result[0]['id'], $delete_result[0]['id']);
+  }
+
+  /**
+   * Tests the retrieval of a lead by email address.
+   */
+  public function testGetLead() {
+    // Create a new lead.
+    $create_result = $this->_sync_lead();
+
+    // Retrieve the new lead via the api call.
+    $lead = $this->api_client->getLead('email', $this->test_lead_email);
+
+    // We should have a numeric lead id,
+    self::assertTrue(is_numeric($lead->id()));
+    // Check the retrieved leads email against the test value.
+    self::assertEquals($this->test_lead_email, $lead->getEmail());
+    // check that the lead ids match up.
+    self::assertEquals($create_result[0]['id'], $lead->id());
+    // Clean up and delete the lead.
+    $this->api_client->deleteLead($create_result[0]['id']);
+  }
+
+
+  /**
+   * Tests the retrieval of a lead by email address.
+   */
+  public function testGetLeadActivity() {
+    // Create a new lead.
+    $create_result = $this->_sync_lead();
+
+    // Get lead activity.
+    $activity = $this->api_client->getLeadActivity($this->test_lead_email, 'email');
+
+    // @todo: Add test for valy activity information.
+
+    // Clean up and delete the lead.
+    $this->api_client->deleteLead($create_result[0]['id']);
+  }
+
+  /**
+   * @return array the syncLead result.
+   */
+  private function _sync_lead() {
+    return $this->api_client->syncLead(new Lead([
+      'email' => $this->test_lead_email,
+      'firstName' => 'Lead 1',
+      'postalCode' => '94105',
+    ]));
+  }
+
+}
diff --git a/tests/src/Kernel/MarketoMaMunchkinServiceTest.php b/tests/src/Kernel/MarketoMaMunchkinServiceTest.php
new file mode 100644
index 0000000..0d05a34
--- /dev/null
+++ b/tests/src/Kernel/MarketoMaMunchkinServiceTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\Tests\marketo_ma\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\Core\Site\Settings;
+use Drupal\marketo_ma\MarketoMaMunchkinInterface;
+
+/**
+ * @group marketo_ma
+ */
+class MarketoMaMunchkinServiceTest extends KernelTestBase {
+
+  /**
+   * @var \Drupal\marketo_ma\MarketoMaMunchkinInterface The marketo_ma munchkin service.
+   */
+  protected $munchkin;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'user',
+    'encryption',
+    'marketo_ma',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Install config for this module.
+    $this->installConfig('marketo_ma');
+
+    // Get the settings object.
+    $settings = Settings::getAll();
+    // Add a randomly generated encryption key.
+    new Settings($settings + ['encryption_key' => base64_encode(random_bytes(32))]);
+
+    // Get the encryption service.
+    $encryption_service = \Drupal::service('encryption');
+
+    // Get the API settings.
+    $config = \Drupal::configFactory()->getEditable('marketo_ma.settings');
+
+    // Set up required settings.
+    $config->set('tracking_method', 'munchkin');
+    $config->set('munchkin.account_id', $encryption_service->encrypt(getenv('marketo_ma_munchkin_account_id')));
+    $config->set('munchkin.api_private_key', $encryption_service->encrypt(getenv('marketo_ma_munchkin_api_private_key')));
+    $config->save();
+
+    // Get the API client service.
+    $this->munchkin = \Drupal::service('marketo_ma.munchkin');
+  }
+
+  /**
+   * Tests the marketo_ma service.
+   */
+  public function testMarketoMaService() {
+    self::assertTrue($this->munchkin instanceof MarketoMaMunchkinInterface);
+
+    self::assertTrue($this->munchkin->isConfigured());
+    self::assertEquals(getenv('marketo_ma_munchkin_account_id'), $this->munchkin->getAccountID());
+  }
+
+}
diff --git a/tests/src/Kernel/MarketoMaServiceTest.php b/tests/src/Kernel/MarketoMaServiceTest.php
new file mode 100644
index 0000000..5ce675e
--- /dev/null
+++ b/tests/src/Kernel/MarketoMaServiceTest.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace Drupal\Tests\marketo_ma\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\Core\Site\Settings;
+use Drupal\marketo_ma\Lead;
+use Drupal\marketo_ma\MarketoMaApiClientInterface;
+use Drupal\marketo_ma\MarketoMaServiceInterface;
+
+/**
+ * @group marketo_ma
+ */
+class MarketoMaServiceTest extends KernelTestBase {
+
+  /**
+   * The marketo_ma service.
+   *
+   * @var \Drupal\marketo_ma\MarketoMaServiceInterface
+   */
+  protected $service;
+
+  /**
+   * The marketo_ma rest client service.
+   *
+   * @var MarketoMaApiClientInterface
+   */
+  protected $api_client;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'user',
+    'encryption',
+    'marketo_ma',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Install config for this module.
+    $this->installConfig('marketo_ma');
+
+    // Get the settings object.
+    $settings = Settings::getAll();
+    // Add a randomly generated encryption key.
+    new Settings($settings + ['encryption_key' => base64_encode(random_bytes(32))]);
+
+    // Get the encryption service.
+    $encryption_service = \Drupal::service('encryption');
+
+    // Get the API settings.
+    $config = \Drupal::configFactory()->getEditable('marketo_ma.settings');
+
+    // Set up required settings.
+    $config->set('tracking_method', 'api_client')
+      ->set('munchkin.account_id', $encryption_service->encrypt(getenv('marketo_ma_munchkin_account_id')))
+      ->set('rest.client_id', $encryption_service->encrypt(getenv('marketo_ma_rest_client_id')))
+      ->set('rest.client_secret', $encryption_service->encrypt(getenv('marketo_ma_rest_client_secret')))
+      ->save();
+
+    // Get the `marketo_ma` service.
+    $this->service = \Drupal::service('marketo_ma');
+    // Get the API client service.
+    $this->api_client = \Drupal::service('marketo_ma.client');
+  }
+
+  /**
+   * Tests the marketo_ma service.
+   */
+  public function testMarketoMaService() {
+    self::assertTrue($this->service instanceof MarketoMaServiceInterface);
+    self::assertEquals('api_client', $this->service->trackingMethod());
+  }
+
+  /**
+   * Tests syncing leads in batch mode.
+   */
+  public function testBatchSync() {
+    // Get the API settings.
+    $config = \Drupal::configFactory()->getEditable('marketo_ma.settings');
+
+    // Set up required settings.
+    $config->set('rest.batch_requests', 1)
+      ->save();
+
+    // Queue up a lead.
+    $user_email = $this->randomMachineName() . '@marketo.com';
+    $this->service->updateLead(new Lead(['email' => $user_email]));
+
+    // Try to load the new lead.
+    $synced_lead = $this->api_client->getLead('email', $user_email);
+    // Make sure the lead wasn't created.
+    self::assertEmpty($synced_lead, 'The lead has not been created.');
+
+    // Run cron, which should insert the user.
+    \Drupal::service('cron')->run();
+
+    // Get the queue.
+    $lead_queue = \Drupal::queue('marketo_ma_lead');
+    self::assertEquals($lead_queue->numberOfItems(), 0, 'The lead queue was emptied.');
+
+    // Try to load the new lead.
+    $synced_lead = $this->api_client->getLead('email', $user_email);
+    // Make sure the lead was created.
+    self::assertNotEmpty($synced_lead, 'The lead has not been created.');
+
+    // Delete the newly created lead.
+    $delete_result = $this->api_client->deleteLead($synced_lead->id());
+    // Make sure an item was deleted.
+    self::assertEquals('deleted', $delete_result[0]['status'], 'The tem lead was deleted.');
+
+    // Try to load the new lead.
+    $deleted_lead = $this->api_client->getLead('email', $user_email);
+    // Make sure the lead wasn't created.
+    self::assertEmpty($deleted_lead, 'The lead has not been created.');
+  }
+
+  // @todo: Add tests for service public methods.
+}
diff --git a/tests/travis-ci-apache b/tests/travis-ci-apache
deleted file mode 100644
index b477d48..0000000
--- a/tests/travis-ci-apache
+++ /dev/null
@@ -1,34 +0,0 @@
-<VirtualHost *:80>
-  ServerAdmin webmaster@localhost
-  DocumentRoot /var/www
-  <Directory />
-    Options FollowSymLinks
-    AllowOverride All
-  </Directory>
-  <Directory /var/www/>
-    Options Indexes FollowSymLinks MultiViews
-    AllowOverride All
-    Order allow,deny
-    allow from all
-  </Directory>
-  ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
-  <Directory "/usr/lib/cgi-bin">
-    AllowOverride None
-    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
-    Order allow,deny
-    Allow from all
-  </Directory>
-  ErrorLog ${APACHE_LOG_DIR}/error.log
-  # Possible values include: debug, info, notice, warn, error, crit,
-  # alert, emerg.
-  LogLevel warn
-  CustomLog ${APACHE_LOG_DIR}/access.log combined
-    Alias /doc/ "/usr/share/doc/"
-    <Directory "/usr/share/doc/">
-        Options Indexes MultiViews FollowSymLinks
-        AllowOverride None
-        Order deny,allow
-        Deny from all
-        Allow from 127.0.0.0/255.0.0.0 ::1/128
-    </Directory>
-</VirtualHost>
\ No newline at end of file
diff --git a/user/marketo_ma_user.info b/user/marketo_ma_user.info
deleted file mode 100644
index a35bf87..0000000
--- a/user/marketo_ma_user.info
+++ /dev/null
@@ -1,6 +0,0 @@
-name = Marketo MA User
-description = Integrates Marketo with core User actions.
-core = 7.x
-package = Marketo
-
-dependencies[] = marketo_ma
diff --git a/user/marketo_ma_user.install b/user/marketo_ma_user.install
deleted file mode 100644
index 3a1f413..0000000
--- a/user/marketo_ma_user.install
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * @file
- * Install and schema hooks for Marketo MA Webform module.
- */
-
-/**
- * Enable user activity triggers.
- */
-function marketo_ma_user_update_7100(&$sandbox) {
-  variable_set('marketo_ma_user_triggers', array(
-  'login' => 'login',
-  'insert' => 'insert',
-  'update' => 'update',
-  ));
-  return t('All user activity triggers enabled');
-}
diff --git a/user/marketo_ma_user.module b/user/marketo_ma_user.module
deleted file mode 100644
index b09d118..0000000
--- a/user/marketo_ma_user.module
+++ /dev/null
@@ -1,371 +0,0 @@
-<?php
-
-/**
- * @file
- * Module functions for Marketo MA User.
- */
-
-/**
- * Implements hook_admin_paths().
- */
-function marketo_ma_user_admin_paths() {
-  $paths = array(
-    'user/*/marketo' => TRUE,
-    'user/*/marketo/*' => TRUE,
-  );
-  return $paths;
-}
-
-/**
- * Implements hook_menu().
- */
-function marketo_ma_user_menu() {
-  $items['user/%user/marketo'] = array(
-    'title' => 'Marketo',
-    'page callback' => '_marketo_ma_user_display_lead',
-    'page arguments' => array(1),
-    'access callback' => 'marketo_ma_user_view_lead_data_callback',
-    'access arguments' => array(1),
-    'type' => MENU_LOCAL_TASK,
-    'weight' => 100,
-  );
-  $items['user/%user/marketo/lead'] = array(
-    'title' => 'Lead',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  );
-  $items['user/%user/marketo/activity'] = array(
-    'title' => 'Activity',
-    'page callback' => '_marketo_ma_user_display_lead_activity',
-    'page arguments' => array(1),
-    'access callback' => 'marketo_ma_user_view_lead_data_callback',
-    'access arguments' => array(1),
-    'type' => MENU_LOCAL_TASK,
-    'weight' => 100,
-  );
-  return $items;
-}
-
-/**
- * Implements hook_permission().
- */
-function marketo_ma_user_permission() {
-  return array(
-    'access all marketo lead data' => array(
-      'title' => t('View all lead data'),
-      'description' => t('Allows viewing of Marketo lead and activity data for any user.'),
-    ),
-    'access own marketo lead data' => array(
-      'title' => t('View own lead data'),
-    ),
-  );
-}
-
-/**
- * Access callback for viewing lead information.
- */
-function marketo_ma_user_view_lead_data_callback($account) {
-  global $user;
-  return user_access('access all marketo lead data', $user) || (user_access('access own marketo lead data', $user) && $user->uid == $account->uid);
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function marketo_ma_user_form_marketo_ma_admin_settings_form_alter(&$form, &$form_state, $form_id) {
-  module_load_include('inc', 'webform', 'includes/webform.admin');
-  $form['marketo_ma_tabs']['marketo_ma_user'] = array(
-    '#title' => t('User Integration'),
-    '#type' => 'fieldset',
-    '#weight' => 5,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_user']['marketo_ma_user_triggers'] = array(
-    '#title' => t('Trigger a lead update on the following events:'),
-    '#type' => 'checkboxes',
-    '#default_value' => variable_get('marketo_ma_user_triggers', array(
-      'login' => 'login',
-      'insert' => 'insert',
-      'update' => 'update',
-    )),
-    '#options' => array(
-      'login' => t('User login'),
-      'insert' => t('User registration / creation'),
-      'update' => t('User update'),
-    ),
-  );
-  $form['marketo_ma_tabs']['marketo_ma_user']['marketo_ma_user_fieldmap'] = array(
-    '#title' => t('User field mapping'),
-    '#type' => 'fieldset',
-    '#description' => t('When a lead update is triggered, the fields mapped below will be included in the data sent to Marketo. Select the associated Marketo field for each User field displayed.<br />NOTE: Marketo Email Address is automatically set to [account:mail] and cannot be overridden with these settings.'),
-    '#tree' => TRUE,
-    '#theme' => 'marketo_ma_user_fieldmap',
-  );
-
-  $user_fields = _marketo_ma_user_get_user_fields();
-  $user_fields += field_info_instances('user', 'user');
-  $fieldmap = variable_get('marketo_ma_user_fieldmap', array());
-  $default_options = array(MARKETO_MA_WEBFORM_COMPONENT_NONE => '- None -');
-  $marketo_options = _marketo_ma_get_field_options();
-  $options = array_merge($default_options, $marketo_options);
-
-  foreach ($user_fields as $key => $value) {
-    $form['marketo_ma_tabs']['marketo_ma_user']['marketo_ma_user_fieldmap'][$key] = array(
-      '#title' => check_plain($value['label'] . ' (' . $value['field_name'] . ')'),
-      '#title_display' => 'invisible',
-      '#type' => 'select',
-      '#options' => $options,
-    );
-    if (array_key_exists($key, $fieldmap)) {
-      if (!array_key_exists($fieldmap[$key], $options)) {
-        $form['marketo_ma_tabs']['marketo_ma_user']['marketo_ma_user_fieldmap'][$key]['#options'] = array_merge(
-          array($fieldmap[$key] => "Undefined Field ($fieldmap[$key])"), $options
-        );
-      }
-
-      $form['marketo_ma_tabs']['marketo_ma_user']['marketo_ma_user_fieldmap'][$key]['#default_value'] = $fieldmap[$key];
-    }
-  }
-}
-
-/**
- * Implements hook_user_login().
- */
-function marketo_ma_user_user_login(&$edit, $account) {
-  $triggers = variable_get('marketo_ma_user_triggers', array());
-  if (array_key_exists('login', $triggers) && $triggers['login'] && isset($account->mail)) {
-    $data = _marketo_ma_user_get_mapped_fields($account);
-    $data['Email'] = $account->mail;
-    marketo_ma_add_lead($account->mail, $data);
-  }
-}
-
-/**
- * Implements hook_user_insert().
- */
-function marketo_ma_user_user_insert(&$edit, $account, $category) {
-  $triggers = variable_get('marketo_ma_user_triggers', array());
-  if (array_key_exists('insert', $triggers) && $triggers['insert'] && isset($account->mail)) {
-    $data = _marketo_ma_user_get_mapped_fields($account);
-    $data['Email'] = $account->mail;
-    marketo_ma_add_lead($account->mail, $data);
-  }
-}
-
-/**
- * Implements hook_user_update().
- */
-function marketo_ma_user_user_update(&$edit, $account, $category) {
-  $triggers = variable_get('marketo_ma_user_triggers', array());
-  if (array_key_exists('update', $triggers) && $triggers['update'] && isset($account->mail)) {
-    // @todo decide how to handle an email address change
-    // See https://api.drupal.org/comment/23088#comment-23088
-    // When email changes, can we create a new lead then merge the two?
-    $data = _marketo_ma_user_get_mapped_fields($account);
-    $data['Email'] = $account->mail;
-    marketo_ma_add_lead($account->mail, $data);
-  }
-}
-
-/**
- * Implements hook_field_delete_field().
- */
-function marketo_ma_user_field_delete_field($field) {
-  $fieldmap = variable_get('marketo_ma_user_fieldmap', array());
-  unset($fieldmap[$field['field_name']]);
-  variable_set('marketo_ma_user_fieldmap', $fieldmap);
-}
-
-/**
- * Returns core user fields which can be mapped to Marketo fields.
- *
- * @param int $uid
- * @return array
- */
-function _marketo_ma_user_get_user_fields($uid = 0) {
-  $data = array();
-  $account = user_load($uid);
-  foreach ($account as $key => $value) {
-    if (!is_array($value) && !is_object($value)) {
-      $data[$key] = ["field_name" => $key, "label" => "[account:" . $key . "]"];
-    }
-  }
-  return $data;
-}
-
-/**
- * Returns Drupal user fields which have been mapped to Marketo fields.
- * 
- * @param $account
- * 
- * @return array
- */
-function _marketo_ma_user_get_mapped_fields($account) {
-  $data = array();
-  $fieldmap = variable_get('marketo_ma_user_fieldmap', array());
-  foreach ($fieldmap as $key => $value) {
-    if ($value != 'none') {
-      if (isset($account->{$key}) && !is_object($account->{$key}) && !is_array($account->{$key})) {
-        // These are the core user fields: uid, name, pass, mail, etc..
-        $data[$value] = $account->{$key};
-      }
-      elseif (isset($account->{$key}['und'][0]['value'])) {
-        // These are extended account fields which have been defined.
-        $data[$value] = $account->{$key}['und'][0]['value'];
-      }
-      else {
-        watchdog('marketo', 'An unhandled field mapping exists for <pre>@field</pre>', array('@field' => $key), WATCHDOG_WARNING);
-      }
-    }
-  }
-  return $data;
-}
-
-/**
- * Returns a formatted table of lead information.
- * 
- * @param $account
- * 
- * @return string
- */
-function _marketo_ma_user_display_lead($account) {
-  $leads = marketo_ma_get_lead($account->mail);
-  return theme('marketo_ma_user_lead', array(
-    'lead' => $leads,
-    'email' => $account->mail,
-    'fields' => _marketo_ma_get_field_options(FALSE, FALSE),
-  ));
-}
-
-/**
- * Returns a formatted table of lead activity.
- * 
- * @param $account
- * 
- * @return string
- */
-function _marketo_ma_user_display_lead_activity($account) {
-  $activity = marketo_ma_get_lead_activity($account->mail);
-  return theme('marketo_ma_user_activity', array('activity' => $activity, 'email' => $account->mail));
-}
-
-/**
- * Implements hook_theme().
- */
-function marketo_ma_user_theme() {
-  $theme = array(
-    'marketo_ma_user_fieldmap' => array(
-      'render element' => 'element',
-    ),
-    'marketo_ma_user_activity' => array(
-      'variables' => array(
-        'activity' => array(),
-        'email' => NULL,
-      ),
-    ),
-    'marketo_ma_user_lead' => array(
-      'variables' => array(
-        'lead' => array(),
-        'email' => NULL,
-        'fields' => array(),
-      ),
-    ),
-  );
-  return $theme;
-}
-
-/**
- * Theme function for formatting user profile fieldmap form.
- * 
- * @param array $vars
- *   element: Form element
- */
-function theme_marketo_ma_user_fieldmap($vars) {
-  $element = $vars['element'];
-
-  $table = array(
-    'header' => array(
-      'drupal' => t('Drupal'),
-      'marketo' => t('Marketo'),
-    ),
-    'rows' => array(),
-    'empty' => t('There are currently no user account fields available for mapping. Fields can be managed in <a href="@url">Account settings</a>.', array('@url' => url('admin/config/people/accounts/fields'))),
-  );
-
-  foreach (element_children($element) as $key) {
-    $row = array();
-    $row['data'] = array();
-    $row['data'][] = $element[$key]['#title'];
-    $row['data'][] = drupal_render($element[$key]);
-    $table['rows'][] = $row;
-  }
-
-  return theme('table', $table);
-}
-
-/**
- * Theme function for formatting lead activity.
- * 
- * @param array $vars
- *   activity: Arrary of activity data
- *   email: Email address associated with activity data
- */
-function theme_marketo_ma_user_activity($vars) {
-  $activity = $vars['activity'];
-  $email = $vars['email'];
-
-  $table = array(
-    'header' => array(
-      t('ID'),
-      t('Date/Time'),
-      t('Activity Type'),
-      t('Asset Name'),
-    ),
-    'rows' => array(),
-    'empty' => t("No activity found for @mail.", array('@mail' => $email)),
-  );
-
-  $fields = array('id', 'activityDateTime', 'activityType', 'mktgAssetName');
-  foreach ($activity as $event) {
-    $row = array();
-    foreach ($fields as $field) {
-      $row[] = $event->$field;
-    }
-    $table['rows'][] = $row;
-  }
-  return theme('table', $table);
-}
-
-/**
- * Theme function for formatting lead data.
- * 
- * @param array $vars
- *   lead: Arrary of lead data
- *   email: Email address associated with activity data
- *   fields: Array of defined Marketo fields
- */
-function theme_marketo_ma_user_lead($vars) {
-  $leads = $vars['lead'];
-  $email = $vars['email'];
-  $fields = $vars['fields'];
-
-  $table = array(
-    'header' => array(t('Key'), t('Value')),
-    'rows' => array(),
-    'empty' => t("No lead found for @mail.", array('@mail' => $email)),
-  );
-
-  foreach ($leads as $lead) {
-    $table['rows'][] = array(
-      array(
-        'data' => 'ID ' . $lead->Id,
-        'colspan' => 2,
-        'header' => TRUE,
-      ),
-    );
-    foreach ($lead->attributes as $key => $value) {
-      $label = (array_key_exists($key, $fields)) ? $fields[$key] : $key;
-      $table['rows'][] = array($label, $value);
-    }
-  }
-
-  return theme('table', $table);
-}
diff --git a/webform/includes/marketo_ma_webform.webform_settings.inc b/webform/includes/marketo_ma_webform.webform_settings.inc
deleted file mode 100644
index a028981..0000000
--- a/webform/includes/marketo_ma_webform.webform_settings.inc
+++ /dev/null
@@ -1,218 +0,0 @@
-<?php
-
-/**
- * @file
- * Form elements and menu callbacks for managing Marketo settings on a Webform.
- */
-
-/**
- * Form builder for managing a webform configuration.
- */
-function marketo_ma_webform_settings_form($form, &$form_state, $node) {
-  $form['#node'] = $node;
-  $is_active = 0;
-  $tracking_method = 'default';
-
-  $webform = _marketo_ma_webform_details($node->nid);
-  if ($webform) {
-    $is_active = $webform->is_active;
-    if (isset($webform->options['tracking_method'])) {
-      $tracking_method = $webform->options['tracking_method'];
-    }
-  }
-
-  $form['marketo_ma_webform_is_active'] = array(
-    '#title' => t('Capture Data'),
-    '#type' => 'checkbox',
-    '#description' => t('If checked, captured form information will be sent to Marketo'),
-    '#default_value' => $is_active,
-  );
-  $form['marketo_ma_advanced'] = array(
-    '#title' => t('Advanced Options'),
-    '#type' => 'fieldset',
-    '#collapsible' => TRUE,
-    '#collapsed' => ($tracking_method == 'default'),
-  );
-
-  $methods = array('default' => t('Default (As defined in Marketo MA config)'));
-  if (_marketo_ma_munchkin_is_configured()) {
-    $methods['munchkin'] = t('Munchkin');
-  }
-  if (_marketo_ma_forms2_is_configured()) {
-    $methods['forms2'] = t('Forms 2.0');
-  }
-  if (_marketo_ma_soap_is_configured()) {
-    $methods['soap'] = t('SOAP API (Synchronous)');
-    $methods['soap_async'] = t('SOAP API (Asynchronous)');
-  }
-  $form['marketo_ma_advanced']['marketo_ma_webform_capture_method'] = array(
-    '#title' => t('Capture Method'),
-    '#type' => 'select',
-    '#description' => t('Use an alternate tracking method for this specific form.'),
-    '#default_value' => $tracking_method,
-    '#options' => $methods,
-  );
-
-  $formid = isset($webform->options['formid']) ? $webform->options['formid'] : '';
-  $form['marketo_ma_advanced']['marketo_ma_forms2_formid'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Form ID'),
-    '#description' => t('Required when capture method is "Forms 2.0". Form ID is a number and can be found in the Marketo form embed code.'),
-    '#default_value' => $formid,
-    '#states' => array(
-      'visible' => array(
-        ':input[name="marketo_ma_webform_capture_method"]' => array('value' => 'forms2'),
-      ),
-    ),
-  );
-
-  $form['components'] = array(
-    '#title' => t('Webform field mapping'),
-    '#tree' => TRUE,
-    '#theme' => 'marketo_ma_webform_fieldmap',
-  );
-
-  $default_options = array(MARKETO_MA_WEBFORM_COMPONENT_NONE => '- None -');
-  $marketo_options = _marketo_ma_get_field_options();
-  $options = array_merge($default_options, $marketo_options);
-  $fieldmap = _marketo_ma_webform_get_mapped_fields($node->nid);
-  foreach ($node->webform['components'] as $cid => $component) {
-    $form['components'][$cid] = array(
-      '#title' => filter_xss($component['name'] . ' (' . $component['form_key'] . ')'),
-      '#title_display' => 'invisible',
-      '#type' => 'select',
-      '#options' => $options,
-    );
-    if (array_key_exists($cid, $fieldmap)) {
-      if (!array_key_exists($fieldmap[$cid], $options)) {
-        $form['components'][$cid]['#options'] = array_merge(
-          array($fieldmap[$cid] => "Undefined Field ($fieldmap[$cid])"), $options
-        );
-      }
-      $form['components'][$cid]['#default_value'] = $fieldmap[$cid];
-    }
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-    '#validate' => array('_marketo_ma_webform_validate_webform'),
-    '#submit' => array('_marketo_ma_webform_save_webform'),
-  );
-
-  return $form;
-}
-
-/**
- * Validation function for form.
- */
-function _marketo_ma_webform_validate_webform($form, &$form_state) {
-  $values = $form_state['values'];
-  if (
-    $values['marketo_ma_webform_capture_method'] == 'forms2' &&
-    ($values['marketo_ma_forms2_formid'] == '' || !is_numeric($values['marketo_ma_forms2_formid']))
-  ) {
-    form_set_error('marketo_ma_forms2_formid', t('Form ID must be set to a number when using the Forms 2.0 capture method.'));
-  }
-}
-
-/**
- * Inserts marketo webform settings into the database.
- */
-function _marketo_ma_webform_save_webform($form, &$form_state) {
-  $node = $form['#node'];
-  $values = $form_state['values'];
-
-  $nid = $node->nid;
-
-  $options = array();
-  if ($values['marketo_ma_webform_capture_method'] != 'default') {
-    $options['tracking_method'] = $values['marketo_ma_webform_capture_method'];
-
-    if ($values['marketo_ma_webform_capture_method'] == 'forms2') {
-      $options['formid'] = check_plain(trim($values['marketo_ma_forms2_formid']));
-    }
-  }
-
-  db_merge(MARKETO_MA_SCHEMA_WEBFORM)
-    ->key(array('nid' => $nid))
-    ->fields(array(
-      MARKETO_MA_WEBFORM_FIELD_ACTIVE => $values['marketo_ma_webform_is_active'],
-      MARKETO_MA_WEBFORM_OPTIONS => serialize($options),
-    ))
-    ->execute();
-
-  if (isset($form_state['values']['components'])) {
-    foreach ($form_state['values']['components'] as $cid => $marketo) {
-      db_merge(MARKETO_MA_SCHEMA_WEBFORM_COMPONENT)
-        ->key(array(
-          'nid' => $nid,
-          'cid' => $cid,
-        ))
-        ->fields(array(
-          MARKETO_MA_WEBFORM_COMPONENT_KEY => $marketo,
-        ))
-        ->execute();
-    }
-  }
-
-  drupal_set_message(t('The configuration options have been saved.'));
-}
-
-/**
- * Theme function for formatting user profile fieldmap form.
- * 
- * @param array $vars
- *   element: Form element
- */
-function theme_marketo_ma_webform_fieldmap($vars) {
-  $element = $vars['element'];
-
-  $table = array(
-    'header' => array(
-      'webform' => t('Webform'),
-      'marketo' => t('Marketo'),
-    ),
-    'rows' => array(),
-    'empty' => t('No form components have been defined.'),
-  );
-
-  foreach (element_children($element) as $key) {
-    $row = array();
-    $row['data'] = array();
-    $row['data'][] = $element[$key]['#title'];
-    $row['data'][] = drupal_render($element[$key]);
-    $table['rows'][] = $row;
-  }
-
-  return theme('table', $table);
-}
-
-/**
- * Generate a list of all webforms avaliable on this site.
- */
-function theme_marketo_ma_webform_list($vars) {
-  $nodes = $vars['nodes'];
-  $table = array(
-    'header' => array(
-      t('Title'),
-      t('Tracking Enabled'),
-      t('Components Mapped'),
-      t('Manage'),
-    ),
-    'rows' => array(),
-    'empty' => t('There are currently no webforms on your site.'),
-  );
-
-  foreach ($nodes as $node) {
-    $table['rows'][] = array(
-      l($node->title, 'node/' . $node->nid),
-      $node->is_active ? t('Yes') : t('No'),
-      $node->components ? $node->components : 0,
-      node_access('update', $node) ? l(t('Edit'), 'node/' . $node->nid . '/webform/marketo') : '',
-    );
-  }
-
-  return theme('table', $table);
-}
diff --git a/webform/marketo_ma_webform.info b/webform/marketo_ma_webform.info
deleted file mode 100644
index 6d453d8..0000000
--- a/webform/marketo_ma_webform.info
+++ /dev/null
@@ -1,7 +0,0 @@
-name = Marketo MA Webform
-description = Integrates Marketo with Webforms.
-core = 7.x
-package = Marketo
-
-dependencies[] = marketo_ma
-dependencies[] = webform (>=3.x)
diff --git a/webform/marketo_ma_webform.install b/webform/marketo_ma_webform.install
deleted file mode 100644
index 98bd174..0000000
--- a/webform/marketo_ma_webform.install
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-/**
- * @file
- * Install and schema hooks for Marketo MA Webform module.
- */
-
-/**
- * Implements hook_schema().
- */
-function marketo_ma_webform_schema() {
-  $schema = array();
-  $schema[MARKETO_MA_SCHEMA_WEBFORM] = array(
-    'description' => 'Webform Settings',
-    'primary key' => array('nid'),
-    'unique keys' => array(),
-    'fields' => array(
-      'nid' => array(
-        'description' => 'The node identifier of a webform.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-      ),
-      MARKETO_MA_WEBFORM_FIELD_ACTIVE => array(
-        // Is this webform marketo enabled.
-        'description' => 'Should this form send data to Marketo',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      MARKETO_MA_WEBFORM_OPTIONS => array(
-        'description' => 'Additional options for controlling tracking behavior',
-        'type' => 'blob',
-      ),
-    ),
-  );
-  $schema[MARKETO_MA_SCHEMA_WEBFORM_COMPONENT] = array(
-    'description' => 'Webform Component Settings',
-    'primary key' => array('nid', 'cid'),
-    'unique keys' => array(),
-    'fields' => array(
-      'nid' => array(
-        'description' => 'The node identifier of a webform.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'cid' => array(
-        'description' => 'The identifier for this component within this node, starts at 0 for each node.',
-        'type' => 'int',
-        'size' => 'small',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      MARKETO_MA_WEBFORM_COMPONENT_KEY => array(
-        'description' => 'The Marketo form field.',
-        'type' => 'varchar',
-        'length' => 128,
-      ),
-    ),
-  );
-  return $schema;
-}
-
-/**
- * Implements hook_uninstall().
- */
-function marketo_ma_webform_uninstall() {
-  /*
-   * This is required in order to pick up the constants defined in
-   * marketo_ma.module. This hook will execute before the attempt to remove
-   * the schema occurs and ensure that uninstall does not leave any orphaned
-   * tables. See https://api.drupal.org/comment/15344#comment-15344
-   */
-  drupal_load('module', 'marketo_ma');
-}
-
-/**
- * Add field for webform specific tracking options.
- */
-function marketo_ma_webform_update_7100(&$sandbox) {
-  db_add_field(
-    MARKETO_MA_SCHEMA_WEBFORM, MARKETO_MA_WEBFORM_OPTIONS, array(
-      'description' => 'Additional options for controlling tracking behavior',
-      'type' => 'blob',
-    )
-  );
-  return t('Option field created.');
-}
diff --git a/webform/marketo_ma_webform.module b/webform/marketo_ma_webform.module
deleted file mode 100644
index 21edc3f..0000000
--- a/webform/marketo_ma_webform.module
+++ /dev/null
@@ -1,233 +0,0 @@
-<?php
-
-/**
- * @file
- * Module functions for Marketo MA Webform.
- */
-
-/**
- * Implements hook_menu().
- */
-function marketo_ma_webform_menu() {
-  $items['node/%webform_menu/webform/marketo'] = array(
-    'title' => 'Marketo',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('marketo_ma_webform_settings_form', 1),
-    'access callback' => 'marketo_ma_webform_permission_callback',
-    'access arguments' => array(1),
-    'file' => 'includes/marketo_ma_webform.webform_settings.inc',
-    'type' => MENU_LOCAL_TASK,
-  );
-  return $items;
-}
-
-/**
- * Implements hook_permission().
- */
-function marketo_ma_webform_permission() {
-  return array(
-    'administer marketo webform settings' => array(
-      'title' => t('Webform settings'),
-      'description' => t('Administer Marketo settings on Webforms.'),
-    ),
-  );
-}
-
-/**
- * Permission callback for managing webform setup.
- *
- * @param $node
- *   The node holding the webform.
- *
- * @return bool
- *   TRUE if access is allowed otherwise FALSE.
- */
-function marketo_ma_webform_permission_callback($node) {
-  global $user;
-  return (user_access('administer marketo webform settings', $user) && node_access('update', $node));
-}
-
-/**
- * Implements hook_theme().
- */
-function marketo_ma_webform_theme() {
-  $theme = array(
-    'marketo_ma_webform_fieldmap' => array(
-      'render element' => 'element',
-      'file' => 'includes/marketo_ma_webform.webform_settings.inc',
-    ),
-    'marketo_ma_webform_list' => array(
-      'variables' => array(
-        'nodes' => array(),
-      ),
-      'file' => 'includes/marketo_ma_webform.webform_settings.inc',
-    ),
-  );
-  return $theme;
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- */
-function marketo_ma_webform_form_marketo_ma_admin_settings_form_alter(&$form, &$form_state, $form_id) {
-  module_load_include('inc', 'webform', 'includes/webform.admin');
-  $form['marketo_ma_tabs']['marketo_ma_webform'] = array(
-    '#title' => t('Webform Integration'),
-    '#type' => 'fieldset',
-    '#description' => t('Configuration is managed on each individual webform. This section lists all of the content on the site that may have a webform attached to it.'),
-    '#weight' => 10,
-  );
-  $form['marketo_ma_tabs']['marketo_ma_webform']['marketo_ma_webform_list'] = array(
-    '#markup' => marketo_ma_webform_list(),
-  );
-}
-
-/**
- * Implements hook_webform_component_delete().
- */
-function marketo_ma_webform_webform_component_delete($component) {
-  db_delete(MARKETO_MA_SCHEMA_WEBFORM_COMPONENT)
-    ->condition('nid', $component['nid'])
-    ->condition('cid', $component['cid'])
-    ->execute();
-}
-
-/**
- * Implements hook_webform_submission_insert().
- *
- * TODO: refactor this growing function
- */
-function marketo_ma_webform_webform_submission_insert($node, $submission) {
-  $webform = _marketo_ma_webform_details($node->nid);
-  if ($webform && $webform->is_active) {
-
-    $data = array();
-    $fieldmap = _marketo_ma_webform_get_mapped_fields($node->nid);
-    foreach ($fieldmap as $cid => $key) {
-      if ($key != MARKETO_MA_WEBFORM_COMPONENT_NONE) {
-        // https://drupal.org/node/1609324#submission-structure
-        if (isset($submission->data[$cid]['value'][0])) {
-          $data[$key] = $submission->data[$cid]['value'][0];
-        }
-        elseif (isset($submission->data[$cid][0])) {
-          $data[$key] = $submission->data[$cid][0];
-        }
-        else {
-          // Handle mapped selectbox components with no option selected.
-          $data[$key] = '';
-        }
-      }
-    }
-
-    /*
-     * Check to see if an Email field has been provided. If not, we will try
-     * to use the current logged in user info
-     */
-    if (!isset($data['Email']) || $data['Email'] == '') {
-      global $user;
-      if (isset($user->mail)) {
-        $data['Email'] = $user->mail;
-      }
-    }
-
-    if (array_key_exists('Email', $data)) {
-      marketo_ma_add_lead($data['Email'], $data, FALSE, $webform->options);
-    }
-  }
-}
-
-/**
- * Returns fields which have been mapped to Marketo fields.
- * 
- * @param int $nid
- *   Node ID
- * 
- * @return array
- *   Array of webform component IDs and the Marketo field they are mapped to
- */
-function _marketo_ma_webform_get_mapped_fields($nid) {
-  $data = array();
-  $result = db_select(MARKETO_MA_SCHEMA_WEBFORM_COMPONENT)
-    ->fields(MARKETO_MA_SCHEMA_WEBFORM_COMPONENT)
-    ->condition('nid', $nid)
-    ->execute()
-    ->fetchAll();
-  foreach ($result as $field) {
-    $data[$field->cid] = $field->marketo_ma_key;
-  }
-  return $data;
-}
-
-/**
- * Tests to see if tracking is on or off for this webform.
- * 
- * @param int $nid
- *   Node ID
- * 
- * @return bool
- *   TRUE if tracking is active on this webform otherwise FALSE
- */
-function _marketo_ma_webform_is_active($nid) {
-  $result = db_select(MARKETO_MA_SCHEMA_WEBFORM)
-    ->fields(MARKETO_MA_SCHEMA_WEBFORM)
-    ->condition('nid', $nid)
-    ->condition('is_active', 1)
-    ->execute()
-    ->fetchAll();
-  return (count($result) ? TRUE : FALSE);
-}
-
-/**
- * Retrieves tracking settings for this webform.
- *
- * @param int $nid
- *   Node ID
- * 
- * @return array
- *   Array of details otherwise NULL if webform is not configured
- */
-function _marketo_ma_webform_details($nid) {
-  $result = db_select(MARKETO_MA_SCHEMA_WEBFORM)
-    ->fields(MARKETO_MA_SCHEMA_WEBFORM)
-    ->condition('nid', $nid)
-    ->execute()
-    ->fetchObject();
-  if ($result) {
-    $result->options = unserialize($result->options);
-    return $result;
-  }
-  else {
-    return NULL;
-  }
-}
-
-/**
- * Finds Webforms and associated Marketo configuration for display.
- * 
- * @return string
- *   A themed table of webforms defined on the site
- */
-function marketo_ma_webform_list() {
-  $webform_types = webform_variable_get('webform_node_types');
-
-  $nodes = array();
-  if ($webform_types) {
-    $components = db_select(MARKETO_MA_SCHEMA_WEBFORM_COMPONENT, 'mmwc');
-    $components->addField('mmwc', 'nid');
-    $components->addExpression('count(' . MARKETO_MA_WEBFORM_COMPONENT_KEY . ')', 'components');
-    $components->condition('marketo_ma_key', 'none', '<>');
-    $components->groupBy('nid');
-
-    $nodes = db_select('node', 'n');
-    $nodes->leftJoin(MARKETO_MA_SCHEMA_WEBFORM, 'mmw', 'n.nid = mmw.nid');
-    $nodes->leftJoin($components, 'mmwc', 'n.nid = mmwc.nid');
-    $nodes->fields('n', array('nid', 'title'));
-    $nodes->addField('mmw', MARKETO_MA_WEBFORM_FIELD_ACTIVE);
-    $nodes->addField('mmwc', 'components');
-    $result = $nodes->condition('n.type', $webform_types, 'IN')
-      ->execute()
-      ->fetchAllAssoc('nid');
-  }
-
-  return theme('marketo_ma_webform_list', array('nodes' => $result));
-}
