Accounts and groups
Measure companies and workspaces, not only individual seats.
An account is the company or workspace a contact belongs to. Other tools call this a group. FounderHQ calls it an account, because it is also who pays.
Consumer product? You can skip this page. Contacts are enough.
Why accounts exist
If your product is sold to teams, one payment covers many seats. Tie
that payment to a single contact and you credit the payer, ignore the
teammate whose demo actually won the deal, and read $0 next to every
other seat. An account fixes that: revenue and touches belong to the
workspace.
Tell FounderHQ which account is active
founderhq.setAccount("workspace_123");
founderhq.setAccountProperties({ name: "Acme", seats: 12 });
founderhq.clearAccount();Every event captured afterwards carries that account. On the web, the active account is per tab, so someone with two workspaces open in two tabs reports each correctly. On mobile, it is per app session.
When a sign-in changes both the person and the workspace, change them together:
founderhq.identify("user_8421", { email: "jane@acme.com" }, {
account: "workspace_123",
});Switching accounts does not restart the person's session. It rotates a separate account span, so your session metrics stay honest.
Name and picture
name names the account everywhere in FounderHQ. avatarUrl is its
picture, such as the company's logo: the same property a contact's photo
uses. Send a full https:// address.
founderhq.setAccountProperties({
name: "Acme",
avatarUrl: "https://cdn.acme.com/logo.png",
});From your server, upsertAccount takes the same properties. Without a
picture, FounderHQ draws the account's initials.
Declare membership from your server
The active account on an event is an observation. Your backend knows the truth: who joined, who left, and when. Only the Node SDK can say so.
events.accountMembership({
account: "workspace_123",
userId: "user_8421",
state: "left",
effectiveAt: new Date("2026-08-17T10:30:00Z"),
idempotencyKey: "membership_user_8421_left_2026_08_17",
});joinedandleftrecord real changes. A leave stops later activity from counting for that account.retractedmeans the membership was wrong from the start. Do not use it when someone departs.upsertAccountupdates the account's own properties from the server.
How accounts change the numbers
- A payment belongs to exactly one subject: a contact or an account. Never both, and never copied to each seat.
- With Revenue owner set to Organization, a checkout that
carries the account's context token goes to that account when the
account is confirmed and the payer is a declared member of it at the
time of the payment. A context token does not declare a membership: on
a browser or app event it counts only for a payer your server already
declared with
accountMembership. Without the token, a payer in exactly one account pays for that account, and a payer in several accounts, or in none, pays as a contact. A goal that follows the revenue owner counts that contact's conversions for the contact. See Which account gets a payment. - Account attribution uses the touches of everyone who was a member at the time the money was earned. Touches from before the account existed still count, because visit → signup → create workspace → pay is the normal path.
- If a person belongs to two accounts and browses without an active account, that touch counts for neither.
- Bot traffic never creates membership and never counts as a touch.