Groups
Track analytics at the account, company, or team level — not just individual users.
Groups let you associate users with a shared entity — like a company, workspace, or team — and analyze behavior at that level. Instead of asking "what did Jane do?", you can ask "what is Acme Inc doing?"
This is especially useful for B2B SaaS products where a single paying account has many users.
How Groups work
There are two separate concepts:
- The group entity — created/updated with
upsertGroup(). Stores metadata about the group (name, plan, etc.). - Group membership — set with
setGroup()/setGroups(). Links a user profile to one or more groups, and automatically attaches those group IDs to every subsequenttrack()call.
Creating or updating a group
Call upsertGroup() to create a group or update its properties. The group is identified by its id and type.
op.upsertGroup({
id: 'org_acme', // Your group's unique ID
type: 'company', // Group type (company, workspace, team, etc.)
name: 'Acme Inc', // Display name
properties: {
plan: 'enterprise',
seats: 25,
industry: 'logistics',
},
});Group payload
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for the group |
type | string | Yes | Category of group (e.g. "company", "workspace") |
name | string | Yes | Human-readable display name |
properties | object | No | Custom metadata about the group |
Managing groups in the dashboard
The easiest way to create, edit, and delete groups is directly in the OpenPanel dashboard. Navigate to your project and open the Groups section — from there you can manage group names, types, and properties without touching any code.
upsertGroup() is the right tool when your group properties are dynamic and driven by your own data — for example, syncing a customer's current plan, seat count, or MRR from your backend at login time.
A good rule of thumb: call upsertGroup() on login or when group properties change — not on every request or page view. If you find yourself calling it frequently with the same data, the dashboard is probably the better place to manage that group.
Assigning a user to a group
After identifying a user, call setGroup() to link them to a group. This also attaches the group ID to all future track() calls for the current session.
// After login
op.identify({ profileId: 'user_123' });
// Link the user to their organization
op.setGroup('org_acme');For users that belong to multiple groups:
op.setGroups(['org_acme', 'team_engineering']);setGroup() and setGroups() persist group IDs on the SDK instance. All subsequent track() calls will automatically include these group IDs until clear() is called.
Full login flow example
setGroup() doesn't require the group to exist first. You can call it with just an ID — events will be tagged with that group ID, and you can create the group later in the dashboard or via upsertGroup().
// 1. Identify the user
op.identify({
profileId: 'user_123',
firstName: 'Jane',
email: 'jane@acme.com',
});
// 2. Assign the user to the group — the group doesn't need to exist yet
op.setGroup('org_acme');
// 3. All subsequent events are now tagged with the group
op.track('dashboard_viewed'); // → includes groups: ['org_acme']
op.track('report_exported'); // → includes groups: ['org_acme']If you want to sync dynamic group properties from your own data (plan, seats, MRR), add upsertGroup() to the flow:
op.identify({ profileId: 'user_123', email: 'jane@acme.com' });
// Sync group metadata from your backend
op.upsertGroup({
id: 'org_acme',
type: 'company',
name: 'Acme Inc',
properties: { plan: 'pro' },
});
op.setGroup('org_acme');Per-event group override
You can attach group IDs to a specific event without affecting the SDK's persistent group state:
op.track('file_shared', {
filename: 'q4-report.pdf',
groups: ['org_acme', 'org_partner'], // Only applies to this event
});Groups passed in track() are merged with any groups already set on the SDK instance.
Clearing groups on logout
clear() resets the profile, device, session, and all groups. Always call it on logout.
function handleLogout() {
op.clear();
// redirect to login...
}Common patterns
B2B SaaS — company accounts
// On login
op.identify({ profileId: user.id, email: user.email });
op.upsertGroup({
id: user.organizationId,
type: 'company',
name: user.organizationName,
properties: { plan: user.plan, mrr: user.mrr },
});
op.setGroup(user.organizationId);Multi-tenant — workspaces
// When user switches workspace
op.upsertGroup({
id: workspace.id,
type: 'workspace',
name: workspace.name,
});
op.setGroup(workspace.id);Teams within a company
// User belongs to a company and a specific team
op.setGroups([user.organizationId, user.teamId]);API reference
upsertGroup(payload)
Creates the group if it doesn't exist, or merges properties into the existing group.
op.upsertGroup({
id: string; // Required
type: string; // Required
name: string; // Required
properties?: Record<string, unknown>;
});setGroup(groupId)
Adds a single group ID to the SDK's internal group list and sends an assign_group event to link the current profile to that group.
op.setGroup('org_acme');setGroups(groupIds)
Same as setGroup() but for multiple group IDs at once.
op.setGroups(['org_acme', 'team_engineering']);What to avoid
- Calling
upsertGroup()on every event or page view — call it on login or when group properties actually change. For static group management, use the dashboard instead. - Not calling
setGroup()afteridentify()— without it, events won't be tagged with the group and you won't see group-level data in the dashboard. - Forgetting
clear()on logout — groups persist on the SDK instance, so a new user logging in on the same session could inherit the previous user's groups. - Using
upsertGroup()to link a user to a group —upsertGroup()manages the group entity only. UsesetGroup()to link a user profile to it.