Identifying Customers

Proper user behavior analytics (UBA) requires a mechanism for identifying and stitching together user journeys as they acces your platform. While this sounds simple in theory since you could append a user id to logs, there are a variety of issues that usually make this challenging in production if you want an accurate representation of the customer journey. Many times, the same user can be found in different contexts:

  • As an anonymous visitor viewing your doc pages.
  • Signed into your app and interacting with it.
  • Making authenticated calls against your API.

Overview of user tracking

The challenge stitching together a customer journey

Most platform-focused companies have a variety of different products. Some products are API-based applications that are accessed by customers programmatically whereas other products are web-based applications that a customer logs into with their browser and uses interactively. It’s critical to stitch together a holistic customer journey so you can understand funnel metrics like initial sign up to first API call even if across both your API and web apps.

Funnel Analysis Overview

Yet, each of these products may have different ways to identify and authenticate users which create multiple challenges. An API may require adding a bearer token whereas a website may require a user to log in via an OAuth flow and store a session cookie.

In addition, not all users have identifying information. For example, you may have anonymous visitors who browser your website before they signed up. These visitors don’t even have a user id generated yet. What if the same anonymous visitor browses your website from multiple devices? A naive user tracking implementation would double count that person twice reducing the integrity of your metrics.

How Moesif solves this

The recommended way to set up Moesif is with two integration points:

  1. A server integration to monitor API calls.
  2. A client integration to track users and their web activity.

By using both integrations together, a complete picture can be drawn which shows user interactions with each API (the server integration) and also interactions with the frontend UI (the client integration).

Note that the server integration can be used without the client integration since both are mutually exclusive implementations.

A complete implementation is shown in the below diagram.

Diagram of Moesif Moesif client and server monitoring architecture

Behind the scenes, the Moesif platform leverages a variety of tricks to accurately stitch together the journey of each customer. This mechanism includes user aliasing, anonymous ids, and merging users. Most of this is handled automatically by Moesif, but this article goes into detail on what you need and how it works.

User Id

A user id (or company id) is used by Moesif to associate API and web traffic to the same customer. If you are using both the server and client integrations, you should ensure your user id is consistent across both. You will also want to make sure that the user id is unique and unchanging

if you use a user id that may change in the future, it will likely break your metrics. For instance, using an identifier such as email may be risky if you allow users to change their email in your application. A randomly generated UID that stay with the user regardless of profile changes is recommended.

In order to attach a user id to an event or action, server and client integrations should call the identifyUser method. It is important that when calling this method that every call, whether it be from the client or server integrations, be made with the same id for the target user so metrics are properly tracked.

The id used to track the user should be a permanent and robust identifier that never changes, like a database id. It is not recommended to use fields that can change like email addresses or API keys. Using different ids for the same user will lead to issues with tracking the end-to-end journey (such as overcounting unique users), since Moesif uses 1 unqiue identifier per individual user.

Server integration

To track API calls by user, you will want to use a Moesif server integration. We support many different platforms as well as the ability to create a custom integration for unsupported platforms.

Identifying users

The ability to identify users can only happen with authenticated API calls. Anonymous calls to the API will not be tracked by user ID.

You’ll need to ensure your server integration correctly identifies users so that API traffic is associated to each user. This is done by implementing the identifyUser hook in the Moesif middleware. the identifyUser hook should be supplied with your custom logic that returns the user id.

The identifyUser() (or identifyCompany()) function you supply has access to the request context. For example, if your API authentication middleware sets a variable req.user.id, you can use that. You can also read from a HTTP header like X-User-Id, extract a field from a JWT, or use any other field available in the request context.

Here is an example for a Node.js integration.

const moesifMiddleware = moesif({
  applicationId: 'Your Moesif Application Id',

  // Return thr user id set by the auth middleware. 
  identifyUser: function (req, res) {
    return req.user ? req.user.id : undefined;
  },
});

app.use(moesifMiddleware);

You can see in the above snippet that identifyUser is passed a function which has the request and response for the API call. In this example, we are grabbing the id from the requests user.id field and if a user is not defined, no user id to the call/event in Moesif.

Depending on the framework, language, or gateway you are using you should review the identify user section for your respective server integration.

Many of the API gateway plugins and SDKs have a default identifyUser function out of the box based on the conventions for that framework. For example, the Kong plugin reads the value set in x-consumer-custom-id, the AWS API gateway reads the value principalId and cognitoIdentityId, and moesif-nodejs uses the value set in req.user.id

By attaching a user id to an event in Moesif, you are able to do more advanced filtering and metrics. Here’s what it looks like in the Metrics screen in Moesif.

Metrics screen with user ID present for event

Anonymous Ids

Unlike the client integration, the server integration doesn’t have the capability to track anonymous user ids. This is because API traffic is usually authenticated traffic by definition. If an ID is not attached to an event, the userID field in Moesif will simply just be undefined.

Client integration

