or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

api-client.mdbase-stream-classes.mdcrm-streams.mdcustom-objects.mdengagement-streams.mderror-handling.mdindex.mdmarketing-sales-streams.mdproperty-history-streams.mdsource-connector.mdweb-analytics.md

engagement-streams.mddocs/

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`