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

# HubSpot

## Overview

The HubSpot integration connects Younium subscription management to HubSpot CRM at the **legal entity** level. Sales and customer success teams work from HubSpot companies and deals while Younium remains the system of record for subscriptions, billing, and revenue metrics.

The integration supports account import from HubSpot companies, deal-driven order and quote creation, embedded CRM cards on HubSpot records, and export of subscription orders and charges to HubSpot custom objects. OAuth authorises each legal entity to a HubSpot portal; a **private access token** is additionally required for custom object export and schema operations.

***

## Authentication

You connect Younium to HubSpot by authorising it from HubSpot itself. Saving the integration settings produces an authorisation link; following it takes you to HubSpot, where you sign in and approve the access Younium asks for — the companies, deals, contacts, line items, custom objects, owners and timeline entries it reads and writes. HubSpot returns you to Younium and the connection is live.

The connection is made per legal entity, and each legal entity connects to one HubSpot account. Younium keeps the connection alive on its own, renewing its access before each import and export, so nobody has to reauthorise it on a schedule. The integration settings show whether the connection is currently valid.

Exporting orders to HubSpot custom objects needs one thing the authorisation does not provide: a private app token, which you generate in HubSpot and paste into the integration settings. Without it, order export stops and tells you the token is missing. Everything else — companies, contacts, deals and line items — works on the authorised connection alone.

***

## Activation

Approving the connection in HubSpot is what activates the integration. From that point Younium can read and write the HubSpot records covered by the integration.

Activation creates nothing on either side. The HubSpot properties and custom object schemas the integration needs are created when they are first needed — during property sync, a deal workflow, or an export — not at the moment you connect. Younium's own integration custom fields on **Account**, **Order** and **Quote** are created the same way (see **Custom fields**).

***

## Deactivation

Disconnecting clears everything Younium held for the legal entity: the authorisation, the private app token, and the record of when each sync last ran. Imports and exports stop.

Nothing is removed in HubSpot. The properties, custom objects and records the integration created stay exactly as they are — they are simply no longer maintained from Younium.

***

## Save and Setup

Saving settings updates connection and synchronisation options without re-running OAuth:

| Setting                          | Description                                                                                                              |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `PrivateAccessToken`             | PAT for custom object APIs                                                                                               |
| `AccountCustomFieldsMapping`     | Comma-separated HubSpot company property names mapped to Younium account custom fields (legacy; spaces stripped on save) |
| `DealCustomFieldsMapping`        | Comma-separated deal properties for quote mapping (legacy; spaces stripped on save)                                      |
| `SyncToInvoiceAccount`           | When true, export and account resolution use **invoice account** instead of order account                                |
| `InvoiceAccountAssociationLabel` | HubSpot association label used to pick invoice company when multiple companies are on a deal                             |
| `UpdateAddresses`                | When true, account import updates invoice address from HubSpot when country is present                                   |
| `OnlySynchNewAccounts`           | When true, batch import only creates new accounts; existing linked accounts are not updated                              |
| `HubspotDealAmountValue`         | Controls which deal amount field drives quote/deal amount updates                                                        |
| `HideUpdateDealAmount`           | Hides deal amount update behaviour in UI when configured                                                                 |
| `LastAccountSynchronize`         | Writable from settings; used as incremental filter for company search                                                    |

On first save, if `HubspotAccessAuthorizationUrl` is null, Younium builds the OAuth authorisation URL (client ID, redirect URI, scopes, state).

***

## Custom Fields Created by the Integration

Integration-owned custom fields use the naming pattern `integrationHubspot` + description (spaces and punctuation removed). Do not rename or delete these fields.

| Entity  | Field description (source) | Resolved custom field name        | Purpose                                                          |
| ------- | -------------------------- | --------------------------------- | ---------------------------------------------------------------- |
| Account | Company Id                 | `integrationHubspotCompanyId`     | HubSpot company ID                                               |
| Order   | Hubspot Deal Id            | `integrationHubspotHubspotDealId` | HubSpot deal ID                                                  |
| Quote   | Hubspot Deal Id            | `integrationHubspotHubspotDealId` | HubSpot deal ID                                                  |
| Quote   | Hubspot Deal               | `integrationHubspotHubspotDeal`   | Link field to deal URL (created when quote is created from deal) |

