Skip to main content
Version: 2.4

Custom Entities

Overview

Custom Entities let you introduce new record families into tSM that behave like native records across the platform: they have their own API resource path, can be rendered in listings and detail pages, and can own Supporting Entities (attachments, comments, worklogs, …) via the standard ownership model. (tSM APP)

Custom Entities are based on the same foundational building blocks already used throughout tSM:

  • EntityType as the canonical "class of records" registry (e.g., Customer, Ticket, Order, Attachment) used for routing, UI composition, and cross-module links. (tSM APP)
  • EntitySpecification (Configuration Profile) + Characteristics to define dynamic attributes, forms, rules, and indexing behavior. (tSM APP)
  • Supporting Entities using ownerType + ownerId to attach reusable "side entities" to any record. (tSM APP)

Key terms

CustomEntity

A record family registered in EntityType that is marked as a Custom Entity. EntityType.code becomes the stable identifier used by:

  • API routing and discovery,
  • UI composition (widgets, icons),
  • ownership and references (ownerType, refType). (tSM APP)

CustomEntityType

A "type catalog" for a Custom Entity (e.g., CableType for Cable). A CustomEntityType record points to EntitySpecification, which defines:

  • characteristics (dynamic fields),
  • new/detail forms,
  • behavior rules. (tSM APP)

Shared vs type-specific configuration

tSM supports:

  • entity-level shared attributes (apply to all records of a CustomEntity),
  • type-specific attributes (apply only to a selected CustomEntityType).

This is implemented by combining:

  • an optional shared EntitySpecification configured at the CustomEntity level, and
  • the EntitySpecification referenced by the selected CustomEntityType.

How Custom Entities integrate with tSM core

EntityType is the anchor for routing, UI composition, and ownership

EntityType is explicitly described as the canonical classification of records and the basis for:

  • cross-module linking (ownerType / refType),
  • discoverability & routing (ties records to a microservice and public API),
  • presentation (icons and UI widgets). (tSM APP)

Because Supporting Entities use the universal ownerType + ownerId pattern, any record family that exists as an EntityType (including a Custom Entity) can own attachments/comments/worklogs immediately. (tSM APP)

Microservice and Module provide deployment + domain grouping

  • Microservice Registry stores deployment inventory metadata, including a gateway-relative backendUrl used by UIs and tools. (tSM APP)
  • Module groups what users experience around a business domain (forms, processes, listings, widgets) and points to the primary microservice powering that domain. (tSM APP)

Configuration guide

This section is written for configurators who want to add a new Custom Entity end-to-end.

1) Choose where the Custom Entity will live (Microservice)

Ensure a Microservice entry exists for the runtime hosting Custom Entities and that it has a correct gateway-relative backendUrl. (tSM APP)

Microservice example

{
"code": "tsm-dynamic",
"name": "Dynamic Runtime",
"backendUrl": "tsm-dynamic/api",
"dataTags": ["core", "dynamic"]
}

Good practices for Microservice (stable code, gateway-relative backendUrl, minimal config) are documented in the Microservice Registry chapter. (tSM APP)


Use a Module to group the Custom Entity into a clear business domain (navigation, governance, rollout, filters). Modules are "purpose-oriented building blocks" that group user-facing domain elements. (tSM APP)

Module example

{
"code": "NETWORK_ASSETS",
"name": "Network Assets",
"primaryMicroservice": "tsm-dynamic",
"dataTags": ["network", "assets"]
}

3) Register the Custom Entity in EntityType

Create an EntityType entry for the Custom Entity (example below uses Cable, which is a common real-world case for rich evidence, attachments, and maintenance worklogs).

EntityType attributes (microservice routing, optional public API URL override, UI hints) are documented in the EntityType chapter. (tSM APP)

EntityType example: Cable

{
"code": "Cable",
"name": "Cable",
"microservice": "tsm-dynamic",
"publicApiUrl": "cables",
"defaultCardProfile": "cable-detail",
"listingCardWidget": "GenericEntityCard",
"icon": "cable",

"type": "CustomEntity",
"customEntity": {
"customEntityTypeCode": "CableType",
"sharedEntitySpecificationId": "ESPEC.CABLE.COMMON",
"search": { "enabled": true, "indexAlias": "cable" }
},

"dataTags": ["network", "custom"]
}

Notes:

  • publicApiUrl is relative to Microservice.backendUrl. If not set, EntityType supports deriving it from the code (kebab-case, plural). (tSM APP)
  • Keep EntityType.code stable: it becomes a key in references (ownerType, refType), configs, and routing. (tSM APP)

4) Register the corresponding CustomEntityType family in EntityType

Create a second EntityType entry for the CustomEntityType records.

EntityType example: CableType

{
"code": "CableType",
"name": "Cable Type",
"microservice": "tsm-dynamic",
"publicApiUrl": "cable-types",
"defaultCardProfile": "cable-type-detail",
"listingCardWidget": "GenericTypeCard",
"icon": "tag",

"type": "CustomEntity",
"customEntity": { "role": "CustomEntityType" },

"dataTags": ["network", "custom", "types"]
}

