OpenPanel

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:

  1. The group entity — created/updated with upsertGroup(). Stores metadata about the group (name, plan, etc.).
  2. Group membership — set with setGroup() / setGroups(). Links a user profile to one or more groups, and automatically attaches those group IDs to every subsequent track() 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

FieldTypeRequiredDescription
idstringYesUnique identifier for the group
typestringYesCategory of group (e.g. "company", "workspace")
namestringYesHuman-readable display name
propertiesobjectNoCustom 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() after identify() — 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 groupupsertGroup() manages the group entity only. Use setGroup() to link a user profile to it.

On this page