Limits and Behaviours
Every fixed cap and every deliberate limitation in custom fields, each with the reason it is that way — so nobody spends an afternoon looking for a setting that does not exist.
This page collects every limit a custom-fields administrator can reasonably expect to hit, and says why each one is where it is. Everything here describes current behaviour rather than a promise about the future. A limit stated plainly is cheaper than a limit discovered at four in the afternoon.
The fixed numbers
These are constants in the product. None of them can be raised or lowered for an individual field, and only the last one varies at all.
| Limit | Value | Configurable? |
|---|---|---|
| Text field length, in characters | 4,000 | No |
| LongText field length, in characters | 10,000 | No |
| MultiSelect selections per value | 19 | No |
| Rating scale, whole numbers only | 1 – 5 | No |
| Percent range, inclusive | 0 – 100 | No |
| Phone digits, after the leading + | 8 – 15 | No |
| Currency code length, uppercase letters | 3 | No |
| Duration upper bound | None | No |
| Records per page in a reference picker | 20 | No |
| Largest page a reference picker may ask for | 100 | No |
| Field groups per record type in one reorder | 100 | No |
| Definitions per spreadsheet export | 10,000 | No |
| Custom fields per workspace | Plan quota — zero on the Free edition | Set by your plan |
Validator behaviours
| Behaviour | Why |
|---|---|
| Validators attach to Text fields only. | The safety argument for the built-in patterns was derived for single-line text input. Extending it to a differently shaped input needs that analysis redone, and that is not something to smuggle into a feature release. A Text field with a validator is the answer when you need an email address with extra constraints. |
| Attaching a validator never re-checks answers already saved. | Validation runs in exactly one place: the save path. Nothing walks historical data when a validator is newly attached, so a field can legitimately hold values its own current validator would refuse, until somebody re-enters them. |
| A whitespace-only value skips validation entirely unless the field is Required. | The emptiness check runs before any type or validator check. On an optional field a value of nothing but spaces is therefore stored as cleared with no validator error at all. Mark the field Required if a blank answer should be refused. |
| There is no pattern or regular-expression box anywhere. | A hand-written pattern can be made to consume enormous processing time on a short input, turning a data-entry form into a way of taking the system down. The 13 curated checks exist precisely so that nobody has to author one. |
| The definitions list cannot be filtered or searched by validator. | No such view was built. To see which validator a field uses, open that field's definition form. |
| There is no browsable validator reference inside the product. | Value types and record types each got a read-only reference screen; validators did not. The dropdown on a Text field's definition form is the only in-product list. |
| The Egyptian and Emirati ID checks verify structure but not a check digit. | Neither country publishes a check-digit algorithm, and the community guesses found during research disagreed with each other. A wrong algorithm would reject real, valid IDs, which is worse than not checking the final digit at all. |
| Postal Code does not support the United Arab Emirates. | The UAE has no national postal-code system, so there is nothing to validate against. Attempting it is refused with its own explanatory message rather than a generic one. |
Value-type behaviours
| Behaviour | Why |
|---|---|
| The key, the record type and the scope can never be changed once a field is saved. | Renaming, retargeting or rescoping after the fact would make every already-stored answer ambiguous about what it means. The value type is the one exception with a narrow escape hatch: nine specific type pairs can be converted after the fact — see Managing — everything else still means delete and recreate. |
| A MultiSelect answer reads back in selection order, not option order. | Preserving the order somebody picked in is what makes the value round-trip faithfully. The cost is that a list column showing that answer is not guaranteed to follow the order you authored the options in. |
| LongText lets you type past its 10,000-character cap. | The on-screen counter turns red, but there is no pre-submit block the way MultiSelect blocks a twentieth selection. The refusal comes from the save. |
| A Currency code is checked for shape only. | There is no authoritative list of real currency codes in the product to check against, and a workspace may legitimately need any of roughly 180 real ones. Three uppercase letters is therefore the whole check, and a well-formed but non-existent code such as ZZZ is accepted. |
| Currency stores a plain amount, never minor units. | It follows the same convention as every other monetary amount in the product. 100.50 is stored as 100.50, never as 10050 — which matters if you ever read the raw data or build a report on it. |
| Duration's unit is always minutes, and it has no maximum. | Minutes is the convention the scheduling and booking parts of the product already use for duration-shaped data, and the form labels the unit visibly rather than leaving a bare number. Only negative values are refused; there is no upper bound and no per-field way to set one. |
| An untouched Rating field shows its slider at 1 while still being empty. | A slider always needs a real number to position its thumb. Nothing is submitted until somebody actually moves it, so the field genuinely saves as empty — but it looks like a 1 until you know that. |
| A Rating of 0 is refused rather than treated as unrated. | Unrated means the field was left genuinely empty. An explicitly submitted 0 is a real value that fails the 1-to-5 check exactly as a 6 would, and it gets the same message. |
| Phone validates shape, not whether the number could exist. | The server checks the international grammar only. The form's own picker additionally checks the digits against the selected country's real numbering plan, so the gap is only reachable from a request that bypasses the form — an accepted data-quality limitation rather than a security one. |
| Phone's displayed country flag can be wrong on a shared calling code. | Some calling codes are shared by several countries, and there is no separate country column — the flag is derived from the number itself. The stored number is unaffected; only the flag beside it can pick the wrong country within a shared code. |
| Color never unifies the three-digit and six-digit forms. | Both are valid and both persist exactly as submitted, so the same colour can be stored two ways across different records. Only case is normalised, always to lower case. |
| Time is stored as canonical text rather than as a database time. | A deliberate storage choice, made to avoid repeating a known sorting problem that an existing time column elsewhere in the product has on one database. Unpadded input is accepted and normalised, so two spellings of the same time always converge. |
| Percent stores the number you would say aloud, not a fraction. | 25 is stored as 25 and displayed as 25%. It is never 0.25, and the display appends the sign rather than running a fraction-based formatter, specifically so a 25 can never be shown as 2500%. |
| Text does not trim surrounding spaces; Select does. | A Text value is stored exactly as submitted, because leading or trailing space can be meaningful in free text. A Select value is trimmed on both sides before being matched against the options, so a stray space never causes a spurious rejection. |
| Long Arabic text can be refused below the stated character cap on one database. | The 4,000-character Text cap is an exact character count on two of the three supported databases. On the third it is counted in bytes, so multi-byte text — Arabic included — can reach the limit sooner. Use LongText if you are close to the boundary. |
Reference behaviours
| Behaviour | Why |
|---|---|
| A reference never stores the name of the record it points at. | A stored name would sit inside the record holding the field, and would therefore be readable by anybody who can read that record — while the name itself is guarded by the target's own permission. There is no setting to turn this on, and there will not be one. The compensating benefit is that a name corrected on its own record is corrected everywhere it is referenced, immediately. |
| The identity of the referenced record is opaque and must be round-tripped unchanged. | It is another module's key, encrypted for the wire, and nothing about it is meant to be read or reshaped. One altered character and the product correctly reports the stored reference as malformed. Send back exactly the string you received. |
| A reference is written under the same two names it is read under. | There is no asymmetry between the read shape and the write shape. Anyone integrating against the values API should echo the two property names they were given; inventing a different name for the identity on the way in produces a save carrying no pointer at all, which is then refused as an incomplete reference. |
| A reference that will not display says which of five things happened. | No permission, record gone, malformed value, a lookup that failed just now, and a kind of record this installation cannot answer for are five different problems with five different remedies. Rendering them all as one blank field is what leaves a pointer at a deleted record unnoticed for a year. |
| "Deleted" and "in a workspace you cannot see" are one answer. | Telling them apart would let somebody test identities one at a time to discover what exists in another workspace. "You may not view this kind of record" is told apart from both, because it describes the reader's own access and reveals nothing. |
| Deleting a referenced record clears every pointer at it and keeps every value row. | Both pieces of each affected answer are cleared together, never one without the other. Nothing is deleted: the answer keeps its row, its version and its audit trail, so the field afterwards reads as genuinely empty rather than as broken. |
| Nothing lists the references pointing at a given record. | There is no "what points at this?" view anywhere, and deleting a record does not warn you how many pointers it is about to clear. The clearing is silent because it is safe, not because it is hidden. |
| Only three kinds of record can currently be referenced. | Staff members, user accounts and party people — the kinds whose owning module supplies a searchable, permission-checked list. Anything else is refused rather than answered with an empty list, because an empty list looks like a correct result and would say "there are none of these" when the truth is "this cannot be asked". |
| Administrator records cannot be referenced at all. | An administrator may belong to no workspace — a platform administrator has none — so a pointer at one could reach past every workspace boundary in the product. A User Reference field refuses one outright, and the definition form never offers one. |
| Leaving a reference field unpinned is a permanent, supported state. | It means "any kind this person may reference", and each answer records which kind it chose. It must never be read as "nothing configured, therefore nothing valid" — the record form handles it by asking for the kind of record first and the record second. |
| A populated unpinned field offers no way to change the kind of record. | The stored answer's own kind is used for the picker, so re-picking is confined to that kind. Clearing the field brings the type control back. A real limit rather than a defect, and the shape of this feature most likely to be reported as one. |
| A pinned target type is not in the definitions export. | The spreadsheet has 18 columns and none of them is the target type, so an exported definition does not record what its field points at. |
| A reference field holds exactly one pointer. | There is no multi-value reference type. Two answers means two fields, and MultiSelect cannot be pointed at records — its answers are text you authored. |
Option behaviours
| Behaviour | Why |
|---|---|
| The English option text is the stored answer. | There is no separate code behind an option, so renaming one changes what every existing record displays. Prefer adding a new option and retiring the old one when the distinction matters. |
| Option matching is exact and case-sensitive. | Two options differing only by case are a legitimately distinct pair, and folding case would make them collide. Both sides are trimmed first, so only case and content matter. |
| The Arabic option label is display only. | The two label lists are matched row by row, and the English one is what is written to the record and validated against. An Arabic reader sees Arabic on the way in and on the way out; the data underneath stays one consistent value. |
| An attached Option Set does not automatically follow its own later edits. | A Countries Option Set can be defined once and attached to several fields, which is the way to avoid writing the same list three times. What it does not do is propagate a later edit to that Option Set onto fields that already attached it — each attachment has to be refreshed on purpose. |
Field-group behaviours
| Behaviour | Why |
|---|---|
| A group's stable key can never be changed, by anybody. | Exported schema names a group by this key, so a rename would silently turn a future re-import from an update into a create, against a bundle already shipped. A wrong key means recreating the group. |
| Reordering refuses more than 100 groups on one record type. | A reorder request carries the whole set at once. Past 100, no group on that record type can be moved at all — the screen says so rather than failing generically. |
| A workspace cannot position its group relative to a global one. | Reordering is all-or-nothing and refuses any group the caller does not own, so a workspace's own groups are renumbered from zero. Those numbers can collide with a global group's, the tie is broken on the English label, and the visible effect is that moving your group to the top can leave it below a global one. |
| Field groups need their own permissions. | They are gated separately from field definitions, including a distinct permission for reordering. A role holding every custom-fields permission does not get them automatically, and without them the link and the picker are simply absent. |
| A group belongs to exactly one record type. | Nothing is listed until you pick a record type, and changing a field's record type clears its group, because a group from one type is never valid for another. |
| In an upgraded database, stable-key uniqueness rests on the application check. | Groups that existed before stable keys carry an empty key until a backfill is run, and the database-level uniqueness constraint stays switched off until that has happened everywhere — it would otherwise reject the second of those empty keys. |
Security and classification behaviours
| Behaviour | Why |
|---|---|
| Sensitivity is a label, not an access control. | It is stored, round-tripped and reportable, and it changes nothing about who can read a value. Field-level security is the mechanism that restricts access, and the two are unconnected. |
| Restrictions are keyed by permission resource, not by record type. | It is the same resource that already guards the record itself, so one restricted-fields list covers a screen's built-in fields and its custom fields alike. Names are matched without regard to case. |
| A restricted field is absent, not blank. | Showing a placeholder would reveal that a value exists, which is itself information. The consequence is that a restricted field is indistinguishable from one that was never defined — worth remembering when somebody reports a missing field. |
| Writing a restricted field refuses the entire save. | Quietly dropping the one field and reporting success is the harder failure to notice. The refusal also fires when the submitted value equals the stored one, so nobody can probe a hidden value by testing what is accepted. |
| Required and restricted cannot be combined. | Somebody who cannot see a field could never satisfy it, so the record would be unsaveable for them. Both directions are refused, whichever you try first, and the message names the field. |
| Definition history never shows value changes. | Including them would make the dialog a readable copy of everybody's field data, going around field-level security and every other visibility rule at once. The value-bearing records are excluded by name rather than by omission. |
Export and portability behaviours
| Behaviour | Why |
|---|---|
| The spreadsheet export contains definitions, never answers. | It is a definitions export by design — a separate values export exists as its own endpoint and header button for the answers themselves, capped at 10,000 cells rather than truncated past it. |
| Past 10,000 definitions the export refuses instead of truncating. | A silently truncated file is worse than no file, because it looks complete. The refusal tells you to narrow the export to a single record type. |
| Fields restricted from you are absent from the file, not blank. | Field-level security applies to the export exactly as it does on screen, and a blank column would still reveal that the field exists. |
| The spreadsheet export is one-way, and the one bulk-creation path this product has ever offered is switched off. | The exported spreadsheet is a report for reading, not a template that can be re-imported. A JSON schema-bundle import exists — its own dialog, its own endpoint, its own per-group outcome table — but every call to it is refused with a 409 by a deliberate, permanent containment switch, alongside the matching schema export. Bulk field creation is not available through the product today, by that switch's design rather than by omission. |
| Every export cell is written as text. | A label beginning with =, +, - or @ arrives as literal characters rather than as a spreadsheet formula. This is categorical rather than a filter of known cases, so nothing that looks like a calculation can become one. |
Where fields do and do not appear
| Behaviour | Why |
|---|---|
| Some record types have no screen at all. | They are legal targets and are listed last on the definition form with an API only suffix. A field defined against one is reachable through the API and has nowhere in the interface to render. |
| A handful of screens wire their custom fields by hand. | Most screens pick custom fields up automatically. A few whose create and edit interfaces predate that mechanism — among them webhooks, message templates, tenant plans, plugin definitions, leads and themes — implement the same Custom Fields section themselves. Behaviour should be identical; if it is not, that is worth reporting. |
| Data subject requests take custom fields on create only. | A submitted request moves through a review workflow rather than being generally editable, so there is no edit form to carry custom fields into. That is by design, not an omission. |
| Most record create and edit forms are still dialogs. | Custom-field authoring itself moved out of a nested dialog into a side panel, which is why adding a field from inside a record no longer stacks two dialogs. The surrounding record forms were deliberately left alone — moving them is a much wider change across modules that have nothing to do with custom fields. |
| The Value Types and Entity Types screens have no sidebar entry. | Sidebar navigation is seeded centrally, and these two were deliberately left out of that seed. They are reached from links in the Custom Fields page header instead. |
Things the product does not do
Asked often enough to be worth stating plainly. None of these is a fault to report.
- The twenty-two value types are the complete set. File, Image and RichText are in that set, not missing from it — though attaching a new file or image from a record's own form is not built yet; see their own sections above for exactly what works today. Pointing at another record is the one item that used to be on this list and no longer is: Entity Reference and User Reference do exactly that, and have two pages of their own.
- There is a values export, not just a definitions export — up to 10,000 cells in one file. Larger sets are refused rather than silently truncated.
- There is a bulk-creation path, a JSON schema-bundle import with its own dialog — but it is currently switched off, refusing every call outright rather than actually creating anything, alongside its matching schema export. Today, in practice, fields are still created one at a time on the form.
- There is a shared options list, called an Option Set, that several fields can attach. What it does not do is push a later edit onto fields that already attached it automatically.
- There is no conditional show-or-hide that an administrator can configure. A field is either on the form or it is not, subject to Active and to field-level security.
- There is no calculation, no default value, and no cross-field rule. A custom field records an answer; it does not derive one.