Skip to main content

Appointment Fields

The ServiceAppointment model defines the essentials every booking needs — service, customer, time, duration, price, and status. Appointment fields capture everything beyond that: the business-specific detail that makes a booking useful but that no fixed model could anticipate. A salon needs a preferred stylist and allergy notes; a clinic needs a reason for visit and insurance details; a consultancy needs a project brief.

Rather than forcing every business into one rigid form, appointment fields let you declare your own data model for appointments — typed fields, validation, grouping, and conditional logic — defined once at the organization level and tailored per service. When a customer books through an AI voice or chat agent, the agent reads these definitions and guides the customer through providing exactly the right detail, validating answers as it goes.

Declarative configuration

Appointment fields are defined declaratively as configuration (the schemas below) and applied at two scopes: organization-wide and per-service. The structures on this page describe that configuration and the data captured against it.

The three-level hierarchy

Configuration flows downward, data flows back up. The organization defines a reusable library; each service selects, overrides, and extends it; each appointment captures the resulting values.

Appointment fields hierarchyAn organization field library (AppointmentFieldConfig) is inherited by service-level configuration (requiredDatafieldConfig), which is rendered for per-appointment capture (AppointmentAdditionalInfo). An AI agent reads the configuration and guides the customer through providing values.Organization field libraryAppointmentFieldConfig — reusable fields & groups, defined onceinherited byService-level configurationrequiredDatafieldConfig — inherit · override · add per servicerendered forPer-appointment captureAppointmentAdditionalInfo — typed values in data{}AI agentreads theconfig,guides thecustomer &validateseach answer

This architecture gives you reusable field definitions at the organization level, service-specific customization of which fields appear, and per-appointment data capture linked to the customer for reuse on future visits.

Organization field library

AppointmentFieldConfig is the organization-wide library of fields and groups that services draw from. Define a field once here and any service can inherit it.

FieldTypeRequiredDescription
idstringYesUnique identifier
fieldsFieldDefinition[]YesField definitions (default: [])
groupsFieldGroup[]YesField groupings for form sections (default: [])
reuseDetailsbooleanYesPre-fill from a returning customer's last answers (default: false)
ensureEmailbooleanYesAlways include an email field (default: false)
ensurePhonebooleanYesAlways include a phone field (default: false)
supportServiceenum | nullNoWhich platform service this config applies to

supportService

Scopes a field library to one platform service, so appointment booking and, say, order capture can each have their own fields.

ValueApplies to
appointment_managementService appointments
menu_order_managementMenu orders
product_order_managementProduct orders
reservation_managementReservations
property_managementProperty inquiries
none / nullNot scoped to a specific service

FieldDefinition

Each field declares its key, type, label, and optional validation, conditions, and UI hints.

PropertyTypeRequiredDescription
fieldKeystringYesStable key; pattern ^[a-z][a-z0-9_]*$ (used as the data key)
fieldTypeenumYesOne of the field types below
labelstringYesHuman-readable label (min length 1)
descriptionstringNoHelper description
groupKeystringNoGroup this field belongs to
optionsobject[]NoChoices for select / multiselect (value, label, displayOrder)
defaultValueanyNoDefault value
isActivebooleanNoWhether the field is shown
conditionobjectNoShow the field only when another field's value matches (see below)
validationobjectNoValidation rules (see below)
uiHintsobjectNoPresentation hints (see below)

Field types

text, textarea, number, boolean, date, time, datetime, email, phone, select, multiselect.

Validation

RuleTypeDescription
requiredbooleanField must be answered
minLength / maxLengthinteger (> 0)Length bounds for text values
min / maxnumberNumeric bounds
patternstringRegex the value must match
patternMessagestringMessage shown when pattern fails

Conditional display

A field can depend on another field's value, so the agent only asks when relevant.

PropertyTypeDescription
dependsOnstringfieldKey of the controlling field
operatorenumequals, notEquals, contains, isEmpty, isNotEmpty, greaterThan, lessThan
valueanyValue to compare against

UI hints

PropertyTypeDescription
placeholderstringPlaceholder text
helpTextstringInline help
displayOrderintegerOrder within the group
widthenumfull, half, or third

FieldGroup

Groups organize fields into labeled, optionally collapsible sections.

FieldRequiredDescription
groupKeyYesStable group key referenced by fields
labelYesSection label
descriptionNoSection description
displayOrderNoSection order
collapsibleNoWhether the section can collapse
defaultCollapsedNoWhether it starts collapsed

Example

