remote_id) for deep links into HubSpot.
Prerequisites
Confirm the following before connecting HubSpot:- 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.
- Write scopes for invoices and related CRM objects if DualEntry has enabled write-back for your organization. New connections request those scopes during OAuth. Existing connections that were authorized before write-back must reconnect.
- 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 Configuration → Company → 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.
Reconnect to grant write scopes
Existing HubSpot connections that were authorized before write-back do not yet have permission to create or update HubSpot invoices and related objects. Reconnect so HubSpot can grant the write scopes. Missing write scopes do not break deal, company, or product pull; DualEntry skips the write and leaves inbound sync running.- Navigate to Configuration → Company → Integrations → HubSpot.
- Choose Reconnect (or connect again if the page only offers Connect with HubSpot).
- Approve the updated permissions in HubSpot, including invoice and CRM object write access.
- Confirm the integration returns to connected.
- Open Settings, turn on the invoice, payment, and item push toggles you want, and save.
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 Configuration → Company → Integrations → HubSpot → Settings. Until every required field is valid, setup remains incomplete and you should treat automated sync as not production-ready.
Step 3: Choose invoice and write-back settings
Invoice pull and invoice write-back are mutually exclusive. DualEntry either imports HubSpot invoices into DualEntry, or it writes DualEntry invoices into HubSpot. It does not do both for the same organization. When write-back is enabled, DualEntry skips the HubSpot-to-DualEntry invoice pull. Use the write-back toggles instead. You can turn invoice, payment, and item push on or off separately, and you can set a sync-from date so older invoices, payments, and items stay out of HubSpot.
When write-back is not enabled, the older Sync HubSpot invoices toggle remains available. It is off by default. Leave it off unless you have reviewed the placeholder-party behavior in HubSpot invoices to sales invoices. Imported HubSpot invoices post against placeholder Unknown customer and Unknown company records, not the CRM company or contact on the invoice header, so each import needs manual cleanup.
Save the settings you want, then run sync. Turning a write-back toggle off stops later writes of that object type; records already written to HubSpot stay in place. Turning invoice import off skips later HubSpot invoice pulls; invoices already imported stay in DualEntry.
Step 4: Run sync
Trigger a sync from Configuration → Company → 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.
Inbound pull always includes products, companies, and closed-won deals. Invoice pull runs only when write-back is off and Sync HubSpot invoices is on. Write-back then creates or updates HubSpot invoices, payment properties, and products for the object types you enabled, using the sync-from date when one is set.
The inbound order is:
- Invoices (HubSpot to DualEntry): only if write-back is off and invoice import is enabled in Step 3.
- Products to items: SKU, name, type, and the income, expense, and deferral accounts you configured in settings.
- Companies to customers: companies linked to deals in stage
closedwonbecome company-type customers. - Deals to draft contracts: deals in stage
closedwonwithclosedateon 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.
hs_invoice_date greater than or equal to the cutoff when one is set. Write-back uses the sync-from date from Step 3 for DualEntry invoices, payments, and items. 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.
Who owns which records
The connector uses an object-partitioned, single-writer model so each field has one system of record. HubSpot remains the source of truth for sales objects. DualEntry remains the source of truth for accounting objects created from those deals.
A DualEntry invoice writes back only when it belongs to a contract that already maps to a HubSpot deal. Resync updates the same HubSpot invoice in place and does not create a second invoice. One deal can have multiple DualEntry invoices; each maps to its own HubSpot invoice, and payments apply to the correct invoice.
What syncs into DualEntry
The connector imports companies, products, and closed-won deals from HubSpot, each mapping to a specific DualEntry record. The sections below describe how each object is matched, populated, and updated on subsequent syncs. Optional HubSpot invoice import is documented separately under HubSpot invoices to sales invoices and runs only when write-back is off.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)
Invoice import from HubSpot into DualEntry is the older path, and it runs only when write-back is not enabled for your organization. When write-back is on, DualEntry skips this pull entirely and writes DualEntry invoices to HubSpot instead. See Step 3 to confirm which path your settings use. When write-back is off and Sync HubSpot invoices is on, each HubSpot invoice with line items becomes or updates a DualEntry sales invoice on the next sync. DualEntry reads the invoice date, due date, currency, and line items from HubSpot. Lines must carry ahs_product_id; lines with zero extended amount are skipped, and discounts become separate negative lines when present. Invoices without hs_invoice_date or hs_due_date fail until those properties are populated. When a cutoff date is set, DualEntry also filters imported invoices to hs_invoice_date on or after that date.
Imported invoices do not resolve the HubSpot company or contact on the invoice header. They post against placeholder Unknown customer and Unknown company records created for the organization, so you need to reassign the customer after each import. Leave this toggle off unless that cleanup is acceptable. Turning the toggle off later skips new pulls; invoices already imported stay in DualEntry.
What DualEntry writes back to HubSpot
When write-back is enabled and the matching toggle is on, DualEntry writes accounting state to HubSpot on the next sync after the DualEntry change. Writes are idempotent: a second sync updates the same HubSpot record instead of creating a duplicate.Invoices
Each DualEntry invoice on a HubSpot-linked contract becomes or updates a native HubSpot invoice. DualEntry maps the invoice number, title, invoice date, due date, and currency, and associates the HubSpot invoice to the originating deal and company. Subsequent syncs update that same HubSpot invoice. The HubSpot invoice also carries links back to the matching DualEntry invoice and its PDF, so someone working the deal can open the accounting artifact without leaving HubSpot.Payments and aging
When payment push is on, DualEntry updates billing properties on the mirrored HubSpot invoice as DualEntry payments and archive changes occur:- Amount billed, amount paid, and balance due, including partial payments.
- Invoice status: waiting, sent, payment made, fully paid, overdue, or voided.
- Overdue and paid-late aging buckets: on time, 1–30 days, 31–60 days, 61–90 days, more than 90 days, or not applicable.
- Archived: archiving the DualEntry invoice updates the existing HubSpot invoice and does not create a second one. An archived DualEntry invoice with no HubSpot mapping is not created on sync.
Items to products
When item push is on, DualEntry items create or update HubSpot products, following the same catalog pattern used by other DualEntry connectors. A later edit to the DualEntry item updates the mapped HubSpot product in place.Contract termination on the deal
Terminating, archiving, or reversing a DualEntry contract writes the termination date, type, and fee back to the linked HubSpot deal. DualEntry also writes contract and invoice references onto that deal so sales can see which DualEntry contract and invoices belong to it.What sales sees on the HubSpot deal
After write-back has run, a salesperson opening the originating HubSpot deal can review AR status without opening DualEntry. The deal shows the linked DualEntry contract, the HubSpot invoices created from DualEntry invoices, and (when the contract has been terminated, archived, or reversed) the termination date, type, and fee. Each mirrored HubSpot invoice shows the DualEntry invoice number, dates, and currency; billed, paid, and balance-due amounts; status and aging; and a link to the DualEntry invoice and PDF. Status values match DualEntry activity: waiting before send, sent after the invoice email goes out, payment made after a partial payment, fully paid after settlement, overdue when the due date has passed with a balance, and voided when the DualEntry invoice is voided. If a write fails, the error appears on the DualEntry record with a retry option. Fix the cause, then retry that record or run sync again. Inbound deal and company pull continues even when a write is skipped because write scopes are missing; reconnect to restore write-back.Current limitations
A few aspects of the integration are worth knowing before you rely on it:- Single writer per object: DualEntry does not overwrite HubSpot deal or company fields that HubSpot owns, and HubSpot invoice edits do not flow back into DualEntry when write-back is on.
- Write-back needs reconnect: Connections authorized before write-back must reconnect so HubSpot can grant invoice and object write scopes. Until then, pull continues and writes are skipped.
- 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: When write-back is off and you enable HubSpot invoice import, imported invoices post against Unknown customer and company records. Mapping invoice parties to accounting records is not implemented.
- Contacts are not in the default plan: A full HubSpot contact export is not part of sync unless your deployment has added it.
- 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.