# Identity and contacts (https://www.getfounderhq.com/docs/analytics/concepts/identity-and-contacts)

A contact is one person in your audience. Every event belongs to a
contact, even before you know who they are.

## Anonymous first

On the first visit, the SDK creates an anonymous ID and stores it. On
the web it also writes a first-party cookie on your registrable domain,
so `www.example.com` and `app.example.com` stay one visitor with one
attribution chain. Until you identify them, the contact is a guest.

## Identify with your own ID

Call `identify` when someone signs in or signs up. Pass the stable user
ID from your database.

```ts
founderhq.identify("user_8421", { email: "jane@acme.com" });
```

FounderHQ then merges the guest's history into that contact. The
pageviews and events from before the sign-up stay attached, which is
what makes attribution work.

Rules worth knowing:

- One external ID per contact. Use the same ID in your browser SDK,
  your mobile SDK, and the [Node SDK](/analytics/getting-started/quickstart-server-node).
- Never pass an email, a session ID, or a random value as the ID.
- Placeholder values are rejected: `anonymous`, `guest`, `id`, `null`,
  `undefined`, `none`, `nil`, `nan`, `[object Object]`, and empty
  strings.
- If that browser was already identified as a different person,
  FounderHQ does not merge the two. It starts a fresh anonymous
  identity instead.
- Call `reset()` on sign-out, so the next person on a shared machine
  starts clean.
- Calling `identify` again for the same person is safe. Do it on every
  page load if you like. Only the first call records an identification;
  later calls with new properties become a property update, and calls
  with nothing new send nothing.

## Properties on the person

`identify` accepts properties. You can also set them later.

```ts
founderhq.setPersonProperties({ plan: "growth" }, { signup_source: "podcast" });
```

The first argument overwrites. The second is set once and never
overwritten, which is how you keep first-touch facts honest. FounderHQ
does this for captured facts too: it stores both the latest value and
the initial value of things like the campaign, the browser, and the
country.

By default (`person_profiles: "identified_only"`) property changes made
before `identify` wait on the device and travel with the identify call,
so you never create profiles for people you do not know. Set
`person_profiles: "always"` to profile guests too, or `"never"` to
stitch identity without writing person properties.

### Properties FounderHQ understands

Any property is stored and shown on the contact. These names also fill
the contact's own fields:

| Property | Contact field |
| --- | --- |
| `email` | Email, used to match contacts |
| `phone` | Phone, used to match contacts |
| `name` | Split on the first space into first and last name |
| `firstName` / `first_name`, `lastName` / `last_name` | First and last name; these win over `name` |
| `avatarUrl` | Photo on the contact |
| `timezone` | Time zone |
| `locale` | Locale |

```ts
founderhq.identify("user_8421", {
  email: "jane@acme.com",
  name: "Jane Cooper",
  avatarUrl: "https://cdn.acme.com/avatars/jane.png",
});
```

## Properties on every event

Super properties ride along on every later event from that device.

```ts
founderhq.register({ workspace_tier: "growth" });
founderhq.registerOnce({ first_seen_variant: "b" });
founderhq.unregister("workspace_tier");
```

## From the server

Server events name the contact directly, by `externalId`, `email`, or
`phone`. Use the same external ID as the client and you keep one
contact.

## Related

- [Web SDK](/analytics/sdks/web)
- [Node SDK](/analytics/sdks/node)
- [Accounts and groups](/analytics/concepts/accounts-and-groups)
- [Consent and privacy](/analytics/concepts/consent-and-privacy)
