Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.outlit.ai/llms.txt

Use this file to discover all available pages before exploring further.

TL;DR: Identity resolution is how Outlit links anonymous website visitors, known contacts, and customer accounts in the customer context graph. When someone browses your site anonymously and later signs up or logs in, Outlit connects their history to the right complete customer profile.

Auto-Identify

By default, Outlit automatically identifies visitors when they submit a form containing an email field—no code required.

How It Works

When a form is submitted, Outlit:
  1. Detects email by looking for:
    • Field names: email, e-mail, user_email, emailAddress
    • Input type: type="email"
    • Any field with a valid email value (fallback)
  2. Extracts name by looking for:
    • Full name: name, full_name, fullname, your_name
    • First name: first_name, fname, firstname, given_name
    • Last name: last_name, lname, surname, family_name
  3. Calls identify() automatically:
// This happens behind the scenes when a form with email is submitted
identify({
  email: 'jane@acme.com',
  traits: {
    name: 'Jane Doe',      // If full name found
    firstName: 'Jane',     // If first name found
    lastName: 'Doe'        // If last name found
  }
})
The form submission event is still captured separately. Auto-identify adds an additional identify event when an email is found.

Disabling Auto-Identify

If you prefer to handle identification manually:
<script
  src="https://cdn.outlit.ai/stable/outlit.js"
  data-public-key="pk_xxx"
  data-auto-identify="false"
  async
></script>

How does identity resolution work?

Identity Priority

When multiple identifiers exist, Outlit uses this priority order:
PriorityIdentifier TypeExampleSource
1 (Highest)Emailjane@acme.comIdentify call, form submission
2External User IDusr_12345Auth provider (Supabase, Auth0)
3Fingerprintdevice_abc123Server SDK (device identifier)
4 (Lowest)Anonymous IDabc-def-123Browser visitor ID

Resolution Process

When you call identify():
  1. Search existing contacts by provided identifiers
  2. Match found? Link the anonymous visitor to existing contact
  3. No match? Create new CustomerContact and Customer
  4. Multiple matches? Merge profiles, keeping the most complete one
When customerId is provided with an identify call, Outlit also links that contact to your system-owned account/workspace identifier. That is how earlier customerId-only server events become part of the same customer profile once a user identifies.
Outlit treats organizational domains differently from common personal email providers. An address like jane@acme.com can establish company account context, while jane@gmail.com, academic addresses, and other personal/common-provider emails are resolved as personal contacts unless another source links them to an account.

What are the identifier types?

Email (Primary)

Email is the most reliable identifier because it’s unique per person, verified through login/signup, and works across devices.
outlit.identify({
  email: 'jane@acme.com', // Primary identifier
  traits: { name: 'Jane' }
})

External User ID

Your application’s internal user ID (from Supabase, Auth0, Firebase, etc.):
outlit.identify({
  userId: 'usr_12345', // Your auth system's ID
  email: 'jane@acme.com',
  traits: { authProvider: 'auth0' }
})
Always send both email and userId when available. This creates the strongest identity link.

Customer ID

Your system-owned customer, account, or workspace ID. Use this when product usage or billing is account-scoped:
outlit.identify({
  email: 'jane@acme.com',
  userId: 'usr_12345',
  customerId: 'cust_123',
  customerTraits: { plan: 'enterprise' }
})
Server-side track() calls can also use customerId without a user identity:
outlit.track({
  customerId: 'cust_123',
  eventName: 'account_synced'
})
Those events are attributable immediately to the account/workspace and can later link to the contact resolved from email when identify({ email, customerId }) is sent.

Anonymous Visitor ID

Automatically generated for browser visitors:
// Generated and stored automatically
const visitorId = outlit.getVisitorId()
// "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
This ID is stored in localStorage and cookies, persists across sessions (same browser), and gets linked to CustomerContact after identify.

Fingerprint (Device ID)

For native apps without browser storage (desktop, mobile, CLI), use a fingerprint—a stable device identifier you generate and manage:
// Rust SDK
client.track_by_fingerprint("app_opened", fingerprint("device_abc123"))
    .send()
    .await?;

// Later, link to user
client.identify(email("user@example.com"))
    .fingerprint("device_abc123")
    .send()
    .await?;
See Device Tracking for implementation details.

How does profile merging work?

When Outlit detects the same person with different identifiers, profiles are merged.

Merge Triggers

User visits on phone (visitorId: A), then laptop (visitorId: B), identifies with same email on both.Result: Both visitor histories merge into one CustomerContact.
User identifies as jane@old-company.com, later updates to jane@new-company.com while logged into same account (userId).Result: New email added to contact identifiers, profiles linked.
User signs up with email, later links Google OAuth which has different email.Result: Both emails link to same contact via shared userId.

Merge Rules

DataRule
Events/ActivitiesCombined into unified timeline
TraitsNewer values overwrite older
IdentifiersAll identifiers preserved
Anonymous historyAll visitor events included

Browser vs Server Identity

Anonymous visitors are supported
  • visitorId auto-generated
  • Events stored until identification
  • Later linked via identify() or setUser()
// Works without identity
outlit.track('page_viewed', { page: '/pricing' })

// Later, when they sign up
outlit.identify({ email: 'user@example.com' })

// Or for SPAs, use setUser after authentication
outlit.setUser({
  email: 'user@example.com',
  userId: 'usr_123'
})

Best Practices

1. Identify Early

Call identify() as soon as you have user information:
// After login
onLogin(user) {
  outlit.identify({
    email: user.email,
    userId: user.id,
    traits: { plan: user.plan }
  })
}

// After form submission
onFormSubmit(data) {
  outlit.identify({
    email: data.email,
    traits: { source: 'contact_form' }
  })
}

2. Always Send Both Identifiers

When you have both email and userId, send both:
// Good - creates strong identity link
outlit.identify({
  email: user.email,
  userId: user.id,
  traits: { ... }
})

// Less good - single identifier
outlit.identify({
  email: user.email,
  traits: { ... }
})

Debugging

Common identity issues:
  • Events not linking to user: Ensure identify() is called after authentication and that email/userId matches what you expect.
  • Duplicate customer profiles: Usually caused by the same person using different emails or missing userId. Contact support for manual merge.
  • Anonymous history not appearing: The identify() call must come from the same browser/device. Background processing may take up to 5 minutes.

Next Steps

Website Visitors

Deep dive into the anonymous phase

API Reference

Direct API for custom integrations