Overview

The rule expression engine evaluates bracket functions and field placeholders in eADM. Expressions appear in:

Consumer: Rulesets (Regelsett)  |  Typical use: Filter users, departments, groups; gate message flows, sync steps, permissions

Consumer: Message templates  |  Typical use: Dynamic recipient, subject, body (HTML)

Consumer: Export templates / sync template flow lines  |  Typical use: Target attribute mapping

Consumer: Customer setup  |  Typical use: Username expression, auto-group naming, position filter

Consumer: Form templates  |  Typical use: Form field bindings

Consumer: Expression builder  |  Typical use: Preview against a test user, department, or group


How evaluation works

  1. The engine scans the expression for [...] tokens.

  2. [ARRAYFOREACH...] runs first — before the innermost-first pass — so template placeholders ??? and ???i bind per pipe-separated element.

  3. Otherwise it resolves the innermost bracket pair first.

  4. Each function returns a plain text result that replaces the bracket.

  5. When no functions remain, remaining [FieldName] tokens are replaced from the current object (and optional history row in message flows).

  6. Unrecognised field names stay as literal [FieldName] in the output.

Pipe-separated lists (|) are used for multi-value data — especially HR positions on a user.

Dummy data used in examples below

Unless stated otherwise, examples assume customer Eksempel kommune with:

Object: User Kari Nordmann  |  Key values: GivenName=Kari, Surname=Nordmann, DisplayName=Kari Nordmann, UserName=kari.nordmann, Email=kari.nordmann@eksempel.kommune.no, Start=01.04.2026 00:00:00, Department=IT og digitalisering, DepartmentNumber=100

Object: User positions (pipe-aligned)  |  Key values: APositionCodeId=POS-01|POS-02, AUnitId=IT-100|HR-200, APositionCodeName=Utvikler|HR-rådgiver, APrimaryPosition=true|false, APositionPercentage=100.0|20.0

Object: Department IT (type 2)  |  Key values: Id=2100, SourceId=IT-100, DepartmentNumber=100, OrgUnitnr=IT-100, ParentDepartmentNumber=0

Object: Department HR (child of IT)  |  Key values: Id=2101, SourceId=HR-200, ParentDepartmentNumber=100

Object: System role (permission)  |  Key values: Id=501, Name=Bruker Fagsystem X, linked to system id=12

Object: Permission on Kari  |  Key values: SystemRoleId=501, ManagerOf=2100, Comment=Automatisk tildelt, ExpiryDate=empty


Object type ids (for lookups): User=1, Department=2, Group=3, API user=14.


Syntax conventions

  • Functions: [NAME;arg1;arg2;...] — arguments separated by semicolon (;).

  • Fields: [FieldName] — value from current object (case-insensitive).

  • Nested expressions: inner brackets resolved before outer.

  • List separator: pipe character | (vertical bar) between values — e.g. POS-01|POS-02. Never type a backslash; | appears only as markdown escaping in source files, not in expressions.

  • Dates in fields are often stored as dd.MM.yyyy HH:mm:ss; imports may use yyyy-MM-dd and other common formats.

Query mini-language

Used inside [FOREACH], [IFTHENELSE], and advanced [OBJECT...] filters.

Token: =  |  Meaning: Equal

Token: >, <  |  Meaning: Greater / less than

Token: !  |  Meaning: Not equal

Token: *  |  Meaning: Pattern match (like)

Token: #  |  Meaning: Value in list

Token: &  |  Meaning: AND next condition

Token: |  |  Meaning: OR next condition


Prefix related data with object., system., systemrole., objectpermission., etc.


Nesting

Inner field first, then outer function

Step: 1  |  Expression fragment: [start]  |  Resolves to: 01.04.2026 00:00:00

Step: 2  |  Expression fragment: [SUBSTRING;0;10;01.04.2026 00:00:00]  |  Resolves to: 01.04.2026


Blocked if inner field missing: [SUBSTRING;0;10;[UnknownField]] stays unchanged when UnknownField is not on the object.

Typical permission chain (read inside-out):

[REFERENCE;DisplayName;UserId;1;[REFERENCE;Manager;Id;2;[OBJECTPERMISSION;ManagerOf;SystemRoleId;501]]]
  1. Permission → department id 2100

  2. Department → manager user id

  3. User → manager display name

[ARRAYFOREACH...] is different: it expands a pipe-separated list before inner functions in the template run. Use ??? for the current element and ???i for its 0-based index. Nesting [ARRAYFOREACH...] inside another [ARRAYFOREACH...] is not supported — use [ARRAYJOIN] / [ARRAYPREFIX] or sibling calls instead.


Access levels

Role: Super manager  |  What it affects: Expression builder preview

Role: Company administrator  |  What it affects: Full function list in builder; [APITOKEN...], [SYNCSTEPPARAMETER...] in preview

Role: Organization administrator  |  What it affects: Expressions touching protected fields in preview

Role: Super administrator  |  What it affects: [UPDATE...], [DECRYPT...], [ENCRYPT...] — omitted from this guide


