Skip to content

Fields

A workflow's fields live in workspace/<WorkflowName>_<id>/fields.ts, exported as an array of HailerFieldGeneric. pull generates the file (ids replaced with enum references), you edit it, and ws-config fields push applies it via the process.create_field / process.update_field / process.remove_field APIs.

typescript
export const fields: HailerFieldGeneric[] = [
  {
    _id: "60a1...",             // existing field — keep the _id
    key: "customer_email",
    label: "Customer Email",
    type: "text",
    required: true,
  },
  {
    key: "priority",           // new field — no _id → created on push
    label: "Priority",
    type: "textpredefinedoptions",
    data: ["Low", "Medium", "High"],
  },
];

Fields follow the standard sync model: no _id creates, matching _id updates when changed, removing a field deletes it (snapshot-guarded, prompts unless --force).

Field types

type comes from HailerFieldType. It is required when creating a field. On update the push explicitly drops type (const { _id, type, ...fieldData } = field), so it is immutable — changing a field's type means deleting and recreating it, which may require data migration.

typeFieldNotes
textSingle-line text
textareaMulti-line text
textunitText with a unitSet unit.
textpredefinedoptionsDropdown / selectOptions in data: string[].
numericNumber
numericunitNumber with a unitSet unit (e.g. "€", "kg").
dateDate
datetimeDate + time
daterangeDate range
datetimerangeDate + time range
timeTime of day
timerangeTime range
countryCountry picker
usersUser picker
teamsTeam picker
activitylinkLink to activities in other workflowsTarget workflow ids in data.
linkedfromReverse of activitylink — activities linking to this one
subheaderVisual section header, not a data fieldExcluded from generated enums.

Shared properties

PropertyTypeDescription
typeHailerFieldTypeRequired on create, immutable after.
keystring?Stable identifier, unique within the workflow. It's how a field's values are referenced and queried, and it drives the generated name-based enums. Editable — but because things reference it by value, changing a key changes what those references resolve to, so change it deliberately.
labelstring?Display label shown in the UI.
requiredboolean?Whether a value is required.
descriptionstring?Help text.
placeholderstring?Placeholder text.
dataunknown[]?Type-dependent: options for textpredefinedoptions, target workflow ids for activitylink.
unitstring?Unit label for numericunit / textunit.
defaultToboolean?Whether to apply defaultValue.
defaultValueunknown?Default value for new activities.
editableboolean?Whether users can edit the value.
collapsedByDefaultboolean?Collapse the field in the UI by default.
inviteToDiscussionOnChangeboolean?For users fields — invite the selected user to the activity discussion when the value changes.

Read-only — the SDK strips these before hashing/push: uid, created, updated (_id is kept, but only to match the field to its remote counterpart).

key and generated enums

When you generate name-based enums (enums.ts from pull, or hailer-sdk generate without --ids), a field is keyed by its key (falling back to label, then id). Give data fields a stable, unique key so those enums stay meaningful.

Function fields

A field can compute its value from JavaScript. Enable it with functionEnabled, put the code in function, and declare its data dependencies in functionVariables.

typescript
{
  _id: "60b2...",
  key: "total_price",
  label: "Total price",
  type: "numericunit",
  unit: "€",
  functionEnabled: true,
  function: "@function:total_price_60b2",   // reference to a functions/ file (see below)
  functionVariables: {
    qty:   { type: "=", data: ["<qtyFieldId>"] },
    price: { type: "=", data: ["<priceFieldId>"] },
  },
}

functionVariables maps a variable name to a dependency (HailerFieldFunctionVariable):

typeDependencydata contains
=A field on the current activityfield id(s)
>An activity this one is linked tofield/workflow ids
<An activity linked from this onefield/workflow ids
?Workflow / phase static valuesids

The functions/ directory

Function code isn't stored inline. On pull the SDK extracts each field's function into its own file under workspace/<WorkflowName>_<id>/functions/ (plus an index.ts and, first time, a main.test.ts test template), and leaves a @function:<name> reference in field.function. On push it reads the file back and inlines the function body. Edit the code in its functions/*.ts file, not in fields.ts.

Reminders

Fields can trigger reminders relative to a date value.

typescript
{
  _id: "60c3...",
  key: "due_date",
  label: "Due date",
  type: "date",
  reminderEnabled: true,
  reminderSettings: {
    fieldOffset: { milliseconds: 0, days: 1, months: 0, beforeOrAfter: "before" },
    users: ["<userId>"],
    teams: [],
    fields: [],            // notify users selected in these user-fields
    phases: [],            // only in these phases
    activityOwner: true,
    activityOwnerTeam: false,
    activityParticipants: false,
    reminderText: "This activity is due tomorrow",
  },
}
reminderSettings keyTypeDescription
fieldOffset{ milliseconds; days; months; beforeOrAfter: "before" | "after" }When to fire, relative to the field's date.
users / teamsstring[]Explicit recipients.
fieldsstring[]User-field ids whose selected users are notified.
phasesstring[]Restrict to these phases.
activityOwner / activityOwnerTeam / activityParticipantsbooleanInclude these roles as recipients.
reminderTextstringThe reminder message.

Field modifiers

modifier configures advanced field behaviors:

modifier keyTypeDescription
checkboxboolean?Render as a checkbox modifier.
fileboolean?File-related modifier.
matchAllFieldFiltersboolean?Require all activityFieldFilters to match (AND) rather than any (OR).
quickAdd{ targetFieldId: string; fieldIds?: string[] }Quick-add configuration targeting another field.
activityFieldFiltersArray<{ targetWorkflowId; sourceFieldId; targetFieldId }>For activitylink fields — filter selectable activities by matching field values.

Command

ws-config fields push — creates, updates, and deletes fields per workflow. A field change can trigger heavy server work (schema rebuilds, re-evaluating functions across activities), so the SDK waits up to 30 seconds for confirmation per change and then continues in the background; the summary flags any changes still "acknowledgement pending" so you can verify them in the Hailer UI.

Hailer Developer Documentation