JSON format
Your family is saved as a small JSON file. You normally never edit it by hand — the editor writes it — but it is plain, so here is the shape. Field names are camelCase; lengths are in mm and angles in radians.
{
"name": "Door Basic",
"template": "metric-door",
"parameters": [
{ "key": "Width", "type": "length", "group": "dimensions" },
{ "key": "Height", "type": "length", "group": "dimensions" },
{ "key": "Thickness", "type": "length", "group": "dimensions" },
{ "key": "HalfHeight", "type": "length", "group": "constraints",
"formula": "Height / 2" }
],
"types": [
{ "name": "0800 x 2100mm",
"values": { "Width": 800, "Height": 2100, "Thickness": 45 } },
{ "name": "0900 x 2100mm",
"values": { "Width": 900, "Height": 2100, "Thickness": 45 } }
],
"materials": [
{ "key": "Wood - Walnut", "args": { "color": [120, 81, 45], "transparency": 0 } }
],
"subcategories": [
{ "key": "Panel", "args": { "material": "Wood - Walnut" } }
],
"nodes": [
{ "nodeType": "rectangle", "key": "PanelProfile",
"args": { "width": "Width", "height": "Height", "center": [0, "HalfHeight"] } },
{ "nodeType": "extrusion", "key": "DoorPanel", "label": "Door Panel",
"args": { "profile": ["PanelProfile"], "depth": "Thickness", "isSolid": true },
"properties": { "material": "Wood - Walnut", "subcategory": "Panel" } }
]
}Lanes
The root is a set of lanes — one array per kind of Revit content, plus the family's name and template:
| Lane | What it holds |
|---|---|
parameters | The inputs that drive geometry — a flat record per parameter, with no value of its own. See Parameters & coordinates. |
types | The named size variants, and the only place parameter values live. Required, at least one. |
materials | Revit materials this family owns — colour and transparency. |
subcategories | Revit subcategories of the family's category (Object Styles), each with an optional default material. |
nodes | The geometry: shapes, forms, curves, arrays, annotations. See Node reference. |
properties | Optional. How Revit treats the family as a whole — see below. |
One key space
Every key — parameter, material, subcategory, node — is unique across all lanes. That is what makes a single reference string unambiguous: a form's material may name a materials-lane key (a fixed material) or a material-type parameter key (the material becomes editable in Revit), and there is never a question which one you meant.
A key is also the Revit name of what it creates: the material key is the material's name in Revit, the subcategory key is the subcategory's name, the parameter key is the parameter's name. label is editor chrome only — a friendlier display name that never reaches Revit's naming.
Keys must be unique, and any parameter key a coordinate references must exist — otherwise generation reports an error.
Parameters are flat; values live on types
A parameter is a flat record that mirrors Revit's own FamilyParameter — key, type, group, instance, an optional formula — and it carries no value. group is one of dimensions, constraints, construction, graphics, materials, identity, text, data, general, structural, electrical, mechanical, plumbing, analysis (see the fields of a parameter). That is Revit's rule, not ours: in Revit a value belongs to a family type, which is why types is required and must hold at least one entry.
{ "key": "Width", "type": "length", "group": "dimensions", "instance": true }
{ "key": "HalfHeight", "type": "length", "group": "constraints", "formula": "Height / 2" }A parameter is driven either by a formula or by the per-type values — never both. Every parameter without a formula gets a value in every type; a formula parameter never appears in values, because Revit computes it.
args vs properties
Nodes have two tiers:
args— what the thing is. A rectangle'swidth/height, an extrusion'sprofile/depth.properties— how Revit shows it.material,subcategory,visible,detailLevels— only on the node kinds that take them: the forms that build a solid (openingtakes none),modelText, and themodelCurve/symbolicCurve/detailCurveelements (nomaterialon a curve).
Materials and subcategories carry args only — a material's color / transparency, a subcategory's default material — and no properties tier. Parameters have neither: they are flat.
Every bindable property takes a literal or a parameter key, and the runner associates it to that family parameter so Revit's own solver drives it. "visible": "ShowHandle" follows a Yes/No parameter; "material": "PanelMaterial" follows a material parameter.
"properties": {
"material": "PanelMaterial",
"subcategory": "Hardware",
"visible": "ShowHandle",
"detailLevels": { "coarse": false, "medium": true, "fine": true }
}Family settings — the root's properties
The root carries the same properties tier, one level up: there is nothing to define at the root (the lanes do that), so only the "how Revit treats it" half exists. These are the switches in Revit's Family Category and Parameters dialog, and you edit them in the editor's Types tab under the FAMILY heading.
"properties": {
"workPlaneBased": true,
"alwaysVertical": false,
"cutWithVoidsWhenLoaded": true,
"shared": false,
"roomCalculationPoint": true
}| Field | Revit |
|---|---|
workPlaneBased | Work Plane-Based |
alwaysVertical | Always Vertical |
cutWithVoidsWhenLoaded | Cut with Voids When Loaded |
shared | Shared |
roomCalculationPoint | Room Calculation Point |
Each one is a literal true or false — unlike a node property, it takes no parameter key, because Revit offers no family-parameter association for these switches. A field you leave out keeps whatever the .rft template already had, which is what the editor's Template option means. A switch the template's category does not offer is a hard error at generation time, not a silent no-op.
Coordinate encoding
A numeric coordinate is a bare number ("depth": 45), while a quoted string is a parameter reference ("depth": "Thickness"). Writing "45" looks for a parameter named 45.
{ "width": 900 } // hardcoded 900 mm
{ "width": "Width" } // parameter reference
{ "width": "-HalfWidth" } // negated parameter
{ "center": [0, "HalfHeight"] } // 2D point: [x, y]