Unified.to
All articles

How to Push Leads and Contacts into CRMs with Unified's CRM API


February 2, 2026

Pushing data into a CRM sounds simple. Create a contact. Create a lead. Attach them to a company. Move on.

In practice, it only works if you understand what the CRM actually accepts.

CRMs differ in how they handle identity, associations, and custom fields. Some treat contacts as primary records. Others prioritize leads. Company association may require an ID in one system and a name in another. Custom fields may exist, but only if they're written in the right format. Required fields are often integration-specific and undocumented until a write fails.

If you don't design for that variability, write flows become brittle. You end up retrying failed writes blindly or hardcoding logic per CRM.

Unified's CRM API gives you a normalized write surface across CRMs. You work with a single set of CRM objects—contacts, leads, and companies—and use documented create, update, retrieve, and list operations to implement safe, portable write flows.

This guide shows how to push leads and contacts into CRMs using Unified's CRM API in TypeScript. It focuses on create, find-then-update patterns, associations, and custom fields, without assuming required fields, upsert behavior, or integration-specific guarantees.

Prerequisites

  • Node.js v18+
  • A Unified account with a CRM integration enabled
  • Your Unified API key
  • A customer CRM connectionId

Step 1: Set up your project

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

Create a .env file:

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;

if (!UNIFIED_API_KEY) throw new Error("Missing UNIFIED_API_KEY");
if (!CONNECTION_CRM) throw new Error("Missing CONNECTION_CRM");

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

const connectionId = CONNECTION_CRM;

Step 3: Understand what you can write (and what varies)

Unified normalizes CRM objects, but write support still varies by integration. Required fields, association behavior, and custom field support can differ across CRMs.

For this article, we rely only on fields that are:

  • defined in the CRM data models
  • accepted by create and update endpoints
  • commonly writable across CRMs

Anything integration-specific should be verified in the Feature Support tab for the integration you've enabled.

Contacts (CrmContact)

Contacts represent people and support structured fields, associations, and metadata.

Commonly written fields include:

  • identity: name, first_name, last_name, title, department
  • company context: company (string), company_ids[] (IDs if known)
  • contact methods:
    • emails[] with type (WORK, HOME, OTHER)
    • telephones[] with type (WORK, HOME, OTHER, FAX, MOBILE)
  • address fields with ISO-2 country_code
  • attribution: user_id
  • custom fields via metadata[]
  • raw passthrough when needed

Leads (CrmLead)

Leads represent top-of-funnel records and can be associated to contacts and companies.

Commonly written fields include:

  • identity: name, first_name, last_name
  • associations: contact_id, company_id, or company_name
  • state: source, status, is_active
  • contact methods: emails[], telephones[]
  • attribution: user_id, creator_user_id
  • custom fields via metadata[]
  • raw passthrough

Companies (CrmCompany) optional

If you want to create or update companies directly, Unified supports full CRUD for companies as well. For this article, company creation is optional. You can associate contacts and leads using names or IDs without creating companies explicitly.

Step 4: Create a contact

Creating a contact uses POST /crm/{connection_id}/contact via the SDK.

Start with a minimal payload. Required fields are integration-dependent, so avoid assuming email or name requirements unless you've verified them for your CRM.

const contact = await sdk.crm.createCrmContact({
  connectionId,
  crmContact: {
    first_name: "Alex",
    last_name: "Morgan",
    title: "VP Engineering",
    emails: [
      { email: "alex@example.com", type: "WORK" },
    ],
    company: "Example Corp",
  },
});

The call returns a CrmContact object, including the generated id. Store this ID if you plan to update or associate the contact later.

Step 5: Find an existing contact safely

Unified does not provide a true upsert endpoint. To avoid duplicates, implement a find-then-update pattern.

The list endpoint supports a query parameter that searches by name or email, depending on the CRM.

const matches = await sdk.crm.listCrmContacts({
  connectionId,
  limit: 1,
  offset: 0,
  query: "alex@example.com",
});

If matches.length > 0, treat the first result as the existing contact. If no results are returned, create a new contact.

Step 6: Update an existing contact

Updates use PUT /crm/{connection_id}/contact/{id}.

Send only the fields you want to change. Fields not included in the payload are left unchanged.

const updated = await sdk.crm.updateCrmContact({
  connectionId,
  id: contact.id,
  crmContact: {
    title: "Chief Architect",
    department: "Platform",
  },
});

The response returns the updated CrmContact.

Step 7: Create a lead

Creating a lead uses POST /crm/{connection_id}/lead.

You can associate a lead to a company by ID or by name. Use IDs when you already have them; otherwise, pass a name and let the CRM handle matching.

const lead = await sdk.crm.createCrmLead({
  connectionId,
  crmLead: {
    first_name: "Jordan",
    last_name: "Lee",
    company_name: "Example Corp",
    source: "Website",
    status: "New",
    emails: [
      { email: "jordan@example.com", type: "WORK" },
    ],
  },
});

As with contacts, the response includes the lead id.

Step 8: Find or update a lead

Use the same find-then-update pattern for leads.

const leads = await sdk.crm.listCrmLeads({
  connectionId,
  limit: 1,
  offset: 0,
  query: "jordan@example.com",
});

if (leads.length > 0) {
  await sdk.crm.updateCrmLead({
    connectionId,
    id: leads[0].id,
    crmLead: {
      status: "Qualified",
    },
  });
}

Step 9: Write custom fields with metadata

Both contacts and leads support metadata[] for custom fields.

Each metadata entry includes:

  • slug to identify the field
  • value
  • format to describe the data type
await sdk.crm.updateCrmContact({
  connectionId,
  id: contact.id,
  crmContact: {
    metadata: [
      {
        slug: "account_tier",
        value: "enterprise",
        format: "SINGLE_SELECT",
      },
      {
        slug: "employee_count",
        value: 1200,
        format: "NUMBER",
      },
    ],
  },
});

Custom field support varies by CRM. Always verify field availability and write support in the Feature Support tab.

Step 10: Verify writes with retrieve

After creating or updating records, retrieve them by ID to confirm the write succeeded.

const verified = await sdk.crm.getCrmContact({
  connectionId,
  id: contact.id,
});

This is especially useful when debugging integration-specific constraints or required fields.

Step 11: Optional cleanup

Unified supports record deletion if your workflow requires it.

await sdk.crm.removeCrmLead({
  connectionId,
  id: lead.id,
});

Deletion support varies by CRM and should be used cautiously.

Summary

Using Unified's CRM API, you can push leads and contacts into CRMs with a single, consistent write surface:

  • Create and update contacts and leads using normalized schemas
  • Associate records using IDs or names
  • Write custom fields through metadata[]
  • Implement safe find-then-update flows without assuming upsert
  • Verify writes using retrieve endpoints
  • Handle pagination, filtering, and integration variance explicitly

Start your 30-day free trial

Book a demo

All articles