CRM-система хранит контакты. Email-валидатор проверяет адреса. По отдельности каждый инструмент работает нормально. Вместе - закрывают дыру, через которую в базу попадают несуществующие адреса, спам-ловушки и одноразовые ящики.
В этой статье - конкретные интеграции: Bitrix24, amoCRM, HubSpot. Для каждой CRM - архитектура, код, подводные камни. Без абстрактных диаграмм, только то, что можно взять и внедрить.
Зачем проверять email внутри CRM
Стандартный сценарий без валидации: менеджер вбивает контакт в CRM, через неделю маркетинг отправляет рассылку, письмо уходит на несуществующий адрес, ESP фиксирует hard bounce. Двадцать таких адресов - и репутация домена начинает проседать. Пятьдесят - и письма уходят в спам у всех получателей, включая живых.
Проблема усиливается, когда контакты приходят из нескольких источников: форма на сайте, импорт из Excel, ручной ввод менеджером, парсинг из LinkedIn. Каждый источник добавляет свой процент мусора. Форма - опечатки и одноразовые адреса. Excel - устаревшие контакты. Ручной ввод - банальные ошибки. Без валидации на входе CRM превращается в коллектор невалидных адресов.
Решение - проверять email в момент создания или обновления контакта. Не раз в месяц, не перед рассылкой, а сразу. Это отсекает мусор до того, как он успеет попасть в сегменты и рассылочные списки.
Общая архитектура: CRM + API валидатора
Независимо от конкретной CRM, схема одна и та же. Три точки интеграции покрывают все сценарии:
Webhook при создании контакта
CRM отправляет событие на ваш сервер. Сервер вызывает API валидатора, получает результат, записывает статус обратно в CRM. Задержка - 2-5 секунд.
Bulk-проверка существующей базы
Выгружаете контакты через API CRM, отправляете списком на bulk-валидацию, по готовности обновляете статусы в CRM. Для первичной очистки.
Регулярная переproверка по расписанию
Cron-задача раз в месяц: берёт контакты, которые не проверялись дольше 30 дней, отправляет на валидацию. Ящики умирают - адрес, валидный в январе, может не существовать в марте.
Для хранения результатов создайте в CRM пользовательское поле - например, email_validation_status с вариантами: valid, invalid, risky, unknown. Отдельное поле email_validated_at с датой последней проверки. Это позволяет строить сегменты: «только валидные адреса» для рассылок, «risky» для ручной проверки менеджером.
Bitrix24: входящий webhook + REST API
Bitrix24 поддерживает исходящие вебхуки на события CRM. Нас интересует событие ONCRMCONTACTADD - срабатывает при создании нового контакта. Для обновления есть ONCRMCONTACTUPDATE.
Предварительно создайте пользовательские поля в Bitrix24: зайдите в CRM → Настройки → Пользовательские поля, добавьте UF_EMAIL_VALID (список) и UF_EMAIL_CHECKED (дата). Запомните ID полей - они понадобятся в коде.
Обработчик webhook-а на PHP
Bitrix24 отправляет POST-запрос с ID контакта. Ваш сервер получает ID, забирает email через REST API, отправляет на валидацию, записывает результат обратно.
<?php
// Webhook endpoint: POST /webhooks/bitrix24-contact
$contactId = $_POST['data']['FIELDS']['ID'] ?? null;
if (!$contactId) { http_response_code(400); exit; }
$bitrixWebhook = getenv('BITRIX24_WEBHOOK_URL');
// e.g. https://your-domain.bitrix24.ru/rest/1/abc123xyz/
// 1. Fetch contact email from Bitrix24
$contact = json_decode(file_get_contents(
$bitrixWebhook . "crm.contact.get?ID=" . $contactId
), true)['result'];
$email = $contact['EMAIL'][0]['VALUE'] ?? null;
if (!$email) exit;
// 2. Validate via uChecker API
$ch = curl_init('https://api.uchecker.net/api/v1/validate/single');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'x-api-key: ' . getenv('UCHECKER_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['email' => $email]),
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
$taskId = $result['task_id'];
// 3. Poll for result (in production use webhook_url instead)
sleep(5);
$ch = curl_init("https://api.uchecker.net/api/v1/tasks/$taskId");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'x-api-key: ' . getenv('UCHECKER_API_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$task = json_decode(curl_exec($ch), true);
curl_close($ch);
$status = $task['result']['status'] ?? 'unknown';
// Map to your custom field values
$statusMap = [
'deliverable' => 'valid',
'undeliverable' => 'invalid',
'risky' => 'risky',
'unknown' => 'unknown',
];
// 4. Write result back to Bitrix24
file_get_contents($bitrixWebhook . "crm.contact.update?" . http_build_query([
'ID' => $contactId,
'FIELDS' => [
'UF_EMAIL_VALID' => $statusMap[$status] ?? 'unknown',
'UF_EMAIL_CHECKED' => date('Y-m-d'),
],
]));В production-коде замените sleep(5) на обработку через webhook_url. Передайте параметр webhook_url в запросе валидации - uChecker отправит результат на указанный URL, как только проверка завершится. Это избавляет от polling и блокировки потока.
Для Bitrix24 on-premise (коробка) логика та же, только webhook URL будет другим. Облачная версия и коробка используют одинаковый REST API. Если у вас несколько порталов, храните webhook URL каждого в конфигурации и выбирайте по source-параметру в запросе.
amoCRM: Digital Pipeline + webhook
В amoCRM события по контактам доступны через вебхуки. Настройка: Настройки → Интеграции → создать интеграцию с типом «Внешний обработчик», указать URL вашего сервера. amoCRM пришлёт POST-запрос при добавлении или обновлении контакта.
Особенность amoCRM - авторизация через OAuth 2.0. Для серверной интеграции вам нужен long-lived токен через client_credentials flow или периодическое обновление через refresh_token. Храните токены в базе, обновляйте автоматически.
Обработчик на Python
import os, requests
from flask import Flask, request, jsonify
app = Flask(__name__)
UCHECKER_KEY = os.environ["UCHECKER_API_KEY"]
AMO_TOKEN = os.environ["AMOCRM_ACCESS_TOKEN"]
AMO_DOMAIN = os.environ["AMOCRM_DOMAIN"] # e.g. mycompany
def validate_email(email: str) -> dict:
"""Send email to uChecker, return validation result."""
resp = requests.post(
"https://api.uchecker.net/api/v1/validate/single",
headers={"x-api-key": UCHECKER_KEY},
json={"email": email},
)
task_id = resp.json()["task_id"]
# Poll until done (use webhook_url in production)
import time
for _ in range(10):
time.sleep(3)
r = requests.get(
f"https://api.uchecker.net/api/v1/tasks/{task_id}",
headers={"x-api-key": UCHECKER_KEY},
)
data = r.json()
if data.get("status") == "completed":
return data["result"]
return {"status": "unknown"}
def update_amo_contact(contact_id: int, validation_status: str):
"""Write validation result back to amoCRM custom field."""
requests.patch(
f"https://{AMO_DOMAIN}.amocrm.ru/api/v4/contacts/{contact_id}",
headers={
"Authorization": f"Bearer {AMO_TOKEN}",
"Content-Type": "application/json",
},
json={
"custom_fields_values": [
{
"field_id": 123456, # your custom field ID
"values": [{"value": validation_status}],
}
]
},
)
@app.route("/webhooks/amocrm", methods=["POST"])
def handle_amocrm_webhook():
payload = request.json or {}
contacts = payload.get("contacts", {}).get("add", [])
contacts += payload.get("contacts", {}).get("update", [])
for contact in contacts:
contact_id = contact["id"]
# Fetch full contact to get email
r = requests.get(
f"https://{AMO_DOMAIN}.amocrm.ru/api/v4/contacts/{contact_id}",
headers={"Authorization": f"Bearer {AMO_TOKEN}"},
)
fields = r.json().get("custom_fields_values", [])
email = None
for f in fields:
if f["field_code"] == "EMAIL":
email = f["values"][0]["value"]
break
if not email:
continue
result = validate_email(email)
status = result.get("status", "unknown")
update_amo_contact(contact_id, status)
return jsonify({"ok": True})Обратите внимание на field_id: в amoCRM у каждого кастомного поля есть числовой ID. Его можно получить через GET /api/v4/contacts/custom_fields. Подставьте реальный ID вместо 123456.
Если контактов много и webhook-и идут потоком, вынесите валидацию в очередь задач - Celery, RQ или любой другой менеджер. Webhook-обработчик должен отвечать за 200-300 миллисекунд, иначе amoCRM перестанет отправлять события.
HubSpot: Workflows + custom coded action
HubSpot предлагает два пути. Первый - Workflows с custom coded action (Operations Hub Professional). Второй - webhook-подписка через API. Для большинства команд Workflows удобнее: настройка через интерфейс, логирование из коробки, повторные попытки при ошибках.
Создайте workflow с триггером «Contact is created». Добавьте шаг «Custom coded action». Внутри - Node.js-скрипт, который вызывает uChecker API и записывает результат в свойство контакта.
Custom coded action (Node.js)
// HubSpot Custom Coded Action
// Input: contact email (configured in workflow)
// Secrets: UCHECKER_API_KEY (add in workflow settings)
const hubspot = require("@hubspot/api-client");
const axios = require("axios");
exports.main = async (event, callback) => {
const email = event.inputFields["email"];
if (!email) return callback({ outputFields: {} });
const apiKey = process.env.UCHECKER_API_KEY;
// 1. Submit for validation
const { data: task } = await axios.post(
"https://api.uchecker.net/api/v1/validate/single",
{ email },
{ headers: { "x-api-key": apiKey } }
);
// 2. Poll for result
let result = null;
for (let i = 0; i < 10; i++) {
await new Promise((r) => setTimeout(r, 3000));
const { data } = await axios.get(
`https://api.uchecker.net/api/v1/tasks/${task.task_id}`,
{ headers: { "x-api-key": apiKey } }
);
if (data.status === "completed") {
result = data.result;
break;
}
}
const status = result?.status || "unknown";
// 3. Update contact property
const hsClient = new hubspot.Client({
accessToken: process.env.HUBSPOT_TOKEN,
});
await hsClient.crm.contacts.basicApi.update(
event.object.objectId,
{
properties: {
email_validation_status: status,
email_validated_date: new Date().toISOString().split("T")[0],
},
}
);
callback({ outputFields: { validation_status: status } });
};Перед запуском создайте свойства контакта в HubSpot: Settings → Properties → Create property. Тип - Single-line text для email_validation_status и Date для email_validated_date. Internal name должен совпадать с тем, что в коде.
Альтернатива без Operations Hub: внешний сервер + HubSpot webhook subscription API. Подписываетесь на событие contact.creation, обрабатываете аналогично Bitrix24 и amoCRM. Webhook subscription требует верификации через challenge-запрос - HubSpot отправляет GET с параметром, ваш сервер должен вернуть его в ответе.
Bulk-очистка существующей базы
Webhook-интеграция закрывает новые контакты. Но что делать с теми тысячами адресов, которые уже в CRM? Нужна разовая bulk-проверка.
Алгоритм для любой CRM: выгрузите контакты через API, соберите email-адреса в список, отправьте на bulk-валидацию, дождитесь результата, обновите статусы обратно. Вот универсальный скрипт.
import os, time, requests
API_KEY = os.environ["UCHECKER_API_KEY"]
BASE = "https://api.uchecker.net"
HEADERS = {"x-api-key": API_KEY, "Content-Type": "application/json"}
def bulk_validate(emails: list[str]) -> dict:
"""Submit bulk validation and wait for results."""
# 1. Create bulk task
resp = requests.post(
f"{BASE}/api/v1/validate/bulk",
headers=HEADERS,
json={"emails": emails},
)
task_id = resp.json()["task_id"]
print(f"Bulk task created: {task_id}, emails: {len(emails)}")
# 2. Poll until complete
while True:
time.sleep(10)
r = requests.get(f"{BASE}/api/v1/tasks/{task_id}", headers=HEADERS)
data = r.json()
if data["status"] == "completed":
return data["results"] # {email: status, ...}
if data["status"] == "failed":
raise RuntimeError(f"Task {task_id} failed")
print(f" status: {data['status']}, progress: {data.get('progress', '?')}")
# Usage example:
# contacts = fetch_contacts_from_crm()
# emails = [c["email"] for c in contacts if c.get("email")]
# results = bulk_validate(emails)
# for contact in contacts:
# status = results.get(contact["email"], "unknown")
# update_crm_contact(contact["id"], status)Для больших баз (больше 10 000 адресов) разбивайте на пакеты. uChecker bulk API принимает до 50 000 адресов за запрос, но обновление CRM лучше делать батчами по 100-500 контактов - это снижает нагрузку на API CRM и уменьшает риск rate limiting.
Результат bulk-проверки можно использовать для сегментации. Контакты со статусом invalid - удалить или поставить флаг «не отправлять». Статус risky - отправлять только критически важные письма. Valid - полноценная работа.
Обработка ошибок и граничные случаи
Интеграция двух систем - это всегда борьба с нештатными ситуациями. Вот что ломается на практике и как с этим справляться.
Таймаут API валидатора
uChecker отвечает за 2-5 секунд на single-валидацию. Если ответа нет 15 секунд - ставьте статус unknown и добавляйте контакт в очередь повторной проверки. Не блокируйте создание контакта в CRM.
Rate limiting CRM API
Bitrix24 - 2 запроса в секунду на webhook. amoCRM - 7 запросов в секунду. HubSpot - 100 запросов за 10 секунд (private app). При bulk-обновлении используйте batch-эндпоинты, где они есть, и добавляйте задержку между запросами.
Дубликаты контактов
Один email может быть у нескольких контактов. Кэшируйте результат валидации на стороне вашего сервера (Redis, SQLite) на 24 часа. Если тот же адрес приходит повторно - берите из кэша, не тратьте кредиты.
Контакт без email
Не все контакты в CRM имеют email. Проверяйте наличие поля перед отправкой на валидацию. Это очевидно, но каждый второй обработчик падает именно на этом.
Хорошая практика - логировать каждый вызов: входящий webhook, запрос к API валидатора, результат, обновление CRM. При отладке интеграции двух систем логи экономят часы. Храните логи минимум 7 дней.
Чек-лист внедрения
Независимо от CRM, порядок внедрения одинаковый. Вот последовательность шагов, которая работает.
Весь процесс - от получения API-ключа до работающего webhook-а - занимает 2-4 часа для опытного разработчика. Bulk-очистка базы в 10 000 контактов - ещё 30-40 минут, включая обновление статусов в CRM.
Валидация email в CRM - не разовая акция. Это процесс: проверка на входе, регулярная переproверка, сегментация по статусу. Адреса устаревают, и результат трёхмесячной давности ничего не гарантирует.
Результат интеграции измеряется просто: bounce rate рассылок до и после. Если до внедрения было 5-8%, а после стало меньше 2% - интеграция работает. Если bounce rate растёт через 2-3 месяца - сломалась регулярная переproверка. Метрика одна, и она не врёт.
Подключите валидацию к вашей CRM
Зарегистрируйтесь в uChecker, получите API-ключ и 30 бесплатных проверок для тестирования интеграции. Документация API - в руководстве для разработчиков.
Попробовать бесплатно