Cold Leads

Ověření nebo dohledání e-mailových adres nových kontaktů v HubSpotu a Pipedrive

Pro koho
Týmy sales operations, které pracují v HubSpotu nebo Pipedrive
Problém
Kontakty přicházejí z formulářů, importů a ručního zadávání s překlepy, mrtvými doménami nebo úplně bez adresy a nikdo je nekontroluje, dokud se e-maily z kampaně nezačnou vracet jako nedoručitelné.
Řešení
Zkontrolujte každý kontakt při jeho vytvoření: akcí Custom code ve workflow HubSpotu nebo malým přijímačem webhooků pro Pipedrive. Kontakt s e-mailem se ověří; kontakt bez e-mailu, ale se jménem a webem firmy dostane navrženou adresu, jasně označenou jako odhad.
Co získáte
Vlastnosti kontaktu v HubSpotu nebo pole osoby v Pipedrive, které ukazují stav, skóre a důvod z Cold Leads, nebo navrženou adresu s její metodou a mírou jistoty.

Adresy, identifikátory a výsledky v příkladech jsou ilustrativní. Doména example.com je vyhrazená pro dokumentaci, takže skutečná kontrola těchto adres vrátí invalid.

Ukázky kódu jsou ve všech jazycích stejné, komentáře v nich jsou anglicky.

Jak to do sebe zapadá

  • Neexistuje žádná vestavěná integrace: workflow nebo přijímač volá API Cold Leads s vaším tajným klíčem a odpověď zapisují zpět vlastní nástroje CRM. Volání API kontakt do Cold Leads nepřidává.
  • Kontakt s e-mailem: POST /api/v1/verify (1 kredit) vrací status (valid, risky nebo invalid), skóre a kódy důvodu.
  • Kontakt bez e-mailu, ale s křestním jménem nebo příjmením a webem firmy: POST /api/v1/find (1 kredit) vrací nejpravděpodobnější adresu. Její method je verified jen tehdy, když poštovní server schránku potvrdil, jinak pattern: odhad z běžných formátů adres. Ukládejte ji do samostatného pole a zkontrolujte ji; pole e-mailu nikdy nepřepisujte odhadem.

HubSpot: akce Custom code ve workflow kontaktů

  1. Vytvořte vlastnosti kontaktu pro výsledky, například Cold Leads stav, Cold Leads skóre, Cold Leads důvod a Navržený e-mail.
  2. Přejděte do Automation > Workflows a vytvořte workflow pro kontakty. Spouštěč: událost Object created (kategorie CRM) se zpřesňujícím filtrem (refinement filter), například Email is known, nebo spouštěč podle filtru (Met filter criteria) na Email is known. Zpřesňující filtry se vyhodnocují jen v okamžiku události, takže kontakt vytvořený bez e-mailu, který ho dostane později, se přes spouštěč události do workflow nezařadí.
  3. Klikněte na ikonu +, vyhledejte Custom code a vyberte ho. Custom code vyžaduje Data Hub Professional nebo Enterprise.
  4. Jako jazyk ponechte Node.js. Klikněte na Add secret, zadejte název secretu COLDLEADS_API_KEY a jako hodnotu svůj klíč sk_, uložte a secret zaškrtněte.
  5. V části Properties to include in code přidejte Email, First name, Last name a Website URL s názvy email, firstname, lastname a website.
  6. Vložte kód níže. V části Data outputs přidejte výstupy uvedené na začátku kódu s jejich datovými typy.
  7. Použijte Test action na testovacím kontaktu. Test spouští skutečný kód nad kontaktem, který vyberete, takže spotřebuje kredit.
  8. Přidejte akci Edit record (ikona +, CRM, Edit record), vyberte vlastnost, pak v části Action data klikněte na More action data, na akci Custom code a na výstup. Opakujte pro každou vlastnost.
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 dává akci Custom code 20 sekund a 128 MB. Když kód po chybě 429 nebo 5xx z axios vyhodí výjimku, HubSpot akci opakuje až tři dny, poprvé o minutu později; proto kód tyto chyby vyhazuje dál a každou jinou chybu, například 402 no_credits, převádí na výstupní hodnotu.

