docs: add groups

This commit is contained in:
Carl-Gerhard Lindesvärd
2026-03-18 21:49:08 +01:00
parent 2dc622cbf2
commit 3ee1463d4f
6 changed files with 320 additions and 3 deletions

View File

@@ -68,6 +68,34 @@ app.listen(3000, () => {
- `trackRequest` - A function that returns `true` if the request should be tracked.
- `getProfileId` - A function that returns the profile ID of the user making the request.
## Working with Groups
Groups let you track analytics at the account or company level. Since Express is a backend SDK, you can upsert groups and assign users from your route handlers.
See the [Groups guide](/docs/get-started/groups) for the full walkthrough.
```ts
app.post('/login', async (req, res) => {
const user = await loginUser(req.body);
// Identify the user
req.op.identify({ profileId: user.id, email: user.email });
// Create/update the group entity
req.op.upsertGroup({
id: user.organizationId,
type: 'company',
name: user.organizationName,
properties: { plan: user.plan },
});
// Assign the user to the group
req.op.setGroup(user.organizationId);
res.json({ ok: true });
});
```
## Typescript
If `req.op` is not typed you can extend the `Request` interface.

View File

@@ -116,9 +116,38 @@ op.decrement({
});
```
### Working with Groups
Groups let you track analytics at the account or company level. See the [Groups guide](/docs/get-started/groups) for the full walkthrough.
**Create or update a group:**
```js title="index.js"
import { op } from './op.ts'
op.upsertGroup({
id: 'org_acme',
type: 'company',
name: 'Acme Inc',
properties: { plan: 'enterprise' },
});
```
**Assign the current user to a group** (call after `identify`):
```js title="index.js"
import { op } from './op.ts'
op.setGroup('org_acme');
// or multiple groups:
op.setGroups(['org_acme', 'team_eng']);
```
Once set, all subsequent `track()` calls will automatically include the group IDs.
### Clearing User Data
To clear the current user's data:
To clear the current user's data (including groups):
```js title="index.js"
import { op } from './op.ts'

View File

@@ -227,9 +227,32 @@ useOpenPanel().decrement({
});
```
### Working with Groups
Groups let you track analytics at the account or company level. See the [Groups guide](/docs/get-started/groups) for the full walkthrough.
**Create or update a group:**
```tsx title="app/login/page.tsx"
useOpenPanel().upsertGroup({
id: 'org_acme',
type: 'company',
name: 'Acme Inc',
properties: { plan: 'enterprise' },
});
```
**Assign the current user to a group** (call after `identify`):
```tsx title="app/login/page.tsx"
useOpenPanel().setGroup('org_acme');
```
Once set, all subsequent `track()` calls will automatically include the group IDs.
### Clearing User Data
To clear the current user's data:
To clear the current user's data (including groups):
```js title="index.js"
useOpenPanel().clear()

View File

@@ -174,9 +174,37 @@ function MyComponent() {
}
```
### Working with Groups
Groups let you track analytics at the account or company level. See the [Groups guide](/docs/get-started/groups) for the full walkthrough.
```tsx
import { op } from '@/openpanel';
function LoginComponent() {
const handleLogin = async (user: User) => {
// 1. Identify the user
op.identify({ profileId: user.id, email: user.email });
// 2. Create/update the group entity (only when data changes)
op.upsertGroup({
id: user.organizationId,
type: 'company',
name: user.organizationName,
properties: { plan: user.plan },
});
// 3. Link the user to their group — tags all future events
op.setGroup(user.organizationId);
};
return <button onClick={() => handleLogin(user)}>Login</button>;
}
```
### Clearing User Data
To clear the current user's data:
To clear the current user's data (including groups):
```tsx
import { op } from '@/openpanel';

View File

@@ -0,0 +1,208 @@
---
title: Groups
description: Track analytics at the account, company, or team level — not just individual users.
---
import { Callout } from 'fumadocs-ui/components/callout';
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`.
```typescript
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.
<Callout>
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.
</Callout>
## 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.
```typescript
// After login
op.identify({ profileId: 'user_123' });
// Link the user to their organization
op.setGroup('org_acme');
```
For users that belong to multiple groups:
```typescript
op.setGroups(['org_acme', 'team_engineering']);
```
<Callout>
`setGroup()` and `setGroups()` persist group IDs on the SDK instance. All subsequent `track()` calls will automatically include these group IDs until `clear()` is called.
</Callout>
## 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()`.
```typescript
// 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:
```typescript
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:
```typescript
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.
```typescript
function handleLogout() {
op.clear();
// redirect to login...
}
```
## Common patterns
### B2B SaaS — company accounts
```typescript
// 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
```typescript
// When user switches workspace
op.upsertGroup({
id: workspace.id,
type: 'workspace',
name: workspace.name,
});
op.setGroup(workspace.id);
```
### Teams within a company
```typescript
// 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.
```typescript
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.
```typescript
op.setGroup('org_acme');
```
### `setGroups(groupIds)`
Same as `setGroup()` but for multiple group IDs at once.
```typescript
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 group** — `upsertGroup()` manages the group entity only. Use `setGroup()` to link a user profile to it.

View File

@@ -3,6 +3,7 @@
"install-openpanel",
"track-events",
"identify-users",
"groups",
"revenue-tracking"
]
}