5) Create EntitySpecifications (shared + per-type)

tSM's characteristics model is based on EntityType + Configuration Profiles (EntitySpecification) + Characteristics, where EntitySpecification carries the dynamic attribute definitions and the form specification used for rendering and validation. (tSM APP)

Recommended structure:

  • Shared spec: ESPEC.CABLE.COMMON Common fields for all cables (e.g., location, owner organization, installation date, documentation link).

  • Type specs: e.g. ESPEC.CABLE.FIBER_TRUNK, ESPEC.CABLE.COPPER_DROP, … Fields specific to that cable subtype.


6) Create CustomEntityType records (the "type catalog")

Create records of entity type CableType, each referencing its type-specific EntitySpecification and pointing back to the owning CustomEntity (entityTypeCode = "Cable").

CableType example record

{
"entityType": "CableType",
"entityTypeCode": "Cable",
"code": "FIBER_TRUNK",
"name": "Fiber trunk cable",
"entitySpecificationId": "ESPEC.CABLE.FIBER_TRUNK"
}

7) (Optional) Menu items for UI navigation

EntityType provides presentation hints (icon, listing widget, card profile). (tSM APP) For explicit navigation entries in the left menu, add Menu Items that route to:

  • /cables (instances)
  • /cable-types (types)

(Exact router links depend on your UI routing conventions.)


Data model diagram


API behavior

Public API path and service routing

EntityType ties a record family to:

  • a microservice (EntityType.microservice), and
  • a public API path (EntityType.publicApiUrl) that is relative to the microservice backendUrl. (tSM APP)

tSM Public API documentation describes the platform as modular and microservice-based, with consistent REST operations per module. (tSM APP)

Typical endpoints (Cable):

  • GET /cables
  • POST /cables
  • GET /cables/{id}
  • PATCH /cables/{id}

Type endpoints:

  • GET /cable-types
  • POST /cable-types
  • PATCH /cable-types/{id}

Creating a Custom Entity record

Create Cable example payload

{
"typeCode": "FIBER_TRUNK",
"name": "Brno – Pohořelice trunk #12",
"statusCode": "ACTIVE",
"characteristics": {
"fiberCount": 96,
"sheathType": "PE",
"installedAt": "2024-10-14",
"vendor": "ACME Cables"
}
}

The selected typeCode determines the CustomEntityType record, which determines the EntitySpecification and therefore the characteristics schema and UI behavior. (tSM APP)


Supporting Entities with Custom Entities

Supporting Entities (Attachment, Comment, Worklog, …) share a universal ownership model based on ownerType and ownerId. (tSM APP) Because ownerType is the "type/class of the parent record" (e.g., Ticket, Customer, …), a Custom Entity can be used as an owner by setting ownerType = "<EntityType.code>". (tSM APP)

Attachment example (SpEL)

The Supporting Entities documentation shows an Attachment created by @attachmentPublicService.create(...) with ownerType and ownerId. (tSM APP)

@attachmentPublicService.create({
"ownerType": "Cable",
"ownerId": #cable.id,
"attachmentType": "Report",
"name": "OTDR_2026-01-05.pdf",
"data": #file.encodeBase64()
})

Worklog example (SpEL)

Worklogs follow the same ownership pattern and are created with @worklogPublicService.create(...). (tSM APP)

@worklogPublicService.create({
"ownerType": "Cable",
"ownerId": #cable.id,
"worklogType": "Investigation",
"duration": 1.5,
"description": "Measured attenuation and compared with baseline."
})

Best practices

  • Keep EntityType.code stable. It is used in references (ownerType, refType), configs, and API routing. (tSM APP)
  • Prefer derived publicApiUrl unless you need an override, and keep it relative to Microservice.backendUrl. (tSM APP)
  • Use dataTags consistently on Microservice, Module, and EntityType for filtering and governance. (tSM APP)
  • Keep config/customEntity lightweight (feature flags, UI hints, routing knobs). Microservice and EntityType docs explicitly position config as "small configuration," not deployment configuration. (tSM APP)
  • Avoid characteristic name conflicts within the same EntityType. The Characteristics documentation notes that within the same EntityType, characteristics with the same attribute name must be identical for consistent interpretation across specifications. (tSM APP)

Troubleshooting checklist

The entity does not appear in UI

  • Verify EntityType.validityFrom/To (or derived valid) allows it to be active. (tSM APP)
  • Verify EntityType.microservice points to an existing Microservice and that Microservice backendUrl is set. (tSM APP)
  • Ensure UI hints are set (listingCardWidget, defaultCardProfile, icon) for consistent rendering. (tSM APP)

API returns 404 on /cables

  • Confirm publicApiUrl (or its derived value) matches the path you're calling. (tSM APP)
  • Confirm Microservice backendUrl is correct and reachable behind the gateway. (tSM APP)

Type selection fails / characteristics validation fails

  • Confirm the referenced EntitySpecification exists and includes the Characteristics schema and forms as expected. (tSM APP)
  • Check that shared and type-specific specs do not define conflicting characteristics under the same attributeName for the same EntityType. (tSM APP)

See Also