Почему нативная интеграция не работает
Rocketlane - платформа для клиентского онбординга и project delivery. Её главное отличие от Jira или Asana - customer portal: клиент видит прогресс своего онбординга в реальном времени, может общаться с командой в контексте задач, загружать документы. Это снижает нагрузку на CSM (Customer Success Manager) и создаёт прозрачный процесс для клиента.
Нативной интеграции Rocketlane + Kommo не существует. Типичная проблема: сделка закрывается в Kommo, продажник сообщает в Slack «клиент Acme Corp подписал контракт», CSM вручную создаёт проект в Rocketlane, вводит данные клиента, добавляет участников. Проходит 2-3 дня до начала фактического онбординга. Клиент в этот период не получает никакой информации.
Связка кастомных интеграций Kommo с Rocketlane API решает эту проблему: проект создаётся автоматически в момент закрытия сделки.
Что реализуется - архитектура решения
Kommo: сделка переходит в статус Won
--> Webhook --> Python сервис
--> Rocketlane API: POST /projects (из template_id)
--> Rocketlane API: POST /projects/{id}/members (добавить клиента)
--> Kommo API: добавить Note о созданном проекте
Rocketlane: milestone.completed
--> Webhook --> Python сервис
--> Kommo API: добавить Note в сделку
Технические детали
Rocketlane API Auth. Bearer token. Получается в Rocketlane Settings -> API -> Generate Token. Передаётся в заголовке Authorization: Bearer {token}.
Rocketlane API эндпоинты:
POST /v1/projects- создать проект. Параметры:name,templateId,startDate,descriptionGET /v1/templates- список шаблонов проектовPOST /v1/projects/{id}/members- добавить участника. Параметры:email,role(customer/team)GET /v1/projects/{id}/milestones- список milestone- Webhooks: настраиваются в Rocketlane Settings -> Integrations -> Webhooks
Won-статус в Kommo. В Kommo каждая воронка имеет специальный статус «Победа» (Won). Его ID можно найти через GET /api/v4/pipelines/{pipeline_id}/statuses. Статус Won имеет тип is_final: true и type: won.
Customer Portal Rocketlane. При добавлении участника с role: customer Rocketlane автоматически отправляет email-приглашение на портал. Клиент получает персональную ссылку и видит план онбординга.
Пошаговая реализация
Шаг 1. Определение Won-статуса
import os
import requests
KOMMO_DOMAIN = os.environ["KOMMO_DOMAIN"]
KOMMO_TOKEN = os.environ["KOMMO_ACCESS_TOKEN"]
ROCKETLANE_TOKEN = os.environ["ROCKETLANE_TOKEN"]
ROCKETLANE_BASE = "https://api.rocketlane.com/api/v1"
def get_won_status_ids() -> set[int]:
"""Получаем все ID Won-статусов из всех воронок Kommo."""
url = f"https://{KOMMO_DOMAIN}/api/v4/pipelines"
headers = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
r = requests.get(url, params={"with": "statuses"}, headers=headers, timeout=10)
if not r.ok:
return set()
won_ids = set()
pipelines = r.json().get("_embedded", {}).get("pipelines", [])
for pipeline in pipelines:
statuses = pipeline.get("_embedded", {}).get("statuses", [])
for status in statuses:
if status.get("type") == 142: # 142 = Won в Kommo
won_ids.add(status["id"])
return won_ids
Шаг 2. Создание проекта Rocketlane из шаблона
from flask import Flask, request
from datetime import datetime, timedelta
app = Flask(__name__)
ROCKETLANE_TEMPLATE_ID = os.environ.get("ROCKETLANE_TEMPLATE_ID", "")
# Кэшируем Won-статусы при старте
WON_STATUS_IDS = get_won_status_ids()
def get_lead_data(lead_id: int) -> dict:
"""Получаем данные сделки включая контакты и компанию."""
url = f"https://{KOMMO_DOMAIN}/api/v4/leads/{lead_id}"
headers = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
r = requests.get(url, params={"with": "contacts,companies"}, headers=headers, timeout=10)
r.raise_for_status()
return r.json()
def get_contact_email_and_name(contact_id: int) -> tuple[str, str]:
"""Получаем email и имя контакта."""
url = f"https://{KOMMO_DOMAIN}/api/v4/contacts/{contact_id}"
headers = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
r = requests.get(url, headers=headers, timeout=10)
if not r.ok:
return "", ""
contact = r.json()
name = contact.get("name", "")
email = ""
for cf in contact.get("custom_fields_values", []) or []:
if cf.get("field_code") == "EMAIL":
email = cf["values"][0]["value"]
break
return email, name
def create_rocketlane_project(project_name: str, template_id: str) -> dict | None:
"""Создаём проект в Rocketlane из шаблона."""
url = f"{ROCKETLANE_BASE}/projects"
headers = {
"Authorization": f"Bearer {ROCKETLANE_TOKEN}",
"Content-Type": "application/json",
}
start_date = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
payload = {
"name": project_name,
"templateId": template_id,
"startDate": start_date,
"status": "active",
}
r = requests.post(url, json=payload, headers=headers, timeout=30)
if not r.ok:
print(f"Rocketlane project creation failed: {r.status_code} {r.text}")
return None
return r.json()
def add_customer_to_project(project_id: str, email: str, name: str) -> bool:
"""Добавляем клиента в проект Rocketlane (получит приглашение на portal)."""
url = f"{ROCKETLANE_BASE}/projects/{project_id}/members"
headers = {
"Authorization": f"Bearer {ROCKETLANE_TOKEN}",
"Content-Type": "application/json",
}
first, *last_parts = name.split(" ", 1)
payload = {
"email": email,
"role": "customer",
"firstName": first,
"lastName": last_parts[0] if last_parts else "",
}
r = requests.post(url, json=payload, headers=headers, timeout=10)
return r.ok
def add_kommo_note(lead_id: int, text: str):
url = f"https://{KOMMO_DOMAIN}/api/v4/leads/{lead_id}/notes"
headers = {
"Authorization": f"Bearer {KOMMO_TOKEN}",
"Content-Type": "application/json",
}
payload = [{"note_type": "common", "params": {"text": text}}]
requests.post(url, json=payload, headers=headers, timeout=10)
@app.route("/webhooks/kommo/won", methods=["POST"])
def kommo_won_webhook():
"""Обрабатываем переход сделки в Won."""
data = request.form
lead_id = int(data.get("leads[status][0][id]", 0))
new_status_id = int(data.get("leads[status][0][status_id]", 0))
if not lead_id or new_status_id not in WON_STATUS_IDS:
return {"ok": True}
lead = get_lead_data(lead_id)
lead_name = lead.get("name", f"Сделка #{lead_id}")
# Получаем данные основного контакта
contacts = lead.get("_embedded", {}).get("contacts", [])
if not contacts:
add_kommo_note(lead_id, "Rocketlane: не удалось создать проект - нет контакта в сделке")
return {"ok": True}
contact_id = contacts[0]["id"]
email, name = get_contact_email_and_name(contact_id)
if not email:
add_kommo_note(lead_id, "Rocketlane: не удалось создать проект - нет email у контакта")
return {"ok": True}
# Получаем название компании
companies = lead.get("_embedded", {}).get("companies", [])
company_name = companies[0].get("name", "") if companies else ""
project_name = f"Онбординг: {company_name or name}"
# Создаём проект
project = create_rocketlane_project(project_name, ROCKETLANE_TEMPLATE_ID)
if not project:
add_kommo_note(lead_id, "Rocketlane: ошибка при создании проекта")
return {"ok": True}
project_id = project["id"]
project_url = project.get("portalUrl", "")
# Добавляем клиента
add_customer_to_project(project_id, email, name)
# Фиксируем в Kommo
note_text = (
f"Rocketlane: проект онбординга создан\n"
f"Проект: {project_name}\n"
f"ID: {project_id}\n"
f"Portal: {project_url}\n"
f"Клиент добавлен: {email}"
)
add_kommo_note(lead_id, note_text)
# Сохраняем ID проекта в кастомное поле Kommo
ROCKETLANE_FIELD_ID = int(os.environ.get("KOMMO_ROCKETLANE_FIELD_ID", 0))
if ROCKETLANE_FIELD_ID:
patch_url = f"https://{KOMMO_DOMAIN}/api/v4/leads"
headers = {
"Authorization": f"Bearer {KOMMO_TOKEN}",
"Content-Type": "application/json",
}
requests.patch(patch_url, json=[{
"id": lead_id,
"custom_fields_values": [{
"field_id": ROCKETLANE_FIELD_ID,
"values": [{"value": project_id}]
}]
}], headers=headers, timeout=10)
return {"ok": True}
@app.route("/webhooks/rocketlane", methods=["POST"])
def rocketlane_webhook():
"""Обрабатываем события Rocketlane (milestone completed)."""
event = request.json
event_type = event.get("eventType")
if event_type != "milestone.completed":
return {"ok": True}
project_id = event.get("projectId", "")
milestone_name = event.get("milestoneName", "")
# Находим сделку по project_id из кастомного поля
lead_id = find_lead_by_rocketlane_project(project_id)
if lead_id:
add_kommo_note(
lead_id,
f"Rocketlane: milestone завершён - '{milestone_name}'"
)
return {"ok": True}
def find_lead_by_rocketlane_project(project_id: str) -> int | None:
"""Поиск сделки в Kommo по кастомному полю Rocketlane Project ID."""
ROCKETLANE_FIELD_ID = int(os.environ.get("KOMMO_ROCKETLANE_FIELD_ID", 0))
if not ROCKETLANE_FIELD_ID:
return None
url = f"https://{KOMMO_DOMAIN}/api/v4/leads"
headers = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
params = {f"filter[custom_fields][{ROCKETLANE_FIELD_ID}][]": project_id}
r = requests.get(url, params=params, headers=headers, timeout=10)
if r.ok:
leads = r.json().get("_embedded", {}).get("leads", [])
return leads[0]["id"] if leads else None
return None
if __name__ == "__main__":
app.run(port=5000)
Реальный кейс с цифрами
Для SaaS-компании с ARR от $500k и 5-10 новых клиентов в месяц автоматизация онбординга через Rocketlane критична для масштабирования CSM-команды.
До интеграции: CSM получал сигнал о новом клиенте из Slack-уведомления от продажника. Создание проекта в Rocketlane занимало 20-40 минут: заполнить данные, выбрать шаблон, добавить клиента, настроить временные рамки. При 8 новых клиентах в месяц - 3-5 часов CSM-времени только на создание проектов.
После интеграции: проект создаётся автоматически в течение 30 секунд после закрытия сделки. Клиент получает приглашение на customer portal через email от Rocketlane ещё до того, как CSM успевает открыть ноутбук. Типичный эффект - сокращение time-to-onboarding с 2-3 дней до нескольких часов.
Дополнительный эффект: CSM видит в Rocketlane все данные клиента (название компании, имя, размер сделки) уже заполненными - не нужно переносить вручную из Kommo.
Для кого подходит
Интеграция актуальна для компаний, которые:
- Используют Rocketlane для структурированного онбординга клиентов с customer portal
- Ведут воронку продаж в Kommo и хотят автоматизировать передачу клиента от sales к success
- Имеют повторяемый процесс онбординга (шаблон в Rocketlane), который применяется к каждому новому клиенту
- Работают в B2B SaaS или professional services с циклом онбординга от 2 недель
Если вы используете другие task management инструменты - например, Kommo + Asana или Kommo + ClickUp - принцип автоматизации при Won-событии аналогичен.
Часто задаваемые вопросы
Можно ли использовать разные шаблоны Rocketlane для разных тарифных планов?
Да. Добавьте кастомное поле «Тарифный план» в Kommo. В обработчике webhook читайте это поле и выбирайте соответствующий templateId из маппинга. Например: Plan_Basic -> template_001, Plan_Pro -> template_002.
Что если сделка «Won» создана задним числом и онбординг уже начался?
Рекомендуется добавить проверку на дубли: перед созданием проекта проверяйте кастомное поле rocketlane_project_id в сделке. Если оно заполнено - проект уже создан, пропускаем.
Можно ли добавить нескольких контактов клиента в Rocketlane?
Да. Kommo позволяет привязывать несколько контактов к сделке. В обработчике пройдитесь по всем контактам с типом «клиент» и вызовите add_customer_to_project для каждого.
Нужна ли обратная синхронизация всех задач Rocketlane в Kommo? Не рекомендуется - это создаёт шум в Kommo. Достаточно синхронизировать ключевые milestone (завершение onboarding, первый успех клиента). Мелкие задачи Rocketlane не нужны в CRM.
Если вам нужна интеграция Kommo с Rocketlane - опишите ваш стек и сценарий команде Exceltic.dev. Разберём архитектуру за одну встречу.