Skip to main content

CRM systems

Introduction to CRM Data Models and APIs: HubSpot & Salesforce

Modern CRM platforms like Salesforce and HubSpot serve as the system of record for customer and sales data. Understanding their core data models and APIs is essential for any SaaS product integrating with these systems — whether for syncing customer information, automating sales processes, or enabling data-driven workflows.

This guide introduces the key CRM objects, their relationships, and the API approaches used in Salesforce and HubSpot.

Core CRM Objects

Both Salesforce and HubSpot organize customer data around standard business entities, referred to as "Objects." These objects map directly to common concepts in sales and customer management.

CRM ObjectSalesforceHubSpotPurpose
AccountAccountCompanyRepresents a business or organization.
ContactContactContactRepresents an individual person (prospect, lead, or customer).
Lead*Lead*Contact (Lifecycle Stage: Lead)Prospective customer not yet qualified. Salesforce treats as a separate object; HubSpot uses a stage within Contacts.
Opportunity / DealOpportunityDealRepresents a potential revenue-generating event (sale, renewal, upsell).
Activity / EngagementTask / Event / EmailEngagementRecords customer interactions like calls, emails, meetings.
Custom ObjectsCustom ObjectsCustom ObjectsAllows modeling of additional entities unique to your business (subscriptions, assets, tickets, etc).

*Note that some of our clients might use Leads to store at least proportion of their "contact" records. However, given that Leads are not associated to the Accounts by default we've made decision to only sync to Contact object.

Relationships Between Objects

CRMs use well-defined object relationships to reflect real-world associations between people, companies, and sales processes.

RelationshipDescriptionExample
Account ⇄ ContactMany Contacts belong to a single Account (Company).Employees of a customer organization.
Contact ⇄ DealContacts can be associated with multiple Deals.A buyer involved in several sales cycles.
Account ⇄ DealAn Account (Company) may have multiple Deals.Multiple opportunities.
Contact ⇄ ActivityActivities log interactions for a specific Contact.Calls, emails, meetings tracked over time.
Account ⇄ ActivityActivities can be associated with Accounts as well.Calls, emails, meetings tracked over time.

API Access and Integration

Both Salesforce and HubSpot offer comprehensive APIs for programmatic access to CRM data, enabling SaaS products to read, write, and sync customer data at scale.

Salesforce API Overview

Salesforce provides a highly flexible and powerful API ecosystem:

HubSpot API Overview

HubSpot offers a simpler, more opinionated API structure optimized for ease of use:

Common fields and relationships

Company (Account) Object — Key Fields Comparison

Concept / PurposeSalesforce Field NameHubSpot Property NameNotes
Company NameNamenamePrimary identifier of the company/account.
Website / DomainWebsitedomainCritical for matching, enrichment, deduplication. Salesforce uses full URL; HubSpot uses root domain (e.g., example.com).
Note that it is not uncommon for a client to maintain a custom domain field in Salesforce.
CRM OwnerOwnerId (User reference)hubspot_owner_idThe user that owns the account.
Last Activity DateLastActivityDatehs_last_engagement_dateAuto-updated on engagement (email, call, meeting, etc).
Last Modified DateLastModifiedDatehs_lastmodifieddateTracks when record was last updated.
Lifecycle Stage / StatusCustom or derived from OpportunitieslifecyclestageIn HubSpot, lifecycle stage is a built-in property that auto-updates based on deal creation/close. In Salesforce, this often requires configuration.
Number of DealsRoll-Up Summary Field (Count of Opportunities)num_associated_dealsHubSpot auto-calculates; Salesforce uses Roll-Up Summary Fields.
Recent Deal AmountRoll-Up Summary Field (Sum of Opportunity Amount)recent_deal_amountHubSpot built-in; Salesforce requires Roll-Up Summary Field.

Contact (Person) Object — Key Fields Comparison

