Kommo + Proposify: коммерческие предложения из карточки сделки
Proposify — платформа для создания интерактивных коммерческих предложений с электронной подписью, трекингом просмотров и REST API. Без интеграции с Kommo менеджер после демо вручную открывает Proposify, копирует имя, компанию, сумму из CRM, выбирает шаблон. С интеграцией нажатие кнопки в карточке сделки создаёт пропозал с уже заполненными переменными, отправляет клиенту — и при подписании автоматически переводит сделку в Won.
Почему нативных инструментов недостаточно
Proposify интегрируется с HubSpot, Pipedrive, Salesforce и Zoho нативно — через встроенные коннекторы в интерфейсе. Kommo в этот список не входит. Zapier предлагает базовый триггер «proposal signed», но не умеет передавать кастомные поля из Kommo как переменные пропозала и не поддерживает двустороннюю синхронизацию статусов.
Проблема глубже в архитектуре: переменные в шаблоне Proposify ({{client_name}}, {{deal_amount}}, {{plan}}) нужно заполнять на этапе создания документа — из полей конкретной сделки Kommo, включая кастомные поля. Это требует прямого вызова Proposify API с параметрами из Kommo API. Ни один no-code коннектор это не реализует корректно.
Proposify — сервис интерактивных пропозалов, где клиент может просмотреть предложение онлайн, оставить комментарий и подписать электронной подписью. Каждое действие клиента (открыл, просмотрел секцию, подписал) генерирует webhook-событие.
Что даёт связка Kommo + Proposify
Без интеграции:
— Менеджер после демо открывает Proposify вручную, копирует данные из Kommo
— Среднее время от демо до отправки пропозала — 20–40 минут
— При подписании Proposify не уведомляет Kommo — сделка обновляется вручную или забывается
— Sales Director не видит в CRM, когда клиент открыл и читал предложение
С интеграцией:
— Кнопка в карточке сделки -> пропозал создан и отправлен за 30 секунд
— Имя контакта, компания, тариф, сумма подставляются автоматически из полей сделки
— proposal.viewed -> Note в Kommo: «Клиент открыл пропозал — {дата}»
— proposal.signed -> сделка переходит в Won, задача бухгалтеру на инвойс
— proposal.declined -> задача менеджеру + кастомное поле с причиной
Что синхронизируется
Kommo -> Proposify:
— Имя и email контакта -> переменные {{client_name}}, {{client_email}}
— Название компании -> {{company_name}}
— Сумма сделки -> {{deal_amount}}
— Тариф из кастомного поля -> {{plan}}, {{billing_period}}
— ID сделки -> custom attribute для обратной трассировки
Proposify -> Kommo:
— proposal.viewed -> Note с временем просмотра
— proposal.signed -> Won + Note «Пропозал подписан» + задача на инвойс
— proposal.declined -> кастомное поле proposal_status = declined + задача менеджеру
— URL пропозала -> кастомное поле в сделке (для быстрого доступа)
Архитектура
Kommo: менеджер нажимает кнопку «Создать пропозал» (кастомная виджет-кнопка)
↓ Backend
1. GET /api/v4/leads/{id} + contacts
-> имя, email, компания, тариф, сумма
2. Proposify: POST /proposals
-> template_id + variables (client_name, deal_amount, plan...)
-> получить proposal_id + proposal_url
3. Kommo: PATCH /leads/{id}
-> кастомное поле proposal_url = ссылка на пропозал
-> кастомное поле proposal_status = sent
4. Kommo: POST /leads/{id}/notes
-> «Пропозал отправлен клиенту: {proposal_url}»
Proposify Webhook: proposal.viewed
↓ Backend
1. Из payload: proposal_id, viewed_at, contact_email
2. Найти deal_id по proposal_id (сохранён в шаге 3 выше)
3. Kommo: POST /leads/{deal_id}/notes
-> «Клиент открыл пропозал: {viewed_at}»
Proposify Webhook: proposal.signed
↓ Backend
1. Из payload: proposal_id, signed_at, signer_name
2. Найти deal_id
3. Kommo: PATCH /leads/{deal_id}
-> pipeline_id + status_id -> Won
-> proposal_status = signed
4. Kommo: POST /tasks
-> задача: «Выставить инвойс - пропозал подписан {signed_at}»
Proposify REST API: ключевые запросы
Base URL: https://app.proposify.com/api/v2/. Аутентификация: Bearer token в заголовке Authorization: Bearer {API_KEY}.
Создать пропозал из шаблона:
import requests
PROPOSIFY_API_KEY = 'your_api_key'
PROPOSIFY_BASE = 'https://app.proposify.com/api/v2'
def create_proposal(template_id: str, variables: dict,
recipient_email: str, recipient_name: str) -> dict:
headers = {
'Authorization': f'Bearer {PROPOSIFY_API_KEY}',
'Content-Type': 'application/json'
}
payload = {
'document_template_id': template_id,
'recipients': [{
'email': recipient_email,
'name': recipient_name,
'role': 'client'
}],
'variables': variables # {'{client_name}': 'John Smith', '{plan}': 'Pro'}
}
resp = requests.post(
f'{PROPOSIFY_BASE}/proposals',
json=payload,
headers=headers
)
resp.raise_for_status()
data = resp.json()
return {
'proposal_id': data['proposal']['id'],
'proposal_url': data['proposal']['url']
}
Обработка Proposify webhook:
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = 'your_webhook_secret'
def verify_proposify_signature(payload_bytes: bytes, signature: str) -> bool:
expected = hmac.new(
WEBHOOK_SECRET.encode(),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route('/webhooks/proposify', methods=['POST'])
def proposify_webhook():
signature = request.headers.get('X-Proposify-Signature', '')
if not verify_proposify_signature(request.data, signature):
return '', 401
payload = request.json
event_type = payload.get('event') # 'proposal.signed', 'proposal.viewed' и т.д.
proposal = payload.get('proposal', {})
proposal_id = proposal.get('id')
deal_id = get_deal_id_by_proposal(proposal_id) # из вашей БД или кастомного поля
if not deal_id:
return '', 200 # пропозал не от Kommo - игнорируем
if event_type == 'proposal.signed':
signed_at = proposal.get('signed_at')
# Переводим сделку в Won
move_kommo_deal_to_won(deal_id)
# Создаём Note
create_kommo_note(deal_id,
f'Пропозал Proposify подписан {signed_at}. '
f'Подписант: {proposal.get("signer_name")}')
# Задача на инвойс
create_kommo_task(deal_id, 'Пропозал подписан - выставить инвойс')
elif event_type == 'proposal.viewed':
viewed_at = proposal.get('viewed_at')
create_kommo_note(deal_id,
f'Клиент открыл пропозал Proposify: {viewed_at}')
elif event_type == 'proposal.declined':
reason = proposal.get('decline_reason', 'не указана')
update_kommo_deal(deal_id, {'proposal_status': 'declined'})
create_kommo_task(deal_id,
f'Клиент отклонил пропозал. Причина: {reason}')
return '', 200
Верификация: Proposify подписывает каждый webhook через HMAC-SHA256, передавая подпись в заголовке X-Proposify-Signature. Всегда проверяйте подпись перед обработкой — это защита от поддельных запросов.
Идемпотентность: Proposify может повторить webhook при таймауте. Сохраняйте обработанные proposal_id + event_type и пропускайте дубликаты.
Маппинг переменных Kommo -> Proposify
def build_proposify_variables(lead: dict, contact: dict) -> dict:
custom_fields = {cf['field_id']: cf.get('values', [{}])[0].get('value')
for cf in lead.get('custom_fields_values', [])}
PLAN_FIELD_ID = 123456 # ID кастомного поля «Тариф»
PERIOD_FIELD_ID = 123457 # ID поля «Период биллинга»
return {
'{client_name}': contact.get('name', ''),
'{client_email}': next(
(e['value'] for e in contact.get('custom_fields_values', [])
if e.get('field_code') == 'EMAIL'), ''),
'{company_name}': contact.get('company', {}).get('name', ''),
'{deal_amount}': str(lead.get('price', 0)),
'{plan}': custom_fields.get(PLAN_FIELD_ID, 'Pro'),
'{billing_period}': custom_fields.get(PERIOD_FIELD_ID, 'monthly'),
'{deal_id}': str(lead.get('id'))
}
Переменные в шаблоне Proposify создаются в редакторе: вставьте {{название}} в текст — и они станут доступны через API. Имена переменных должны совпадать с ключами в словаре variables.
Реальный кейс
B2B SaaS (DACH-регион, 25–30 новых квалифицированных лидов в месяц, Kommo + Proposify + Stripe):
- До: менеджер после демо тратил 30–40 минут на подготовку пропозала. Ошибки в суммах и тарифах из-за ручного копирования. При подписании Kommo обновлялся с задержкой 1–2 дня.
- После: кнопка «Отправить пропозал» в карточке -> пропозал у клиента за 30 секунд. Данные подставляются из полей сделки — ошибок нет. При подписании Kommo автоматически уходит в Won, бухгалтер получает задачу на инвойс.
- Дополнительно: Sales Director видит в таймлайне сделки, когда клиент открыл пропозал — и сколько раз. Это меняет тактику follow-up: звонить через час после первого просмотра эффективнее, чем на следующий день.
Аналогичный паттерн для PandaDoc и DocuSign — там те же принципы webhook + переменные шаблона.
Для кого актуально
- B2B-компании, которые отправляют 15+ пропозалов в месяц
- Используют Proposify как основной инструмент для коммерческих предложений
- Хотят видеть статус предложения прямо в Kommo без перехода в Proposify
- Процесс: демо -> пропозал -> подписание -> Won должен быть автоматическим
Часто задаваемые вопросы
Proposify API — это публичный или enterprise-only API?
Proposify публичный API доступен для пользователей на тарифе Business и выше. API Key создаётся в настройках аккаунта: Settings -> Integrations -> API. Аутентификация через Bearer token в заголовке запроса.
Как настроить webhook в Proposify?
Settings -> Integrations -> Webhooks -> Add Webhook URL. Выбрать события: proposal.viewed, proposal.signed, proposal.declined. Proposify подписывает payload через HMAC-SHA256 — секретный ключ задаётся там же и используется для верификации запросов на вашем сервере.
Можно ли создавать пропозалы без нажатия кнопки — автоматически при переходе на этап?
Да — через Kommo webhook на смену статуса. При переходе сделки на этап «Коммерческое предложение» backend автоматически создаёт и отправляет пропозал. Это работает если шаблон стандартный, а кастомизация минимальна.
Что если клиент хочет изменить условия после отправки?
Proposify позволяет редактировать пропозал после отправки — через API PATCH /proposals/{id}/variables. Удобнее реализовать кнопку «Обновить пропозал» в Kommo, которая обновляет переменные без пересоздания документа. История версий сохраняется.
Как связать proposal_id с deal_id для обратной трассировки?
Два варианта: передавать deal_id как переменную в пропозале ({deal_id}) — она будет в webhook payload. Или хранить в таблице {proposal_id -> deal_id} в вашей БД. Первый вариант проще при небольших объёмах.
Итого
- Proposify REST API: Bearer token, Base URL
https://app.proposify.com/api/v2/ - Создание пропозала:
POST /proposalsсdocument_template_idиvariablesиз полей Kommo - Webhook события:
proposal.signed-> Won + задача,proposal.viewed-> Note,proposal.declined-> задача с причиной - Верификация webhook: HMAC-SHA256, заголовок
X-Proposify-Signature - Идемпотентность: сохранять
proposal_id + event_typeдля защиты от дублей - Типовой срок разработки — 1–2 недели
Если вы используете Proposify и Kommo и хотите автоматизировать отправку пропозалов из воронки — опишите вашу структуру шаблонов и кастомные поля сделки. Exceltic.dev настроит маппинг переменных и обработку событий подписания.