Kommo + Oneflow: автоматическая отправка контрактов из воронки продаж

Kommo + Oneflow: автоматическая отправка контрактов из воронки продаж

Oneflow — шведская платформа управления жизненным циклом контрактов (CLM): создание, согласование, электронная подпись, хранение. В отличие от PandaDoc или DocuSign, Oneflow специализируется на интерактивных HTML-контрактах (не PDF) — клиент может редактировать отдельные поля прямо в браузере до подписания. GDPR-compliant, данные в EU. Без интеграции с Kommo менеджер создаёт контракт вручную. С интеграцией Won -> контракт с заполненными данными из сделки уходит клиенту за секунды.

Oneflow vs PandaDoc vs Docuseal для EU-команд

ПлатформаФорматХранение данныхEU-фокусAPI
OneflowHTML-контрактыШвеция / EUДа, шведский вендорREST + webhooks
PandaDocPDF + rich contentUS (AWS)ЧастичноREST
DocusealPDFSelf-hosted или EU cloudДа (self-hosted)REST
DocuSignPDFUSНет EU-cloudREST

Oneflow выбирают EU-команды с требованиями к data residency и где контракты — не просто форма для подписи, а инструмент переговоров (встроенный чат, redlining, версии).

Что синхронизируется

Kommo -> Oneflow:
— Won -> создать контракт из шаблона с данными из сделки (имя, email, компания, сумма, тариф)
— Won -> установить участников (клиент -> внутренний подписант)
— Won -> отправить контракт на подписание

Oneflow -> Kommo:
contract.signed (все подписали) -> Note + смена этапа «Контракт подписан»
contract.participant_signed (один из участников подписал) -> Note
contract.declined -> Note + задача: «Клиент отклонил контракт»
contract.expired -> Note + задача: «Срок подписания истёк»

Oneflow API: ключевые запросы

Base URL: https://api.oneflow.com/v1.
Аутентификация: x-oneflow-api-token: {api_token} (персональный токен из настроек).
Дополнительно: x-oneflow-user-email: {email} — от имени какого пользователя действуем.

import requests

ONEFLOW_TOKEN = "your_api_token"
ONEFLOW_USER_EMAIL = "sales@yourcompany.com"
ONEFLOW_BASE_URL = "https://api.oneflow.com/v1"

HEADERS = {
    "x-oneflow-api-token": ONEFLOW_TOKEN,
    "x-oneflow-user-email": ONEFLOW_USER_EMAIL,
    "Content-Type": "application/json",
}

def get_templates(workspace_id: str) -> list:
    resp = requests.get(
        f"{ONEFLOW_BASE_URL}/templates",
        headers=HEADERS,
        params={"workspace_id": workspace_id}
    )
    resp.raise_for_status()
    return resp.json().get("data", [])

def create_contract(template_id: str, workspace_id: str,
                    name: str, parties: list) -> dict:
    # parties: список участников-компаний/физлиц с ролями
    payload = {
        "template_id": template_id,
        "workspace_id": workspace_id,
        "name": name,
        "parties": parties,
    }
    resp = requests.post(
        f"{ONEFLOW_BASE_URL}/contracts",
        headers=HEADERS,
        json=payload
    )
    resp.raise_for_status()
    return resp.json()

def update_contract_data_fields(contract_id: str, data_fields: list) -> None:
    # Заполнить кастомные поля контракта (тариф, сумма, дата)
    for field in data_fields:
        resp = requests.patch(
            f"{ONEFLOW_BASE_URL}/contracts/{contract_id}/data_fields/{field['id']}",
            headers=HEADERS,
            json={"value": field["value"]}
        )
        resp.raise_for_status()

def publish_contract(contract_id: str) -> dict:
    # Отправить контракт участникам для подписания
    resp = requests.post(
        f"{ONEFLOW_BASE_URL}/contracts/{contract_id}/publish",
        headers=HEADERS,
        json={}
    )
    resp.raise_for_status()
    return resp.json()

def on_deal_won(lead: dict, contact: dict):
    email = get_contact_email(contact)
    name = contact["name"]
    company = get_custom_field(lead, COMPANY_FIELD_ID) or name
    plan = get_custom_field(lead, PLAN_FIELD_ID) or "Growth"
    amount = lead.get("price", 0)
    from datetime import datetime
    today = datetime.now().strftime("%d.%m.%Y")

    # Структура участников: клиент + наша компания
    parties = [
        {
            "type": "company",
            "name": company,
            "participants": [{
                "name": name,
                "email": email,
                "signatory": True,
            }]
        },
        {
            "type": "company",
            "name": "Your Company Name",
            "participants": [{
                "name": INTERNAL_SIGNER_NAME,
                "email": INTERNAL_SIGNER_EMAIL,
                "signatory": True,
            }]
        }
    ]

    contract = create_contract(
        template_id=ONEFLOW_TEMPLATE_ID,
        workspace_id=ONEFLOW_WORKSPACE_ID,
        name=f"Договор с {company} - {plan}",
        parties=parties,
    )
    contract_id = contract["id"]

    # Заполнить поля шаблона (id полей взять из Oneflow template editor)
    data_fields = [
        {"id": FIELD_PLAN_ID,    "value": plan},
        {"id": FIELD_AMOUNT_ID,  "value": str(amount)},
        {"id": FIELD_DATE_ID,    "value": today},
        {"id": FIELD_COMPANY_ID, "value": company},
    ]
    update_contract_data_fields(contract_id, data_fields)

    # Опубликовать (отправить участникам)
    publish_contract(contract_id)

    update_kommo_deal(lead["id"], {"oneflow_contract_id": str(contract_id)})
    create_kommo_note(lead["id"],
        f"Oneflow: контракт #{contract_id} отправлен на подпись -> {email}")