Automation (sync, messages, exports) evaluates expressions with server privileges. A function that works in production may still be blocked in the expression builder for your role.


Field placeholders

Not functions — replaced from the current object (or history row in event-driven message flows).

Example: Template Hello [GivenName] [Surname] on Kari Nordmann → Hello Kari Nordmann

Placeholder: [GivenName], [Surname], [DisplayName]  |  Typical source: Person name

Placeholder: [UserName], [Email], [Upn], [Mail]  |  Typical source: Account / mail

Placeholder: [Department], [DepartmentNumber]  |  Typical source: Primary department

Placeholder: [Start]  |  Typical source: Employment start datetime

Placeholder: [Manager]  |  Typical source: Manager reference from HR

Placeholder: [APositionCodeId], [AUnitId], [APositionCodeName]  |  Typical source: Multi-position (pipe-separated)


History-only (message flows after sync): [OldValue], [NewValue], [EntryType], [AttributeName], [Comment].


Function reference with examples

Each entry lists parameters, a worked example with dummy data, and expected output.


Date and time

[NÅ] / [NOW]

Returns server date and time.

Parameter: (none)  |  Purpose: Current moment

Parameter: +N / -N suffix  |  Purpose: Add or subtract N days


Expression: [NÅ]  |  Assumed server date: 17.06.2026 14:30:00  |  Output: 17.06.2026 14:30:00

Expression: [NOW+7]  |  Assumed server date: 17.06.2026  |  Output: 24.06.2026 14:30:00 (same time, +7 days)


Use: Permission start display, relative deadlines in templates.


[DATEFORMAT;date;format]

Reformats a date string.

Parameter: date  |  Purpose: Input date, or pipe-separated dates

Parameter: format  |  Purpose: Output pattern (e.g. yyyy-MM-dd, dd/MM/yyyy)


Expression: [DATEFORMAT;01.04.2026 00:00:00;yyyy-MM-dd]  |  Input (after field resolve): —  |  Output: 2026-04-01

Expression: [DATEFORMAT;01.04.2026 00:00:00|15.06.2026 00:00:00;yyyy-MM-dd]  |  Input (after field resolve): two dates  |  Output: 2026-04-01|2026-06-15


Use: Export formats, ISO dates in API payloads.


[DATEDIFF;date1;date2;unit]

Numeric difference between two dates.

Parameter: date1  |  Purpose: Later or primary date

Parameter: date2  |  Purpose: Earlier or reference date

Parameter: unit  |  Purpose: d days (default), y years, m months, h hours, mi minutes, s seconds


Expression: [DATEDIFF;17.06.2026 00:00:00;01.04.2026 00:00:00;d]  |  Output: 77  |  Meaning: 77 days between start and today

Expression: [DATEDIFF;17.06.2026 00:00:00;01.04.2026 00:00:00;y]  |  Output: 0  |  Meaning: Same calendar year in this example


Use: Tenure checks in rulesets, "days until start" in messages.


[DAYOFYEAR;date]

Parameter: date  |  Purpose: Date to evaluate


Expression: [DAYOFYEAR;01.04.2026 00:00:00]  |  Output: 91


Use: Seasonal rules, fiscal period tricks.


String manipulation

[SUBSTRING;start;length;text]

Parameter: start  |  Purpose: 0-based start index

Parameter: length  |  Purpose: Number of characters to take

Parameter: text  |  Purpose: Source string (often a nested field)


Expression: [SUBSTRING;0;10;01.04.2026 00:00:00]  |  Resolved text: —  |  Output: 01.04.2026

Expression: [SUBSTRING;0;4;POS-01]  |  Resolved text: —  |  Output: POS-0


Use: Date-only from datetime in welcome mails: [SUBSTRING;0;10;[Start]]01.04.2026.


[SPLIT;text;delimiter;index]

Parameter: text  |  Purpose: String to split

Parameter: delimiter  |  Purpose: Separator (e.g. |, ;, space)

Parameter: index  |  Purpose: 0-based piece index, or last / last-1


Expression: [SPLIT;Kari|Nordmann;|;0]  |  Output: Kari

Expression: [SPLIT;POS-01|POS-02;|;last]  |  Output: POS-02


Use: Salutation from combined name field; pick last position code.


[REPLACE;text;find;replace]

Parameter: text  |  Purpose: Original string

Parameter: find  |  Purpose: Substring(s) to replace; pipe = multiple; ^x = only at start; x^ = only at end

Parameter: replace  |  Purpose: Replacement text


Expression: [REPLACE;kari.nordmann;.;-]  |  Output: kari-nordmann

Expression: [REPLACE;+4712345678;+47|;;]  |  Output: 12345678


Use: Normalise phone numbers, swap separators in usernames.


[REPLACELIST;text;fromChars;toChars]

Character-by-character map (both char lists must be same length).

Parameter: text  |  Purpose: Input

Parameter: fromChars  |  Purpose: Characters to map from

Parameter: toChars  |  Purpose: Characters to map to


