# How OakData knows who's who > How OakData links anonymous activity to identified users, how profiles are matched and merged across devices, and how traits and groups are resolved over time. Source: https://oakdata.co/docs/concepts/identity --- OakData follows a visitor anonymously from their very first pageview. When they sign in, that earlier activity is stitched to the identified profile - one continuous history, including everything they did *before* they had an account. Timeline showing anonymous events before identify() merging into an identified distinct id afterwards. One visitor, one timeline — pre-sign-in activity stitches onto the identified profile. ## The two ids - **anonymous_id** - a random id generated on first visit and stored in the browser. Follows the visitor while they're unknown. - **distinct_id** - the visitor's canonical id. Before `identify` it equals the anonymous id; after, it becomes the user id you supplied. ## identify: anonymous → known Call `identify` once you know who the visitor is - right after sign-in or sign-up. It emits an `$identify` event carrying both the new user id and the previous anonymous id, which is how the server merges the two histories. **identify** ```ts oak.identify(user.id, { email: user.email, name: user.name }) ``` From then on `oak.getDistinctId()` returns the user id, and new events are attributed to the identified person. ## Traits Traits are durable attributes of a person - email, plan, role. Set them via `identify`'s second argument, or directly with `set` and `setOnce`. The server replays `$identify`, `$set`, and `$set_once` events chronologically, so a profile always reflects the latest values (`setOnce` never overwrites an existing key). **traits** ```ts oak.set({ plan: 'enterprise' }) // overwrites oak.setOnce({ signup_source: 'docs' }) // only if not already set ``` ## Groups A `group` ties a person to an account, company, or workspace - an entity many users share. Group membership and traits resolve the same way person traits do, and surface on the [visitor profile](https://oakdata.co/docs/api/rest). **group** ```ts oak.group('company', 'acme-co', { name: 'Acme', plan: 'enterprise' }) ``` ## Profile matching & merging Beyond `identify`, OakData continuously looks for profiles that belong to the same person and surfaces them as **related people** on the visitor profile. Each candidate is labeled with *why* it's related, and only one kind of evidence merges automatically: - **Same login - merged automatically.** Two profiles that ever signed in with the same user id are the same person by definition. Their activity is folded together and the people list shows them as one row, no action needed. - **Device match - suggested, never automatic.** The GPU and CPU fingerprint hashes identify a hardware *model*, not a specific unit - two factory-identical laptops share them. OakData scores each candidate against cross-browser-stable signals (screen geometry, language, timezone, system appearance and motion preferences, storage quota, peripheral counts) and shows the result as a confidence level with a signal-by-signal comparison. You decide. - **Same network - context only.** Profiles seen on any of the same IPs (a household, office, or VPN) are listed for context but carry no identity weight on their own. Confirming a match works like a git merge: the secondary profile folds into a **master** - the identified profile if there is one, else the one with the most events, else the oldest. Stats aggregate across the pair, the people list collapses to the master row (members stay visible under it, tagged *Merged*), and the underlying events are never rewritten - every decision is reversible from the same dialog. > **Why device matches stay manual** Two family members with identical phones on the same Wi-Fi agree on nearly every hardware signal. Person-level signals (dark mode, reduced motion, storage quota) help split them, but only a shared login is proof - so everything short of it stays a suggestion. ## alias: merging ids you control `alias` links the current distinct id to another id you own. Use it when you mint an id on your backend and need to tie it to the browser's anonymous id - cases `identify` alone doesn't cover. ## reset: sign-out On **sign-out**, call `oak.reset()`. It clears stored ids, traits, super properties, and groups, then issues a fresh anonymous id - so the next person on a shared device starts clean. > **Always reset on logout** Skip `reset()` and a second user on the same browser inherits the first user's identity, corrupting both profiles. Wire it into your sign-out handler. ## Looking someone up The [visitor endpoint](https://oakdata.co/docs/api/rest) (and the `get_visitor` [MCP tool](https://oakdata.co/docs/api/mcp)) accept either the distinct id or the anonymous id, and return the resolved profile: user id, traits, groups, attribution, identity timeline, and recent sessions. > **How a person is named and pictured** Identified people show their real name and email. An anonymous human gets a stable, friendly alias (like *Amber Panda*) and an avatar seeded from their distinct id, so you can recognise them across visits without knowing who they are. A [bot](https://oakdata.co/docs/concepts/bots) keeps its real crawler name (like *Googlebot*) and a robot glyph - never a disguised human face.