HubSpot и Loom имеют нативную интеграцию: вставка Loom-видео в HubSpot Email Sequences, отслеживание кликов. Команды продаж используют это для video prospecting: записал персональное видео - отправил через Sequence - видел что клиент посмотрел. Проблема: стандартная интеграция фиксирует только клик по превью в email, не сам факт просмотра видео. И самое важное - ни клик, ни просмотр не появляется в Deal Timeline. Активность видна в Contact Activity, но не привязана к сделке.
Это архитектурный ограничение нативной интеграции: Loom для HubSpot - плагин Chrome для вставки видео в письма, не двусторонняя API-интеграция. Loom не отправляет события в HubSpot при каждом просмотре видео.
Что теряет команда продаж
- Менеджер не видит в карточке сделки: кто из команды клиента смотрел видео, когда, сколько раз
- RevOps не может построить отчёт: сделки, где был просмотр Loom, конвертируются лучше?
- Нет trigger для follow-up: “клиент посмотрел видео -> поставить задачу позвонить”
Это не “nice to have” - для команд активно использующих video selling (Loom в каждом touch) потеря контекста в Deal ломает весь workflow.
Почему нативная интеграция работает именно так
Loom нативная интеграция с HubSpot - это HubSpot Sales Extension: вставляет превью видео в тело письма через HubSpot Email editor. При клике по превью HubSpot фиксирует Email Click event. Это стандартная механика Email tracking - не специфика Loom.
Loom не уведомляет HubSpot о просмотрах на своей платформе. Данные о просмотрах (viewer email, watch percentage, rewatch) хранятся в Loom Analytics и недоступны HubSpot в реальном времени.
Правильный подход: Loom Webhook -> HubSpot Engagement
Loom предоставляет webhooks для событий: video.viewed, video.completed, video.shared. При настройке webhook Loom отправляет данные о просмотре на ваш endpoint - включая email зрителя (если известен) и video_id.
Loom webhook: video.viewed
{viewer_email, video_id, watch_time_percentage, timestamp}
-> Ваш сервер
Ваш сервер
-> HubSpot API: найти Contact по viewer_email
-> HubSpot API: найти открытую Deal для этого Contact
-> HubSpot API: создать Engagement (Note) в Deal Timeline
"Loom просмотрен: {video_title}, {watch_pct}%, {duration}s"
Реализация
import requests, os, hmac, hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
HS_TOKEN = os.environ["HUBSPOT_ACCESS_TOKEN"]
LOOM_SECRET = os.environ["LOOM_WEBHOOK_SECRET"]
HS_BASE = "https://api.hubapi.com"
HS_HDR = {"Authorization": f"Bearer {HS_TOKEN}", "Content-Type": "application/json"}
def verify_loom_sig(body: bytes, sig: str) -> bool:
if not LOOM_SECRET:
return True
expected = hmac.new(LOOM_SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", sig)
def find_contact_id(email: str) -> str | None:
r = requests.post(
f"{HS_BASE}/crm/v3/objects/contacts/search",
headers=HS_HDR,
json={
"filterGroups": [{"filters": [{
"propertyName": "email",
"operator": "EQ",
"value": email,
}]}],
"properties": ["email"],
"limit": 1,
},
)
results = r.json().get("results", []) or []
return results[0]["id"] if results else None
def find_deal_for_contact(contact_id: str) -> str | None:
r = requests.get(
f"{HS_BASE}/crm/v3/objects/contacts/{contact_id}/associations/deals",
headers=HS_HDR,
)
results = r.json().get("results", []) or []
if not results:
return None
deal_id = results[0]["id"]
# Проверить что сделка открытая
rd = requests.get(
f"{HS_BASE}/crm/v3/objects/deals/{deal_id}",
headers=HS_HDR,
params={"properties": "dealstage,closedate"},
)
stage = rd.json().get("properties", {}).get("dealstage", "")
closed_stages = {"closedwon", "closedlost"}
if stage in closed_stages:
return None
return deal_id
def create_note_in_deal(deal_id: str, note_text: str):
# Шаг 1: создать Engagement (Note)
r = requests.post(
f"{HS_BASE}/crm/v3/objects/notes",
headers=HS_HDR,
json={
"properties": {
"hs_note_body": note_text,
"hs_timestamp": str(int(__import__("time").time() * 1000)),
},
},
)
r.raise_for_status()
note_id = r.json().get("id", "")
# Шаг 2: привязать Note к Deal
requests.put(
f"{HS_BASE}/crm/v4/objects/notes/{note_id}/associations/deals/{deal_id}",
headers=HS_HDR,
json=[{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 214}],
)
@app.route("/webhooks/loom", methods=["POST"])
def loom_webhook():
sig = request.headers.get("X-Loom-Signature", "")
if not verify_loom_sig(request.data, sig):
return jsonify({"error": "invalid signature"}), 401
event = request.json or {}
ev_type = event.get("type", "")
if ev_type not in ("video.viewed", "video.completed"):
return jsonify({"status": "ignored"}), 200
data = event.get("data", {})
viewer_email = data.get("viewer_email", "")
video_title = data.get("video_title", "Loom видео")
watch_pct = data.get("watch_percentage", 0)
duration = data.get("duration_seconds", 0)
video_url = data.get("video_url", "")
if not viewer_email:
return jsonify({"status": "no_email"}), 200
contact_id = find_contact_id(viewer_email)
if not contact_id:
return jsonify({"status": "contact_not_found"}), 200
deal_id = find_deal_for_contact(contact_id)
if not deal_id:
return jsonify({"status": "no_open_deal"}), 200
pct_str = f"{watch_pct:.0f}%" if watch_pct else "неизвестно"
note = (
f"Loom: {viewer_email} просмотрел видео '{video_title}' "
f"({pct_str}, {duration}s). "
f"{video_url}"
)
create_note_in_deal(deal_id, note)
return jsonify({"status": "ok"}), 200
Настройка Loom Webhook
Loom Dashboard -> Settings -> Webhooks. Доступно на Loom Business и Enterprise планах. События: video.viewed, video.completed. URL: ваш endpoint. Secret: используется в X-Loom-Signature.
Loom отправляет webhook при каждом просмотре, включая повторные. Если клиент посмотрел видео 3 раза - 3 события. В note_text можно добавить (пересмотр) если watch_percentage был уже близок к 100% в предыдущем событии (нужна дополнительная логика дедупликации).
Trigger для follow-up задачи
def create_followup_task(contact_id: str, deal_id: str, viewer_email: str):
r = requests.post(
f"{HS_BASE}/crm/v3/objects/tasks",
headers=HS_HDR,
json={
"properties": {
"hs_task_body": f"Follow up: {viewer_email} посмотрел Loom видео. Время позвонить.",
"hs_task_subject": "Позвонить после просмотра Loom",
"hs_task_status": "NOT_STARTED",
"hs_task_type": "CALL",
"hs_timestamp": str(int(__import__("time").time() * 1000) + 3600000), # +1h
},
},
)
task_id = r.json().get("id", "")
if task_id and deal_id:
requests.put(
f"{HS_BASE}/crm/v4/objects/tasks/{task_id}/associations/deals/{deal_id}",
headers=HS_HDR,
json=[{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 216}],
)
Для кого актуально
Sales-команды HubSpot, активно использующие Loom для personalized video prospecting. Особенно если video selling - основной touchpoint в email sequences: нужна история просмотров в Deal Timeline для оценки вовлечённости и построения follow-up задач.
Аналогичный антипаттерн разобран для HubSpot + Drift.
Часто задаваемые вопросы
Передаёт ли Loom email зрителя в webhook всегда?
Нет. viewer_email присутствует только если зритель авторизован в Loom при просмотре, или если ссылка открыта из email-клиента с tracking. Если видео встроено на сайте или открыто анонимно - email будет пустым. Для prospecting (персональные ссылки в emails) зритель обычно идентифицируется.
Как работать с несколькими открытыми сделками для одного контакта?
find_deal_for_contact в примере берёт первую. Для точной привязки: в Loom webhook добавьте кастомный параметр hs_deal_id в URL видео (через Loom Custom Link Parameters, Enterprise план). Или привяжите видео к конкретной сделке через кастомное поле при отправке.
Нужен ли HubSpot Enterprise для этой интеграции?
Нет. HubSpot Notes API (создание Note + привязка к Deal) работает на всех paid планах HubSpot CRM (Starter и выше). Loom webhook доступен на Business и Enterprise планах Loom ($12.50/user/mo и выше).
Итог
HubSpot + Loom - видео-события в Deal Timeline:
- Нативная интеграция: только Email Click, не просмотр видео
- Loom webhook
video.viewed-> find Contact by email -> find open Deal -> Note - Note в Deal через CRM v3:
POST /crm/v3/objects/notes+PUT associations/deals associationTypeId: 214для Note -> Deal- Опционально:
CREATE TASKчерезassociationTypeId: 216Task -> Deal для follow-up
Если нужна интеграция Loom с HubSpot Deal Timeline - опишите задачу команде Exceltic.dev.