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())
```

{% callout type="note" heading="" %}
If `execute()` doesn't return an event, the event doesn't propagate through the remaining plugins.
{% /callout %}

## Enrichment plugins

Enrichment plugins modify properties in Event objects or drop an event. For the [available keys for the Event object](/docs/apis/analytics/http-v2#event-array-keys), refer to the HTTP V2 API reference.

### Drop an event

{% code-group %}
```js JavaScript
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');
```

```ts TypeScript
import * as amplitude from '@amplitude/analytics-browser';

import { EnrichmentPlugin, BrowserConfig, PluginType, Event } from '@amplitude/analytics-types';

class FilterEventsPlugin implements EnrichmentPlugin {
  name = 'filter-events-plugin';
  type = PluginType.ENRICHMENT as any;

  async setup(config: BrowserConfig): Promise<void> {
    return undefined;
  }

  async execute(event: Event): Promise<Event | null> {
    // 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');
```
{% /code-group %}

### Set universal user properties

{% code-group %}
```js JavaScript
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');
```

```ts TypeScript
import * as amplitude from '@amplitude/analytics-browser';

import { EnrichmentPlugin, BrowserConfig, PluginType, Event } from '@amplitude/analytics-types';

class PropertiesEnrichmentPlugin implements EnrichmentPlugin {
  name = 'filter-events-plugin';
  type = PluginType.ENRICHMENT as any;

  async setup(config: BrowserConfig, amplitude: Amplitude): Promise<void> {
    if (shouldSetUserProperties) {
      const identifyEvent = new amplitude.Identify();
      identifyEvent.set("testKey", "testValue");
      amplitude.identify(identifyEvent);
    }
    return undefined;
  }

  async execute(event: Event): Promise<Event> {
      return event
  }
}

amplitude.add(new PropertiesEnrichmentPlugin());
amplitude.init('API_KEY');
```
{% /code-group %}

### Remove personally identifiable information (PII)

{% code-group %}
```js JavaScript
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());
```

```ts TypeScript
import * as amplitude from '@amplitude/analytics-browser';

import { EnrichmentPlugin, BrowserConfig, PluginType, Event } from '@amplitude/analytics-types';

class FilterEventsPlugin implements EnrichmentPlugin {
  name = 'remove-PII-plugin';
  type = PluginType.ENRICHMENT as any;

  async setup(config: BrowserConfig): Promise<void> {
    return undefined;
  }

  async execute(event: Event): Promise<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.add(new FilterEventsPlugin());
amplitude.init('API_KEY');
```
{% /code-group %}

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

{% code-group %}
```js JavaScript
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);
```

```ts TypeScript
import { EnrichmentPlugin, BrowserConfig, PluginType, Event } from '@amplitude/analytics-types';

class EventLevelGroupPlugin implements EnrichmentPlugin {
  name = 'group-plugin';
  type = PluginType.ENRICHMENT as any;

  async setup(config: BrowserConfig): Promise<void> {
    return undefined;
  }

  async execute(event: Event): Promise<Event> {
      event.groups = event.extra['groups'];
      return event;
  }
}

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

// Pass the event level groups info though middleware extra when calling the tracking plan.
const extra = {extra: { groups: ["test_group_name": "test_group_value"]}};
ampli.eventWithGroups({requiredNumber: 1.23, requiredBoolean: false}, extra);
```
{% /code-group %}

## 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](https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/quickstart/).

{% code-group %}
```js JavaScript
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;
      }
    });
  }
}
```

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

export default class SegmentPlugin implements Types.DestinationPlugin {
  name = 'segment';
  type = Types.PluginType.DESTINATION as any;
  segment: AnalyticsBrowser;

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

  async setup(config: Types.Config): Promise<undefined> {
    this.segment.load({
      writeKey: this.writeKey,
    });
    return;
  }

  execute(context: Types.Event): Promise<Types.Result> {
    return new Promise<Types.Result>(resolve => {
      const {
        event_type,
        event_properties,
        user_id,
        user_properties,
        groups,
        group_properties,
      } = context;
      const callback = (ctx: any) => {
        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;
      }
    });
  }
}
```
{% /code-group %}

### Send to Hotjar

Before you begin, refer to [Hotjar's tracking code](https://help.hotjar.com/hc/en-us/articles/115011639927-What-is-the-Hotjar-Tracking-Code-).

{% code-group %}
```js JavaScript
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"
    }
  }
}
```

```ts TypeScript
import { BrowserConfig, DestinationPlugin, Event, PluginType, Result } from '@amplitude/analytics-types';
import { default as hj } from '@hotjar/browser';
export class HotjarPlugin implements DestinationPlugin {
  name = 'hotjar';
  type = PluginType.DESTINATION as const;
  siteId: number;
  hotjarVersion: number;

  constructor(siteId: number, hotjarVersion: number, debug: boolean = false) {
    this.siteId = siteId;
    this.hotjarVersion = hotjarVersion;
  }

  async setup(): Promise<void> {
    hj.init(this.siteId, this.hotjarVersion);
  }

  async execute(event: Event): Promise<Result> {
    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 API',
    };
  }
}
```
{% /code-group %}

### Send to Google Analytics

Before you begin, refer to the documentation for [Google's Data Layer](https://developers.google.com/tag-platform/tag-manager/web/datalayer).

{% code-group %}
```js JavaScript
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"
    }
  }
}
```

```ts TypeScript
import { DestinationPlugin, Event, PluginType, Result } from '@amplitude/analytics-types';

export class GTMPlugin implements DestinationPlugin {
  name = 'google-tag-manager';
  type = PluginType.DESTINATION as const;
  containerId: string;

  constructor(containerId: string) {
    this.containerId = containerId;
  }

  async setup(): Promise<void> {
    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'),
        dataLayer = 'datalayer' != 'dataLayer' ? '&l=' + 'datalayer' : '';
      script.async = true;
      script.src = 'https://www.googletagmanager.com/gtm.js?id=' + this.containerId + dataLayer;
      head.insertBefore(script, head.firstChild);
    }
  }

  async execute(event: Event): Promise<Result> {
    window.dataLayer.push(event);

    return {
      code: 200,
      event: event,
      message: 'Event pushed onto GTM Data Layer',
    };
  }
}
```
{% /code-group %}

### Send to FullStory

Before you begin, refer to the documentation for [FullStory's browser SDK](https://help.fullstory.com/hc/en-us/articles/360020828273-Getting-Started-with-FullStory#h_01FXB8T39JB6TPBWMR3727QMVV).

{% code-group %}
```js JavaScript
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',
    };
 }
}
```

```ts TypeScript
import { DestinationPlugin, Event, PluginType, Result } from '@amplitude/analytics-types';

export class FullstoryPlugin implements DestinationPlugin {
  name = 'fullstory';
  type = PluginType.DESTINATION as const;
  fsOrg: string;
  FS: Object

  constructor(fsOrg: string) {
    this.fsOrg = fsOrg;
    this.FS = window.FS;
  }

  async setup(): Promise<void> {
    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: Event): Promise<Result> {
    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',
    };
  }
}
```
{% /code-group %}

## Supported SDKs

| Platform                                                 | SDK                                                                                                                           | Github                                                                                                                                |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| [Browser](/docs/sdks/analytics/browser/browser-sdk-2/#plugin)            | [`@amplitude/analytics-browser`](https://www.npmjs.com/package/@amplitude/analytics-browser)            | [Amplitude-TypeScript](https://github.com/amplitude/Amplitude-TypeScript)                                           |
| [Android](/docs/sdks/analytics/android/android-kotlin-sdk)  | [`com.amplitude:analytics-android`](https://mvnrepository.com/artifact/com.amplitude/analytics-android) | [Amplitude-Kotlin](https://github.com/amplitude/Amplitude-Kotlin)                                                   |
| [Node.js](/docs/sdks/analytics/node/node-js-sdk#plugins)              | [`@amplitude/analytics-node`](https://www.npmjs.com/package/@amplitude/analytics-node)                  | [Amplitude-Typescript](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-node)         |
| [React Native](/docs/sdks/analytics/react-native/#plugins) | [`@amplitude/analytics-react-native`](https://www.npmjs.com/package/@amplitude/analytics-react-native)  | [Amplitude-TypeScript](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-react-native) |
| [iOS](/docs/sdks/analytics/ios/ios-swift-sdk#amplitude-sdk-plugin)           | `AmplitudeSwift`                                                                                        | [Amplitude-Swift](https://github.com/amplitude/Amplitude-Swift)                                                     |
| [Python](/docs/sdks/analytics/python/#amplitude-sdk-plugin)           | [`amplitude-analytics`](https://pypi.org/project/amplitude-analytics/)                                  | [Amplitude-Python](https://github.com/amplitude/Amplitude-Python)                                                   |
| [Go](/docs/sdks/analytics/go#amplitude-sdk-plugin)                   | [`github.com/amplitude/analytics-go`](https://pkg.go.dev/github.com/amplitude/analytics-go)             | [analytics-go](https://github.com/amplitude/analytics-go)                                                           |
