Back to Blog

Building a HubSpot–QuickBooks Online Tax Estimation Integration When the Native Solution Isn’t Enough

HubSpot’s tax calculation beta wasn’t cutting it for this client. Here’s how we built a custom QuickBooks Online integration from scratch—OAuth server, token manager, and all.

Building a HubSpot–QuickBooks Online Tax Estimation Integration When the Native Solution Isnt Enough overlayed on a rendering of a chip board with the QuickBooks and HubSpot logos Case Study

 

Case Study - Project Overview Skip to project details
Project Overview
Custom Financial Integration  ·  Stone Fabrication Equipment
Industry
Stone Fabrication Equipment (Manufacturing)
Engagement Type
Solutions
Core Challenge
Real-time regional sales tax estimation on HubSpot deal records
HubSpot Hubs
Sales Hub
Key Integrations & Systems
QuickBooks Online AWS Lambda AWS API Gateway AWS Parameter Store QBO Estimates API OAuth 2.0
Hypha Team
Integration Developer Project Manager

The Problem with Tax on Complex Equipment Quotes

The client manufactures and sells CNC bridge saws, waterjet systems, polishers, and stone fabrication parts to shops across the United States. A single deal often includes the machine, installation, training, and service components as separate line items in HubSpot, each with its own SKU and price. When a sales rep assembled one of these packages, the quote needed accurate regional sales tax before it went to the customer. Getting that number right before the quote leaves matters.

HubSpot’s tax calculation feature was in beta at the time and wasn’t functioning correctly for this client’s setup. The company’s CFO was an active stakeholder in the engagement, which reflects how the accuracy question was being treated internally. A manual state tax lookup had been filling the gap, but it added friction between sales and finance every time a quote was ready. The goal was straightforward: when a rep marks a deal as finalized, the estimated tax should appear on the record automatically, without anyone having to calculate it separately.

Why This Needed a Custom Build

The native HubSpot–QuickBooks Online connector handles contact and deal synchronization between the two platforms, but tax estimation isn’t part of what it does. QuickBooks Online (QBO) has an Estimates API that calculates regional tax accurately. Getting to it in a production environment requires building a full OAuth authentication layer from scratch.

The engagement covered two distinct deliverables: an OAuth server and token manager to establish and maintain authenticated access to the QBO API, and the tax estimation integration itself. If you’re working through the same question—native connector vs. custom build—this breakdown of when to go custom covers the decision criteria in detail.


Building the OAuth Server and Token Manager

Setting Up the OAuth Server

Getting authenticated against QBO’s API in production involves a circular dependency that doesn’t appear in the sandbox environment. Intuit requires a live HTTPS endpoint for the OAuth redirect before they’ll issue production credentials. Getting those credentials requires completing their production questionnaire, which asks for that endpoint URL. You need a functioning server to get the credentials, and you need the credentials to make the server fully functional.

The path through it: deploy a near-complete server to AWS first, capture the HTTPS URL from API Gateway, complete the questionnaire with that URL, then update the app’s client ID and secret once Intuit issued them. It adds a step, but there’s no shortcut through the dependency. Teams doing production QBO OAuth work for the first time tend to hit this and lose time on it.

The server runs on AWS Lambda with API Gateway providing the HTTPS endpoint. When a user navigates to the /oauth route, the app uses the intuit-oauth Node.js SDK to calculate the correct QBO authorization URL and redirects them to it. The user signs in with a QuickBooks account that has admin privileges—QBO requires a human sign-in at this step, and it can’t be automated. QBO then redirects back to the app’s /oauth/redirect route with an authorization code, which the app immediately exchanges for an access token and refresh token. Those initial tokens are manually copied from the OAuth server logs into AWS Parameter Store to start the token lifecycle.

