Skip to main content

Hi everyone, I am working on a multi-tenant application setup where users can access:

  • customer1.example.com for Customer 1's tenant
  • customer2.example.com for Customer 2's tenant
  • And so on

In my code, I have a tenant variable that is sent to Amplitude, as shown below:

useEffect(() => {
if (!userId || !tenant) {
return;
}
amplitude.init(AMPLITUDE_ID, { userId });
const identifyEvent = new amplitude.Identify();
identifyEvent.setOnce('tenant', tenant);
amplitude.identify(identifyEvent);
}, [userId, tenant]);

 

I am using @amplitude/analytics-browser version ^2.11.2. This works well for our custom events when triggered (like page load or button clicks) as the tenant property is included.

However, we also have Amplitude's autocapture enabled, which captures two initial events on page load that lack the tenant property:

  • >Amplitude] Start Session - missing tenant
  • >Amplitude] Page Viewed - missing tenant
  • Page Load View - includes both tenant and userId

 

See the screenshot for more detail

When session start and the first Page Viewed event, tenant didn’t get sent as expected

 

When I send my custom event Home Page Load, tenant is being sent as expected

 

Question: Is there a way to delay Amplitude initialisation until a certain user property, like tenant, is set? Or are there alternatives to ensure these autocapture events include tenant?

Without this, we risk having a large amount of data that is missing the tenant, which is problematic for our multi-tenant analytics. Thanks so much for all of your help!

 

What I have try so far

I noticed in the documentation that trackOn can be used for conditional autocapture. I attempted the following code to ensure Page Viewed events only trigger when both tenant and userId are available. However, I still see events being sent without the tenant, so this does not seem to work as expected.

Documentation reference: https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2#track-page-views 

amplitude.init(AMPLITUDE_ID, {
userId,
autocapture: {
pageViews: {
trackOn() {
return Boolean(tenant && userId);
},
},
},
});

 

Hi Trung,

This is Yuanyuan from the Amplitude Support team - thank you for the detailed description!

trackOn is only set on initialization at Amplitude-TypeScript/packages/plugin-page-view-tracking-browser/src/page-view-tracking.ts at 46d77ddb057ebc1b2775212e840692541338d9ba · amplitude/Amplitude-TypeScript · GitHub. It won’t listen for updates in browser config. Would it help you case if it is configured to be dynamic?

Best,
Yuanyuan


P.S. Checkout upcoming events and user meetups on our events page.

Hi Yuanyuan, thanks for your swift response.

Would it help your case if it is configured to be dynamic?

I am not entirely sure what you mean here. Based on your response, I checked out page-view-tracking.ts in the Amplitude-TypeScript library. From my testing, it appears that the function I provide to trackOn is evaluated each time I navigate within the browser, meaning it re-evaluates, not just on the initial load.

 

I tried using trackOn since I could not find any other options. Ideally, I would like tenant to work similarly to amplitude.setUserId(), where userId is set before the session initialises. Do you happen to know of any way to set a user property before Amplitude begins tracking?

Thanks again for your help.


Hi Trung,

By 'dynamic', I was referring to `trackOn` listening for updates in browser config, instead of only being set at initialization. When you change the URL, does amplitude gets initialied each time for a new URL?

Best,
Yuanyuan


P.S. Checkout upcoming events and user meetups on our events page.

Hi Yuanyuan, we are using Amplitude in a Next.js application, which means Amplitude is initialised only once. Subsequent URL changes will not reinitialise Amplitude, as the view updates without a full page reload. Hope this answers your question.


Hi Trung,

Thanks for letting me know! Let me check with the engineer and get back to you!

Best,
Yuanyuan


P.S. Checkout upcoming events and user meetups on our events page.
Hi Trung,

The engineer clarified that trackOn() should execute every time when url changes but the value of trackOn() is unchanged after initialization.


> I would like _tenant_ to work similarly to `amplitude.setUserId()` , where `userId` is set before the session initialises. Do you happen to know of any way to set a user property before Amplitude begins tracking?


For this use case, you can call identify before init. For example,

const identifyEvent = new amplitude.Identify();identifyEvent.set('tenant', 'tenant-id'); amplitude.identify(identifyEvent);amplitude.init();


Let me know if this helps!

Best,
Yuanyuan


P.S. Checkout upcoming events and user meetups on our events page.

Hi Yuanyang, thank you for your reply the other day. I have applied the code on our staging environment to first call identify and then amplitude.init. However, the traffic still shows events being sent without a tenant value.

We are using the 'Any Active Event' option provided by Amplitude.

Is there anything I might be missing? During local testing, I also noticed events are sent without a tenant, even when the init call is placed after identify

Here’s my code

useEffect(() => {
if (!userId) {
return;
}

const identifyEvent = new amplitude.Identify();
identifyEvent.setOnce('tenant', tenant);
amplitude.identify(identifyEvent);
amplitude.init(AMPLITUDE_ID, {
userId,
});
}, }userId, tenant]);

 

And here are the results


 

The result is similar to what I share with you earlier a week ago

 

 

Would you be able to share any additional insights or check with the engineering team? I would greatly appreciate it. Thank you!


Hi Trung,

Thanks for getting back to me with feedback!

I checked the raw data and saw the identify call is still fired after the page view event. I am checking with the engineer again and will keep you updated.

Best,
Yuanyuan


P.S. Checkout upcoming events and user meetups on our events page.

Thank you Yuanyuan for your support. Looking forward to your response. For now, have a great weekend!


You are very welcome Trung. I will keep you updated!

Best,
Yuanyuan


P.S. Checkout upcoming events and user meetups on our events page.

Hi @Trung Vo !
I haven’t tested it, but I think it could work.
https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts
By adding a "pageViewTrackingEnrichment" plugin, you could perform an identify only when event.event_type == 'Start session'


Thank you so much for your input @Roque Argañaraz !

@Trung Vo What Roque mentioned would work and I have confirmed it with the engineer . 

Example code: 

// Set tenant user property on session start event
const tenantUserPropertyPlugin = (): EnrichmentPlugin => {
return {
execute: async (event: Event) => {
if (event.event_type === 'session_start')
// "user_properties": {
// "$setOnce": {
// "tenant": "tenant-id"
// }
event.user_properties = {
...event.user_properties,
"$setOnce": {
"tenant": "tenant-id"
},
};
return event;
},
}
}

amplitude.add(tenantUserPropertyPlugin());

amplitude.init('API-KEY', 'USER-ID', {
logLevel: LogLevel.Debug
});

 


Thank you, Yuanyuan and Roque, for your input—it looks very promising. I have pushed the changes to our staging environment for testing and will monitor them over the next few days. I will keep you updated on the results.


Hi ​@Yuanyuan Zhang, apologies for the very late update. We deployed the changes you suggested in early November, and the number of page titles with empty tenants has now dropped to 0, confirming that the fix works on PROD.

Here is my final plugin code:

const tenantPropertyPlugin = (
tenant: string,
): EnrichmentPlugin => {
return {
execute: async (event) => {
event.user_properties = {
...event.user_properties,
$setOnce: {
tenant,
domain: 'client',
},
};
return event;
},
};
};

 

 However, on staging, we started using the Experiment package.

 

import { Experiment, ExperimentClient } from '@amplitude/experiment-js-client';

const tenantPropertyPlugin = (
tenant: string,
): EnrichmentPlugin => {
return {
execute: async (event) => {
event.user_properties = {
...event.user_properties,
$setOnce: {
tenant,
domain: 'client',
},
};
return event;
},
};
};


const experiment = Experiment.initializeWithAmplitudeAnalytics(AMPLITUDE_ID, {
debug: true,
automaticExposureTracking: true, // Default to true for now, but let's set to true to ensure we don't lose exposure props for variant value
exposureTrackingProvider: {
track: (exposure) => {
amplitude.track('$exposure', exposure);
},
},
});

amplitude.add(
tenantPropertyPlugin(
bootstrapData.tenant
),
);
amplitude.init(AMPLITUDE_ID, {
userId,
});

experiment?.start({
user_properties: {
platform: 'rc-us', // TODO - need to make this dynamic and easy to determine from 'rc-in' later on
environment: appEnv as string,
tenant: bootstrapData.tenantId,
},
});

 

Noted that on my code, experiment?.start is called after amplitude.init. Upon tracking, staging began showing events without tenant data. This behaviour makes sense based on the code, as we only added the tenantPropertyPlugin using amplitude.add, but not for the Experiment package. As a result, any Experiment event is missing the user properties set by tenantPropertyPlugin.

My follow-up question is: is there a way to apply the same plugin when using the Experiment SDK?

Thank you so much for your support so far, Yuanyuan.


Reply