***

## Eligibility and Scope

### Legal entity

Connection, settings, CRM cards, and synchronisation run per **legal entity**. Company and deal properties include `younium_legal_entity` (format: `{legalEntityId},{tenantId}`). Import batch processing skips companies whose `younium_legal_entity` does not match the integration’s legal entity. Post-import, companies with empty legal entity receive the current legal entity value written back to HubSpot.

### Account import (batch)

HubSpot company search uses **OR** filter groups:

1. `lifecyclestage` = `customer` **and** (optional) `hs_lastmodifieddate` ≥ last sync
2. `younium_account_id` **has property** **and** (optional) same modified-date filter

After fetch, companies are filtered to those with valid `younium_legal_entity` (matches scope or null). Within each batch, companies with a non-matching legal entity ID are skipped.

`CreateAccount` only materialises or updates Younium data when HubSpot `lifecyclestage` is `customer`, the call is deal-driven (`dealAccount`), or the existing Younium record is a **Prospect**. Non-customer companies without those conditions are ignored.

When `OnlySynchNewAccounts` is true, existing accounts matched by HubSpot company ID are returned without field updates (except deal-driven import).

### Account resolution after a HubSpot company merge

Before creating a new account, Younium first looks for an existing account whose **Company Id** custom field already holds the HubSpot company's id. When no account holds it, Younium checks two further signals on the HubSpot company before falling back to creating a new account — this applies both to the batch import job and to account resolution for deal-driven order, change order, and quote creation:

1. **Merged companies (`hs_merged_object_ids`):** if HubSpot recorded the company as the survivor of a merge, Younium looks for an account whose **Company Id** matches any of the ids the company was merged from.
   * Exactly one match — that account is used, and its **Company Id** is overwritten with the current HubSpot company id, so future syncs match directly.
   * More than one match — the accounts are duplicates left over from separate merges and Younium cannot pick one automatically (see below).
2. **`younium_account_id` property:** if the company carries this property and it resolves to an existing account, that account is used and its **Company Id** is stamped with the current HubSpot company id — unless the account already has a *different* Company Id set, meaning it is already linked to another HubSpot company (see below).

If neither signal resolves to a single account, Younium creates a new account as it would if the company had never been seen before.

> **Note on the correction being permanent:** the **Company Id** update from either signal is saved immediately, so it is not lost on a later run where `OnlySynchNewAccounts` is enabled (which otherwise leaves existing accounts untouched).

The batch import job and deal-driven creation handle an unresolved case (multiple merge matches, or a `younium_account_id` account already linked elsewhere) differently:

| Context                                                                          | Behaviour when unresolved                                                                                                                                                                                                               |
| -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Batch import job                                                                 | The company is skipped for that run — no account is created or updated. The integration log records each candidate account so the duplicates can be reconciled and merged, or the **Company Id** fields corrected, before the next run. |
| Deal-driven creation (create order, create change order, create quote from deal) | The flow must still return an account, so Younium creates a new account rather than blocking the deal. The integration log records the same mismatch, so the resulting duplicate can be identified and merged afterwards.               |

### Subscription export

| Rule               | Detail                                                                              |
| ------------------ | ----------------------------------------------------------------------------------- |
| Account link       | Account must have non-empty `integrationHubspotCompanyId`                           |
| Order type         | `OrderType.Subscription`                                                            |
| Versions           | Last-version order and last-version order product charge                            |
| Order status       | Active, Cancelled, or Deleted                                                       |
| Account resolution | `SyncToInvoiceAccount`: match on `InvoiceAccountId`; otherwise `AccountId`          |
| Incremental        | If `LastExportOrders` set: charge or order `ModifiedWithDependencies` must be newer |
| Deleted orders     | Skipped for create if no existing HubSpot order; updates still apply when ID exists |
| Charge number      | Charges with null `ChargeNumber` are skipped                                        |

