atrixANALYTICS

Identity

How anonymous visitors become people, when merges are refused, and how to undo one.

Identity in Atrix is deterministic, auditable and reversible. Every link between two ids is an edge in an identity graph. The same edges always produce the same people, whatever order they arrive in, every edge is kept with a before-and-after snapshot, and any edge can be undone.

Anonymous first

Before sign-in, a device has an anonymous id: a UUIDv7 made on the device. Anonymous events are personless by default. They are counted and queryable by device and session, but they do not create a person until the device is identified. That keeps anonymous traffic cheap and your People list clean.

identify: one-way

Call identify with your own user id when someone signs up or signs in:

import * as atrix from "@atrix.dev/analytics-web";

atrix.identify("user_42", { plan: "pro" });

This sends $identify, which links the device's anonymous id to user_42. From then on the device's events carry user_42, and its earlier anonymous events belong to the same person.

  • identify is one-way: once a device is identified, a different id is ignored until you call reset().
  • Call reset() on sign-out. The device gets a new anonymous id and session.
  • On the server there is no device, so server SDKs take the anonymous id explicitly: identify({ distinctId, anonId }) in Node, identify(distinct_id, anon_id=…) in Python, AnonID in Go.

There is no client alias

SDKs cannot merge two identified users. That is the most common cause of corrupted identity in other tools: two accounts on one shared device, or a reused id, silently become one person. Atrix refuses it.

The rules, in order

For each edge, Atrix checks, in this order:

  1. Blocked. The id is on the static blocklist or the project's dynamic blocklist. The events are stored, but the id never joins a person.
  2. Marked. The target id is marked as identified.
  3. No-op. Both ids already belong to the same person.
  4. Refused. Both sides already contain an identified user, unless the edge is $merge_dangerously.
  5. Capped. The merged person would exceed 2,000 ids.
  6. Otherwise the anonymous side is absorbed, or the two people are merged.

The surviving person id does not depend on arrival order: it is chosen by a fixed rule over the members, so replaying the edge log always reproduces the same graph.

The blocklist

These ids are refused everywhere (trimmed, case-insensitive): an empty string, null, undefined, none, nil, nan, guest, anonymous, anon, 0, -1, true, false, [object object], distinct_id, distinctid, id, user, user_id, userid, not_authenticated, unknown, email and the all-zero UUID. Ids over 400 characters, with leading or trailing whitespace, or containing NUL are refused too.

An id that gets linked to more than 1,000 distinct ids within 24 hours joins the project's dynamic blocklist, because it is almost certainly a shared placeholder rather than a person.

Merging two identified users on purpose

When you really do need to join two identified users (for example after you merge two accounts in your own database), send $merge_dangerously from your server with the other id in alias:

import { Atrix } from "@atrix.dev/analytics-node";

const atrix = new Atrix(process.env.ATRIX_PUBLIC_KEY ?? "");
atrix.capture({ distinctId: "user_1", event: "$merge_dangerously", properties: { alias: "user_2" } });
await atrix.shutdown();

Like every edge, it is audited and can be undone.

Unmerge

Open a person in People → Persons in the console. The Identity tab shows the graph: every id, every edge, when it arrived, and what it did (absorbed, merged, refused, blocked or capped). Unmerging an edge reverts it, recomputes the affected ids, and records who did it and why.

Through the region API, the same operation is:

curl -X POST "https://eu.api.analytics.atrix.dev/persons/$PERSON_ID/unmerge" \
  -H "Authorization: Bearer $REGION_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "edge_id": "…", "reason": "Shared family iPad merged two accounts" }'

It needs the persons:write scope; see Region API for how to get a region token.

Personal data in the console

Person properties that look like personal data (email, phone, name, address, IP, coordinates, national ids, bank numbers, and values shaped like emails or phone numbers) are shown masked as •••• unless the viewer's token carries the named persons.pii scope.

Groups

Groups model accounts in B2B products: group(type, key, properties?) on the client, groupIdentify on the server. A project can have up to five group types (for example company, team, club). Events carry the groups they belong to, and every insight can count unique groups.

On this page