HubSpot source connector for Airbyte that syncs CRM data including contacts, companies, deals, and marketing activities with support for OAuth and Private App authentication
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Historical tracking streams for CRM entity property changes, providing detailed audit trails and change tracking capabilities for contacts, companies, and deals.
Historical changes to contact properties with timestamps and change details.
contacts_property_history:
primary_key: ["contactId", "property", "timestamp"]
cursor_field: "timestamp"
sync_mode: incremental
schema:
type: object
properties:
contactId:
type: string
description: "Contact ID associated with the property change"
property:
type: string
description: "Name of the property that changed"
value:
type: string
description: "New property value"
sourceType:
type: string
description: "Source of the change (e.g., API, IMPORT, CRM_UI)"
sourceId:
type: string
description: "Identifier of the change source"
sourceLabel:
type: string
description: "Human-readable source description"
timestamp:
type: string
format: date-time
description: "When the property change occurred"
updatedByUserId:
type: string
description: "ID of user who made the change"Usage Example:
streams:
- name: contacts_property_history
sync_mode: incremental
cursor_field: ["timestamp"]
destination_sync_mode: appendHistorical changes to company properties with timestamps and change details.
companies_property_history:
primary_key: ["companyId", "property", "timestamp"]
cursor_field: "timestamp"
sync_mode: incremental
schema:
type: object
properties:
companyId:
type: string
description: "Company ID"
property:
type: string
description: "Name of the property that changed"
value:
type: string
description: "New property value"
sourceType:
type: string
description: "Source of the change (e.g., API, IMPORT, CRM_UI)"
sourceId:
type: string
description: "Identifier of the change source"
sourceLabel:
type: string
description: "Human-readable source description"
timestamp:
type: string
format: date-time
description: "When the property change occurred"
updatedByUserId:
type: string
description: "ID of user who made the change"Historical changes to deal properties with timestamps and change details.
deals_property_history:
primary_key: ["dealId", "property", "timestamp"]
cursor_field: "timestamp"
sync_mode: incremental
schema:
type: object
properties:
dealId:
type: string
description: "Deal ID"
property:
type: string
description: "Name of the property that changed"
value:
type: string
description: "New property value"
sourceType:
type: string
description: "Source of the change (e.g., API, IMPORT, CRM_UI)"
sourceId:
type: string
description: "Identifier of the change source"
sourceLabel:
type: string
description: "Human-readable source description"
timestamp:
type: string
format: date-time
description: "When the property change occurred"
updatedByUserId:
type: string
description: "ID of user who made the change"Property history streams use a specialized extractor that processes HubSpot's propertiesWithHistory API response format:
HubspotPropertyHistoryExtractor:
type: RecordExtractor
field_path: ["results"]
entity_primary_key: string # Entity ID field name (vid, companyId, dealId)
additional_keys: array # Additional fields to include from parent record
# Extraction behavior:
# 1. Iterates over each entity in results array
# 2. Extracts propertiesWithHistory object for each entity
# 3. Creates individual records for each property version
# 4. Injects entity ID and timestamp into each property history record
# 5. Skips hs_lastmodifieddate to avoid duplicate change trackingEach property change generates a separate record with the following structure:
PropertyHistoryRecord:
type: object
properties:
# Entity identifier (varies by stream)
vid: string # For contacts_property_history
companyId: string # For companies_property_history
dealId: string # For deals_property_history
# Property change details
property: string # Property name that changed
value: string # New property value
timestamp: string # When change occurred (ISO format)
sourceType: string # Change source type
sourceId: string # Source identifier
sourceLabel: string # Human-readable source
updatedByUserId: string # User who made changeProperty history streams handle multiple timestamp formats from HubSpot:
Common sourceType values include:
API: Changes made via API callsCRM_UI: Changes made through HubSpot interfaceIMPORT: Changes from data importsAUTOMATION: Changes from workflows/automationCALCULATED: Changes from calculated propertiesMIGRATION: Changes from data migrationsAudit Trail Analysis:
SELECT
vid as contact_id,
property,
value,
timestamp,
sourceType,
updatedByUserId
FROM contacts_property_history
WHERE property = 'lifecyclestage'
ORDER BY timestamp DESC;Property Change Frequency:
SELECT
property,
COUNT(*) as change_count,
COUNT(DISTINCT companyId) as companies_affected
FROM companies_property_history
WHERE timestamp >= '2024-01-01'
GROUP BY property
ORDER BY change_count DESC;User Activity Tracking:
SELECT
updatedByUserId,
COUNT(*) as changes_made,
MIN(timestamp) as first_change,
MAX(timestamp) as last_change
FROM deals_property_history
WHERE sourceType = 'CRM_UI'
GROUP BY updatedByUserId;Property history streams include state migration to handle legacy empty cursor states:
MigrateEmptyStringState:
type: StateMigration
cursor_field: "timestamp"
cursor_format: "%ms" # Millisecond timestamp format
# Migration logic:
# - If cursor_field is empty string, migrate to start_date
# - Convert start_date to appropriate timestamp format
# - Default to "2006-06-01T00:00:00.000Z" if no start_dateInstall with Tessl CLI
npx tessl i tessl/airbyte-airbyte-source-hubspot