0
# Property History Streams
1
2
Specialized streams for tracking changes to CRM object properties over time. These streams provide detailed audit trails showing when and how property values changed for contacts, companies, and deals.
3
4
## Capabilities
5
6
### Companies Property History
7
8
Stream for tracking changes to company properties over time.
9
10
```python { .api }
11
class CompaniesPropertyHistory(IncrementalStream):
12
"""
13
Stream for HubSpot company property change history.
14
15
Provides access to property history data including:
16
- Property value changes over time
17
- Change timestamps and source attribution
18
- User or system attribution for changes
19
- Property metadata and context
20
- Bulk import tracking
21
22
Requires OAuth scope: crm.schemas.companies.read
23
"""
24
```
25
26
### Contacts Property History
27
28
Stream for tracking changes to contact properties over time.
29
30
```python { .api }
31
class ContactsPropertyHistory(IncrementalStream):
32
"""
33
Stream for HubSpot contact property change history.
34
35
Provides access to property history data including:
36
- Contact property value changes
37
- Change timestamps and attribution
38
- Form submission property updates
39
- Email engagement property changes
40
- Integration sync property updates
41
42
Requires OAuth scope: crm.schemas.contacts.read
43
"""
44
```
45
46
### Deals Property History
47
48
Stream for tracking changes to deal properties over time.
49
50
```python { .api }
51
class DealsPropertyHistory(IncrementalStream):
52
"""
53
Stream for HubSpot deal property change history.
54
55
Provides access to property history data including:
56
- Deal stage progression tracking
57
- Amount and close date changes
58
- Owner assignment changes
59
- Pipeline movements
60
- Custom property modifications
61
62
Requires OAuth scope: crm.schemas.deals.read
63
"""
64
```
65
66
## Usage Examples
67
68
### Company Property Tracking
69
70
```python
71
from source_hubspot.streams import API
72
from source_hubspot.source import SourceHubspot
73
74
# Setup authentication
75
credentials = {
76
"credentials_title": "OAuth Credentials",
77
"client_id": "your_client_id",
78
"client_secret": "your_client_secret",
79
"refresh_token": "your_refresh_token"
80
}
81
82
config = {
83
"credentials": credentials,
84
"start_date": "2023-01-01T00:00:00Z"
85
}
86
87
# Get company property history
88
source = SourceHubspot(catalog=None, config=config, state=None)
89
streams = source.streams(config)
90
91
# Find property history stream
92
companies_history = next(
93
s for s in streams
94
if s.name == "companies_property_history"
95
)
96
97
# Process property changes
98
for record in companies_history.read_records(sync_mode="incremental"):
99
company_id = record['objectId']
100
property_name = record['propertyName']
101
old_value = record.get('previousValue')
102
new_value = record['value']
103
timestamp = record['timestamp']
104
105
print(f"Company {company_id}: {property_name}")
106
print(f" Changed from '{old_value}' to '{new_value}'")
107
print(f" At: {timestamp}")
108
```
109
110
### Contact Property Audit
111
112
```python
113
# Track specific contact property changes
114
contacts_history = next(
115
s for s in streams
116
if s.name == "contacts_property_history"
117
)
118
119
# Filter for specific properties
120
target_properties = ['email', 'firstname', 'lastname', 'lifecyclestage']
121
122
for record in contacts_history.read_records(sync_mode="incremental"):
123
if record['propertyName'] in target_properties:
124
contact_id = record['objectId']
125
property_name = record['propertyName']
126
change_source = record.get('sourceType', 'Unknown')
127
change_user = record.get('sourceId', 'System')
128
129
print(f"Contact {contact_id} - {property_name}")
130
print(f" Source: {change_source} by {change_user}")
131
print(f" Value: {record['value']}")
132
```
133
134
### Deal Pipeline Analysis
135
136
```python
137
# Analyze deal stage progression
138
deals_history = next(
139
s for s in streams
140
if s.name == "deals_property_history"
141
)
142
143
stage_changes = {}
144
for record in deals_history.read_records(sync_mode="incremental"):
145
if record['propertyName'] == 'dealstage':
146
deal_id = record['objectId']
147
new_stage = record['value']
148
timestamp = record['timestamp']
149
150
if deal_id not in stage_changes:
151
stage_changes[deal_id] = []
152
153
stage_changes[deal_id].append({
154
'stage': new_stage,
155
'timestamp': timestamp
156
})
157
158
# Calculate average time in each stage
159
for deal_id, changes in stage_changes.items():
160
changes.sort(key=lambda x: x['timestamp'])
161
print(f"Deal {deal_id} progression:")
162
for i, change in enumerate(changes):
163
print(f" Stage {i+1}: {change['stage']} at {change['timestamp']}")
164
```
165
166
## OAuth Scopes Required
167
168
Property history streams require specific OAuth scopes to access schema information:
169
170
- **Companies Property History**: `crm.schemas.companies.read`
171
- **Contacts Property History**: `crm.schemas.contacts.read`
172
- **Deals Property History**: `crm.schemas.deals.read`
173
174
These scopes are in addition to the basic object read permissions and must be explicitly granted during OAuth configuration.
175
176
## Data Structure
177
178
Each property history record contains:
179
180
```python
181
{
182
"objectId": "12345", # ID of the object that changed
183
"propertyName": "email", # Name of the property that changed
184
"value": "new@example.com", # New property value
185
"previousValue": "old@example.com", # Previous value (may be null)
186
"timestamp": "2023-01-15T10:30:00Z", # When the change occurred
187
"sourceType": "FORM", # How the change was made
188
"sourceId": "form-123", # Specific source identifier
189
"sourceLabel": "Contact Form", # Human-readable source description
190
"objectTypeId": "0-1" # HubSpot object type identifier
191
}
192
```
193
194
## Incremental Sync
195
196
Property history streams support incremental synchronization using timestamp-based cursors:
197
198
- **Cursor Field**: `timestamp`
199
- **Cursor Granularity**: Millisecond precision
200
- **Lookback Window**: 24 hours (configurable)
201
- **State Format**: ISO 8601 datetime string
202
203
The streams automatically handle pagination and ensure complete data consistency during incremental updates.