### Jobs

| Job                      | Minimum repeat interval        |
| ------------------------ | ------------------------------ |
| Import HubSpot accounts  | None defined in job definition |
| Export Orders to Hubspot | None defined in job definition |

### Deal company resolution

Creating an order from a deal, creating a change order from a deal, creating a quote from a deal, and the deal's quote/change-order card all resolve the deal's associated company the same way. A deal with a single associated company uses that company directly. A deal with several associated companies resolves the account through HubSpot's association metadata: the company carrying the **Primary** association, or the one carrying the `InvoiceAccountAssociationLabel` association label when set, determines the linked Younium account (and, separately, the invoice account). If that lookup does not return a company, Younium falls back to the first company HubSpot lists for the deal.

The quote/change-order card requires at least one company associated with the deal, and a Younium account linked to the resolved company. If either is missing, the card shows an error instead of rendering with no account.

Once a company is resolved, the Younium account for it is found using the same matching described in Account resolution after a HubSpot company merge, including the merge and `younium_account_id` fallbacks.

***

## Custom Field Mapping

Configurable field-to-field mappings (**integration custom mappings**) pair a HubSpot property with a Younium field, and apply to the account, quote, order, and order product charge entities. The deprecated `AccountCustomFieldsMapping` and `DealCustomFieldsMapping` settings work the same way for account and quote fields.

A mapping can target most Younium field types, including whole numbers such as **Notice Period**, **Term**, and **Renewal Term**. Calculated fields, fields that reference another record (single or multiple), image fields, ID fields, and currency-amount fields cannot be used as mapping targets.

> **Note on custom field names:** Younium matches an incoming custom field to its configured field without regard to letter case, and always stores the value under the field's exact configured name — so a value mapped to `region` and one mapped to `Region` update the same Younium field rather than creating two.

> **Note on blank source values:** An integration custom mapping only ever sets a Younium field when the HubSpot source property has a value. If the property is empty, blank, or missing, the mapping is skipped and the existing Younium value is left as it is — clearing a mapped HubSpot property never clears the corresponding Younium field or custom field.

***

## Field Mapping — Account Import (HubSpot → Younium)

Standard company properties map as follows. Configurable **integration custom mappings** (entity `account`) and deprecated `AccountCustomFieldsMapping` can add or override fields.

| Younium Field                         | HubSpot company property                                        |
| ------------------------------------- | --------------------------------------------------------------- |
| `Account.Name`                        | `name`                                                          |
| `Account.Domain`                      | `domain`                                                        |
| `Account.Description`                 | `description`                                                   |
| `Account.InvoiceEmailAddress`         | `younium_invoice_email`                                         |
| `Account.OrganizationNumber`          | `younium_org_nr`                                                |
| `Account.TaxRegistrationNumber`       | `younium_tax_reg_nr`                                            |
| Custom: `integrationHubspotCompanyId` | Company `id` (HubSpot object ID)                                |
| `Address.Name` (invoice)              | `younium_invoice_address_name` (or default “Invoice address”)   |
| `Address.Street`                      | `address`                                                       |
| `Address.Street2`                     | `address2`                                                      |
| `Address.City`                        | `city`                                                          |
| `Address.Zip`                         | `zip`                                                           |
| `Address.State`                       | `state` (normalised for US/Canada)                              |
| `Address.CountryId`                   | `country` (matched to Younium country; US/CA aliases supported) |

Invoice address create/update runs only when `country` is non-empty and (`UpdateAddresses` is true or no invoice address exists yet).

A blank HubSpot value for any of the standard company properties above (`name`, `domain`, `description`, `younium_org_nr`, `younium_tax_reg_nr`, `younium_invoice_email`) is skipped: the existing Younium field keeps its current value rather than being cleared.

List-type custom fields from deprecated mapping resolve HubSpot values to list **Key** or **Value** before storing.

Owner sub-properties in custom mappings trigger a fetch of HubSpot owners for the mapped owner ID.

***

