Mnemonic
A Mnemonic defines how tSM generates readable business identifiers (keys/codes) for your entities.
While every record has a technical UUID (id), users and integrations often need meaningful identifiers such as TCK-2025-00456 or ORD-EU-00123.
Where it’s used
- Business data (e.g., Ticket, Order, Account): generates a Key
- Registers / code tables (e.g., SLA type, Process type): generates a Code
- Many public APIs accept both UUID
idand businesskey/codein URLs for CRUD
Concept & Capabilities
A Mnemonic is a configurable pattern that combines:
- Constants — prefixes/suffixes (
"TCK-","ORD-") - Dynamic variables — entity fields, user/org context, date parts
- Sequences — auto-incremented numbers with optional restart periods (yearly, monthly, …) and padding
Examples:
| Entity | Pattern (conceptual) | Example result |
|---|---|---|
| Ticket | TCK_${yyyy}_${sequence(4)} | TCK_2025_0456 |
| Order | ORD_${region}_${sequence(5)} | ORD_EU_01234 |
| Customer Account | ACC_${type}_${yyyyMM}_${sequence(3)} | ACC_BUS_202501_007 |
The generation is unique within scope (global, per type, per region, etc., depending on your SpEL and sequence configuration).
How it works (high level)
- You define a Mnemonic configuration with a SpEL expression in
spel. - When a record needs a business key/code, the engine evaluates the expression, reading runtime data and consuming a sequence.
- The computed value is stored on the entity (e.g.,
Ticket.key, or a registercode) and used in UI and APIs.
SpEL anatomy (examples)
#date.format('yyyy') + '-' + #sequence.next('ticket_yearly', 4)
#root.customer?.region + '-' + #sequence.next('order_region_' + #root.customer?.region, 5)
'TCK-' + #date.format('yyyyMM') + '-' + #sequence.next('tck_' + #date.format('yyyyMM'), 4)
#root… the current entity/context payload (fields you pass during generation)#date… helper for formatting current time#sequence.next(name, pad)… obtains the next number for a named sequence, left-padded topaddigits
#sequence.next(name, pad)- Works with database sequences only. The counter stored in
nextSequenceNumis not available to it — see Numbering modes. A configuration that relies on that counter has to be moved to a database sequence before its expression can use#sequence.next(). namemust be the name of an existing database sequence. Sequences are never created on demand — create one withPOST /api/mnemonic/sequenceand list them withGET /api/mnemonic/sequence-list. A missing sequence fails key generation withSequence '…' does not exist in schema '…'.POST /api/mnemonic/sequenceputs the name straight intoCREATE SEQUENCE, so keep it to lower-case letters, digits and underscores, not starting with a digit.tck_yearlyworks;TCK-YEARLYandORD:EUare rejected by the database, while an unquoted upper-case name is not rejected but silently folded —ORD_EUis created asord_eu.- When
nameis built from runtime values — a region, a customer type, a period — a sequence has to exist for every value the expression can produce, and each of those values has to be a valid lower-case identifier itself. There is no fallback: the first unseen value fails key generation. POST /api/v1/mnemonic-configs/with-sequencecreates the sequence too, but derives the name itself — it convertssequenceNameto snake case and appends a number if that name is taken, soTCK-YEARLYbecomest_c_k_y_e_a_r_l_y. Read thesequenceNamefrom the response and use that in the expression.padmust be between0and64; anything else fails withApiConfigurationException. A number longer thanpadis never truncated, it only overflows the width.restartPerioddoes not apply here — it only restarts the sequence insequenceName. To restart numbering per period, put the period in the sequence name (ord_2026) and create a sequence for each period before the first record of that period is saved.- Drawing through the v2 API — which is what
#sequence.next()uses — requiresConfig.MnemonicConfig.Viewfrom the signed-in user. The deprecatedGET /api/mnemonic/next_value_spel/code/{code}andGET /api/mnemonic/expression/code/{code}are not protected in 2.3 and draw without it.
#date.format(pattern)Formats the current time in the business time zone (tsm.locale.timezone, CET by default), so a key rolls over at local midnight. Use #date.format(pattern, zone) to override it.
Exact helper names in your environment may vary—keep the intent: combine context + formatted date/time + a sequence.
Typical flows
- On create: generate a business key automatically before save, using entity data (type, priority, region…).
- Bulk imports: pre-generate or generate-on-insert to keep sequential order and avoid collisions.
- Scoped sequences: reset numbering per month/year, or per region/customer, by including scope in the sequence name.
Reference
MnemonicConfig (attributes)
| Field | Type | Required | Read-only | Description / Notes |
|---|---|---|---|---|
id | UUID | – | – | Identifier (not for end-user display). |
code | String | Yes | – | Unique ASCII code (no spaces). Stable key used by tools/processes. |
name | String | Yes | – | Human-readable name. |
description | String | – | – | Longer help text/tool-tip. |
validityFrom/To | Date | – | – | Active window for this mnemonic config. |
valid | Bool | – | Yes | Computed from validity; not stored. |
spel | String | Yes | – | SpEL expression that builds the final identifier (may reference date/time, payload fields, and sequences). |
configType | String | – | – | Links to a ConfigType (grouping for backup/restore, Studio filters). |
restartPeriod | Enum | – | – | Sequence restart policy: PERPETUAL, YEARLY, MONTHLY, DAILY. |
sequenceName | String | – | – | Database sequence the SEQUENCE placeholder draws from. Filling it in selects the numbering mode — see Numbering modes. Not used by #sequence.next(), which names its own sequence. |
nextSequenceNum | Integer | – | – | Counter used only when sequenceName is empty and the expression uses the SEQUENCE placeholder. Holds the last number handed out; the first generated value is 1. GET /api/v1/mnemonic-configs/{idOrCode}/nextSequenceNum means something different in each mode despite its name: with a sequence it returns the last value + 1, so the number to be issued next; with this counter it returns the counter as stored, i.e. the last number handed out — one less than the next key. POST /api/v1/mnemonic-configs/{idOrCode}/nextSequenceNum/{n} sets it, and means something different in each mode: with the counter the next key is n+1, with a sequence it is n — and the sequence is dropped and recreated. |
dataTags | List | – | – | Labels for dashboards/filters (e.g., ticketing, ops). |
config | Map | – | – | Free-form metadata used by admin tooling. |
localizationData | Object | – | – | Translations for name / description. |
auditInfo | Object | – | – | Standard audit metadata. |
restartPeriod
PERPETUAL– sequence never resetsYEARLY– resets each calendar yearMONTHLY– resets each calendar monthDAILY– resets each calendar day
Applies only to the sequence named in
sequenceName. A configuration whose expression uses#sequence.next()never causes a restart, and one withoutsequenceNameis skipped — but the job restarts any sequence that is some configuration'ssequenceName, so a sequence shared by both styles is restarted anyway.
Numbering modes
The SEQUENCE placeholder — as in 'TCK-' + YEAR + '-' + SEQUENCE — is resolved in one of two ways, decided by whether sequenceName is filled in. (The deprecated GET /api/mnemonic/next_value_spel/code/{code} is the one exception: it always uses the counter.)
Both modes apply to the SEQUENCE placeholder only. #sequence.next() always draws from a database sequence and can never use the counter mode described below.
Database sequence — the production mode. With sequenceName filled in, each key takes the next value of that sequence. Numbering is handled by the database, so it stays correct even when several requests or several instances of the service generate keys at the same time. It is also required for batch generation — with mnemonic pre-fetching enabled, a configuration without sequenceName fails with Sequence name is required for batch generation.
Counter on the configuration — development only. With sequenceName empty, the number comes from nextSequenceNum on the configuration itself: the application reads the value, increments it and writes it back. Every generated key logs Mnemonic generation for development purpose only used. Use table sequence for production use.
Take that warning literally. The increment happens in application code, not in the database, so two concurrent requests can read the same value and produce duplicate keys — the more instances of the service run, the more likely that is.
Use this mode only for a quick try-out on a development environment. Before going live, create a sequence and fill in sequenceName.
Example configurations
Yearly ticket sequence with prefix and zero-padding
'TCK-' + #date.format('yyyy') + '-' + #sequence.next('tck_yearly', 4)
Region-scoped order sequence
'ORD-' + #root.region + '-' + #sequence.next('ord_' + #root.region, 5)
Customer-type + month scope
'ACC-' + #root.customerType + '-' + #date.format('yyyyMM') + '-' + #sequence.next('acc_' + #root.customerType + '_' + #date.format('yyyyMM'), 3)
Pass the fields you need (e.g.,
region,customerType) in the generation payload so the expression can use them.
Good Practices
- Keep
codestable — referenced by processes and admin tools. - Design for uniqueness — include enough scope in the sequence name to avoid collisions (e.g., year/month/region).
- Human-first — prefer short, readable keys; keep noisy data out of the prefix.
- Reset wisely — use
restartPeriodto match business expectations (monthly orders vs. perpetual account numbers). It only drives the sequence insequenceName; with#sequence.next(), put the period in the sequence name instead. - Idempotency — guard against duplicate generation on retries (generate once per record and persist atomically).
- APIs — expose both UUID and business key/code where practical to simplify integrations and user operations.
See Also
- System Settings: ConfigType — organize mnemonic configs for promotion and tooling
- System Settings: EntityType — where keys/codes are ultimately used and exposed in UI/API
- Backup / Restorer, tSM Studio — tools that rely on consistent grouping and stable
codes