OAuth Lifecycle
Auth Architecture
OAuth Server & Token Manager Lifecycle
2 Phases
Initial OAuth Handshake
Completed once to establish access
AWS Lambda · API Gateway
Deploy OAuth server to AWS; capture HTTPS endpoint URL
Server is deployed before production credentials exist to resolve the circular dependency—Intuit requires the redirect URL before issuing keys.
AWS Lambda + API Gateway
Intuit Developer Portal
Complete production questionnaire; receive client ID and secret
Production credentials are issued after Intuit reviews the questionnaire and confirms the HTTPS redirect URL. Credentials are then loaded into the deployed app.
Production Credentials
OAuth Server · QBO
User navigates to /oauth → redirected to QBO sign-in
The intuit-oauth Node.js SDK calculates the QBO authorization URL. The user signs in with a QuickBooks admin account—this human step cannot be automated.
intuit-oauth SDK
OAuth Server
QBO returns auth code → app exchanges it for tokens
QBO redirects to /oauth/redirect with an authorization code. The app immediately exchanges this for an access token and refresh token.
Access Token + Refresh Token
Manual Step · Done Once
Initial tokens copied from server logs into AWS Parameter Store
This manual seed is the only human step in the token lifecycle. Once complete, the Token Manager takes over all future refreshes automatically.
Manual, One Time Only
Token Manager
Automated refresh cycle — runs continuously
AWS Lambda · Scheduled
Token Manager Lambda fires every 45 minutes
The refresh runs inside the ~60-minute token expiry window, ensuring no Lambda function ever encounters an expired credential mid-execution.
Every 45 min
AWS Parameter Store
Lambda retrieves current tokens from Parameter Store
Parameter Store serves as the single source of truth for current credentials. All Lambda functions read from here before making QBO API calls.
AWS Parameter Store
QuickBooks Online API
Refresh token exchanged for a new access + refresh token pair
QBO returns fresh credentials. Both the access token and refresh token are updated—keeping the full cycle running without any manual intervention.
New Token Pair Issued
AWS Parameter Store
New tokens written back to Parameter Store
Updated credentials are stored and ready. Any Lambda function calling the QBO API—including the tax estimation function—picks up fresh tokens automatically.
Cycle Repeats
Token Expiry vs. Refresh Timing
QBO access tokens expire in approximately 60 minutes. The Token Manager refreshes every 45 minutes, maintaining a 15-minute safety buffer so credentials are never stale when a tax estimation request arrives.
Refresh: 45 min
vs.
Token expiry: ~60 min
Initial handshake
Manual step (once only)
Automated cycle


Managing Token Expiry: The Token Manager

QBO access tokens expire in approximately one hour. If a Lambda function tries to call the QBO API with an expired token, the request fails. The Token Manager addresses this by running a refresh cycle every 45 minutes, well inside the expiry window. It uses the stored refresh token to retrieve a fresh access token and refresh token pair, then updates both values in Parameter Store. Every Lambda function that needs to call the QBO API fetches a current credential from the store rather than depending on one that may have aged out.

Once the initial OAuth handshake is complete and the first tokens are seeded manually, the cycle runs without intervention. Developers can invoke Lambda functions against the QBO API without managing token state themselves. For a broader look at how HubSpot’s API handles OAuth and webhook authentication, this post on HubSpot API endpoints covers the underlying mechanics.

How the Tax Estimation Integration Works

Tax Estimation Flow
Integration Flow
How Sales Tax Gets from QBO to HubSpot
7 Steps
HubSpot · Trigger
Rep checks “Bundle Now” on the deal record
A custom checkbox property signals the package is assembled and ready for quoting.
Custom Deal Property
HubSpot · AWS
Workflow fires and sends a webhook to API Gateway
HubSpot workflow detects the property change and routes the request to AWS.
HubSpot Workflow
AWS Lambda
Lambda retrieves line items and company address from HubSpot
The function pulls all deal line items and the associated company’s billing state to determine the correct tax jurisdiction.
AWS Lambda
Integration Logic
HubSpot SKUs translated to QBO Product IDs with quantities
A maintained mapping table converts each HubSpot SKU to its QuickBooks Online equivalent. Bundle parent SKUs are excluded; only child products are passed.
SKU Translation Map
QuickBooks Online
Dummy customer address updated to match the company’s state
A “Tax Calculation” customer record in QBO is dynamically updated with the deal’s state address before each estimate—no customer sync required.
QBO Estimates API
QuickBooks Online
QBO Estimates endpoint returns the regional tax calculation
The API applies the correct state tax rate to the line items and returns the total estimated tax in the response payload.
API Response
HubSpot · Output
Lambda writes a $0 line item to the HubSpot deal
The line item is named “Estimated Tax: $[amount]” and carries a $0 unit price—visible on the customer quote without affecting the deal total.
$0 Line Item
Trigger
Process
Output


