On this page

SDK Plugins

Plugins extend Amplitude's behavior. This flexible pattern supports event enrichment, transformation, filtering, routing to third-party destinations, and more.

Plugin methods

A plugin is an object with two methods, setup() and execute():

Plugin.setup

This method prepares the plugin for use and takes config as a parameter. It returns undefined. A typical use copies configuration from config or instantiates plugin dependencies. Amplitude calls this method when you register the plugin with amplitude.add().

Plugin.execute

This method processes events and takes event as a parameter. For an enrichment-type plugin, it returns the modified or enriched event. For a destination-type plugin, it returns a map with keys: event (BaseEvent), code (number), and message (string). Amplitude calls this method for each event, including Identify, GroupIdentify, and Revenue events instrumented through the client interface.

Add a plugin to Ampli with amplitude.add(). You can add as many plugins as you want. Each plugin runs in order based on the plugin type.

js
amplitude.add(yourPlugin())

If execute() doesn't return an event, the event doesn't propagate through the remaining plugins.

Enrichment plugins

Enrichment plugins modify properties in Event objects or drop an event. For the available keys for the Event object, refer to the HTTP V2 API reference.

Drop an event

js
import * as amplitude from '@amplitude/analytics-browser';

import {PluginType} from '@amplitude/analytics-types';

class FilterEventsPlugin {
  name = 'filter-events-plugin';
  type = PluginType.ENRICHMENT;

  async setup(config) {
    return undefined;
  }

  async execute(event) {
    // ignore events with a certain property
    if (event.event_properties['ignore'] === true){
      // returning null will prevent this event from being processed by subsequent plugins
      return null;
    }

    // Allow other events to be processed and sent to destination plugins
    return event;
  }
}

amplitude.add(new FilterEventsPlugin());
amplitude.init('API_KEY');

Set universal user properties

js
import * as amplitude from '@amplitude/analytics-browser';

import {PluginType} from '@amplitude/analytics-types';

class PropertiesEnrichmentPlugin {
  name = 'properties-plugin';
  type = PluginType.ENRICHMENT;

  async setup(_, amplitude) {
    if (shouldSetUserProperties) {
      const identifyEvent = new amplitude.Identify();
      identifyEvent.set("testKey", "testValue");
      amplitude.identify(identifyEvent);
    }
    return undefined;
  }

  async execute(event) {
      return event
  }
}

amplitude.add(new PropertiesEnrichmentPlugin());
amplitude.init('API_KEY');

Remove personally identifiable information (PII)

js
import * as amplitude from '@amplitude/analytics-browser';
import {PluginType} from '@amplitude/analytics-types';

class FilterEventsPlugin {
  name = 'remove-PII-plugin';
  type = PluginType.ENRICHMENT;

  async setup(config) {
    return undefined;
  }

  async execute(event) {
      // remove PII on the event
      if(event.user_properties['phone']) {
        delete event.user_properties['phone'];

        // set a new prop to mark this event as modified
        event.event_properties['pii-removed'] = true;
      }

      // return modified event with PII removed
      return event
  }
}

amplitude.init('API_KEY');
amplitude.add(new FilterEventsPlugin());

Send event level groups with Ampli v2

This example shows how to send event-level groups in Ampli V2. Sending event-level groups in SDKs (not in Ampli) works differently. Check the specific SDK for usage details.

js
import * as amplitude from '@amplitude/analytics-browser';

import {PluginType} from '@amplitude/analytics-types';

class EventLevelGroupPlugin {
  name = 'group-plugin';
  type = PluginType.ENRICHMENT;

  async setup(config) {
    return undefined;
  }

  async execute(event) {
      event.groups = event.extra['groups'];
      return event;
  }

  // Allow other events to be processed and sent to destination plugins
  return event;
}

ampli.client.add(new EventLevelGroupPlugin());

const extra = {extra: { groups: ["test_group_name": "test_group_value"]}};
ampli.eventWithGroups({requiredNumber: 1.23, requiredBoolean: false}, extra);

Destination plugins

Use a destination plugin to send events to third-party APIs.

Send to Segment

First, follow Segment's guide to install the Segment Analytics.js 2.0 Web SDK.

js
import { AnalyticsBrowser } from '@segment/analytics-next';
import { Types } from '@amplitude/analytics-browser';

export default class SegmentPlugin {
  name = 'segment';
  type = Types.PluginType.DESTINATION;

  constructor(private readonly writeKey) {
    // Create Segment tracker
    this.segment = new AnalyticsBrowser();
  }

  async setup(config) {
    this.segment.load({
      writeKey: this.writeKey,
    });
    return;
  }

