Интеграция Instagram, Lead Ads и Telegram с amoCRM и Bitrix24
Чтобы заявки из Instagram Lead Ads или Telegram автоматически попадали в amoCRM или Bitrix24, между платформой и CRM ставится ваш бэкенд. Он принимает вебхук, проверяет подпись, получает данные лида, приводит телефон к формату +998 и пишет в API CRM. Готового коннектора для этих CRM в Socialift нет: SDK закрывает сторону платформы, вызовы CRM — в вашем коде.
Обновлено:
Как подключить Instagram к amoCRM или Bitrix24?
Есть два пути. Первый — встроенные канальные интеграции CRM (например, открытые линии Bitrix24): удобно для переписки. Второй — собственный бэкенд: вебхук Meta → проверка подписи → получение лида → нормализация → API CRM. Если нужны атрибуция рекламы, свои правила дублей и маршрутизация, второй путь даёт полный контроль.
В справке Bitrix24 среди каналов открытых линий перечислены Instagram и Telegram. У amoCRM есть Chats API для интеграторов. Эти решения приводят переписку в CRM, но записать ID кампании, объявления и формы Lead Ads в нужные поля обычно можно только своим кодом.
Готового коннектора для amoCRM, Bitrix24 или другой CRM в Socialift нет. Socialift — это Python SDK: он проверяет подпись вебхука, разбирает события, получает лид из Graph API и нормализует ответы формы. Ниже — те 20–40 строк, которые пишут данные в CRM.
Архитектура: от вебхука до записи в CRM за 6 шагов
Каждый шаг независим — если один упал, данные не теряются.
Подключите вебхук
В Meta App подпишитесь на поле
leadgenи подключите каждую Page к приложению (в SDK:fb.subscribe_page(page_id, page_token, fields=["leadgen"])). Для Telegram-бота —set_webhook(url, secret_token=...).Проверьте подпись
Запрос Meta приходит с X-Hub-Signature-256:
verify_request(app_secret, body, headers). Telegram тело не подписывает —verify_telegram_request(headers, secret_token)сверяет секретную строку в заголовке.Разберите события и отсейте дубли
parse_events(payload)превращает payload в потокWebhookEvent. Пишитеevent.keyв уникальную колонку: Meta может повторить доставку.Получите данные лида
В вебхуке leadgen нет ответов формы — только leadgen_id, page_id, form_id, ad_id и время. Полная запись:
fb.lead(leadgen_id, page_token).Нормализуйте
normalize(lead.field_data)приводит поля с любыми названиями кfull_name,phone,email. Телефон доведите до E.164 (+998...) сами.Пишите в CRM через очередь
Сразу отвечайте вебхуку 200, а вызов CRM отдайте фоновому воркеру: поиск дубля по телефону, затем создание. На 429/503 — повтор с задержкой.
Приёмник вебхука (aiohttp)
Обработчик только проверяет, отсеивает дубли и ставит в очередь — тяжёлая работа позже.
import json, os
from aiohttp import web
from socialift.webhooks import verify_request, parse_events
APP_SECRET = os.environ["META_APP_SECRET"]
async def meta_webhook(request: web.Request) -> web.Response:
body = await request.read() # сырые байты — подпись считается по ним
if not verify_request(APP_SECRET, body, request.headers):
return web.Response(status=403)
for event in parse_events(json.loads(body)):
if event.field == "leadgen" and await mark_seen(event.key):
await queue.put({"leadgen_id": event.leadgen_id, "page_id": event.page_id})
return web.Response(text="ok")mark_seen — ваша функция: INSERT ... ON CONFLICT DO NOTHING, возвращает True для новой записи. queue — Redis, RabbitMQ или таблица в базе.
Получение лида и приведение телефона к +998
normalize() из SDK убирает пробелы и дефисы и сохраняет +, но код страны не добавляет.
import re
from socialift import Facebook
from socialift.leads import normalize
def to_e164_uz(raw: str | None) -> str | None:
digits = re.sub(r"\D", "", raw or "")
if digits.startswith("00"):
digits = digits[2:]
if len(digits) == 12 and digits.startswith("998"):
return "+" + digits
if len(digits) == 9: # 90 123 45 67
return "+998" + digits
return None # на ручную проверку
async def load_lead(fb: Facebook, leadgen_id: str, page_token: str) -> dict:
lead = await fb.lead(leadgen_id, page_token)
person = normalize(lead.field_data)
return {"lead_id": lead.id, "name": person.full_name, "email": person.email,
"phone": to_e164_uz(person.phone), "phone_raw": person.phone,
"form_id": lead.form_id, "ad_id": lead.ad_id,
"campaign": lead.campaign_name, "organic": lead.is_organic}Узбекский номер — это 9 цифр после +998. Не выбрасывайте номер, который не подошёл под формат: запишите phone_raw в примечание, менеджер разберётся.
API amoCRM и Bitrix24: чем писать
| amoCRM (API v4) | Bitrix24 (REST) | |
|---|---|---|
| Доступ | OAuth 2.0; для приватной интеграции — долгосрочный токен | Входящий вебхук (секрет в URL) или OAuth-приложение |
| Срок токенов | В примере ответа expires_in 86400 (24 часа); refresh token — 3 месяца, обменивается один раз | OAuth: access — 1 час, refresh — 180 дней; вебхук работает с правами создавшего сотрудника |
| Создание лида | POST /api/v4/leads/complex — сделка + контакт + компания одним запросом, до 50 штук | crm.item.add (entityTypeId: 1); crm.lead.add работает, но его развитие остановлено |
| Телефон | custom_fields_values: field_code PHONE, enum_code WORK/MOB | fm: typeId PHONE, valueType WORK/MOBILE |
| Поиск дублей | GET /api/v4/contacts?query=...; контроль дублей в complex (если включён для интеграции) | crm.duplicate.findbycomm — PHONE/EMAIL, до 20 значений за запрос |
| Лимиты | 7 запросов/с на интеграцию, 50 запросов/с на аккаунт; превышение — 429, систематическое — блокировка с 403 | Leaky bucket: 2 запроса/с, блок на 50 (Enterprise: 5/с, 250); 503 QUERY_LIMIT_EXCEEDED |
| Ошибки | 401 — не авторизован, 400 — некорректные данные | В теле error и error_description; неверный вебхук — 401 INVALID_CREDENTIALS |
Значения — из официальной документации amoCRM и Bitrix24 на 2026-09-17. Лимиты меняются — перепроверьте перед запуском.
amoCRM: сделка и контакт одним запросом
leads/complex создаёт сделку, контакт и телефон вместе. request_id возвращается в ответе — свяжите его с ID лида.
import os, aiohttp
AMO = f"https://{os.environ['AMO_SUBDOMAIN']}.amocrm.ru/api/v4"
HEADERS = {"Authorization": f"Bearer {os.environ['AMO_ACCESS_TOKEN']}"}
async def amo_create(session: aiohttp.ClientSession, lead: dict) -> dict:
payload = [{
"name": f"Lead Ads: {lead['campaign'] or lead['form_id']}",
"request_id": lead["lead_id"],
"_embedded": {
"tags": [{"name": "instagram-lead-ads"}],
"contacts": [{
"first_name": lead["name"] or "Lead",
"custom_fields_values": [{"field_code": "PHONE",
"values": [{"enum_code": "MOB", "value": lead["phone"]}]}],
}],
},
}]
async with session.post(f"{AMO}/leads/complex", json=payload, headers=HEADERS) as r:
if r.status == 429:
raise RetryLater(r.status)
r.raise_for_status()
return (await r.json())[0] # id, contact_id, mergedmerged: true в ответе означает, что контроль дублей amoCRM нашёл существующий контакт и объединил. Если контроль выключен, сначала ищите через GET /api/v4/contacts?query=<телефон>.
Атрибуцию удобно писать примечанием: POST /api/v4/leads/{id}/notes с note_type: "common" и текстом в params.text — форма, объявление, кампания.
Bitrix24: входящий вебхук и crm.item.add
URL вебхука — это и есть ключ: храните его в переменной окружения и никогда не кладите во фронтенд.
import os, aiohttp
B24 = os.environ["B24_WEBHOOK_URL"] # https://<portal>/rest/<user_id>/<secret>/
async def b24(session: aiohttp.ClientSession, method: str, params: dict):
async with session.post(f"{B24}{method}.json", json=params) as r:
data = await r.json()
if "error" in data: # QUERY_LIMIT_EXCEEDED -> повторить позже
raise B24Error(data["error"], data.get("error_description"))
return data["result"]
async def b24_create(session: aiohttp.ClientSession, lead: dict) -> int:
dup = await b24(session, "crm.duplicate.findbycomm",
{"entity_type": "LEAD", "type": "PHONE", "values": [lead["phone"]]})
if isinstance(dup, dict) and dup.get("LEAD"):
return dup["LEAD"][0]
res = await b24(session, "crm.item.add", {"entityTypeId": 1, "fields": {
"title": f"Lead Ads: {lead['campaign'] or lead['form_id']}",
"name": lead["name"],
"utmCampaign": lead["campaign"],
"sourceDescription": f"form={lead['form_id']} ad={lead['ad_id']}",
"fm": [{"typeId": "PHONE", "valueType": "MOBILE", "value": lead["phone"]}],
}})
return res["item"]["id"]Bitrix24 советует определять ошибку по полю error в теле, а не по HTTP-статусу: ошибки внутри batch приходят со статусом 200.
Если портал в простом режиме CRM, лидов нет: созданный лид сразу превращается в сделку. Режим проверяется через crm.settings.mode.get. Допустимые значения sourceId — в crm.status.list (ENTITY_ID = SOURCE).
Атрибуция: куда класть поля Lead Ads в CRM
Объект Lead из SDK возвращает ID формы и рекламы. Не теряйте их — иначе не понять, какая кампания принесла продажу.
| Поле лида | amoCRM | Bitrix24 |
|---|---|---|
| form_id | Тег или доп. поле; в примечании | sourceDescription |
| ad_id, ad_name | Доп. поле или примечание | utmContent или comments |
| campaign_id, campaign_name | Название сделки и тег | utmCampaign |
| is_organic | Тег (organic / paid) | sourceId (источник, настроенный в портале) |
| lead.id | request_id и примечание | comments или доп. поле |
Если нужны группа объявлений, цель кампании и таргетинг — fb.ad_context(ad_id, token). Без разрешения ads_management метод вернёт пустой словарь и не остановит поток лидов.
Как передавать обращения из Telegram-бота в CRM?
Вебхук Telegram-бота не подписывается: строка, переданная в set_webhook(url, secret_token=...), возвращается в каждом запросе в заголовке X-Telegram-Bot-Api-Secret-Token. Проверяйте её через verify_telegram_request(headers, secret_token).
Официальный способ запросить номер — кнопка request_contact: по нажатию номер пользователя приходит сообщением contact (только в личном чате). Приведите contact.phone_number к +998 так же, как для Lead Ads, и передайте в ту же amo_create или b24_create.
Ключ дедупликации — update_id или пара ID чата + ID сообщения. Если клиент пишет боту несколько раз, обычно правильнее добавить примечание к существующему лиду, чем создавать новый.
Дубли, повторы и надёжность
- Двухуровневая дедупликация:
event.keyна уровне вебхука (уникальная колонка), поиск по телефону на уровне CRM. - 429 в amoCRM и QUERY_LIMIT_EXCEEDED в Bitrix24 — временные: повторяйте с экспоненциальной задержкой и jitter.
- При систематическом превышении лимитов amoCRM блокирует аккаунт (403) — ограничьте число воркеров, на 403 останавливайтесь и шлите алерт.
- Refresh token amoCRM одноразовый: сохраняйте новую пару access/refresh в одной транзакции.
- Для пропущенного вебхуком периода делайте backfill:
fb.leads(form_id, page_token, since=...). - Не пишите в логи телефоны, email и токены; URL вебхука Bitrix24 храните как секрет.
- Не выбрасывайте ненормализуемый номер — сохраните сырое значение в примечании для менеджера.
Частые вопросы
Есть ли в Socialift готовый коннектор к amoCRM или Bitrix24?
Нет. Socialift — это Python SDK и кабинет для разработчиков. Он закрывает сторону Instagram, Facebook, Telegram и других платформ, а код записи в API CRM вы пишете в своём бэкенде. Примеры из этой статьи — основа такого кода.
Приходит ли телефон клиента в вебхуке Lead Ads?
Нет. Вебхук leadgen присылает только идентификаторы: leadgen_id, page_id, form_id, ad_id и время. Ответы формы получают отдельным запросом к Graph API — в SDK это fb.lead(leadgen_id, page_token).
В Bitrix24 использовать crm.lead.add или crm.item.add?
Для новой интеграции — crm.item.add с entityTypeId: 1. В документации Bitrix24 сказано, что развитие crm.lead.add остановлено и рекомендуется crm.item.add. Телефон в crm.item.add передаётся через массив fm.
Какие лимиты у API amoCRM?
По документации — не более 7 запросов в секунду на интеграцию и до 50 запросов в секунду на весь аккаунт. При превышении возвращается 429, при систематических нарушениях аккаунт блокируется и отвечает 403. За один запрос рекомендуется передавать не более 50 сделок.
Что значит ошибка QUERY_LIMIT_EXCEEDED в Bitrix24?
Это лимит интенсивности запросов: когда счётчик превышает порог, Bitrix24 отвечает 503 с кодом QUERY_LIMIT_EXCEEDED. На обычных тарифах счётчик уменьшается на 2 в секунду и блокирует на 50, на Enterprise — 5 и 250. Решение — очередь и повтор с небольшой задержкой.