Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dualentry.com/llms.txt

Use this file to discover all available pages before exploring further.

HubSpot Integration: OAuth, settings, and revenue objects

Connect HubSpot to DualEntry so closed-won deals become draft revenue contracts (with line items as performance obligations), companies on those deals become customers, products become items, and - if you turn the option on - HubSpot invoices become sales invoices in DualEntry. The connector is HubSpot → DualEntry only in the current implementation: DualEntry does not write metrics, properties, or invoices back into HubSpot. DualEntry uses the official HubSpot OAuth 2.0 authorization-code flow, stores access and refresh tokens, refreshes tokens before they expire, and records your HubSpot portal id (remote_id) for deep links into HubSpot.

Prerequisites

Confirm the following before connecting:
  • Admin (or integration-eligible) access in DualEntry and permission to install or authorize apps in HubSpot.
  • A HubSpot OAuth app whose client id, client secret, redirect URI, and scopes match what your DualEntry environment expects. These values are supplied by deployment configuration, not entered per customer; your implementation team or DualEntry support provides them.
  • Your chart of accounts includes the GL accounts you will point at in integration settings (see Step 2).
  • Alignment with sales on which HubSpot deal stage counts as closed won. DualEntry’s company and deal sync use the internal stage id closedwon (HubSpot’s default closed-won identifier), not a display label.

Step 1: Connect with OAuth

DualEntry uses the standard OAuth 2.0 authorization-code flow against HubSpot’s auth and API endpoints.
  1. Navigate to Settings → Integrations → HubSpot.
  2. Choose Connect with HubSpot. DualEntry requests an authorization URL from GET /api/integrations/hubspot/auth/, which returns a HubSpot consent URL built from your deployment’s OAuth client and scope.
  3. Approve the app in HubSpot. HubSpot redirects to your configured redirect URI with an authorization code.
  4. DualEntry exchanges the code for tokens via POST /api/integrations/hubspot/oauth-callback/. On success it stores access_token, refresh_token, expires_in, scope, and token_created_at, sets the integration to connected, reads account details from HubSpot (portalId as remote_id, company currency when present), and authenticates the sync service.
If exchange or validation fails, the integration moves to error with a status message. Reconnect by repeating OAuth after fixing the HubSpot app or redirect mismatch. For outages on HubSpot’s side, see HubSpot status. For API changes that might affect imports, see the HubSpot developer changelog.

Step 2: Configure required integration settings

Before sync can treat the integration as set up, you must save settings that point to real DualEntry records. The following keys are required, and the integration will not run a clean sync until every one resolves to a valid account or company:
SettingPurpose
Accounts receivable accountAR side of revenue and invoicing workflows where applicable.
Deferred revenue accountUsed when building items and obligations (for example, service-type products use deferral on the item).
Expense accountDefault expense account on items created from HubSpot products and line items.
Income accountDefault income account on those items.
Clearing accountRequired to be set for a complete configuration.
Company (entity)The DualEntry company used as the accounting entity on contracts created from deals and on placeholder-backed invoice import.
Sync HubSpot invoicesBoolean toggle. Off by default; turn on only after reading Step 3.
Configure these under Settings → Integrations → HubSpot → Settings. Until every required field is valid, setup remains incomplete and you should treat automated sync as not production-ready.

Step 3: Decide whether to enable invoice import

HubSpot invoice import is off by default. When the Sync HubSpot invoices setting is off, the sync service skips the invoice pull entirely and the rest of the integration is unaffected. Leave invoice import off unless you have reviewed the customer-mapping behavior. Imported HubSpot invoices currently post against placeholder Unknown customer and Unknown company records, not the CRM company or contact on the invoice header - this means manual cleanup is required after every invoice sync. The full behavior is detailed under HubSpot invoices to sales invoices below. Mapping HubSpot invoice parties to accounting records is a known future improvement. When you do enable invoice import, save the toggle in HubSpot integration settings, then run sync. To turn it back off, clear the toggle and save; subsequent syncs will skip invoices going forward but already-imported invoices remain in DualEntry.

Step 4: Run sync

Trigger a sync from Settings → Integrations → HubSpot → Sync, or call POST /api/integrations/hubspot/{integration_id}/trigger-sync/, which enqueues a background task. The service authenticates (refreshing the access token when it is near expiry), then runs its sync plan in this fixed order:
  1. Invoices - only if invoice sync is enabled in Step 2 settings.
  2. Products → items - SKU, name, type, and the income, expense, and deferral accounts you configured in settings.
  3. Companies → customers - companies linked to deals in stage closedwon become company-type customers.
  4. Deals → draft contracts - deals in stage closedwon with closedate on or after the integration cutoff date become draft contracts with performance obligations built from deal line items.
