Kommo + Basecamp: создание проектов и задач из выигранных сделок
Basecamp — инструмент управления проектами, популярный среди агентств, дизайн-студий и digital-консалтинга: To-do lists, Message Boards, Schedules, Docs & Files, Campfire (чат) в одном проекте. Проще чем Jira и Asana, без Gantt и зависимостей задач — но именно это привлекает команды, которым нужен порядок, а не функциональность ради функциональности. Без интеграции с Kommo менеджер создаёт проект вручную при каждом Won. С интеграцией Won -> проект с задачами за секунды, без ручной работы.
Basecamp vs Asana vs Trello для delivery-интеграции
| Параметр | Basecamp | Asana | Trello |
|---|---|---|---|
| Философия | Простота + коммуникация | Мощные workflow | Канбан |
| Шаблоны проектов | Да (Project Templates) | Да | Нет (через Power-Up) |
| Webhook | Да | Да | Да |
| API | REST v2 (OAuth 2.0) | REST | REST + API Key |
| Подходит для | Агентства, консалтинг | SaaS, IT | Малые команды |
Basecamp выбирают агентства с постоянными клиентами: один Basecamp-проект = один клиент, со всей историей коммуникаций, задач и документов.
Что синхронизируется
Kommo -> Basecamp:
— Won -> создать Project с именем клиента
— Won -> создать To-do list с задачами onboarding-шаблона
— Won -> добавить Description с данными сделки (тариф, сумма, менеджер)
— Won -> назначить ответственных на задачи (маппинг менеджер -> Basecamp-пользователь)
Basecamp -> Kommo:
— To-do item completed -> Note: «Basecamp: задача «{title}» выполнена»
— Project archived (через polling) -> Note: «Проект завершён»
Basecamp API: ключевые запросы
Base URL: https://3.basecampapi.com/{account_id}.
Account ID: из URL Basecamp Dashboard (app.basecamp.com/{account_id}/...).
Аутентификация: OAuth 2.0 (рекомендуется) или Personal Access Token.
User-Agent обязателен: Basecamp блокирует запросы без корректного UA.
import requests
BASECAMP_TOKEN = "your_personal_access_token" # или OAuth access token
BASECAMP_ACCOUNT_ID = "your_account_id"
BASECAMP_BASE_URL = f"https://3.basecampapi.com/{BASECAMP_ACCOUNT_ID}"
HEADERS = {
"Authorization": f"Bearer {BASECAMP_TOKEN}",
"Content-Type": "application/json",
"User-Agent": "YourApp (yourname@company.com)", # обязателен!
}
def get_project_templates() -> list:
# Получить список шаблонов проектов
resp = requests.get(
f"{BASECAMP_BASE_URL}/templates.json",
headers=HEADERS
)
resp.raise_for_status()
return resp.json()
def create_project_from_template(template_id: int, name: str,
description: str = "") -> dict:
# Создать проект из шаблона
resp = requests.post(
f"{BASECAMP_BASE_URL}/templates/{template_id}/project_constructions.json",
headers=HEADERS,
json={"project": {"name": name, "description": description}}
)
resp.raise_for_status()
return resp.json()
def create_project(name: str, description: str = "") -> dict:
# Создать проект с нуля (без шаблона)
resp = requests.post(
f"{BASECAMP_BASE_URL}/projects.json",
headers=HEADERS,
json={"name": name, "description": description}
)
resp.raise_for_status()
return resp.json()
def get_todoset(project_id: int) -> dict:
# Получить todo-list контейнер проекта
resp = requests.get(
f"{BASECAMP_BASE_URL}/buckets/{project_id}/todosets.json",
headers=HEADERS
)
resp.raise_for_status()
return resp.json()[0] # один todoset на проект
def create_todolist(project_id: int, todoset_id: int, name: str,
description: str = "") -> dict:
resp = requests.post(
f"{BASECAMP_BASE_URL}/buckets/{project_id}/todosets/{todoset_id}/todolists.json",
headers=HEADERS,
json={"name": name, "description": description}
)
resp.raise_for_status()
return resp.json()
def create_todo(project_id: int, todolist_id: int, content: str,
assignee_ids: list = None, due_on: str = None) -> dict:
payload: dict = {"content": content}
if assignee_ids:
payload["assignee_ids"] = assignee_ids
if due_on:
payload["due_on"] = due_on # "2026-06-15"
resp = requests.post(
f"{BASECAMP_BASE_URL}/buckets/{project_id}/todolists/{todolist_id}/todos.json",
headers=HEADERS,
json=payload
)
resp.raise_for_status()
return resp.json()
ONBOARDING_TODOS = [
"Вводный звонок с командой клиента",
"Передать доступы и материалы",
"Согласовать roadmap проекта",
"Kick-off встреча",
"Первый milestone review",
]
MANAGER_TO_BASECAMP = {
"alice@company.com": 1234567, # Basecamp person ID
"bob@company.com": 7654321,
}
def on_deal_won(lead: dict, contact: dict):
client_name = contact["name"]
plan = get_custom_field(lead, PLAN_FIELD_ID) or "Growth"
amount = lead.get("price", 0)
manager_email = get_manager_email(lead)
description = (
f"Клиент: {client_name}\n"
f"Тариф: {plan}\n"
f"Сумма: {amount}\n"
f"Kommo deal ID: {lead['id']}"
)
# Создать проект (из шаблона или с нуля)
if BASECAMP_TEMPLATE_ID:
construction = create_project_from_template(
template_id=BASECAMP_TEMPLATE_ID,
name=f"Проект: {client_name}",
description=description,
)
# project_construction: статус pending, poll пока не completed
project_id = poll_construction_until_done(construction["id"])
else:
project = create_project(
name=f"Проект: {client_name}",
description=description,
)
project_id = project["id"]
# Создать To-do list вручную
todoset = get_todoset(project_id)
todoset_id = todoset["id"]
todolist = create_todolist(
project_id, todoset_id,
name="Онбординг",
description="Задачи запуска проекта",
)
assignee_id = MANAGER_TO_BASECAMP.get(manager_email)
for task in ONBOARDING_TODOS:
create_todo(
project_id, todolist["id"], task,
assignee_ids=[assignee_id] if assignee_id else None,
)
update_kommo_deal(lead["id"], {"basecamp_project_id": str(project_id)})
create_kommo_note(lead["id"],
f"Basecamp: проект «Проект: {client_name}» создан (ID: {project_id})")
Обработка Basecamp Webhook:
@app.route("/webhooks/basecamp", methods=["POST"])
def basecamp_webhook():
payload = request.json
kind = payload.get("kind") # "todo_completed", "todo_uncompleted" etc.
recording = payload.get("recording", {})
bucket = payload.get("bucket", {})
project_id = str(bucket.get("id", ""))
deal_id = find_deal_by_field("basecamp_project_id", project_id)
if not deal_id:
return "", 200
if kind == "todo_completed":
title = recording.get("title", "")
creator = payload.get("creator", {}).get("name", "")
create_kommo_note(deal_id,
f"Basecamp: задача «{title}» выполнена ({creator})")
elif kind == "message_created":
# Сообщение на Message Board -> Note в сделку (по желанию)
subject = recording.get("subject", "")
create_kommo_note(deal_id,
f"Basecamp: новое сообщение в проекте - «{subject}»")
return "", 200
Регистрация Webhook в Basecamp через API:
def register_webhook(project_id: int, payload_url: str, types: list) -> dict:
resp = requests.post(
f"{BASECAMP_BASE_URL}/buckets/{project_id}/webhooks.json",
headers=HEADERS,
json={"payload_url": payload_url, "types": types}
)
resp.raise_for_status()
return resp.json()
# Зарегистрировать для нового проекта:
# register_webhook(project_id, "https://yourapp.com/webhooks/basecamp",
# ["Todo", "Message"])
Project Templates: создание из шаблона
Basecamp поддерживает Project Templates — предзаполненный проект с To-do lists, документами, расписанием. POST /templates/{id}/project_constructions.json создаёт проект асинхронно: нужно polling GET /project_constructions/{id}.json пока status != "completed".
Шаблоны: Basecamp -> Templates (в левом меню) -> создать новый. Template ID — из URL шаблона.
Реальный кейс
Дизайн-агентство (EU, 8–12 новых проектов в месяц, Kommo + Basecamp):
- До: PM вручную создавал проект в Basecamp, добавлял стандартный To-do list, приглашал клиента. Занимало 15–20 минут при каждом Won. Иногда забывал создать Basecamp-проект — клиент ждал доступ.
- После: Won -> проект из шаблона за 20 секунд. Все стандартные задачи, расписание, доступ клиента — из Template. PM получает уведомление что проект создан, а не создаёт его сам.
- Дополнительно:
todo_completedдля задачи «Приёмка и подписание акта» -> Note в Kommo + смена этапа «Проект завершён» -> триггер NPS-опроса.
Для кого актуально
- Агентства и консалтинг с Basecamp как основным PM-инструментом
- Команды с 5–20 параллельными проектами — при меньшем объёме ручная работа терпима
- Дизайн-студии и digital-агентства: клиент-ориентированные проекты, не разработка
- Компании где клиент — участник Basecamp-проекта (client access features)
Часто задаваемые вопросы
Basecamp Personal Access Token vs OAuth — что выбрать?
Personal Access Token (PAT) — проще для серверной интеграции: не истекает, не требует OAuth flow. Создать: Basecamp -> My Profile -> Access Tokens. OAuth нужен только для мультипользовательских приложений (SaaS-интеграция для разных Basecamp-аккаунтов).
Basecamp project_construction — почему статус pending?
Создание проекта из шаблона — асинхронный процесс. Basecamp копирует все структуры шаблона (todos, schedules, docs). Нужно poll GET /project_constructions/{id}.json каждые 2–3 секунды пока status == "completed". Обычно занимает 5–15 секунд.
Как пригласить клиента в Basecamp-проект через API?
POST /projects/{id}/people/users.json с массивом {"grant": [{"id": person_id}]}. Если клиента нет в Basecamp — сначала создать через POST /people.json. Для client-access (Basecamp Clientside): отдельный endpoint для внешних пользователей.
Basecamp webhook — как верифицировать запрос?
Basecamp подписывает webhook через X-Basecamp-Signature: HMAC-SHA256 с секретом, указанным при регистрации. Верификация аналогична другим платформам: hmac.new(secret, body, sha256).hexdigest().
Итого
- Basecamp API:
Bearer {token}+ обязательныйUser-Agent - Создать из шаблона:
POST /templates/{id}/project_constructions.json-> async, нужен polling - Создать todo:
POST /buckets/{project_id}/todolists/{todolist_id}/todos.json - Webhook: HMAC-SHA256 через
X-Basecamp-Signature, регистрация через API - Project Templates: мощнее ручного создания — всё за один API-вызов
Если вы используете Basecamp и Kommo и хотите автоматизировать создание проектов при Won — опишите структуру вашего шаблона. Exceltic.dev настроит интеграцию с template_construction и обработку webhook.