Documentation
Concepts

Identity and contacts

How an anonymous visitor becomes one contact across devices.

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.

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.
  • 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.

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:

PropertyContact field
emailEmail, used to match contacts
phonePhone, used to match contacts
nameSplit on the first space into first and last name
firstName / first_name, lastName / last_nameFirst and last name; these win over name
avatarUrlPhoto on the contact
timezoneTime zone
localeLocale
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.

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.

AI agent or LLM? Read this page as markdown

On this page