{
"id": "afc_salon_booking",
"fields": [
{
"fieldKey": "preferred_stylist",
"fieldType": "select",
"label": "Preferred Stylist",
"groupKey": "preferences",
"options": [
{ "value": "no_preference", "label": "No Preference", "displayOrder": 0 },
{ "value": "jane", "label": "Jane Smith", "displayOrder": 1 },
{ "value": "john", "label": "John Doe", "displayOrder": 2 }
],
"uiHints": { "displayOrder": 1 }
},
{
"fieldKey": "special_requests",
"fieldType": "textarea",
"label": "Special Requests",
"groupKey": "preferences",
"uiHints": {
"placeholder": "Any allergies, preferences, or special requests?",
"displayOrder": 2
}
},
{
"fieldKey": "referral_source",
"fieldType": "select",
"label": "How did you hear about us?",
"groupKey": "marketing",
"options": [
{ "value": "google", "label": "Google Search" },
{ "value": "social", "label": "Social Media" },
{ "value": "friend", "label": "Friend/Family" },
{ "value": "other", "label": "Other" }
],
"validation": { "required": true },
"uiHints": { "displayOrder": 1 }
}
],
"groups": [
{
"groupKey": "preferences",
"label": "Your Preferences",
"description": "Help us personalize your experience",
"displayOrder": 1,
"collapsible": false
},
{
"groupKey": "marketing",
"label": "About You",
"displayOrder": 2,
"collapsible": true,
"defaultCollapsed": false
}
],
"reuseDetails": true,
"ensureEmail": true,
"ensurePhone": true,
"supportService": "appointment_management"
}

Service-level overrides

Most services don't need every field in the library, and some need their own. A service tailors the library through its requiredDatafieldConfig — a ServiceAppointmentFieldConfig embedded on the BusinessServiceConfig. It inherits a subset of org fields, overrides individual properties, and adds service-only fields.

FieldTypeDefaultDescription
inheritedFieldKeysstring[][]fieldKey values pulled in from the org library
fieldOverridesFieldDefinition[][]Per-field overrides (e.g. relabel, make required)
additionalFieldsFieldDefinition[][]Fields unique to this service
isActivebooleantrueWhether this service config is active
reuseDetailsbooleanfalseReuse the customer's previous values for this service

Example

A haircut service inherits two org fields, makes one of them required with a friendlier label, and adds a hair-length question only it needs:

{
"inheritedFieldKeys": ["special_requests", "referral_source"],
"fieldOverrides": [
{
"fieldKey": "special_requests",
"fieldType": "textarea",
"label": "Notes for your stylist",
"validation": { "required": true }
}
],
"additionalFields": [
{
"fieldKey": "hair_length",
"fieldType": "select",
"label": "Current Hair Length",
"options": [
{ "value": "short", "label": "Short" },
{ "value": "medium", "label": "Medium" },
{ "value": "long", "label": "Long" }
],
"validation": { "required": true }
}
],
"isActive": true,
"reuseDetails": true
}
Set it on the service

requiredDatafieldConfig is a field on the service itself. See Services & Categories for where it sits in BusinessServiceConfig, alongside duration, bookingRules, and pricing.

Per-appointment capture

When a booking is made, the answered values are stored as an AppointmentAdditionalInfo record — one per appointment.

FieldTypeRequiredDescription
idstringYesUnique identifier
businessServiceIdstringYesReference to the service
appointmentIdstringYesReference to the appointment (1:1)
customerIdstringYesReference to the customer
dataobjectYesKey-value store of captured field values

How values are typed

In data, each key is a field's fieldKey and each value is typed by that field's fieldType:

Field typeValue in data
text, textarea, email, phonestring
numbernumber
booleanboolean
datestring — YYYY-MM-DD
timestring — HH:MM (24-hour)
datetimestring — YYYY-MM-DDTHH:MM:SS
selectstring (the selected value)
multiselectstring[] (selected values)

Example

{
"id": "aai_booking_12345",
"businessServiceId": "svc_haircut",
"appointmentId": "apt_12345",
"customerId": "cust_jane",
"data": {
"preferred_stylist": "jane",
"special_requests": "Please use hypoallergenic products",
"referral_source": "friend",
"hair_length": "medium"
}
}

How AI agents use these fields

Because the fields are declared with types, validation, and conditions, an AI voice or chat agent can run the whole capture conversationally — no static form required:

  • Guides the customer — the agent asks for each active field in displayOrder, using the label, description, and uiHints.helpText to phrase natural questions.
  • Validates in the moment — answers are checked against validation (required, length, numeric bounds, pattern), and the agent re-asks with patternMessage when something doesn't fit.
  • Skips irrelevant questionscondition rules mean the agent only asks a field when its dependsOn value matches, keeping the conversation short.
  • Reuses what it knows — when reuseDetails is true, the agent pre-fills from the customer's most recent AppointmentAdditionalInfo for that service and simply confirms rather than re-asking.
  • Captures structured data — every answer lands in data as a typed value, ready for your systems, staff, and reporting.

Query options

Both resources support filtering and sorting.

ResourceFiltersSort fields
AppointmentFieldConfigreuseDetails, ensureEmail, ensurePhonecreatedAt, updatedAt (asc / desc)
AppointmentAdditionalInfobusinessServiceId, appointmentId, customerIdcreatedAt, updatedAt (asc / desc)

Usage patterns

  • Build the form (or conversation) — fetch the org AppointmentFieldConfig, apply the service's requiredDatafieldConfig to resolve the final field set, then render by fieldType and group, or let the agent drive it.
  • Capture values — validate answers against the field definitions, then store an AppointmentAdditionalInfo linked by appointmentId and customerId.
  • Reuse data — when reuseDetails is true, look up the customer's most recent entry for the same service and pre-fill, so returning customers only confirm.