  execute(context) {
    return new Promise(resolve => {
      const {
        event_type,
        event_properties,
        user_id,
        user_properties,
        groups,
        group_properties,
      } = context;
      const callback = (ctx) => {
        resolve({ event: context, code: 200, message: '' });
      };

      switch (event_type) {
        case Types.SpecialEventType.IDENTIFY:
        case Types.SpecialEventType.GROUP_IDENTIFY:
          const groupValues = groups ? Object.values(groups) : [];
          if (groupValues.length === 0) {
            this.segment.identify(
              user_id,
              user_properties?.[Types.IdentifyOperation.SET],
              {},
              callback,
            );
          } else {
            this.segment.group(
              groupValues[0],
              group_properties?.[Types.IdentifyOperation.SET],
              {},
              callback,
            );
          }
          break;

        case 'page':
          // @ts-ignore
          const { name, category, ...properties } = event_properties;
          this.segment.page(category, name, properties, {}, callback);
          break;

        default:
          this.segment.track(event_type, event_properties, {}, callback);
          break;
      }
    });
  }
}

Send to Hotjar

Before you begin, refer to Hotjar's tracking code.

js
import { PluginType } from "@amplitude/analytics-types"
import { default as hj } from "@hotjar/browser"
export class HotjarPlugin {
  name = "hotjar"
  type = PluginType.DESTINATION
  constructor(siteId, hotjarVersion, debug = false) {
    this.siteId = siteId
    this.hotjarVersion = hotjarVersion
  }
  async setup() {
    hj.init(this.siteId, this.hotjarVersion)
  }
  async execute(event) {
    if (event.event_type === "$identify") {
      const { user_id, device_id, user_properties } = event
      const hotjarId = user_id || device_id || ""
      hj.identify(hotjarId, user_properties || {})
    } else {
      hj.event(event.event_type)
    }
    return {
      code: 0,
      event: event,
      message: "Event forwarded to Hotjar SDK"
    }
  }
}

Send to Google Analytics

Before you begin, refer to the documentation for Google's Data Layer.

js
import { PluginType } from "@amplitude/analytics-types"

export class GTMPlugin {
  name = "google-tag-manager"
  type = PluginType.DESTINATION

  constructor(containerId) {
    this.containerId = containerId
  }

  async setup() {
    if (!window.dataLayer) {
      window.dataLayer = window.dataLayer || []
      window.dataLayer.push({
        "gtm.start": new Date().getTime(),
        event: "gtm.js"
      })
      const head = document.getElementsByTagName("head")[0],
        script = document.createElement("script");
      script.async = true
      script.src =
        `https://www.googletagmanager.com/gtm.js?id=${this.containerId}&l=datalayer`
      head.insertBefore(script, head.firstChild)
    }
  }

  async execute(event) {
    window.dataLayer.push(event)

    return {
      code: 200,
      event: event,
      message: "Event pushed onto GTM Data Layer"
    }
  }
}

Send to FullStory

Before you begin, refer to the documentation for FullStory's browser SDK.

js
import { PluginType } from '@amplitude/analytics-types';

export class FullstoryPlugin {
  constructor(fsOrg) {
    this.name = 'fullstory';
    this.type = PluginType.DESTINATION;
    this.fsOrg = fsOrg;
    this.FS = window.FS;
  }

  async setup() {
    window._fs_host || (window._fs_host = "fullstory.com", window._fs_script = "edge.fullstory.com/s/fs.js", window._fs_org = this.fsOrg, window._fs_namespace = "FS", function (n, t, e, o, s, c, i, f) { e in n ? n.console && n.console.log && n.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].') : ((i = n[e] = function (n, t, e) { i.q ? i.q.push([n, t, e]) : i._api(n, t, e); }).q = [], (c = t.createElement(o)).async = 1, c.crossOrigin = "anonymous", c.src = "https://" + _fs_script, (f = t.getElementsByTagName(o)[0]).parentNode.insertBefore(c, f), i.identify = function (n, t, e) { i(s, { uid: n }, e), t && i(s, t, e); }, i.setUserVars = function (n, t) { i(s, n, t); }, i.event = function (n, t, e) { i("event", { n: n, p: t }, e); }, i.anonymize = function () { i.identify(!1); }, i.shutdown = function () { i("rec", !1); }, i.restart = function () { i("rec", !0); }, i.log = function (n, t) { i("log", [n, t]); }, i.consent = function (n) { i("consent", !arguments.length || n); }, i.identifyAccount = function (n, t) { c = "account", (t = t || {}).acctId = n, i(c, t); }, i.clearUserCookie = function () { }, i.setVars = function (n, t) { i("setVars", [n, t]); }, i._w = {}, f = "XMLHTTPRequest", i._w[f] = n[f], f = "fetch", i._w[f] = n[f], n[f] && (n[f] = function () { return i._w[f].apply(this, arguments); }), i._v = "1.3.0"); }(window, document, window._fs_namespace, "script", "user"));
    this.FS = window.FS;
  }

  async execute(event) {
    if (event.event_type === '$identify') {
      this.FS.identify(event.user_id);
    }
    else {
      this.FS.event(event.event_type, event.event_properties);
    }
    return {
      code: 200,
      event: event,
        message: 'Event forwarded to Fullstory',
    };
 }
}

Supported SDKs

Was this helpful?