> ## Documentation Index
> Fetch the complete documentation index at: https://docs.salesfinity.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# HubSpot

> Permissions Salesfinity needs in HubSpot, what it imports, what it writes back, and how to configure the integration.

The HubSpot integration imports contacts from HubSpot lists or from your due call tasks, and logs
every dialed call back to HubSpot as a call engagement on the contact. Connect it from
**Settings → Connections & API → HubSpot**.

## Architecture

```mermaid theme={null}
sequenceDiagram
  autonumber
  actor Rep
  participant SF as Salesfinity
  participant HS as HubSpot portal
  Rep->>SF: Connect HubSpot
  SF->>HS: OAuth (public app, required + optional scopes)
  HS-->>SF: Access + refresh token, portal ID, owner ID
  Rep->>SF: Import a list or "All due tasks"
  SF->>HS: Read list members / search open tasks by owner
  HS-->>SF: Contacts with mapped properties
  SF-->>Rep: Contact list with HubSpot contact IDs
  Rep->>SF: Dial, then log the call
  SF->>HS: Create call engagement, associate to contact
  SF->>HS: Mark the contact's open call tasks completed
  HS-->>SF: Engagement ID
  SF-->>Rep: Call log shows sync state and link
```

Salesfinity is a public HubSpot app installed through OAuth. It uses HubSpot's CRM and
engagements APIs only; nothing is added to your portal beyond the app itself, which appears under
**Settings → Integrations → Connected Apps** once a user has connected.

## Permissions required to connect

Salesfinity authorizes with HubSpot through OAuth. When you click Connect, HubSpot shows the
permissions below. The first group is required: HubSpot will refuse the install if your portal
cannot grant one of them. The second group is requested but optional: HubSpot grants each one
only if your portal has the feature, and silently drops it otherwise, so a portal without deals or
sequences can still connect.

### Required

| Permission                   | Why Salesfinity needs it                                                                         |
| ---------------------------- | ------------------------------------------------------------------------------------------------ |
| `crm.lists.read`             | List your HubSpot lists and read their members so you can import one.                            |
| `crm.objects.contacts.read`  | Read contact properties during import and when matching a dialed contact.                        |
| `crm.objects.contacts.write` | Create a contact when you log a call to someone not yet in HubSpot, and sync phone numbers back. |
| `crm.objects.owners.read`    | Resolve owners so calls and imports are attributed to the right HubSpot user.                    |
| `sales-email-read`           | Read a contact's logged email history for the contact activity card.                             |
| `crm.objects.companies.read` | Read company properties for the account view and custom field mapping.                           |
| `crm.schemas.companies.read` | Discover which company properties exist so you can map them.                                     |
| `automation.sequences.read`  | List sequences so you can enroll contacts from the dialer.                                       |

### Optional

| Permission                                         | What it unlocks                                                                        |
| -------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `crm.objects.deals.read`, `crm.schemas.deals.read` | Deal fields on the contact card, deal custom fields, and deal-based lists in sourcing. |
| `crm.objects.deals.write`                          | Updating deals from the AI Companion.                                                  |
| `crm.objects.companies.write`                      | Updating companies from the AI Companion.                                              |
| `automation.sequences.enrollments.write`           | Enrolling a contact in a sequence from the dialer.                                     |
| `crm.objects.leads.read`                           | Reading HubSpot leads.                                                                 |