## Field Mapping — Account Write-Back (Younium → HubSpot)

After successful import/update, Younium batch-updates HubSpot companies (max 99 per async batch) for properties that are empty in HubSpot:

| HubSpot property         | Younium source                             |
| ------------------------ | ------------------------------------------ |
| `younium_account_id`     | `Account.Id`                               |
| `younium_account_number` | `Account.AccountNumber`                    |
| `younium_legal_entity`   | `{LegalEntityId},{TenantId}` from settings |
| `younium_org_nr`         | `Account.OrganizationNumber`               |
| `younium_tax_reg_nr`     | `Account.TaxRegistrationNumber`            |
| `younium_invoice_email`  | `Account.InvoiceEmailAddress`              |

***

## Field Mapping — Subscription Export (Younium → HubSpot Custom Objects)

Custom object schemas: **`younium_order`** (order), **`younium_order_charge`** (charge). Younium creates schemas and properties if missing and associates orders to companies (`0-2`) and charges to parent orders.

Dates sent to HubSpot use Unix milliseconds at **start of day** (UTC).

### Younium Order (`younium_order`)

| Younium Field                             | HubSpot property                             |
| ----------------------------------------- | -------------------------------------------- |
| `Order.OrderNumber`                       | `order_number`                               |
| `Order.Description`                       | `order_description`                          |
| `Order.EffectiveStartDate`                | `order_effective_start_date`                 |
| `Order.EffectiveEndDate`                  | `order_effective_end_date` (if set)          |
| `Order.OrderDate`                         | `order_date` (if set)                        |
| `Order.IsAutoRenewed`                     | `order_is_auto_renewed`                      |
| `Order.CMRR.Amount`                       | `order_cmrr`                                 |
| `Order.ACV.Amount`                        | `order_acv`                                  |
| `Order.TCV.Amount`                        | `order_tcv`                                  |
| `Order.RenewalTerm`                       | `order_renewal_term`                         |
| `Order.Term`                              | `order_initial_term`                         |
| `Order.Currency.Code`                     | `order_currency`                             |
| `Order.Version`                           | `order_version`                              |
| `Order.Status`                            | `order_status`                               |
| `Order.TermType`                          | `order_term_type`                            |
| `Order.NoticePeriod`                      | `order_notice_period`                        |
| `Order.NoticePeriodDate`                  | `order_notice_period_date` (if set)          |
| `PaymentTerm.Name`                        | `order_payment_term`                         |
| `Order.Id`                                | `order_id`                                   |
| Custom: `integrationHubspotHubspotDealId` | `hubspot_deal_id`                            |
| Association                               | HubSpot company ID from account custom field |

Create vs update: match existing HubSpot order by `order_number`.

### Younium Order Charge (`younium_order_charge`)

| Younium Field                           | HubSpot property                       |
| --------------------------------------- | -------------------------------------- |
| `OrderProductCharge.ChargeNumber`       | `charge_number`                        |
| `OrderProductCharge.Name`               | `charge_description`                   |
| `OrderProductCharge.CMRR.Amount`        | `charge_cmrr`                          |
| `OrderProductCharge.TCV.Amount`         | `charge_tcv`                           |
| `OrderProductCharge.ACV.Amount`         | `charge_acv`                           |
| `OrderProductCharge.ACV.CurrencyCode`   | `charge_currency`                      |
| `OrderProductCharge.Quantity`           | `charge_quantity`                      |
| `OrderProduct.Order.ProductNumber`      | `product_number`                       |
| `OrderProductCharge.ChargeType`         | `charge_type`                          |
| `OrderProductCharge.Version`            | `charge_version`                       |
| `OrderProductCharge.Model`              | `charge_model`                         |
| `OrderProductCharge.PricePeriod`        | `charge_price_period`                  |
| `OrderProductCharge.BillingPeriod`      | `charge_billing_period`                |
| `UnitOfMeasure.DisplayName`             | `charge_uom`                           |
| Charge plan name                        | `charge_chargeplan_name`               |
| `OrderProductCharge.EndOn`              | `charge_end_on`                        |
| `OrderProductCharge.StartOn`            | `charge_start_on`                      |
| `OrderProduct.Name`                     | `charge_product_name`                  |
| `OrderProductCharge.Remarks`            | `charge_remarks`                       |
| `OrderProductCharge.ExternalCRMId`      | `external_crm_id`                      |
| Active detail list price                | `list_price_amount`                    |
| Active detail price                     | `price_amount`                         |
| Active detail line discount             | `discount_amount`                      |
| Active detail price (PPu)               | `charge_ppu`                           |
| `OrderProductCharge.EffectiveEndDate`   | `charge_effective_end_date` (if set)   |
| `OrderProductCharge.EffectiveStartDate` | `charge_effective_start_date` (if set) |

