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):

FieldNotes
statevisitor by default; promoted to lead/customer
firstName, lastName, middleName, primaryEmail, emails, primaryPhone, phones, codeIdentity and contact points
emailValidationStatus, phoneValidationStatusunknown until verified
visitorContactInfo, trackedData, locationVisitor/session capture
leadStatus, status, isSubscribed, ownerId, position, departmentPipeline and ownership
tagIds, customFieldsData, propertiesData, linksLabels and custom data
searchText, searchTokensMaintained 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:

PathModeMin length
firstName, middleName, lastNameprefix2
primaryEmail, emailsword-prefix3
primaryPhone, phonesword-prefix1
codeexact1
visitorContactInfo.emailword-prefix3
visitorContactInfo.phoneword-prefix1

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)

OperationKey arguments
customerssearchValue, 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!
customersCounttypes: [CUSTOMER_RELATION_TYPE]
contactsLogsaction, content, contentType
cpCustomers / cpCustomerDetailclient-portal variants
customersAdd / customersEditstate, firstName, lastName, primaryEmail, emails, primaryPhone, phones, primaryAddress, addresses, ownerId, position, department, leadStatus, description, links, code, sex, birthDate, avatar, isSubscribed, propertiesData, validation statuses
customersMergecustomerIds, customerFields
customersRemovecustomerIds
customersChangeState / customersChangeStateBulk_id!, value! / _ids: [String!]!, value!
customersVerify, customersChangeVerificationStatusverificationType! / customerIds, type!, status!

Companies (graphql/schemas/company.ts)

OperationKey arguments
companiessame selector family as customers; returns CompaniesListResponse
companyDetail_id!
companiesAdd / companiesEditprimaryName, names, avatar, size, industry, businessType, website, primaryEmail, emails, primaryPhone, phones, primaryAddress, addresses, parentCompanyId, code, location, ownerId, tagIds, propertiesData, links, isSubscribed
companiesMergecompanyIds, companyFields
companiesRemovecompanyIds

Linking records

  • Conformities (modules/conformities): conformityAdd, conformityEdit mutations store main-type/rel-type pairs. Filter contacts with conformityMainType, conformityMainTypeId, conformityRelType, conformityIsRelated, conformityIsSaved on the list queries.
  • Relations (modules/relations): generic entities: [{ contentType, contentId }] edges. createRelation, manageRelations, getRelationsByEntity let 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.

Was this helpful?