All CRM endpoints are under the prefix /api/brands/{brand}/crm/ and authenticate via EF-Access-Key.
The CRM is structured as:
Entity (e.g. "Leads", "Deals")
└── Pipeline (e.g. "Sales Pipeline")
└── Stage (e.g. "Prospect", "Qualified", "Closed")
└── Fields (custom fields defined per entity)
└── Entries (the actual records)
Entity Presets
Before creating a custom entity you can list and apply built-in presets (e.g. Leads, Contacts, Deals) which come pre-configured with pipelines and fields.
GET /api/brands/{brand}/crm/entity-presets
curl https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entity-presets \
-H "EF-Access-Key: your_api_key_here"
{
"presets" : [
{ "key" : "leads" , "label" : "Leads" , "description" : "Track inbound leads through a sales process" },
{ "key" : "contacts" , "label" : "Contacts" , "description" : "Manage contacts and customers" },
{ "key" : "deals" , "label" : "Deals" , "description" : "Deal tracking with revenue stages" }
]
}
Entities
Entities define the type of record tracked in the CRM (e.g. “Leads”, “Customers”). Each brand can have multiple entities.
List Entities
GET /api/brands/{brand}/crm/entities
curl https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entities \
-H "EF-Access-Key: your_api_key_here"
[
{
"id" : 1 ,
"name" : "Leads" ,
"slug" : "leads" ,
"singular_name" : "Lead" ,
"plural_name" : "Leads" ,
"icon" : "user" ,
"color" : "#3b82f6" ,
"brand_id" : 42 ,
"pipelines_count" : 1 ,
"fields_count" : 5
}
]
Get Entity
GET /api/brands/{brand}/crm/entities/{entity}
Returns the entity with its full pipelines (including stages) and fields loaded.
{
"id" : 1 ,
"name" : "Leads" ,
"slug" : "leads" ,
"pipelines" : [
{
"id" : 1 ,
"name" : "Sales Pipeline" ,
"stages" : [
{ "id" : 1 , "name" : "New Lead" , "position" : 0 , "color" : "#94a3b8" },
{ "id" : 2 , "name" : "Contacted" , "position" : 1 , "color" : "#3b82f6" },
{ "id" : 3 , "name" : "Qualified" , "position" : 2 , "color" : "#10b981" },
{ "id" : 4 , "name" : "Closed Won" , "position" : 3 , "color" : "#22c55e" }
]
}
],
"fields" : [
{ "id" : 1 , "name" : "Email" , "key" : "email" , "type" : "text" },
{ "id" : 2 , "name" : "Phone" , "key" : "phone" , "type" : "text" },
{ "id" : 3 , "name" : "Source" , "key" : "source" , "type" : "select" ,
"options" : [ "Facebook" , "Google" , "Organic" ] }
]
}
Create Entity
POST /api/brands/{brand}/crm/entities
Display name (e.g. Leads)
URL-safe identifier — lowercase alphanumeric, hyphens, underscores (e.g. leads). Must be unique per brand.
Singular label (e.g. Lead)
Plural label (e.g. Leads)
Icon identifier (max 64 chars)
Optional preset key from Entity Presets . When set, pre-populates pipelines and fields.
curl -X POST https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entities \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Leads",
"slug": "leads",
"singular_name": "Lead",
"plural_name": "Leads",
"icon": "user",
"color": "#3b82f6",
"preset": "leads"
}'
201 Created
422 Duplicate slug
{
"id" : 1 ,
"name" : "Leads" ,
"slug" : "leads" ,
"singular_name" : "Lead" ,
"plural_name" : "Leads" ,
"icon" : "user" ,
"color" : "#3b82f6" ,
"brand_id" : 42 ,
"created_at" : "2024-12-11T10:00:00.000000Z"
}
Update Entity
PUT /api/brands/{brand}/crm/entities/{entity}
Same fields as Create, all optional on update.
Delete Entity
DELETE /api/brands/{brand}/crm/entities/{entity}
Pipelines
Pipelines belong to an entity and contain an ordered list of stages.
List Pipelines
GET /api/brands/{brand}/crm/entities/{entity}/pipelines
Get Pipeline
GET /api/brands/{brand}/crm/pipelines/{pipeline}
Create Pipeline
POST /api/brands/{brand}/crm/entities/{entity}/pipelines
Pipeline name (e.g. Sales Pipeline)
curl -X POST https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entities/1/pipelines \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "Sales Pipeline"}'
{
"id" : 1 ,
"name" : "Sales Pipeline" ,
"crm_entity_id" : 1 ,
"brand_id" : 42 ,
"created_at" : "2024-12-11T10:00:00.000000Z"
}
Update Pipeline
PUT /api/brands/{brand}/crm/pipelines/{pipeline}
Delete Pipeline
DELETE /api/brands/{brand}/crm/pipelines/{pipeline}
Stages
Stages belong to a pipeline and represent progress steps (e.g. “Prospect → Qualified → Closed”).
List Stages
GET /api/brands/{brand}/crm/pipelines/{pipeline}/stages
Get Stage
GET /api/brands/{brand}/crm/stages/{stage}
Create Stage
POST /api/brands/{brand}/crm/pipelines/{pipeline}/stages
Stage name (e.g. Qualified)
Hex color for the stage badge
Sort order (0-based integer). Use Reorder to batch-update positions.
curl -X POST https://app.elasticfunnels.io/api/brands/{brand_id}/crm/pipelines/1/stages \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "Qualified", "color": "#10b981", "position": 2}'
{
"id" : 3 ,
"name" : "Qualified" ,
"color" : "#10b981" ,
"position" : 2 ,
"pipeline_id" : 1 ,
"created_at" : "2024-12-11T10:00:00.000000Z"
}
Update Stage
PUT /api/brands/{brand}/crm/stages/{stage}
Reorder Stages
PUT /api/brands/{brand}/crm/stages/reorder
Array of {id, position} objects in the desired order.
curl -X PUT https://app.elasticfunnels.io/api/brands/{brand_id}/crm/stages/reorder \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"stages": [
{"id": 1, "position": 0},
{"id": 3, "position": 1},
{"id": 2, "position": 2},
{"id": 4, "position": 3}
]
}'
Delete Stage
DELETE /api/brands/{brand}/crm/stages/{stage}
Custom Fields
Custom fields define the schema for entry data. Each entity has its own set of fields.
List Fields
GET /api/brands/{brand}/crm/entities/{entity}/fields
Get Field
GET /api/brands/{brand}/crm/fields/{field}
Create Field
POST /api/brands/{brand}/crm/entities/{entity}/fields
Field label (e.g. Email Address)
Slug key used in values on entries (e.g. email). Lowercase alphanumeric + underscores.
Field type: text, number, date, boolean, select, multi_select, url, email, phone
For select and multi_select types — array of option strings.
Whether this field is required on entry creation (default: false)
curl -X POST https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entities/1/fields \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Lead Source",
"key": "lead_source",
"type": "select",
"options": ["Facebook", "Google", "Organic", "Referral"],
"required": false,
"position": 2
}'
{
"id" : 4 ,
"name" : "Lead Source" ,
"key" : "lead_source" ,
"type" : "select" ,
"options" : [ "Facebook" , "Google" , "Organic" , "Referral" ],
"required" : false ,
"position" : 2 ,
"crm_entity_id" : 1 ,
"created_at" : "2024-12-11T10:00:00.000000Z"
}
Update Field
PUT /api/brands/{brand}/crm/fields/{field}
Delete Field
DELETE /api/brands/{brand}/crm/fields/{field}
Entries
Entries are the individual records in a CRM entity (e.g. a single lead or deal). They are stored and queried via Elasticsearch.
List Entries
GET /api/brands/{brand}/crm/entities/{entity}/entries
Results per page (max 100, default: 25)
Filter by external reference type (e.g. conversion, lead)
Filter by external reference ID
Full-text search on title
Attach matched customer record to each entry (default: false)
curl "https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entities/1/entries?pipeline_id=1&stage_id=2&per_page=50" \
-H "EF-Access-Key: your_api_key_here"
{
"data" : [
{
"id" : "es-doc-id-abc" ,
"title" : "Jane Doe" ,
"pipeline_id" : 1 ,
"stage_id" : 2 ,
"crm_entity_id" : 1 ,
"brand_id" : 42 ,
"reference_type" : "conversion" ,
"reference_id" : "conv-123" ,
"assigned_to_user_id" : null ,
"values" : [
{ "key" : "email" , "value" : "jane@example.com" },
{ "key" : "lead_source" , "value" : "Facebook" }
],
"values_flat" : {
"email" : "jane@example.com" ,
"lead_source" : "Facebook"
},
"updated_at" : "2024-12-10T14:00:00Z"
}
],
"current_page" : 1 ,
"last_page" : 4 ,
"per_page" : 25 ,
"total" : 87
}
Get Entry
GET /api/brands/{brand}/crm/entries/{entry}
GET /api/brands/{brand}/crm/entities/{entity}/entries/{entry}
Create Entry
POST /api/brands/{brand}/crm/entities/{entity}/entries
Human-readable label for the entry (e.g. the lead’s name or email)
ID of the pipeline this entry belongs to
Flat key-value object of custom field values (e.g. {"email": "jane@example.com", "lead_source": "Facebook"}). Keys must match a defined field key on the entity.
External ID to link the entry to another record (e.g. a conversion ID)
Type of external reference (e.g. conversion, lead, order)
Assign to a specific team member by user ID
Assign to a CRM team by team ID
curl -X POST https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entities/1/entries \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Jane Doe",
"pipeline_id": 1,
"stage_id": 1,
"reference_type": "conversion",
"reference_id": "conv-123",
"values": {
"email": "jane@example.com",
"phone": "+15551234567",
"lead_source": "Facebook"
}
}'
import requests
r = requests.post(
'https://app.elasticfunnels.io/api/brands/ {brand_id} /crm/entities/1/entries' ,
headers = { 'EF-Access-Key' : 'your_api_key_here' , 'Content-Type' : 'application/json' },
json = {
'title' : 'Jane Doe' ,
'pipeline_id' : 1 ,
'stage_id' : 1 ,
'reference_type' : 'conversion' ,
'reference_id' : 'conv-123' ,
'values' : {
'email' : 'jane@example.com' ,
'lead_source' : 'Facebook' ,
},
},
)
entry = r.json()
print (entry[ 'id' ])
201 Created
422 Missing required fields
{
"id" : "es-doc-id-xyz" ,
"title" : "Jane Doe" ,
"pipeline_id" : 1 ,
"stage_id" : 1 ,
"crm_entity_id" : 1 ,
"brand_id" : 42 ,
"reference_type" : "conversion" ,
"reference_id" : "conv-123" ,
"values" : [
{ "key" : "email" , "value" : "jane@example.com" },
{ "key" : "lead_source" , "value" : "Facebook" }
],
"values_flat" : {
"email" : "jane@example.com" ,
"lead_source" : "Facebook"
},
"created_at" : "2024-12-11T10:00:00Z" ,
"updated_at" : "2024-12-11T10:00:00Z"
}
Update Entry
PUT /api/brands/{brand}/crm/entries/{entry}
PUT /api/brands/{brand}/crm/entities/{entity}/entries/{entry}
Same body as Create. All fields optional — only provided fields are updated.
cURL — update values and reassign
curl -X PUT https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entries/es-doc-id-xyz \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"values": { "lead_source": "Google", "phone": "+15559876543" },
"assigned_to_user_id": 7
}'
Move Entry to Stage
Move an entry to a different stage within the same pipeline.
PUT /api/brands/{brand}/crm/entries/{entry}/stage
PUT /api/brands/{brand}/crm/entities/{entity}/entries/{entry}/stage
Target stage ID (must belong to the entry’s current pipeline)
curl -X PUT https://app.elasticfunnels.io/api/brands/{brand_id}/crm/entries/es-doc-id-xyz/stage \
-H "EF-Access-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{"stage_id": 3}'
{
"id" : "es-doc-id-xyz" ,
"stage_id" : 3 ,
"updated_at" : "2024-12-11T10:05:00Z"
}
Delete Entry
DELETE /api/brands/{brand}/crm/entries/{entry}
DELETE /api/brands/{brand}/crm/entities/{entity}/entries/{entry}
Automation: Create CRM Entry from Conversion
A common pattern is creating a CRM entry whenever a conversion is recorded. Using the EF automation pipeline or an external webhook:
import requests
BRAND_ID = 42
API_KEY = "your_api_key_here"
BASE = f "https://app.elasticfunnels.io/api/brands/ { BRAND_ID } /crm"
H = { "EF-Access-Key" : API_KEY , "Content-Type" : "application/json" }
def create_lead_from_conversion ( conversion : dict ) -> dict :
"""Create a CRM entry from a conversion webhook payload."""
return requests.post(
f " { BASE } /entities/1/entries" ,
headers = H,
json = {
"title" : f " { conversion[ 'first_name' ] } { conversion[ 'last_name' ] } " ,
"pipeline_id" : 1 ,
"stage_id" : 1 , # "New Lead"
"reference_type" : "conversion" ,
"reference_id" : str (conversion[ "id" ]),
"values" : {
"email" : conversion.get( "email" ),
"phone" : conversion.get( "phone" ),
"lead_source" : conversion.get( "utm_source" , "unknown" ),
},
},
).json()