Appearance
Families
Author native parametric families from a JSON spec. Accessed via snaptrude.design.family.
A parametric family is a reusable, parameter-driven building component: a curtain-wall panel, a louvre screen, a balustrade, a shading fin. Unlike the rest of design.*, you do not build one out of geometry calls — you write a JSON spec describing its parameters and its geometry rules, and the host compiles that into a live parametric definition the user can place, flex and save to their library.
Workflow
getReference() read the spec DSL once — grammar, ops, expressions, example
prepare({ bundle }) validate + compile a draft in memory; nothing committed
flex({ values }) re-run at new values; returns the geometry delta
place({ position }) put it in the model — saves the family as the first instance drops
place() save the family, then arm placement — the USER clicks to drop it
create({ teamId }) share with a team; a save with nothing placed also drops one instance
delete({ id }) archive the library row; add instances: true to also delete this project's instancesA prepared family lives in memory only. Handing it over with place saves it to the user's personal library (pass teamId to create to share it) and pins every instance to the saved family, so it survives reload with no extra step. Nothing asks the user to confirm; a family they do not want is removed from the library with delete, which leaves placed instances alone unless asked.
Placed instances are "parametricComponent" entities to the rest of the API (see PluginEntityType): move and rotate them with design.transform, but read and drive their geometry through getParameters / setParameters rather than editing their meshes.
Read getReference() first
The spec DSL is a closed expression language with its own op set, coordinate convention (+Y up, Snaptrude units — the scene's own unit, 1 SU = 254 mm; literals may carry a unit suffix such as 600mm or 2ft) and part-naming law. It is deliberately NOT duplicated in these types: getReference returns the whole reference, generated from a family that is actually registered in the host, so the documentation cannot drift from what the compiler accepts.
The repair contract
prepare does not throw on an invalid spec — it returns { ok: false, errors } where each error carries a JSON pointer into the bundle you sent (e.g. "/root/geometry/2/body/0/w"). Read errors[0].path, fix that node, and call prepare again. A failed prepare leaves the previous good draft standing, so it costs nothing but the node you broke. Re-authoring from scratch throws away everything already correct.
Types
PluginFamilyReference
The family-spec DSL reference returned by getReference.
| Property | Type | Description |
|---|---|---|
reference | string | Grammar, op table, expression whitelist, repair contract, worked example |
PluginFamilyBundle
A family spec bundle. root is the family; deps carries every spec reachable through an instance op, keyed by spec id, so a saved family is self-contained and reloads anywhere.
The internal shape of a spec is documented by getReference, not by these types — it is a data DSL whose grammar changes independently of this API surface.
| Property | Type | Description |
|---|---|---|
root | Record<string, unknown> | The family spec |
deps | Record<string, Record<string, unknown>> (optional) | Dependency specs, keyed by spec id |
PluginFamilyBBox
Axis-aligned extents of a built family. Snaptrude units (1 SU = 254 mm), +Y up.
| Property | Type | Description |
|---|---|---|
min | [number, number, number] | Minimum corner |
max | [number, number, number] | Maximum corner |
size | [number, number, number] | [width, height, depth] in Snaptrude units — convert with core.units.convert for display |
PluginFamilyParameter
One row of a family's parameter sheet.
| Property | Type | Description |
|---|---|---|
key | string | Parameter key — the name the spec's expressions use |
label | string | Display label |
type | "length" | "integer" | "enum" | "text" | "boolean" | Value kind |
value | string | number | Current value. length/integer are numbers; the rest are strings |
default | string | number | Default value |
drivesGeometry | boolean | Whether this parameter actually drives geometry — INFERRED by the host from which names the spec's expressions reference. false means the value is stored and round-tripped but moves nothing |
min | number (optional) | Lower bound |
max | number (optional) | Upper bound |
options | readonly string[] (optional) | Allowed values for enum |
PluginFamilyDiagnostic
A validation diagnostic. path is a JSON pointer into the submitted bundle — fix that node and re-prepare.
| Property | Type | Description |
|---|---|---|
path | string | JSON pointer, e.g. "/root/geometry/2/body/0/w" |
code | string | One of "schema", "bad-expression", "unknown-name", "unknown-def", "cycle", "duplicate-name", "unbounded-repeat", "depth", "bad-dimension", "proportion" |
message | string | What is wrong |
hint | string (optional) | How to fix it |
PluginFamilySnapshotOption
Ask for a picture with the result: true for the default sheet (iso, front, right and top tiles with a dimension line), or an object to choose views and tile size. The picture is an isolated offscreen render of the family alone — the viewport is not moved — and comes back as a PNG data URL in the result's snapshot.image. Through the agent's run_worker, return the result and the image reaches the model as an image.
true | { views?: ("iso" | "front" | "right" | "top")[]; tileSize?: number }
| Property | Type | Description |
|---|---|---|
views | ("iso" | "front" | "right" | "top")[] (optional) | Tiles to render; default all four |
tileSize | number (optional) | Pixels per tile (square), 64–2048; default 512 |
PluginFamilySnapshot
The picture block on prepare / flex / getParameters results when snapshot was asked for.
| Property | Type | Description |
|---|---|---|
ok | boolean | Whether the picture was taken |
image | string (optional) | PNG data URL of the composed sheet |
views | ("iso" | "front" | "right" | "top")[] (optional) | Tiles rendered |
size | [number, number, number] (optional) | [width, height, depth] in Snaptrude units |
ms | number (optional) | Render time |
error | string (optional) | Set when the picture could not be taken (e.g. the legacy renderer); the call itself still succeeded |
PluginFamilyPrepareResult
What prepare reports.
| Property | Type | Description |
|---|---|---|
ok | boolean | false means the spec was rejected — read errors, fix, re-prepare |
familyId | string (optional) | Compiled family id |
version | number (optional) | Compiled family version |
label | string (optional) | The spec's label |
partCount | number (optional) | How many parts the family built. Sanity-check against intent |
bbox | PluginFamilyBBox (optional) | Extents in Snaptrude units. Sanity-check against intent |
evalMs | number (optional) | Build time |
parameters | PluginFamilyParameter[] (optional) | The parameter sheet |
takeoff | Record<string, number | string> (optional) | Computed quantities, when the spec declares a takeoff block |
sampleParts | string[] (optional) | The first few part names — enough to check the naming came out right |
snapshot | PluginFamilySnapshot (optional) | Present when snapshot was asked for |
errors | PluginFamilyDiagnostic[] | Rejections, with JSON-pointer paths |
warnings | PluginFamilyDiagnostic[] | Non-fatal findings |
error | string (optional) | Set when the bundle validated but threw while building |
PluginFamilyChangeBuckets
The host's diff of what a flex did to the geometry. This is the evidence that a family is parametric.
| Property | Type | Description |
|---|---|---|
added | number | Parts added |
removed | number | Parts removed |
moved | number | Parts moved |
rebuilt | number | Parts rebuilt |
addedNames | string[] | Names of added parts |
removedNames | string[] | Names of removed parts |
PluginFamilyFlexResult
What flex reports.
| Property | Type | Description |
|---|---|---|
ok | boolean | Whether the re-run succeeded |
values | Record<string, number | string> (optional) | The RESOLVED values after merge + clamping — not necessarily what was sent |
partCount | number (optional) | Parts built |
bbox | PluginFamilyBBox (optional) | Extents in Snaptrude units |
evalMs | number (optional) | Build time |
changed | PluginFamilyChangeBuckets (optional) | The geometry delta |
takeoff | Record<string, number | string> (optional) | Computed quantities at the flexed values |
warnings | PluginFamilyDiagnostic[] (optional) | Proportion warnings at the flexed values (code proportion): a part thinner than 0.5 mm, a part or family over 50 m, a family under 10 mm — the signature of a unit slip |
snapshot | PluginFamilySnapshot (optional) | Present when snapshot was asked for |
error | string (optional) | Set when the re-run threw |
PluginFamilySummary
One family available to place — from the session registry or the catalog.
| Property | Type | Description |
|---|---|---|
id | string | Family id |
version | number | Family version |
label | string | Display label |
category | string (optional) | Category |
parameters | number | Number of rows on the family's parameter sheet |
kind | "native" | "revit" | Where the family came from, which decides how far you can edit it. "native" — authored from a JSON spec through this API; its recipe is data, so every parameter genuinely re-runs the geometry. "revit" — imported from a Revit family; parameters exist and are editable, but only those the importer could bind to geometry actually change the mesh, the rest are carried for round-tripping. Check drivesGeometry on the parameter before promising the user a change |
PluginFamilyPlaceResult
What place reports.
| Property | Type | Description |
|---|---|---|
ok | boolean | Whether placement happened (positioned) or was armed (interactive) |
familyId | string (optional) | The family placed |
version | number (optional) | Its version |
component | ComponentHandle (optional) | The created component — present ONLY when position was given. In interactive mode nothing exists yet, so there is nothing to hand back; the user has not clicked |
error | string (optional) | Set when placement failed |
PluginFamilyInstanceParameters
A placed component's parameter state, from getParameters / setParameters.
| Property | Type | Description |
|---|---|---|
familyId | string | The definition this instance is pinned to |
version | number | Its version |
label | string | The family's label |
values | Record<string, number | string> | Current values, keyed by parameter key |
parameters | PluginFamilyParameter[] | The sheet: what each key means, its bounds, and whether it moves geometry |
snapshot | PluginFamilySnapshot (optional) | Present when getParameters was asked for a snapshot |
PluginFamilyCreateResult
What create reports.
| Property | Type | Description |
|---|---|---|
ok | boolean | Whether the definition was saved |
definitionId | string (optional) | The catalogued definition's id |
version | number (optional) | The catalogued definition's version |
guid | string (optional) | Catalog record guid |
placed | boolean (optional) | True when the save also dropped one instance into the model (nothing was placed yet) |
hint | string (optional) | Host guidance, e.g. that an instance was placed at the view centre |
error | string (optional) | Set when saving failed |
PluginFamilyDeleteResult
What delete reports.
| Property | Type | Description |
|---|---|---|
ok | boolean | Whether the family was removed |
familyId | string (optional) | The family that was targeted |
removedInstances | number (optional) | Instances deleted from the scene, in one undoable command |
archived | boolean (optional) | True when a library row was archived; false for an unsaved draft |
error | string (optional) | Set when removal failed |
PluginDesignFamilyPrepareArgs
Arguments for prepare. Exactly one of bundle / spec is required. A bare FamilySpec (no root wrapper) is also accepted by the host and wrapped for the caller.
| Property | Type | Description |
|---|---|---|
bundle | PluginFamilyBundle (optional) | The family spec bundle |
spec | PluginFamilyBundle (optional) | Alias for bundle |
snapshot | PluginFamilySnapshotOption (optional) | Also return a picture of the built family |
PluginDesignFamilyFlexArgs
Arguments for flex.
| Property | Type | Description |
|---|---|---|
values | Record<string, number | string> | Parameter values to change (partial) |
snapshot | PluginFamilySnapshotOption (optional) | Also return a picture of the flexed family |
PluginDesignFamilyPlaceArgs
Arguments for place.
| Property | Type | Description |
|---|---|---|
id | string (optional) | Family to place, from list. Omit to place the prepared draft |
version | number (optional) | Exact version; defaults to the newest known |
position | { x, y, z } (optional) | Place HERE, without user interaction. Omit to arm the cursor instead |
rotation | number (optional) | Yaw in degrees about +Y. Only with position |
values | Record<string, number | string> (optional) | Parameter values for THIS instance |
storey | number (optional) | Storey to place on; defaults to the active one |
PluginDesignFamilyGetParametersArgs
Arguments for getParameters.
| Property | Type | Description |
|---|---|---|
component | ComponentHandle | A placed parametric component |
snapshot | PluginFamilySnapshotOption (optional) | Also return a picture of this instance (its recipe re-run at its values, with its paint) |
PluginDesignFamilySetParametersArgs
Arguments for setParameters.
| Property | Type | Description |
|---|---|---|
component | ComponentHandle | A placed parametric component |
values | Record<string, number | string> | Values to change (partial) |
PluginDesignFamilyCreateArgs
Arguments for create.
| Property | Type | Description |
|---|---|---|
name | string (optional) | Library name; defaults to the spec's own label |
teamId | string (optional) | Team library to save into; omit for personal |
PluginDesignFamilyDeleteArgs
Arguments for delete.
| Property | Type | Description |
|---|---|---|
id | string (optional) | Family to remove; omit for the prepared draft |
instances | boolean (optional) | Also delete this project's placed instances (one undoable command); default false |
Functions
getReference()
Read the family-spec DSL reference: grammar, the op table, the expression whitelist, the coordinate convention, the repair contract, and a worked example taken from a registered family.
Read this before authoring a family for the first time in a session. The DSL is a closed language — a guessed op or function is a rejected spec.
- Returns:
PluginFamilyReference— The reference text
ts
const { reference } = await snaptrude.design.family.getReference();prepare(args)
Validate, compile and preview a family spec. Nothing is committed to the model or the library — call place or create for that.
Safe to call repeatedly while iterating. On success, check partCount and bbox against what you intended before treating the family as correct — a facade that built 3 parts, or a 3 m screen reporting 40 m of height, is a visible bug the validator cannot catch.
- Parameters:
args:PluginDesignFamilyPrepareArgs— The spec bundle, as{ bundle }or{ spec }
- Returns:
PluginFamilyPrepareResult— Build report, or{ ok: false, errors }with JSON-pointer paths
ts
const r = await snaptrude.design.family.prepare({ bundle });
if (!r.ok) console.error(r.errors[0].path, r.errors[0].message);flex(args)
Re-run the prepared family's recipe at new parameter values.
Values are merged over the current ones and clamped to the parameter sheet's min/max, so a partial { bays: 6 } is fine. The returned changed buckets are the host's own diff of what the geometry did — flexing a driving parameter and reporting them demonstrates the family is parametric.
- Parameters:
args:PluginDesignFamilyFlexArgs— The parameter values to change
- Returns:
PluginFamilyFlexResult— The new build plus the geometry delta
ts
const { changed } = await snaptrude.design.family.flex({ values: { bays: 6 } });
console.log(`removed ${changed.removed}, rebuilt ${changed.rebuilt}`);get()
The prepared family's spec bundle, or null when nothing is prepared.
- Returns:
PluginFamilyBundle| null— The draft bundle, ornull
ts
const bundle = await snaptrude.design.family.get();list()
Every parametric family registered in this session — native and imported.
- Returns:
PluginFamilySummary[]— One summary per family
ts
const families = await snaptrude.design.family.list();place(args?)
Put a family into the model — either the prepared draft or any family from list.
Two modes, chosen by whether you pass position:
With position the component is created there and then, undoably. Use this when you know where it goes — you were given coordinates, or you are copying an existing instance. The returned component handle works with design.transform.move / .rotate and with setParameters.
Without position it arms placement mode with a ghost on the cursor and returns immediately — no geometry yet. The USER clicks to drop it, so tell them to. Prefer this when the location is a judgement call.
To COPY a placed component, place its family again at the new position with the same values — there is no separate copy call.
- Parameters:
args:PluginDesignFamilyPlaceArgs(optional) — Which family, where, and at what parameter values
- Returns:
PluginFamilyPlaceResult— What was placed;componentis present only in positioned mode
ts
// positioned — geometry exists when this resolves
const { component } = await snaptrude.design.family.place({
id: "revit:gypsum-stud-partition~90ad72",
position: { x: 0, y: 0, z: 0 },
rotation: 90
});
await snaptrude.design.transform.move([component], { x: 2, y: 0, z: 0 });
// interactive — the user clicks
await snaptrude.design.family.place();getParameters(args)
Read a placed component's current parameter values and its sheet.
Use before setParameters so you change what is actually there: parameter KEYS are the family's own, not the label the user says, and a Revit-imported family carries parameters that do not drive geometry at all.
- Parameters:
args:PluginDesignFamilyGetParametersArgs— The component to read
- Returns:
PluginFamilyInstanceParameters| null— Its definition, current values, and the parameter sheet;nullwhen the handle is not a parametric component
ts
const { values, parameters } = await snaptrude.design.family.getParameters({ component });setParameters(args)
Change a placed component's parameters and rebuild its geometry in place.
Partial: pass only what changes. The component keeps its position, rotation and identity — this is an edit, not a replace, and it is undoable.
Only parameters with drivesGeometry actually move the mesh. Setting one that does not is accepted and stored (it round-trips to Revit) but nothing visible happens — say so rather than claiming a change the user cannot see.
- Parameters:
args:PluginDesignFamilySetParametersArgs— The component and the values to change
- Returns:
PluginFamilyInstanceParameters— The values now in effect
ts
await snaptrude.design.family.setParameters({ component, values: { seats: 8 } });create(args?)
Save the prepared bundle to the library — the user's personal library unless teamId is given. Handing the family over with place already does this, so create is for sharing with a team. name applies only to the first save of a draft; it does not rename a saved family. When nothing of the family is in the model yet, one instance is also dropped at the centre of the current view on the active storey (placed: true), so a save is never invisible.
- Parameters:
args:PluginDesignFamilyCreateArgs(optional) — Optional library name and team scope
- Returns:
PluginFamilyCreateResult— The catalogued definition's id and version
ts
const { definitionId } = await snaptrude.design.family.create({ name: "Louvre Screen" });delete(args?)
Remove a family from the library: its catalog row is archived, so it leaves the Families panel. Placed instances are untouched, in this project and every other — archived pins keep resolving. Pass instances: true to also delete this project's instances in one undoable command. id may be a draft id, a saved catalog id, or any family from list; omit it for the prepared draft.
- Parameters:
args:PluginDesignFamilyDeleteArgs(optional) — Optional family id and whether to delete instances too
- Returns:
PluginFamilyDeleteResult— How many instances were removed and whether a library row was archived
ts
await snaptrude.design.family.delete({ id: "revit:steel-bollard~bef047" });Errors
Failed calls reject with a typed PluginError — see Error Handling. Note that an invalid spec is not an error: prepare and flex report it in their result (ok: false with JSON-pointer diagnostics) so the draft can be repaired.
| Code | Thrown by | When |
|---|---|---|
METHOD_NOT_PERMITTED | all methods | Family creation is not enabled for this account |
VALIDATION | prepare | The argument is not an object, or bundle is not a spec bundle / bare spec |
VALIDATION | flex, setParameters | No values given |
VALIDATION | place | Nothing is prepared and no id was given, or the id (and version) names no family in this session or the catalog — call list first |
OPERATION_FAILED | getParameters, setParameters | The component's definition is not registered in this session, so its parameters cannot be read — usually the catalog payload failed to load on project open |