Expression: [REPLACELIST;abcåøæ;åøæaoe]  |  Output: abcaoe


Use: Legacy character set fixes in imports.


[RTRIM;text;chars] / [LTRIM;text;chars]

Parameter: text  |  Purpose: Input; if it contains |, each segment is trimmed

Parameter: chars  |  Purpose: Character(s) to strip from end (RTRIM) or start (LTRIM)


Expression: [RTRIM;POS-01|POS-02|;]  |  Output: POS-01|POS-02 (trailing empty segment removed)

Expression: [LTRIM;; Kari; ]  |  Output: Kari


Use: Clean pipe lists after [ARRAYDIFF].


[RPAD;text;padChar;totalLength] / [LPAD;text;padChar;totalLength]

Parameter: text  |  Purpose: Value to pad

Parameter: padChar  |  Purpose: Fill character

Parameter: totalLength  |  Purpose: Target width


Expression: [RPAD;42;0;5]  |  Output: 42000

Expression: [LPAD;7;0;4]  |  Output: 0007


Use: Fixed-width employee numbers in file exports.


[RIGHT;length;text]

Parameter: length  |  Purpose: How many characters from the right

Parameter: text  |  Purpose: Source


Expression: [RIGHT;4;12345678]  |  Output: 5678


Use: Last digits of national id in test environments.


[INITIALS;text;startWord;endWord]

Parameter: text  |  Purpose: Full name or phrase

Parameter: startWord  |  Purpose: Word index to start (0-based)

Parameter: endWord  |  Purpose: Word index to end, or last / last-1


Expression: [INITIALS;Kari Marie Nordmann;0;last]  |  Output: K M N (letters plus spaces as produced)

Expression: [INITIALS;Kari Marie Nordmann;1;1]  |  Output: M


Use: Short codes in group names or mail aliases.


[PRETTIFY;text]

Title-cases after space or hyphen.

Expression: [PRETTIFY;kari nordmann]  |  Output: Kari Nordmann

Expression: [PRETTIFY;it-og-digitalisering]  |  Output: It-Og-Digitalisering


Use: Display names in messages when HR sends uppercase.


[LOWER;text] / [UPPER;text]

Expression: [LOWER;Kari.Nordmann@Eksempel.Kommune.NO]  |  Output: kari.nordmann@eksempel.kommune.no

Expression: [UPPER;avdeling-it]  |  Output: AVDELING-IT


Use: Normalise before [CLEAN] in username rules.


[CLEAN;text] / [STRICTCLEAN;text] / [CNCLEAN;text]

Strip or replace characters unsafe for usernames, strict ids, or AD common names (CN).

Expression: [CLEAN;Kari Åse Nordmann!]  |  Typical output: kariasenordmann (non-letters removed, lowercased style)  |  Use: Username / uid

Expression: [CNCLEAN;Kari (test) Nordmann]  |  Typical output: CN-safe form for AD  |  Use: Group or user CN


Exact output depends on customer character rules; test in expression builder.


[GETKEYVALUE;key;delimiter;metadata]

Reads key value pairs inside a pipe-separated metadata string.

Parameter: key  |  Purpose: Key to find

Parameter: delimiter  |  Purpose: Separator between key and value (often = or :)

Parameter: metadata  |  Purpose: Pipe-separated list of key-value tokens


Expression: [GETKEYVALUE;cost;=;cost=IT|site=Oslo]  |  metadata value: —  |  Output: IT


Use: Custom import metadata on extension attributes.


Logic and control flow

[IFTHENELSE;query;thenValue;elseValue]

Parameter: query  |  Purpose: Condition on current object (e.g. ObjectTypeId=1)

Parameter: thenValue  |  Purpose: Returned if query matches

Parameter: elseValue  |  Purpose: Returned if not


Context: User Kari (type 1)  |  Expression: [IFTHENELSE;ObjectTypeId=1;Ansatt;Ikke ansatt]  |  Output: Ansatt

Context: Department IT (type 2)  |  Expression: same  |  Output: Ikke ansatt


Use: Single template for mixed object types.


[IFEMPTYUSE;primary;fallback;reversed]

Parameter: primary  |  Purpose: Value to test

Parameter: fallback  |  Purpose: Used when primary is empty (default)

Parameter: reversed  |  Purpose: Optional true: use fallback when primary is not empty


Expression: [IFEMPTYUSE;;Permanent tilgang]  |  Kari's permission expiry: empty  |  Output: Permanent tilgang

Expression: [IFEMPTYUSE;15.12.2026;Permanent tilgang]  |  Kari's permission expiry: 15.12.2026  |  Output: 15.12.2026

Expression: [IFEMPTYUSE;[Manager];;true]  |  Kari's permission expiry: Manager=empty  |  Output: `` (empty — reversed: fallback only when manager exists)


Use: Default text for missing expiry, manager, or comment.


[EXISTS;value;resultIfNonEmpty]

Parameter: value  |  Purpose: Tested string

Parameter: resultIfNonEmpty  |  Purpose: Returned only if value is non-empty


