Overview
Create and manage contacts and contact lists via the API
A contact represents a person you call or message. Contacts are organized into contact lists — named groups used by campaigns and automation triggers. A contact can belong to multiple lists.
| Method | Endpoint | Purpose |
|---|---|---|
GET | /contacts | List contacts, with filters and pagination |
POST | /contacts | Create a new contact |
POST | /contacts/bulk-upsert | Bulk upsert up to 5000 contacts into a named list |
POST | /contacts/upsert | Upsert a single contact and add it to a named list |
GET | /contacts/{contact_id} | Get a contact by ID, including list memberships |
PUT | /contacts/{contact_id} | Update an existing contact |
DELETE | /contacts/{contact_id} | Delete a contact |
GET | /contacts/{contact_id}/lists | Get all lists a contact belongs to |
GET | /contacts/{contact_id}/events | Get inbound events for a contact, newest first |
GET | /contacts/{contact_id}/calls | Get voice calls placed to a contact, newest first |
GET | /contacts/custom-field-keys | Get all editable contact columns for the organization |
GET | /contacts/lists | List contact lists, with pagination |
POST | /contacts/lists | Create a new contact list |
GET | /contacts/lists/{list_id} | Get a contact list by ID |
PUT | /contacts/lists/{list_id} | Update an existing contact list |
DELETE | /contacts/lists/{list_id} | Delete a contact list |
GET | /contacts/lists/{list_id}/contacts | Get contacts in a list, with pagination and search |
POST | /contacts/lists/{list_id}/contacts | Add existing contacts to a list |
DELETE | /contacts/lists/{list_id}/contacts/{contact_id} | Remove a contact from a list |
Contact fields#
| Field | Description |
|---|---|
external_id | ID from an external system (CRM, etc) |
phone | Phone number — the upsert key for /contacts/upsert |
email | Email address |
first_name / last_name | Name fields |
company | Company name |
title | Job title |
tags | List of tags for categorization |
custom_fields | Arbitrary key-value pairs |
Bulk upsert contacts#
/contacts/bulk-upsert is the fastest way to load or refresh a contact list — it matches each contact by phone, then external_id, creating or updating as needed, and creates the list automatically if it doesn't exist. Any field beyond the standard set (e.g. a CSV column like mobile or signup_date) is automatically folded into custom_fields.
curl -X POST https://app.lemuai.com/api/v1/contacts/bulk-upsert \
-H "X-API-Key: lemu_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"list_name": "Q3 leads",
"contacts": [
{ "phone": "+14155550100", "first_name": "Ana", "company": "Acme Co" },
{ "phone": "+14155550101", "first_name": "Ben", "tags": ["vip"] }
]
}'{
"created": 2,
"updated": 0,
"failed": 0,
"errors": [],
"list_created": true,
"contacts": [
{
"id": 901,
"organization_id": 7,
"external_id": null,
"phone": "+14155550100",
"email": null,
"first_name": "Ana",
"last_name": null,
"company": "Acme Co",
"title": null,
"tags": null,
"custom_fields": {},
"created_at": "2026-08-20T10:00:00Z",
"updated_at": "2026-08-20T10:00:00Z"
}
]
}Upsert a single contact#
/contacts/upsert looks up a contact by phone within the org, merging custom_fields and updating scalar fields if found, or creating it if not. Either phone or external_id is required.
curl -X POST https://app.lemuai.com/api/v1/contacts/upsert \
-H "X-API-Key: lemu_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"list_name": "Q3 leads",
"phone": "+14155550100",
"first_name": "Ana",
"tags": ["vip"]
}'{
"contact": {
"id": 901,
"organization_id": 7,
"external_id": null,
"phone": "+14155550100",
"email": null,
"first_name": "Ana",
"last_name": null,
"company": "Acme Co",
"title": null,
"tags": ["vip"],
"custom_fields": {},
"created_at": "2026-08-20T10:00:00Z",
"updated_at": "2026-08-20T10:05:00Z"
},
"contact_list": {
"id": 55,
"organization_id": 7,
"name": "Q3 leads",
"description": null,
"metadata": {},
"column_schema": [],
"created_at": "2026-08-20T10:00:00Z",
"updated_at": "2026-08-20T10:00:00Z"
},
"contact_created": false,
"list_created": false
}