Skip to content

Users

Kitbase tracks two types of users: anonymous users who have not been identified, and identified users who have been linked to a known user ID via the identify() method.

User Types

TypeIdentifierHow It Works
Anonymousanonymous_id (auto-generated UUID)Created automatically on first visit. Each device gets its own anonymous ID.
Identifieduser_id (your identifier)Linked to a known user via identify(). Associates all activity with a single identity.

Anonymous Tracking

When a user first visits your site, the SDK automatically generates a UUID and stores it in localStorage. This anonymous ID is attached to every event the user triggers.

typescript
// Anonymous ID is generated and included automatically
await kitbase.track({
  channel: 'engagement',
  event: 'Landing Page Viewed',
});

// Retrieve the current anonymous ID
const anonymousId = kitbase.getAnonymousId();
// => "550e8400-e29b-41d4-a716-446655440000"

Key points about anonymous tracking:

  • The UUID is generated on the user's first visit.
  • It is stored in localStorage, so it persists across page loads and browser restarts.
  • All events are attributed to this anonymous_id until the user is identified.
  • Each device gets its own anonymous_id. A user on two devices will appear as two anonymous users until identify() is called on both.

Disabling Storage

If you do not want to persist the anonymous ID across page loads, disable storage:

typescript
const kitbase = init({
  sdkKey: '<YOUR_API_KEY>',
  storage: null, // Anonymous ID regenerated each session
});

User Identification

Call identify() after a user logs in or signs up to link their anonymous activity to a known user ID.

typescript
await kitbase.identify({
  userId: 'user-123',
  traits: {
    email: 'jane@example.com',
    name: 'Jane Doe',
    plan: 'premium',
  },
});

When identify() is called:

  1. The SDK sends the current anonymous_id and the provided userId to the server.
  2. The server creates a link between the two identifiers.
  3. All past events associated with that anonymous_id are backfilled with the user_id.
  4. All subsequent events automatically include the user_id.

Multi-Device Identification

When the same user logs in on multiple devices, each device has its own anonymous_id. Calling identify() on each device links all anonymous IDs to the same user_id, unifying the user's activity across devices.

typescript
// Device A: anonymous_id = "aaa-111"
await kitbase.identify({ userId: 'user-123' });
// => aaa-111 linked to user-123

// Device B: anonymous_id = "bbb-222"
await kitbase.identify({ userId: 'user-123' });
// => bbb-222 also linked to user-123

// Both devices' events are now attributed to user-123

User Analytics Dashboard

The Users page in the dashboard gives you a complete view of your user base.

User List

Browse all users with the following controls:

ControlDescription
Type filterShow all users, only identified users, or only anonymous users
SearchSearch by user ID or anonymous ID
Country filterFilter by geographic location
Browser filterFilter by browser name
Date rangeFilter by when users were first or last seen

User Detail View

Click on any user to see their full profile:

SectionDescription
User typeWhether the user is identified or anonymous
Session countTotal number of sessions recorded for this user
Event countTotal number of events triggered by this user
First seenTimestamp of the user's earliest event
Last seenTimestamp of the user's most recent event
User propertiesTraits set via identify() (email, name, plan, etc.)
Activity heatmapA calendar heatmap showing events per day over recent months
Event historyA chronological list of all events with timestamps and details

User Properties (Traits)

Traits are key-value pairs that describe the user. They are set through the identify() method and are visible in the user detail view.

typescript
await kitbase.identify({
  userId: 'user-123',
  traits: {
    email: 'jane@example.com',
    name: 'Jane Doe',
    company: 'Acme Corp',
    plan: 'premium',
    created_at: '2025-01-15T10:00:00Z',
  },
});

Traits can be updated at any time by calling identify() again with the same userId and new or changed values:

typescript
// User upgrades their plan
await kitbase.identify({
  userId: 'user-123',
  traits: {
    plan: 'enterprise',
  },
});

The new traits are merged with existing ones. You do not need to resend all traits on every call.

Identity Resolution

Identity resolution is the process of connecting anonymous and identified user records into a single profile.

How It Works

  1. Before identify() -- The user is tracked under their anonymous_id. All events are attributed to this anonymous identifier.
  2. When identify() is called -- The server creates a mapping from anonymous_id to user_id.
  3. Backfill -- All past events that were recorded with the anonymous_id are retroactively associated with the user_id.
  4. Response -- The server response indicates whether this was a new link or a previously established one.

This ensures accurate Monthly Active User (MAU) counts. A user who browses anonymously and later creates an account is counted as one user, not two.

TIP

Identity resolution prevents double-counting. Without it, a user who visits your site anonymously and then signs up would appear as two separate users in your analytics.

Best Practices

Use Database IDs as user_id

Use your application's internal database ID or UUID as the userId, not personally identifiable information like email addresses. This avoids leaking PII into analytics and keeps the identifier stable even if the user changes their email.

typescript
// Recommended
await kitbase.identify({ userId: user.databaseId });

// Avoid
await kitbase.identify({ userId: user.email });

Call identify() Early

Call identify() as soon as the user is authenticated. This ensures that events triggered immediately after login are correctly attributed.

typescript
async function handleLogin(credentials) {
  const user = await authService.login(credentials);

  // Identify immediately after authentication
  await kitbase.identify({
    userId: user.id,
    traits: {
      email: user.email,
      name: user.name,
      plan: user.subscription?.plan,
    },
  });
}

Update Traits on Profile Changes

When user properties change (e.g., plan upgrade, name change), call identify() again to keep traits current.

typescript
async function handlePlanUpgrade(newPlan) {
  await kitbase.identify({
    userId: currentUser.id,
    traits: { plan: newPlan },
  });
}

Call reset() on Logout

When a user logs out, call reset() to clear the user ID and generate a new anonymous ID. This prevents the next user on the same device from being attributed to the previous user's identity.

typescript
function handleLogout() {
  kitbase.reset();
  // Clears user_id, generates a new anonymous_id, starts a new session
}

Be Consistent with user_id Format

Use the same format for userId across all platforms and services. If you use a database integer ID on the web, use the same ID in your mobile app and backend. Inconsistent formats lead to fragmented user profiles.

typescript
// Consistent: always use the database ID
userId: String(user.id) // "12345" everywhere

// Inconsistent: different formats in different places
userId: user.id         // "12345" on web
userId: user.email      // "jane@example.com" on mobile

Released under the MIT License.