Properties, Tags & Segments

Model custom fields and system-field visibility, label records with tags, and build dynamic audiences with segments.

For bulk changes, see Import & Export.

Properties

backend/core-api/src/modules/properties/:

db/definitions/{common,field,group,systemField}.ts
db/models/{Field,Group,SystemField}.ts
graphql/schemas/{field,group,property,systemField,index}.ts
graphql/resolvers/{queries,mutations,customResolvers}/
trpc/{field,group,index}.ts

A Field (field.ts) defines name, code, groupId, contentType, contentTypeId, type, order, options[], logics, validations, configs, icon, isVisible, isVisibleToCreate, isRequired, isVisibleInCard. A FieldGroup orders fields per content type. Values written to records land in customFieldsData/propertiesData as structured JSON.

OperationKey arguments
fieldsparams: { contentType, contentTypeId, groupId, cursor params }; returns FieldListResponse
fieldDetail_id!
fieldAdd / fieldEdit / fieldRemovename, code, groupId, contentType, contentTypeId, type, options, validations, logics, configs, icon, isVisible, isVisibleToCreate, isRequired, isVisibleInCard, order
fieldGroupsparams: { contentType, contentTypeId, cursor params }
fieldGroupAdd / fieldGroupEdit / fieldGroupRemovegroup fields / _id!
fieldGroupsUpdateOrderorders: [FieldGroupOrderItem!]!
propertyTypesnone; returns every registered content type
propertySystemFieldscontentType!
propertySystemFieldEditcontentType!, code!, isVisible, isVisibleToCreate, isRequired, logics
cpFields, cpFieldDetail, cpFieldGroupsclient-portal variants

System fields ("Basic information")

Each plugin can declare built-in systemFields inside meta/properties.ts types[] (core declares them for customer, company, product, user; for example customer firstName, primaryEmail, ownerId, tagIds). propertySystemFields merges the declaration with tenant overrides stored in the SystemFieldSettings collection (db/models/SystemField.ts), and propertySystemFieldEdit persists isVisible, isVisibleToCreate, isRequired, and logics (operators is/isNot, actions show/hide). Settings → Properties renders them as the "Basic information" section (frontend/core-ui/src/modules/properties/components/record/PropertiesSystemFieldsSection.tsx).

Tags

backend/core-api/src/modules/tags/ (graphql/schemas.ts, queries.ts, mutations.ts, db/definitions/tags.ts, db/models/Tags.ts, taggable.ts, trpc/tag.ts).

A Tag has name (unique), colorCode, parentId, relatedIds, isGroup, type (the content type it applies to), description, objectCount, order. taggable.ts looks up the target model from the tag's type so any entity can be tagged.

OperationKey arguments
tagsGetTypesnone; returns meta.tags.types from every plugin
tagstype, searchValue, parentId, ids, excludeIds, isGroup, instanceId, includeWorkspaceTags, sortField, cursor params; returns TagsListResponse
tagsMain / tagDetail / tagsQueryCounttype, excludeWorkspaceTags / _id! / type, searchValue
tagsAdd / tagsEditname! plus type, colorCode, parentId, isGroup, description
tagsTagtype!, targetIds: [String!]!, tagIds: [String!]!
tagsRemove_id!
cpTags, cpTagsAdd, cpTagsTagclient-portal variants

Tag types are declared per plugin in meta/tags.ts (tags.types; core registers customer, company, product, user, form, automation, documents). A tag only applies to records whose content type matches its type; tagsGetTypes shows the registry. Use tags for operator-driven labels, not for access control.

Segments

backend/core-api/src/modules/segments/:

graphql/schemas/index.ts, resolvers/{queries,mutations,customResolvers}
db/definitions/{segments,segmentNodes,segmentHistory}.ts
db/models/{Segments,SegmentHistory}.ts
trpc/{index,segments}.ts
utils/{access,publishBuild,relationEdges,runSegment,segmentGateway}.ts

A Segment (segments.ts) has contentType, name, ownedBy, description, color, root (the condition tree), dependsOn, fingerprint, visibility (private/organization), ownerId, status (draft/building/active/failed/cancelled), revision, membersCount, membersCountedAt, timeSensitive, and build* progress fields (buildStartedAt, buildProcessed, buildTotal, buildCancelRequested). SegmentHistory stores per-day membership (count, joined, left) for segmentGrowth.

OperationKey arguments
segmentsGetTypesnone; returns registered content types
segments / segmentDetailcontentTypes: [String]!, ids, excludeIds, searchValue / _id!
segmentFieldscontentType!; returns filterable fields incl. custom properties
segmentRelationssubjectType!; returns relations the segment can traverse
segmentsPreviewCountcontentType!, root!; live count for the form
segmentSameDefinitioncontentType!, root!, excludeId; duplicate detection
segmentMembers / segmentMemberCountsegmentId!, cursor, limit
segmentGrowthsegmentId!, days
segmentUsageids: [String!]!; returns automations and segments referencing it
segmentsAdd / segmentsEditcontentType!, name, description, color, root!, visibility, status, ownedBy
segmentsRemoveids: [String!]!
segmentsRebuild / segmentsStopRebuild_id!

How evaluation runs

Evaluation is MongoDB, not Elasticsearch. erxes-api-shared/src/core-modules/segments/ translates the root node tree into a SegmentMongoFilter (mongoFilter.ts, evaluate.ts, memberQuery.ts, evaluateBatch.ts, measureRelations.ts). The segmentation worker runs in the logs service (segmentWorker.ts) with four BullMQ queues (changed, forget, rebuild, reconcile), each with a SEGMENT_CONCURRENCY_* env override. Membership changes are applied per plugin through applyMembership producers and logged into SegmentHistory.

Segments depend on the logs service

A segment stuck in building usually means the logs service is not running; check SEGMENT_CONCURRENCY_REBUILD and the service logs (segmentsStopRebuild cancels a build). If a count comes back lower than expected, SegmentMemberCount.unsupported lists tree nodes the Mongo filter could not express and exceeded means evaluation bailed early.

Plugins register segment content types through meta/segments passed to startPlugin (or initSegmentProducers for core): contentTypes, segmentFields, segmentFieldNamespaces, segmentRelations, plus evaluateFields, listSegmentMembers, countSegmentMembers, applyMembership handlers. Core registers core:organization.users, core:contacts.companies, core:contacts.customers, core:contacts.leads, core:products.products in meta/segments/contentTypes.ts; sales registers its deal/pos types the same way.

Permissions

From meta/permissions.ts: fieldsManage, fieldGroupsManage (properties, read is always: true); tagsCreate, tagsUpdate, tagsDelete, tagsTag (tagsRead always); segmentsManage (segmentsRead always). All use scope all.

Was this helpful?