| [ Web Proxy ] |
| Viewing: https://docs.kitbase.dev/identify-users | [Back] [Original] |
Link anonymous visitors to known users so every event, from first page view to final purchase, is attributed to a single person.
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.
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_idCall 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.
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" |
Call identify() immediately after any of these events:
// 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.
Retrieve the current identifiers at any time:
kitbase.getAnonymousId(); // Returns current anonymous UUID
kitbase.getUserId(); // Returns user_id if identified, null otherwiseThe 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
});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:
async function handleLogout() {
await authService.logout();
kitbase.reset(); // Start tracking as a new anonymous user
}When the same person uses your app on multiple devices:
anonymous_ididentify() with the same userId on each device links all activity to one user// 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.
When identify() is called, the following happens on the server:
POST /sdk/v1/identify with { userId, anonymousId, traits }anonymous_id and the user_iduser_id on all past events matching that anonymous_id in ClickHouse{
"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 |
You can call the identify endpoint directly without the SDK:
curl -X POST https://ingest.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"
}
}'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 elsewhereDo 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'
});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()
}
});
}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();
}Place your identify() call at the earliest point after authentication. The longer you wait, the more events are tracked without a user_id.
identify(), getAnonymousId(), reset().Released under the MIT License.
Copyright 2024-present Kitbase
| Web Proxy Viewer | New URL | Original Page |