Expression: [EXISTS;ola.hansen;has-manager]  |  Kari Manager field: ola.hansen  |  Output: has-manager

Expression: [EXISTS;;has-manager]  |  Kari Manager field: empty  |  Output: `` (empty)


Use: Gate nested lookups — only run [REFERENCE...] when [Manager] is set.


[TRANSFORM;input;mapping...]

Parameter: input  |  Purpose: Value to map

Parameter: key|value pairs  |  Purpose: Match input to output

Parameter: *|default  |  Purpose: Wildcard if no key matches


Expression: [TRANSFORM;F;M|Mann;F|Kvinne;*|Ukjent]  |  [Sex] = F: —  |  Output: Kvinne

Expression: [TRANSFORM;X;M|Mann;F|Kvinne;*|Ukjent]  |  [Sex] = F: —  |  Output: Ukjent


Use: HR codes → display text in messages.


[CONTAINS;valueList;targetList;ifYes;ifNo]

Parameter: valueList  |  Purpose: Pipe-separated needles

Parameter: targetList  |  Purpose: Pipe-separated haystack

Parameter: ifYes / ifNo  |  Purpose: Strings returned on match / no match


Expression: [CONTAINS;IT-100|FIN-300;HR-200|IT-100|SALG-400;ja;nei]  |  Output: ja (IT-100 found in target)

Expression: [CONTAINS;FIN-300;HR-200|IT-100;ja;nei]  |  Output: nei


Use: Ruleset-style branching inside a template.


Array (pipe-separated) operations

Lists look like A|B|C. Many HR position fields use this shape.

[COUNT;ignored;list]

Parameter: ignored  |  Purpose: Placeholder (unused)

Parameter: list  |  Purpose: Pipe-separated values


Expression: [COUNT;x;POS-01|POS-02|]  |  Output: 2 (empty trailing segments may affect count — test with your data)


Use: "How many positions does this user have?"


[ARRAYAPPEND;list1;list2]

Expression: [ARRAYAPPEND;POS-01|;POS-02|POS-03|]  |  Output: POS-01|POS-02|POS-03|


Use: Merge role lists from two permission queries.


[ARRAYUNIQUE;list]

Expression: [ARRAYUNIQUE;A|B|A|C|]  |  Output: A|B|C|


Use: Deduplicate department ids before export.


[ARRAYFILTER;list;allowed]

Keeps only items that appear in allowed.

Expression: [ARRAYFILTER;POS-01|POS-02|POS-99;POS-01|POS-02|]  |  Output: POS-01|POS-02|


Use: Whitelist position types for a message.


[ARRAYDIFF;current;previous]

Items in current not in previous.

Context: New position added  |  current: POS-01|POS-02|  |  previous: POS-01|  |  Output: POS-02|

Context: No change  |  current: POS-01|  |  previous: POS-01|  |  Output: ``


Use: Detect new position in message flow (pair with [LASTVALUE;APositionCodeId]). Warning: unreliable if several positions change in one import.


[ARRAYJOIN;list1;list2]

Zips two lists by index.

Expression: [ARRAYJOIN;Utvikler|HR-rådgiver;100%|20%]  |  Output: Utvikler100%|HR-rådgiver20%


Use: Combine parallel columns for CSV-style export lines.


[ARRAYFOREACH;array;template] / [ARRAYFOREACH;array;separator;template]

Iterates over a pipe-separated list. For each non-empty element, substitutes ??? with the element and ???i with its 0-based index in the template, evaluates nested [...] in the template, then joins results.

Parameter: array  |  Purpose: Pipe-separated list (often a field like [APositionCodeName])

Parameter: separator  |  Purpose: Optional join string between iterations (default |)

Parameter: template  |  Purpose: Text or nested expression; ??? = current item, ???i = index


Expression: [ARRAYFOREACH;[APositionCodeName];Stilling: ???]  |  Kari APositionCodeName: Utvikler|HR-rådgiver  |  Output: Stilling: Utvikler|Stilling: HR-rådgiver

Expression: [ARRAYFOREACH;[APositionCodeId];, ;DEPT-???]  |  Kari APositionCodeName: POS-01|POS-02  |  Output: DEPT-POS-01, DEPT-POS-02

Expression: [ARRAYFOREACH;[APositionCodeId];[SUBSTRING;0;4;???]]  |  Kari APositionCodeName: POS-01|POS-02  |  Output: POS-|POS-

Expression: [ARRAYFOREACH;[APositionCodeName];???i:???]  |  Kari APositionCodeName: Utvikler|HR-rådgiver  |  Output: 0:Utvikler|1:HR-rådgiver


Empty pipe segments are skipped (same convention as [ARRAYUNIQUE] / [ARRAYPREFIX]).

Not supported: [ARRAYFOREACH...] nested inside another [ARRAYFOREACH...] — expression evaluation fails. Use two sibling calls or [ARRAYJOIN] instead.

Use: Format each position as its own line in a message; prefix each unit id for export.


