Appearance
Update
Edit existing scene entities — properties and geometry. Accessed via snaptrude.design.update.
Migrated from the removed entity.space.{update, updateGeometryFromProfile, bulkUpdate}. Only provided fields change; each call is undoable. Failures throw (the RPC rejects).
Performance
Editing many spaces? Use the plural spaces(items) — one host round-trip for the whole batch — rather than looping space. See Performance.
Types
PluginSpacePropertyUpdates
Sparse property changes for a space (all optional).
| Property | Type | Description |
|---|---|---|
room_type | string | Room label |
massType | PluginMassType | Mass type |
spaceType | PluginSpaceType | Space type |
areaClass | PluginAreaClass | Area class (NET / GROSS / EXCLUDED) |
departmentId | PluginDepartmentId | Department |
PluginSpaceGeometryUpdate
Geometry replacement — re-extrude a profile.
| Property | Type | Description |
|---|---|---|
profile | ProfileHandle | New footprint profile |
extrudeHeight | number | New extrude height |
PluginUpdateSpaceItem
One space to update via spaces. { space: ComponentHandle; properties?: PluginSpacePropertyUpdates; geometry?: PluginSpaceGeometryUpdate }.
PluginStaircaseParamUpdates
Sparse parametric changes for a staircase (all optional) — the same fields the staircase properties panel exposes. Dimensions are in Snaptrude (engine) units. See design.query.getStaircaseParams for the paired read.
| Property | Type | Description |
|---|---|---|
staircaseHeight | number | Total rise |
steps | number | Step count |
riser | number | Riser height |
tread | number | Tread depth |
width | number | Flight width |
depth | number | Flight depth |
baseOffset | number | Base offset from the storey |
storeyHeight | number | Storey height (when decoupled) |
wellSize | number | Well size (well-based presets) |
landingWidth | number | Landing width (landing-based presets) |
flightStartDistAfterTurn | number | Flight start distance after a turn |
isStoreyHeightUnlocked | boolean | Decouple height from the storey |
isStaircaseHeightUnlocked | boolean | Decouple height from steps × riser |
MassParametersPatch
The argument of parameters: either a partial of a mass record's values (plus an optional operations list) — keys you leave out stay as they are — or the whole MassParametersRecord you read from design.query.getParameters, edited. kind and version are immutable. Points and axes are world coordinates in Snaptrude (engine) units, exactly as getParameters reports them.
Scalars and points merge by key ({ radius: 8 } changes only the radius); lists and records — contour, sections, path, profile, options, operations — replace whole when present, so send the complete list or bag you read ({ operations: [] } drops every operation; { options: { endScale: 2 } } drops every other recorded option).
| Patch key | Value |
|---|---|
any values key of the record's kind | number, a { x, y, z } point, a point array, a ContourRecord (or array), or an options object |
operations | OperationRecord[] — the whole list, in order |
An optional key the record does not carry yet may still be patched in when the kind allows it — that is how a straight extrusion acquires twistInDegrees or topScale, and how an options-less loft or sweep acquires options. levels is the exception in both directions: it is not a dimension but how the twist was built, so a straight extrusion has no such key (a first-twist patch carrying it is refused as an unknown key — send the angle, not the section count), and on a twisted mass it may only be echoed back unchanged. The host re-resolves it whenever an edit changes the angle.
PluginWallUpdates
Sparse changes for a wall (all optional; at least one required). Dimensions are in Snaptrude (engine) units and must be > 0.
| Property | Type | Description |
|---|---|---|
thickness | number | Wall thickness |
height | number | Wall height |
type | string | Convert the wall to another wall type — a name, label, or wall:-prefixed id from design.types.list("wall") |
PluginSlabUpdates
Sparse changes for a slab-family element — slab / floor / roof / ceiling. Values are in Snaptrude (engine) units; thickness must be > 0.
overhang is only available on slabs and roofs (the panels' Overhang slider) — floors and ceilings reject it with PRECONDITION_FAILED.
| Property | Type | Description |
|---|---|---|
thickness | number | Element thickness |
overhang | number | Absolute overhang offset (negative insets the edge) — slab / roof only |
PluginObjectType
Object types setObjectType can turn a mass into — the design.query type vocabulary: "wall" | "roof" | "slab" | "floor".
Functions
grid(grid, patch)
Update a structural grid's group-level fields with a sparse patch — default line styling, absolute angle, and per-storey 2D visibility. Only provided fields change. Wing- and line-level updates live on the grids entity family: design.grids.updateWings / updateLines.
Each changed field routes through the exact setter the native panel uses and commits its own undoable command — identical to editing that field in the panel. angle is an absolute target in degrees (CCW about +Y).
- Parameters:
grid:ComponentHandle— The grid group to updatepatch:angle?: number— absolute angle, degreesprops?: { style?, weight?, color?, showStart?, showEnd? }— group defaultsstoreyVisibility?: Array<{ storey: number; visible: boolean }>— per-storey 2D-plan visibility
- Returns:
trueonce applied
ts
const [grid] = await snaptrude.design.query.listGrids();
await snaptrude.design.update.grid(grid, { angle: 45, props: { style: "DASHED" } });space(space, options)
Update ONE space — sparse properties and/or a geometry re-extrude. Undoable.
- Parameters:
space:ComponentHandle— The space to updateoptions:{ properties?: PluginSpacePropertyUpdates; geometry?: PluginSpaceGeometryUpdate }
- Returns: the updated space's echoed
PluginSpaceUpdateResult - Throws: for an unknown/forged handle or an engine failure
ts
await snaptrude.design.update.space("space-id", { properties: { room_type: "Office" } });spaces(items)
Update many spaces in one host round-trip (bulk plural of space) — always prefer this over looping space; it also commits as a single undoable operation. Validate-all-or-throw; one command.
- Parameters:
items:PluginUpdateSpaceItem[]
- Returns: one
PluginSpaceUpdateResultper item, in input order
ts
await snaptrude.design.update.spaces([
{ space: "s1", properties: { spaceType: "Room" } },
{ space: "s2", geometry: { profile, extrudeHeight: 4 } }
]);setLabel(component, label)
Set a component's Label — the same editable Label field the properties panel shows (doors, windows, furniture, spaces, and every other labelled component). The paired read is design.query.getLabel. Undoable; the canvas label text updates immediately.
Strictly per component: labelling one door of a placed family never touches its sibling doors (the panel's Label edit, by contrast, applies to the whole family). On legacy masses without a space type, panel parity applies — a label matching a room-type preset with a height (e.g. "Water Body") can also adjust the mass's height and material, exactly like typing it in the panel.
- Parameters:
component:ComponentHandle— The component to relabellabel:string— New label text (non-empty; leading/trailing whitespace trimmed)
- Returns:
trueonce the label is applied - Throws: when the component is locked, the handle is gone/forged, the label is empty, or the component has no mesh to label.
ts
// Door tag numbering: label every door as D-<storey>-<n>, numbered per storey
await snaptrude.design.selection.setByFilter({ types: ["door"] });
const doors = await snaptrude.design.selection.get();
const counts = {};
for (const door of doors) {
const { storey = 0 } = await snaptrude.design.query.getProperties(door);
counts[storey] = (counts[storey] ?? 0) + 1;
await snaptrude.design.update.setLabel(
door,
`D-${storey}-${String(counts[storey]).padStart(2, "0")}`
);
}staircase(staircases, params)
Update the parametric fields of one or more preset staircases — riser, tread, width, depth, step count, height, base offset, and the type-specific well/landing/flight fields. Values are Snaptrude (engine) units; the engine round-trips each dimension through the panel's display representation, so a plugin read → write returns the staircase to the exact panel values.
Presets-only: this edits the parameters of an existing preset staircase; it does not re-type it. staircaseHeight / steps / riser are coupled (height = steps × riser) and governed by the two lock booleans — the locks are applied first so the coupled recompute sees them. Every field rebuilds the geometry, and every edit across every staircase commits as one undo batch. A locked or non-staircase handle rejects the whole call before any geometry is rebuilt.
- Parameters:
staircases:ComponentHandle[]— The staircases to updateparams:PluginStaircaseParamUpdates— Sparse field changes
- Returns:
PluginDesignChangeResult— the affected staircases - Throws: for a locked or non-staircase handle, an unknown/forged handle, or an out-of-range value
ts
const [stair] = await snaptrude.design.query.listStaircases();
await snaptrude.design.update.staircase([stair], { steps: 16, width });parameters(component, patch)
Edit a parametric mass by patching its creation recipe and rebuilding the solid in place — the mass keeps its id, storey, materials, position and linked copies; only the geometry changes. This is the write pair of design.query.getParameters: read the current record first, patch the keys you mean, and send them back. Never delete-and-recreate to change a size — that mints a new component and loses its id, materials and linked copies.
patch is a MassParametersPatch: a partial of record.values (plus an optional operations), or the whole record you read back, edited. A key you leave out is unchanged — including operations. Lists and records (options included) replace whole when present. kind and version are immutable: to change the kind, build a new mass. To change a fillet radius, edit operations[i].radius in the record you read and send { operations } back. A patch that changes nothing returns the current record and records no edit (no undo step).
The solid is rebuilt in memory first, then swapped onto the SAME component, so a failure leaves the model untouched; every linked copy regenerates together (one undo step, saved and synced to collaborators like any geometry edit). Kernel kinds — the four primitives, revolution, both sweeps, loft, a twisted or tapered extrusion, and any recipe carrying operations — run on OpenCascade, so expect a short pause.
The reply is the new record: read it rather than re-calling getParameters.
- Parameters:
component:ComponentHandle— A mass whosegetParametersis non-nullpatch:MassParametersPatch— Partialvalues(+ optionaloperations), or the full edited record
- Returns:
MassParametersRecord— The complete post-edit record in world coordinates (whatgetParametersnow returns) - Throws:
PRECONDITION_FAILEDwhen the mass carries no live recipe, when it (or a linked copy) is locked — the recipe stays intact; unlock and retry — when a recorded operation's edge/face selectors no longer resolve on the rebuilt base, or when this edit is the first twist or taper, or the straightening, of a mass that already carries a shell, fillet or chamfer (an offset selects nothing and is not affected; the message names the two-step re-apply);VALIDATIONfor a key the kind does not have,kind/versionin a partial patch, or a value outside the constructor's own rules;OPERATION_FAILEDwhen the kernel cannot build the edited recipe (the model is left unchanged)
The three PRECONDITION_FAILED causes want different responses
No live recipe — the mass was built from a boolean, a split, brepFromFaces/brepFromMesh, or drawn; was edited free-form since (push/pull, move face, boolean); or was scaled with the transform tool. getParameters returns null for exactly these. Do not retry — rebuild it with a recipe constructor and say so (for a scaled mass, undo the scale).
Locked — the message says the mass (or a linked copy) is locked. The recipe is intact and getParameters still returns the full record — a lock is invisible to the read, so this is the one refusal it does not predict. Unlock the mass (design.unlock) and call again; never rebuild or delete a mass the user locked, and never report it as non-parametric.
Selector drift — the message names an operation and an edge: a recorded fillet/chamfer/shell no longer finds its edges on the rebuilt base (the base edit changed the topology). Re-select the edges on the new shape and re-apply that one operation.
First twist or taper of a mass that already carries a shell, fillet or chamfer (an offset selects nothing and is not affected) — a straight extrusion is built by the engine and a twisted one through the kernel, which numbers the faces differently, so every recorded selector would resolve against different geometry. The edit is refused before the rebuild, with the two-step re-apply spelled out: send { twistInDegrees, operations: [] } first, read the new shape (getBrep → brep.listFaces → face.getCentroid picks the top by position, never by the old index), then send { operations } back with the new index. The mass keeps its id across the two calls; in the agent lane one worker body groups them into a single undo step — a plugin calling directly records two. Once a mass is twisted, later twist and taper edits keep their selectors and need no re-apply.
A mass standing on its storey's base keeps that base where it is through an edit, however much the rebuild grew — the solid grows upward, and the record moves with it. A free-floating mass keeps its placement instead and grows about itself.
ts
// Make the dome's radius 8 m (internal units)
const before = await snaptrude.design.query.getParameters(dome);
// { kind: "sphere", version: 1, values: { centre, radius: 19.685 } }
const after = await snaptrude.design.update.parameters(dome, { radius: 31.496 });
// Twist a tower that already exists — `levels` is the host's, not yours
await snaptrude.design.update.parameters(tower, { twistInDegrees: 90 });
// Change the fillet on a rounded column: read → edit → send the array back
const rec = await snaptrude.design.query.getParameters(column);
const operations = rec?.operations ?? [];
if (operations[0]?.op === "fillet") {
operations[0].radius = 1.181;
await snaptrude.design.update.parameters(column, { operations });
}wall(component, updates)
Edit a wall — its thickness and/or height (the same command the properties panel's Thickness/Height fields drive) and/or its wall type (the panel's Wall Types dropdown: converts the wall to another type, rebuilding its layer stack and materials, stacked-wall conversions included). Sparse and undoable; the geometry rebuilds, dimension lines anchored to the wall refresh, and stacked-wall companions and hosted fenestration are handled exactly as the panel does. Dimensions are in Snaptrude (engine) units and must be > 0.
type accepts the tokens design.types.list("wall") returns — the type name, its label, or the wall:<name> id. When combined with dimensions the type conversion is applied first, so an explicit thickness/height wins over the new type's defaults. Re-applying the wall's current type is a no-op.
- Parameters:
component:ComponentHandle— The wall to editupdates:PluginWallUpdates— Sparse thickness/height/type (≥ 1 field)
- Returns:
PluginDesignChangeResult— the affected wall - Throws: for a non-wall handle, a locked wall, an unknown/forged handle, an unknown wall type, a conversion the engine rejects (e.g. a curved wall thinner than the new type's minimum thickness), or no field supplied
Rebuilds replace the wall — re-bind from affected
A type conversion (and a thickness edit on a parametric wall) rebuilds the wall, replacing it with a new component. Your input handle is then dead (further calls throw HANDLE_INVALID); affected[0] carries the replacement's handle — use it for any follow-up calls.
ts
const walls = await snaptrude.design.query.listWalls({ storeys: [1] });
for (const wall of walls) await snaptrude.design.update.wall(wall, { thickness: 230 });
// Convert a wall to another type from the panel's Wall Types dropdown.
// The conversion rebuilds the wall — keep working with the handle it returns.
const [wallType] = await snaptrude.design.types.list("wall");
const { affected } = await snaptrude.design.update.wall(walls[0], { type: wallType.id });
const converted = affected[0]; // NOT walls[0] — that handle is dead after the rebuildslab(component, updates) · floor · roof · ceiling
Edit a slab-family element — its thickness (the same command the properties panel's Thickness field drives), and on slabs and roofs also its overhang (the panels' Overhang slider). One method per kind (slab, floor, roof, ceiling); they share the underlying commands and each rejects a handle of the wrong kind. Sparse and undoable; the element rebuilds and dimension lines anchored to it refresh. Values are in Snaptrude (engine) units; thickness must be > 0.
overhang is an absolute offset (not a delta): re-applying the current value is a no-op, 0 removes the overhang, and a negative value insets the edge. Plinth/basement slabs and elements whose overhang is disabled have no Overhang control — setting overhang on them (or on any floor/ceiling) throws PRECONDITION_FAILED. When both fields are given, thickness applies first, then overhang (each is its own undo entry, exactly like two panel edits).
- Parameters:
component:ComponentHandle— The slab / floor / roof / ceiling to editupdates:PluginSlabUpdates—thicknessand/oroverhang(≥ 1 field)
- Returns:
PluginDesignChangeResult— the affected element - Throws: for a wrong-kind handle, a locked element, an unknown/forged handle, an
overhangon an element without one, or no field supplied
ts
const [slab] = await snaptrude.design.query.listSlabs();
await snaptrude.design.update.slab(slab, { thickness: 250, overhang: 200 });
const [roof] = await snaptrude.design.query.listRoofs();
await snaptrude.design.update.roof(roof, { overhang: 600 });buildableEnvelope(buildableEnvelopeId, sitePolygon, lengthUnit, setbacks, verticalCap, floorToFloor, farRatio?, lotCoverageMaxPct?)
Update an existing parametric buildable envelope in place. buildableEnvelopeId is required and must match an envelope previously created with design.create.buildableEnvelope; the remaining arguments fully re-describe the envelope (nothing is inherited from its current state). This is the canonical home of entity.buildableEnvelope.update (now deprecated).
- Parameters:
buildableEnvelopeId:string— Existing envelope id to update; non-emptysitePolygon:PluginBuildableEnvelopePolygonVertex[]— Site polygon vertices inlengthUnit; minimum 3 verticeslengthUnit:"ft" | "m"— Unit used by all length fieldssetbacks:PluginBuildableEnvelopeSetbackTier[]— Setback profile, ground tier first; minimum 1 tierverticalCap:PluginBuildableEnvelopeVerticalCap— Maximum height or floor countfloorToFloor:number— Required floor-to-floor height inlengthUnitfarRatio:number(optional) — Optional FAR value, positive when providedlotCoverageMaxPct:number(optional) — Optional lot coverage cap,0..100
- Returns:
{ buildableEnvelopeId: string }— The id of the updated envelope - Throws: If validation fails, the envelope does not exist, or generation produces no renderable geometry
ts
const { buildableEnvelopeId } = await snaptrude.design.update.buildableEnvelope(
existingId,
[
{ x: 0, z: 0 },
{ x: 100, z: 0 },
{ x: 100, z: 80 },
{ x: 0, z: 80 }
],
"ft",
[{ aboveHeight: 0, front: 10, side: 5, rear: 10 }],
{ kind: "max_height", maxHeight: 175 },
12
);setElevation(component, elevation)
Set a component's absolute world elevation — moves it vertically so its base (the bounding-box bottom, world Y) lands at elevation. Composed over the canonical command-backed move machinery, so it is undoable and persisted exactly like dragging the element up or down; per-type invariants (e.g. a staircase's base offset) stay in sync. elevation is in Snaptrude (engine) units. Re-applying the current elevation is a no-op.
- Parameters:
component:ComponentHandle— The component to move verticallyelevation:number— Target world elevation of the component's base
- Returns:
PluginDesignChangeResult— the affected component - Throws: for a locked component or an unknown/forged handle
ts
const [roof] = await snaptrude.design.query.listRoofs();
await snaptrude.design.update.setElevation(roof, 3000);setBaseOffset(component, offset)
Set a component's base offset — its vertical distance above its own storey's base level. Moves the component so its base lands at storeyBase + offset, composed over the same command-backed move machinery as setElevation (undoable, persisted, per-type invariants kept in sync). Use this when you want an offset relative to the storey rather than an absolute world height. offset is in Snaptrude (engine) units; 0 sits the base flush with the storey base. Re-applying the current offset is a no-op.
- Parameters:
component:ComponentHandle— The component to offsetoffset:number— Distance of the component's base above its storey base
- Returns:
PluginDesignChangeResult— the affected component - Throws: for a locked component, a component with no storey, or an unknown/forged handle
ts
const [stair] = await snaptrude.design.query.listStaircases();
await snaptrude.design.update.setBaseOffset(stair, 0);setStorey(components, storey)
Reassign one or more components to another storey — the same operation as the properties panel's Story dropdown, working across element kinds exactly like the panel (walls, slabs, masses, furniture, staircases, ...). Each component moves vertically to the target storey's level (per-kind placement rules — a slab sits at the storey base, a wall on it), its storey assignment and children update, stale canvas labels are removed, and storey visibility flags re-apply. All moves commit as one undoable operation; parametric walls/floors are made non-parametric first, exactly as the panel does.
storey is an integer storey number in the entity.story convention (1 ground, -1 first basement; 0 does not exist) and must already exist in each component's building — this call never creates storeys (use entity.story.create first; the panel's dropdown likewise only offers existing storeys). Components already on the target storey are skipped as no-ops (their custom elevation is preserved) and are not echoed in affected.
All guards run up front (all-or-nothing): a locked component, a missing storey, or a bad handle rejects the whole call before anything moves.
- Parameters:
components:ComponentHandle[]— The components to reassignstorey:number— Target storey number (integer,core.storeysconvention)
- Returns:
PluginDesignChangeResult— the components actually reassigned - Throws: for a non-existent target storey, a locked component, or an unknown/forged handle
ts
// Move the current selection up to storey 2
const selection = await snaptrude.design.selection.get();
const { affected } = await snaptrude.design.update.setStorey(selection, 2);
console.log("moved", affected.length, "components");setObjectType(components, objectType)
Change the object type of one or more masses — turn a mass into a real wall, roof, slab or floor: the properties panel's Object Type dropdown, headless. The geometry is kept exactly as it is (a wedge stays a wedge; nothing is flattened to a default thickness), so a sloped mass built with design.create.massFromBrep becomes a sloped roof or a raked wall. The component keeps its handle, label and storey; it gains the target type's construction layers and properties, and the type's default material when the mass still wore the default. One undoable operation for the whole list; undo turns them back into masses.
Instances convert as a family: passing one instance (or the source) of an instanced mass converts its source and every sibling; affected echoes every visible member (the hidden instancing template converts too but is not listed). A converted component cannot be converted again (same rule as the panel). A converted wall has no parametric centerline: it hosts doors and windows and exports, but neighbours do not re-mitre against it.
- Parameters:
components:ComponentHandle[]— The masses to convertobjectType:PluginObjectType— Target type:"wall","roof","slab"or"floor"(thegetEntityTypevocabulary)
- Returns:
PluginDesignChangeResult— the visible components converted: the same handles, plus any instance siblings pulled in by family expansion - Throws: before anything changes (all-or-nothing) when a component is not a mass, is a plinth, is locked or in a locked bucket, already had its type changed, or when a handle is gone/forged
ts
const brep = await snaptrude.core.geom.create.brepFromFaces(wedgeFaces);
const wedge = await snaptrude.design.create.massFromBrep(brep, "Gable end");
const { affected } = await snaptrude.design.update.setObjectType([wedge], "wall");
console.log(await snaptrude.design.query.getEntityType(affected[0])); // "wall"Errors
Failed calls reject with a typed PluginError — see Error Handling. Conditions specific to this namespace:
| Code | Thrown by | When | details |
|---|---|---|---|
VALIDATION | space, spaces | geometry.extrudeHeight is zero or negative (must be > 0) | extrudeHeight — the offending value |
VALIDATION | wall, slab, floor, roof, ceiling | No field supplied, or a thickness/height that is zero or negative (must be > 0) | — |
HANDLE_INVALID | space, spaces | An id does not resolve to a live space/mass (not-found and wrong-kind collapse to the one code); the geometry.profile handle cannot be resolved | — |
HANDLE_INVALID | all element editors | The component id is gone or forged | — |
PRECONDITION_FAILED | wall, slab, floor, roof, ceiling | The handle is not of the expected kind, or the element is locked | — |
PRECONDITION_FAILED | wall | type names an unknown wall type (see design.types.list("wall")) | type — the offending token |
PRECONDITION_FAILED | slab, floor, roof, ceiling | overhang set on an element without one: any floor/ceiling, a plinth/basement slab, or an element whose overhang is disabled | — |
VALIDATION | parameters | A key the record's kind does not have (the message lists the kind's keys), kind/version in a partial patch, or a value outside the constructor's own rules (torus tube ≥ ring, revolution angle ∉ (0, 360], a profile crossing its axis, a spline with fewer than degree + 1 poles) | — |
PRECONDITION_FAILED | parameters | The mass carries no live recipe (getParameters is null — built from a boolean/split/brepFromFaces/brepFromMesh/drawn, edited free-form since, or scaled with the transform tool) — do not retry; or the handle is not a mass | — |
PRECONDITION_FAILED | parameters | The mass or one of its linked copies is locked — the recipe is intact and getParameters still returns it; unlock and call again, never rebuild | — |
PRECONDITION_FAILED | parameters | A recorded operations entry's edge/face selectors no longer resolve on the rebuilt base — the message names the operation and edge. hint: re-select on the new shape and re-apply that operation | — |
OPERATION_FAILED | parameters | The kernel could not build the edited recipe, or the rebuilt solid could not be committed — the model is left unchanged | — |
PRECONDITION_FAILED | setStorey | The target storey does not exist in a component's building, or a component is locked | storey — the missing value |
PRECONDITION_FAILED | setObjectType | A component is not a mass, is a plinth, is locked or in a locked bucket, or already had its type changed | handles — the offending component |
OPERATION_FAILED | setObjectType | The engine could not convert a component (no command was recorded — undo is unavailable for that step) | engineCode: "TYPE_CHANGE_FAILED" |
PRECONDITION_FAILED | setElevation, setBaseOffset | The component is locked; setBaseOffset also throws when the component has no storey | — |
HANDLE_INVALID | grid | The id is unknown or does not belong to a grid group | — |
PRECONDITION_FAILED | grid | The grid is locked | handles — the grid id |
OPERATION_FAILED | space, spaces, setElevation, setBaseOffset | The engine cannot apply the geometry/property update, or the vertical move is rejected (hosted/constrained) | — |
OPERATION_FAILED | wall | The engine rejected a type conversion without applying it (e.g. a curved/non-orthogonal wall thinner than the new type's minimum thickness) | type — the requested type name |
OPERATION_FAILED | slab, roof | The engine could not rebuild the overhang geometry (e.g. a slanted slab whose thickness varies, or a profile that cannot be offset) | engineMessage — the engine's reason |
VALIDATION | buildableEnvelope | verticalCap + floorToFloor resolve to a non-finite or non-positive height (e.g. an extreme maxFloors × floorToFloor) | verticalCap, floorToFloor |
HANDLE_INVALID | buildableEnvelope | buildableEnvelopeId does not resolve to an envelope on the canvas | — |
OPERATION_FAILED | buildableEnvelope | The in-place rebuild failed — typically the setbacks leave no buildable footprint on the site polygon | warnings, handles |
spaces validates every item before anything is applied, so one bad item rejects the whole call with no partial updates (all-or-nothing) — the same guarantee space gets as a batch of one.