HubSpot + Notion: Why the Native Integration Goes Stale Before the Data Syncs

There is no native integration between HubSpot and Notion. The HubSpot Marketplace has no official Notion connector. Teams use Zapier, Make, or n8n to connect them — and this is exactly where the problems begin. Data in Notion goes stale before the team can use it, field types do not match, and updates from Notion never make it back to HubSpot. Below is a concrete breakdown of what breaks and how to fix it.

What Happens: Symptoms

A typical scenario: a team wants to see client data from HubSpot in Notion — for a client portal, operational tracking, onboarding checklist, or knowledge base. They connect via Zapier and within a week notice:

  • Data in Notion diverges from HubSpot — deal owner or amount changes did not sync
  • New contacts appear in Notion with a 15–60 minute delay (Zapier polling interval on the free plan)
  • HubSpot custom fields with type enumeration (dropdown) show up as raw internal values in Notion instead of readable labels
  • The team starts editing Notion directly — changes do not flow back to HubSpot
  • CRM data degrades: the truth is in Notion, the current state is in HubSpot, and where reality actually lives is unclear

Why Zapier/Make Do Not Solve the Problem

Delays: Zapier on the paid Business plan ($49+/mo) updates every 1–2 minutes. On the free plan — every 15 minutes. Make — from 15 minutes on the minimum plan. For operational data (owner change, status update) this is unacceptable.

Field type mapping: HubSpot and Notion have fundamentally different data models:

HubSpot typeWhat arrives in NotionProblem
enumeration (dropdown)internal value: "new_business"Notion needs to display "New Business"
datetimeISO timestampNotion date fields only accept a date without time
multi_checkboxarray of internal valuesNotion multi-select expects an array of strings with the correct names
rich_text (Notes)HTML stringNotion RichText is a separate format with blocks
Associations (Deal -> Contact -> Company)numeric IDNotion has no relationships between databases via external IDs

No reverse sync: Zapier and Make work in one direction — HubSpot -> Notion. Any changes in Notion (added a note, updated a status) do not reach HubSpot. Two sources of truth — the classic cause of CRM degradation.

HubSpot Webhook limitations via Zapier: Zapier uses polling, not real-time webhook. The true HubSpot webhook (instant) is unavailable through standard Zapier triggers.

What the Business Loses

Operational losses:
— CS manager updates client status in Notion — sales manager sees the old status in HubSpot
— Deal owner changes in HubSpot — Notion still shows the previous manager
— Client switches to a different plan — the update syncs 40 minutes later or not at all

Archive losses:
— HubSpot Activity log (calls, emails) does not sync to Notion at all — Zapier cannot replicate activities
— Notes from HubSpot arrive as raw HTML — <strong>important</strong> instead of readable formatting
— Associations (deal -> contact -> company) are lost — Notion does not support relational links between different databases via external IDs

The Right Approach: HubSpot Webhook -> Custom Backend -> Notion API

The correct architecture: HubSpot Webhook App (native) -> your backend -> Notion API. This provides instant synchronization without polling and full control over mapping.

HubSpot Webhook: deal.propertyChange (amount, owner, stage)
  ↓ Backend (FastAPI / Flask)
  1. Get full deal data: GET /crm/v3/objects/deals/{id}?properties=...
  2. Resolve associations: GET /crm/v3/objects/deals/{id}/associations/contacts
  3. Transform data: enumeration -> label, datetime -> date, HTML -> Notion blocks
  4. Find page in Notion: POST /databases/{db_id}/query (filter by hubspot_deal_id)
     -> if found: PATCH /pages/{page_id} with updated properties
     -> if not found: POST /pages with hubspot_deal_id as identifier

Notion API: create/update a page in the database:

import requests
from datetime import datetime

NOTION_TOKEN = "secret_your_integration_token"
NOTION_VERSION = "2022-06-28"
NOTION_DB_ID = "your_database_id"

headers = {
    "Authorization": f"Bearer {NOTION_TOKEN}",
    "Notion-Version": NOTION_VERSION,
    "Content-Type": "application/json",
}

# Mapping HubSpot enumeration -> readable label
STAGE_LABELS = {
    "appointmentscheduled": "Meeting Scheduled",
    "qualifiedtobuy":       "Qualified",
    "presentationscheduled":"Presentation Scheduled",
    "decisionmakerboughtin":"Decision Maker Bought In",
    "contractsent":         "Contract Sent",
    "closedwon":            "Closed Won",
    "closedlost":           "Closed Lost",
}

def find_notion_page(hubspot_deal_id: str) -> str | None:
    resp = requests.post(
        f"https://api.notion.com/v1/databases/{NOTION_DB_ID}/query",
        headers=headers,
        json={
            "filter": {
                "property": "HubSpot Deal ID",
                "rich_text": {"equals": hubspot_deal_id}
            }
        }
    )
    resp.raise_for_status()
    results = resp.json().get("results", [])
    return results[0]["id"] if results else None