[ARRAYPREFIX;list;mode;text]

Parameter: list  |  Purpose: Pipe-separated items

Parameter: mode  |  Purpose: 1 = prefix each item with text; other = suffix

Parameter: text  |  Purpose: Prefix or suffix string


Expression: [ARRAYPREFIX;IT-100|HR-200;1;DEPT-]  |  Output: DEPT-IT-100|DEPT-HR-200|


Use: Namespace external ids.


[ARRAYTRIM;list;delimiter;maxCount]

Expression: [ARRAYTRIM;A|B|C|D;|;2]  |  Output: A|B


Use: Only first N positions in a summary mail.


[PRUNEARRAY;list;regex;reverse]

Parameter: list  |  Purpose: Pipe-separated items

Parameter: regex  |  Purpose: .NET regular expression

Parameter: reverse  |  Purpose: true = keep non-matching items instead


Expression: [PRUNEARRAY;POS-01|TEMP-99|POS-02;^TEMP;false]  |  Output: POS-01|POS-02

Expression: [PRUNEARRAY;POS-01|TEMP-99;^TEMP;true]  |  Output: TEMP-99


Use: Drop temporary position codes from export.


[UNION;headers;list1;list2;...]

Merges parallel lists row-wise into spaced/pipe combined rows.

Expression: [UNION;Role|Dept;Admin|User;IT|HR]  |  Conceptual output: Role Admin Dept IT|User HR (pattern: concatenate columns per index)


Use: Multi-column text blocks in reports.


Random, hash, uniqueness

[RND;min;max]

Parameter: min  |  Purpose: Minimum integer (inclusive)

Parameter: max  |  Purpose: Maximum integer (exclusive in practice — test your range)


Expression: [RND;1000;9999]  |  Example output: 4521 (varies each run)


Use: Ticket numbers in test templates.


[STRRND;minLen;maxLen;chars]

Parameter: minLen / maxLen  |  Purpose: Length range

Parameter: chars  |  Purpose: Allowed character set (empty = alphanumeric default)


Expression: [STRRND;8;8;abcdefghjkmnpqrstuvwxyz]  |  Example output: kthqwrnb (varies)


Use: One-time codes when password rule is not used.


[PASSWORD;ruleId]

Parameter: ruleId  |  Purpose: Id of password generation rule configured in eADM


Expression: [PASSWORD;3]  |  Output: e.g. Xk9#mP2$vL (follows rule 3 complexity; varies)


Use: Initial password in welcome message — handle securely.


[HASH;text]

Expression: [HASH;kari.nordmann@eksempel.kommune.no]  |  Output (example): 32-character MD5 hex string (fixed for same input)


Use: Opaque tokens in links (not for security-critical hashing alone).


[UNIQUE;base;fieldName;replacement]

Ensures base does not already exist in fieldName on another object; appends 2, 3, … or inserts before replacement.

Parameter: base  |  Purpose: Proposed username or email

Parameter: fieldName  |  Purpose: Field to check uniqueness against (e.g. UserName)

Parameter: replacement  |  Purpose: Optional suffix marker to replace with number


Scenario: kari.nordmann free  |  Expression: [UNIQUE;kari.nordmann;UserName;.]  |  Output: kari.nordmann

Scenario: kari.nordmann taken  |  Expression: same  |  Output: kari.nordmann2 or kari2.nordmann depending on replacement


Use: Customer setup username expression together with [CLEAN] / [LOWER].


Database lookup and relations

These read live eADM data for the customer. Ids must exist in your environment.

[REFERENCE;returnField;whereField;objectTypeId;searchValue;exactMatch]

Parameter: returnField  |  Purpose: Field(s) to return from matched object (§ joins multiple fields)

Parameter: whereField  |  Purpose: Field to match on

Parameter: objectTypeId  |  Purpose: 1 user, 2 department, 3 group

Parameter: searchValue  |  Purpose: Value to find

Parameter: exactMatch  |  Purpose: true exact; false pattern


Expression: [REFERENCE;OrgUnitnr;Id;2;2100]  |  Dummy match: Dept id 2100 = IT  |  Output: IT-100

Expression: [REFERENCE;DisplayName;UserId;1;ola.hansen]  |  Dummy match: Manager user  |  Output: Ola Hansen


Use: Department number from permission ManagerOf id:

[REFERENCE;OrgUnitnr;Id;2;2100]

[OBJECTPERMISSION;returnField;whereField;filterValue;exactMatch;aggregate]

Reads permissions on the current user.

Parameter: returnField  |  Purpose: Permission field (Comment, ManagerOf, ExpiryDate, SystemRoleId, …)

Parameter: whereField  |  Purpose: Field to filter on

Parameter: filterValue  |  Purpose: Match value, or * for all

Parameter: exactMatch  |  Purpose: String match mode

Parameter: aggregate  |  Purpose: true = all matches joined with |; false = first only


Expression: [OBJECTPERMISSION;Comment;SystemRoleId;501;true;false]  |  Output (Kari): Automatisk tildelt

