Cold Leads

Перевірка або пошук 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 для контактів

  1. Створіть властивості контакту для результатів, наприклад Cold Leads status, Cold Leads score, Cold Leads reason і Suggested e-mail.
  2. Перейдіть в Automation > Workflows і створіть workflow для контактів. Тригер: подія Object created (категорія CRM) з уточнювальним фільтром (refinement filter), наприклад Email is known, або тригер за фільтром (Met filter criteria) з умовою Email is known. Уточнювальні фільтри оцінюються лише в момент події, тож контакт, створений без e-mail, якому адресу додали пізніше, не потрапить у workflow через тригер події.
  3. Натисніть значок +, знайдіть Custom code і виберіть його. Для Custom code потрібен Data Hub Professional або Enterprise.
  4. Залиште мовою Node.js. Натисніть Add secret, введіть назву секрету COLDLEADS_API_KEY і ваш ключ sk_ як значення, збережіть і позначте секрет галочкою.
  5. У Properties to include in code додайте Email, First name, Last name і Website URL з іменами email, firstname, lastname і website.
  6. Вставте код нижче. У Data outputs додайте виходи, перелічені на початку коду, з їхніми типами даних.
  7. Скористайтеся Test action на тестовому контакті. Тест виконує справжній код для вибраного контакту, тож витрачає кредит.
  8. Додайте дію Edit record (значок +, CRM, Edit record), виберіть властивість, потім в Action data натисніть More action data, дію Custom code і потрібний вихід. Повторіть для кожної властивості.
Custom code (Node.js)
// 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: приймач вебхуків

  1. Створіть два текстові поля особи, наприклад E-mail check і Suggested e-mail, і скопіюйте їхні API-ключі в Company settings > Data fields > Person, у меню кожного поля (Copy API key). Для пошуку адреси також запишіть ключ поля особи, яке містить сайт або домен компанії.
  2. Запустіть приймач нижче на будь-якому хості з Node.js 18+ і HTTPS-адресою, задавши змінні оточення, які він перелічує.
  3. У 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.
  4. Додайте особу з e-mail-адресою; поле E-mail check заповниться невдовзі після цього.
pipedrive_receiver.mjs
// 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.