The trigger is a custom checkbox property on the HubSpot deal record called “Bundle Now.” When a sales rep checks it—signaling that a package is assembled and ready for quoting—a HubSpot workflow fires and sends a webhook to AWS API Gateway, which routes the request to the tax estimation Lambda function.

The Lambda retrieves two things from HubSpot: the complete list of line items on the deal, and the address of the associated company record. Line items arrive as HubSpot SKU IDs, which don’t correspond directly to QuickBooks Online product IDs. The integration maintains a translation map pairing each HubSpot SKU with its QBO equivalent, so the Lambda can translate each SKU to its QBO product ID, carry the associated quantity into the request, and build a valid Estimates call from data that lives entirely in HubSpot’s format.

To call the QBO Estimates endpoint, the Lambda uses a dummy customer record in QuickBooks named “Tax Calculation,” created specifically for this purpose. Before each estimate, the Lambda updates that dummy customer’s address to match the company’s address from HubSpot. QBO uses the customer address to determine the applicable state tax rate, so the record needs the correct location for each request rather than a fixed placeholder. The Estimates endpoint returns the full tax calculation, which the Lambda parses from the response.

Getting the result back into HubSpot is a single step. The Lambda creates a new line item on the deal with a unit price of $0 and a name in the format “Estimated Tax: $1,235.00.” The $0 price keeps the line item from affecting the deal total or the quote value. The tax figure appears as a labeled line on the customer-facing quote without changing the commercial terms of the deal.

A Note on Product Bundles in QBO

One detail worth flagging for anyone building something similar: QuickBooks Online product bundles aren’t standalone products in the Estimates API. A bundle is a container for its child products, not a billable line item itself. Any HubSpot line item referencing a bundle SKU carries a $0 value, and when constructing the Estimate, only the nested child products go into the API call. The translation map accounts for this distinction, but it’s a QBO data model behavior that only surfaces when you’re working directly with the endpoint. For more on how HubSpot workflow actions can be extended with custom code to support integrations like this, this post on custom-coded workflow actions is worth a read.

What the Integration Delivers

From the sales rep’s side, the workflow is: assemble the deal, check “Bundle Now,” and the estimated tax appears on the record. No separate lookup, no spreadsheet routed to finance, no delay before the quote goes out. For a small team quoting multi-SKU equipment packages to buyers across multiple states, removing that step from the process has a measurable effect on how quickly deals get finalized.

The OAuth server and Token Manager also establish infrastructure that extends to other QBO API use cases. The authentication layer is in place; connecting additional Lambda functions to QBO from here is a smaller project than the initial build. For a similar engagement—connecting a manufacturing client’s legacy ERP to HubSpot via custom API—see how we approached the Global Shop integration.

Connect Multiple Platforms Seamlessly

Is a HubSpot Integration Right for Your Business?

Explore Custom Integration Solutions arrow_forward

Work With Us

If your HubSpot quoting workflow depends on financial data the platform can’t calculate natively—tax, pricing logic, or connections to external financial systems—a custom integration is often more achievable than it looks once the authentication layer is handled correctly.

We’ve built production-grade connections between HubSpot and QuickBooks Online, and we can scope what that looks like for your setup. Get in touch with our integrations team.