A type defines one object model in Jetstack. It is the top-level builder object that owns a property set, controls baseline object rendering, and acts as the main contract for how records of that kind behave across forms, detail pages, queries, permissions, canvases, and navigation.
Once a type exists and other platform features start depending on it, several decisions become expensive to change:
In practice, the type is where the platform-level design of a business object starts.
This chapter follows the same form groups the Type builder uses:
For each option, this page explains:
Use this chapter together with:
Keep these distinctions clear when configuring a type:
type_name and type_name_plural are user-facing labels.system_name is the stable implementation identifier.role tells Jetstack what kind of type this is conceptually.object_properties defines the schema surface of the type.viewer, form variants, and canvas slots define how the type is presented and interacted with.acl_enabled, is_cached, and is_active are runtime behavior flags, not cosmetic settings.These fields define the identity, structure, and main platform behavior of the type.
| Option | What it controls | How to configure it | Notes |
|---|---|---|---|
id |
Stable internal identifier of the type record. | You normally do not configure this manually. It is assigned by the platform. | Useful for troubleshooting, exports, sync, and internal references. |
role |
Conceptual role of the type in the model. | Choose one of the built-in values: Entity, Enumeration, or Checklist. |
Entity is the normal business-object role. Enumeration is for managed vocabularies. Checklist is for simpler controlled lists. |
system_name |
Stable machine-facing identifier for the type. | Use only English letters, digits, and underscores. The validation pattern is [a-zA-Z0-9][a-zA-Z0-9_]*, so the name must start with a letter or digit and cannot contain spaces. |
Treat this as a durable implementation contract. Prefer names like invoice, contract, or project_task. |
type_name |
Main singular business-facing name of the type. | Enter the singular name users should recognize, such as Invoice, Contact, or Asset. |
This field is required on create and edit. It also has an onchange helper that prefills type_name_plural with the same value during editing. |
type_name_plural |
Plural user-facing name of the type. | Enter the plural name used in lists and navigation, such as Invoices, Contacts, or Assets. |
This field is required. Review the auto-prefilled value because correct pluralization often needs manual adjustment. |
icon |
Main icon associated with the type. | Choose an icon that is meaningful in navigation and object headers. | This is a semantic UI choice. Reuse icon conventions across similar domains. |
object_properties |
The property collection owned by the type. | Add, remove, and order the properties that define the schema of this type. | This is one of the most important fields on the type. It defines the data contract and affects forms, queries, views, permissions, and automations. |
parent_module |
Module under which the type appears in navigation and organization. | Select the module that should own this type in the implementation hierarchy. | Read Modules. This affects discoverability and tenant structure more than data semantics. |
description |
Human-readable description of the type. | Enter a concise explanation of what the type represents and how it should be used. | Use this to document business purpose, not source-code detail. |
is_cached |
Whether objects of this type should be cached locally until changed. | Enable only when the type's records are safe to reuse between reads without always reloading from the database. | Use with care. The field description explicitly says cached types are reused locally until changed. |
acl_enabled |
Whether ACL rules are enforced for the type. | Enable when access to this type must be constrained by roles, permissions, or type-level rules. | Default is enabled. It is best to decide this early because turning ACL on late affects views, automations, APIs, and user expectations. |
is_active |
Whether the type is active in the current implementation. | Leave enabled for types that should participate in the active tenant experience. Disable only when the type should exist in the model but not be actively used. | This defaults to an active state when creating a new type. |
roleThe role is more than a label. It affects how implementers and users think about the type.
Entity: use for ordinary business objects with full lifecycle, such as Invoice, Opportunity, or Task.Enumeration: use when the records represent a managed value vocabulary, such as Priority, Department, or Industry.Checklist: use for simple controlled item sets where records behave like ordered checklist entries.Practical implications:
system_nameTreat this as the implementation-safe identifier for:
Good examples:
invoiceproject_taskservice_contractAvoid:
object_propertiesThis field points to the full property set of the type. In runtime terms, it defines:
It is configured as a multi-value relation to Property, with uniqueness enforced and dynamic property creation enabled. In practical builder terms, that means:
Read Properties alongside this section.
These options define the default create and edit form experience for objects of this type.
| Option | What it controls | How to configure it | Notes |
|---|---|---|---|
object_form_variant_add |
Default form variant used when creating objects of this type. | Choose one of: Automatic one page, Custom one page, Tabbed, or Wizard. |
This determines the baseline add experience unless a custom add canvas replaces it. |
object_form_variant_edit |
Default form variant used when editing existing objects of this type. | Choose one of: Automatic one page, Custom one page, Tabbed, or Wizard. |
This determines the baseline edit experience unless a custom edit canvas replaces it. |
Automatic one pageUse when:
This is the most typical baseline.
Custom one pageUse when:
This is usually a better fit when a type has medium complexity and the default automatic layout is no longer expressive enough.
TabbedUse when:
This works especially well with thoughtfully designed form groups.
WizardUse when:
This is a process-oriented choice, not just a layout choice.
These options define the main object-detail presentation and some high-level viewing behavior.
| Option | What it controls | How to configure it | Notes |
|---|---|---|---|
layout_file |
Main layout template file used around the object rendering. | Select the layout file to be used. The default points to the standard @layout.latte template path relative to the project templates directory. |
This is an advanced presentation setting. Change it only when you need a different layout shell for this type's rendering behavior. |
viewer |
Baseline single-object viewer used for the type. | Choose one of the registered single viewers: Form, Post, or Public site. |
The default is the first configured single viewer, which is currently Form. |
object_form_variant_show |
Default show-mode property presentation style. | Choose one of: Simple list, Custom list, Groups on one page, or Groups as tabs. |
This defines how properties are shown when using the standard detail rendering rather than a custom show canvas. |
comments |
Whether comment support is enabled for this type. | Choose Yes or No. |
Use comments only when the type genuinely benefits from conversational or review-style discussion. |
modal_prefers_xl |
Whether the type prefers opening in a large modal. | Enable when forms or object details for this type usually need more space. | This is a UX preference, especially important for dense forms, wide layouts, or richer object detail views. |
layout_fileThis field controls the broader rendering shell around the type's presentation. The default value is the standard shared layout file. You should change it only when:
This is not the first tool to reach for when you only need a different field layout. In most cases, form variants, show variants, form groups, or canvases are the better place to work.
viewerThe viewer determines the overall single-object presentation mode.
Available values:
FormPostPublic siteUse them like this:
Form: best for standard back-office object interaction and structured editing/showing.Post: useful when the object reads more like a content item than a dense data form.Public site: use when the object should be presented with a more public-facing rendering model.This field and the show variant work together:
object_form_variant_showAvailable values:
Simple listCustom listGroups on one pageGroups as tabsUse them like this:
Simple list: good for compact straightforward detail pages.Custom list: use when the standard list format is not enough but you still want a list-oriented presentation.Groups on one page: best when the type has deliberate group structure and users benefit from seeing all sections together.Groups as tabs: best when the type is large and users need section-based navigation on object detail.commentsEnable comments when:
Leave comments off when:
These slots let the type replace standard add, edit, show, delete, or not-found behavior with custom canvases.
| Option | What it controls | How to configure it | Notes |
|---|---|---|---|
object_canvas_add |
Custom canvas used when creating an object of this type. | Select a canvas when the standard add form should be replaced or heavily customized. | Use this only when form variants and form design are not enough. |
object_canvas_edit |
Custom canvas used when editing an object of this type. | Select a canvas when edit behavior needs a custom interaction model or layout. | This is often useful for process-heavy edit screens. |
object_canvas_delete |
Custom canvas used for delete handling. | Select a canvas when deletion needs a richer confirmation or contextual workflow. | Most types do not need this unless delete behavior is business-sensitive. |
object_canvas_show |
Custom canvas used when showing object details. | Select a canvas when the standard show rendering is not expressive enough. | This is a common way to build highly tailored detail pages. |
object_canvas_not_found |
Custom canvas used when the requested object is not found. | Select a canvas when missing-object handling should be customized. | This is a specialized UX tool and is usually only needed in advanced implementations. |
Use custom canvases when:
Do not use a custom canvas just because:
In those cases, form groups, show variants, and property-level configuration are usually better tools.
Read Canvases.
These options opt the type in to the Event Log. When a flag is enabled, the platform automatically records an entry into the ledger every time an object of this type is created, read, updated, deleted, or restored from the Trash — without any automation or code.
Lifecycle logging is off by default on every type. It is deliberately an opt-in per type and per operation, because the log is meant to be a curated stream of meaningful activity, not a blanket write-ahead trace.
| Option | What it controls | How to configure it | Notes |
|---|---|---|---|
log_create_enabled |
Whether an Object Created event is written whenever an object of this type is successfully created. |
Enable on types whose creation is governance-relevant. | Default off. |
log_create_message |
Custom content stored on the creation event. | Enter free-form text. Shortcodes and expressions are evaluated against the newly created object. Leave empty to use a platform default. | Visible only when creation logging is enabled. Cross-link: Expressions. |
log_read_enabled |
Whether an Object Accessed event is written whenever an object of this type is opened on an operator-visible surface. |
Enable only when read traceability is explicitly required. | Default off. Fires on the object detail page, on a REST single-resource fetch, and on an MCP single-object retrieval — not on list views or on internal resolver calls. |
log_read_message |
Custom content stored on the access event. | Shortcodes/expressions evaluate against the accessed object. Leave empty for the platform default. | Visible only when read logging is enabled. |
log_update_enabled |
Whether an Object Updated event is written whenever a change to an object of this type is actually persisted. |
Enable on types whose changes should be visible in the operator log. | Default off. The event fires only when at least one property value actually changed. |
log_update_message |
Custom content stored on the update event. | Shortcodes/expressions evaluate against the updated object (post-change state). Leave empty for the platform default. | Visible only when update logging is enabled. |
log_delete_enabled |
Whether an Object Deleted and Object Restored event is written whenever an object of this type is deleted or restored from the Trash. |
Enable on types where deletions are sensitive. | Default off. Deletion and restoration are two halves of one lifecycle, so a single flag controls both. The object state is captured before deletion so expressions can still resolve, including under hard-delete. |
log_delete_message |
Custom content stored on the deletion and restoration events. | Shortcodes/expressions evaluate against the object. Used for both deletion and restoration — phrase it neutrally, or leave empty for the platform default (which distinguishes "has been deleted" from "has been restored" automatically). | Visible only when the delete flag is enabled. |
When the message field is empty, the platform writes a neutral default of the form:
Object of type
<type name>(type ID<N>) with ID<M>and system name "<system name>" has been created / accessed / updated / deleted.
This is designed to be readable in the operator surface without configuration. Use it when the identifier and operation name are enough. Provide a custom message when the event needs domain-specific phrasing, extra context from object properties, or a translation key for localization.
Custom message fields are passed through the same expression layer used elsewhere in the platform. This means property shortcodes resolve against the object in scope:
Shortcode syntax and available functions are documented in Expressions. This is how a tenant implementer phrases a custom entry like "Invoice {$this.invoice_number} for {$this.customer.type_name} was archived." without writing any automation.
If the message begins with __, the log treats it as a translation key. See Event Log → Title And Content.
Read logging fires only on operator-visible single-object retrievals. Specifically:
GET /<type>/<id>) or by property value (get-by)get operation on a type's MCP toolset)Read logging deliberately does not fire on:
ajax-background side-tasks such as recreateTable or readaptTable)This narrow scope keeps the ledger meaningful: one event per deliberate "show me this record" interaction, not one per framework-internal lookup or partial redraw.
Even with the narrow scope, read logging is still the noisier of the four lifecycle flags — a single user browsing through a handful of records can produce a visible stream of access events. Enable it when read traceability is itself a governance requirement:
Leave it off for everything else.
Update events are only written when at least one property value actually changed as a result of the update. Calls that resolve to no-op updates do not enter the ledger, so operators are not shown empty "updated but nothing changed" noise.
Before the delete takes effect, the platform captures the object instance so that expressions in the custom deletion message can still resolve against it. This holds even when the tenant is configured for hard delete (objectsRealDelete) and the row is physically removed — the event content is still populated correctly.
There is no separate flag for restoration. log_delete_enabled controls both Object Deleted and Object Restored events, because the two are halves of one lifecycle: a tenant that cares about tracking deletions also cares about tracking restorations. Logging one without the other would leave the governance trail asymmetric.
Restore events fire when a user explicitly restores a soft-deleted record from the Trash surface. There is no automatic or internal "restore" in the platform. If the tenant is configured for hard delete, soft-deleted rows do not exist in the first place and restore events therefore cannot occur for that type.
The restore event is emitted after the is_deleted flag has been flipped back, after the search index is refreshed, and after the object cache is invalidated — so the object passed to the custom message reflects the now-live record, not a stale in-memory copy.
The custom message in log_delete_message is reused for both events. The platform's default message distinguishes "has been deleted." from "has been restored." automatically based on the event category, so leaving the message field empty yields correctly-worded defaults for both halves.
log_create_enabled and log_delete_enabled first on types where lifecycle is sensitive. The delete flag automatically covers restoration too, so the ledger narrates both halves of the soft-delete / restore flow out of the box.log_update_enabled only on types where field-level changes are audit-relevant.log_read_enabled only when read traceability is a formal requirement. The scope is narrow (operator-visible single-object retrievals, not internal resolution), but a browsing session can still produce a visible stream of access events.Read Event Log for the operator-facing surface, filtering model, and retention behavior of the resulting events.
Some important type-level structures are not ordinary editable fields in the grouped type form, but they are still part of the type contract.
object_actionsThis is the type-owned list of object actions. It defines which actions can be configured and later shown on objects of the type.
Use this when:
top_navThis is the type-owned top navigation structure.
Use this when:
form_groupsThese are the groups used to organize properties for form and show rendering.
Use this when:
These structures are edited through dedicated type-oriented editors such as:
acl_enabledWhen this is enabled, access to type records should be designed together with:
This is not a small toggle. It affects the security model of the type.
is_cachedWhen enabled, objects of the type are cached locally until changed. This can help when:
It is a poor fit when:
roleThe role influences how the type is expected to be used:
These choices shape how much structure the default runtime gives users before any custom canvas is introduced. Many implementations can go very far using only:
That is usually preferable to jumping straight to canvases.
Use:
role = EntityAutomatic one page or TabbedGroups on one page or Groups as tabsThis is a good baseline for things like invoices, contracts, tickets, or projects.
Use:
role = EnumerationThis works well for status sets, categories, industries, and other controlled reference data.
Use:
role = ChecklistThis is a good fit when the records behave like maintainable list entries rather than full lifecycle objects.
Use:
This is the right pattern for highly tailored record experiences.
Entity, Enumeration, or Checklist?system_name early and avoid renaming it later.role based on business meaning, not just on record count.