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.
| Operation | Key arguments |
|---|---|
fields | params: { contentType, contentTypeId, groupId, cursor params }; returns FieldListResponse |
fieldDetail | _id! |
fieldAdd / fieldEdit / fieldRemove | name, code, groupId, contentType, contentTypeId, type, options, validations, logics, configs, icon, isVisible, isVisibleToCreate, isRequired, isVisibleInCard, order |
fieldGroups | params: { contentType, contentTypeId, cursor params } |
fieldGroupAdd / fieldGroupEdit / fieldGroupRemove | group fields / _id! |
fieldGroupsUpdateOrder | orders: [FieldGroupOrderItem!]! |
propertyTypes | none; returns every registered content type |
propertySystemFields | contentType! |
propertySystemFieldEdit | contentType!, code!, isVisible, isVisibleToCreate, isRequired, logics |
cpFields, cpFieldDetail, cpFieldGroups | client-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.
| Operation | Key arguments |
|---|---|
tagsGetTypes | none; returns meta.tags.types from every plugin |
tags | type, searchValue, parentId, ids, excludeIds, isGroup, instanceId, includeWorkspaceTags, sortField, cursor params; returns TagsListResponse |
tagsMain / tagDetail / tagsQueryCount | type, excludeWorkspaceTags / _id! / type, searchValue |
tagsAdd / tagsEdit | name! plus type, colorCode, parentId, isGroup, description |
tagsTag | type!, targetIds: [String!]!, tagIds: [String!]! |
tagsRemove | _id! |
cpTags, cpTagsAdd, cpTagsTag | client-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.
| Operation | Key arguments |
|---|---|
segmentsGetTypes | none; returns registered content types |
segments / segmentDetail | contentTypes: [String]!, ids, excludeIds, searchValue / _id! |
segmentFields | contentType!; returns filterable fields incl. custom properties |
segmentRelations | subjectType!; returns relations the segment can traverse |
segmentsPreviewCount | contentType!, root!; live count for the form |
segmentSameDefinition | contentType!, root!, excludeId; duplicate detection |
segmentMembers / segmentMemberCount | segmentId!, cursor, limit |
segmentGrowth | segmentId!, days |
segmentUsage | ids: [String!]!; returns automations and segments referencing it |
segmentsAdd / segmentsEdit | contentType!, name, description, color, root!, visibility, status, ownedBy |
segmentsRemove | ids: [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.