How entries attach to records
Each CRM entry is linked to a record in your funnel or store through two values:| Field | Description |
|---|---|
reference_type | The type of record (e.g. customer, order, lead) |
reference_id | The ID of that record |
Concepts
Entries vs. Fields: An entry is a single record or document stored in the CRM (like a row in a database, representing e.g. a specific check-in for a customer). A field is a specific piece of data that lives inside an entry (like a column, e.g. “current_weight” or “deal_value”).
Entity Modes
Entities operate in one of two modes:| Mode | Description |
|---|---|
| CRM (default) | Traditional pipeline-based entity with stages, suitable for deals, leads, contacts |
| Data | Standalone data collection without pipelines — ideal for storing structured records like properties, inventory items, cities, products |
Entities
An entity defines a type of CRM record (e.g. “Deals”, “Contacts”, “Properties”). Each entity has:- A name and slug (machine-readable identifier)
- An entity mode (
crmordata) - Fields that define the data structure
- Pipelines with stages for workflow tracking (CRM mode only)
Fields
Fields define what data can be stored on entries. Each field has:| Property | Description |
|---|---|
label | Human-readable label |
key | Machine-readable key (used in API and templates) |
type | See supported types below |
ui_editable | Whether this field can be edited in the admin UI (default true) |
reporting_config | Optional analytics metadata for CRM reports |
Supported field types
| Type | Renders as | Notes |
|---|---|---|
text | Text input | Default type |
textarea | Multi-line text | |
rich_text, wysiwyg | Rich text | Formatted content stored as HTML |
email | Email input | Rendered as mailto link |
phone | Phone input | Rendered as tel link |
url | URL input | Rendered as clickable link |
number | Numeric input | Stored as double |
date | Date picker | ISO 8601 string |
datetime | Date+time picker | ISO 8601 string |
boolean | Toggle switch | true/false |
select | Dropdown | Requires options array |
multiselect | Multi-select tags | Requires options array |
color | Color picker | Hex color code |
image | Image URL / uploader | Renders as thumbnail |
file | File URL / uploader | Renders as download link |
json | JSON editor | Renders as collapsible tree |
reference | Entity reference | Links to single entry in another entity |
multi_reference | Multi-reference | Links to multiple entries in another entity |
setCrmField always sends a JavaScript value — a string, number, boolean, or a JSON object. For fields configured as date in the CRM, pass a date-time string from scripts, typically ISO 8601 (e.g. new Date().toISOString() or '2026-03-15').
UI-editable fields
Setui_editable: false on fields that should only be updated programmatically (via backend scripts). These fields display with a lock icon in the admin UI and cannot be inline-edited.
Use reporting_config to mark fields as reportable and suggest how the Reports tab should aggregate and chart them. Supported values include reportable, aggregation_type (numeric, categorical, date, boolean), baseline (min/max), bucket_size, and preferred_chart (kpi, histogram, pie, bar, line).
Relations
Relations link entries across entities. A field of typereference or multi_reference creates a relation between the current entity and a target entity. Relations are stored in Elasticsearch alongside the entry data.
Configuring reference fields
When creating a reference field, specify:- Target entity: Which entity the reference points to
- Display field: Which field from the target entity to show as the label (defaults to title)
Working with relations programmatically
UseSetCrmRelation and RemoveCrmRelation to manage relations from backend scripts:
Pipelines & Stages
Pipelines represent workflows (CRM mode only). Each pipeline contains ordered stages with optional colors and semantic statuses (e.g.won, lost, active). Entries move through stages as they progress.
Customer CRM Data
The most common pattern is linking CRM entries to customers. When a customer visits a page, their session supplies the customer context so CRM defaults attach to the right person (creating the customer record when needed).Example: Weight Loss Tracker
- Create a CRM entity called “Weight Tracker” with slug
weight-trackerand fields likecurrent_weight,goal_weight,last_checkin - Create a pipeline “Progress” with stages “Starting”, “In Progress”, “Goal Reached”
- Store data via backend scripts or the template engine:
- Read it back on any page:
Viewing Customer CRM Data
Customer CRM entries are visible in the Customer Details page under the CRM tab. This shows all CRM entries linked to that customer across all entity types.Functions
The following functions are available in both the template engine and backend scripts. Write functions accept an optionalasync parameter (default false). When true, the write runs in the background for faster response times. Read functions are always synchronous.
| Function | Description |
|---|---|
EnsureCrmEntity({ slug, ... }) | Idempotently create an entity with fields (returns { id, slug, created, fields_added }) |
CreateCrmEntry({ slug, title?, values?, ... }) | Create a new entry and return its ID |
getCrmEntries({ slug, ... }) | Fetch all entries for one entity slug + reference |
getCrmField({ slug, fieldKey, ... }) | Get one field value (slug + fieldKey required) |
getCrmFields({ slug, fieldKeys, ... }) | Get multiple field values (slug + fieldKeys required) |
setCrmField({ slug, fieldKey, value, ... }) | Set one field value — upserts a single value per key |
setCrmFields({ slug, fields, ... }) | Set multiple field values in one read/write cycle |
addCrmFieldValue({ slug, fieldKey, value, ... }) | Append a new entry with a field value — enables multiple values per key |
getCrmFieldValues({ slug, fieldKey, ... }) | Get all values for a field key across entries (returns array) |
clearCrmEntries({ slug, ... }) | Delete all entries for an entity slug + reference |
SetCrmRelation({ slug, relationKey, targetEntryId, ... }) | Add a relation from entry to another entry |
RemoveCrmRelation({ slug, relationKey, targetEntryId, ... }) | Remove a specific relation |
moveCrmLead({ slug, pipelineId/pipelineSlug, stageId/stageSlug, ... }) | Move entry to a specific pipeline + stage |
moveCrmToStage({ slug, stageId/stageSlug, ... }) | Move entry to a stage (pipeline resolved from stage) |
moveCrmToPipeline({ slug, pipelineId/pipelineSlug, ... }) | Move entry to a pipeline (lands on first stage) |
moveCrmToEntity({ fromSlug, toSlug, fieldMap, ... }) | Copy mapped fields across entity types |
slug is always required (the entity’s slug from CRM settings). Reference defaults: customer + session customer id.
Common options
Every CRM function accepts these optional properties:| Property | Type | Default | Description |
|---|---|---|---|
referenceType | string | 'customer' | Linked record type |
referenceId | string | session customer_id | Linked record id |
| Property | Type | Default | Description |
|---|---|---|---|
async | boolean | false | When true, writes run in the background — faster but eventual consistency |
Single-value fields
UsesetCrmField / getCrmField for fields that hold one value per key (e.g. weight, goal, last check-in date):
getCrmFields and pass an array of fieldKeys:
setCrmFields so all values are merged in one read/write cycle:
setCrmField calls, pass force: true to skip the per-key scan and merge directly into the newest entry — much faster for sequential writes:
Multi-value fields
UseaddCrmFieldValue / getCrmFieldValues when a key can have multiple values over time (e.g. weigh-in history). Each addCrmFieldValue call creates a separate CRM entry:
Clearing entries
UseclearCrmEntries to delete all entries for an entity + reference:
Pipeline and stage movement
UsemoveCrmLead to move a CRM entry to a specific pipeline and stage by id or slug:
{ ok, moved, totalMatched, pipeline_id, stage_id }. moved is the number of entries updated (1 unless allEntries: true).
Use moveCrmToStage when you only know the stage — the pipeline is resolved automatically:
moveCrmToPipeline to move an entry into a pipeline; it lands on the first stage:
moveCrmToEntity to copy field values from one entity type to another (e.g. promoting a lead to a deal):
{ ok, moved, removed_from_old }.
Pass allEntries: true to any move function to update all matching entries instead of just the newest one.
Ensuring entities programmatically
UseEnsureCrmEntity at the top of a backend script to idempotently provision an entity. Re-runs never overwrite admin edits — only missing fields are added.
Creating entries
UseCreateCrmEntry to create a new entry and get back its ID (useful for setting relations):
Managing relations
UseSetCrmRelation to link entries:
RemoveCrmRelation to unlink:
Async writes
For non-critical writes where you don’t need immediate consistency, passasync: true so the write runs in the background: