Value Types

All twenty-two custom-field value types: what each one stores, exactly what it accepts and rejects, worked example inputs, and the error codes the product returns.

Every custom field has exactly one value type, chosen when the field is defined. The value type decides what control appears on the form, what the product accepts, how the value is stored and how it is displayed afterwards. This page covers all twenty-two, one at a time, with example inputs that are accepted and example inputs that are refused. Eighteen of them store something you typed; the other four store a pointer instead — two at a record somewhere else in the product, with a page of their own as well, and two at a single uploaded file or image.

How a submitted value is checked

Every save runs the same four steps in the same order for every type. Knowing the order explains most surprises.

  1. Is the value empty? A missing value, a blank string, or a string of nothing but spaces counts as empty. For MultiSelect an empty list counts too, and for DateTime, Currency and the four reference-shaped types (EntityReference, UserReference, File, Image) a value counts as empty only when both of its parts are missing.
  2. If it is empty and the field is Required, the save is refused with VALIDATION_REQUIRED. If it is empty and the field is not required, the stored value is cleared and nothing else runs — no type check, no validator.
  3. If it is not empty, the type's own rules run: length caps, number parsing, range checks, allowed-option matching, format checks.
  4. For a Text field with a validator attached, and only then, the validator runs last — after the global 4,000-character cap and after the validator's own shorter length cap.

One detail worth knowing before you read any error message: the message names the field's key, not its label. A field labelled Nationality with the key nationality produces "'nationality' expects a date.", not "'Nationality'".

Text and choices

Text

A single line of free-form text, up to 4,000 characters. Renders as an ordinary single-line input.

The only check is the length cap — unless a validator is attached, which makes Text the only type that can carry a format check. The value is stored exactly as submitted; unlike Select, Text does not trim surrounding spaces.

Example inputWhat happens
EgyptianAccepted, and stored exactly as submitted.
A value 4,500 characters longRefused: VALIDATION_MAX_LENGTH. Text stops at 4,000 characters — use LongText for anything longer.
Three spaces, on a field that is not RequiredAccepted, and stored as cleared. Whitespace-only counts as empty, so any attached validator never runs on it.
Three spaces, on a Required fieldRefused: VALIDATION_REQUIRED. Whitespace-only counts as empty here too.

LongText

Longer free-form content, up to 10,000 characters. Renders as a real multi-line text area, not a taller single-line box.

Only the 10,000-character cap. LongText cannot carry a validator. The on-screen counter turns red once you pass the cap, but it does not stop you typing — the refusal comes when you save.

Example inputWhat happens
A 6,000-character descriptionAccepted. This is well past Text's own 4,000-character cap, which is the reason LongText exists.
A 12,000-character descriptionRefused: VALIDATION_MAX_LENGTH, naming the 10,000-character cap.

Select

One answer chosen from a list you write yourself. Renders as a dropdown offering exactly your options.

The submitted value must match one of the field's configured options exactly. Both sides are trimmed before comparison, and the comparison is case-sensitive. For an options list of Small, Medium, Large:

Example inputWhat happens
MediumAccepted, and stored as the option text itself.
" Medium" with a leading spaceAccepted. Surrounding spaces are trimmed before the comparison.
mediumRefused: VALIDATION_INVALID_FORMAT. Matching is case-sensitive, so Medium and medium are different answers — which also means the two can legitimately both exist as separate options.
Extra-LargeRefused: VALIDATION_INVALID_FORMAT. The message quotes the rejected value and the field's key.

MultiSelect

Several answers from the same kind of list, up to 19 of them. Renders as a multi-select combobox with a live "N of 19 selected" counter.

Every submitted answer must be one of the field's configured options, no answer may repeat, and there may be at most 19. The order you pick in is preserved end to end. For an options list of Red, Green, Blue, Yellow:

Example inputWhat happens
Blue, then RedAccepted, and read back in the order picked — Blue first, then Red — not re-sorted into the order the options were listed in.
20 selectionsRefused: VALIDATION_MAX_LENGTH, naming the ceiling of 19. The picker itself makes the twentieth option unselectable, so reaching this needs a request that bypasses the form.
Red, then Red againRefused: VALIDATION_UNIQUE. A repeated answer is rejected rather than quietly collapsed to one.
PurpleRefused: VALIDATION_INVALID_FORMAT — Purple is not one of the field's options.
An explicitly empty listTreated as empty: cleared if the field is optional, refused with VALIDATION_REQUIRED if it is required.

Numbers and measures

Number

Any number, whole or with decimals, positive or negative, with up to six decimal places.

Only that the value parses as a number. No minimum, maximum, precision or rounding rule is applied, so choose Number when genuinely any number is a valid answer — and choose Percent, Rating, Currency or Duration when it is not.

Example inputWhat happens
42Accepted.
-17.5Accepted. Negative values are perfectly valid for this type.
1.2345678Accepted, and stored to six decimal places. Anything finer than that is not preserved.
"about 40"Refused: VALIDATION_INVALID_FORMAT — the message reads "expects a number". A number written as words is not parsed.

Percent

A percentage between 0 and 100 inclusive, decimals allowed. Renders as a plain numeric input, and displays afterwards as the number with a % sign appended.

The value must parse as a number and fall inside 0 to 100. It is stored exactly as typed — this is the detail to get right if you ever read the raw data or build an export.

Example inputWhat happens
25Accepted, and shown afterwards as 25%.
33.5Accepted, and shown as 33.5%. Fractions of a percentage point are kept exactly.
0.25Accepted — but it means a quarter of one percent, shown as 0.25%. Percent stores the number you would say out loud, never a 0-to-1 fraction.
150Refused: VALIDATION_RANGE, naming the bounds 0 and 100.
-5Refused: VALIDATION_RANGE. The lower bound is 0, and it is inclusive.

Rating

A whole number from 1 to 5, captured on a slider. Displays afterwards as "4 / 5".

The value must parse as a number, be a whole number, and fall between 1 and 5 inclusive. There is no star control and no free-text entry.

Example inputWhat happens
4Accepted, and shown as 4 / 5.
0Refused: VALIDATION_RANGE. A zero is a real submitted value that fails the 1-to-5 check; it is not read as "unrated".
3.5Refused: VALIDATION_RANGE. Half ratings are not supported — this is a genuine difference from Number, which allows any decimal.
6Refused: VALIDATION_RANGE, with the same message a 0 gets.
The slider left untouched on a new recordSaved as empty, not as 1. The slider thumb has to sit somewhere, so an untouched field shows at its leftmost position — that is a display artefact, not a stored answer.

Currency

An amount together with its three-letter currency code, held as two independent inputs inside one labelled group. Displays afterwards through the reader's own number formatting, showing the code rather than a symbol so EUR and USD are never ambiguous.

Both parts are required together. The amount must parse as a number; the code must be exactly three uppercase ASCII letters. The code input uppercases and letter-filters as you type, because the check itself does not coerce lower case — it rejects it.

Example inputWhat happens
100.50 with the code USDAccepted. Displays as the amount alongside the code, for example USD 100.50.
100.50 with the code usdRefused if it ever reaches the server: VALIDATION_INVALID_FORMAT, naming the 3-letter ISO 4217 requirement. In the form itself the input forces upper case as you type, so you will not normally see this.
100.50 with the code left blankRefused: VALIDATION_INVALID_FORMAT. The form also blocks this before it calls the server, with a message saying the field needs both an amount and a currency code.
The amount left blank with the code USDRefused the same way. A code with no amount is a broken value, not a cleared one — only both parts missing counts as empty.
100.50 with the code ZZZAccepted. Only the shape of the code is checked, never its membership of the real ISO 4217 list, so a well-formed code that does not exist gets through. The display falls back to "ZZZ 100.50" for a code the reader's browser does not recognise.
10050 with the code USDAccepted, and it means ten thousand and fifty. There are no minor units anywhere in custom-field storage — 100.50 is stored as 100.50, never as 10050.

Duration

A length of time counted in minutes. Renders as a number input with a visible "minutes" label beside it, never as a bare unlabelled number.

The value must parse as a number and must not be negative. Zero is accepted — a legitimate "no buffer". There is no upper bound at all.

Example inputWhat happens
90Accepted, and displayed as 90 minutes.
1.5Accepted, and kept exactly as 1.5 — ninety seconds. Decimals are not rounded to whole minutes.
0Accepted. Zero is a real answer, not an empty one.
5400Accepted — 5,400 minutes, which is three and a half days. Nothing warns you, because there is no maximum.
-5Refused: VALIDATION_RANGE, with a message saying the value must not be negative.

Dates and times

Date

A calendar date with no time component at all — a birthday, a contract date, an expiry. Renders as a date picker.

Only that the value parses as a date. Because the stored value is a plain calendar date rather than a moment in time, it reads back identically for every viewer regardless of their time zone.

Example inputWhat happens
2026-08-21Accepted, and read back as the same calendar date for every viewer, anywhere.
A date with a time component attachedIgnored. Date holds no time component, so a time submitted alongside the date is simply not stored. Use DateTime when the time matters.
"next Tuesday"Refused: VALIDATION_INVALID_FORMAT — the message reads "expects a date".

DateTime

A precise moment together with the time zone it belongs to. Both halves are stored, so a kick-off at 18:00 in Cairo still reads as 18:00 in Cairo for somebody looking at it from London.

The instant must parse, and the time zone must be a zone id the server recognises — in practice an IANA identifier such as "Africa/Cairo", though the underlying check is platform-dependent and a Windows-hosted deployment also accepts a native Windows id such as "Egypt Standard Time". The zone is required as soon as either half is present — an instant with no zone is refused, not silently interpreted. The form shows the zone as a small disclosure beside the entered time, with a Change link that opens a searchable picker.

Example inputWhat happens
18:00 on 21 August 2026, zone Africa/CairoAccepted. Both the instant and its zone are read back exactly as entered.
18:00 on 21 August 2026, zone left blankRefused: VALIDATION_INVALID_TIMEZONE. A moment with no zone is exactly what DateTime exists to prevent.
18:00 on 21 August 2026, zone Not/AZoneRefused: VALIDATION_INVALID_TIMEZONE, naming the unrecognised identifier. Zones are real IANA names such as Africa/Cairo or Asia/Tokyo.
Both the instant and the zone left blankTreated as empty: cleared if the field is optional, refused with VALIDATION_REQUIRED if it is required. Only both halves missing counts as empty.

Time

A time of day on a 24-hour clock, seconds included, with no date attached — an opening time, a curfew, a kick-off slot. Renders as a native time picker with seconds enabled, and displays afterwards in each reader's own local time format.

The value must be hours, minutes and seconds separated by colons, with hours 0 to 23, minutes 0 to 59 and seconds 0 to 59. Unpadded input is accepted and normalised rather than refused.

Example inputWhat happens
14:30:00Accepted, and shown in the reader's own format — for example 2:30:00 PM for an English (US) reader.
9:5:0Accepted, and normalised to 09:05:00 before storage. Two submissions of the same time written with different digit widths always end up identical.
24:00:00Refused: VALIDATION_INVALID_FORMAT. Hours run 0 to 23, so 24 is out of range.
12:60:00Refused: VALIDATION_INVALID_FORMAT. Minutes run 0 to 59.
2:30 PMRefused: VALIDATION_INVALID_FORMAT. Twelve-hour text is not parsed — the stored form is always 24-hour, even though the display is not.

Contact details and links

Email

An email address. Renders as a native email input, and displays afterwards as a clickable mail link.

The address is parsed as a real address rather than matched against a pattern, and it must be nothing but the address. Case is preserved exactly as typed — no lowercasing.

Example inputWhat happens
User.Name+Tag@Example.COMAccepted, stored with its exact capitalisation, and shown as a clickable mail link.
"Test User <test@example.com>"Refused: VALIDATION_INVALID_EMAIL. A display-name wrapper parses as an address but is rejected rather than silently stripped, because an Email field has no display name to keep.
not-an-emailRefused: VALIDATION_INVALID_EMAIL.

Url

A web address. Renders as a native URL input, and displays afterwards as a real link that opens in a new tab.

The value must be an absolute address whose scheme is exactly http or https. Every other scheme is refused. The scheme is checked again on the way out, before the value is ever rendered as a link.

Example inputWhat happens
https://example.com/teamAccepted, and shown as a link opening in a new tab.
http://localhost:3000Accepted. Plain http is deliberately allowed — a company site or an internal address during setup is legitimate data.
example.comRefused: VALIDATION_INVALID_FORMAT. A bare host is rejected rather than guessed at, so nothing has to decide whether you meant http or https.
javascript:alert(1)Refused: VALIDATION_INVALID_FORMAT. This is a real security boundary, not a style rule — and because the scheme is re-checked before display, even a value stored before this check existed shows as inert text rather than a live link.
ftp://example.comRefused: VALIDATION_INVALID_FORMAT. Only http and https are on the list.

Phone

A phone number in international format. Renders through a country picker with flags and search, and displays afterwards reformatted for readability — for example +20 123 456 7890.

The stored value must start with a +, its first digit must not be zero, and it must hold between 8 and 15 digits in total. That is a check on shape only.

Example inputWhat happens
+201234567890Accepted, and displayed reformatted rather than as the bare stored string.
01234567890Refused: VALIDATION_INVALID_FORMAT. The leading + is part of the format.
+0123456789Refused: VALIDATION_INVALID_FORMAT. A country code never starts with zero.
+1234567Refused: VALIDATION_INVALID_FORMAT. Seven digits is below the minimum of eight.
+10000000000Accepted by the server, which checks shape only and not whether the number could really exist. The form's own picker additionally checks the number against the selected country's real numbering plan, so you cannot build this value through the interface — only a request that bypasses the form.

Yes/no and colour

Boolean

A plain yes or no. Renders as an on/off toggle. Has no placeholder and no options.

Only the words true and false are parsed, in either case. Nothing else is treated as a synonym.

Example inputWhat happens
trueAccepted.
falseAccepted.
1Refused: VALIDATION_INVALID_FORMAT — the message reads "expects a boolean". A numeric 1 is not read as true.
yesRefused: VALIDATION_INVALID_FORMAT. Neither yes/no nor on/off is accepted.

Color

A colour, stored as a hex value. Renders as a grid of twenty swatches plus a custom hex entry, and displays afterwards as the hex text with a small matching colour chip beside it.

The value must be a # followed by exactly three or exactly six hexadecimal digits. Case is normalised to lower case on save; length is not.

Example inputWhat happens
#AABBCCAccepted, and stored as #aabbcc. Upper case is folded down to lower case.
#abcAccepted, and kept as #abc. The shorthand is never expanded to #aabbcc, even though a renderer treats the two as the same colour — so the same colour can legitimately be stored two ways across different records.
aabbccRefused: VALIDATION_INVALID_FORMAT. The leading # is required.
#abRefused: VALIDATION_INVALID_FORMAT. Three or six digits, nothing in between.
redRefused: VALIDATION_INVALID_FORMAT. Colour names are not accepted, only hex values.

References to another record

The last two types store no text of their own. Each one stores a pointer at a record somewhere else in the product, and the name you see is looked up fresh every time the field is displayed rather than saved alongside the pointer. Both store the same two pieces — the kind of record and that record's own identity — and both treat a value as empty only when both pieces are missing. There is a great deal more to say about them than fits a table; the Reference Fields and Reference Lookups pages say it.

EntityReference

A pointer at a record of any kind this installation can answer for and you are allowed to view. Renders as a searchable picker over that kind of record — preceded by a second picker for the kind itself, when the definition does not pin one.

Both pieces are required together. The kind of record must be registered and, when the definition pins one, must be that one. The identity must be readable. And you must have been able to read that record at the moment you saved, which is what stops a pointer being used to reach data you cannot open directly. Each check refuses with its own message rather than a generic one.