Expression: [OBJECTPERMISSION;ManagerOf;SystemRoleId;501;true;false]  |  Output (Kari): 2100


Shorthand for role name:

Expression: [OBJECTPERMISSION.SYSTEMROLE;Name;SystemRoleId;501]  |  Output: Bruker Fagsystem X



[OBJECTHISTORIES;returnField;whereField;filterValue;exactMatch;aggregate]

Reads object history on current user (audit trail from sync/UI).

Example filter: Last permission add  |  Expression: [OBJECTHISTORIES;Comment;EntryType;AddPermission;false;false]  |  Typical output: Tildelt av admin


Use: "Who assigned this permission?" in message flows (often nested in [SPLIT] / [REFERENCE]).


[FOREACH;collection;query;returnFields;headers;format]

Parameter: collection  |  Purpose: object, objectpermission, objecthistory, …

Parameter: query  |  Purpose: Filter (company scope added automatically)

Parameter: returnFields  |  Purpose: Comma-separated fields, optionally object.Field

Parameter: headers  |  Purpose: Column titles for csv/html

Parameter: format  |  Purpose: csv, html, json, xml, eadm


Example (conceptual): All users with role 501, CSV:

[FOREACH;objectpermission;SystemRoleId=501;object.DisplayName,object.Email;Name,Email;csv]

Output (dummy):

Name,Email
Kari Nordmann,kari.nordmann@eksempel.kommune.no
Ola Hansen,ola.hansen@eksempel.kommune.no

Use: Manager report embedded in email body.


[DESCENDANTS;rootId;returnField;parentKey;foreignKey]

Parameter: rootId  |  Purpose: Department object id to start from

Parameter: returnField  |  Purpose: Field to collect from each node

Parameter: parentKey  |  Purpose: Child field pointing to parent (e.g. DepartmentNumber)

Parameter: foreignKey  |  Purpose: Parent identifier field (e.g. ParentDepartmentNumber)


Start: IT id 2100  |  Tree: IT → HR  |  Expression: [DESCENDANTS;2100;SourceId;DepartmentNumber;ParentDepartmentNumber]  |  Output: IT-100,HR-200


Use: All sub-departments for scope on a manager permission.


[SYSTEM;type;id;field]

Parameter: type  |  Purpose: system, systemrole, or systemroles

Parameter: id  |  Purpose: Numeric id or *

Parameter: field  |  Purpose: Field name(s) to return


Expression: [SYSTEM;systemrole;501;Name]  |  Output (dummy): Bruker Fagsystem X

Expression: [SYSTEM;system;12;Name]  |  Output (dummy): Fagsystem X


Use: System owner contact in admin notification.


[SELECTION;rulesetId;returnField]

Parameter: rulesetId  |  Purpose: Ruleset to evaluate per object

Parameter: returnField  |  Purpose: Field from each object that matches


Expression: [SELECTION;1001;Email]  |  Output (dummy): kari.nordmann@eksempel.kommune.no|ola.hansen@eksempel.kommune.no|


Use: Mailing list of everyone matching "New starters" ruleset.


[AVAILABLESYSTEM;field] / [AVAILABLEROLE;systemId;field]

Returns pipe-separated values for systems or roles that pass their Available for ruleset on the current user.

Expression: [AVAILABLESYSTEM;Name]  |  Meaning: Names of systems Kari may receive permissions for

Expression: [AVAILABLEROLE;12;Name]  |  Meaning: Role names on system 12 she may be assigned


Use: Dynamic pick lists in forms or messages.


[AVAILABLEDEPARTMENTS;fields;targetUserId]

Parameter: fields  |  Purpose: Comma-separated department fields

Parameter: targetUserId  |  Purpose: User to evaluate; empty = current object


Use: List departments a manager may administer (from ManagerOf, unit ids, mega.eadm.no roles).


[DATAOWNER;returnField]

Follows data-owner relation on object; returns field from owner.

Expression: [DATAOWNER;Name]  |  Output (if owner group name stored): IT dataeiere



[ANTECEDANTS.SYSTEMROLE;roleId;departmentId;returnField]

Managers holding roleId on ancestor departments of departmentId.

Expression: [ANTECEDANTS.SYSTEMROLE;501;2101;Email]  |  Output (dummy): it-leder@eksempel.kommune.no,


Use: Escalation mail when HR child dept has no local approver.


[COUNTEMPLOYEE] / [COUNTMEMBERS]

Evaluated on department or group object respectively.

Object: Department IT  |  Expression: [COUNTEMPLOYEE]  |  Output (dummy): 47

Object: Group "Prosjekt Alfa"  |  Expression: [COUNTMEMBERS]  |  Output (dummy): 12



[GETLASTSYNCID]

Expression: [GETLASTSYNCID]  |  Output (dummy): 88421 (latest import file id for company)


Use: Troubleshooting footers in admin mails.


[LASTVALUE;attributeName]

Previous value of field from last import HR history, else current field.

Context: Before sync Kari had one position  |  Expression: [LASTVALUE;APositionCodeId]  |  Output: POS-01|

