Kommo + Teamwork: создание проектов для агентств из выигранных сделок
Teamwork — PM-платформа, созданная специально для агентств и консалтинга: проекты, задачи, milestones, time tracking с биллингом клиентам, ресурсное планирование, client portal. В отличие от Asana или Monday.com, Teamwork имеет встроенный тайм-трекинг с hourly billing — критично для агентств работающих по Time & Materials модели. Без интеграции с Kommo создание проекта при Won — ручной процесс. С интеграцией Won -> проект из шаблона с задачами за секунды.
Teamwork vs Asana vs Basecamp для agency delivery
| Параметр | Teamwork | Asana | Basecamp |
|---|---|---|---|
| Time tracking + billing | Нативный, billable/non-billable | Через интеграцию | Нет |
| Client portal | Да | Нет | Ограниченно |
| Шаблоны проектов | Да | Да | Да |
| Resource planning | Да | Да (Business+) | Нет |
| Ретроспективы | Через Reports | Нет | Нет |
| Подходит для | Агентства T&M, консалтинг | Все типы | Простые проекты |
Teamwork выбирают агентства где каждый час — биллируемый, и клиент видит прогресс через client portal.
Что синхронизируется
Kommo -> Teamwork:
— Won -> создать Project из шаблона с данными клиента
— Won -> добавить Description с данными сделки (тариф, сумма, менеджер)
— Won -> добавить клиента как Company в Teamwork
— Won -> назначить задачи на команду через People маппинг
Teamwork -> Kommo:
— task.completed milestone-задачи -> Note в сделку
— project.completed -> Note + смена этапа «Проект завершён»
— Logged time достигает бюджета -> Note: «80% бюджета использовано»
Teamwork API: ключевые запросы
Base URL: https://{yourdomain}.teamwork.com/projects/api/v3.
Аутентификация: Basic Auth — API key как username, любой пароль.
API Key: Teamwork -> Profile Settings -> API Keys.
import requests
from requests.auth import HTTPBasicAuth
TW_API_KEY = "your_api_key"
TW_DOMAIN = "yourcompany" # yourcompany.teamwork.com
TW_BASE_URL = f"https://{TW_DOMAIN}.teamwork.com/projects/api/v3"
TW_AUTH = HTTPBasicAuth(TW_API_KEY, "x") # пароль любой
def get_project_templates() -> list:
resp = requests.get(
f"{TW_BASE_URL}/projecttemplates.json",
auth=TW_AUTH,
)
resp.raise_for_status()
return resp.json().get("projecttemplates", [])
def create_project_from_template(template_id: int, name: str,
description: str = "") -> dict:
payload = {
"project": {
"name": name,
"description": description,
}
}
resp = requests.post(
f"{TW_BASE_URL}/projecttemplates/{template_id}/projects.json",
auth=TW_AUTH,
json=payload,
)
resp.raise_for_status()
return resp.json().get("project", {})
def create_project(name: str, description: str = "") -> dict:
payload = {
"project": {
"name": name,
"description": description,
}
}
resp = requests.post(
f"{TW_BASE_URL}/projects.json",
auth=TW_AUTH,
json=payload,
)
resp.raise_for_status()
return resp.json().get("project", {})
def get_tasklists(project_id: int) -> list:
resp = requests.get(
f"{TW_BASE_URL}/projects/{project_id}/tasklists.json",
auth=TW_AUTH,
)
resp.raise_for_status()
return resp.json().get("tasklists", [])
def create_tasklist(project_id: int, name: str) -> dict:
payload = {"tasklist": {"name": name}}
resp = requests.post(
f"{TW_BASE_URL}/projects/{project_id}/tasklists.json",
auth=TW_AUTH,
json=payload,
)
resp.raise_for_status()
return resp.json().get("tasklist", {})
def create_task(project_id: int, tasklist_id: int, content: str,
assignee_id: int = None, due_date: str = None) -> dict:
# due_date: "YYYYMMDD"
payload: dict = {
"todo-item": {
"content": content,
}
}
if assignee_id:
payload["todo-item"]["responsible-party-id"] = str(assignee_id)
if due_date:
payload["todo-item"]["due-date"] = due_date
resp = requests.post(
f"{TW_BASE_URL}/tasklists/{tasklist_id}/tasks.json",
auth=TW_AUTH,
json=payload,
)
resp.raise_for_status()
return resp.json().get("todo-item", {})
ONBOARDING_TASKS = [
"Kick-off звонок с командой клиента",
"Сбор требований и бриф",
"Согласование roadmap",
"Первый delivery review",
"Финальная приёмка и закрытие",
]
MANAGER_TO_TW = {
"alice@company.com": 100001,
"bob@company.com": 100002,
}
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: {lead['id']}"
)
project_name = f"{client_name} - {plan}"
if TW_TEMPLATE_ID:
project = create_project_from_template(TW_TEMPLATE_ID, project_name, description)
else:
project = create_project(project_name, description)
project_id = project["id"]
tasklists = get_tasklists(project_id)
if not tasklists:
tl = create_tasklist(project_id, "Онбординг")
tasklist_id = tl["id"]
else:
tasklist_id = tasklists[0]["id"]
assignee_id = MANAGER_TO_TW.get(manager_email)
for task_name in ONBOARDING_TASKS:
create_task(project_id, tasklist_id, task_name, assignee_id)
update_kommo_deal(lead["id"], {"teamwork_project_id": str(project_id)})
create_kommo_note(lead["id"],
f"Teamwork: проект «{project_name}» создан (ID: {project_id})")
Webhook Teamwork:
@app.route("/webhooks/teamwork", methods=["POST"])
def teamwork_webhook():
payload = request.json
event = payload.get("eventName")
project = payload.get("project", {})
project_id = str(project.get("id", ""))
deal_id = find_deal_by_field("teamwork_project_id", project_id)
if not deal_id:
return "", 200
if event == "TASK.COMPLETED":
task_name = payload.get("task", {}).get("name", "")
create_kommo_note(deal_id,
f"Teamwork: задача «{task_name}» выполнена")
elif event == "PROJECT.COMPLETED":
update_kommo_deal(deal_id, {"stage_id": STAGE_PROJECT_DONE})
create_kommo_note(deal_id, "Teamwork: проект завершён")
return "", 200
Time Tracking: биллинговый цикл через Kommo
Для агентств на T&M-модели Teamwork даёт возможность видеть сколько часов потрачено на клиента напрямую из его сделки в Kommo. Polling API GET /projects/{id}/time.json каждый день:
- Если billable_hours >= budget_hours × 0.8 -> Note в Kommo: «80% бюджета проекта использовано»
- Менеджер видит это в карточке и инициирует разговор о расширении scope
Это не автоматизируется нативно — нужна кастомная логика, но polling Teamwork Time API + Kommo Note — стандартная задача для такой интеграции.
Реальный кейс
Digital-агентство (Ирландия, Teamwork + Kommo, 15–20 проектов/мес):
- До: project manager создавал Teamwork-проект вручную при каждом Won. 25–30 минут на стандартный онбординг. Иногда забывал назначить задачи на конкретных людей -> задачи зависали без ответственного.
- После: Won -> проект из шаблона с 5 задачами за 20 секунд. Ответственные назначены автоматически по маппингу менеджер -> Teamwork ID. PM получает уведомление от Teamwork, не создаёт проект сам.
- Дополнительно:
PROJECT.COMPLETED-> смена этапа в Kommo -> триггер NPS-опроса через GetResponse.
Для кого актуально
- Агентства и консалтинг с Teamwork как основным PM-инструментом
- Компании на Time & Materials с биллингом часов клиентам
- Команды 5–30 человек с 10–30 активными клиентами параллельно
- Агентства где client portal важен — клиент видит прогресс в Teamwork
Часто задаваемые вопросы
Teamwork API v1 vs v3 — какую использовать?
v3 — актуальная версия с REST-архитектурой. v1 (устаревшая) использует другую нотацию и не развивается. Для новых интеграций: всегда v3 на /{domain}.teamwork.com/projects/api/v3.
Teamwork шаблоны проектов — как создать?
Teamwork -> More -> Templates -> New Template. Добавьте tasklists и задачи которые нужны при каждом новом проекте. Template ID — из URL при редактировании: /templates/{id}/edit. Через API: GET /projecttemplates.json.
Как Teamwork авторизует webhook?
Teamwork подписывает webhook через HMAC-SHA256. В настройках webhook — поле Webhook Secret. Верификация: hmac.new(secret, payload, sha256).hexdigest() сравнить с заголовком X-Webhook-Signature.
Teamwork Client Portal — как настроить доступ для клиента?
Teamwork -> проект -> People -> Add Client. Клиент получает email-приглашение. В portal клиент видит только разрешённые tasklists и задачи (не всё). Для Kommo-интеграции: при создании проекта через API можно сразу добавить клиентский email через POST /projects/{id}/people.json.
Итого
- Teamwork API: Basic Auth (api_key + «x»),
/{domain}.teamwork.com/projects/api/v3 - Создать из шаблона:
POST /projecttemplates/{id}/projects.json - Задачи:
POST /tasklists/{id}/tasks.jsonс responsible-party-id - Webhook:
TASK.COMPLETED,PROJECT.COMPLETED, HMAC-SHA256 - Time tracking: polling
GET /projects/{id}/time.json-> Note при приближении к бюджету
Если вы используете Teamwork и Kommo и хотите автоматизировать создание проектов при Won — опишите структуру шаблонов и маппинг менеджеров. Exceltic.dev настроит интеграцию.