universalIdentifier constant for you to import. Instead, the server derives each identifier deterministically, and twenty-sdk exposes the same derivation so your manifest can resolve the exact value the server uses.
System fields
The scalar fields present on every object, none of which you declare withdefineField():
id, createdAt, updatedAt, deletedAt, createdBy, updatedBy, position, searchVector
So how do you reference createdAt as a column in a view?
The problem
Since Twenty 2.19, a system field’s universal identifier is derived deterministically by the server from three inputs: the application universal identifier, the object universal identifier, and the field name. Inventing an id and hardcoding it won’t work: it matches nothing on the server, and the sync rejects the dangling reference:The solution
getFieldUniversalIdentifier is available from twenty-sdk 2.21 onward.getFieldUniversalIdentifier to resolve the exact same value the server uses. It takes the three inputs and returns the field’s universal identifier:
applicationUniversalIdentifieris your app’s identifier, the one you pass todefineApplication().objectUniversalIdentifieris the identifier of the object the field belongs to.nameis the system field name, one of the values listed above.
Example: a createdAt column in a view
The typical case is adding acreatedAt column to a view of one of your custom objects. Resolve the field id and reference it as any other fieldMetadataUniversalIdentifier:
src/views/example-view.ts
fieldMetadataUniversalIdentifier is expected: view fields, filters, sorts, groups, and page-layout widgets.
Resolve the id, don’t hardcode it. Because the server derives the value from
the application id, the object id and the field name, calling
getFieldUniversalIdentifier keeps your reference correct even if those
inputs change, and avoids drift if the derivation ever evolves.System relation fields
getSystemRelationFieldUniversalIdentifier is available from twenty-sdk
2.23 onward and requires a Twenty server on 2.23 or later.timelineActivities, attachments, noteTargets and taskTargets, each pointing at the matching standard relation object.
These fields are not resolved with getFieldUniversalIdentifier: their identifier is derived name-free, from the object hosting the field and the object the field points to. That way renaming an object never changes the identifiers of its relation fields.
Use getSystemRelationFieldUniversalIdentifier to resolve them:
objectUniversalIdentifieris the object hosting the field.relationTargetObjectUniversalIdentifieris the object the field points to.
attachment.targetRocket, the morph field the server creates on the standard relation object), swap the two:
fieldMetadataUniversalIdentifier is expected.
System views
getSystemViewUniversalIdentifier and getSystemViewFieldUniversalIdentifier
are available from twenty-sdk 2.26 onward and require a Twenty server on
2.26 or later.All {objectLabelPlural}, keyed on ViewKey.INDEX), with one column per displayable field. Like system relation fields, their identifiers are derived name-free, so renaming an object or a field never changes them.
Use getSystemViewUniversalIdentifier to resolve the view:
objectMetadataApplicationUniversalIdentifieris the application owning the object, which is what the view is namespaced by.objectUniversalIdentifieris the object the view lists.viewKeyis the system view key,ViewKey.INDEXtoday.
viewUniversalIdentifier is expected, such as a NavigationMenuItemType.VIEW sidebar entry. To simply open an object’s main list, prefer NavigationMenuItemType.OBJECT with targetObjectUniversalIdentifier: it needs no derivation.
getSystemViewFieldUniversalIdentifier resolves a single column on a system view, from the view and the field it displays:
Standard Twenty objects
For a standard Twenty object (Person, Company, Opportunity, …), you don’t need to derive anything: the identifiers are pre-computed constants you can import directly, for both fields and views.defineObject(), where no such constant exists.
name is a default field, not a system field. It keeps its own hardcoded
universal identifier and is not resolved through
getFieldUniversalIdentifier. On objects you define, reference the
name field by the identifier you gave it in defineObject().