0
# Engagement Streams
1
2
Stream classes for HubSpot engagement data including calls, emails, meetings, notes, and tasks. These streams provide access to interaction history and communication records between users and CRM objects.
3
4
## Capabilities
5
6
### Call Engagements
7
8
Access to call engagement records including call details, duration, and outcomes.
9
10
```python { .api }
11
class EngagementsCalls(CRMSearchStream):
12
"""
13
Stream for HubSpot call engagement records.
14
15
Provides access to call data including:
16
- Call duration and timestamp
17
- Call outcome and disposition
18
- Associated contacts, companies, and deals
19
- Call recording and notes
20
- Custom call properties
21
"""
22
```
23
24
### Email Engagements
25
26
Access to email engagement records including email content and interaction data.
27
28
```python { .api }
29
class EngagementsEmails(CRMSearchStream):
30
"""
31
Stream for HubSpot email engagement records.
32
33
Provides access to email data including:
34
- Email subject and body content
35
- Send timestamp and status
36
- Email recipient information
37
- Associated contacts, companies, and deals
38
- Email tracking data (opens, clicks)
39
- Custom email properties
40
"""
41
```
42
43
### Meeting Engagements
44
45
Access to meeting engagement records including meeting details and attendee information.
46
47
```python { .api }
48
class EngagementsMeetings(CRMSearchStream):
49
"""
50
Stream for HubSpot meeting engagement records.
51
52
Provides access to meeting data including:
53
- Meeting title and description
54
- Start and end timestamps
55
- Meeting location and type
56
- Attendee information
57
- Associated contacts, companies, and deals
58
- Meeting outcome and follow-up notes
59
- Custom meeting properties
60
"""
61
```
62
63
### Note Engagements
64
65
Access to note engagement records including text content and associations.
66
67
```python { .api }
68
class EngagementsNotes(CRMSearchStream):
69
"""
70
Stream for HubSpot note engagement records.
71
72
Provides access to note data including:
73
- Note content and timestamp
74
- Note author information
75
- Associated contacts, companies, and deals
76
- Note attachments
77
- Custom note properties
78
"""
79
```
80
81
### Task Engagements
82
83
Access to task engagement records including task details and completion status.
84
85
```python { .api }
86
class EngagementsTasks(CRMSearchStream):
87
"""
88
Stream for HubSpot task engagement records.
89
90
Provides access to task data including:
91
- Task subject and description
92
- Due date and completion status
93
- Task priority and type
94
- Task assignee information
95
- Associated contacts, companies, and deals
96
- Task completion notes
97
- Custom task properties
98
"""
99
```
100
101
## Usage Examples
102
103
### Call Engagement Analysis
104
105
```python
106
from source_hubspot.streams import EngagementsCalls, API
107
108
# Setup API client
109
api = API(credentials)
110
111
# Create calls stream
112
calls = EngagementsCalls(
113
api=api,
114
start_date="2023-01-01T00:00:00Z",
115
credentials=credentials
116
)
117
118
# Analyze call outcomes
119
call_outcomes = {}
120
total_duration = 0
121
122
for record in calls.read_records(sync_mode="full_refresh"):
123
engagement = record['engagement']
124
metadata = record['metadata']
125
126
# Call outcome analysis
127
outcome = metadata.get('disposition', 'Unknown')
128
duration = metadata.get('durationMilliseconds', 0)
129
130
if outcome not in call_outcomes:
131
call_outcomes[outcome] = {'count': 0, 'total_duration': 0}
132
133
call_outcomes[outcome]['count'] += 1
134
call_outcomes[outcome]['total_duration'] += duration
135
total_duration += duration
136
137
print(f"Total call duration: {total_duration / 1000 / 60:.2f} minutes")
138
for outcome, data in call_outcomes.items():
139
avg_duration = data['total_duration'] / data['count'] / 1000 / 60
140
print(f"{outcome}: {data['count']} calls, avg {avg_duration:.2f} min")
141
```
142
143
### Email Engagement Tracking
144
145
```python
146
from source_hubspot.streams import EngagementsEmails
147
148
emails = EngagementsEmails(
149
api=api,
150
start_date="2023-01-01T00:00:00Z",
151
credentials=credentials
152
)
153
154
# Track email engagement metrics
155
email_stats = {
156
'sent': 0,
157
'opened': 0,
158
'clicked': 0,
159
'replied': 0
160
}
161
162
for record in emails.read_records(sync_mode="full_refresh"):
163
metadata = record['metadata']
164
165
email_stats['sent'] += 1
166
167
if metadata.get('opened'):
168
email_stats['opened'] += 1
169
170
if metadata.get('clicked'):
171
email_stats['clicked'] += 1
172
173
if metadata.get('replied'):
174
email_stats['replied'] += 1
175
176
print(f"Email Performance:")
177
print(f"Sent: {email_stats['sent']}")
178
print(f"Open Rate: {email_stats['opened'] / email_stats['sent'] * 100:.1f}%")
179
print(f"Click Rate: {email_stats['clicked'] / email_stats['sent'] * 100:.1f}%")
180
print(f"Reply Rate: {email_stats['replied'] / email_stats['sent'] * 100:.1f}%")
181
```
182
183
### Meeting Engagement Schedule
184
185
```python
186
from source_hubspot.streams import EngagementsMeetings
187
from datetime import datetime, timedelta
188
189
meetings = EngagementsMeetings(
190
api=api,
191
start_date="2023-01-01T00:00:00Z",
192
credentials=credentials
193
)
194
195
# Analyze meeting patterns
196
meeting_by_day = {}
197
upcoming_meetings = []
198
199
for record in meetings.read_records(sync_mode="full_refresh"):
200
engagement = record['engagement']
201
metadata = record['metadata']
202
203
# Parse meeting timestamp
204
timestamp = engagement['timestamp']
205
meeting_date = datetime.fromtimestamp(timestamp / 1000)
206
day_of_week = meeting_date.strftime('%A')
207
208
# Count meetings by day of week
209
if day_of_week not in meeting_by_day:
210
meeting_by_day[day_of_week] = 0
211
meeting_by_day[day_of_week] += 1
212
213
# Check for upcoming meetings
214
if meeting_date > datetime.now():
215
upcoming_meetings.append({
216
'title': metadata.get('title', 'Untitled'),
217
'date': meeting_date,
218
'location': metadata.get('location', 'Not specified')
219
})
220
221
print("Meetings by day of week:")
222
for day, count in meeting_by_day.items():
223
print(f"{day}: {count}")
224
225
print(f"\nUpcoming meetings: {len(upcoming_meetings)}")
226
```
227
228
### Task Management
229
230
```python
231
from source_hubspot.streams import EngagementsTasks
232
233
tasks = EngagementsTasks(
234
api=api,
235
start_date="2023-01-01T00:00:00Z",
236
credentials=credentials
237
)
238
239
# Track task completion
240
task_stats = {
241
'total': 0,
242
'completed': 0,
243
'overdue': 0,
244
'by_priority': {}
245
}
246
247
current_time = datetime.now().timestamp() * 1000
248
249
for record in tasks.read_records(sync_mode="full_refresh"):
250
engagement = record['engagement']
251
metadata = record['metadata']
252
253
task_stats['total'] += 1
254
255
# Check completion status
256
if metadata.get('status') == 'COMPLETED':
257
task_stats['completed'] += 1
258
259
# Check for overdue tasks
260
due_date = metadata.get('forObjectType') # This would need proper field mapping
261
if due_date and due_date < current_time and metadata.get('status') != 'COMPLETED':
262
task_stats['overdue'] += 1
263
264
# Track by priority
265
priority = metadata.get('priority', 'NONE')
266
if priority not in task_stats['by_priority']:
267
task_stats['by_priority'][priority] = 0
268
task_stats['by_priority'][priority] += 1
269
270
completion_rate = task_stats['completed'] / task_stats['total'] * 100
271
print(f"Task completion rate: {completion_rate:.1f}%")
272
print(f"Overdue tasks: {task_stats['overdue']}")
273
print("Tasks by priority:", task_stats['by_priority'])
274
```
275
276
### Note Content Analysis
277
278
```python
279
from source_hubspot.streams import EngagementsNotes
280
281
notes = EngagementsNotes(
282
api=api,
283
start_date="2023-01-01T00:00:00Z",
284
credentials=credentials
285
)
286
287
# Analyze note content
288
note_stats = {
289
'total': 0,
290
'total_length': 0,
291
'by_author': {}
292
}
293
294
for record in notes.read_records(sync_mode="full_refresh"):
295
engagement = record['engagement']
296
metadata = record['metadata']
297
298
note_stats['total'] += 1
299
300
# Note content length
301
body = metadata.get('body', '')
302
note_stats['total_length'] += len(body)
303
304
# Notes by author
305
owner_id = engagement.get('ownerId')
306
if owner_id not in note_stats['by_author']:
307
note_stats['by_author'][owner_id] = 0
308
note_stats['by_author'][owner_id] += 1
309
310
avg_length = note_stats['total_length'] / note_stats['total']
311
print(f"Average note length: {avg_length:.0f} characters")
312
print(f"Total notes: {note_stats['total']}")
313
print("Most active note authors:",
314
sorted(note_stats['by_author'].items(), key=lambda x: x[1], reverse=True)[:5])
315
```
316
317
## Engagement Data Structure
318
319
All engagement streams return records with a consistent structure:
320
321
```python { .api }
322
# Engagement record structure
323
{
324
"engagement": {
325
"id": str, # Engagement ID
326
"timestamp": int, # Unix timestamp in milliseconds
327
"type": str, # Engagement type (CALL, EMAIL, MEETING, NOTE, TASK)
328
"ownerId": str, # HubSpot user ID of engagement owner
329
"active": bool, # Whether engagement is active
330
"createdAt": int, # Creation timestamp
331
"lastUpdated": int # Last update timestamp
332
},
333
"associations": {
334
"contactIds": List[str], # Associated contact IDs
335
"companyIds": List[str], # Associated company IDs
336
"dealIds": List[str], # Associated deal IDs
337
"ticketIds": List[str] # Associated ticket IDs
338
},
339
"metadata": {
340
# Type-specific metadata fields
341
# Varies by engagement type (calls, emails, meetings, notes, tasks)
342
}
343
}
344
```
345
346
## OAuth Scopes
347
348
Engagement streams require specific OAuth scopes:
349
350
- **Calls**: `crm.objects.contacts.read`
351
- **Emails**: `crm.objects.contacts.read`, `sales-email-read`
352
- **Meetings**: `crm.objects.contacts.read`
353
- **Notes**: `crm.objects.contacts.read`
354
- **Tasks**: `crm.objects.contacts.read`
355
356
Some engagement types may require additional scopes for full functionality:
357
- **Companies**: `crm.objects.companies.read`
358
- **Deals**: `crm.objects.deals.read`
359
- **Tickets**: `tickets`