Identify Users
Link anonymous visitors to known users so every event, from first page view to final purchase, is attributed to a single person.
Overview
Kitbase tracks users with two identifiers:
| Identifier | Description |
|---|---|
anonymous_id | Auto-generated UUID assigned to every visitor before they log in |
user_id | Your unique user identifier, set when you call identify() |
Before identification, all events are linked to the auto-generated anonymous ID. After identify() is called, the SDK sends a request to POST /sdk/v1/identify (authenticated with your SDK key), and subsequent events are attributed to the real user. The server also backfills past anonymous events so nothing is lost.
How It Works
1. User visits site → anonymous_id generated (UUID, stored in localStorage)
2. User browses → events tracked with anonymous_id only
3. User logs in → identify(userId, traits) called
4. SDK sends request → server links anonymous_id to user_id
5. Server backfills → previous anonymous events now attributed to user
6. Subsequent events → include both anonymous_id and user_idBasic Usage
Call identify() immediately after the user authenticates:
// After user logs in
await kitbase.identify({
userId: 'user_123',
traits: {
email: 'jane@example.com',
name: 'Jane Doe',
plan: 'premium',
company: 'Acme Inc'
}
});Identity Linking Requires identify()
Kitbase does not automatically link anonymous users to identified users. You must explicitly call identify() after authentication.
Identify Options
interface IdentifyOptions {
userId: string; // Required: your unique user identifier
traits?: Record<string, unknown>; // Optional: user properties
}| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | Your unique user identifier (database ID recommended) |
traits | Record<string, unknown> | No | Arbitrary key-value pairs describing the user |
Traits can include any key-value pairs. Common examples:
| Trait | Example |
|---|---|
email | "jane@example.com" |
name | "Jane Doe" |
plan | "premium" |
company | "Acme Inc" |
role | "admin" |
created_at | "2025-03-15T10:00:00Z" |
When to Call identify()
Call identify() immediately after any of these events:
- After login (email/password, OAuth, magic link)
- After signup (new account creation)
- After OAuth callback (Google, GitHub, etc.)
- On app load when the user is already authenticated (check your auth state)
// After OAuth login
async function handleOAuthCallback(user) {
await kitbase.identify({
userId: user.id,
traits: {
email: user.email,
name: user.name,
provider: 'google'
}
});
}
// On app load if already authenticated
if (currentUser) {
await kitbase.identify({
userId: currentUser.id,
traits: {
email: currentUser.email,
plan: currentUser.plan
}
});
}Call identify() as early as possible
The sooner you identify a user after authentication, the fewer events remain anonymous. Place your identify() call at the earliest point in your auth flow.
Anonymous ID Management
Retrieve the current identifiers at any time:
kitbase.getAnonymousId(); // Returns current anonymous UUID
kitbase.getUserId(); // Returns user_id if identified, null otherwiseStorage Options
The anonymous ID is persisted across page reloads. You can configure how and where it is stored:
// Default: localStorage
const kitbase = init({
sdkKey: '<YOUR_SDK_KEY>',
storage: localStorage
});
// Custom storage (must implement getItem / setItem / removeItem)
const kitbase = init({
sdkKey: '<YOUR_SDK_KEY>',
storage: myCustomStorage
});
// No persistence (new anonymous ID generated each session)
const kitbase = init({
sdkKey: '<YOUR_SDK_KEY>',
storage: null
});Reset Identity
Clear the current user and start a fresh anonymous session:
kitbase.reset();
// Clears user_id, generates a new anonymous_id, starts a new sessionUse reset() when:
- The user logs out
- The user switches accounts
async function handleLogout() {
await authService.logout();
kitbase.reset(); // Start tracking as a new anonymous user
}Multi-Device Support
When the same person uses your app on multiple devices:
- Each device generates its own
anonymous_id - Calling
identify()with the sameuserIdon each device links all activity to one user - Events from every device are attributed to the same user profile
// 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
// All events from both devices are now attributed to user_123MAU Accuracy
Identity resolution prevents double-counting users who browse anonymously before signing in. A user who visits your site anonymously and later creates an account counts as one MAU, not two.
Server-Side Behavior
When identify() is called, the following happens on the server:
- The SDK sends
POST /sdk/v1/identifywith{ userId, anonymousId, traits } - The server creates a link between the
anonymous_idand theuser_id - The server backfills
user_idon all past events matching thatanonymous_idin ClickHouse - The server responds with:
{
"success": true,
"userId": "user_123",
"previouslyLinked": false
}| Field | Type | Description |
|---|---|---|
success | boolean | Whether the identification succeeded |
userId | string | The identified user ID |
previouslyLinked | boolean | true if this anonymous ID was already linked to this user |
REST API (Direct)
You can call the identify endpoint directly without the SDK:
curl -X POST https://api.kitbase.dev/sdk/v1/identify \
-H "x-sdk-key: YOUR_SDK_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"anonymous_id": "550e8400-e29b-41d4-a716-446655440000",
"traits": {
"email": "jane@example.com",
"name": "Jane Doe"
}
}'Best Practices
Use consistent user IDs
Always use the same identifier format for a given user. Database IDs or UUIDs work well:
// Good: consistent database ID
await kitbase.identify({ userId: user.id });
// Bad: mixing formats across your app
await kitbase.identify({ userId: user.email }); // email in one place
await kitbase.identify({ userId: String(user.id) }); // numeric ID elsewhereAvoid PII as user IDs
Do not use personally identifiable information (email addresses, phone numbers) as the userId. Use opaque database IDs instead, and pass PII as traits:
// Good
await kitbase.identify({
userId: 'usr_8f3a2b',
traits: { email: 'jane@example.com' }
});
// Bad
await kitbase.identify({
userId: 'jane@example.com'
});Update traits when the profile changes
Call identify() again whenever user properties change (plan upgrade, name update, etc.) to keep traits current:
async function handlePlanUpgrade(newPlan: string) {
await billingService.upgrade(newPlan);
await kitbase.identify({
userId: currentUser.id,
traits: {
plan: newPlan,
upgraded_at: new Date().toISOString()
}
});
}Call reset() on logout
Always call reset() when a user logs out to prevent the next user on a shared device from inheriting the previous identity:
async function handleLogout() {
await authService.logout();
kitbase.reset();
}Identify as early as possible
Place your identify() call at the earliest point after authentication. The longer you wait, the more events are tracked without a user_id.