diff --git a/integrationExamples/gpt/growthcode.html b/integrationExamples/gpt/growthcode.html
new file mode 100644
index 00000000000..ede51d2d869
--- /dev/null
+++ b/integrationExamples/gpt/growthcode.html
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Prebid.js Test
+Div-1
+
+
+
+
+
diff --git a/modules/growthCodeAnalyticsAdapter.js b/modules/growthCodeAnalyticsAdapter.js
new file mode 100644
index 00000000000..1f11b891139
--- /dev/null
+++ b/modules/growthCodeAnalyticsAdapter.js
@@ -0,0 +1,176 @@
+/**
+ * growthCodeAnalyticsAdapter.js - GrowthCode Analytics Adapter
+ */
+import { ajax } from '../src/ajax.js';
+import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
+import adapterManager from '../src/adapterManager.js';
+import * as utils from '../src/utils.js';
+import CONSTANTS from '../src/constants.json';
+import { getStorageManager } from '../src/storageManager.js';
+import {getRefererInfo} from '../src/refererDetection.js';
+import {logError, logInfo} from '../src/utils.js';
+
+const MODULE_NAME = 'growthCodeAnalytics';
+const DEFAULT_PID = 'INVALID_PID'
+const ENDPOINT_URL = 'https://p2.gcprivacy.com/v1/pb/analytics'
+
+export const storage = getStorageManager();
+
+let sessionId = utils.generateUUID();
+
+let trackEvents = [];
+let pid = DEFAULT_PID;
+let url = ENDPOINT_URL;
+
+let eventQueue = [];
+
+let startAuction = 0;
+let bidRequestTimeout = 0;
+let analyticsType = 'endpoint';
+
+let growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType}), {
+ track({eventType, eventData}) {
+ eventData = eventData ? JSON.parse(JSON.stringify(eventData)) : {};
+ let data = {};
+ if (!trackEvents.includes(eventType)) return;
+ switch (eventType) {
+ case CONSTANTS.EVENTS.AUCTION_INIT: {
+ data = eventData;
+ startAuction = data.timestamp;
+ bidRequestTimeout = data.timeout;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.AUCTION_END: {
+ data = eventData;
+ data.start = startAuction;
+ data.end = Date.now();
+ break;
+ }
+
+ case CONSTANTS.EVENTS.BID_ADJUSTMENT: {
+ data.bidders = eventData;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.BID_TIMEOUT: {
+ data.bidders = eventData;
+ data.duration = bidRequestTimeout;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.BID_REQUESTED: {
+ data = eventData;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.BID_RESPONSE: {
+ data = eventData;
+ delete data.ad;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.BID_WON: {
+ data = eventData;
+ delete data.ad;
+ delete data.adUrl;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.BIDDER_DONE: {
+ data = eventData;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.SET_TARGETING: {
+ data.targetings = eventData;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.REQUEST_BIDS: {
+ data = eventData;
+ break;
+ }
+
+ case CONSTANTS.EVENTS.ADD_AD_UNITS: {
+ data = eventData;
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ data.eventType = eventType;
+ data.timestamp = data.timestamp || Date.now();
+
+ sendEvent(data);
+ }
+});
+
+growthCodeAnalyticsAdapter.originEnableAnalytics = growthCodeAnalyticsAdapter.enableAnalytics;
+
+growthCodeAnalyticsAdapter.enableAnalytics = function(conf = {}) {
+ if (typeof conf.options === 'object') {
+ if (conf.options.pid) {
+ pid = conf.options.pid;
+ url = conf.options.url ? conf.options.url : ENDPOINT_URL;
+ } else {
+ logError(MODULE_NAME + ' Not a valid PartnerID')
+ return
+ }
+ if (conf.options.trackEvents) {
+ trackEvents = conf.options.trackEvents;
+ }
+ } else {
+ logError(MODULE_NAME + ' Invalid configuration');
+ return;
+ }
+
+ growthCodeAnalyticsAdapter.originEnableAnalytics(conf);
+};
+
+function logToServer() {
+ if (pid === DEFAULT_PID) return;
+ if (eventQueue.length > 1) {
+ let data = {
+ session: sessionId,
+ pid: pid,
+ timestamp: Date.now(),
+ timezoneoffset: new Date().getTimezoneOffset(),
+ url: getRefererInfo().page,
+ referer: document.referrer,
+ events: eventQueue
+ };
+
+ ajax(url, {
+ success: response => {
+ logInfo(MODULE_NAME + ' Send Data to Server')
+ },
+ error: error => {
+ logInfo(MODULE_NAME + ' Problem Send Data to Server: ' + error)
+ }
+ }, JSON.stringify(data), {method: 'POST', withCredentials: true})
+
+ eventQueue = [
+ ];
+ }
+}
+
+function sendEvent(event) {
+ eventQueue.push(event);
+ logInfo(MODULE_NAME + 'Analytics Event: ' + event);
+
+ if (event.eventType === CONSTANTS.EVENTS.AUCTION_END) {
+ logToServer();
+ }
+}
+
+adapterManager.registerAnalyticsAdapter({
+ adapter: growthCodeAnalyticsAdapter,
+ code: 'growthCodeAnalytics'
+});
+
+growthCodeAnalyticsAdapter.logToServer = logToServer;
+
+export default growthCodeAnalyticsAdapter;
diff --git a/modules/growthCodeAnalyticsAdapter.md b/modules/growthCodeAnalyticsAdapter.md
new file mode 100644
index 00000000000..e45cb2e9c62
--- /dev/null
+++ b/modules/growthCodeAnalyticsAdapter.md
@@ -0,0 +1,41 @@
+## GrowthCode Analytics Adapter
+
+[GrowthCode](https://growthcode.io) offers scaled infrastructure-as-a-service to
+empower independent publishers to harness data and take control of identity and
+audience while rapidly aligning to industry changes and margin pressure.
+
+## Building Prebid with GrowthCode Support
+
+First, make sure to add the GrowthCode submodule to your Prebid.js package with:
+
+```
+gulp build --modules=growthCodeIdSystem,growthCodeAnalyticsAdapter,userId
+```
+
+The following configuration parameters are available:
+
+```javascript
+pbjs.enableAnalytics({
+ provider: 'growthCodeAnalytics',
+ options: {
+ pid: '',
+ trackEvents: [
+ 'auctionEnd',
+ 'bidAdjustment',
+ 'bidTimeout',
+ 'bidRequested',
+ 'bidResponse',
+ 'noBid',
+ 'bidWon',
+ 'bidderDone']
+ }
+});
+```
+
+| Param enableAnalytics | Scope | Type | Description | Example |
+|-----------------------|----------|--------|-------------------------------------------------------------|--------------------------|
+| provider | Required | String | The name of this Adapter. | `"growthCodeAnalytics"` |
+| params | Required | Object | Details of module params. | |
+| params.pid | Required | String | This is the Customer ID value obtained via Intimate Merger. | `""` |
+| params.url | Optional | String | Custom URL for server | |
+| params.trackEvents | Required | String | Name if the variable that holds your publisher ID | |
diff --git a/test/spec/modules/growthCodeAnalyticsAdapter_spec.js b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js
new file mode 100644
index 00000000000..e542a2641e8
--- /dev/null
+++ b/test/spec/modules/growthCodeAnalyticsAdapter_spec.js
@@ -0,0 +1,70 @@
+import adapterManager from '../../../src/adapterManager.js';
+import growthCodeAnalyticsAdapter from '../../../modules/growthCodeAnalyticsAdapter.js';
+import { expect } from 'chai';
+import * as events from '../../../src/events.js';
+import constants from '../../../src/constants.json';
+import { generateUUID } from '../../../src/utils.js';
+import { server } from 'test/mocks/xhr.js';
+
+describe('growthCode analytics adapter', () => {
+ beforeEach(() => {
+ growthCodeAnalyticsAdapter.enableAnalytics({
+ provider: 'growthCodeAnalytics',
+ options: {
+ pid: 'TEST01',
+ trackEvents: [
+ 'auctionInit',
+ 'auctionEnd',
+ 'bidAdjustment',
+ 'bidTimeout',
+ 'bidTimeout',
+ 'bidRequested',
+ 'bidResponse',
+ 'setTargeting',
+ 'requestBids',
+ 'addAdUnits',
+ 'noBid',
+ 'bidWon',
+ 'bidderDone']
+ }
+ });
+ });
+
+ afterEach(() => {
+ growthCodeAnalyticsAdapter.disableAnalytics();
+ });
+
+ it('registers itself with the adapter manager', () => {
+ const adapter = adapterManager.getAnalyticsAdapter('growthCodeAnalytics');
+ expect(adapter).to.exist;
+ expect(adapter.adapter).to.equal(growthCodeAnalyticsAdapter);
+ });
+
+ it('tolerates undefined or empty config', () => {
+ growthCodeAnalyticsAdapter.enableAnalytics(undefined);
+ growthCodeAnalyticsAdapter.enableAnalytics({});
+ });
+
+ it('sends auction end events to the backend', () => {
+ const auction = {
+ auctionId: generateUUID(),
+ adUnits: [{
+ code: 'usr1234',
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250], [300, 600], [728, 90]]
+ }
+ },
+ adUnitCodes: ['usr1234']
+ }],
+ };
+ events.emit(constants.EVENTS.AUCTION_END, auction);
+ assert(server.requests.length > 0)
+ const body = JSON.parse(server.requests[0].requestBody);
+ var eventTypes = [];
+ body.events.forEach(e => eventTypes.push(e.eventType));
+ assert(eventTypes.length > 0)
+ assert(eventTypes.indexOf(constants.EVENTS.AUCTION_END) > -1);
+ growthCodeAnalyticsAdapter.disableAnalytics();
+ });
+});