Kommo + Better Proposals: интерактивные предложения из карточки сделки с трекингом
Better Proposals — платформа для создания интерактивных веб-предложений (не PDF): потенциальный клиент открывает ссылку в браузере, видит красиво оформленный документ, может подписать электронно и оплатить прямо на странице. В отличие от PandaDoc или DocuSign, Better Proposals специализируется именно на proposal-стадии воронки — с детальным трекингом просмотров, heat maps и A/B тестами шаблонов. Без интеграции с Kommo: каждое предложение создаётся вручную, данные о просмотрах не попадают в CRM, момент подписания менеджер узнаёт из email.
Better Proposals vs PandaDoc vs Qwilr
| Параметр | Better Proposals | PandaDoc | Qwilr |
|---|---|---|---|
| Формат | Веб-страница | Веб + PDF | Веб-страница |
| Трекинг просмотров | Детальный (scroll, time per section) | Базовый | Базовый |
| Электронная подпись | Да | Да (eSign) | Да |
| Встроенный платёж | Да (Stripe) | Нет | Да |
| Шаблоны | 200+ | 750+ | 60+ |
| Цена | от $19/мес | от $35/мес | от $35/мес |
Better Proposals выбирают агентства и консалтинговые компании, где визуальное качество предложения влияет на close rate, а трекинг «когда клиент читал» — критичен для тайминга follow-up.
Что синхронизируется
Kommo -> Better Proposals:
— Won (или кастомный этап) -> создать предложение из шаблона, подставить данные контакта и сделки
— Изменение суммы в Kommo -> обновить цену в черновике предложения
Better Proposals -> Kommo:
— proposal.opened -> Note: «Предложение открыто клиентом» + timestamp
— proposal.signed -> Note: «Подписано» + смена статуса сделки или этапа воронки
— proposal.paid -> Note: «Оплата получена через страницу предложения»
— proposal.expired -> Task: «Предложение истекло — follow-up»
Better Proposals API: создание предложения
Base URL: https://api.betterproposals.io/v1. Аутентификация: заголовок Authorization: Bearer {api_token} (токен из Better Proposals -> Settings -> Integrations -> API).
import requests
BP_TOKEN = "your_bearer_token"
BP_BASE = "https://api.betterproposals.io/v1"
BP_HEADERS = {
"Authorization": f"Bearer {BP_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
}
def create_proposal(template_id: int, contact_name: str, contact_email: str,
company: str, deal_value: float, currency: str = "USD") -> dict:
# template_id из Better Proposals -> Templates -> Edit -> URL
payload = {
"template_id": template_id,
"name": f"Proposal for {company}",
"contacts": [
{
"name": contact_name,
"email": contact_email,
"company": company,
}
],
"variables": {
"company_name": company,
"deal_value": f"{deal_value:,.2f} {currency}",
},
"expiry_date": None,
"currency": currency,
"price": deal_value,
}
resp = requests.post(f"{BP_BASE}/proposals", headers=BP_HEADERS, json=payload)
resp.raise_for_status()
return resp.json()
def get_proposal_link(proposal_id: int) -> str:
resp = requests.get(f"{BP_BASE}/proposals/{proposal_id}", headers=BP_HEADERS)
resp.raise_for_status()
data = resp.json()
return data.get("url", "")
# Маппинг шаблонов Kommo -> Better Proposals
TEMPLATE_MAP = {
"consulting": 12345,
"saas": 12346,
"retainer": 12347,
}
def on_deal_reached_proposal_stage(lead: dict, contact: dict):
# Вызывается когда сделка переходит в этап "Proposal"
company = get_custom_field(lead, COMPANY_FIELD_ID) or contact.get("name", "")
email = get_contact_email(contact)
deal_type = get_custom_field(lead, DEAL_TYPE_FIELD_ID) or "saas"
template_id = TEMPLATE_MAP.get(deal_type.lower(), TEMPLATE_MAP["saas"])
proposal = create_proposal(
template_id = template_id,
contact_name = contact.get("name", ""),
contact_email = email,
company = company,
deal_value = lead.get("price", 0),
)
proposal_id = proposal.get("id")
proposal_url = get_proposal_link(proposal_id)
save_to_kommo_deal(lead["id"], {
"bp_proposal_id": proposal_id,
"bp_proposal_url": proposal_url,
})
create_kommo_note(
lead["id"],
f"Better Proposals: предложение создано -> {proposal_url}",
)
Webhooks: Better Proposals -> Kommo
Better Proposals поддерживает webhooks: Settings -> Webhooks -> Add Webhook. Payload подписывается HMAC-SHA256 заголовком X-BP-Signature.
import hmac, hashlib
BP_WEBHOOK_SECRET = "your_webhook_secret"
@app.route("/webhooks/better-proposals", methods=["POST"])
def bp_webhook():
sig = request.headers.get("X-BP-Signature", "")
expected = hmac.new(
BP_WEBHOOK_SECRET.encode(),
request.data,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(sig, expected):
return "", 401
payload = request.json
event = payload.get("event")
proposal_id = payload.get("proposal", {}).get("id")
lead_id = find_kommo_deal_by_custom_field("bp_proposal_id", str(proposal_id))
if not lead_id:
return "", 200
if event == "proposal.opened":
opened_at = payload.get("proposal", {}).get("opened_at", "")
create_kommo_note(lead_id, f"Better Proposals: предложение открыто клиентом ({opened_at})")
elif event == "proposal.signed":
signed_at = payload.get("proposal", {}).get("signed_at", "")
create_kommo_note(lead_id,
f"Better Proposals: предложение подписано ({signed_at}) - ожидаем оплату")
move_kommo_deal_to_stage(lead_id, SIGNED_STAGE_ID)
elif event == "proposal.paid":
amount = payload.get("proposal", {}).get("price", 0)
create_kommo_note(lead_id,
f"Better Proposals: оплата получена - ${amount:.2f}")
move_kommo_deal_to_stage(lead_id, WON_STAGE_ID)
elif event == "proposal.expired":
create_kommo_note(lead_id, "Better Proposals: срок предложения истёк")
create_kommo_task(lead_id, "Better Proposals: follow-up - выслать обновлённое предложение")
return "", 200
Трекинг просмотров: как использовать данные
Better Proposals фиксирует: время открытия каждого раздела, сколько секунд клиент провёл на каждой секции, с какого устройства. Через API можно получить эти данные:
def get_proposal_analytics(proposal_id: int) -> dict:
resp = requests.get(
f"{BP_BASE}/proposals/{proposal_id}/analytics",
headers=BP_HEADERS,
)
resp.raise_for_status()
return resp.json()
def sync_proposal_analytics_to_kommo(lead_id: int, proposal_id: int):
analytics = get_proposal_analytics(proposal_id)
views = analytics.get("views", 0)
avg_time = analytics.get("average_time_spent", 0)
note = (
f"Better Proposals analytics: просмотров {views}, "
f"среднее время на предложении {avg_time} сек."
)
create_kommo_note(lead_id, note)
Эти данные помогают менеджеру выбрать момент follow-up: если клиент провёл на странице цены 3 минуты — самое время позвонить.
Реальный кейс
Digital-агентство (EU, 25 человек, Kommo + Better Proposals):
- До: менеджер вручную копировал данные из Kommo в Better Proposals. 15–20 минут на каждый proposal. Уведомление о подписании — email, которое иногда проходило мимо внимания. Proposal stage -> Won задерживался на 2–3 дня.
- После: переход в этап «Proposal» в воронке -> автоматически создаётся предложение из шаблона (по типу сделки) -> ссылка сохраняется в Kommo.
proposal.opened-> Note.proposal.signed-> автоматическая смена этапа на «Won». - Результат: время от «Proposal» до «Won» сократилось с 4.2 дня до 1.8 дня — менеджеры видели в реальном времени когда клиент изучал предложение и звонили в правильный момент.
Для кого актуально
- Агентства (digital, consulting, marketing) где proposal — ключевой этап воронки
- B2B с несколькими стандартными типами предложений (шаблоны по service line)
- Компании где важен тайминг follow-up — трекинг просмотров даёт правильный момент
- Команды где proposal стадия занимает > 3 дней — автоматизация ускоряет цикл
Часто задаваемые вопросы
Better Proposals поддерживает переменные для автозаполнения данных сделки?
Да. В шаблоне Better Proposals можно создавать переменные ({company_name}, {deal_value}, {manager_name}) — они заполняются при создании предложения через API через поле variables. Стандартные поля контакта (имя, email, компания) заполняются автоматически из contacts объекта в payload.
Можно ли обновить цену в уже отправленном предложении?
Да, через PATCH /proposals/{id} с новым price — но только если предложение не подписано. После подписания предложение заблокировано. Если сумма изменилась после отправки — создаётся новое предложение (POST /proposals) с пометкой «Revised» в названии, старое закрывается через DELETE /proposals/{id}.
Как настроить разные шаблоны для разных типов сделок?
В Kommo добавить кастомное поле «Тип услуги» (select). При переходе в этап Proposal — webhook передаёт значение поля -> Python-обработчик смотрит TEMPLATE_MAP -> выбирает нужный template_id Better Proposals. Шаблоны создаются в Better Proposals один раз командой, далее используются API-автоматически.
Better Proposals vs Qwilr — в чём разница для интеграции с Kommo?
API-уровень аналогичен: Bearer token, CRUD proposals, webhooks. Главное отличие: Better Proposals даёт более детальную аналитику просмотров (секция-per-секция), Qwilr — более гибкий дизайн. Для интеграции с Kommo архитектура одинакова. Выбор платформы — вопрос UX для менеджеров и дизайна proposals, не технической стороны.
Итого
- API: Bearer token,
Authorization: Bearer {token}, base URLhttps://api.betterproposals.io/v1 - Поток: этап «Proposal» в Kommo ->
POST /proposalsс template_id + данные контакта - Webhook:
proposal.opened/signed/paid/expired-> Note/Task/смена этапа в Kommo - Аналитика просмотров:
GET /proposals/{id}/analytics-> Note с engagement данными - Триггер не обязательно Won — можно любой этап воронки где начинается proposal-работа
Если у вас агентство или B2B с proposal-стадией воронки и вы хотите автоматизировать создание предложений из Kommo — опишите структуру шаблонов и типы сделок. Exceltic.dev настроит двустороннюю интеграцию с трекингом.