Batch create/update uses chunks of 50 on `crm/v3/objects/{chargeSchemeId}/batch/create` or `update`.

***

## Field Mapping — Create Order from Deal (HubSpot → Younium)

| Younium Field              | HubSpot deal / line item source                                       |
| -------------------------- | --------------------------------------------------------------------- |
| `Order.Description`        | `dealname`                                                            |
| `Order.OrderDate`          | `closedate`                                                           |
| `Order.Remarks`            | `younium_remarks`                                                     |
| `Order.IsAutoRenewed`      | `younium_auto_renewal`                                                |
| `Order.RenewalTerm`        | `younium_renewal_term`                                                |
| `Order.Term`               | `younium_initial_term`                                                |
| `Order.EffectiveStartDate` | `younium_order_effective_start_date` (required)                       |
| `Order.EffectiveEndDate`   | Start date + term months − 1 day                                      |
| `Order.Status`             | `younium_order_status` (parsed to order status)                       |
| Currency                   | `hs_line_item_currency_code` (all line items must share one currency) |
| Custom: deal ID            | Deal `associatedObjectId` → `integrationHubspotHubspotDealId`         |
| Account / invoice account  | Primary and labelled invoice companies on deal                        |
| Charges                    | Built from deal line items (see line item table)                      |

### Deal line item → order product charge

| Younium charge field          | HubSpot line item property                                                                    |
| ----------------------------- | --------------------------------------------------------------------------------------------- |
| Product name                  | `name`                                                                                        |
| Line item ID                  | `hs_object_id` (stored on charge)                                                             |
| HubSpot product ID            | `hs_product_id`                                                                               |
| Charge number                 | `younium_order_charge`                                                                        |
| Charge type                   | `younium_charge_type`                                                                         |
| Charge model                  | `younium_charge_model`                                                                        |
| List price / price / quantity | `hs_pre_discount_amount`, `amount`, `quantity`                                                |
| Currency                      | `hs_line_item_currency_code`                                                                  |
| Start/end options             | `younium_start_on`, `younium_end_on`                                                          |
| Effective dates               | `younium_charge_effective_start_date`, `younium_charge_effective_end_date` (sv-SE date parse) |
| Billing                       | `younium_billing_period`, `recurringbillingfrequency`                                         |
| Discount                      | `discount`                                                                                    |
| Line status                   | `younium_line_item_status`                                                                    |

HubSpot pricing models (flat, volume, stairstep, graduated) map to Younium charge models with tier validation.

> **Note on multiple companies:** See Deal company resolution under Eligibility and Scope — the same resolution rule applies here.

***

## Field Mapping — Create Change Order from Deal

Uses the same deal property sources as create order, plus:

| Younium Field            | HubSpot deal property                            |
| ------------------------ | ------------------------------------------------ |
| Change effective date    | `younium_order_effective_change_date` (required) |
| Existing order           | `younium_order_number`                           |
| Deal already synced flag | `younium_deal_synched`                           |

Line items are matched to existing charges by `younium_order_charge` on last-version charges.

> **Note on blank deal properties:** Unlike creating a new order, a change order updates an order that already has values. `dealname`, `younium_remarks`, and `closedate` only overwrite the order's description, remarks, and order date when the deal property is not blank; a property that has been cleared in HubSpot leaves the existing order value untouched. The same applies to `younium_renewal_term`, `younium_initial_term`, and `younium_auto_renewal` — a cleared HubSpot number or checkbox property is treated as no change rather than failing the change order.

