> For the complete documentation index, see [llms.txt](https://docs.samita.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.samita.io/sa-request-a-quote/integrations/hubspot.md).

# HubSpot

Create HubSpot contacts from quote requests so your sales team can work them as leads.

Connecting HubSpot means every quote request also becomes a contact you can follow up with, without anyone copying details by hand.

{% hint style="warning" %}
**HubSpot needs two values, not one:** a **Portal ID** and a **HubSpot access token**. It also needs three scopes ticked on the private app that issues the token. Miss either and the connection looks configured but sends nothing — Step 1 covers both.
{% endhint %}

{% hint style="info" %}
HubSpot is listed under the **PRO** plan. Klaviyo, Mailchimp and Shopify are the integrations listed under **FREE**. See [Plan comparison](/sa-request-a-quote/reference/plan-comparison.md).

If your plan does not include it, the **Third-party integration** list shows a disabled **Upgrade** button instead of the settings icon, with a padlock beside it. Selecting the padlock explains which plan the feature needs and links to **Pricing plans** in the app. That is your plan, not a fault.
{% endhint %}

## Step 1 — Get your Portal ID and access token from HubSpot

HubSpot no longer issues classic API keys. You create a private app inside your HubSpot account instead, and it gives you an access token. The **Portal ID** is separate — it identifies your HubSpot account itself.

{% stepper %}
{% step %}

### Find your Portal ID

Your Portal ID (also called the account ID or hub ID) identifies which HubSpot account the app writes to. HubSpot's own guide: [Check your current account](https://knowledge.hubspot.com/account/manage-multiple-hubspot-accounts#check-your-current-account).

The same link is repeated in the app as the **Find your Portal ID** help text under the field.
{% endstep %}

{% step %}

### Create a private app with the right scopes

Create the private app inside your HubSpot account. HubSpot's own guide walks through it: [Private apps](https://developers.hubspot.com/docs/api/private-apps).

Before you finish creating it, tick at least these three scopes:

* `forms`
* `crm.objects.contacts.write`
* `crm.schemas.contacts.write`

The app lists the same three under the token field, so you can check them against your private app later.

<figure><img src="https://2993310686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIvNtDAMNVIdy5nl9kiOU%2Fuploads%2Fgit-blob-6bd2ec5be6e93e1c0ffd30169e85600adb167d36%2Fneeds-screenshot-b7b1e266.png?alt=media" alt="The HubSpot private-app scopes screen with forms, crm.objects.contacts.write and crm.schemas.contacts.write ticked"><figcaption><p><strong>Screenshot needed.</strong> The HubSpot private-app scopes screen with forms, crm.objects.contacts.write and crm.schemas.contacts.write ticked.</p></figcaption></figure>
{% endstep %}

{% step %}

### Copy its access token

HubSpot shows the token once the private app is created. Copy it and keep it somewhere safe while you finish the setup.
{% endstep %}
{% endstepper %}

{% hint style="danger" %}
Scopes are the quiet failure. A token created without `forms`, `crm.objects.contacts.write` and `crm.schemas.contacts.write` is a perfectly valid token: **Enable** stays on, the app saves without complaint, and HubSpot refuses every contact it is sent. If contacts never arrive, check the scopes before you check anything else.
{% endhint %}

{% hint style="warning" %}
The app connects quote requests to a HubSpot **form**, not to a contact list. Create that form in HubSpot before you start — the dropdown can only offer forms that already exist.
{% endhint %}

## Step 2 — Connect it in the app

{% stepper %}
{% step %}

### Open the Integrations tab

In your Shopify admin, open **SA Request a Quote**, select **Advanced settings**, then open the **Integrations** tab.
{% endstep %}

{% step %}

### Select HubSpot

Under **Third-party integration**, select **HubSpot** — described in the app as "Seamlessly connect your submissions to your data".

The HubSpot page opens, with a **Third-party** breadcrumb to take you back to the list.

<figure><img src="https://2993310686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIvNtDAMNVIdy5nl9kiOU%2Fuploads%2Fgit-blob-f801fbaf08cc0d26bc548b53463c2da29e2d44d5%2FScreenshot%202026-09-30%20at%2000-24-33.png?alt=media" alt="The Integrations tab of Advanced settings with HubSpot outlined in red"><figcaption><p>Pick the service, then open its settings.</p></figcaption></figure>
{% endstep %}

{% step %}

### Turn on Enable

Switch on **Enable**, the first control on the page. Nothing is sent to HubSpot until you do.
{% endstep %}

{% step %}

### Paste both credentials

The two fields sit directly under **Enable**, in this order:

1. **Portal ID** — paste the account ID from Step 1. Its help text is **Find your Portal ID**.
2. **HubSpot access token** — paste the private app token from Step 1. Its help text is **Find or generate your access token**, and under it the app prints the scopes the token needs: **forms**, **crm.objects.contacts.write** and **crm.schemas.contacts.write**.

The field is called **HubSpot access token**, not "API key" — the token is what a private app issues.

Fill in both. The app only fetches your HubSpot forms once it has the pair, so a page with just the token stays stuck with nothing to choose from. If the dropdown does not appear after you type, select **Merge fields** beside the token field to make the app fetch again.

<figure><img src="https://2993310686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIvNtDAMNVIdy5nl9kiOU%2Fuploads%2Fgit-blob-a476e43337b815ea270be30cc141215e2330d674%2FScreenshot%202026-09-30%20at%2000-25-12.png?alt=media" alt="The HubSpot settings with both credential fields outlined in red"><figcaption><p>HubSpot needs two values, and they are not interchangeable.</p></figcaption></figure>
{% endstep %}

{% step %}

### Choose the HubSpot form

Open **Select a HubSpot form to integrate** and pick the form quote requests should feed. Until you choose, it reads "Please select a HubSpot form".

If the app says "It looks like you don't have any HubSpot forms", either the form does not exist yet, one of the two credentials is wrong, or the token is missing the `forms` scope. Fix that before going on — the field mapping stays hidden until a form is selected.
{% endstep %}

{% step %}

### Match your form fields

The mapping rows appear only once the **Portal ID**, the **HubSpot access token** and the form are all set. The rows come from the HubSpot form you just picked, so they are that form's own fields rather than a fixed list — pick the form first and the rows follow.

Read each row left to right:

* **On the left** — the HubSpot field, greyed out because you cannot rename it.
* **In the middle** — how the value is supplied: **has no value**, **is dynamic value by** (take it from the quote form) or **is fixed value by** (send the same text on every quote).
* **On the right** — the quote form field you are sending, or the fixed text you typed.

So you pick the quote form value on the right to fill the HubSpot field named on the left, never the other way round.

The email row is the one you cannot skip: without it there is no contact to create. If your form collects **Company information**, match the company name too, so sales can see who is asking. If a field is marked required on the HubSpot form and you leave it unmatched, the app flags the row and refuses to save.

<figure><img src="https://2993310686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIvNtDAMNVIdy5nl9kiOU%2Fuploads%2Fgit-blob-6b724ffeb06bc249b72b3d8d51e98e9d78833946%2FScreenshot%202026-10-01%20at%2000-20-05.png?alt=media" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Save

Select **Save**. Nothing is stored until you do, and an unsaved page sends nothing.

While you have unsaved changes the app shows a save bar with **Save** and **Discard changes** — **Discard changes** throws away everything you have typed on the page. A **Changes saved** message confirms the save.
{% endstep %}
{% endstepper %}

{% hint style="info" %}
[Mailchimp](/sa-request-a-quote/integrations/mailchimp.md) shows the same field-mapping table in close-up. Follow it for the mapping only — Mailchimp needs a single API key and picks a list, while HubSpot needs the **Portal ID** as well and picks a form.
{% endhint %}

## Step 3 — Test it

Submit a real quote request from your storefront, then check HubSpot for the new contact. Confirm the fields you matched actually arrived — not just the email address.

{% hint style="warning" %}
Do not skip the test. A connection can look active and still deliver nothing useful if the fields are matched wrongly, and you will not notice until you try to use the data.
{% endhint %}

## Is this the right tool?

The right choice if quotes are handled by a sales team rather than by marketing.

If you do not already use HubSpot, do not adopt it just for this — [Zapier](/sa-request-a-quote/integrations/zapier.md) connects quote data to almost anything, and [Create a Shopify customer after submit](/sa-request-a-quote/integrations/create-shopify-customer.md) keeps everything inside Shopify on the **FREE** plan.

## If contacts are not arriving

* **Check the scopes on your private app.** `forms`, `crm.objects.contacts.write` and `crm.schemas.contacts.write` all have to be granted. A token without them saves cleanly and delivers nothing. This is the most common cause.
* **Check Enable is still on.** Nothing is sent while it is off.
* **Check the Portal ID as well as the token.** A correct token with a missing or mistyped **Portal ID** leaves the connection half-made — the app cannot load your forms, so nothing is sent.
* **Check the token is still valid.** Rotating a token in HubSpot breaks the connection until you paste the new one in. Saved tokens are shown masked, so paste a fresh one if in doubt.
* **Check you picked the right form.** Contacts may be arriving somewhere you are not looking.
* **Check the email row is matched.** Without it there is no contact to create.

Still stuck? Email <support@samita.io>.

## Next steps

* [Integrations overview](/sa-request-a-quote/integrations/overview.md) — the other tools you can connect
* [Build a B2B form](/sa-request-a-quote/quote-form/build-b2b-form.md) — collect company details worth sending to a CRM
* [Plan comparison](/sa-request-a-quote/reference/plan-comparison.md) — what the PRO plan includes


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.samita.io/sa-request-a-quote/integrations/hubspot.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
