Reference Fields

The two value types that point at a record in another module — Entity Reference and User Reference: which to use, what is actually stored, why the name is never saved with it, how a target type is pinned, what may be referenced, and the workspace rules.

Every other value type stores something you typed. These two store a pointer: the field holds no text of its own, only the identity of another record somewhere else in the product. A field on an administrator record that says which staff member they are, a field on a booking that says who reviewed it, a field on a person that says which account manager looks after them — all three are one record pointing at another, and until these types existed there was no way to record that without retyping a name and watching it drift.

What a reference field gives you

A reference is not a text field that happens to contain somebody's name. It is a real pointer, checked when you save it and re-checked every time it is read, and every one of the following follows from that.

  • Points at a real record

    You pick from a searchable list of records that actually exist, in your own workspace, rather than typing a name and hoping it matches. Nothing is stored until a real record has been chosen.

  • Always shows the current name

    The name is looked up fresh each time the field is displayed. When somebody's name is corrected on their own record, every reference pointing at them shows the correction immediately — there is no copy to go stale.

  • Carries the target's own permissions

    Reading the name needs permission to view that kind of record, not permission to view the record holding the field. Somebody who may edit the owner record but may not read staff sees that a reference is set and does not see who it points at.

  • Searchable, paged, and it tells you what it cannot do

    The picker searches the target module's own records a page at a time, marks a dormant record as inactive rather than hiding it, and says in words when there is nothing you are allowed to point at — never an empty dropdown that reads as "there are no records".

  • Can be pinned to one kind of record

    An Entity Reference field can be pinned so that every value must point at, say, a staff member — or left unpinned, in which case each value chooses its own kind of record and records that choice alongside the pointer.

  • Clears itself when the target is deleted

    Delete the record a reference points at and the pointer is cleared automatically. The value row itself survives with its audit trail — only the pointer goes, and nothing has to be tidied up by hand.

Entity Reference or User Reference

There are two reference value types and they are mechanically almost identical. The difference is entirely about what each one is allowed to point at, and therefore about how much you have to configure. Pick User Reference whenever the answer is "a person who logs in"; pick Entity Reference for everything else.

AspectEntity ReferenceUser Reference
What it may point atAny kind of record the platform can currently answer for and you are allowed to view.Exactly one kind of record: a user account. Nothing else is accepted, ever, and that list is fixed by the platform rather than by configuration.
What you configureOptionally, a Target Entity Type on the definition. Leaving it unpinned is a real and permanently supported choice, not an unfinished one.Nothing at all. There is no target picker on the definition form for this type, because there is no decision to make.
What the person filling it in seesOn a pinned field, one searchable list of that kind of record. On an unpinned field, two controls: first the kind of record, then the record.One searchable list of user accounts. There is never a type control.
Reach for it whenThe answer is a business record — a staff member, a person, a facility — or when different records under the same field legitimately point at different kinds of thing.The answer is an account: assigned to, reviewed by, account manager, approved by.
How the answer is storedThe kind of record, plus that record's own identity. Both, always together.Identically. The stored value is self-describing in exactly the same way, which is what keeps an old answer readable after the definition changes.

What is actually stored

A reference value is two pieces, held together. This is the same shape Currency uses for its amount and its code, and for the same reason: neither piece means anything on its own.

PieceWhat it isRequired?
The kind of recordThe kind of record being pointed at, as a stable identifier — for example hrms.staff-member. It is stored on the answer itself, not looked up from the definition.Yes — always, on every answer
The record's identityThe identity of the specific record being pointed at, as an opaque string.Yes — always, on every answer

An identity with no kind of record names a row but no table; a kind of record with no identity names a table but no row. So a value is treated as empty only when both pieces are missing — which is exactly how Currency and Date & Time behave — and half of a reference is refused rather than quietly stored or quietly cleared. If you ever see a save refused for an incomplete reference, one of the two controls was left alone.

Why the display name is never stored

The obvious design is to save the name next to the identity, so a reference can be displayed without asking anybody anything. The product deliberately does not do that, and the reason is a permission boundary rather than a preference about freshness.

A name saved alongside the pointer would sit inside the record that holds the field, and would therefore be readable by anybody holding permission to view that record. But the name belongs to the target — it is guarded by the permission that protects that kind of record. Snapshotting it hands a name to somebody who was never granted the permission that protects it. That is a permission bypass wearing a performance argument, and no amount of caching makes it a different thing.

So a name is resolved live, on every read, through a call that re-applies the target's own view permission and the target module's own workspace filter every single time. The practical benefit is the one you would want anyway: a name corrected on its own record is corrected everywhere it is referenced, instantly, with nothing to re-run and no stale copies to hunt down.

Pinning a target type on the definition

An Entity Reference definition carries one optional setting of its own: Target Entity Type. It answers "what kind of record may this field point at?", and it is offered only for Entity Reference — a User Reference field never shows it, because its answer is already fixed.

State of the settingWhat it meansWhat the record form then shows
Not pinned — any allowed typeEvery answer may point at any kind of record the person filling it in is allowed to reference, and each answer records which kind it chose. This is the state a brand-new definition starts in, and it stays legal forever.Two controls in order: a Record type control, then the record itself. The second is inert until the first is answered, and choosing a type does not move the cursor into the record control — you are left where you are, with the record control now available.
Pinned to one typeEvery new answer must point at a record of that one kind. An answer of any other kind is refused with a message naming both what was expected and what arrived.One control: the record. There is no type control at all.
A User Reference fieldPermanently equivalent to being pinned to user accounts, decided by the platform. An attempt to pin it to anything else is refused at definition time, not at save time.One control: the user account. There is never a type control.

The setting can be changed later, including on a field that already holds answers, and this is deliberate — refusing would mean a mis-pinned field could never be corrected without first destroying real data. What happens is worth stating exactly, because both halves matter: every answer already stored is left completely alone and still reads back correctly, because each answer carries its own kind of record. The next save of a record whose answer is of the old kind is refused, until somebody picks that answer again.

The edit form says so before you save it. Read that line rather than assuming either extreme — re-pointing is neither free nor destructive.

What can currently be referenced

The list is not "every record type in the product". A kind of record can only be referenced when the module that owns it supplies a way to search and resolve its records — reading its own data through its own screens' rules, so a picker can never be broader than the screen it mirrors. Six kinds of record supply that today; the last three joined the first three in a later release.

Record typeIdentifierOwned byWhat the picker shows for each row
Staff Memberhrms.staff-memberThe staff management moduleThe person's name, with their job title beneath it as the disambiguator. Deliberately the job title rather than an email address: a picker needs to tell two people with the same name apart, and does not need their contact details to do it.
Useridentity.userThe identity moduleThe account holder's name, with the username beneath it. An account whose sign-in is currently locked out is shown as inactive but stays selectable.
Party Personparty.personThe party and relationship moduleThe person's name only. The owning module supplies no second line at all, having judged that anything it could add would be personal data a picker does not need.
Administratoridentity.adminThe identity moduleThe administrator's name, falling back to the username when both name parts are blank, with the username as the second line. Deliberately never the email address, phone number, role names, or whether the row is a Super Admin — the narrowest of the three person-like rows on this list, because an admin record is the most sensitive thing this seam can point at.
Teamorganization.teamThe organization moduleThe team's name only, with no second line. Two teams sharing a name in different departments render identically today — the department that would tell them apart is not on this picker's row.
Branchorganization.branchThe organization moduleThe branch's name, with its time zone beneath it as the disambiguator — the same reason two branches both called "Main" are told apart on the branches screen itself.

Anything else is refused rather than answered with an empty list, and the difference is the whole point: an empty list looks like a normal result and would tell an administrator "there are no staff members", which is a false statement wearing the appearance of a correct one. A kind of record the platform cannot answer for produces a clear refusal instead, which the record form renders as a sentence saying this kind of record is not available in this installation.

And a genuinely empty list of available types is itself a legitimate answer, not a failure. It means "there is nothing you may point a reference at", which happens for two quite different reasons: the modules owning those records may not be part of this installation, or you may not hold view access to any of them. The product names both possibilities without claiming either, because only one of them is fixed by asking for permissions.

Two kinds of record that look as though they belong on that list and are excluded on purpose (administrator records used to be a third, until a later release gave them a lookup provider of their own — they are on the table above now, not here):

  • User groups. Perfectly safe, and simply not a person. A field typed User Reference that resolved to a group would be lying about what it holds.
  • Shared platform catalogue rows such as login themes. They belong to no workspace by design, so they fail the same test administrator records fail, and they are not people either.

Workspace and platform rules

References cross a module boundary, which makes the workspace boundary the thing to be precise about. Five rules, all of them enforced rather than advisory.

  1. Everything is workspace-guarded. Both searching for a record and resolving one you already hold read through the owning module's own repository, so the same workspace filter and the same deleted-record filter apply that apply on that module's own screens. You can only point at records your workspace can already see.
  2. Holding an identity is not a permission. A reference is re-authorised on every single read: the target's own view permission is required again, every time, and the fact that the pointer is already stored counts for nothing.
  3. Another workspace's record and a deleted record are one indistinguishable answer, deliberately. If they were told apart, somebody could test identities one at a time to learn what exists in a workspace they cannot see. "You may not see this kind of record" is told apart from "this record is gone", because those two have opposite fixes and neither reveals anything.
  4. Platform-level records belong to platform administrators. A custom field created at platform level is inherited by every workspace and can only be created, edited or deleted by a platform administrator — including the pinned target type on a platform-level reference field, which no workspace can change.
  5. Nobody may assign an administrator outside their own workspace. In practice the product goes further than the rule requires: an administrator record cannot be pointed at by a reference field at all, in your own workspace or any other, precisely because an administrator may sit outside every workspace.

A worked example: an administrator record pointing at a staff member

The case these types were built for. Your administrators are also employees, and you want each administrator record to say which staff record is the same person — recorded once, correctly, and never retyped.

  1. Decide which type you need

    The answer is a staff member, not a login account, so this is an Entity Reference. If the question had been "who reviewed this?", the answer would be an account and User Reference would be the right choice — and the value type is permanent, so it is worth the moment's thought.
  2. Define the field

    On the Custom Fields screen choose Add, pick the administrator record type, set the key to staff_record, the English label to Staff record, and the value type to Entity Reference. A Target Entity Type control appears as soon as you choose that value type.
  3. Pin the target to Staff Member

    Set Target Entity Type to Staff Member. That is what turns the field from "a pointer at something" into "a pointer at a staff member", and it is what lets the record form show one control instead of two. Leave it as Not pinned only if you genuinely want different administrators pointing at different kinds of record.
  4. Fill it in on a record

    Open any administrator record. The Custom Fields section now shows a Staff record control with a placeholder inviting you to select a record. Open it, type part of a name, and the list narrows to matching staff members with their job titles beneath. Pick one and save the record.
  5. Read it back, and notice what happened

    Reopen the record. The field shows the staff member's name — fetched just now, not remembered from your save. Change that person's surname on their own staff record, come back, and the reference shows the new surname without anybody touching the administrator record.
  6. Check the two behaviours that matter

    Sign in as somebody who may edit administrators but may not view staff: the field is present, it says the stored value is fine and that they may not see the name, and they cannot overwrite it. Then delete the staff member: the reference clears itself, the administrator record keeps its value row and its history, and the field reads as empty rather than as a broken pointer.

A worked example: a Reviewed by field

The User Reference case, which is shorter precisely because there is nothing to configure.

  1. Define the field

    Add a field on the record type you want, set the key to reviewed_by, the label to Reviewed by, and the value type to User Reference. No target control appears, and that is correct — the answer can only ever be a user account.
  2. Fill it in

    Open a record of that type. The Reviewed by control offers a searchable list of user accounts, each with its username beneath the name. Accounts that are currently locked out are marked inactive and remain selectable, because they are legitimate answers for something that already happened.
  3. Confirm what it stores

    The answer records the user account type and that account's identity — the same two pieces an Entity Reference stores, so a field that says User Reference is telling you what it holds rather than just what it was configured as.
  4. Confirm what it refuses

    There is no way, from this form or from a request that bypasses it, to make this field point at an administrator, a user group or a platform catalogue row. The refusal comes with a message naming what is allowed, and it is refused at definition time as well as at save time.

What reference fields are not

Reasonable expectations that these types deliberately do not meet. None of these is a fault to report.

  • They are not a relationship the product understands. Nothing is calculated from a reference, nothing is triggered by it, and no screen gains a "records pointing at this one" list because a reference exists.
  • They are not a way to hide records. A picker shows exactly what the target module's own screens show to that same person. If somebody should not see a kind of record, that is a permission on that kind of record.
  • They do not store a name, ever, and there is no setting to make them. A field that must survive the target being deleted with the old name still readable is a Text field, and accepting that it will drift is the price of that choice.
  • They are not many-to-many. One reference field holds one pointer. There is no multi-value reference type, and Multi-Select cannot be pointed at records — its answers are text you authored.
  • They cannot point at every kind of record. Only the kinds whose owning module supplies a searchable, permission-checked list can be referenced, and the rest are refused rather than silently offered.
  • They are not carried in the definitions spreadsheet export. That file has eighteen columns and a pinned target type is not one of them, so an exported definition does not record what its field points at.

Where to go next

The mechanics of looking a reference up — the three lookups, every failure state, and what to do about each — are on their own page.

PageWhat it covers
Reference LookupsThe three lookups behind a reference, what every answer and every refusal means, the five failure states and whose problem each one is, delete behaviour, and how the picker paginates.
Value TypesAll twenty-two value types side by side, including these two, with worked example inputs and the error code each rejection returns.
Defining a FieldThe definition form control by control, including the Target Entity Type control and every rejection it can produce.