Changing `younium_initial_term` also moves the order's effective end date to match the new term, the same way changing the term does on a change order created directly in Younium — so the term and the end date never drift out of sync. Tenants with **Enable specific effective end date** switched on are the exception: HubSpot has no property for a specific end date, so the term cannot be changed this way for those tenants and must be changed directly in Younium instead.

***

## Field Mapping — CRM Cards (Younium → HubSpot display)

### Company card (linked account)

| Card property           | Younium source                             |
| ----------------------- | ------------------------------------------ |
| Account name (link)     | Account name + deep link                   |
| Account number          | `Account.AccountNumber`                    |
| TCV / ACV / CMRR / EMRR | Aggregated from active last-version orders |
| Account balance         | Posted/settled invoice totals              |
| Last charged            | Latest posted/settled invoice date         |

The card matches the HubSpot company to a Younium account by its stamped **Company Id** custom field first; if no account carries that ID, it falls back to matching on the account referenced by `younium_account_id`. If more than one account is stamped with the same company ID, the oldest one is shown.

### Deal — order card

Actions depend on `changeType`, `orderNumber`, and `younium_deal_synched`: create order, create change order, or open iframe for existing order.

### Deal — quote card

Shows quote metrics when a quote exists for the deal; otherwise empty placeholders with create-quote action.

***

## Account Import (batch job)

**Trigger:** scheduled job **Import HubSpot accounts** (refreshes token, then `ImportAccounts`).

**Flow:**

1. Search companies (filters above), paginate 100 per page (max 1000 pages), 300 ms delay between pages.
2. Process in batches of 500 companies.
3. Match existing account by `integrationHubspotCompanyId`; if none matches, resolve the account across a HubSpot merge or `younium_account_id` before falling back to a new account (see Account resolution after a HubSpot company merge under Eligibility and Scope).
4. Create or update via `CreateAccount`; apply integration custom mappings.
5. Write back empty Younium properties to HubSpot (batch async update, chunks of 99).
6. Set `LastAccountSynchronize` to UTC now; log new/updated counts.

Failures on individual companies are logged; batch failures log and continue with next batch.

***

## Export Subscriptions (batch job)

**Trigger:** scheduled job **Export Orders to Hubspot**.

**Flow:**

1. Refresh OAuth token; require PAT.
2. Resolve or create `younium_order` and `younium_order_charge` schemas and associations.
3. Load existing HubSpot orders (by `order_number`) and charges (by `charge_number`).
4. Select changed last-version subscription charges for linked accounts.
5. Process in batches of 100 subscriptions; export order then charges.
6. Set `LastExportOrders`, `LastExportOrdersCount`; log created/updated counts.

***

## Deal and Quote Actions (on demand)

| Action                         | Trigger                       | Outcome                                |
| ------------------------------ | ----------------------------- | -------------------------------------- |
| Create order from deal         | CRM card / API from deal      | New subscription order with line items |
| Create change order from deal  | CRM card when change type set | Change order on existing order number  |
| Create quote from deal         | Quote card                    | New quote linked to deal               |
| Update deal from quote         | Quote workflow                | Patches HubSpot deal amount/properties |
| Create/update/delete line item | Product selector              | HubSpot line items for deal charges    |
| Create new deal from Hubspot   | Change subscription request   | New HubSpot deal from order            |

***

## HubSpot Property Provisioning

On demand, Younium can create HubSpot properties for products, deals, companies, and line items (`CreateYouniumProductProperties`, `CreateYouniumDealProperties`, `CreateYouniumCompanyProperties`, `CreateYouniumLineItemProperties`, `SynchronizeCustomProperties`). Property names follow the `younium_*` convention used in mappings above.

`ClearLastDateSync` clears `LastExportOrders` when entity name `orders` is passed.


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.younium.com/crm-integrations/hubspot.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
