Перевірка або пошук e-mail-адрес для нових контактів HubSpot і Pipedrive
- Для кого
- Команди sales operations, які працюють у HubSpot або Pipedrive
- Проблема
- Контакти надходять із форм, імпортів і ручного введення з помилками, мертвими доменами або взагалі без адреси, і ніхто їх не перевіряє, доки кампанія не почне отримувати відмови.
- Рішення
- Перевіряйте кожен контакт у момент створення: дією Custom code у workflow HubSpot або невеликим приймачем вебхуків для Pipedrive. Контакт з e-mail перевіряється; контакт без нього, але з іменем і сайтом компанії, отримує запропоновану адресу, чітко позначену як припущення.
- Що ви отримаєте
- Властивості контакту в HubSpot або поля особи в Pipedrive, які показують статус, оцінку й причину від Cold Leads або запропоновану адресу з її методом і впевненістю.
Адреси, ідентифікатори та результати в прикладах ілюстративні. Домен example.com зарезервовано для документації, тож реальна перевірка цих адрес поверне invalid.
Приклади коду однакові для всіх мов, коментарі в них — англійською.
Як це поєднується
- Вбудованої інтеграції немає: workflow або приймач викликає API Cold Leads з вашим секретним ключем, а відповідь записують назад власні інструменти CRM. Виклик API не додає контакт у Cold Leads.
- Контакт з e-mail: POST /api/v1/verify (1 кредит) повертає status (valid, risky або invalid), score і коди причин.
- Контакт без e-mail, але з ім’ям або прізвищем і сайтом компанії: POST /api/v1/find (1 кредит) повертає найімовірнішу адресу. Її method — verified лише тоді, коли поштовий сервер підтвердив скриньку, інакше pattern: припущення за поширеними форматами адрес. Зберігайте її в окремому полі й переглядайте; ніколи не перезаписуйте поле e-mail припущенням.
HubSpot: дія Custom code у workflow для контактів
- Створіть властивості контакту для результатів, наприклад Cold Leads status, Cold Leads score, Cold Leads reason і Suggested e-mail.
- Перейдіть в Automation > Workflows і створіть workflow для контактів. Тригер: подія Object created (категорія CRM) з уточнювальним фільтром (refinement filter), наприклад Email is known, або тригер за фільтром (Met filter criteria) з умовою Email is known. Уточнювальні фільтри оцінюються лише в момент події, тож контакт, створений без e-mail, якому адресу додали пізніше, не потрапить у workflow через тригер події.
- Натисніть значок +, знайдіть Custom code і виберіть його. Для Custom code потрібен Data Hub Professional або Enterprise.
- Залиште мовою Node.js. Натисніть Add secret, введіть назву секрету COLDLEADS_API_KEY і ваш ключ sk_ як значення, збережіть і позначте секрет галочкою.
- У Properties to include in code додайте Email, First name, Last name і Website URL з іменами email, firstname, lastname і website.
- Вставте код нижче. У Data outputs додайте виходи, перелічені на початку коду, з їхніми типами даних.
- Скористайтеся Test action на тестовому контакті. Тест виконує справжній код для вибраного контакту, тож витрачає кредит.
- Додайте дію Edit record (значок +, CRM, Edit record), виберіть властивість, потім в Action data натисніть More action data, дію Custom code і потрібний вихід. Повторіть для кожної властивості.
// HubSpot workflow, Custom code action (Node.js)
// Secret: COLDLEADS_API_KEY (your Cold Leads secret key, sk_...)
// Properties to include in code: email, firstname, lastname, website
// Data outputs (add them in the action): verify_status, verify_reason, suggested_email, find_method (String);
// verify_score, find_confidence (Number); mailbox_checked (Boolean)
const axios = require("axios");
const coldleads = axios.create({
baseURL: "https://coldleads.app/api/v1",
headers: { "x-api-key": process.env.COLDLEADS_API_KEY },
timeout: 17000, // the action has 20 seconds in total
});
exports.main = async (event, callback) => {
const { email, firstname, lastname, website } = event.inputFields;
try {
if (email) {
// 1 credit; timeout_ms keeps the check inside HubSpot's time limit
const { data } = await coldleads.post("/verify", { email, timeout_ms: 12000 });
const reason = data.reasons[data.reasons.length - 1];
return callback({
outputFields: {
verify_status: data.status,
verify_score: data.score,
verify_reason: reason,
mailbox_checked: ["ok", "catch_all", "mailbox_missing"].includes(reason),
},
});
}
if ((firstname || lastname) && website) {
// 1 credit; website may be a URL, Cold Leads reduces it to the domain
const { data } = await coldleads.post("/find", { first: firstname || "", last: lastname || "", domain: website });
return callback({
outputFields: {
suggested_email: data.email || "",
find_method: data.method, // "verified" only when a mail server confirmed the mailbox, otherwise "pattern"
find_confidence: data.confidence,
},
});
}
return callback({ outputFields: { verify_status: "skipped" } });
} catch (err) {
const status = err.response ? err.response.status : 0;
// rethrow rate limits and server errors: HubSpot retries them (for up to three days)
if (status === 429 || status >= 500) throw err;
const code = err.response && err.response.data && err.response.data.error;
return callback({ outputFields: { verify_status: "error", verify_reason: code || err.code || String(status) } });
}
};HubSpot дає дії Custom code 20 секунд і 128 МБ. Коли код кидає виняток після помилки 429 або 5xx від axios, HubSpot повторює дію протягом до трьох днів, починаючи через хвилину; тому код повторно кидає ці помилки, а кожну іншу, як-от 402 no_credits, перетворює на вихідне значення.
Pipedrive: приймач вебхуків
- Створіть два текстові поля особи, наприклад E-mail check і Suggested e-mail, і скопіюйте їхні API-ключі в Company settings > Data fields > Person, у меню кожного поля (Copy API key). Для пошуку адреси також запишіть ключ поля особи, яке містить сайт або домен компанії.
- Запустіть приймач нижче на будь-якому хості з Node.js 18+ і HTTPS-адресою, задавши змінні оточення, які він перелічує.
- У Pipedrive відкрийте Settings > Tools and apps > Webhooks і створіть вебхук: Event action create, Event object person, User permission level, видимість якого охоплює нових осіб, Webhook name, Endpoint URL вашого приймача (його HTTPS-адреса, а за нею /pipedrive/person) і HTTP Auth username та password, що збігаються з HOOK_USER і HOOK_PASSWORD.
- Додайте особу з e-mail-адресою; поле E-mail check заповниться невдовзі після цього.
// npm install express (Node.js 18 or newer)
// Pipedrive webhook: event action "create", event object "person", endpoint https://<your-host>/pipedrive/person,
// HTTP Auth username and password = HOOK_USER and HOOK_PASSWORD below.
import express from "express";
const {
COLDLEADS_API_KEY, // Cold Leads secret key (sk_...)
PIPEDRIVE_API_TOKEN, // Pipedrive personal API token
HOOK_USER,
HOOK_PASSWORD,
STATUS_FIELD, // 40-character key of a person text field, e.g. "E-mail check"
SUGGESTED_FIELD, // 40-character key of a person text field, e.g. "Suggested e-mail"
DOMAIN_FIELD, // optional: key of a person field that holds the company website or domain
} = process.env;
async function coldleads(path, body) {
const res = await fetch(`https://coldleads.app/api/v1${path}`, {
method: "POST",
headers: { "x-api-key": COLDLEADS_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = await res.json();
if (!res.ok) throw new Error(`Cold Leads ${res.status} ${data.error}`);
return data;
}
async function updatePerson(id, customFields) {
const res = await fetch(`https://api.pipedrive.com/api/v2/persons/${id}`, {
method: "PATCH",
headers: { "x-api-token": PIPEDRIVE_API_TOKEN, "Content-Type": "application/json" },
body: JSON.stringify({ custom_fields: customFields }),
});
if (!res.ok) throw new Error(`Pipedrive ${res.status} ${await res.text()}`);
}
// webhook payloads carry custom fields as typed objects ({ type, value })
const plain = (v) => (v && typeof v === "object" && "value" in v ? v.value : v);
async function handlePerson(person) {
const emails = Array.isArray(person.emails) ? person.emails : [];
const primary = emails.find((e) => e && e.primary) ?? emails[0];
const email = typeof primary === "string" ? primary : primary?.value;
if (email) {
const r = await coldleads("/verify", { email, timeout_ms: 15000 }); // 1 credit
const reason = r.reasons[r.reasons.length - 1];
await updatePerson(person.id, { [STATUS_FIELD]: `${r.status} (${reason}, score ${r.score})` });
return;
}
const domain = DOMAIN_FIELD ? plain(person.custom_fields?.[DOMAIN_FIELD]) : "";
if (domain && (person.first_name || person.last_name)) {
const r = await coldleads("/find", { first: person.first_name ?? "", last: person.last_name ?? "", domain }); // 1 credit
if (r.email) await updatePerson(person.id, { [SUGGESTED_FIELD]: `${r.email} (${r.method}, confidence ${r.confidence})` });
}
}
const seen = new Set(); // a retried delivery must not be charged twice
const app = express();
app.post("/pipedrive/person", express.json(), (req, res) => {
const expected = "Basic " + Buffer.from(`${HOOK_USER}:${HOOK_PASSWORD}`).toString("base64");
if (req.get("authorization") !== expected) return res.sendStatus(401);
const { meta, data } = req.body ?? {};
res.sendStatus(200); // answer at once: Pipedrive waits 10 seconds, then retries
if (meta?.entity !== "person" || !data?.id || seen.has(meta.id)) return;
seen.add(meta.id);
handlePerson(data).catch((e) => console.error(`person ${data.id}:`, e.message));
});
app.listen(process.env.PORT ?? 3000);- Pipedrive вважає доставленою будь-яку відповідь 2xx, чекає до 10 секунд і повторює невдалу доставку через 3, 30 і 150 секунд. Приймач відповідає одразу й ігнорує id подій, які вже обробив, тож за повтор не доведеться платити двічі. Набір id зберігається в пам’яті; якщо запускаєте кілька екземплярів, використовуйте базу даних.
- Вебхуки, створені в інтерфейсі Pipedrive, використовують формат v2: meta описує подію, data містить особу з first_name, last_name, emails і custom_fields.
- Поля особи оновлюються через PATCH /api/v2/persons/{id} з об’єктом custom_fields, з автентифікацією заголовком x-api-token. Старіший ендпоінт PUT /v1/persons не підтримується з 1 серпня 2026 року.
- Вебхук спрацьовує лише під час створення особи, тож оновлення, яке він робить, не запускає його знову. Якщо ви також підписалися на зміни, пропускайте події, у яких meta.change_source дорівнює api.
Що записувати назад і що з цим робити
| Результат | Рекомендована обробка в CRM |
|---|---|
| valid, причина ok | Готовий до розсилки: поштовий сервер прийняв скриньку. |
| valid, причина smtp_unreachable | Домен приймає пошту, але скриньку не перевірено. Надсилайте обережно й прибирайте жорсткі відмови. |
| risky (catch_all, smtp_unknown, timeout або одноразовий домен) | Перегляньте перед надсиланням. |
| invalid (mailbox_missing, no_mx, syntax) | Не надсилайте листів; виправте адресу або видаліть її. |
| пошук адреси, method verified | Поштовий сервер підтвердив запропоновану скриньку. |
| пошук адреси, method pattern | Припущення за поширеними форматами адрес: підтвердьте його перед використанням. |
| пошук адреси, method none | Кандидата немає: домен не має поштового сервера або ім’я не вдалося використати. |
Ліміти та вартість
- 1 кредит Cold Leads за кожну перевірку чи виклик пошуку адреси, незалежно від результату. Тариф Business ($99 на місяць) містить API та 10 000 кредитів на місяць; додаткові пакети по 1 000 кредитів коштують $5.
- 120 запитів на хвилину на ключ Cold Leads; відповідь 429 містить Retry-After: 60.
- Код для HubSpot просить у Cold Leads 12 секунд (timeout_ms), щоб укластися у 20 секунд HubSpot. Пошук адреси не має параметра ліміту часу; на повільному домені дії може забракнути часу, а кредит буде витрачено.
- Пошук адреси прибирає діакритику з імен (Novák стає novak) і відкидає інші символи поза діапазоном a–z, тож імена нелатинськими літерами спершу потрібно транслітерувати.
- Pipedrive рахує використання свого API в токенах на день на компанію; оновлення особи через PATCH /api/v2/persons/{id} коштує 5 токенів.
Питання
Який тариф HubSpot мені потрібен?
Для дій Custom code (і дій із вебхуками) у workflow HubSpot потрібен Data Hub Professional або Enterprise. З боку Cold Leads для API потрібен тариф Business.
Чому не записати запропоновану адресу одразу в поле e-mail?
Тому що з method pattern це припущення за поширеними форматами, а не підтверджена скринька. Окреме поле дає змогу комусь підтвердити адресу, перш ніж її використає послідовність листів.
Чи може пошук адреси знайти осіб, які ухвалюють рішення в компанії?
Ні. Cold Leads не має бази людей. Для пошуку адреси потрібна людина, чиє ім’я ви вже знаєте, і домен компанії.
Чи працює це з автоматизаціями Pipedrive замість вебхука?
Автоматизації Pipedrive теж можуть викликати вебхук (на тарифах Growth і вище), але вони надсилають тіло, яке ви визначаєте самі, тож розбір у приймачі має відповідати цьому тілу, а не формату вебхука v2.