Drift (теперь - Salesloft Conversations) - платформа conversational marketing: чат-боты, live chat, видео, Sales meetings. Компании используют Drift для захвата лидов с сайта и квалификации через разговор. HubSpot имеет нативную интеграцию с Drift через HubSpot Marketplace. Проблема: нативная интеграция создаёт Контакт (Contact) в HubSpot при каждом новом разговоре в Drift, но не создаёт и не обновляет Сделку (Deal). История разговора (transcript, meeting booked, playbook triggered) не попадает в Deal Timeline.
Sales ops не видит контекст Drift-разговора в сделке. Когда AE перехватывает лид от Drift-бота - история переписки и результаты квалификации хранятся в Drift, а не в HubSpot Deal. Кастомная интеграция через Drift Conversation API + HubSpot Deals API закрывает этот разрыв.
Drift Playbook - сценарий чат-бота: набор вопросов и ветвлений для квалификации лида. Результат Playbook (email, company, budget, timeline) - ценные данные для CRM, которые нативная интеграция не передаёт в Deal.
Что делает нативная интеграция
Нативная HubSpot + Drift интеграция умеет:
- Создавать или обновлять Contact в HubSpot при начале разговора
- Синхронизировать email и базовые поля контакта
Нативная интеграция НЕ умеет:
- Создавать Deal при начале или завершении разговора
- Добавлять transcript разговора в Deal Timeline
- Передавать Playbook-ответы как Deal Properties
- Обновлять Deal Stage при booking meeting через Drift
- Логировать Drift meeting в Deal как активность
Правильная архитектура
Посетитель сайта -> Drift чат -> Playbook квалификации
-> Собирает: email, company, budget, timeline, intent
Drift webhook: conversation.status_updated (status = closed/won)
-> Ваш сервер
Ваш сервер
-> Drift API: GET /v2/conversations/{id} (полный transcript)
-> HubSpot Contacts API: найти/создать Contact по email
-> HubSpot Deals API: создать Deal с:
- Названием: "Drift: {company} - {date}"
- Properties: drift_conversation_id, budget, timeline, playbook_outcome
-> HubSpot Engagements API: добавить Note с transcript в Deal Timeline
-> HubSpot Associations: связать Deal -> Contact
Реализация: Drift webhook -> HubSpot Deal
import requests, os, hmac, hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
DRIFT_TOKEN = os.environ["DRIFT_API_TOKEN"]
DRIFT_WEBHOOK_SEC = os.environ["DRIFT_WEBHOOK_SECRET"]
HS_TOKEN = os.environ["HUBSPOT_PRIVATE_APP_TOKEN"]
DRIFT_BASE = "https://driftapi.com"
DRIFT_HDR = {"Authorization": f"Bearer {DRIFT_TOKEN}", "Content-Type": "application/json"}
HS_BASE = "https://api.hubapi.com"
HS_HDR = {"Authorization": f"Bearer {HS_TOKEN}", "Content-Type": "application/json"}
HS_DEAL_STAGE_NEW = os.environ.get("HS_DEAL_STAGE_ID", "appointmentscheduled")
def verify_drift_signature(body: bytes, sig_header: str) -> bool:
# Drift HMAC-SHA256 hex
computed = hmac.new(DRIFT_WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(computed, sig_header.replace("sha256=", ""))
def get_drift_conversation(conv_id: int) -> dict:
r = requests.get(f"{DRIFT_BASE}/v2/conversations/{conv_id}/messages", headers=DRIFT_HDR)
if r.status_code == 200:
return r.json().get("data", {})
return {}
def get_drift_playbook_data(conv_id: int) -> dict:
r = requests.get(f"{DRIFT_BASE}/v2/conversations/{conv_id}", headers=DRIFT_HDR)
if r.status_code != 200:
return {}
conv = r.json().get("data", {})
return conv.get("attributes", {})
def build_transcript(messages: list) -> str:
lines = []
for msg in messages:
author = msg.get("author", {})
name = author.get("name", "Bot") if author.get("type") == "agent" else "Посетитель"
body = msg.get("body", "")
if body:
lines.append(f"{name}: {body}")
return "
".join(lines)
def find_or_create_hs_contact(email: str, name: str) -> str:
r = requests.get(
f"{HS_BASE}/crm/v3/objects/contacts/search",
headers=HS_HDR,
json={"filterGroups": [{"filters": [{"propertyName": "email", "operator": "EQ", "value": email}]}]},
)
if r.status_code == 200:
results = r.json().get("results", [])
if results:
return results[0]["id"]
parts = name.split(" ", 1)
r_create = requests.post(
f"{HS_BASE}/crm/v3/objects/contacts",
headers=HS_HDR,
json={"properties": {
"email": email,
"firstname": parts[0],
"lastname": parts[1] if len(parts) > 1 else "",
}},
)
return r_create.json().get("id", "")
def create_hs_deal(contact_id: str, conv_id: int, attrs: dict) -> str:
company = attrs.get("company", "")
budget = attrs.get("budget", "")
timeline = attrs.get("timeline", "")
import time
now_ms = int(time.time() * 1000)
deal_name = f"Drift: {company} - {__import__('datetime').date.today().isoformat()}"
r = requests.post(
f"{HS_BASE}/crm/v3/objects/deals",
headers=HS_HDR,
json={
"properties": {
"dealname": deal_name,
"dealstage": HS_DEAL_STAGE_NEW,
"drift_conversation_id": str(conv_id),
"budget_range": budget,
"timeline": timeline,
"lead_source": "Drift Chat",
},
"associations": [{
"to": {"id": contact_id},
"types": [{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 3}]
}]
},
)
r.raise_for_status()
return r.json().get("id", "")
def add_note_to_deal(deal_id: str, transcript: str, conv_id: int):
import time
note_body = f"Drift разговор #{conv_id}:
{transcript[:4000]}"
requests.post(
f"{HS_BASE}/crm/v3/objects/notes",
headers=HS_HDR,
json={
"properties": {
"hs_note_body": note_body,
"hs_timestamp": str(int(time.time() * 1000)),
},
"associations": [{
"to": {"id": deal_id},
"types": [{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 214}]
}]
},
)
@app.route("/webhooks/drift", methods=["POST"])
def drift_webhook():
sig = request.headers.get("X-Drift-Signature", "")
if DRIFT_WEBHOOK_SECRET and not verify_drift_signature(request.data, sig):
return jsonify({"error": "invalid signature"}), 401
event = request.json or {}
# Drift отправляет массив событий
for ev in (event if isinstance(event, list) else [event]):
ev_type = ev.get("type", "")
if ev_type != "conversation_status_updated":
continue
data = ev.get("data", {})
conv_id = data.get("conversationId")
new_status = data.get("statusId", "")
if new_status not in ("closed", "won"):
continue
attrs = get_drift_playbook_data(conv_id)
email = attrs.get("email", "")
name = attrs.get("name", "Неизвестный")
if not email:
continue
msg_data = get_drift_conversation(conv_id)
messages = msg_data.get("messages", []) if isinstance(msg_data, dict) else []
transcript = build_transcript(messages)
contact_id = find_or_create_hs_contact(email, name)
deal_id = create_hs_deal(contact_id, conv_id, attrs)
if transcript:
add_note_to_deal(deal_id, transcript, conv_id)
return jsonify({"status": "ok"}), 200
Настройка Drift Webhooks
- Drift Dashboard -> App Settings -> Webhooks -> Add Endpoint
- URL:
https://your-server.com/webhooks/drift - Events:
conversation_status_updated - Скопировать Signing Secret (используется для
X-Drift-Signature)
Важно: Drift (теперь в составе Salesloft) - функциональность webhook может отличаться в зависимости от вашего тарифного плана и региона. Проверьте документацию вашей версии Drift/Salesloft Conversations.
Playbook-данные как HubSpot Deal Properties
Создать кастомные properties в HubSpot для хранения Drift Playbook ответов:
drift_conversation_id(Text)budget_range(Text или Dropdown)timeline(Text или Dropdown)playbook_outcome(Text): qualified / not-qualified / meeting_booked
Эти поля заполняются автоматически при создании Deal через webhook.
Реальный кейс
SaaS-компания, 300 Drift-разговоров в месяц, 40% квалифицированных. До кастомной интеграции: AE открывал Drift, находил историю разговора, вручную переносил данные в HubSpot Deal (5-7 мин на контакт). После: Deal создаётся автоматически с transcript и Playbook-ответами. AE видит полный контекст в HubSpot без переключения в Drift.
Для кого актуально
B2B SaaS и сервисные компании, использующие Drift для захвата inbound-лидов с сайта и квалификации через чат-бот. Особенно если объём Drift-разговоров >50/месяц и каждый квалифицированный лид обрабатывается AE через HubSpot.
Аналогичный антипаттерн описан для HubSpot + Zoom нативной интеграции и HubSpot + DocuSign.
Часто задаваемые вопросы
Drift переименовался в Salesloft Conversations - изменился ли API?
После приобретения Salesloft в 2023 году Drift rebrand завершился в 2024. API endpoint driftapi.com продолжает работать. Новые клиенты Salesloft могут получить доступ к Conversations через Salesloft API. Проверьте актуальную документацию для вашего аккаунта - endpoint может отличаться.
Как связать Drift meeting (Calendly внутри Drift) с HubSpot Deal?
Drift позволяет встраивать calendars (включая Drift Meetings) в чат. Webhook meeting_booked содержит contact email и meeting details. При этом событии - создать Task в HubSpot Deal (/crm/v3/objects/tasks) с деталями встречи, обновить Deal Stage на appointmentscheduled.
Нативная интеграция создаёт дублирующиеся контакты - как избежать?
В нашей кастомной реализации используется поиск Contact по email (/crm/v3/objects/contacts/search) перед созданием. Если contact существует - используется его ID. Нативная интеграция Drift создаёт контакт без проверки дублей.
Итог
Нативная HubSpot + Drift интеграция создаёт Contact, но не Deal и не привязывает transcript к сделке. Кастомная интеграция:
- Drift webhook
conversation_status_updated(status = closed/won) - Drift Conversation API: получить transcript + Playbook-ответы
- HubSpot: find or create Contact -> create Deal -> add Note с transcript
associationTypeId: 3для Deal -> Contact,214для Note -> Deal- Кастомные Deal properties:
drift_conversation_id,budget_range,timeline
Если нативная Drift интеграция теряет контекст разговоров в HubSpot - обратитесь в Exceltic.dev.