Concept / PurposeSalesforce Field NameHubSpot Property NameNotes
First NameFirstNamefirstnameCore identity fields.
Last NameLastNamelastnameCore identity fields.
EmailEmailemailCommon integration identifier.
Phone NumberPhonephonePrimary phone number.
Associated CompanyAccountIdassociatedcompanyidRelationship to Account/Company object.
CRM OwnerOwnerIdhubspot_owner_idAssigned user responsible for the contact.
Lead SourceLeadSourcelead_sourceOrigin of the lead/contact. Useful for attribution.
Lifecycle StageCustom or Lead ObjectlifecyclestageHubSpot uses a built-in property. Salesforce uses separate Lead object or custom field.
Last Activity DateLastActivityDatehs_last_engagement_dateAuto-updated on engagement (email, call, meeting, etc).
Last Modified DateLastModifiedDatehs_lastmodifieddateTracks when record was last updated.
Number of DealsRoll-Up Summary Field (Count of Opportunities)num_associated_dealsHubSpot built-in; Salesforce requires Roll-Up Summary Field.

Notes on Field Behavior

Field TypeHubSpotSalesforce
Auto-Populated PropertiesExtensive — lifecyclestage, num_associated_deals, hs_last_engagement_date auto-update without custom config.Standard Fields like LastActivityDate exist, but deal counts and revenue require Roll-Up Summary Fields (setup dependent).
Often, the companies maintain their own fields rather than using defaults.
CustomizationProperty model — easy to add custom fields per object. User can add roll-up fields (i.e. copying data from Deal → Company) via workflows.Highly customizable schema — supports custom fields, roll-ups, and logic via automation tools.
Relationship ManagementAssociations API to link Contacts, Companies, Deals, Activities.Relational database model with lookup fields and junction objects.

How to Resolve OwnerId (Salesforce) and hubspot_owner_id (HubSpot) to Owner Name

Both Salesforce and HubSpot store the Owner of a record (Account/Contact/Deal) as an internal ID reference — not the user’s name or email directly.

To display or sync the name or email of the record owner, you need to perform an additional API lookup to the Users object (Salesforce) or Owners object (HubSpot).

Salesforce: Resolving OwnerId

Field

Field NameStored ValueNotes
OwnerIdSalesforce User ID (e.g., 005ABC123456789XYZ)Reference to a User record.

How To Fetch Owner Name

Use the Salesforce API to fetch the User object:

GET /services/data/vXX.X/sobjects/User/{OwnerId}

Response will include:

FieldNotes
NameFull name of the user.
EmailEmail address of the user.
UsernameInternal Salesforce username (may be email or custom).

Alternatively, in SOQL:

SELECT Id, Name, Email FROM User WHERE Id = '005ABC123456789XYZ'

And can ‘join’ (aka traverse relationship) like:

SELECT
Id,
Name,
Industry,
Website,
OwnerId,
Owner.Name,
Owner.Email
FROM Account
WHERE Website != NULL
LIMIT 100

Owner.Name and Owner.Email can be included directly when fetching Account, Contact, Opportunity via REST API

HubSpot: Resolving hubspot_owner_id

Field

Field NameStored ValueNotes
hubspot_owner_idHubSpot Owner ID (UUID format)Reference to an Owner record.

How To Fetch Owner Name

Use the HubSpot Owners API:

GET https://api.hubapi.com/crm/v3/owners/{ownerId}

Response will include:

FieldNotes
firstName / lastNameOwner’s name.
emailOwner’s email address.
idInternal ID (matches hubspot_owner_id).

HubSpot Owners API Reference:

https://developers.hubspot.com/docs/api/crm/owners

Summary

CRMField in RecordAPI to ResolveOutput Fields
SalesforceOwnerId/sobjects/User/{Id}Name, Email
HubSpothubspot_owner_id/crm/v3/owners/{id}firstName, lastName, email

Best Practices for Integrations

  1. Cache Owner Lookups (might only be needed for Hubspot)
    • Owners don’t change often → Cache User/Owner info locally.
    • Could have a write through cache table?
  2. Store Owner Name & Email Locally
    • When syncing data from Salesforce/HubSpot → Store both ID + Owner Name/Email in your system for fast display.
  3. Avoid Relying on ID in Front-End
    • Always resolve to human-friendly name or email before displaying in UI or notifications.

Handling Multi-Select Fields in Salesforce & HubSpot APIs

Salesforce — Multi-Picklist Fields

  • Multi-select fields (e.g., Industries__c) are returned as a single string with values separated by a ; (semicolon).
  • Example Response:
"Industries__c": "Technology;Healthcare;Finance"
  • Integration Handling:
  • Split the string client-side:
industries = response['Industries__c'].split(';')

HubSpot — Multi-Checkbox Fields

  • Multi-select fields (e.g., industry) are returned as a JSON array of strings.
  • Example Response:
"industry": ["technology", "healthcare", "finance"]
  • Integration Handling:
  • Use directly as an array — no parsing needed.

Summary

CRMAPI ResponseIntegration Handling
SalesforceString with ; delimiterSplit string by ; to array.
HubSpotJSON ArrayUse array as-is.

Key Field-Type Gotchas in Salesforce & HubSpot

1. Multi-Select Fields

(Already covered — but worth reiterating)

CRMBehaviorGotcha
SalesforceString with ; separatorMust split manually in code.
HubSpotArray of stringsSafe — native array.

2. Date & DateTime Fields

CRMBehaviorGotcha
SalesforceISO8601 but Date vs DateTime are different fieldsDate is date-only (no timezone), DateTime is full timestamp. Watch for timezone issues — stored in UTC.
HubSpotAlways timestamp in milliseconds since epochMust convert from ms to ISO or Date object in your app. Easily missed! Example: 1673455500000 = UTC datetime.

3. Boolean / Checkbox Fields

CRMBehaviorGotcha
SalesforceBoolean true / false valuesSafe — native boolean values in API.
HubSpotStored as string "true" / "false" in APIMust cast to real boolean in your code.

4. Picklist / Dropdown Fields

CRMBehaviorGotcha
SalesforceReturns stored value — not labelOften a machine-friendly value — need to map to label if displaying to users. Labels available via describe API.
HubSpotReturns stored value (not label)Same — you must map stored values to display labels from the properties API metadata.

5. Owner / User Fields

CRMBehaviorGotcha
SalesforceOwnerId requires relationship query (Owner.Name)Fetch via SOQL or fields=Owner.Name. Otherwise, second API call needed.
HubSpothubspot_owner_id — must fetch Owner separatelyNo inline Owner object. Prefetch and cache Owners for efficiency.

6. Number Fields

CRMBehaviorGotcha
SalesforceSafe — returns numeric valueHandle null/empty values gracefully.
HubSpotAlways stored as string in APIMust cast to number in code. Even numeric fields like annualrevenue come back as strings.

7. Null / Empty Field Behavior

CRMBehaviorGotcha
SalesforceReturns field as nullStraightforward — handle null checks.
HubSpotField may be missing from properties entirelyMust check for existence of key in the response before accessing value.

Summary

Field TypeSalesforceHubSpotIntegration Tip
Multi-select; separated stringArrayNormalize to array in both cases.
DateISO, UTCEpoch msAlways convert to ISO Date object.
BooleanTrue booleanString "true" / "false"Normalize to boolean in code.
PicklistStored valueStored valueMap to label via schema API.
OwnerOwnerId (relationship query)hubspot_owner_id (extra API call)Prefetch and cache Owners or use Owner.Name (SFDC)
NumberNative numberStringCast to number.
Nullnull valueField may be missingUse .get() or null checks.

Salesforce 15 vs 18 char length IDs

Salesforce uses 15-character IDs for internal use (e.g. in the UI) and 18-character IDs for external systems because the 15-character version is case-sensitive, which can cause issues in systems like Excel or databases that ignore case. The 18-character ID adds a 3-character checksum to make the ID case-insensitive safe — both refer to the same record. To convert a 15-character ID to 18, you can use https://github.com/GoodFit-io/gf-sourcers/blob/main/src/services/enrichmentApi/utils/sfdc.ts#L1; to go back, just take the first 15 characters.

Whenever we accept an ID we should accept both 15 and 18 chars, but only store 18 char versions in DB.

Soft deletion

Both CRMs support soft deletion:

  • salesforce = recycle bin
  • hubspot = archived
