Whereby Embedded позволяет создавать комнаты для видеозвонков через API за один HTTP-запрос. Для Kommo это решает конкретную задачу: когда менеджер переводит сделку на этап “Демо” или “Онбординг”, комната создаётся автоматически, ссылка добавляется в note к сделке - и можно сразу отправить клиенту. Никакого перехода в Whereby, никакого ручного копирования ссылки.
Whereby Embedded API использует Bearer token (API Key из Whereby Embedded аккаунта). Один эндпоинт для создания комнаты: POST /v1/meetings. В ответе - roomUrl для гостей и hostRoomUrl с правами хоста. Минимальный запрос - 3 поля.
Whereby Embedded - версия Whereby для разработчиков с API для создания комнат программно и возможностью встройки видео в собственный интерфейс. Отличается от обычного Whereby: Embedded требует платного плана и API Key.
Почему стандартный подход не работает
Стандартный Zapier-коннектор Whereby создаёт “постоянные” комнаты без привязки к конкретному временному слоту и без hostRoomUrl. В результате менеджер получает ссылку на комнату, которая либо уже занята другим звонком, либо доступна без его ведома.
Более глубокая проблема: Zapier-комнаты не удаляются автоматически. Если сделка отменяется, комната остаётся открытой. За месяц накапливается 20-30 брошенных комнат.
Архитектура
Kommo: сделка -> этап "Демо" или "Онбординг"
-> Kommo webhook leads.status.changed
-> Ваш сервер
Ваш сервер:
-> POST /v1/meetings (endDate, roomNamePrefix)
-> Response: {roomUrl, meetingId, hostRoomUrl}
-> Kommo: note с roomUrl (для клиента) и hostRoomUrl (для менеджера)
Реализация
import requests, os
from datetime import datetime, timedelta, timezone
from flask import Flask, request, jsonify
app = Flask(__name__)
WHEREBY_API_KEY = os.environ["WHEREBY_API_KEY"]
WHEREBY_BASE = "https://api.whereby.dev/v1"
WHEREBY_HDR = {"Authorization": f"Bearer {WHEREBY_API_KEY}",
"Content-Type": "application/json"}
KOMMO_SUBDOMAIN = os.environ["KOMMO_SUBDOMAIN"]
KOMMO_TOKEN = os.environ["KOMMO_ACCESS_TOKEN"]
DEMO_STAGE_ID = int(os.environ["KOMMO_DEMO_STAGE_ID"])
KOMMO_BASE = f"https://{KOMMO_SUBDOMAIN}.kommo.com/api/v4"
KOMMO_HDR = {"Authorization": f"Bearer {KOMMO_TOKEN}",
"Content-Type": "application/json"}
MEETING_DURATION_HOURS = 2 # комната живёт 2 часа после создания
def create_whereby_room(lead_id: int) -> dict:
end_date = (
datetime.now(timezone.utc) + timedelta(hours=MEETING_DURATION_HOURS)
).strftime("%Y-%m-%dT%H:%M:%S.000Z")
r = requests.post(
f"{WHEREBY_BASE}/meetings",
headers=WHEREBY_HDR,
json={
"endDate": end_date,
"fields": ["hostRoomUrl"],
"roomNamePrefix": f"kommo-{lead_id}-",
"roomMode": "normal",
},
)
r.raise_for_status()
return r.json()
def delete_whereby_room(meeting_id: str):
requests.delete(
f"{WHEREBY_BASE}/meetings/{meeting_id}",
headers=WHEREBY_HDR,
)
def add_note(lead_id: int, text: str):
requests.post(
f"{KOMMO_BASE}/notes",
headers=KOMMO_HDR,
json=[{"entity_id": lead_id, "entity_type": "leads",
"note_type": "common", "params": {"text": text}}],
)
def save_meeting_id(lead_id: int, meeting_id: str):
# Сохраняем meetingId в кастомное поле Kommo для последующего удаления
cf_meeting_id = int(os.environ.get("CF_WHEREBY_MEETING_ID", "0"))
if cf_meeting_id:
requests.patch(
f"{KOMMO_BASE}/leads/{lead_id}",
headers=KOMMO_HDR,
json={"custom_fields_values": [
{"field_id": cf_meeting_id,
"values": [{"value": meeting_id}]}
]},
)
@app.route("/webhooks/kommo", methods=["POST"])
def kommo_webhook():
data = request.json or {}
# Создание комнаты при переходе на этап Демо
for lead_data in data.get("leads", {}).get("status", []):
lead_id = lead_data.get("id")
new_status = lead_data.get("status_id")
if new_status != DEMO_STAGE_ID:
continue
meeting = create_whereby_room(lead_id)
room_url = meeting.get("roomUrl", "")
host_url = meeting.get("hostRoomUrl", "")
meeting_id = meeting.get("meetingId", "")
save_meeting_id(lead_id, meeting_id)
add_note(
lead_id,
f"Whereby комната создана:\n"
f"Ссылка для клиента: {room_url}\n"
f"Ссылка для менеджера (хост): {host_url}\n"
f"Meeting ID: {meeting_id}"
)
return jsonify({"status": "ok"}), 200
Удаление комнат при закрытии сделки
Whereby не удаляет комнаты автоматически после endDate - они переходят в неактивное состояние, но занимают лимит. Правильный подход: удалять при закрытии или проигрыше сделки.
def get_cf_value(lead: dict, field_id: int) -> str:
for cf in lead.get("custom_fields_values", []) or []:
if cf.get("field_id") == field_id:
vals = cf.get("values", [])
return vals[0].get("value", "") if vals else ""
return ""
@app.route("/webhooks/kommo/closed", methods=["POST"])
def kommo_closed():
data = request.json or {}
cf_meeting_id = int(os.environ.get("CF_WHEREBY_MEETING_ID", "0"))
if not cf_meeting_id:
return jsonify({"status": "no_cf_configured"}), 200
for lead_data in data.get("leads", {}).get("delete", []):
lead_id = lead_data.get("id")
# Загружаем полные данные сделки чтобы получить кастомное поле
r = requests.get(
f"{KOMMO_BASE}/leads/{lead_id}",
headers=KOMMO_HDR,
params={"with": "custom_fields_values"},
)
lead = r.json()
meeting_id = get_cf_value(lead, cf_meeting_id)
if meeting_id:
delete_whereby_room(meeting_id)
return jsonify({"status": "ok"}), 200
Настройка Whereby Embedded
- Зарегистрируйтесь на whereby.com/embedded (нужен платный план)
- Создайте организацию, перейдите в Configure -> API Keys
- Сгенерируйте API Key - это Bearer token для всех запросов
- Настройте
roomNamePrefixиroomModeпод ваши нужды
Режимы комнаты: normal (стандартная) и group (до 100 участников, требует Enterprise план).
Параметры комнаты
# Расширенный запрос с дополнительными параметрами
meeting = requests.post(
f"{WHEREBY_BASE}/meetings",
headers=WHEREBY_HDR,
json={
"endDate": "2026-07-01T12:00:00.000Z",
"fields": ["hostRoomUrl", "viewerRoomUrl"],
"roomNamePrefix": "acme-demo-",
"roomMode": "normal",
"recording": {"type": "none"}, # или "cloud"
"chat": {"enabled": True},
"screenshare": {"enabled": True},
},
)
Поле viewerRoomUrl - ссылка для наблюдателя без прав говорить (полезно для записи презентаций).
Для кого актуально
B2B компании с sales-процессом, который включает демо-звонки или онбординг-сессии. Особенно актуально для SaaS и профессиональных услуг, где демо - обязательный этап воронки. Whereby Embedded популярен в EU именно потому, что данные не уходят на серверы в США - важно для GDPR-compliant компаний.
Другие коммуникационные интеграции: Kommo + Telnyx (SMS/звонки через CPaaS), Kommo + Sinch (SMS EU).
Часто задаваемые вопросы
Можно ли получить запись звонка через Whereby API?
Да, если включена запись в настройках комнаты ("recording": {"type": "cloud"}). После завершения встречи запись доступна через GET /v1/recordings - список записей с downloadUrl. Записи хранятся на серверах Whereby (EU-регион доступен на Enterprise-плане).
Как ограничить доступ к комнате только для приглашённых?
Whereby Embedded поддерживает roomLock: true при создании комнаты - участники должны ждать допуска от хоста. Хост управляет через hostRoomUrl. Дополнительно можно добавить knock: {"enabled": true} для обязательного стука перед входом.
Поддерживает ли Whereby GDPR и хранение данных в EU?
Да. Whereby - норвежская компания, серверы в EU. Enterprise-план гарантирует Data Processing Agreement (DPA) по GDPR. Записи хранятся в EU-регионе. Это одно из ключевых преимуществ перед Zoom для европейских B2B-компаний.
Работает ли Whereby в браузере без установки приложения?
Да - это ключевое отличие Whereby от Zoom. Встреча открывается в браузере, не нужны плагины или приложения. Клиент просто переходит по ссылке. Поддерживаются Chrome, Firefox, Edge, Safari.
Итог
Kommo + Whereby Embedded - видеозвонки из воронки:
- Bearer token,
POST /v1/meetingsс endDate и roomNamePrefix - Ответ:
roomUrl(клиент) +hostRoomUrl(менеджер-хост) - Сохранять
meetingIdв Kommo для удаления при закрытии сделки - Комнаты не удаляются сами - явно удалять через
DELETE /v1/meetings/{id} - EU-серверы, GDPR, без приложения для клиента
Если ваша команда проводит демо-звонки через Kommo и нужно автоматизировать создание Whereby-комнат - опишите задачу команде Exceltic.dev.