Contacts
Store customers and companies with tags, segments, custom fields, and links to plugin records.
For custom attributes, see Properties, Tags & Segments. For bulk loads, see Import & Export.
Module layout
backend/core-api/src/modules/contacts/
graphql/schemas/{customer,company}.ts
graphql/resolvers/{queries,mutations,customResolvers}/
db/definitions/{customers,company}.ts
db/models/{Customers,Companies}.ts
trpc/{customer,company,index}.ts # service-to-service access
meta/activity-log/{customers,companies}/
meta/import-export/{import,export}/ # CSV handlers per entity
constants.ts, utils.ts, @types/customer.ts
Linking modules alongside it: modules/conformities/ (legacy type-pair links) and modules/relations/ (generic entity-to-entity edges). Frontend lives in frontend/core-ui/src/modules/contacts/{customers,companies,client-portal-users} with shared pickers in frontend/libs/ui-modules/src/modules/contacts/.
Data model
Customer (customers.ts):
| Field | Notes |
|---|---|
state | visitor by default; promoted to lead/customer |
firstName, lastName, middleName, primaryEmail, emails, primaryPhone, phones, code | Identity and contact points |
emailValidationStatus, phoneValidationStatus | unknown until verified |
visitorContactInfo, trackedData, location | Visitor/session capture |
leadStatus, status, isSubscribed, ownerId, position, department | Pipeline and ownership |
tagIds, customFieldsData, propertiesData, links | Labels and custom data |
searchText, searchTokens | Maintained by the search token config (below) |
Company (db/definitions/company.ts): primaryName, names, code, size, industry, businessType, website, primaryEmail, primaryPhone, addresses, parentCompanyId, score, doNotDisturb, mergedIds, plus the same tagIds, customFieldsData, propertiesData, trackedData, ownerId, status pattern.
Search tokens
customerSearchTokenConfig in db/definitions/customers.ts controls partial-match search. Each entry names a path, a mode (prefix, word-prefix, exact), and a minLength:
| Path | Mode | Min length |
|---|---|---|
firstName, middleName, lastName | prefix | 2 |
primaryEmail, emails | word-prefix | 3 |
primaryPhone, phones | word-prefix | 1 |
code | exact | 1 |
visitorContactInfo.email | word-prefix | 3 |
visitorContactInfo.phone | word-prefix | 1 |
Tokens are materialized into searchTokens (indexed with state and status), so searchValue filters stay MongoDB queries. Inputs shorter than minLength do not match, so a too-short search can look like it finds nothing.
GraphQL API
Customers (graphql/schemas/customer.ts)
| Operation | Key arguments |
|---|---|
customers | searchValue, type, ids, excludeIds, tagIds, excludeTagIds, tagWithRelated, segment, segmentIds, brandIds, integrationIds, integrationTypes, clientPortalId, formIds, leadStatus, status, sex, birthDate, dateFilters, propertiesData, emailValidationStatus, sortField, sortDirection, autoCompletion, autoCompletionType, startDate, endDate, conformity args, cursor params; returns CustomersListResponse |
customerDetail | _id! |
customersCount | types: [CUSTOMER_RELATION_TYPE] |
contactsLogs | action, content, contentType |
cpCustomers / cpCustomerDetail | client-portal variants |
customersAdd / customersEdit | state, firstName, lastName, primaryEmail, emails, primaryPhone, phones, primaryAddress, addresses, ownerId, position, department, leadStatus, description, links, code, sex, birthDate, avatar, isSubscribed, propertiesData, validation statuses |
customersMerge | customerIds, customerFields |
customersRemove | customerIds |
customersChangeState / customersChangeStateBulk | _id!, value! / _ids: [String!]!, value! |
customersVerify, customersChangeVerificationStatus | verificationType! / customerIds, type!, status! |
Companies (graphql/schemas/company.ts)
| Operation | Key arguments |
|---|---|
companies | same selector family as customers; returns CompaniesListResponse |
companyDetail | _id! |
companiesAdd / companiesEdit | primaryName, names, avatar, size, industry, businessType, website, primaryEmail, emails, primaryPhone, phones, primaryAddress, addresses, parentCompanyId, code, location, ownerId, tagIds, propertiesData, links, isSubscribed |
companiesMerge | companyIds, companyFields |
companiesRemove | companyIds |
Linking records
- Conformities (
modules/conformities):conformityAdd,conformityEditmutations store main-type/rel-type pairs. Filter contacts withconformityMainType,conformityMainTypeId,conformityRelType,conformityIsRelated,conformityIsSavedon the list queries. - Relations (modules/relations): generic
entities: [{ contentType, contentId }]edges.createRelation,manageRelations,getRelationsByEntitylet plugins link contacts to deals, tickets, or their own types without touching the contact document.
List queries return a cursor envelope { list, pageInfo { hasNextPage, endCursor }, totalCount }, controlled by limit, cursor, cursorMode, direction, orderBy.
Example
query CustomersByTag($tagIds: [String], $cursor: String) {
customers(tagIds: $tagIds, cursor: $cursor, limit: 20) {
list {
_id
firstName
primaryEmail
tagIds
customFieldsData
cursor
}
totalCount
pageInfo { hasNextPage endCursor }
}
}
mutation AddCustomer {
customersAdd(
firstName: "Ari"
primaryEmail: "[email protected]"
primaryPhone: "+15551234567"
code: "CUST-001"
) { _id state }
}
tRPC access
Other services reach contacts through sendTRPCMessage({ pluginName: 'core', module: 'customers', action: ... }). Procedures on trpc/customer.ts include find, findOne, findActiveCustomers, getCustomerName, getWidgetCustomer, count, createCustomer, updateCustomer, updateOne, updateMany, removeCustomers, markCustomerAsActive, createMessengerCustomer, updateMessengerCustomer, saveVisitorContactInfo, updateLocation, updateSession, setUnsubscribed, createOrUpdate, tag. The company router mirrors the core CRUD set.
Permissions
Mutations check core actions from meta/permissions.ts: contactsCreate, contactsUpdate, contactsDelete, contactsMerge, customersImportManage, customersExportManage, companiesImportManage, companiesExportManage. contactsRead is marked always: true and supports scopes own (records in createdBy/ownerId) and all.