Moesif’s support for client integrations allows user interactions to be tracked beyond just the API calls tracked in the server integration. Each event (button click, register, login, etc.) can be easily added into your metrics in Moesif so you can see the full user journey.

Identifying users

To identify a user, you must have a unique ID to associate with them. Once you know the user id of a customer, you can call identifyUser() to associate the current web session with your known permanent identifier. For best results, this should be done as soon as possible such as when a user logs into your app. Otherwise, tracking data can be lost if the customer clears their browser’s cookies and local storage before being identified with Moesif.

To make sure a seamless picture of the journey is recorded, be sure that the user ID supplied to the client integration matches the one your server integration is also using.

Here is an example of how to initialize the Moesif browser-js package in a JavaScript frontend.

<script src="//unpkg.com/moesif-browser-js@v1/moesif.min.js"></script>
<script type="text/javascript">
  moesif.init({
    applicationId: "Your Moesif Application Id",
  });

  // Identify the user with Moesif such as when user logs in
  moesif.identifyUser("12345");
</script>

You can see above that we include the dependency (moesif.min.js), call init with the Moesif Application ID for our app, and then call moesif.identifyUser(USER_ID). This would likely be done once the user logs into the app and a user id is retrieved. Once this is done, each action the user performs on the frontend will be tracked and reported into Moesif.

Anonymous users

When a new visitor browses your website which has a client integration like moesif-browser-js installed, Moesif generates an anonymous user id and stores in the user’s device.

By doing this, users who are yet to create an account or log in can still be accurately tracked and their journey reported to Moesif.

This anonymous id is persisted both into localStorage and replicated to a cookie for redundancy so that if the user clears local storage or visits a different subdomain (like from docs.acmeinc.com to acmeinc.com), there is still a cookie to fall back to and the user can be accurately attributed.

Anonymous users makes it possible to correctly track unique users and stitch together the user journey before and after they signed into your app. Moesif uses two different ids to accomplish this:

Name Description
User Id The internal User Id assigned by Moesif. This may be either a generated Anonymous Id or can be your Identified User Id or Anonymous Id.
Identified User Id The Identified User Id set by you by the identifyUser function. When no Identified User Id is set, the user is anonymous and has not been identified.

You should call moesif.reset() when a user logs out of your application to ensure a new anonymousId is generated. Otherwise, new activity may get associated with an old user session.

User merging

Moesif will automatically merge users if they are identified as the same person. This can happen if an existing user (that was previously tracked in Moesif) starts browsing your app on a new device. A new anonymous profile is created as soon as the user starts browsing anonymously from their new device. Once the user logs in (thus identifyUser() is called), Moesif will merge the newly created anonymous profile with the previously created user profile. You will see only one user in Moesif.

The below table describes different scenarios on how Moesif handles user creation and merging:

Example Scenario How Moesif handles
No User Id identified All events will be associated with an anonymous user profile. Identified User Id is null. User Id is set to a generated Anonymous Id.
User Id is identified after anonymous events Events are associated with an anonymous user profile. Initially Identified User Id is null. Once the user is identified, both User Id and Identified User Id are set to the actual user id.
Same User Id on multiple devices, with no anonymous events Events will be initially associated to two different anonymous profiles. Once the user is identified, both profiles will be merged into a single user profile. User Id and Identified User Id are set to the actual user id.
Multiple User Ids on the same device Two profiles will be created, one for each user id. No merging will occur. Ensure moesif.reset() is called when a user logs out. Otherwise, events may get associated to an old user session.
Multiple User Ids on the same device, with anonymous events Ensure moesif.reset() is called when a user logs out. Otherwise, events may get associated to an old user session.

Linking users to companies

In addition to tracking users, you can also track usage at the by Company. This is helpful if your business primarily sells to other businesses (B2B).
Company tracking works by assigning a user to a company (i.e. a group of users), in a very similar manner to tracking users by user ID.

Adding a Company identifier to a user can be done one of three ways:

  1. Implement the identifyCompany() hook in your server integration. See example for Node.js This can be the easiest option if you already have company context available in your API.
  2. Call the identifyCompany() function in your client integration such as after they sign into your application. See this example for browser-js This is great if you already have company context in your frontend such as when they sign into your application
  3. Define a company_id when saving a user to Moesif directly. See updating user profiles This is great if you’re already syncing users and companies via a separate process

As long as you link a user to a company, Moesif will associate any future web or API traffic from a user to that company as well.

Logging out

In your apps logout routine, if you are using moesif-browser-js, call moesif.reset() when a user logs out of your app. Doing this ensures a new anonymous id is generated and ensuring different sessions are not mixed up and super important for testing

Here is a simple example to demonstrate the usage of the moesif.reset() function.

function logoutUser() {
  // example routine for logging out users from app
  logoutUserFromApp();
  clearSession();

  // Reset browser session for Moesif
  moesif.reset();
}

Updated: