HubSpot + Webflow: что теряет нативная интеграция форм и как это исправить
Webflow — популярный no-code конструктор сайтов для маркетинговых команд. HubSpot — CRM и маркетинг-хаб. Нативная интеграция выглядит просто: установить HubSpot Tracking Code на Webflow сайт, подключить Webflow Forms к HubSpot — лиды идут в CRM. Проблема в деталях: нативная интеграция через стандартный HubSpot embed теряет UTM-атрибуцию, не работает с кастомными Webflow формами, не поддерживает прогрессивное профилирование. Разбираем что конкретно ломается и как строить правильно.
Как работает нативная интеграция (и в чём проблема)
Шаг 1: Добавить HubSpot Tracking Code в <head> Webflow. Это устанавливает hsq cookie и начинает трекинг сессий.
Шаг 2: Webflow Forms -> Integrations -> подключить HubSpot. Webflow при отправке формы делает POST в свой бэкенд -> Webflow передаёт данные в HubSpot через встроенную интеграцию.
Что теряется:
1. UTM-параметры и source attribution. Нативная Webflow-HubSpot интеграция не передаёт UTM-параметры в HubSpot Contact Properties. Контакт создаётся, но hs_analytics_source, utm_source, utm_campaign — пустые или «Direct Traffic». Marketing team не знает из какой кампании пришёл лид.
2. Кастомные Webflow-формы. Нативная интеграция работает только со стандартными Webflow Form элементами. Кастомная форма на JS (React, Alpine.js, кастомный fetch) — не подключается через нативную интеграцию.
3. Прогрессивное профилирование. HubSpot Progressive Profiling — заполнение разных полей при повторных визитах. Нативная Webflow-интеграция не поддерживает это: всегда запрашивает одинаковые поля.
4. File upload в формах. Webflow Forms поддерживает file upload. HubSpot нативная интеграция не передаёт файлы — только текстовые поля.
Правильная архитектура: HubSpot Forms API + JS SDK
Вместо нативной интеграции — прямая отправка данных в HubSpot Forms API при submit:
// Правильный подход: кастомный submit handler
// Подключить HubSpot Tracking Code в <head> - обязательно для cookie
const HUBSPOT_PORTAL_ID = "YOUR_PORTAL_ID";
const HUBSPOT_FORM_GUID = "YOUR_FORM_GUID"; // из HubSpot -> Marketing -> Forms -> Details
async function submitToHubSpot(formData) {
// Получить UTM из URL
const params = new URLSearchParams(window.location.search);
const utmSource = params.get("utm_source") || "";
const utmMedium = params.get("utm_medium") || "";
const utmCampaign = params.get("utm_campaign") || "";
const utmContent = params.get("utm_content") || "";
const utmTerm = params.get("utm_term") || "";
// Получить HubSpot cookie для identity resolution
const getCookie = (name) => {
const match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
return match ? match[2] : "";
};
const hutk = getCookie("hubspotutk");
const payload = {
portalId: HUBSPOT_PORTAL_ID,
formGuid: HUBSPOT_FORM_GUID,
fields: [
{ name: "email", value: formData.email },
{ name: "firstname", value: formData.firstName },
{ name: "lastname", value: formData.lastName },
{ name: "company", value: formData.company || "" },
{ name: "phone", value: formData.phone || "" },
// UTM-параметры как HubSpot contact properties
{ name: "utm_source", value: utmSource },
{ name: "utm_medium", value: utmMedium },
{ name: "utm_campaign", value: utmCampaign },
],
context: {
hutk: hutk, // для session stitching
pageUri: window.location.href,
pageName: document.title,
},
legalConsentOptions: {
// GDPR consent (если нужно)
consent: {
consentToProcess: true,
text: "Я согласен на обработку персональных данных.",
},
},
};
const resp = await fetch(
`https://api.hsforms.com/submissions/v3/integration/submit/${HUBSPOT_PORTAL_ID}/${HUBSPOT_FORM_GUID}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);
if (!resp.ok) {
const err = await resp.json();
throw new Error(JSON.stringify(err));
}
return await resp.json();
}
// Привязать к Webflow form submit
document.querySelector("#contact-form").addEventListener("submit", async (e) => {
e.preventDefault();
const fd = new FormData(e.target);
try {
await submitToHubSpot({
email: fd.get("email"),
firstName: fd.get("first-name"),
lastName: fd.get("last-name"),
company: fd.get("company"),
phone: fd.get("phone"),
});
// Показать success state
document.querySelector("#form-success").style.display = "block";
e.target.style.display = "none";
} catch (err) {
console.error("HubSpot submit error:", err);
}
});
UTM persistence: сохранение параметров между страницами
UTM-параметры теряются при навигации если не сохранять. Правильный подход — сохранить в sessionStorage при первом визите:
// Сохранить UTM при загрузке первой страницы
(function saveUtm() {
const params = new URLSearchParams(window.location.search);
["utm_source","utm_medium","utm_campaign","utm_content","utm_term"].forEach(k => {
if (params.get(k)) {
sessionStorage.setItem(k, params.get(k));
}
});
})();
// При submit - читать из sessionStorage
function getStoredUtm() {
return {
utmSource: sessionStorage.getItem("utm_source") || "",
utmMedium: sessionStorage.getItem("utm_medium") || "",
utmCampaign: sessionStorage.getItem("utm_campaign") || "",
};
}
Server-side вариант: Webflow Webhooks + Python
Для более надёжной архитектуры (особенно если нужна server-side логика):
# Webflow Webhook -> ваш Python endpoint -> HubSpot API
import requests
HUBSPOT_TOKEN = "your_private_app_token"
@app.route("/webhooks/webflow-form", methods=["POST"])
def webflow_form():
data = request.json
# data содержит поля формы из Webflow
# POST в HubSpot Contacts API с upsert по email
resp = requests.post(
"https://api.hubapi.com/crm/v3/objects/contacts/upsert",
headers={"Authorization": f"Bearer {HUBSPOT_TOKEN}"},
json={
"properties": {
"email": data.get("email"),
"firstname": data.get("firstName"),
"lastname": data.get("lastName"),
"utm_source": data.get("utm_source", ""),
"utm_campaign": data.get("utm_campaign", ""),
},
"idProperty": "email",
},
)
resp.raise_for_status()
return "", 200
Server-side преимущество: UTM передаются явно в payload, нет зависимости от браузерных cookies.
Сравнение подходов
| Подход | UTM-атрибуция | Кастомные формы | Progressive Profiling | Сложность |
|---|---|---|---|---|
| Нативная интеграция | Нет | Нет | Нет | Минимальная |
| HubSpot Forms API (JS) | Да | Да | Да (через HubSpot form settings) | Средняя |
| Server-side webhook | Да | Да | Нет | Выше |
Реальный кейс
SaaS (US, Webflow + HubSpot, 200 лидов/мес через формы):
- Проблема: 80% контактов в HubSpot — без источника. Marketing team не могла атрибутировать конверсии к кампаниям. $15k/мес на Google Ads — без ROI-данных.
- Нативная интеграция: передавала имя и email, но не UTM. HubSpot Source = «Direct Traffic» для всех форм.
- После Forms API с UTM persistence: каждый лид с
utm_source,utm_campaign. ROI по Google Ads кампаниям виден напрямую в HubSpot Reports. Выяснилось: Brand кампания (30% бюджета) давала 3x лучший close rate чем Non-Brand.
Для кого актуально
- Команды с Webflow + HubSpot где маркетинг жалуется на «Direct Traffic» в источниках
- SaaS с кастомными Webflow-формами (React, Alpine.js) которые не работают с нативной интеграцией
- Компании с EU-аудиторией где GDPR consent в форме критичен — нативная интеграция не передаёт consent данные корректно
- Маркетинг-команды с paid ads которым нужна атрибуция до сделки
Часто задаваемые вопросы
HubSpot Tracking Code — его всё равно нужно устанавливать?
Да, даже при прямом Forms API. HubSpot Tracking Code создаёт hubspotutk cookie для session stitching — связывает анонимную сессию с контактом. Без него context.hutk будет пустым и HubSpot не сможет связать просмотры страниц с отправкой формы.
HubSpot Forms API требует Private App token или Portal ID достаточно?
Forms API v3 (/submissions/v3/integration/submit/) не требует авторизации — только Portal ID и Form GUID. Это публичный endpoint (форма заполняется клиентом в браузере). Private App token нужен для Contacts API (server-side).
Webflow Logics vs кастомный JS — что лучше?
Webflow Logics (no-code автоматизации) — новый продукт Webflow. Имеет HubSpot-коннектор, но с теми же ограничениями что нативная интеграция: нет UTM, ограниченные field mapping. Для полного контроля — кастомный JS или server-side webhook.
Можно ли передать File upload из Webflow в HubSpot?
HubSpot Forms API не поддерживает file upload напрямую. Workaround: загрузить файл в HubSpot Files API (POST /filemanager/api/v3/files/upload), получить URL -> передать URL как text-поле в форму. Требует server-side прокси.
Итого
- Нативная интеграция Webflow + HubSpot: работает только для базовых форм без UTM и кастомной логики
- Правильный путь: HubSpot Forms API v3 + UTM persistence в sessionStorage
hubspotutkcookie обязательна для session stitching — устанавливать HubSpot Tracking Code- Server-side вариант (Webflow Webhook -> Python -> HubSpot Contacts API) — для надёжности и сложной бизнес-логики
- UTM-атрибуция до закрытой сделки — без правильной интеграции невозможна
Если у вас HubSpot + Webflow и лиды приходят без источника — опишите структуру форм и текущую интеграцию. Exceltic.dev настроит Forms API с полной UTM-атрибуцией.