Через webhook form_response Typeform отправляет все ответы на форму в реальном времени. Кастомная интеграция парсит массив answers по field.ref, создаёт или обновляет контакт в HubSpot через upsert по email, создаёт связанную сделку с кастомными свойствами и пишет UTM-метки из hidden fields в карточку. Без ручного переноса, без потери данных при сложных типах полей.
В проектах Exceltic.dev мы регулярно видим одну и ту же картину: форма квалификации в Typeform заполнена, менеджер получает уведомление на email, открывает ответы в Typeform, вручную копирует поля в HubSpot - компания, бюджет, источник трафика, тип задачи. Это занимает 5-7 минут на каждый лид. При 30 лидах в неделю теряется полный рабочий день в месяц. Нативная интеграция Typeform с HubSpot закрывает базовый сценарий, но ломается именно там, где форма сложнее «имя + email».
Webhook - это HTTP POST-запрос, который Typeform автоматически отправляет на указанный URL в момент отправки формы. В теле запроса - полный JSON с ответами, метаданными и скрытыми полями.
Данная статья разбирает, что именно ломается в нативной интеграции, как устроена кастомная схема через webhook и какой результат это даёт в типовом проекте.
Что делает нативная интеграция (и где ломается)
Нативная интеграция Typeform с HubSpot работает через официальный коннектор из маркетплейса HubSpot. Она умеет создавать контакты по email, обновлять стандартные свойства и - с ограничениями - передавать данные в сделки и компании.
Ограничения, с которыми сталкиваются клиенты:
-
Только стандартные поля для сложных типов. Данные из не-HubSpot полей формы (то есть из custom вопросов Typeform) маппятся только в
single-line textсвойства HubSpot. Если нужно заполнить свойство типаdropdown,numberилиcheckbox, нативная интеграция передаёт значение как текст - и HubSpot его отклоняет или игнорирует. -
Частичные ответы не идут в сделки и компании. Partial responses - заявки, где форма не дошла до финального шага - синхронизируются только в контакты. В сделки и компании они не попадают вообще.
-
Максимум 1000 исторических ответов при первом подключении. Если форма уже использовалась до подключения интеграции, только 1000 последних ответов попадут в HubSpot.
-
Одна форма - один аккаунт HubSpot. Нельзя направлять ответы одновременно в несколько portalId, что блокирует мультиаккаунтные сценарии.
-
Отсутствие дедупликации по кастомному полю. Нативная интеграция ищет контакт только по email. Если email изменился или его нет в форме - создаётся дубль.
Для формы квалификации с 8-10 вопросами - бюджет, тип проекта, размер команды, текущий стек, источник - нативная интеграция переносит в лучшем случае половину данных в нужные поля. Остальное менеджер правит вручную.
Похожая проблема возникает и в других нативных интеграциях HubSpot: HubSpot + Slack: что не умеет нативная интеграция теряет контекст сделки при роутинге уведомлений, а HubSpot + Notion: нативная интеграция не справляется с синхронизацией в реальном времени.
Что реализуется через кастомную интеграцию
Кастомная схема использует Typeform Webhooks API для получения ответов в реальном времени и HubSpot CRM API для записи данных.
Webhook form_response -> парсинг answers
Каждый раз, когда респондент завершает форму, Typeform отправляет POST-запрос на указанный эндпоинт. Тело запроса содержит объект form_response с массивом answers.
Структура каждого элемента в answers:
{
"type": "text",
"text": "HubSpot + Typeform",
"field": {
"id": "JwWggjAKtOkA",
"type": "short_text",
"ref": "project_description"
}
}
Типы ответов, которые поддерживает Typeform Webhooks API (документация):
text- текстовые поля (short_text, long_text)choice- одиночный выбор, содержитchoice.labelиchoice.refchoices- множественный выбор, содержит массивchoices.labelsnumber- числовые поля, рейтинги, opinion scaleemail- поле типа email, значение вanswer.emailphone_number- телефон, значение вanswer.phone_numberboolean- yes/no вопросы
Ключевой момент: ответы в массиве расположены не в том порядке, что вопросы в форме. Для надёжного маппинга используется field.ref - это человекочитаемый идентификатор поля, который задаётся в настройках формы в Typeform.
def parse_answers(form_response):
result = {}
for answer in form_response.get("answers", []):
ref = answer["field"]["ref"]
answer_type = answer["type"]
if answer_type == "text":
result[ref] = answer["text"]
elif answer_type == "choice":
result[ref] = answer["choice"]["label"]
elif answer_type == "choices":
result[ref] = ", ".join(answer["choices"]["labels"])
elif answer_type == "number":
result[ref] = answer["number"]
elif answer_type == "email":
result[ref] = answer["email"]
elif answer_type == "phone_number":
result[ref] = answer["phone_number"]
return result
Hidden fields для UTM-меток
Hidden fields - это параметры, которые передаются в форму через URL без отображения респонденту. Стандартный сценарий: посетитель приходит с рекламы, URL содержит ?utm_source=google&utm_campaign=q2_leads, эти значения передаются в Typeform через hidden fields и возвращаются в webhook.
В payload они находятся вне массива answers, в отдельном объекте:
"hidden": {
"utm_source": "google",
"utm_campaign": "q2_leads",
"utm_medium": "cpc"
}
Кастомная интеграция записывает эти значения в кастомные свойства контакта и сделки в HubSpot. Нативная интеграция не поддерживает маппинг hidden fields в произвольные свойства.
Upsert контакта в HubSpot по email
HubSpot CRM API предоставляет batch upsert эндпоинт, который с сентября 2024 года поддерживает email как idProperty (документация HubSpot):
POST https://api.hubapi.com/crm/v3/objects/contacts/batch/upsert
Тело запроса:
{
"inputs": [
{
"id": "[email protected]",
"idProperty": "email",
"properties": {
"firstname": "Иван",
"company": "Acme Corp",
"budget_range": "50k-100k",
"hs_lead_source": "google",
"utm_campaign": "q2_leads"
}
}
]
}
Логика upsert: если контакт с таким email уже существует - свойства обновляются. Если нет - контакт создаётся. Это исключает дубли при повторных заявках с одного email.
Создание сделки с кастомными полями из формы
После создания или обновления контакта создаётся сделка через POST /crm/v3/objects/deals. Сделка связывается с контактом через associations API. Все релевантные ответы из формы - бюджет, тип проекта, дедлайн, приоритет - записываются в кастомные свойства сделки.
Это ключевое отличие от нативной интеграции: кастомные свойства сделки принимают значения нужного типа - number, dropdown, date - потому что интеграция сама преобразует строку из ответа в правильный формат перед отправкой.
Связанный паттерн используется и для HubSpot + Slack: уведомления о новых сделках - сразу после создания сделки можно отправлять структурированное уведомление в нужный канал.
Пошаговая схема интеграции
-
Настройка hidden fields в Typeform. В настройках формы добавляются hidden fields для utm_source, utm_campaign, utm_medium, utm_content. На посадочной странице скрипт читает URL-параметры и передаёт их в embed-код формы.
-
Настройка field.ref для каждого вопроса. В редакторе Typeform каждый вопрос получает уникальный human-readable ref:
budget_range,team_size,current_crm,project_type. Это делает маппинг устойчивым к переименованию вопросов. -
Деплой webhook-сервера. Небольшой сервис (Python/Node.js) разворачивается на отдельном эндпоинте. Typeform отправляет на него POST-запросы. Сервис проверяет подпись запроса через заголовок
Typeform-Signature(HMAC-SHA256) и отклоняет неподписанные запросы. -
Регистрация webhook в Typeform. Через Typeform Webhooks API или в интерфейсе: Workspace -> Forms -> Integrations -> Webhooks. Указывается URL эндпоинта и секрет для подписи.
-
Парсинг payload и маппинг. Сервис читает
form_response.answers, итерирует по массиву, извлекает значения поfield.ref. Отдельно читаетform_response.hiddenдля UTM-меток. -
Upsert контакта в HubSpot. Запрос к
POST /crm/v3/objects/contacts/batch/upsertсidProperty: "email". Если контакт существует - обновляются свойства. Если нет - создаётся. -
Создание сделки и ассоциация. Через
POST /crm/v3/objects/dealsсоздаётся сделка в нужном pipeline и стадии. ЧерезPUT /crm/v3/objects/deals/{dealId}/associations/contacts/{contactId}/deal_to_contactсделка привязывается к контакту. -
Запись UTM и кастомных полей в обе карточки. UTM-метки пишутся и в контакт, и в сделку - чтобы источник трафика был виден на обоих объектах в HubSpot.
Схема, которую описывает этот процесс, аналогична интеграции Kommo + Tilda: заявки из форм в воронку без Zapier - те же принципы webhook-приёма и upsert по email, но адаптированные под модель данных HubSpot.
Реальный кейс
B2B-компания в сфере IT-услуг, 35 сотрудников. Форма квалификации в Typeform: 9 вопросов, включая бюджет (number), тип проекта (choice из 6 вариантов), размер команды клиента (number), текущий стек (multiple choice), дедлайн (date). Плюс hidden fields для 4 UTM-параметров.
До интеграции: каждый лид обрабатывался вручную за 6-8 минут. 40 лидов в месяц - около 5 часов операционной работы только на перенос данных. Часть данных терялась при копировании, в HubSpot оставались пустые поля, менеджер не видел UTM-источник прямо в карточке.
После запуска кастомной интеграции:
- Все 9 полей формы автоматически попадают в свойства контакта и сделки
- UTM-метки записываются в обе карточки
- Сделка создаётся в нужном pipeline с первым этапом воронки автоматически
- Время от заполнения формы до появления сделки в HubSpot - менее 3 секунд
- Операционное время на обработку лида сократилось с 6-8 минут до 0
Время разработки и деплоя типовой интеграции такого типа - 3-5 рабочих дней.
Аналогичный подход применяется при построении HubSpot + Notion: база клиентов - данные из контакта и сделки можно дублировать в Notion-базу сразу при создании.
Для кого это актуально
Кастомная интеграция Typeform + HubSpot оправдана, если:
- Форма содержит 5+ вопросов, из которых хотя бы 2-3 должны попасть в кастомные свойства HubSpot (не только
single-line text) - В форму передаются UTM-метки через hidden fields и вы хотите видеть источник трафика в карточке сделки
- Объём лидов от 20+ в месяц - при меньшем объёме ручной перенос может быть дешевле разработки
- Нужно автоматически создавать сделку (не только контакт) и привязывать её к правильному pipeline и стадии
- Требуется дедупликация: повторные заявки с одного email должны обновлять существующий контакт, а не создавать дубль
Если форма собирает только имя и email для базовой подписки - нативная интеграция справится. Для квалификационных форм с бизнес-логикой нативная интеграция создаёт больше операционных проблем, чем решает.
Часто задаваемые вопросы
Нативная интеграция Typeform с HubSpot не работает с кастомными полями - это правда?
Да, частично. Нативная интеграция ограничена в маппинге сложных типов: данные из кастомных вопросов Typeform попадают в HubSpot только как single-line text. Это означает, что если в HubSpot есть свойство типа number, dropdown или checkbox, нативная интеграция не сможет корректно заполнить его из формы. Кастомная интеграция через webhook решает эту проблему: сервис сам преобразует значение в нужный тип данных перед отправкой в HubSpot API.
Как работает дедупликация контактов при повторных заявках?
HubSpot Contacts upsert API (доступен с сентября 2024) принимает параметр idProperty: "email". Если контакт с таким email уже существует в CRM - API обновляет его свойства. Если нет - создаёт новый контакт. Это стандартная идемпотентная операция: сколько бы раз один email ни заполнил форму, дублей не будет. Для более сложной дедупликации - например, по полю phone - используется аналогичный механизм с другим idProperty или дополнительная проверка перед upsert.
Можно ли передавать в HubSpot ответы из нескольких разных форм Typeform?
Да. Каждая форма регистрирует отдельный webhook, но они могут указывать на один и тот же сервис-обработчик. Сервис идентифицирует форму по form_id в payload и применяет соответствующий маппинг. Это стандартный паттерн при работе с несколькими формами квалификации - например, отдельные формы для разных продуктов или регионов.
Что происходит, если HubSpot API недоступен в момент получения webhook?
Тypeform не повторяет доставку webhook автоматически при ошибке на вашей стороне - если сервер вернул non-200 код, событие считается потерянным. Правильная архитектура: сервис-обработчик немедленно возвращает 200 и ставит задачу в очередь (Redis, RabbitMQ, SQS). Воркер обрабатывает очередь с retry-логикой с exponential backoff. Такой подход гарантирует, что ни один ответ формы не потеряется даже при временных проблемах с HubSpot API.
Сколько времени занимает разработка такой интеграции?
Типовой проект - интеграция одной формы с созданием контакта и сделки, маппинг 8-12 полей, поддержка UTM hidden fields, деплой с retry-очередью - занимает 3-5 рабочих дней. Если нужна более сложная бизнес-логика - например, роутинг в разные pipeline в зависимости от ответа на вопрос о бюджете, или уведомления в Slack при создании сделки - добавляется 1-2 дня.
Если форма квалификации теряет данные при переносе в HubSpot или менеджеры тратят время на ручное копирование ответов - опишите задачу команде Exceltic.dev. Разберём структуру вашей формы, оценим маппинг и предложим схему интеграции под ваш стек.