Context: After adding POS-02  |  Expression: [APositionCodeId]  |  Output: POS-01|POS-02|


Pair with [ARRAYDIFF;[APositionCodeId];[LASTVALUE;APositionCodeId]]POS-02|.


Positions (multi-position HR)

Position fields share index across pipes: index 0 = first position, 1 = second.

[POSITION;returnField;whereField;matchValue]

Parameter: returnField  |  Purpose: Position field to return (e.g. APositionCodeName)

Parameter: whereField  |  Purpose: Field to match (e.g. AUnitId)

Parameter: matchValue  |  Purpose: Unit or code to find


Expression: [POSITION;APositionCodeName;AUnitId;IT-100]  |  Output: Utvikler

Expression: [POSITION;APositionCodeName;AUnitId;HR-200]  |  Output: HR-rådgiver



[POSITIONS;returnField;whereField;matchValue]

Same as [POSITION] but returns aligned pipe string across all positions (empty slot where no match).

Expression: [POSITIONS;APositionCodeName;AUnitId;IT-100]  |  Output: Utvikler| (second position empty at same index pattern)



[GROUPPRIMARYPOSITION;returnField]

Picks primary position: highest APositionPercentage, tie-break by earliest APositionStartDate.

Kari positions: 100% IT, 20% HR  |  Expression: [GROUPPRIMARYPOSITION;APositionCodeName]  |  Output: Utvikler



[ACTIVEPOSITIONCOUNT;asOfDate]

Expression: [ACTIVEPOSITIONCOUNT;17.06.2026 00:00:00]  |  Output: 2 (both positions active on that date)



Messaging and integration

[COUNTMESSAGESENT;messageId;fromDate]

Parameter: messageId  |  Purpose: Message template id (optional if flow provides context)

Parameter: fromDate  |  Purpose: Only count sends after this date (optional)


Expression: [COUNTMESSAGESENT;1936;01.06.2026 00:00:00]  |  Output (dummy): 1


Use: "Already notified?" guard in follow-up flow.


[SYSTEMOWNER]

Requires history row from permission change (Add/Edit/Delete permission).

Context: Permission on role 501 added  |  Output (dummy): fagsystem-eier@eksempel.kommune.no


Use: CC system owner when automated permission is granted.


[SYNCSTEPPARAMETER;key;companySyncStepId]

Parameter: key  |  Purpose: Parameter name on sync step

Parameter: companySyncStepId  |  Purpose: Customer sync step instance id


Expression: [SYNCSTEPPARAMETER;ApiBaseUrl;42]  |  Output: https://api.example.invalid/hr


Access: Company administrator in expression builder preview.


[APITOKEN;apiUsername]

Parameter: apiUsername  |  Purpose: API user login name (object type 14)


Expression: [APITOKEN;sync.service]  |  Output: Opaque session token string (varies)


Access: Company administrator in preview. Use only in secured server-side flows.


HTML helpers (message templates)

[HTMLLISTMEMBEROF;displayField]

On a group member user: lists parent groups.

Expression: [HTMLLISTMEMBEROF;Name]  |  Output:

  • Prosjekt Alfa
  • IT-utviklere



[HTMLLISTARRAY;list]

Expression: [HTMLLISTARRAY;Utvikler|HR-rådgiver]  |  Output:

  • Utvikler
  • HR-rådgiver



Form template controls

Used in form templates, not standard rulesets.

Function: [INPUTBOX;fieldId;label;default;value]  |  Purpose: Text input bound to form field id

Function: [CHECKBOX;...]  |  Purpose: Boolean input

Function: [LISTBOX;...]  |  Purpose: Dropdown (five parameters)

Function: [RADIO;...]  |  Purpose: Radio group

Function: [DATEBOX;...]  |  Purpose: Date picker

Function: [AREABOX;...]  |  Purpose: Multi-line text

Function: [BUTTON;...]  |  Purpose: Action button


Example pattern: [INPUTBOX;1042;Avdeling;IT-100;] renders input labelled "Avdeling" defaulting to IT-100.

Markers [NOTIFY...], [HIDDEN...], [SERVERFUNCTION...] are form behaviour hooks, not data functions.


End-to-end scenarios (dummy data)

Welcome mail — start date only

Ny ansatt [DisplayName] starter [SUBSTRING;0;10;[Start]].

Output: Ny ansatt Kari Nordmann starter 01.04.2026.


Permission expiry line

Gyldig til: [IFEMPTYUSE;[OBJECTPERMISSION;ExpiryDate;SystemRoleId;501];Permanent tilgang]

Output: Gyldig til: Permanent tilgang


Manager missing — escalate to parent dept manager

[IFEMPTYUSE;
 [EXISTS;[Manager];];
 [REFERENCE;Email;UserId;1;[REFERENCE;Manager;Id;2;[REFERENCE;ParentDepartmentNumber;DepartmentNumber;2;[DepartmentNumber]]]]]

