Frequently Asked Questions
RehabAlpha is still under active development. It is not yet HIPAA compliant and should only be used with dummy data.
This page answers common questions about how RehabAlpha schemas work.
What is a schema?
A schema is a JSON5-based definition that tells RehabAlpha how to render, evaluate, validate, and persist custom form data.
Schemas control things like:
- which top-level forms are available for a document type
- which fields appear
- how fields are grouped
- which fields show conditionally
- which reusable option lists are shared
- which form loads by default for a given document type
What format are schemas written in?
Schemas are written in JSON5.
JSON5 is similar to JSON, but more forgiving. For example, it allows:
- comments
- unquoted property names
- trailing commas
RehabAlpha also applies a small amount of parser forgiveness for common mistakes, such as:
- missing outer array brackets
- missing commas between top-level object blocks
Even so, it is best to write clean, explicit JSON5 whenever possible.
Where do I edit my organization's schema?
You can edit your schema in:
Organization Settings → Schema Editor
Does a schema have to be wrapped in an array?
Usually yes, but RehabAlpha can sometimes correct this automatically.
For example, if you paste multiple top-level node objects without outer square brackets, RehabAlpha may wrap them for you during parsing.
Still, the recommended format is an explicit array:
;[
{
id: 'history_group',
type: 'group',
label: 'History',
children: ['chief_complaint_input'],
},
{
id: 'chief_complaint_input',
type: 'textInput',
label: 'Chief Complaint',
},
]
Why is the schema written as a flat list of nodes?
A flat list makes schemas easier to:
- read
- reuse
- validate
- rearrange
- reference by ID
Instead of nesting everything inline, RehabAlpha lets you define nodes separately and connect them by id.
Inline child objects are still supported, but the flat style is usually easier to maintain.
What is hydration?
Hydration is the process of taking the flat node list and resolving references into a nested runtime tree.
During hydration, RehabAlpha resolves:
childrenelseChildrenbranches[].childrenfallbackChildrenoptionsreferences tooptionsnodes
For example, a group might reference a child input by ID, and a selectInput might reference a reusable options list by ID. Hydration turns those references into usable runtime structures.
What is dehydration?
Dehydration is the reverse process.
RehabAlpha can take a hydrated tree and flatten it back into raw nodes, replacing child objects with their string IDs.
This is useful for serialization, editing workflows, and schema generation utilities.
Which node types are supported?
RehabAlpha currently supports these node types:
Structure and logic nodes
templategroupconditionalswitchmultiSwitch
Data and metadata nodes
textoptions
Input nodes
textInputtextAreaInputdateInputtimeInputnumberInputcheckboxInputselectInputmultiSelectInput
Which nodes are rendered directly?
These node types are rendered directly or influence the rendered tree:
templategroupconditionalswitchmultiSwitchtext- all input node types
These node types are not rendered directly:
options
An options node exists only to provide reusable option data.
What is a template?
A template is the root node of a form.
It defines:
- the top-level label
- which document types it applies to
- its default-selection priority
- the starting point for the tree beneath it
A template is a root node. It is not meant to be nested under another node.
Can one template apply to more than one document type?
Yes.
The appliesTo property can be:
- a single template type string
- an array of template type strings
Example:
{
id: 'general_function_template',
type: 'template',
label: 'General Function Template',
appliesTo: ['evaluation', 'progressReport'],
priority: 1,
children: ['function_group']
}
How does RehabAlpha choose the default template?
RehabAlpha:
- finds the templates whose
appliesTomatches the current document type - chooses the one with the highest numeric
priority
Important details:
- templates without a numeric
priorityare ignored for default selection - if two templates tie, the earlier one in schema order wins
- if no matching template has a numeric
priority, there is no default template
Does showIf apply to templates?
No.
showIf is used on visible/rendered nodes such as groups, logic nodes, text nodes, options nodes, and input nodes.
Templates are selected by:
appliesTopriority
not by showIf.
What is an options node?
An options node stores a reusable option list for inputs such as:
selectInputmultiSelectInput
This keeps repeated option lists centralized and easier to maintain.
Example:
{
id: 'side_options',
type: 'options',
items: ['Left', 'Right', 'Bilateral']
}
Can I define options inline instead of using an options node?
Yes.
Selection inputs can use:
- an inline array of strings
- an inline array of
{ label, value }objects - a string reference to an
optionsnode
Inline arrays are fine for short, one-off lists. Reusable lists are better when the same options appear in multiple places.
When should I use string options vs object options?
Use string options when:
- the display text and stored value can be the same
- you want something simple and fast
Use object options when:
- reporting consistency matters
- downstream logic matters
- you want a friendly label but a stable stored value
Example:
;[
{ label: 'Minimal Assistance', value: 'min_assist' },
{ label: 'Moderate Assistance', value: 'mod_assist' },
]
What selection styles are supported?
selectInput and multiSelectInput support these flavor values:
"buttons""badges""list"
In general:
- use
"buttons"for short, obvious option sets - use
"badges"for short visible toggle-style sets - use
"list"for longer or more compact option sets
What does showExplicitNone do?
showExplicitNone is supported on:
selectInputmultiSelectInput
It allows RehabAlpha to distinguish:
- unanswered
- explicitly none
This matters when both states are clinically meaningful.
For persisted values:
selectInput
null= unanswered''= explicitly none
multiSelectInput
null= unanswered[]= explicitly none
In the form layer, RehabAlpha also keeps a separate shadow flag so the UI can preserve explicit-none intent while still using stable control values.
What kinds of conditions are supported?
RehabAlpha supports:
Field comparisons
equalsdoesNotEqualisInisNotInincludesdoesNotIncludelessThanlessThanOrEqualTogreaterThangreaterThanOrEqualTo
Grouped conditions
allanynotAllnone
What is the difference between isIn and includes?
Use isIn when the field value is not an array and you want to compare it against a list of possible matches.
Example:
{ field: 'discipline_choice', isIn: ['PT', 'OT'] }
Use includes when the field value is an array and you want to see whether it contains a specific value.
Example:
{ field: 'symptoms_list', includes: 'Dizziness' }
How do comparison operators behave?
For these operators:
lessThanlessThanOrEqualTogreaterThangreaterThanOrEqualTo
RehabAlpha uses strict scalar comparison rules.
Important details:
- both sides must be comparable scalars
- number-to-number works
- string-to-string works
- mixed-type comparisons are rejected
undefined,null, and empty strings do not satisfy positive comparisons
This avoids JavaScript coercion surprises.
How do missing values affect conditions?
For positive operators such as:
equalsisInincludes- comparison operators
a missing (undefined) value usually causes the condition to fail.
For negative operators such as:
doesNotEqualisNotIndoesNotInclude
a missing (undefined) value usually causes the condition to pass.
This lets negative conditions behave naturally for unanswered fields.
What are special context variables?
Special context variables are built-in values that RehabAlpha injects into the condition evaluation context.
They begin with *.
Supported values are:
*disciplineId*facilityType*templateType*payorTypes
These let you show or hide fields based on system context, not just user-entered form values.
Can I use special context variables inside conditions?
Yes.
Example:
{
id: 'pt_only_logic',
type: 'conditional',
when: { field: '*disciplineId', equals: 'PT' },
children: ['gait_group']
}
Example using an array-aware condition:
{
id: 'medicare_logic',
type: 'conditional',
when: { field: '*payorTypes', includes: 'medicarePartA' },
children: ['pdpm_group']
}
What happens if a reference points to an ID that does not exist?
Validation fails.
For example, these situations are invalid:
childrenreferences a missing node IDelseChildrenreferences a missing node IDbranches[].childrenreferences a missing node IDfallbackChildrenreferences a missing node IDoptionsreferences a missing node IDoptionspoints to a node that is not anoptionsnode
Can branch nodes contain inline child objects instead of string IDs?
Yes.
Nodes such as:
templategroupconditionalswitchmultiSwitch
may contain nested child objects directly.
However, the recommended style is still to define nodes separately and reference them by ID, because that makes schemas easier to maintain and debug.
Can two nodes share the same ID?
No.
Every node ID must be unique across the entire schema.
What are the ID rules?
IDs:
- cannot be empty
- must be 100 characters or fewer
- may contain only letters, numbers, underscores, and dashes
- cannot start and end with double underscores
Valid examples:
pain_level
left_knee_rom
pt_eval_template
Invalid examples:
''
pain level
pain.level
__reserved__
Can I create circular references?
Not for child references.
RehabAlpha rejects circular child-reference structures during hydration.
That includes loops involving:
childrenelseChildrenbranches[].childrenfallbackChildren
However, RehabAlpha does not statically prove that all condition logic is cycle-free. Instead, visible-default generation is bounded by a configured pass limit so it can stabilize safely.
What is an input collision?
An input collision happens when the same input ID would render more than once in the same visible tree.
That is not allowed.
Important detail:
- this is checked against the actually visible rendered tree
- hidden nodes do not count
- the result depends on the current values and current special variables
Why would I use isRequiredToSave vs isRequiredToSign?
Use:
isRequiredToSavewhen the field must be completed before the document can be savedisRequiredToSignwhen the field must be completed before the document can be signed
A field may use one, both, or neither, depending on your workflow.
Are date and time values stored as Date objects?
No.
In schemas, date and time input values are stored as strings.
Examples:
dateInputstores strings likeYYYY-MM-DDtimeInputstores strings likeHH:mm
How are default form values generated?
When a template is selected, RehabAlpha generates defaults only for inputs that are currently visible after applying:
showIfconditionalswitchmultiSwitch
The default persisted values are:
textInput→''textAreaInput→''dateInput→''timeInput→''numberInput→nullcheckboxInput→falseselectInput→nullmultiSelectInput→null
If a node explicitly defines defaultValue, that value is used instead.
Are form values stored exactly the same way as persisted values?
Not always.
For selection fields, the UI form state is normalized into control-friendly shapes:
selectInputform values use stringsmultiSelectInputform values use arrays
Then, on submit, RehabAlpha combines:
- the form value
- the explicit-none shadow flag
- the node definition
to reconstruct the effective persisted value.
This is how RehabAlpha can distinguish:
- unanswered
- explicitly none
- real selected values
without making the UI state awkward.
What happens to hidden fields when data is pruned?
RehabAlpha can prune custom data so that only visible, meaningful inputs are retained.
That means:
- fields hidden by failed
showIfconditions are excluded - fields hidden by untaken logic branches are excluded
undefinedvalues are excludednullvalues are excluded- empty strings are excluded
- empty arrays are excluded
Explicit-none values are preserved for eligible selection fields.
This helps keep stored form data smaller and cleaner while preserving intentional clinician input.
How does the server verify submitted custom data?
When RehabAlpha receives submitted custom data, it validates that payload against the schema.
The server verifies that:
- the schema document exists
- the selected template exists in that schema
- every submitted key belongs to a real input reachable from that selected template
If a submitted key does not belong to an input inside the selected tree, the request is rejected.
This prevents stale, stray, or malicious custom-data keys from being accepted.
Is there a maximum schema size or nesting depth?
Yes.
RehabAlpha enforces configured limits for things like:
- hydration depth
- rendered depth
- visible-default passes
- option-list size
The exact limits come from application constants and may change over time, so the safest approach is still to keep schemas readable, shallow, and modular.
What is the best way to start learning schemas?
A good path is:
- read the overview page
- inspect the default schema provisioned for a new organization
- make one small change at a time
- reuse
optionsnodes where possible - use clear naming conventions for IDs
Where should I go next?
After this page, the most helpful articles are:
- Schemas Overview
- Writing Your Own Schema
- Writing Schemas with AI
- Tips & Tricks
- Test Your Knowledge
- Schema Library