CRMSoft Delete MechanismAre Soft Deletes Returned by Default?How To Fetch Soft Deleted / Archived RecordsBest Practice for Detecting Deletes
SalesforceRecycle Bin — Records are soft-deleted first (unless hard delete is used).❌ No — API queries exclude soft-deleted records unless specified.Use SOQL with ALL ROWS and isDeleted = true. Example: SELECT Id FROM Account WHERE isDeleted=true ALL ROWSUse Change Data Capture (CDC) for real-time delete detection or poll with ALL ROWS.
HubSpotArchived — Records are soft-deleted into an archived state.❌ No — Standard API calls exclude archived records.Use API with archived=true query param. Example: /crm/v3/objects/companies?archived=trueUse Webhooks for real-time delete events; optionally poll archived records for reconciliation.
  • Salesforce soft deletes are queryable but hidden by default — requires special SOQL syntax to include them. Hard deletes skip Recycle Bin entirely. Can't seem to access via jsforce sobject().find()
  • HubSpot archived records are their soft-deleted state — excluded by default but easily included by passing archived=true in the API.
  • In both systems, Webhooks (HubSpot) or Change Data Capture (Salesforce) are the best way to track deletions in real time.

CRM types

Complete Field Type Mapping — Salesforce vs HubSpot

Concept / Data TypeSalesforce Field TypeHubSpot Field TypeNotes / Integration Considerations
Single Line TextTextStringStandard text field. Salesforce enforces length limits.
Multi-line TextText Area (Long), Rich Text AreaStringHubSpot does not differentiate — always string.
NumberNumber, Currency, PercentNumber (but returned as string via API)HubSpot API always returns numbers as strings.
BooleanCheckbox (True/False)Boolean (returned as string "true" / "false")Cast to Boolean in HubSpot integrations.
Date OnlyDateDateSalesforce stores date without time component. HubSpot stores date as epoch ms (UTC midnight).
DateTimeDateTimeDateTimeSalesforce returns ISO 8601 UTC. HubSpot returns epoch ms.
Picklist (Single Select)PicklistEnumeration (Dropdown Select)Returns stored value — map to label via property metadata.
Picklist (Multi-Select)Multi-PicklistEnumeration (Multiple Checkboxes)Salesforce returns value1;value2;value3 string. HubSpot returns array of strings.
EmailEmailStringBoth CRMs treat as string — used for matching.
Phone NumberPhoneStringNo enforced formatting — clean client-side.
URLURLStringNo enforced scheme in either — normalize https://.
Auto NumberAuto NumberNo Native EquivalentSalesforce auto-incrementing ID. HubSpot lacks this — workarounds required.
Formula FieldFormula (Read-Only)Calculated Property (Enterprise only)Always read-only via API. Used for computed values.
GeolocationGeolocation (Latitude/Longitude)Not SupportedSalesforce-only — HubSpot requires custom fields.
Lookup / ReferenceLookup(SObject)Association (via API)Salesforce has native lookup relationships. HubSpot uses associations API.
Owner / User FieldLookup(User)HubSpot Owner (User ID)Always returns internal ID — resolve to Name/Email via API. Cache locally.
Address (Compound Field)Address (Compound Field)Not Native — Requires separate propertiesSalesforce stores address as multiple subfields: Street, City, State, PostalCode, Country. HubSpot stores as separate fields.
Record IDID (15/18 char)Internal ID (UUID)Salesforce has special ID behavior. HubSpot uses UUID strings.
Encrypted TextEncrypted TextNot SupportedSalesforce-only — API only returns masked value or nothing depending on permissions.
Time (Time Only)TimeNot SupportedSalesforce-only field type for time of day — stored without date.
PercentPercentNumber (returned as string)Salesforce has dedicated Percent type. HubSpot treats as number (string).
CurrencyCurrencyNumber (returned as string)Salesforce has multi-currency features — may affect display. HubSpot stores as plain number.

Notes for Integration Teams

Salesforce-Specific Field Types:

  • Compound Fields (Address, Name) need to be queried explicitly for parts in API:
SELECT BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry FROM Account
  • Auto Number and Encrypted Text are Salesforce-only types.

HubSpot-Specific Behavior:

  • No native Address type — all fields like address, city, state, zip are separate string properties.
  • No Auto Number — any incremental IDs must be handled externally or via workflows.
  • Calculated Properties (Formula equivalent) only available on Enterprise tier.