Unified.to
All articles

How to Build a Sales Lead Generation Product with Unified


September 16, 2025

Unified.to makes it easy for developers to build lead generation applications that integrate with your customers' CRM of choice. With a single integration, you can connect to dozens of CRMs (like HubSpot, Salesforce, Pipedrive, and more), insert leads, enrich them, and sync all sales data—without building custom connectors for each platform.

Prerequisites

  • Node.js (v18+)
  • Unified account with CRM integration enabled (e.g., HubSpot)
  • Unified API key
  • Your customer's CRM connection ID (from Unified's embedded auth flow)
  • (Optional) Enrichment provider connection (e.g., Clearbit, ZoomInfo)

Step 1: Setting up your project

mkdir leadgen-demo
cd leadgen-demo
npm init -y
npm install @unified-api/typescript-sdk dotenv

Add your credentials to .env:

UNIFIED_API_KEY=your_unified_api_key
CONNECTION_CRM=your_customer_crm_connection_id

Step 2: Initialize the SDK

import 'dotenv/config';
import { UnifiedTo } from '@unified-api/typescript-sdk';

const { UNIFIED_API_KEY, CONNECTION_CRM } = process.env;

const sdk = new UnifiedTo({
  security: { jwt: UNIFIED_API_KEY! },
});

Step 3: Add a Lead

Before adding a lead, always check if it already exists as a lead or contact (to avoid duplicates).

export async function createLead(connectionId: string, email: string, name: string) {
  // Check if lead already exists
  const leadsResult = await sdk.crm.listCrmLeads({
    connectionId,
    query: email,
    limit: 1,
  });
  if (leadsResult.crmLeads?.length) {
    return false;
  }

  // Check if contact already exists
  const contactsResult = await sdk.crm.listCrmContacts({
    connectionId,
    query: email,
    limit: 1,
  });
  if (contactsResult.crmContacts?.length) {
    return false;
  }

  // Create the lead
  const createLeadResult = await sdk.crm.createCrmLead({
    connectionId,
    crmLead: {
      emails: [{ email }],
      name,
    },
  });
  return createLeadResult.crmLead;
}

Explanation:

  • This function checks for existing leads and contacts by email.
  • If neither exists, it creates a new lead in the CRM.

Step 4: Add a Contact

If you want to add a contact (instead of a lead), check for an existing contact first.

export async function createContact(connectionId: string, email: string, name: string) {
  const result = await sdk.crm.listCrmContacts({
    connectionId,
    query: email,
    limit: 1,
  });
  if (result.crmContacts?.length) {
    return result.crmContacts[0];
  }
  const createResult = await sdk.crm.createCrmContact({
    connectionId,
    crmContact: {
      emails: [{ email }],
      name,
    },
  });
  return createResult.crmContact;
}

Explanation:

  • Checks for an existing contact by email.
  • If not found, creates a new contact.

Step 5: Add a Company and Deal (Optional)

If you're creating a contact, you may also want to create an associated company and deal, and link them to the contact.

export async function createCompanyAndDeal(connectionId: string, contactId: string, companyName: string, website: string) {
  // Check if company exists
  const result = await sdk.crm.listCrmCompanies({
    connectionId,
    query: companyName,
    limit: 1,
  });
  if (result.crmCompanies?.length) {
    return;
  }
  // Create company
  const createCompanyResult = await sdk.crm.createCrmCompany({
    connectionId,
    crmCompany: {
      name: companyName,
      websites: [website],
    },
  });
  // Create deal
  const createDealResult = await sdk.crm.createCrmDeal({
    connectionId,
    crmDeal: {
      name: `${companyName}'s deal`,
    },
  });
  // Link company and deal to contact
  if (createCompanyResult.crmCompany?.id && createDealResult.crmDeal?.id) {
    const createContactResult = await sdk.crm.getCrmContact({
      connectionId,
      id: contactId,
    });
    await sdk.crm.updateCrmContact({
      connectionId,
      id: contactId,
      crmContact: {
        dealIds: (createContactResult.crmContact?.dealIds || []).concat(createDealResult.crmDeal.id),
        companyIds: (createContactResult.crmContact?.companyIds || []).concat(createCompanyResult.crmCompany.id),
      },
    });
  }
}

Explanation:

  • Checks for an existing company by name.
  • If not found, creates a new company and a new deal.
  • Links both to the contact.

Step 6: Enrich Leads/Contacts (Optional)

You can enrich your leads or contacts using enrichment providers (like Clearbit or ZoomInfo) via Unified.

export async function enrichPerson(connectionId: string, email: string) {
  const result = await sdk.enrichment.enrichPerson({
    connectionId,
    email,
  });
  return result.person;
}

Explanation:

  • Calls the enrichment endpoint to get additional info about a person by email.

Step 7: Putting It All Together

Here's how you might use these functions in your lead gen workflow:

async function main() {
  const email = "lead@example.com";
  const name = "Lead Name";
  const company = "Example Inc";
  const website = "https://example.com";

  // 1. Enrich the lead (optional)
  const enriched = await enrichPerson(CONNECTION_CRM!, email);

  // 2. Create the lead
  const lead = await createLead(CONNECTION_CRM!, email, name);

  // 3. Create the contact (if needed)
  const contact = await createContact(CONNECTION_CRM!, email, name);

  // 4. Create company and deal, and link to contact
  if (contact) {
    await createCompanyAndDeal(CONNECTION_CRM!, contact.id, company, website);
  }

  console.log("Lead, contact, and company/deal created and linked!");
}

main();

Summary

  • Unified lets you build a lead gen product that works with any CRM, using a single API and SDK.
  • You can add leads, contacts, companies, and deals, and enrich them with third-party data.
  • The example uses HubSpot, but the same code works for Salesforce, Pipedrive, and all Unified CRM integrations—just swap the connection ID.
All articles