Example inputWhat happens
A staff member chosen from the pickerAccepted. The answer records both the kind of record and that record's identity, and the picker shows the record's current name from then on.
A kind of record chosen, with no record pickedRefused as an incomplete reference. Half a pointer is not treated as an empty field — it means somebody began answering and stopped.
A record picked, with no kind of record sentRefused the same way. An identity with no kind of record names a row but no table, so there is nothing to look it up in.
A person, on a field pinned to staff membersRefused, and the message names both what the field expects and what arrived. The pin is a deliberate restriction, so this is the refusal working rather than failing.
A kind of record that is not registeredRefused: ENTITY_UNKNOWN_TYPE, naming the identifier. Only reachable from a request that bypasses the picker, which never offers an unregistered kind of record.
A stored identity altered by one characterRefused: ENTITY_INVALID_ID. An identity is opaque and must be sent back exactly as it was received — one altered character makes it unreadable.
A record of a kind you may not viewRefused: AUTH_FORBIDDEN, naming the field. Storing a pointer at a record is a deferred read of that record, so it needs the same permission reading it would.
Both parts left blankTreated as empty: cleared if the field is optional, refused with VALIDATION_REQUIRED if it is required. Only both pieces missing counts as empty.

UserReference

A pointer at a user account — assigned to, reviewed by, account manager. Renders as a searchable picker over user accounts, and never shows a control for choosing a kind of record, because there is only one.

Every check EntityReference makes, plus one narrower rule: the only kind of record accepted is a user account. That list is fixed by the platform rather than by configuration, and an attempt to point this type at anything else is refused both when a definition is configured and when a value is saved.

Example inputWhat happens
A user account chosen from the pickerAccepted, exactly as an EntityReference is. The answer is self-describing in the same way.
An account whose sign-in is currently locked outAccepted. A locked-out account is dormant rather than deleted: it still exists, it is still offered by the picker with an inactive marker, and it is a legitimate answer for something that already happened.
An administrator recordRefused, with a message naming what is allowed. An administrator can belong to no workspace at all, which is the one property a reference target must never have.
A user groupRefused the same way. A group is safe to read but is not a person, and a field typed UserReference that resolved to a group would be lying about what it holds.
A login themeRefused the same way. A shared platform catalogue row belongs to no workspace and is not a person either — excluded twice over.
Both parts left blankTreated as empty on exactly the same terms as EntityReference.

Media and formatted text

File and Image are built the same way as the two reference types above — a pointer, not stored text — but each one points at a single uploaded file instead of another record. RichText is different again: it stores real formatted content, authored in the product's own editor.

File

A pointer at one uploaded file — a signed waiver, a medical certificate, an insurance document. Renders as a small status control showing whether a file is attached, with a Clear button when one is.

A stored value is only accepted when the referenced file is genuinely attached to the record you are editing — a security check that stops a file meant for one record being pointed at from another. Attaching a new file from this screen is not available yet: the field can be defined today, and an existing value can be viewed or cleared, but filling one in for the first time is on its way in a future release.

Example inputWhat happens
A record whose File field already holds a valueShown as attached, with a Clear control. There is currently no attach control alongside it.
Clear an attached file, then saveAccepted — the value is removed.

Image

File's sibling, restricted to images — a player photo, a facility hero shot, a team crest. Same status control, same current limitation on attaching a new one.

Everything File checks, plus the referenced file itself must be an image. Attaching a new image from this screen is not available yet either — see File, above.

Example inputWhat happens
A record whose Image field already holds a valueShown as attached, with a Clear control.

RichText

Formatted prose authored in the product's own editor — a coaching note with paragraphs and a bulleted list, a policy blurb with a link. Renders as a real rich-text editor, not a plain box.

Up to 50,000 characters of markup, checked before it is cleaned up automatically: an inline style and an embedded image are both removed, because the first can visually hijack the surrounding page and the second can quietly track whoever later views the field. There is no separate warning when that happens — reopen the field afterwards and what you see is exactly what was kept.