<Warning>
  A permission that was optional at connect time cannot be added later without reconnecting. If a
  HubSpot feature returns "missing the Companies read permission" or "missing the Deals read
  permission", disconnect and reconnect HubSpot and approve the full list. See
  [Troubleshooting](/integrations/troubleshooting#hubspot-says-a-permission-is-missing).
</Warning>

The person connecting needs a HubSpot seat that can grant these permissions. Super admins always
can. A user with a restricted permission set may see the install refused.

## For HubSpot admins: setup checklist

<Steps>
  <Step title="Decide who connects">
    Every rep can connect with their own HubSpot user, in which case calls are logged as that
    user and tasks are imported from that user's queue. Or a team admin connects once and turns
    on **Share with team**, and reps who never connect use the admin's connection while calls
    are still attributed to the owner the admin assigns them. Most teams have each rep connect.
  </Step>

  <Step title="Check who can install">
    Installing requires a HubSpot user who can grant every
    [required permission](#required). A super admin always can. A user with a restricted
    permission set may be refused at install time, and a portal whose subscription lacks a
    feature will silently drop the matching optional permission.
  </Step>

  <Step title="Connect and approve the full permission list">
    From **Settings → Connections & API → HubSpot** click Connect, choose the portal, and approve.
    Approve everything shown. A permission skipped now can only be added by reconnecting.
  </Step>

  <Step title="Set the call task source">
    In the integration's configuration, pick the HubSpot user whose tasks "All due tasks" should
    import. It defaults to the connecting user. In a shared integration, set it per member under
    "Call task source per member"; a member without one cannot import tasks.
  </Step>

  <Step title="Map dispositions and fields">
    Use the automatic suggestion to map every Salesfinity disposition to a HubSpot call outcome,
    then map contact properties to Salesfinity fields and pick any custom properties you want
    on the contact card.
  </Step>

  <Step title="Import and check attribution">
    Import a small list, dial one contact, and open the call in HubSpot. The engagement should sit
    on the contact's timeline under the rep's owner, and any open call task on that contact
    should now be completed.
  </Step>
</Steps>

## Objects and access

| HubSpot object            | Access                          | What Salesfinity does with it                                                                                                                           |
| ------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Lists                     | Read                            | List your lists, read their members for import                                                                                                          |
| Contacts                  | Read, Write                     | Import mapped properties; find a contact by email or phone when logging a CSV call; create the contact when there is no match; write phone numbers back |
| Tasks                     | Read, Write                     | Import open tasks for "All due tasks"; mark a contact's open call tasks completed after a call                                                          |
| Calls (engagements)       | Write                           | Create one call engagement per logged call and associate it with the contact                                                                            |
| Owners                    | Read                            | Attribute engagements and imports to the right HubSpot user                                                                                             |
| Companies                 | Read (write optional)           | Company fields on the contact card, company custom properties, account lists in sourcing                                                                |
| Deals                     | Read (optional), write optional | Deal fields on the contact card, deal custom properties, deal-based lists in sourcing                                                                   |
| Sequences                 | Read, enrollment write optional | List sequences and enroll a contact from the dialer                                                                                                     |
| Emails (sales email read) | Read                            | Show logged email history on the contact card                                                                                                           |
| Property schemas          | Read                            | Discover contact, company, and deal properties for mapping                                                                                              |

The dialer integration never deletes anything in HubSpot and never archives a contact.

## How task completion works

When a call is logged on a contact, Salesfinity reads the engagements associated with that
contact and finds the tasks that are **call type** and still **not started**. Each one is set to
completed, keeps its original owner, and gets the body "Call was made." Tasks of other types
(email, to-do) and tasks already in progress are left alone.

This is why a task imported through "All due tasks" disappears from the rep's HubSpot queue after
the call without anyone touching HubSpot.

## Owner attribution

HubSpot separates the **user** who logs in from the **owner** record used on CRM objects. When a
rep connects, Salesfinity resolves their owner ID from the owner list and stores both. Every
engagement is created with that owner, and "All due tasks" filters on it. If a rep's calls show
up unowned in HubSpot, the rep has no owner record in the portal; create one for them in HubSpot
and reconnect.

## What Salesfinity imports

You can import from HubSpot in two ways.

* **A list.** Pick any HubSpot list, active or static. Salesfinity reads its members and their
  mapped properties into a Salesfinity contact list named after the HubSpot list.
* **All due tasks.** Import with no list selected and Salesfinity pulls the open tasks assigned to
  your configured call task source user whose due date has passed or is today, along with the
  contact each task is associated with. The list is named "All due tasks". Tasks that are already
  completed are skipped.

Each imported contact keeps its HubSpot contact ID, which is how the call is routed back to the
right record later. Contact fields are filled from your contact field mapping; unmapped
properties are not imported.

## What Salesfinity writes back

When you log a call on a HubSpot-sourced contact, Salesfinity creates a **call engagement** in
HubSpot and associates it with the contact. The engagement carries:

| HubSpot property    | Value                                                               |
| ------------------- | ------------------------------------------------------------------- |
| Title               | "Salesfinity Call: First Last"                                      |
| Body                | Your call notes                                                     |
| From and to numbers | The number you dialed from and the number you reached               |
| Recording URL       | A link to the call recording, when the call was recorded            |
| Status              | Completed, busy, no answer, or failed, derived from the disposition |
| Direction           | Outbound                                                            |
| Duration            | Talk time in milliseconds                                           |
| Disposition         | The HubSpot call outcome mapped from your Salesfinity disposition   |
| Owner               | The HubSpot user connected to Salesfinity                           |

After creating the engagement, Salesfinity marks the contact's open **tasks** as completed, so a
task you imported disappears from your HubSpot queue once you have called it.

Two more write paths run outside of call logging:

* **Contacts you dial from a CSV or custom list** are logged to HubSpot too, if HubSpot is
  connected. Salesfinity searches HubSpot for the person by email or phone number. If nobody
  matches, it creates the contact first and then logs the call.
* **Phone numbers** found by enrichment or edited in the dialer can be pushed back onto the HubSpot
  contact.

The call log in Salesfinity keeps a link to the created engagement so you can open it in HubSpot
from call history.

## Sequences

From the dialer you can **enroll** a contact in a HubSpot sequence. Enrollment needs the optional
sequence enrollment permission and a sender email, which is the connected HubSpot user's inbox.

HubSpot has no public API to unenroll a contact, so **remove** and **move between sequences** are
not available for HubSpot. Do those in HubSpot itself.

## Configuration

Open the integration after connecting to reach these settings.

<AccordionGroup>
  <Accordion title="Call task source">
    The HubSpot user whose call tasks are imported by "All due tasks". Defaults to the user who
    connected. Change it if a manager connects on behalf of a rep, or if tasks are assigned to a
    shared owner.
  </Accordion>

  <Accordion title="Disposition mapping">
    Maps each Salesfinity disposition to a HubSpot call outcome. Salesfinity's built-in
    dispositions fall into answered, no answer, left voicemail, gatekeeper, bad number, and
    cancelled. Every one must be mapped; when HubSpot has fewer outcomes than Salesfinity, several
    Salesfinity dispositions map to the same outcome. Use the automatic suggestion to fill the
    whole table, then adjust.
  </Accordion>

  <Accordion title="Contact field mapping">
    Which HubSpot contact property fills each Salesfinity field. A field can pull from more than
    one property, which is useful when a phone number may live in either "Phone" or "Mobile".
  </Accordion>

  <Accordion title="Custom fields">
    Additional contact, company, and deal properties to bring across. Supported property types are
    text, textarea, number, select, radio, checkbox, and phone number. These show as custom
    fields on the contact in the dialer and can be used in filters.
  </Accordion>

  <Accordion title="Share with team">
    Available to a team admin or owner on their own HubSpot connection. Shares the field and
    disposition mappings with every member, and lets members who have not connected HubSpot use
    this connection for import and call logging. See
    [sharing an integration](/integrations/overview#sharing-an-integration-with-the-team).
  </Accordion>
</AccordionGroup>

## Rate limits

HubSpot enforces a per-portal limit on search requests that is shared by every user and app in
the portal. Salesfinity throttles its own searches to stay under it, and retries automatically
when HubSpot asks it to wait. A very large import or a busy portal can therefore take longer than
usual without anything being wrong.

## Questions admins ask

<AccordionGroup>
  <Accordion title="Do we need a Sales Hub seat?">
    Reps need whatever HubSpot seat lets them create calls and tasks on contacts. Sequences,
    deals, and sales email history depend on Sales Hub features; without them the matching
    optional permissions are dropped and those features are simply absent in Salesfinity.
  </Accordion>

  <Accordion title="Why is the call logged as a call engagement and not a note?">
    A call engagement carries duration, outcome, direction, recording URL, and both phone numbers
    as structured properties, so HubSpot reports and workflows can use them. A note would lose
    all of that.
  </Accordion>

  <Accordion title="Can we control which HubSpot lists reps can import?">
    Reps see every list their HubSpot user can see. Restrict list visibility in HubSpot and the
    picker follows.
  </Accordion>

  <Accordion title="Does HubSpot's rate limit affect us?">
    HubSpot limits search calls per second per portal, shared by every app and user in the
    portal. Salesfinity throttles its own searches and retries when HubSpot asks it to wait, so
    large imports can take longer at busy times but do not fail.
  </Accordion>

  <Accordion title="Where are our tokens stored?">
    Salesfinity stores the OAuth access and refresh token for each connected user in its
    credential store; they are never shown in the UI or returned by the API. Disconnecting
    deletes them; uninstalling the app in HubSpot invalidates them.
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={2}>
  <Card title="How data moves" icon="arrows-rotate" href="/integrations/data-flow">
    Where a call goes after you log it, and what each sync state means.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/integrations/troubleshooting">
    Missing permissions, empty imports, and failed syncs.
  </Card>
</CardGroup>
