# Identify people

> Tie an anonymous visitor to your own user ID with clickclacks('identify'), set traits, follow one person across devices, domains and your backend, and reset on sign-out.

- Canonical URL: https://clickclacks.io/docs/identify
- Section: Tracking
- Last updated: 2026-09-26

Every visitor starts anonymous. Call identify when someone signs in and their past and future events, on this device and others, come together as one person.

## Anonymous until identified {#anonymous}

On a visitor’s first page load the tracker creates a random ID for the browser (`per_…`) and keeps it in local storage, along with a session ID. Every event carries both. ClickClacks never sets a cookie and never guesses who someone is from their IP address or device, so an anonymous visitor stays anonymous until you say who they are.

## Call identify at sign-in {#identify}

```js
// After sign-in, with the stable ID your own system uses for this user
window.clickclacks('identify', 'user_4821', {
  plan: 'pro',
  company: 'Acme',
  signed_up_at: '2026-09-01',
})
```

- The ID is a string of 1 to 128 characters. Use your own stable user ID, such as a database primary key, not an email address that might change.
- It records an `$identify` event carrying the ID as `distinct_id`, plus any traits you pass.
- The ID is saved in the browser, so every later event from that browser carries it as `$distinct_id`, across reloads, until you call `reset`.
- Everything the browser did before, while anonymous, belongs to the same person.
- `$identify` events are free: they don’t count toward your monthly events.

Calling it again with the same ID is harmless, so it’s fine to call on every page load while someone is signed in. That also covers people who were already signed in before you added ClickClacks.

```ts title="auth.ts"
// For example, where your app learns who is signed in
async function onSignedIn(user: { id: string, plan: string }) {
  window.clickclacks?.('identify', user.id, { plan: user.plan })
}

// And on each page load while they stay signed in ($identify events are free)
if (currentUser) {
  window.clickclacks?.('identify', currentUser.id)
}
```

## Traits {#traits}

The object you pass to `identify` describes the person: plan, company, role. A person’s traits are read from their newest `$identify`, so sending a new value replaces the old one.

- The same rules as event properties apply: a plain object, 2 KB of JSON at most. Over that, only the ID is sent.
- Keep personal data to what you need. An email or name is fine as a trait if your team needs it in **People**; a password or payment detail never is.
- Properties on ordinary events describe the event, not the person. Use traits for “who they are now”.

## Across devices {#devices}

`identify` is also the cross-device call; there’s no separate linking command. The first browser to identify with an ID owns it. When a different browser later identifies with the same ID (a phone after a laptop, say), ClickClacks links it to that person, and its anonymous history comes along.

A browser that already belongs to one ID isn’t moved to another. That’s why [reset on sign-out](https://clickclacks.io/docs/identify.md#reset) matters: it gives the next person on a shared computer a fresh browser ID instead of the last user’s. If two records for one person were never joined, you can merge them by hand: open one in **People** and choose **Merge with…** from the profile’s menu. Merges can be undone.

## Across domains {#domains}

Browsers keep local storage separate for every hostname, so the ID you set on `acme.com` isn’t visible on `app.acme.com`. Two things join them:

- **Links between listed domains** carry the anonymous ID and session, so a visitor who clicks from your site into your app arrives as the same person. [Domains and identity](https://clickclacks.io/docs/domains.md#across-domains) explains how.
- **Calling `identify` on each domain** where the person is signed in. The identified ID itself doesn’t travel with the link.

## With your backend {#server}

Server events join the same people when they use the same ID. Send the ID you pass to `identify` in the browser as `distinct_id` from your server, and the events land on that person’s timeline. If the server sees an ID before any browser has identified with it, ClickClacks creates the person, and the browser joins them at its first `identify`. [Identity from the server](https://clickclacks.io/docs/server-identity.md) has the details, including attaching a server event to a specific browser.

## Reset on sign-out {#reset}

```ts title="auth.ts"
async function signOut() {
  await fetch('/api/sign-out', { method: 'POST' })
  window.clickclacks?.('reset')
}
```

`reset` forgets the identified ID, drops any events not sent yet, and starts a new anonymous ID and session. Tracking carries on, as a new visitor. It’s also available as `window.clickclacks.reset()`.

To stop tracking someone altogether, `reset` is the wrong tool: [opt them out](https://clickclacks.io/docs/consent.md) instead.

## Next steps {#next}

- [Domains and identity](https://clickclacks.io/docs/domains.md): one person from your marketing site into your app.
- [Identity from the server](https://clickclacks.io/docs/server-identity.md): identify and track from your backend.
- [Consent and opt-out](https://clickclacks.io/docs/consent.md): when someone says no.