Example inputWhat happens
A paragraph with a bold word and a bulleted listAccepted, and every element is kept.
Content pasted with an inline style appliedAccepted, with the style removed. The visible text and structure are kept.
Content with an embedded imageAccepted, with the image removed. A picture belongs on a File or Image field instead.
More than 50,000 characters of markupRefused: VALIDATION_MAX_LENGTH — shorten it and try again.

Empty values and the Required toggle

Every type shares one definition of empty, and it is checked before anything else. A value counts as empty when:

  • it is missing from the save entirely;
  • it is blank, or made up of nothing but spaces;
  • for MultiSelect, the list of selections is explicitly empty;
  • for DateTime, both the instant and the time zone are missing — not just one of them;
  • for Currency, both the amount and the currency code are missing — not just one of them;
  • for EntityReference, UserReference, File and Image, both halves of the pointer are missing — not just one of them.

An empty value on a Required field is refused with VALIDATION_REQUIRED. An empty value on an optional field is accepted and the stored answer is cleared — the row is kept rather than deleted, so history is not lost.

Error codes you may see

Almost every refusal is an HTTP 422 with one of these machine-readable codes; two of them are a 403 instead, because they are about your access rather than about the shape of what you sent. A third one is worth flagging separately: its code name reads like a 404, but the response is still a 422 — see the note beside it below. If you ever see a 500 from saving a custom-field value, that is a defect worth reporting — the validation path is written to refuse cleanly, never to fail.

CodeWhen it fires
VALIDATION_REQUIREDThe field is Required and the submitted value is empty or whitespace-only.
VALIDATION_INVALID_FORMATThe value does not match the shape the type expects — an unparsable number, date or time, an option that is not on the list, a disallowed URL scheme, a bad phone shape, a bad hex colour, a bad currency code, or most validator failures.
VALIDATION_INVALID_EMAILAn Email field's value is not a real address, or carries a display name.
VALIDATION_INVALID_TIMEZONEA DateTime value is missing its time zone once an instant is present, or names a zone the server does not recognise.
VALIDATION_RANGEA number is outside its type's bounds — Percent outside 0 to 100, Rating outside a whole 1 to 5, a negative Duration, or a Numeric Range validator's own bounds.
VALIDATION_MAX_LENGTHText past 4,000 characters, LongText past 10,000, RichText past 50,000, an Email or Url past 4,000, more than 19 MultiSelect selections, or a Length Range validator's upper bound.
VALIDATION_MIN_LENGTHA Length Range validator's lower bound.
VALIDATION_UNIQUEThe same MultiSelect option was submitted more than once in one save.
ENTITY_UNKNOWN_TYPEA reference names a kind of record that is not registered in this installation.
ENTITY_INVALID_IDA reference's stored identity could not be read — altered on its way through something, or a value that predates a change.
AUTH_FORBIDDENA reference points at a record you are not allowed to read. This one is a 403 rather than a 422, because it is about your access and not about the value's shape.
AUTH_FORBIDDENA File or Image value points at an upload that is not attached to the record you are editing. This one is a 403, for the same reason as the reference Forbidden case above — it is about ownership, not shape.
ENTITY_NOT_FOUNDA File or Image value's id will not decrypt, or decrypts to an upload that no longer exists. The code name reads like a 404, but the response is a 422 — the same shape every other malformed-value refusal on this page uses, not the not-found shape a client might expect from the name.
VALIDATION_INVALID_FORMATAn Image field was pointed at a real, owned upload that is not actually an image.
VALIDATION_INVALID_FORMATA RichText value did not arrive as the expected {"html": "..."} shape.

The Value Types screen in the product

The product carries its own read-only catalogue of these types, reached from a link in the Custom Fields page header. It is documentation, not configuration: nothing on it can be added, edited or removed, because value types are fixed by the platform. It is gated behind the same permission as the Custom Fields screen itself, and it is fully translated, right-to-left included.

Each row shows the type's name, a description of what it is for, whether it takes a placeholder, whether it owns an options list, and whether it supports a validator. Text is the only row showing validator support — that is the Text-only boundary made visible.

There is deliberately no plan or entitlement column on that screen. Value types are not individually plan-gated, so a column implying otherwise would be showing something that does not exist.