Pipedrive: přijímač webhooků

  1. Vytvořte dvě textová pole osoby, například Kontrola e-mailu a Navržený e-mail, a zkopírujte jejich API klíče v Company settings > Data fields > Person, v nabídce každého pole (Copy API key). Pro dohledání adresy si poznamenejte i klíč pole osoby, které obsahuje web nebo doménu firmy.
  2. Spusťte přijímač níže na libovolném hostu s Node.js 18+ a HTTPS adresou, s proměnnými prostředí, které uvádí.
  3. V Pipedrive otevřete Settings > Tools and apps > Webhooks a vytvořte webhook: Event action create, Event object person, User permission level s viditelností, která pokrývá nové osoby, Webhook name, Endpoint URL vašeho přijímače (jeho HTTPS adresa, za kterou následuje /pipedrive/person) a HTTP Auth username a password, které odpovídají HOOK_USER a HOOK_PASSWORD.
  4. Přidejte osobu s e-mailovou adresou; pole Kontrola e-mailu se krátce poté vyplní.
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 považuje jakoukoli odpověď 2xx za doručení, čeká až 10 sekund a neúspěšné doručení opakuje po 3, 30 a 150 sekundách. Přijímač odpoví okamžitě a ignoruje id událostí, které už zpracoval, takže opakované doručení se neúčtuje dvakrát. Množina id je v paměti; pokud provozujete víc instancí, použijte databázi.
  • Webhooky vytvořené v rozhraní Pipedrive používají formát v2: meta popisuje událost, data obsahuje osobu s first_name, last_name, emails a custom_fields.
  • Pole osoby se aktualizují přes PATCH /api/v2/persons/{id} s objektem custom_fields a autentizací hlavičkou x-api-token. Starší endpoint PUT /v1/persons není od 1. srpna 2026 podporován.
  • Webhook se spouští jen při vytvoření osoby, takže aktualizace, kterou provede, ho znovu nespustí. Pokud odebíráte i změny, přeskakujte události, jejichž meta.change_source je api.

Co zapsat zpět a co s tím dělat

VýsledekDoporučený postup v CRM
valid, důvod okPřipraveno k oslovení: poštovní server schránku přijal.
valid, důvod smtp_unreachableDoména přijímá poštu, ale schránka se nekontrolovala. Posílejte opatrně a adresy s hard bounce odstraňujte.
risky (catch_all, smtp_unknown, timeout nebo jednorázová doména)Před odesláním zkontrolovat.
invalid (mailbox_missing, no_mx, syntax)Neposílat e-mail; adresu opravit, nebo odstranit.
dohledání, method verifiedPoštovní server navrženou schránku potvrdil.
dohledání, method patternOdhad z běžných formátů adres: před použitím ho potvrďte.
dohledání, method noneŽádný kandidát: doména nemá poštovní server, nebo jméno nešlo použít.

Limity a náklady

  • 1 kredit Cold Leads za každé ověření nebo dohledání adresy bez ohledu na výsledek. Tarif Business ($99 měsíčně) zahrnuje API a 10 000 kreditů měsíčně; další balíčky po 1 000 kreditech stojí $5.
  • 120 požadavků za minutu na klíč Cold Leads; odpověď 429 nese Retry-After: 60.
  • Kód pro HubSpot žádá Cold Leads o časový limit 12 sekund (timeout_ms), aby se vešel do 20 sekund HubSpotu. Dohledání adresy parametr časového limitu nemá; u pomalé domény může akci dojít čas a kredit je spotřebován.
  • Dohledání adresy odstraňuje ze jmen diakritiku (z Novák se stane novak) a vypouští ostatní znaky mimo a až z, takže jména v nelatinkových písmech je třeba nejdřív přepsat do latinky.
  • Pipedrive měří své API v tokenech za den na firmu; aktualizace osoby přes PATCH /api/v2/persons/{id} stojí 5 tokenů.

Otázky

Jaký tarif HubSpotu potřebuji?

Akce Custom code (i akce webhooků) ve workflow HubSpotu vyžadují Data Hub Professional nebo Enterprise. Na straně Cold Leads vyžaduje API tarif Business.

Proč nezapsat navrženou adresu rovnou do pole e-mailu?

Protože s method pattern jde o odhad z běžných formátů, ne o potvrzenou schránku. Ve vlastním poli ji může někdo potvrdit dřív, než ji použije sekvence.

Dokáže dohledání adresy najít rozhodovatele ve firmě?

Ne. Cold Leads nemá databázi lidí. Dohledání adresy potřebuje osobu, kterou už znáte jménem, a k tomu doménu firmy.

Funguje to i s automatizacemi Pipedrive místo webhooku?

Automatizace Pipedrive umí webhook zavolat také (v tarifech Growth a vyšších), ale posílají tělo, které si definujete sami, takže parsování v přijímači musí odpovídat tomuto tělu, a ne formátu webhooků v2.