Skip to content

HubSpot Integration

MIDDAG Account integrates with HubSpot as the CRM layer. HubSpot tracks the sales process -- deals, quotes, contacts, and companies. Billing and payment happen in WooCommerce and Stripe. HubSpot tracks revenue, not transactions.

Dual-account architecture

AccountLegal entityMarketCurrency
HubSpot BRMIDDAG Tecnologia LTDABrazilBRL
HubSpot LLCMIDDAG, LLCInternationalUSD

Each account has its own access token, portal ID, and webhook endpoint. The same client organization can exist in both HubSpots, linked through the plugin's Organization record.

Setup

API credentials

Configure in your environment or wp-config.php:

VariableDescription
HUBSPOT_BR_ACCESS_TOKENAccess token for HubSpot BR
HUBSPOT_BR_PORTAL_IDPortal ID for HubSpot BR
HUBSPOT_LLC_ACCESS_TOKENAccess token for HubSpot LLC
HUBSPOT_LLC_PORTAL_IDPortal ID for HubSpot LLC

Webhook endpoints

Register these URLs in HubSpot under Settings > Integrations > Webhooks:

AccountWebhook URL
HubSpot BRhttps://yoursite.com/wp-json/middag-account/v1/webhooks/hubspot/br
HubSpot LLChttps://yoursite.com/wp-json/middag-account/v1/webhooks/hubspot/llc

Deal pipelines

Configure these pipelines in each HubSpot account:

PipelineChannelFlow
self-serveWooCommerce / StripeAutomatic: purchase triggers deal closed-won
consultativeHubSpot managedManual: sales rep manages deal stages
renewalAutomaticAutomatic: renewal events update deals
upsellHubSpot managedManual: CS team manages upgrade opportunities

Organization-to-Company mapping

Each organization in the plugin stores two HubSpot company IDs:

FieldDescription
hubspot_company_id_brCompany ID in HubSpot BR
hubspot_company_id_globalCompany ID in HubSpot LLC

The plugin is the deduplication point. When the same client exists in both HubSpots, their single Organization record holds both company IDs.

Source of truth by field

Sync happens at the field level, not the entity level. Each field has a defined owner:

DataSource of truthDirection
Organization fieldsPluginPlugin to HubSpot
Deals and pipeline stagesHubSpotHubSpot to Plugin
Quote status (accepted/paid)PluginPlugin to HubSpot
Quote creation dataHubSpotHubSpot to Plugin
Contact commercial dataHubSpotHubSpot to Plugin
Order/payment statusPluginPlugin to HubSpot

When a conflict is detected (field edited in both systems), the plugin wins for organization data. An audit trail records the field, old value, new value, and which source prevailed.

Quote flow

The quote lifecycle spans both systems:

Each quote belongs to a billing entity (BR or Global). The sync targets the correct HubSpot account.

Webhook events

Deals

EventPlugin action
deal.creationRegister opportunity locally
deal.propertyChange (stage)Update local stage, amount, close date
deal.deletionMark deal as removed

Quotes

EventPlugin action
quote.creationCreate Quote (status: draft)
quote.propertyChangeUpdate local quote
quote.deletionMark quote as cancelled

Contacts

EventPlugin action
contact.creationCreate or update linked contact
contact.propertyChangeUpdate contact data
contact.deletionDeactivate contact

HubSpot custom properties

The following custom properties should be created on HubSpot products for correct sync:

PropertySourcePurpose
middag_skuProduct SKULinks HubSpot product to YAML
middag_tierProduct tierReporting by funnel tier
middag_entitlement_classEntitlement classProvisioning routing
middag_billing_modelBilling modelRevenue type classification
middag_brandBrandMulti-brand reporting

Synced services

The integration uses seven service classes, migrated from the original theme:

ServiceFunction
CompanyServiceSync Organization with HubSpot Company
ContactServiceSync Collaborator to HubSpot Contact
DealServiceRead HubSpot deals for admin display
QuoteServiceBidirectional quote sync
AssociationServiceLink company, contact, and deal records
SearchServiceSearch companies and contacts in HubSpot
LineItemServiceSync quote line items

Deal isolation

Deals in HubSpot BR stay in HubSpot BR. Deals in HubSpot LLC stay in HubSpot LLC. There is no cross-sync between accounts. The plugin reads deals from both accounts for display in the admin UI (read-only).

Troubleshooting

SymptomLikely causeFix
Organization not syncingMissing HubSpot company IDVerify the organization has a company ID set
Quote status not updatingWebhook signature validationConfirm HMAC secret is correct per account
Duplicate contacts in HubSpotDedup not runningCheck that organization links both accounts
Deal data staleWebhook endpoint not registeredRegister the endpoint in HubSpot settings