SalesCal Docs
Integrations

HubSpot CRM Integration

Sync leads and bookings into HubSpot as contacts, with auto field mapping and lifecycle-stage updates.

Benefits

  • Contact sync: Every prospect becomes or updates a HubSpot Contact
  • Meeting records: Bookings appear as HubSpot Meetings linked to the contact
  • Notes: Form answers and disqualification reasons attach as Notes
  • Stage tracking: SalesCal lifecycle stages flow into HubSpot's lifecyclestage and hs_lead_status properties
  • Auto field mapping: Match SalesCal form fields to HubSpot properties in one click, with missing fields created automatically

Connecting HubSpot

HubSpot shares the unified CRM Integration page with our other CRMs (only one CRM can be connected per team at a time).

  1. Go to Settings > Integrations
  2. Open CRM Integration
  3. Pick HubSpot from the CRM provider dropdown
  4. Click Connect
  5. HubSpot's OAuth screen opens — pick the account/portal you want to connect and click Authorize
  6. You're returned to SalesCal on the Configuration tab, ready to set up field mapping

Required Permissions

SalesCal requests these HubSpot scopes:

ScopePurpose
crm.objects.contacts.readLook up existing contacts by email
crm.objects.contacts.writeCreate and update contacts
crm.schemas.contacts.readList the contact properties you already have
crm.schemas.contacts.writeAuto-create missing custom properties when mapping fields

These scopes cover Contacts only. SalesCal does not touch Companies, Deals, or Tickets — if you need a deal created when a meeting books, set up a HubSpot workflow that watches the contact properties SalesCal updates.

Contacts, Not Leads

HubSpot has no separate "Lead" object — everything is a Contact, and "lead-ness" is a value on the lifecyclestage or hs_lead_status property. SalesCal mirrors this: each prospect upserts a Contact (matched by email) and SalesCal lifecycle changes update the property values, not separate records.

Field Mapping

Open the Configuration tab to control how SalesCal fields land in HubSpot.

Click Auto-map Now and SalesCal will:

  1. Match SalesCal fields to HubSpot properties by label
  2. Create a property group called "SalesCal Information" in HubSpot to hold any new properties
  3. Create a salescal_source boolean property so you can filter HubSpot for SalesCal-synced contacts
  4. Set sensible defaults for status mapping (e.g. "potential" → hs_lead_status: NEW + lifecyclestage: lead)

Turn on Auto-map new fields and any new form field you add later will auto-create in HubSpot on first sync. Leave it off if you'd rather review each new field manually.

Manual Mapping

The Field Mapping card lets you pick the HubSpot property for each SalesCal field:

  • Contact fields: first_name, last_name, email, phone — these are always synced as the contact's core identity
  • System fields: event_name, location, host_email, host_name, start_time, booking_status, outcome, outcome_reason
  • Form fields: every custom field you've added to your booking forms

Click the + next to any field to create a new HubSpot property without leaving SalesCal. Auto-created fields are text or number only — for dropdowns or date fields, create them in HubSpot first then map them in SalesCal.

One saved question, one HubSpot property. Form fields come from your team's shared question library, so if the same question is reused across several events, it still maps to a single HubSpot property — you won't end up with duplicate properties for the same question. Archived questions (from Settings → Questions) can't be picked for a new mapping, but if a question was mapped before it was archived, that mapping keeps working and still shows in the list.

Status Mapping

HubSpot has two status-like properties; SalesCal lets you map to either or both:

  • Lifecycle Stage (lifecyclestage) — HubSpot's fixed pipeline: subscriber, lead, MQL, SQL, opportunity, customer, evangelist, other
  • Lead Status (hs_lead_status) — your custom values

For each SalesCal stage (e.g. potential, qualified, booked, completed), you can pick a value for one or both fields.

What Syncs and When

The Connection tab has global toggles for each sync trigger. All are on by default:

ToggleFires when
Form startedProspect enters name + contact info
Form completedProspect submits the booking form
Booking createdA meeting time is confirmed
Booking cancelledThe booking is cancelled
Booking rescheduledThe booking moves to a new time
Outcome recordedYou mark the meeting completed, no-show, won, or lost
Contact info changedProspect details update on an existing lead

These are team-wide — they apply to every event you publish under this team. There's no per-event toggle.

HubSpot-Specific Behaviour

  • Email is the dedup key. SalesCal looks up the contact by email and updates it; new prospects without an email will not sync.
  • Notes are HTML. SalesCal converts newlines to <br> so multi-line answers render correctly in HubSpot.
  • Meetings have outcomes. SalesCal sets hs_meeting_outcome from the SalesCal outcome you record (Completed, No-show, etc.), so you can filter HubSpot meetings by result.
  • Form-started has an 180s delay. SalesCal waits ~3 minutes before syncing "Form started" to avoid creating duplicate contacts when a prospect re-enters the form.

Troubleshooting

"Failed to connect"

Make sure you have admin access to the HubSpot portal you're trying to connect. SalesCal needs to install OAuth scopes that require admin permissions on the portal.

"Contact didn't sync"

Check the Sync Log tab. The most common reasons:

  • Prospect didn't supply an email
  • HubSpot rate-limited us (free tier: 5 req/s) — SalesCal retries these automatically with a backoff
  • A custom property you mapped no longer exists in HubSpot (rename or recreate it, then re-sync)

"Sync failed and will not be retried"

HubSpot rejected the request itself, so retrying would fail in exactly the same way. The message quotes HubSpot's own reason. The most common one is INVALID_EMAIL: the prospect mistyped their address (for example .vom instead of .com), and HubSpot won't store it on a contact.

Nothing is lost. The lead is still in SalesCal with the address they typed, and the prospect will sync to HubSpot the next time an event fires for them — a booking, a reschedule, or an outcome you record — as long as the address on the lead is valid by then. The booking form now checks that an address is deliverable before it ever reaches HubSpot, so this should be rare.

Dismiss the notification once you've read it.

"resource not found"

The HubSpot contact SalesCal had on file for that prospect no longer exists. Someone deleted it, or merged it into another contact and HubSpot retired the old ID.

SalesCal recovers from this on its own: on the next sync it looks the prospect up again by email, re-points to the surviving contact, and creates a new one if it's genuinely gone. You can dismiss the notification.

"Lifecycle stage isn't updating"

HubSpot only allows lifecycle stage to move "forward" on the default pipeline. If you have stage-progression rules in HubSpot, our update may be silently ignored. Switch to hs_lead_status instead, or remove the HubSpot-side restriction.

"Deals aren't being created"

By design — SalesCal does not create Deals. Set up a HubSpot workflow triggered by salescal_source = true (or by hs_lead_status changing) to create the Deal in your pipeline.

Disconnecting HubSpot

  1. Go to Settings > Integrations > CRM Integration
  2. Open the Connection tab and click Disconnect

The contacts and meetings already synced stay in HubSpot. SalesCal stops sending updates and clears the stored OAuth tokens.

Privacy & Security

  • OAuth tokens are stored encrypted and refresh automatically
  • SalesCal only sends data to HubSpot in this direction — HubSpot edits do not sync back into SalesCal
  • The salescal_source property gives you an easy filter for any SalesCal-managed contacts if you need to bulk-export or delete them

Plan Requirements

The OAuth scopes SalesCal uses (crm.objects.contacts.*, crm.schemas.contacts.*) are available on every HubSpot tier including Free CRM. Free-tier portals have lower API limits (5 req/s vs. 100+ on paid plans), so very high booking volume may need a paid plan to avoid retries.