Kari Manager: empty  |  Output: ola.hansen@eksempel.kommune.no (parent dept manager email)

Kari Manager: set  |  Output: empty (EXISTS branch — adjust template logic if you need manager email when present)


Adjust IFEMPTYUSE/EXISTS pairing to match your template intent; test in expression builder.


New position only — message recipient

After sync adds POS-02:

[REFERENCE;Email;UserId;1;[POSITION;APositionManager;AUnitId;[RTRIM;[ARRAYDIFF;[APositionCodeId];[LASTVALUE;APositionCodeId]];|]]]

Output: Manager email for the new position's unit (dummy: ny-leder@eksempel.kommune.no).


Auto security group name (customer setup)

SG-[Department]-[CLEAN;[OrgUnitnr]]

Output: SG-IT og digitalisering-IT-100 (exact CLEAN output may vary).


Export mapping — samAccountName

[UserName]

Output: kari.nordmann


Ruleset — user has role on system

Rule attribute expression:

[OBJECTPERMISSION.SYSTEMROLE;Name;SystemRoleId;501]

Resolved value: Bruker Fagsystem X — compare with operator Er lik / membership operators.


Expression builder

Item: Where  |  Detail: Header icon expression builder, message template editors, some eHub screens

Item: Function list  |  Detail: All object fields for selected type plus standard functions

Item: Preview  |  Detail: Pick test user/department/group; requires super manager

Item: Rulesets  |  Detail: Automation → Rulesets — minimum super manager



Troubleshooting

Symptom: Literal [FUNCTION...] in output  |  Likely cause: Inner field empty or typo  |  Action: Fix nesting; verify field names on object

Symptom: Expression evaluation error  |  Likely cause: Nested [ARRAYFOREACH...]  |  Action: Split into sibling calls or use [ARRAYJOIN]

Symptom: Empty string  |  Likely cause: No matching id / permission  |  Action: Confirm system role id and system id in system access

Symptom: Wrong position in mail  |  Likely cause: Several positions changed at once  |  Action: Do not rely on ARRAYDIFF/LASTVALUE alone

Symptom: Preview blocked  |  Likely cause: Role or protected field  |  Action: Elevate role or simplify test expression

Symptom: Works in builder, not in message  |  Likely cause: Event flow needs object history from sync  |  Action: Run import; check entry type in history



Quick index

Category: Date/time  |  Functions: NÅ, NOW, DATEFORMAT, DATEDIFF, DAYOFYEAR

Category: String  |  Functions: SUBSTRING, SPLIT, REPLACE, REPLACELIST, RTRIM, LTRIM, RPAD, LPAD, RIGHT, INITIALS, PRETTIFY, LOWER, UPPER, CLEAN, STRICTCLEAN, CNCLEAN, GETKEYVALUE

Category: Logic  |  Functions: IFTHENELSE, IFEMPTYUSE, EXISTS, TRANSFORM, CONTAINS

Category: Arrays  |  Functions: COUNT, ARRAYAPPEND, ARRAYUNIQUE, ARRAYFILTER, ARRAYDIFF, ARRAYJOIN, ARRAYFOREACH, ARRAYPREFIX, ARRAYTRIM, PRUNEARRAY, UNION

Category: Security  |  Functions: HASH, PASSWORD, UNIQUE, RND, STRRND

Category: Lookup  |  Functions: REFERENCE, OBJECTPERMISSION, OBJECTHISTORIES, FOREACH, DESCENDANTS, SYSTEM, SELECTION, AVAILABLESYSTEM, AVAILABLEROLE, AVAILABLEDEPARTMENTS, DATAOWNER, ANTECEDANTS.SYSTEMROLE, COUNTEMPLOYEE, COUNTMEMBERS, GETLASTSYNCID, LASTVALUE

Category: Positions  |  Functions: POSITION, POSITIONS, GROUPPRIMARYPOSITION, ACTIVEPOSITIONCOUNT

Category: Messaging  |  Functions: COUNTMESSAGESENT, SYSTEMOWNER, SYNCSTEPPARAMETER, APITOKEN

Category: HTML  |  Functions: HTMLLISTMEMBEROF, HTMLLISTARRAY

Category: Forms  |  Functions: INPUTBOX, CHECKBOX, BUTTON, LISTBOX, RADIO, DATEBOX, AREABOX



AI assistant notes

  1. Prefer [DisplayName]-style fields before wrapping in functions.

  2. Ask where the expression lives: ruleset vs message vs export vs customer setup.

  3. Replace dummy ids (501, 2100) with the customer's real system role and department ids from system access.

  4. Position filter on import excludes matches; sync step ruleset includes matches.

  5. Event message flows need object history from a sync run.

  6. Build nested examples inside-out — except [ARRAYFOREACH...], which binds ??? per element before template functions run.

  7. Do not nest [ARRAYFOREACH...]; use sibling calls or [ARRAYJOIN].

  8. Terms: ruleset (Regelsett), message flow (meldingsflyt), system access (systemtilgang), permission (tilgang), object history (objekthistorikk).