Обработка Oneflow Webhook:

from flask import Flask, request

app = Flask(__name__)

@app.route("/webhooks/oneflow", methods=["POST"])
def oneflow_webhook():
    payload = request.json
    event_type = payload.get("event_type")
    contract_id = str(payload.get("contract_id", ""))

    deal_id = find_deal_by_field("oneflow_contract_id", contract_id)
    if not deal_id:
        return "", 200

    if event_type == "contract.signed":
        update_kommo_deal(deal_id, {"stage_id": STAGE_CONTRACT_SIGNED})
        create_kommo_note(deal_id, "Oneflow: все стороны подписали контракт")

    elif event_type == "contract.participant_signed":
        signer = payload.get("participant", {}).get("name", "")
        create_kommo_note(deal_id, f"Oneflow: {signer} подписал контракт")

    elif event_type == "contract.declined":
        participant = payload.get("participant", {}).get("name", "")
        create_kommo_note(deal_id,
            f"Oneflow: {participant} отклонил контракт")
        create_kommo_task(deal_id,
            "Уточнить возражения - контракт отклонён в Oneflow")

    elif event_type == "contract.expired":
        create_kommo_note(deal_id, "Oneflow: срок подписания контракта истёк")
        create_kommo_task(deal_id, "Отправить новую версию контракта")

    return "", 200

Настройка Webhook в Oneflow: Settings -> Integrations -> Webhooks -> Add webhook. Указать URL и выбрать события.

Интерактивные HTML-контракты: ключевое отличие Oneflow

В Oneflow контракт — не PDF, а HTML-документ. Клиент может:
— Комментировать конкретные клаузы прямо в браузере
— Редактировать согласованные поля (если разрешено шаблоном)
— Видеть историю изменений

Для длинных B2B-переговоров это критично: вместо отправки 5 версий PDF — один живой документ с версионированием. После завершения переговоров — одна кнопка «Подписать».

Реальный кейс

IT-консалтинг (Швеция, 20–30 контрактов в месяц, Kommo + Oneflow):

  • До: PandaDoc + ручное создание. Контракт уходил клиенту через 2–3 часа после Won. 40% контрактов возвращались с запросами на правки — начиналась email-переписка с PDF-версиями.
  • После: Won -> контракт через Oneflow за 30 секунд. Клиент редактирует несогласные пункты прямо в браузере — email с версиями исчезла. Среднее время от Won до подписания: 1.3 дня (было 4.7 дня).
  • Дополнительно: EU GDPR — все контракты хранятся на серверах в Швеции. Аргумент при работе с EU-клиентами с DPA-требованиями.

Для кого актуально

  • EU-компании с GDPR-требованиями к хранению контрактных данных
  • B2B с переговорным процессом: длинные контракты, редлайнинг, несколько клауз
  • Агентства и консалтинг с 10+ контрактами в месяц
  • Команды, уставшие от «отправь PDF, получи правки, отправь снова»

Часто задаваемые вопросы

Oneflow юридически значим в EU?

Да. Подписи через Oneflow соответствуют eIDAS как Simple Electronic Signature (SES). Для квалифицированной подписи (QES) — нужен дополнительный ID-провайдер. Для большинства B2B контрактов SES достаточно.

Где найти Template ID и Field ID в Oneflow?

Template ID: Oneflow Dashboard -> Templates -> нужный шаблон -> URL (/templates/{id}). Field ID: открыть шаблон в редакторе -> выбрать поле данных -> в правой панели виден ID. Или через GET /templates/{id} — возвращает все поля с ID.

Oneflow поддерживает несколько подписантов и порядок подписания?

Да. В participants можно указать signatory_order для каждого участника — контракт отправляется следующему только после подписи предыдущего. Параллельное подписание (без порядка) — по умолчанию.

Как получить подписанный PDF из Oneflow через API?

GET /contracts/{id}/pdf -> возвращает PDF с подписями. Удобно сохранить автоматически в облако или прикрепить к сделке в Kommo как файл.

Итого

  • Oneflow API: x-oneflow-api-token + x-oneflow-user-email в заголовках
  • Создать контракт: POST /contracts -> update data fields -> POST /contracts/{id}/publish
  • Webhook: события contract.signed, contract.participant_signed, contract.declined
  • HTML-контракты: переговоры, редлайнинг, история версий — преимущество над PDF-based инструментами
  • EU data residency: данные в Швеции, GDPR-compliant из коробки

Если вы используете Oneflow и Kommo и хотите автоматизировать создание контрактов при Won — опишите структуру шаблонов и порядок подписантов. Exceltic.dev настроит интеграцию.

Ещё статьи

Все →