0
# User and Group Management
1
2
User identification and property management system for tracking user attributes, behavioral data, and organizational groupings. PostHog's user management supports both individual user properties and group-level data for multi-tenant applications.
3
4
## Capabilities
5
6
### User Property Management
7
8
Set and manage user properties with support for both overwriting and append-only operations.
9
10
```python { .api }
11
def set(**kwargs: OptionalSetArgs) -> Optional[str]:
12
"""
13
Set properties on a user record.
14
15
Parameters:
16
- distinct_id: Optional[ID_TYPES] - Unique identifier for the user (defaults to context user)
17
- properties: Optional[Dict[str, Any]] - Dictionary of properties to set on the user
18
- timestamp: Optional[Union[datetime, str]] - When the properties were set
19
- uuid: Optional[str] - Unique identifier for this operation
20
- disable_geoip: Optional[bool] - Whether to disable GeoIP lookup
21
22
Returns:
23
Optional[str] - The operation UUID if successful
24
25
Notes:
26
- Overwrites existing property values
27
- Context tags are folded into properties
28
- No-op if no distinct_id available
29
"""
30
31
def set_once(**kwargs: OptionalSetArgs) -> Optional[str]:
32
"""
33
Set properties on a user record, only if they do not yet exist.
34
35
Parameters:
36
- Same as set() method
37
38
Returns:
39
Optional[str] - The operation UUID if successful
40
41
Notes:
42
- Does not overwrite existing property values
43
- Otherwise behaves identically to set()
44
"""
45
```
46
47
### Group Management
48
49
Manage group properties and associations for organizational or team-level data tracking.
50
51
```python { .api }
52
def group_identify(
53
group_type: str,
54
group_key: str,
55
properties: Optional[Dict] = None,
56
timestamp: Optional[datetime] = None,
57
uuid: Optional[str] = None,
58
disable_geoip: Optional[bool] = None
59
) -> Optional[str]:
60
"""
61
Set properties on a group.
62
63
Parameters:
64
- group_type: str - Type of your group (e.g., 'company', 'team', 'organization')
65
- group_key: str - Unique identifier of the group
66
- properties: Optional[Dict] - Properties to set on the group
67
- timestamp: Optional[datetime] - When the group was identified
68
- uuid: Optional[str] - Unique identifier for this operation
69
- disable_geoip: Optional[bool] - Whether to disable GeoIP lookup
70
71
Returns:
72
Optional[str] - The operation UUID if successful
73
"""
74
```
75
76
### User Identity Management
77
78
Link user identities across different stages of their lifecycle and associate anonymous behavior with identified users.
79
80
```python { .api }
81
def alias(
82
previous_id: str,
83
distinct_id: str,
84
timestamp: Optional[datetime] = None,
85
uuid: Optional[str] = None,
86
disable_geoip: Optional[bool] = None
87
) -> Optional[str]:
88
"""
89
Associate user behaviour before and after they e.g. register, login, or perform some other identifying action.
90
91
Parameters:
92
- previous_id: str - The unique ID of the user before identification
93
- distinct_id: str - The current unique id after identification
94
- timestamp: Optional[datetime] - When the alias was created
95
- uuid: Optional[str] - Unique identifier for this operation
96
- disable_geoip: Optional[bool] - Whether to disable GeoIP lookup
97
98
Returns:
99
Optional[str] - The operation UUID if successful
100
101
Notes:
102
- Links anonymous behavior to identified users
103
- Enables cohort analysis across user lifecycle
104
- Should be called when user identity becomes known
105
"""
106
```
107
108
## Usage Examples
109
110
### User Property Management
111
112
```python
113
import posthog
114
115
# Configure PostHog
116
posthog.api_key = 'phc_your_project_api_key'
117
118
# Set user properties (overwrites existing)
119
posthog.set('user123', {
120
'email': 'user@example.com',
121
'name': 'John Doe',
122
'plan': 'premium',
123
'signup_date': '2024-01-15',
124
'trial_end': '2024-02-15'
125
})
126
127
# Set properties only if they don't exist
128
posthog.set_once('user123', {
129
'first_visit': '2024-01-10',
130
'initial_referrer': 'google.com',
131
'signup_source': 'landing_page'
132
})
133
134
# Using context for automatic user identification
135
with posthog.new_context():
136
posthog.identify_context('user123')
137
138
# User ID automatically applied from context
139
posthog.set({
140
'last_active': '2024-09-07',
141
'feature_usage_count': 42
142
})
143
```
144
145
### Group Management
146
147
```python
148
import posthog
149
150
# Identify a company/organization
151
posthog.group_identify('company', 'acme_corp', {
152
'name': 'Acme Corporation',
153
'industry': 'Technology',
154
'size': 'Enterprise',
155
'plan': 'Business',
156
'mrr': 5000,
157
'employees': 250
158
})
159
160
# Identify a team within organization
161
posthog.group_identify('team', 'engineering', {
162
'name': 'Engineering Team',
163
'department': 'Product',
164
'lead': 'jane.doe@acme.com',
165
'members_count': 12
166
})
167
168
# Update group properties
169
posthog.group_identify('company', 'acme_corp', {
170
'mrr': 5500, # Updated MRR
171
'employees': 275 # Updated employee count
172
})
173
```
174
175
### User Identity Linking
176
177
```python
178
import posthog
179
180
# User starts as anonymous visitor
181
anonymous_id = 'anonymous_user_abc123'
182
posthog.capture(anonymous_id, 'page_viewed', {'page': 'landing'})
183
posthog.capture(anonymous_id, 'signup_started')
184
185
# User completes registration
186
identified_id = 'user_456'
187
posthog.capture(identified_id, 'signup_completed', {
188
'email': 'user@example.com'
189
})
190
191
# Link anonymous behavior to identified user
192
posthog.alias(anonymous_id, identified_id)
193
194
# Set user properties after identification
195
posthog.set(identified_id, {
196
'email': 'user@example.com',
197
'plan': 'free',
198
'verified': True
199
})
200
```
201
202
### Combined User and Group Tracking
203
204
```python
205
import posthog
206
207
# Set up user with group associations
208
user_id = 'user_789'
209
company_id = 'company_xyz'
210
team_id = 'team_frontend'
211
212
# Identify user properties
213
posthog.set(user_id, {
214
'name': 'Alice Smith',
215
'role': 'Senior Developer',
216
'department': 'Engineering',
217
'hire_date': '2023-06-01'
218
})
219
220
# Identify company
221
posthog.group_identify('company', company_id, {
222
'name': 'XYZ Startup',
223
'industry': 'SaaS',
224
'size': 'Series A',
225
'location': 'San Francisco'
226
})
227
228
# Identify team
229
posthog.group_identify('team', team_id, {
230
'name': 'Frontend Team',
231
'tech_stack': 'React',
232
'team_lead': 'bob.johnson@xyz.com'
233
})
234
235
# Track events with group context
236
posthog.capture(user_id, 'feature_used', {
237
'feature': 'advanced_dashboard'
238
}, groups={
239
'company': company_id,
240
'team': team_id
241
})
242
```
243
244
### Context-Based Property Management
245
246
```python
247
import posthog
248
249
with posthog.new_context():
250
posthog.identify_context('user_456')
251
posthog.tag('session_type', 'premium')
252
posthog.tag('ab_test_group', 'variant_b')
253
254
# Tags are automatically included in user properties
255
posthog.set({
256
'last_login': '2024-09-07T10:30:00Z',
257
'subscription_status': 'active'
258
})
259
260
# Context tags become part of the user profile
261
posthog.capture('dashboard_viewed')
262
```
263
264
## Property Types and Best Practices
265
266
### Supported Property Types
267
268
```python
269
# Strings
270
posthog.set('user123', {'name': 'John Doe', 'plan': 'premium'})
271
272
# Numbers
273
posthog.set('user123', {'age': 28, 'score': 95.5})
274
275
# Booleans
276
posthog.set('user123', {'verified': True, 'trial_expired': False})
277
278
# Dates (as ISO strings)
279
posthog.set('user123', {
280
'signup_date': '2024-01-15T10:30:00Z',
281
'last_active': '2024-09-07'
282
})
283
284
# Arrays
285
posthog.set('user123', {
286
'interests': ['technology', 'sports', 'music'],
287
'visited_pages': ['/home', '/about', '/contact']
288
})
289
```
290
291
### Property Naming Conventions
292
293
```python
294
# Good - clear, consistent naming
295
posthog.set('user123', {
296
'email': 'user@example.com',
297
'first_name': 'John',
298
'last_name': 'Doe',
299
'signup_date': '2024-01-15',
300
'subscription_tier': 'premium',
301
'feature_flags_enabled': ['new_ui', 'beta_features']
302
})
303
304
# Avoid - inconsistent or unclear names
305
posthog.set('user123', {
306
'Email': 'user@example.com', # Inconsistent case
307
'fName': 'John', # Abbreviated
308
'user_registered': '2024-01-15', # Inconsistent naming
309
'tier': 'premium' # Could be ambiguous
310
})
311
```
312
313
### Group Association Patterns
314
315
```python
316
# Multi-level organization structure
317
posthog.capture('user123', 'report_generated', {
318
'report_type': 'monthly_summary'
319
}, groups={
320
'company': 'acme_corp',
321
'division': 'north_america',
322
'team': 'sales_team_west'
323
})
324
325
# Feature flag evaluation with groups
326
enabled = posthog.feature_enabled(
327
'new_dashboard',
328
'user123',
329
groups={'company': 'acme_corp'},
330
person_properties={'plan': 'enterprise'},
331
group_properties={
332
'company': {'size': 'large', 'industry': 'tech'}
333
}
334
)
335
```
336
337
## Error Handling
338
339
### Validation and Fallbacks
340
341
```python
342
# Properties are automatically validated
343
posthog.set('user123', {
344
'valid_string': 'hello',
345
'valid_number': 42,
346
'invalid_function': lambda x: x, # Dropped - not serializable
347
'nested_object': {'key': 'value'} # Flattened automatically
348
})
349
350
# Automatic fallback for missing distinct_id
351
with posthog.new_context():
352
# No context user set - operation is no-op
353
posthog.set({'property': 'value'}) # Does nothing
354
355
posthog.identify_context('user123')
356
posthog.set({'property': 'value'}) # Works correctly
357
```
358
359
### Retry and Reliability
360
361
User and group operations support the same retry mechanisms as events:
362
363
- Automatic retries for network failures
364
- Exponential backoff for rate limits
365
- Queueing for offline scenarios
366
- Error logging for debugging