HubSpot + Notion: почему нативная интеграция устаревает раньше чем данные синхронизируются
Нативной интеграции между HubSpot и Notion не существует. В HubSpot Marketplace нет официального Notion-коннектора. Для связки используют Zapier, Make или n8n — и именно здесь начинаются проблемы. Данные в Notion устаревают раньше чем команда ими пользуется, типы полей не совпадают, а обновления из Notion никогда не попадают обратно в HubSpot. Ниже — конкретный разбор что ломается и как это исправить.
Что происходит: симптомы
Типичный сценарий: команда хочет видеть данные о клиентах из HubSpot в Notion — для client portal, операционного трекинга, onboarding checklist или базы знаний. Подключают через Zapier и через неделю замечают:
- Данные в Notion расходятся с HubSpot — обновление владельца сделки или суммы не синхронизировалось
- Новые контакты появляются в Notion с задержкой 15–60 минут (polling-интервал Zapier на бесплатном тарифе)
- Кастомные поля HubSpot с типом
enumeration(dropdown) превращаются в сырой internal value в Notion, а не в читаемую метку - Команда начинает редактировать Notion напрямую — изменения не идут обратно в HubSpot
- CRM-данные деградируют: реальность — в Notion, актуальность — в HubSpot, правда — непонятно где
Почему Zapier/Make не решают задачу
Задержки: Zapier на платном Business-тарифе ($49+/мес) обновляет каждые 1–2 минуты. На бесплатном — каждые 15 минут. Make — от 15 минут на минимальном тарифе. Для операционных данных (смена ответственного, обновление статуса) это неприемлемо.
Маппинг типов полей: HubSpot и Notion имеют принципиально разные модели данных:
| HubSpot тип | Что приходит в Notion | Проблема |
|---|---|---|
enumeration (dropdown) | internal value: "new_business" | В Notion нужно читать "Новый бизнес" |
datetime | ISO timestamp | Notion принимает только дату без времени в date-полях |
multi_checkbox | массив internal values | Notion multi-select ждёт массив строк с нужными именами |
rich_text (Notes) | HTML-строка | Notion RichText — отдельный формат с блоками |
| Ассоциации (Deal -> Contact -> Company) | ID числом | В Notion нет связей между базами через внешние ID |
Нет обратной синхронизации: Zapier и Make работают в одну сторону — HubSpot -> Notion. Любые изменения в Notion (добавили заметку, обновили статус) не попадают в HubSpot. Два источника истины — классическая причина деградации CRM.
Ограничения HubSpot Webhook через Zapier: Zapier использует polling, а не real-time webhook. Настоящий HubSpot webhook (мгновенный) недоступен через стандартные Zapier-триггеры.
Что теряет бизнес
Операционные потери:
— CS-менеджер обновляет статус клиента в Notion — менеджер по продажам видит старый статус в HubSpot
— Ответственный за сделку меняется в HubSpot — в Notion по-прежнему указан прежний менеджер
— Клиент переходит на другой тариф — обновление синхронизируется через 40 минут или не синхронизируется вовсе
Архивные потери:
— HubSpot Activity log (звонки, письма) не синхронизируется в Notion вообще — Zapier не умеет реплицировать активности
— Notes из HubSpot приходят как raw HTML — <strong>важно</strong> вместо читаемого форматирования
— Ассоциации (сделка -> контакт -> компания) теряются — Notion не поддерживает реляционные связи между разными базами через внешние ID
Правильный подход: HubSpot Webhook -> кастомный backend -> Notion API
Правильная архитектура: HubSpot Webhook App (native) -> ваш backend -> Notion API. Это даёт мгновенную синхронизацию без polling и полный контроль над маппингом.
HubSpot Webhook: deal.propertyChange (amount, owner, stage)
↓ Backend (FastAPI / Flask)
1. Получить полные данные сделки: GET /crm/v3/objects/deals/{id}?properties=...
2. Разрезолвить ассоциации: GET /crm/v3/objects/deals/{id}/associations/contacts
3. Трансформировать данные: enumeration -> label, datetime -> date, HTML -> Notion blocks
4. Найти страницу в Notion: POST /databases/{db_id}/query (filter by hubspot_deal_id)
-> если найдена: PATCH /pages/{page_id} с обновлёнными properties
-> если не найдена: POST /pages с hubspot_deal_id как идентификатором
Notion API: создать/обновить страницу в базе:
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",
}
# Маппинг HubSpot enumeration -> читаемое имя
STAGE_LABELS = {
"appointmentscheduled": "Встреча назначена",
"qualifiedtobuy": "Квалифицирован",
"presentationscheduled":"Презентация",
"decisionmakerboughtin":"ЛПР вовлечён",
"contractsent": "Контракт отправлен",
"closedwon": "Выигран",
"closedlost": "Потерян",
}
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 для 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 настройка: HubSpot -> Settings -> Integrations -> Private Apps -> Create private app -> Webhooks. Подписаться на deal.propertyChange для нужных полей (amount, dealstage, hubspot_owner_id). Или использовать HubSpot Workflow -> Webhook action для триггеров по этапам.
Зачем вообще синхронизировать HubSpot с Notion
Это законный вопрос. Типовые сценарии где это оправдано:
- Client portal в Notion: клиент видит статус своего проекта/onboarding — данные из HubSpot, интерфейс в Notion через Notion Pages
- Операционный трекинг: CS-команда ведёт онбординг в Notion, хочет видеть CRM-контекст без доступа к HubSpot
- Еженедельный revenue report: автоматически обновляемая таблица Won-сделок в Notion для борда
Если цель — аналитика, лучше рассмотреть Prooflytics или BI-инструменты. Если цель — операционный трекинг, кастомная интеграция через Notion API решает задачу правильно.
Для кого актуально
- Команды, где HubSpot — CRM для продаж, Notion — операционная база для CS/delivery
- Client portal сценарии: клиент читает статус в Notion, данные берутся из HubSpot
- Компании, которые уже пробовали Zapier и столкнулись с перечисленными проблемами
Часто задаваемые вопросы
Есть ли официальная интеграция HubSpot + Notion?
По состоянию на Q2 2026 — нет. В HubSpot App Marketplace нет официального Notion-коннектора. Существуют интеграции через Zapier, Make и n8n, но все они работают через polling, а не native webhook.
Notion API поддерживает ассоциации между базами данных?
Да, через Relation property. POST /pages с {"relation": [{"id": "{other_page_id}"}]} создаёт связь между страницами в разных базах. Для HubSpot-интеграции: при синхронизации Deal -> также синхронизировать Contact и Company как отдельные страницы, затем связать через Relation.
Как обработать удалённую сделку в HubSpot?
HubSpot Webhook присылает deal.deletion событие. Соответствующую страницу в Notion — архивировать (PATCH /pages/{id} с {"archived": true}) или обновить статус-поле. Физическое удаление страниц в Notion через API не рекомендуется — лучше архивирование.
HubSpot Notes — как синхронизировать в Notion?
Через отдельный endpoint: GET /crm/v3/objects/notes?associations.objectId={deal_id}&associations.objectType=deals. Notes приходят с полем hs_note_body в HTML. Для Notion нужно конвертировать HTML -> Notion Blocks (paragraph, heading, bulleted_list). Библиотека notion-block-renderer или ручной парсер.
Итого
- Нативной HubSpot + Notion интеграции нет — Zapier/Make дают polling с задержками и неполным маппингом
- Ключевые проблемы: задержки, несовместимость типов, нет обратной синхронизации
- Правильный подход: HubSpot Private App Webhook -> backend -> Notion API (
2022-06-28) - Notion Auth: Bearer token в заголовке
Authorization, заголовокNotion-Version: 2022-06-28 - Идентификатор связи:
hubspot_deal_idкак rich_text поле в Notion для поиска при обновлениях - Типы полей требуют трансформации: enumeration -> label, datetime -> date string, HTML -> Notion blocks
Если вам нужна надёжная синхронизация HubSpot с Notion — опишите сценарий: что должно отображаться в Notion и при каких событиях обновляться. Exceltic.dev разработает правильную архитектуру.