def sync_deal_to_notion(deal: dict, contact_name: str = "", company_name: str = "") -> None:
    deal_id = str(deal["id"])
    props = deal.get("properties", {})

    amount = props.get("amount", "") or ""
    stage_key = props.get("dealstage", "")
    stage_label = STAGE_LABELS.get(stage_key, stage_key)

    # closedate: HubSpot -> ISO date string for Notion
    closedate_raw = props.get("closedate", "")
    closedate = closedate_raw[:10] if closedate_raw else None  # "2026-06-15T..."->"2026-06-15"

    notion_props = {
        "Name": {"title": [{"text": {"content": props.get("dealname", f"Deal {deal_id}")}}]},
        "HubSpot Deal ID": {"rich_text": [{"text": {"content": deal_id}}]},
        "Stage": {"select": {"name": stage_label}},
        "Owner": {"rich_text": [{"text": {"content": props.get("hubspot_owner_id", "")}}]},
        "Contact": {"rich_text": [{"text": {"content": contact_name}}]},
        "Company": {"rich_text": [{"text": {"content": company_name}}]},
    }

    if amount:
        notion_props["Amount"] = {"number": float(amount)}

    if closedate:
        notion_props["Close Date"] = {"date": {"start": closedate}}

    existing_page_id = find_notion_page(deal_id)
    if existing_page_id:
        requests.patch(
            f"https://api.notion.com/v1/pages/{existing_page_id}",
            headers=headers,
            json={"properties": notion_props}
        ).raise_for_status()
    else:
        requests.post(
            "https://api.notion.com/v1/pages",
            headers=headers,
            json={
                "parent": {"database_id": NOTION_DB_ID},
                "properties": notion_props
            }
        ).raise_for_status()

HubSpot Webhook setup: HubSpot -> Settings -> Integrations -> Private Apps -> Create private app -> Webhooks. Subscribe to deal.propertyChange for the required fields (amount, dealstage, hubspot_owner_id). Or use HubSpot Workflow -> Webhook action for stage-based triggers.

Why Sync HubSpot with Notion at All

This is a legitimate question. Typical scenarios where it makes sense:

  • Client portal in Notion: the client sees the status of their project/onboarding — data from HubSpot, interface in Notion via Notion Pages
  • Operational tracking: the CS team manages onboarding in Notion and wants to see CRM context without access to HubSpot
  • Weekly revenue report: automatically updated table of Won deals in Notion for the board

If the goal is analytics, Prooflytics or BI tools are worth considering. If the goal is operational tracking, a custom integration via the Notion API solves the problem correctly.

Who This Is Relevant For

  • Teams where HubSpot is the sales CRM and Notion is the operational base for CS/delivery
  • Client portal scenarios: client reads status in Notion, data comes from HubSpot
  • Companies that have already tried Zapier and encountered the problems described above

Frequently Asked Questions

Is there an official HubSpot + Notion integration?

As of Q2 2026 — no. The HubSpot App Marketplace has no official Notion connector. Integrations via Zapier, Make, and n8n exist, but all operate via polling rather than native webhook.

Does the Notion API support associations between databases?

Yes, via the Relation property. POST /pages with {"relation": [{"id": "{other_page_id}"}]} creates a link between pages in different databases. For HubSpot integration: when syncing a Deal — also sync the Contact and Company as separate pages, then link them via Relation.

How do you handle a deleted deal in HubSpot?

HubSpot Webhook sends a deal.deletion event. The corresponding Notion page should be archived (PATCH /pages/{id} with {"archived": true}) or have a status field updated. Physically deleting pages in Notion via API is not recommended — archiving is preferred.

How do you sync HubSpot Notes into Notion?

Via a separate endpoint: GET /crm/v3/objects/notes?associations.objectId={deal_id}&associations.objectType=deals. Notes arrive with the field hs_note_body in HTML. For Notion, HTML must be converted to Notion Blocks (paragraph, heading, bulleted_list). Use the notion-block-renderer library or a custom parser.

Summary

  • There is no native HubSpot + Notion integration — Zapier/Make provide polling with delays and incomplete mapping
  • Key problems: delays, type incompatibility, no reverse sync
  • The right approach: HubSpot Private App Webhook -> backend -> Notion API (2022-06-28)
  • Notion auth: Bearer token in Authorization header, Notion-Version: 2022-06-28 header
  • Link identifier: hubspot_deal_id as a rich_text field in Notion for lookup on updates
  • Field types require transformation: enumeration -> label, datetime -> date string, HTML -> Notion blocks

If you need reliable HubSpot to Notion synchronization — describe the scenario: what should appear in Notion and on which events it should update. Exceltic.dev will design the right architecture.

More articles

All →