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.- Navigate to Settings → Integrations → HubSpot.
- 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. - Approve the app in HubSpot. HubSpot redirects to your configured redirect URI with an authorization code.
- DualEntry exchanges the code for tokens via
POST /api/integrations/hubspot/oauth-callback/. On success it storesaccess_token,refresh_token,expires_in,scope, andtoken_created_at, sets the integration to connected, reads account details from HubSpot (portalIdasremote_id, company currency when present), and authenticates the sync service.
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:
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 callPOST /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:
- Invoices - only if invoice sync is enabled in Step 2 settings.
- Products → items - SKU, name, type, and the income, expense, and deferral accounts you configured in settings.
- Companies → customers - companies linked to deals in stage
closedwonbecome company-type customers. - Deals → draft contracts - deals in stage
closedwonwithclosedateon or after the integration cutoff date become draft contracts with performance obligations built from deal line items.
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 stageclosedwon 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>.
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 ahs_product_id on each HubSpot line; lines with zero extended amount are skipped, and discounts become separate negative lines when present.
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
closedwonso 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.