Contacts are present in code but not part of the default sync plan. Unless your deployment has explicitly added them, only companies from closed-won deals - and individual customers created from those deal companies - drive customer master data, not a full HubSpot contact export.
Cutoff date. Deals and HubSpot invoices respect the integration’s cutoff, typically aligned with your DualEntry go-live date. Invoices are additionally filtered by hs_invoice_date greater than or equal to the cutoff when one is set. The deal cutoff filter does not apply to contacts or to the raw product listing in the same way; rely on operational monitoring if HubSpot product or contact volume is large.

What syncs into DualEntry

The connector imports four object types from HubSpot, each mapping to a specific DualEntry record. The sections below describe how each object is matched, populated, and updated on subsequent syncs.

Companies to customers

Only HubSpot companies associated with at least one deal in stage closedwon are pulled. Each becomes or updates a company-type customer in DualEntry, populated with name, email from owneremail, phone, website, and address fields when HubSpot provides them. Companies without a closed-won deal are not imported.

Products to items

HubSpot products become DualEntry items. Each item carries the SKU (hs_sku or a generated HS_<id> if HubSpot has none), the product description, and a mapped item type from hs_product_type (service maps to service; inventory-like types map to goods). The item also carries the income, expense, and - for services - deferred revenue accounts you configured in Step 2.

Closed-won deals to draft contracts

Each qualifying deal produces a contract on your configured accounting company in draft status, with the following structure:
  • Line items resolve to items, preferring items already mapped from HubSpot products. If a line references a product with no mapping, DualEntry creates the item inline and registers the integration record.
  • Discounts become discounts on the obligation when HubSpot exposes a percentage or fixed discount on the line.
  • Customer resolves from the deal’s primary associated company when that company is already mapped to a customer; otherwise DualEntry creates and links the customer on the fly from company properties. If the deal has no associated company, sync uses a placeholder unknown customer record.
  • Memo includes the deal description and a deep link to the deal in HubSpot, in the form https://app.hubspot.com/contacts/<portal>/deal/<deal_id>.
Performance obligations use the immediate recognition strategy in the current mapping. For how DualEntry treats contracts and revenue recognition after import, see Revenue recognition.

HubSpot invoices to sales invoices (optional)

When enabled in settings, each HubSpot invoice with line items becomes or updates a DualEntry invoice. Line items must carry a hs_product_id on each HubSpot line; lines with zero extended amount are skipped, and discounts become separate negative lines when present.
Imported HubSpot invoices currently post against placeholder Unknown customer and Unknown company records created for the organization, not the CRM company or contact on the invoice header. Plan manual reassignment after every sync, or leave invoice import off until party mapping meets your controls.

Current limitations

A few aspects of the integration are worth knowing before you rely on it:
  • One-way only. DualEntry does not push revenue, AR aging, payment status, or any other accounting state back into HubSpot. The connector’s push category is empty by design.
  • Closed-won filter is intentional. Companies are pulled only through deals in stage closedwon so the accounting subset matches sales-won CRM records rather than your full CRM company database. There is no setting to broaden this filter today.
  • Invoice import uses placeholder parties. As described in Step 3 and the optional invoice import section above, imported HubSpot invoices post against Unknown customer and company records. Mapping invoice parties to accounting records is on the roadmap but not implemented.
  • Sync cadence varies by deployment. Scheduled jobs run at the organization-integration level set by your deployment. You can always run an on-demand sync from the integration page or via the trigger-sync API.

Troubleshoot sync errors

When a record fails, it appears in the Integration Errors log under Settings → Integrations → HubSpot. The most common causes:
SymptomLikely causeResolution
OAuth fails immediatelyMismatched redirect URI, wrong client secret, or missing scopes on the HubSpot app.Fix the HubSpot OAuth app configuration to match DualEntry’s deployment values; retry connect.
”Setup incomplete” or validation errors on syncOne or more required integration settings is missing or points at a deleted account or company.Open Settings → Integrations → HubSpot → Settings and ensure every required GL account and the accounting company are valid.
Deal skipped: “No valid line items”Every line has zero extended price, or line items could not be read.Fix line items or product associations in HubSpot; ensure products ran in an earlier sync step when needed.
Invoice line fails: “Product id missing”A HubSpot invoice line has no hs_product_id.Add a product to each billable line in HubSpot, or disable invoice sync until lines are valid.
Invoice fails: missing hs_invoice_date or hs_due_dateRequired properties absent on the HubSpot invoice.Populate dates on the HubSpot invoice or exclude it from sync.
Token / 401 errors during syncRefresh token revoked or HubSpot session policy changed.Reconnect OAuth in DualEntry to obtain new tokens.
Locked period errorsAccounting period is closed in DualEntry for the transaction date.Reopen the period or adjust dates in HubSpot per your policy, then resync.

Result

After OAuth, settings, and a successful sync, closed-won deals appear as draft contracts with obligations and links back to HubSpot, products appear as items, and won-associated companies appear as customers. If you enabled invoice import, sales invoices land in DualEntry against placeholder customer and company records ready for manual reassignment. To finish revenue recognition on the imported contracts, see Revenue recognition. To connect more systems, return to Integrations. For HubSpot’s own API reference, see the HubSpot API overview.
Last modified on May 28, 2026