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[]withtype(WORK,HOME,OTHER)telephones[]withtype(WORK,HOME,OTHER,FAX,MOBILE)
- address fields with ISO-2
country_code - attribution:
user_id - custom fields via
metadata[] rawpassthrough 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, orcompany_name - state:
source,status,is_active - contact methods:
emails[],telephones[] - attribution:
user_id,creator_user_id - custom fields via
metadata[] rawpassthrough
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:
slugto identify the fieldvalueformatto 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