> ## Agent Instructions
> These docs contain multiple Speckle product experiences. For Speckle 2026.9, prefer /next/ pages for the topics and changes they cover. The navigation label Current does not override this version-specific precedence.
> Coverage under /next/ is incremental. Use Current documentation for unchanged topics and when a /next/ page says coverage is unavailable or explicitly refers you to Current. Do not infer a feature was removed from a missing page.
> Read the page-specific documentation status, applicability, affected guidance and replacement links in agent-only content. Impacted, changed and superseded apply only to the stated scope; they do not mean the entire feature is deprecated. Historical connector guides apply only to the legacy connector described.
> Match guidance to the customer's deployment version, model data format and connector. Respect plan, permission and compatibility restrictions. If an unknown version or data format changes the answer, ask a focused clarification; do not infer deployment version from the date alone.
# 3D viewer in 2026.9
Source: https://docs.speckle.systems/next/3d-viewer/introduction
What changed when you open a model in the 2026.9 web app — Selection, Filters, Views, section planes, and the new panel chrome.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Opening a model in the 2026.9 web app uses a new 3D viewer. Geometry starts appearing while the
full file is still downloading. **Selection**, **Filters**, and **Views** sit in dockable panels,
and sectioning uses cutting **planes** instead of a box.
This page covers only what changed. For interface basics that still apply — sharing, federation
from the project, and issue fields — see [Interface and navigation](/3d-viewer/interface-nav),
[Exploration](/3d-viewer/exploration), [Saved views](/3d-viewer/saved-views),
[Issues](/3d-viewer/issues), and [Share your models](/3d-viewer/sharing).
Embedded models show only the 3D scene for now. The model tree, **Filters**, selection details,
and the viewer toolbar do not appear inside an embed yet, whichever embed options you choose. This
is a known limitation. Open the model in the web app to filter, select, or measure.
To ask questions about the models loaded in this viewer, use the sparkle. That flow is on
[Ask questions about your models with Speckle Intelligence](/next/analytics/intelligence).
The 2026.9 viewer requires WebGPU. There is no fallback. Firefox on macOS currently allows eight
GPU storage buffer bindings, and the viewer needs nine, so models will not open there — use
Chrome, Edge, or Safari.
## Open a model
Large models use a hybrid load. A first overlay can show **Preparing 3D stream** or
**Downloading model**. Once geometry starts painting, the overlay goes away and a progress stack
in the top-right corner continues in the background. The downloaded copy is stored locally, so
opening the same version again prefers that cache.
You should see objects in the scene before the background download finishes.
Point clouds do not render in this viewer yet. Everything else in the model loads normally, and
your point cloud data is unchanged — the points are not drawn. Support is planned for a later
release.
Some colours and materials do not render in this viewer yet. Affected objects show as flat grey,
even though the colour is in the published data. Known cases are coloured meshes and geometry with
a **Color** input from Grasshopper, Civil 3D surfaces with materials, and models published before
your project moved to the 2026.9 data format. Your data is unchanged, and connectors that load the
model keep the colours. This is a known issue we are working on.
## Find your way around
Panels are dockable. Drag a panel icon onto another panel to make a labelled tab group, split it
into the bottom dock, collapse it, or resize it.
On a first visit, **Selection** is open so the canvas is not empty.
* **Models**, **Filters**, **Views**, and **Issues** sit in the left sidebar tab strip.
* **Selection** sits under those tabs by default and shows properties for the current pick.
* **Elements** sits in the bottom dock and lists every object in the loaded models.
* The status bar at the bottom holds panel icons, undo, redo, screenshot, shortcuts, and
**Settings**.
* The navigation cube sits at the bottom-right of the canvas for canonical camera directions.
The status bar **Settings** menu holds **Orthographic**, **Grid**, and **Reference points**.
**Layout** in that menu lists every panel as a checkbox. **Models** stays on. **Lock layout**
stops accidental drags. **Reset layout** restores the default arrangement after you unlock.
Right-click a panel icon in the tab strip or status bar to move it. The menu lists only slots
that panel can occupy: **Move to sidebar**, **Move to sidebar bottom**, **Move to bottom
panel**, or **Move to bottom right**. **Remove panel** hides it; turn it back on from
**Layout**. **Models** has no **Remove panel**. With **Lock layout** on, the move verbs are
omitted.
These panels start hidden. Turn them on from **Settings** → **Layout**:
* **Materials** lists materials and colours in the loaded models. Select the usage count to pick
the objects that use that material.
* **Browse** pages through tables in the published data.
* **SQL** runs a query against the attached models. **\$selected** inserts the current pick's
application IDs.
* **Inspector** shows envelope relations as a diagram or rows, plus a raw property table. When
nothing is selected it reads **Select an object in the viewer to inspect its rows**.
On narrower widths the left sidebar becomes a slide-over.
Right-click the canvas for **Hide** or **Show**, **Isolate** or **Un-isolate**, **Fit to
view**, **Copy application ID**, **Add to chat**, and **Clear selection**. With more than one
object selected, **Hide** and **Isolate** (and their opposites) become **Hide selection** and
**Isolate selection** (or **Show selection** and **Un-isolate selection**), and the chat verb
reads **Add selection to chat**. Right-click a row in **Models** for the same
verbs except **Copy application ID**. Right-click a scene-tree group to pin that group; the
verb then reads **Add "" to chat**, using the group's name. Right-click a selected
object keeps the whole selection. Right-click an unselected object replaces the selection with
that pick. **Add to chat** pins the pick for Speckle Intelligence. If too many objects are
selected to pin individually, a toast reads **Too many objects selected to add individually**
and tells you to right-click a scene-tree group instead.
### Use the Models card
Right-click the **Models** card header, or use **⋯**, for **Expand one level**, **Collapse one
level**, **Group by…**, **Search this model**, and **Model placement**. When the grouping strip
is already open, **Group by…** becomes **Hide grouping options**. These verbs apply to the
whole card, not to a tree row.
When several models are federated, the same menu also offers **Isolate model** (or **Un-isolate
model**, or **Isolate model (partially isolated)**), **Hide model** or **Show model**, and
**Remove from scene**. Header glyphs show isolate and hide at a glance; they toggle the same
state.
**Search this model** opens a field on the card with the placeholder **Search this model...**.
**Model placement** opens the inspect dialog described in [Inspect model
placement](#inspect-model-placement).
### Group objects in the tree
**Group by…** opens the grouping strip and its editor in one click. A collapsed strip shows
**Grouped by** plus the current levels joined with ›. Select that summary to reopen the
editor.
The editor lists one row per tree level. Helper text reads **Each row is a level of the tree —
drag to renest.** Drag the grip to reorder. Each row is a searchable picker; search with
**Search property or relationship…**. Options show a label, a coverage count, and the full
property path. Relationships such as **Level**, **Collection**, **Model**, and **Network**
appear first when those relations exist. Properties follow; labels come from the path, so
**Family** or **Category** appear when those paths are in the catalog. The default grouping is
a spatial relationship (**Collection** or **Level**) over the highest-coverage property.
**Add grouping** appends a row. **Done** collapses the editor. While the catalog loads, the
strip can show **Loading options…**. If grouping is too fine to precompute, it reads
**Grouping too fine to precompute — the deepest level loads on expand.**
**Keyboard shortcuts** opens from the status bar, or from `?`. That cheat sheet is the
authoritative list, including fly keys (W A S D, E and Q) and Alt+1–5 for standard views.
## Select and inspect
Click geometry, or click rows in **Models**. Selection highlights both ways: the 3D pick reveals
the tree row, and the tree row highlights in 3D. Open **Selection** with Shift+O.
**Selection** shows properties for the pick. The title uses the first non-blank of **name**,
**type**, or **category**. Property groups auto-expand. A **Pinned** group stays at the top when
you pin a path. Revit material quantity blocks are condensed into readable rows named like
** Area** or ** Volume**. A **Relations** group at the bottom lists related
objects by relation kind. Search the list with **Filter properties**.
When nothing is selected, **Selection** shows **No properties**.
From a property you can:
* **Add to filters** — open an empty **Filters** card for that property. The scene does not
narrow until you set the condition.
* **Filter to this value** — add a card already set to this value. The scene narrows
immediately. Relation rows skip this action.
* **Add as table column** — add the path to **Elements**. If it is already a column, the menu
reads **Shown as a table column** and opens **Elements**.
* **Pin property** or **Unpin property**.
* **Copy value** — copies the raw value, not the rounded display string.
* **Copy path**.
* **Copy all properties** — one `path: value` line per row.
* **Copy group path** — from a group header that has a real path. **Pinned** and **Relations**
have no path to copy.
* **Expand all groups** or **Collapse all groups** — on a single-object list. Multi-select
cards fold from the toolbar instead.
When you select several objects, **Selection** shows one card per object. Each card can zoom,
isolate, or hide that element. In a wide dock the cards sit in columns. Fold or expand all
cards from the toolbar. The count reads **N selected**. Selections larger than 1,000 objects
start folded; you can still open one card at a time.
Use **Show all** on the toolbar when objects are hidden or isolated.
### Browse objects in Elements
**Elements** is a table of the objects in the loaded models. It follows the active **Filters**
stack. Double-click a row to zoom to that object. When several models are loaded, a scope
control can limit the table to one model or to the current selection.
Select **Elements** in the bottom dock, or use Shift+T. You should see a table of objects and a
count.
Select a row. **Selection** shows that object's properties. Double-click the row to frame it in
the scene.
**Export view to Excel** downloads the current columns, filter scope, and sort. If grouping or
search is too large, a warning reads **Showing the first … elements — narrow the scope (filters
or selection) for a complete view**.
**Update parameters** turns columns whose path starts with **properties.** into editors. Other
columns stay read-only and show a lock icon in the header. Staged edits do not change the
published version. **Submit change requests** creates an issue that carries those deltas, so
teammates can review the proposed values. Creating that issue needs permission to create issues
on the project.
When **Update parameters** is on and the table has 15 or more elements, **Search rows…** appears
and narrows the table. It is not on the browse toolbar.
Only **properties.** columns become editors. Name, category, and similar columns stay read-only.
## Filter and color
**Filters** is a stack of property cards, not a single search box. Each card has operators that
match the property type. Applied filters stay in place when you change selection or close and
reopen panels. Open **Filters** with Shift+F.
When the stack is empty, **Filters** shows **No filters yet** and an **Add filter** control. If
the catalog fails, it shows **Failed to load properties**. If a resolve fails, it shows
**Failed to resolve filter matches**. While a model is still attaching, it shows **Attaching
model…**.
Operators by property type:
* String: **is one of**, **is not one of**, **contains**, **does not contain**, **has value**,
**is set**, **is not set**.
* Number: **equals**, **is not equal**, **in range**, **has value**, **is set**, **is not set**.
Numeric **equals** and **is not equal** accept more than one value. Equals tolerates small
floating-point differences.
* Boolean: **is** (**True** / **False**), **has value**, **is set**, **is not set**.
* Relation (for example a level or network hop): **is one of**, **is set**, **is not set**.
**Has value** matches objects where the property has a non-empty value. **Is set** matches
objects where the property exists. **Is not set** matches objects where it is missing.
Open **Filters** and select **Add filter**. Choose a property. An empty card appears in the
stack. The scene does not change yet.
Choose an operator and the values to include or exclude. The scene then narrows to matches. If
nothing matches, **Filters** shows **No objects match — everything is hidden**.
On a string or relation card, select **Color by values**. Matching objects take colors for those
values. Only one card colors the scene at a time. Numeric and boolean cards do not color the
scene.
With two or more cards, **All** keeps objects that match every card. **Any** keeps objects that
match at least one. With **All**, each card's value list is scoped to the enabled cards above it.
With **Any**, the cards are independent. Drag the grip, or use Alt+↑ / Alt+↓, to reorder.
Right-click a card for **Change property…**, **Collapse** or **Expand**, **Color by values** or
**Stop coloring**, **Disable filter** or **Enable filter**, **Move up**, **Move down**,
**Duplicate**, and **Remove filter**. A disabled card stays in the stack but does not hide
objects.
**Clear all filters** is in the panel header. **Reset filters** on the toolbar clears the same
stack without undoing hide and isolate.
Intelligence in this viewer can also color and isolate from Chat. That is documented on [Ask
questions about your models with Speckle Intelligence](/next/analytics/intelligence), not here.
## Save and apply views
**Views** lists two kinds of viewpoint in one panel. Open it with Shift+V.
* **Model views** come from the source application when the connector published them. Select a
row to fly the camera. These rows stay in **Model views**; converting one does not remove it.
An orthographic published view can show a **Parallel / orthographic (isometric)** cue.
* Saved views are viewpoints you (or a teammate) stored in Speckle. The panel only lists views
whose models are currently loaded. A view saved on an older version of a loaded model still
applies.
When nothing is saved and there are no groups, **Views** shows **No views yet**. A search with no
hits shows **No matching views**. An empty named group reads **Drop views here**.
Saving a view needs permission to create saved views on the project. Creating saved views needs
the Contributor project role or higher. Without that permission, **Save view** and **New group**
are disabled. **Sketch** is also disabled; its tooltip reads **You don't have write access**. See
[Saved views](/3d-viewer/saved-views) for grouping and sharing rules that have not changed.
On a self-hosted server, saved views are available only on Speckle Enterprise Server. For
deployment setup, see [Enterprise license — Saved
Views](/developers/server/deployment/enterprise-license#optional-saved-views).
Frame the camera, set filters and visibility, and add section planes or sketches you want
stored.
In **Views**, select **Save view**, or use the bookmark control on the toolbar. Name it. The new
view appears in the panel.
To keep a connector-published viewpoint as a saved view, select **Save as view** on that **Model
views** row, or drag the row into a saved-view group. Dragging into a group needs at least one
named group and permission to create views. You cannot drop a model view into **Ungrouped**.
**New group** creates an empty group as a drop target. Search with **Search views**. Cards show
the author and a relative date. Private views show a lock with the tooltip **Only visible to
you**.
On a saved view the ⋯ menu is **Rename**, **Make private** or **Make public**, and **Delete…**.
There is no **Set as home view**, **Replace view**, or **Copy link** on this menu. Use the eye
control to show or hide that view's sketches and measurements.
On a group the ⋯ menu is **Rename**, **Make all private** or **Make all public**, and **Delete
group…**. **Ungrouped** is a tail for views that were saved before grouping. You can drag views
out of it. You cannot drop views into it. Its menu is **Delete all views…**.
Deleting a view also deletes its sketches and measurements. The confirm dialog says so.
A view created in the previous viewer can show **Legacy view — not openable here yet**. Re-save
the look in this viewer if you need a viewpoint you can apply.
## Cut, measure, and sketch
The bottom toolbar holds **Section**, **Sketch**, and **Measure**. Shortcuts: Shift+B, Shift+K,
and Shift+R. Press Esc to leave a tool. Cmd/Ctrl+Z undoes while sketching or measuring.
### Section planes
Sectioning is no longer a six-sided box around the model. You add individual **section planes**
and move each one.
Select **Section** on the toolbar. The tool options appear above the toolbar.
Select **Plane**, then click a surface, or select **XY**, **XZ**, or **YZ** to cut at the scene
centre. The model is clipped on one side of the plane.
Select a plane in the list to show its handle. Drag the handle, **Flip which side is cut away**,
or **Stop cutting** without removing the plane. **Resume cutting** turns that plane back on.
**Hide plane handles** keeps the cut for a cleaner screenshot. **Remove all planes** clears the
list. If **Plane** is disabled, the tooltip reads that all plane slots are in use.
### Measure
Types: **Point to point**, **Perpendicular**, **Angle**, and **Area**.
Measurements stay on the canvas for the session. If you can save views, they are stored on the
saved view with sketches. Without that permission they disappear when you leave the model.
Measurements live on their saved views in the **Views** panel. Use the eye control on a view card
to show or hide that view's sketches and measurements. The same eye on a group header toggles
every view in that group.
### Sketch in 3D
**Sketch** draws on the model in 3D. Ink stays with the geometry as you orbit, pan, and zoom. It
is stored on a saved view: saving the view keeps the markup, and opening that view restores it
in place.
Sketches live on their saved views in the **Views** panel. Use the eye control on a view card to
show or hide that view's sketches and measurements. The same eye on a group header toggles every
view in that group.
Tools: **Pen**, **Arrow**, **Revision cloud**, **Text**, and **Eraser**. Preset ink colours are
**Red**, **Amber**, **Blue**, **Green**, and **White**. **Custom color** adds any colour and
opacity. Brush sizes sit next to the colours. Sketch is disabled without permission to save
views.
### Navigation cube
The cube in the bottom-right of the canvas sets canonical directions. Alt+1–5 also move the
camera to Top, Front, Left, Back, and Right. Fit and zoom still sit on the toolbar: **Fit view
to everything**, or **Zoom to selection** when something is selected. Shift+Space does the same
fit. Shift+P toggles orthographic projection.
Explode, the older view-mode styles (Pen, Arctic, Shaded), and lighting controls are not on this
toolbar. Projection and the grid are in **Settings**.
## Restore an issue's view
**Issues** lists issues for the models in the scene. Open it with Shift+I. Opening an issue that
was created in this viewer restores the camera, filters, visibility, and selection stored with
it. Select a card to apply that stored look. Right-click the card, or use **⋯**, for **Open issue page**
and **Delete…** when you can delete. **Open issue page** opens the full issue in a new tab.
Create, assign, and field editing stay on [Issues](/3d-viewer/issues).
To tie an issue to particular objects, select them in the scene before you select **New issue**.
The selection is stored with the issue, along with the camera, filters, and visibility at the
moment you select **Create issue**. Opening the issue selects those objects again. An issue
raised with nothing selected stores the view only.
The **Issues** tab is always in the strip. If the feature is not available on the project, the
panel body shows that denial instead of a list. Empty states: **No issues for this scene** with
**New issue**, **No matching issues** after search or chip filters, and **Select an issue** in
the wide-dock detail pane.
Issues are available when your workspace plan includes them. On a self-hosted server, they are
available only on Speckle Enterprise Server. For deployment setup, see [Enterprise license —
Issues](/developers/server/deployment/enterprise-license#optional-issues). Issue fields,
assignment, and permissions stay on [Issues](/3d-viewer/issues).
An issue created in the previous viewer may show **Legacy issue — its view can't be applied here
yet**. If the issue points at models that are not loaded, the tooltip reads **References models
not loaded in this scene — opens details only**.
## Inspect model placement
When several models are federated, **Models** can open **Model placement** for a model. That
dialog shows the published placement, geometry storage, Project True North, and translation.
**Inspect placement option** previews Internal Origin, Project Base Point, Survey Point origin,
or Shared Coordinates.
**Model placement** in this viewer is inspect-only. Switching placement is not enabled yet.
How Revit stores placement on publish is on [Revit in 2026.9](/next/connectors/revit/introduction).
Adding models to a scene from the project still follows [Model federation](/3d-viewer/federation).
## Keyboard shortcuts
Open the cheat sheet from the status bar or `?`. Bindings use Cmd on macOS and Ctrl elsewhere.
* Panels: Shift+M **Models**, Shift+F **Filters**, Shift+V **Views**, Shift+I **Issues**,
Shift+T **Elements**, Shift+O **Selection**.
* Tools: Shift+R **Measure**, Shift+B **Section**, Shift+K **Sketch**.
* Camera: Shift+Space **Fit view / zoom to selection**, Shift+P **Orthographic projection**,
Alt+1–5 standard views.
* Display: Shift+G **Grid**, Shift+X **Reference points**.
* History: Cmd/Ctrl+Z **Undo**, Cmd/Ctrl+Shift+Z **Redo**.
* Fly around: W A S D or arrow keys to move. E or Page Up moves up. Q or Page Down moves down.
Hold Shift for faster, Alt for slower.
## FAQ
Point clouds are not drawn in the 2026.9 viewer yet — see [Open a model](#open-a-model).
Re-publishing or re-uploading the model will not change what you see, and connectors that load
the point cloud keep working. Uploading an `.e57` file is a separate case — see [File uploads in
2026.9](/next/connectors/file-uploads).
Some colours and materials are not drawn in the 2026.9 viewer yet — see [Open a
model](#open-a-model). Your data is unchanged. If the model was published before your project
moved to the 2026.9 data format, publish it again from the source application to restore the
colours. For Grasshopper colours and Civil 3D surface materials, publishing again does not help
yet. There is no Shaded or Rendered view mode in this viewer, so switching modes is not a fix.
If the colour stands for a property such as status or category, use **Color by values** on that
property in **Filters** instead.
There is no dedicated diff view in the Models **⋯** menu. Speckle Intelligence covers change
detection, reporting, and impact assessment instead: ask Chat or the viewer sparkle to compare
two versions and summarise what was added, removed, or changed, save that as a shareable Report,
or turn a recurring comparison into a skill. See [Ask questions about your models with Speckle
Intelligence](/next/analytics/intelligence#what-you-can-ask), [Compare versions in a
Report](/next/analytics/reports#compare-versions), and [Assess model changes with Speckle
Intelligence](/next/workflows/assess-model-changes-with-intelligence) for a full worked example.
[Compare Versions](/3d-viewer/compare-versions) covers the deprecated Current-viewer flow.
The 2026.9 viewer cuts with section planes, not a box. Add **XY**, **XZ**, or **YZ**, or place a
plane on a surface, then flip or hide individual planes. Explode and the older view mode styles
are not on this toolbar. Projection and the grid are in **Settings**.
Sketch is stored on saved views, so it uses the same permission as **Save view**. Reviewers
without that permission cannot sketch. Save a view after you draw if you need the ink later.
On their saved view in the **Views** panel. Use the eye on the view card to show or hide that
view's sketches and measurements. A group header eye toggles every view in the group. See
[Sketch in 3D](#sketch-in-3d).
Yes, if they were stored on that view. Measurements live on that view in the **Views** panel
with sketches. Use the eye on the view card to show or hide them. A group header eye toggles
every view in the group. Without permission to save views, measurements last only for the
session. See [Measure](#measure).
Some older views show **Legacy view — not openable here yet**. Re-save the look in this viewer
if you need a viewpoint you can apply.
When no objects match, **Filters** shows **No objects match — everything is hidden**. Change the
values, switch **All** to **Any**, enable a disabled card, or use **Reset filters**.
No. **Color by values** is on string and relation cards only. Use Chat if you want Intelligence
to color by a property; that flow is on [Ask questions about your models with Speckle
Intelligence](/next/analytics/intelligence).
They are not on the 2026.9 view-card menu. You can **Rename**, change public or private, show or
hide ink, and **Delete**. Grouping and dragging still work. Home view and replace remain
documented for the viewer before 2026.9 on [Saved views](/3d-viewer/saved-views).
Not yet. **Model placement** lets you inspect the published datum. Changing placement on publish
is documented for Revit on [Revit in 2026.9](/next/connectors/revit/introduction).
No. Edits stay staged in **Elements** until you **Submit change requests**. That creates an
issue with the proposed values. The published version is unchanged.
Embeds do not show the model tree, **Filters**, selection details, or the toolbar in the 2026.9
viewer yet. Changing the embed options will not bring them back. Your model and its data are
unchanged. Open the model in the web app to filter, select, or measure.
Firefox on macOS supports WebGPU, but it currently allows only eight GPU storage buffer
bindings. That is the WebGPU specification minimum. The 2026.9 viewer needs nine, so the model
will not open. Other browsers on macOS allow ten or more, and Windows and Android browsers
generally allow sixteen or more. This is a known, accepted limitation, not a bug. Firefox may
raise the limit later; until then, use Chrome, Edge, or Safari.
# Dashboards in 2026.9
Source: https://docs.speckle.systems/next/analytics/dashboards
Dashboards load 2026.9-format models. Compatibility mode is optional, and Intelligence Reports remains a separate product.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
Dashboards read 2026.9-format models natively. Do not tell readers to enable compatibility mode so Dashboards can load those models. Compatibility mode stays optional for ACC sync and drag-and-drop consumers that still need the old format. The widget-availability page is the list of placeholders, not a launch blocker.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Dashboards load models in the 2026.9 data format. You do not need
[compatibility mode](/next/it-admin/compatibility-mode) for that.
Models published with 2026.9 connectors, ACC sync that writes the new format,
and Rhino or IFC file uploads all appear in Dashboards. This includes older
versions: Speckle Server has upgraded existing version history to the new
format, so Dashboards load against your full model history, not just data
published after the fix.
A few widgets are not ported yet. Those saved instances stay on the grid as
placeholders. They keep their configuration. They do not load 2026.9-format
data until that widget is ported. See
[Dashboard widgets not yet available](/next/analytics/dashboards-widget-availability).
For building, sharing, and configuring dashboards, use the
[current Dashboards documentation](/analytics/intelligence-dashboards).
Compatibility mode is optional, and it ends on **1 November 2026**. Request it only when ACC sync
or drag-and-drop still has to write the old format for another tool. See [Compatibility
mode](/next/it-admin/compatibility-mode).
## New widgets, not just a compatibility port
Reading 2026.9-format models is the baseline fix. Alongside it, a few widgets are new — they did not exist in Current Dashboards:
* **Dual viewer** — two Model Viewer panes in one widget, cameras synced, for comparing two models side by side.
* **Elements table** — a unified table replacing both Element table and Pivot table, built for the new data format.
* **Ratio value** and **Ratio value by property** (Beta) — a live ratio between two numbers (or a number and a per-property breakdown), traced back through its property sources, calculated fields, and filters.
* **Numeric range** (Beta) — a slider filter widget that publishes a range for other widgets to filter by.
* **Property checker** — model validation rules surfaced as a dashboard widget, rather than only in the model checker tool.
These ship alongside the format fix, not as a separate release.
## Dashboards remain the analytics product
Dashboards stay available on every plan. Workspaces without Speckle
Intelligence keep using them. Workspaces with Intelligence can use Dashboards
and [Intelligence Reports](/next/analytics/reports) side by side.
Our focus for Dashboards from here is stability and reliability: keeping the
existing widgets and workflows working well, and responding to bug reports.
We're not currently working on expanding what Dashboards can do — that
effort is going into Intelligence Reports instead.
If you have built a dashboard that solves a problem in a way we have not seen
before, share it in the [forum](https://speckle.community) or email
[feedback@speckle.systems](mailto:feedback@speckle.systems).
## Intelligence Reports
For workspaces with Speckle Intelligence, **Intelligence Reports** start from
an analysis you already ran in
[Speckle Intelligence Chat](/next/analytics/intelligence). You ask a question
about your model, the chat builds the analysis, and you save that as a Report.
Reports can also draw on external data sources — Snowflake, Databricks, and
others — so cost rates, programme data, or benchmarks sit next to model data.
See [Create and share Intelligence Reports](/next/analytics/reports) for
editing, sources, and sharing.
Intelligence Reports require a workspace plan that includes Intelligence. Dashboards remain
available on all plans.
## FAQ
No. Dashboards already read the 2026.9 format. Request compatibility mode only when ACC sync or
drag-and-drop must keep writing the old format for another tool. See [Compatibility
mode](/next/it-admin/compatibility-mode).
That widget is not ported yet. The saved configuration stays. The list of placeholders is on
[Dashboard widgets not yet available](/next/analytics/dashboards-widget-availability). Other
widgets on the same dashboard still load 2026.9-format data.
Compatibility mode ends for every workspace. That date is for ACC sync and drag-and-drop format,
not for Dashboards. Dashboards already read 2026.9-format models.
All of them. Speckle Server has upgraded existing version history to the new data format, so
Dashboards load against a model's full version history, not just versions published after the
fix.
# Dashboard widgets not yet available
Source: https://docs.speckle.systems/next/analytics/dashboards-widget-availability
Widgets that do not yet run against 2026.9-format models, and what happens to saved instances.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This page is the published not-yet-migrated widget list. A widget belongs here only while its Bashboards registry entry has no `nextCompatible` flag. When that flag is set, remove the widget from this page in the same change as the registry update. Atlas pointer and update rule: `atlas/specs/2026-09-bashboards-2026-9-widget-availability.md`. Deprecated widgets live only in the Deprecated in 2026.9 block. Do not mix them into the not-yet-migrated sections. Do not list hidden developer examples or widgets that already run through the compatibility projection.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Dashboards load 2026.9-format models. See [Dashboards in 2026.9](/next/analytics/dashboards). Compatibility mode is optional and is not required for Dashboards. For building and sharing
dashboards, use the [current Dashboards documentation](/analytics/intelligence-dashboards).
However, some dashboard widgets do not yet run against models in the 2026.9 data format. Saved widgets keep their configuration but appear as placeholders until support for the new format is available.
Widgets that are **not** on this list either already run against 2026.9-format models, or they are presentation widgets that do not read model properties.
For the launch limitation and compatibility mode, see [Dashboards in 2026.9](/next/analytics/dashboards). For building dashboards on pre-2026.9 data, use the [Dashboards documentation for 2026.8 and earlier](/analytics/intelligence-dashboards).
## How to read this list
* A widget leaves the sections below when it can run against 2026.9-format models.
* Pre-2026.9 data, and workspaces in [compatibility mode](/next/it-admin/compatibility-mode), still use the widget set prior to this change.
* Widgets in Deprecated in 2026.9 are retired. They will not gain 2026.9-format support.
## Deprecated in 2026.9
These widgets are retired in 2026.9. A saved instance may still appear on a dashboard. That does
not restore the widget. Use the replacement named for each widget. If no replacement is named, the
widget has no successor.
* **Element table** — replaced by the Elements table.
* **Pivot table** (matrix table) — replaced by the Elements table.
* **Context** (experimental, model on a map) — deprecated. This is not the same as marking a model as a [context model](/analytics/intelligence-dashboards#context-models) in the Model Viewer.
* **Parameter Updater** — no longer a dashboard workflow in 2026.9. Use the [Elements panel in the model viewer](/next/analytics/data-conditioning).
* **Revit-category Material quantities over time** — replaced by Time series Beta Material quantities over time.
* **Cost Estimation for Tekla** — deprecated. No replacement.
* **Render Genie** — no longer supported. No replacement.
* **Model Validation** — retired from new dashboards. Saved widgets still run. The Model validation sidebar appears only on dashboards that already have a Model Validation widget. Use Property checker for new checks.
## Materials and carbon
* Embodied Carbon
* Carbon Assessment
Speckle's carbon tools are not yet tested for production use. If carbon reporting is a hard
requirement, [Speckle Intelligence](/next/analytics/intelligence) may be a better fit today. Let
us know via [speckle.community/help](https://speckle.community/help) if you're relying on Embodied
Carbon or Carbon Assessment so we can factor that into the rollout.
## CAD structure
These widgets remain placeholders until they support CAD model structure in the 2026.9 data format. They may work minimally for some models.
* Blocks
* Layers
* Nested Blocks
* Nested Blocks Pivot Table
## Issues
* Issues Overview
* Issues list
## Other application-specific widgets
* Model Properties
# Update parameters from the model viewer
Source: https://docs.speckle.systems/next/analytics/data-conditioning
In 2026.9 you batch-edit parameters in the Elements panel, then apply them in the connector via Issues.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
In 2026.9 you review and batch-edit model parameters from the **Elements** panel
in the model viewer, then apply those changes back in Revit, Rhino, or Civil3D.
Each batch is still logged as an Issue.
This page covers only what changed. For the dashboard-widget path from before 2026.9,
editable types, connector apply steps, and known limitations, see
[Parameter Updater](/beta/parameter-updater).
Parameter updates in 2026.9 run against models in the new data format. If a model has not moved
yet, follow [Speckle's data model is changing](/workspaces/data-model-migration).
## What changed
Before 2026.9, Parameter Updater is a **Data Conditioning** widget that you add to a
dashboard. In 2026.9 that widget is not the path: you work on the models already
open in the viewer.
* **Where you edit:** the **Elements** panel in the model viewer, not a dashboard
widget.
* **How you start:** open **Elements**, then choose **Update parameters**. You do
not drag a widget from Data Conditioning.
* **Which models you can edit:** models loaded in the viewer, not widget data
sources you pick separately.
* **New attributes:** open **Columns** and use **New attribute name…** while
update mode is on. There is no **+** button on the column headers.
* **What submit creates:** an Issue that stores the parameter changes. It does
not attach a 3D screenshot or viewer state the way the widget does before 2026.9.
Apply in the connector is unchanged: open the Issue on the model card and use
**Apply changes**. Connector version, host support, and the limits in
[Parameter Updater](/beta/parameter-updater) still apply.
## Example use cases
Use Parameter Updater when you know the values that model elements should carry
and want to set or standardise them in bulk. Common examples include:
* **Review and approval:** update an element's **Status** as work progresses, or
set an **Approval status** after a review.
* **Responsibility and coordination:** assign elements to a discipline, team,
owner, or coordination zone.
* **Delivery planning:** add a project phase, work package, milestone, or
installation sequence to selected elements.
* **Asset information:** add or correct asset IDs, system references, locations,
maintenance zones, or handover fields.
* **Quality checks:** fill missing required parameters or correct values found
during model review and validation.
* **Data standardisation:** replace inconsistent names, codes, or allowed values
with the agreed project terminology.
* **Known classification values:** apply an agreed classification code to a
selection when you already know which code is correct.
These are data-conditioning workflows: you are maintaining parameter values on
existing elements. Applying a known classification value is also data
conditioning. It becomes autoclassification when the system infers which
category or type an element belongs to.
## What you can change
With Parameter Updater, you can:
* add a new parameter;
* insert a value where an existing parameter is empty;
* overwrite an existing parameter value; or
* clear a parameter value.
You cannot delete a parameter. Clearing removes its value but leaves the
parameter on the element.
## Update parameters in 2026.9
Open the project and model in the Speckle web app so the 3D view loads. You should see the model
and the viewer panels, including **Elements** in the bottom dock.
If **Elements** is not showing, open it from the viewer panel switcher, or press Shift+T. You
should see a table of elements for the models in the scene.
In the Elements toolbar, choose **Update parameters** (pencil). Columns under `properties`
become editable, headers show a pencil or lock, and you can group rows. Turning the mode off
does not discard staged edits.
Scope the table to **All elements**, the current selection, or one loaded model. Edit cells, or
with grouping on use **Set all…** and **Set selected…**. Cells that the object does not carry
show **Missing parameter** and are skipped. To add a parameter that does not exist yet, open
**Columns**, type a name in **New attribute name…**, and add it. You can insert, overwrite, or
clear values, but you cannot delete a parameter.
When you have unsaved edits, the footer shows the count with **Discard** and **Submit change
requests**. Fill in the title and optional Issue fields, then submit. Speckle creates an Issue
that stores the parameter changes. You should see a success confirmation, and the staged edits
clear.
After you submit, apply the Issue in the connector the same way as today. See the apply step on
[Parameter Updater](/beta/parameter-updater).
## FAQ
In 2026.9 you edit from **Elements** in the model viewer, not from a Data Conditioning widget.
The widget path from before 2026.9 is documented on [Parameter
Updater](/beta/parameter-updater).
Open a model that is already in the new data format, then open **Elements**. **Update
parameters** is hidden when the panel is very narrow; widen the dock until the pencil control
appears. See [Speckle's data model is changing](/workspaces/data-model-migration) if the model
has not moved yet.
Yes. Submit still creates an Issue, and you still apply it from the connector Issues panel. Host
support, connector version, and apply-time limits are unchanged. See [Parameter
Updater](/beta/parameter-updater).
# Data Validation in 2026.9
Source: https://docs.speckle.systems/next/analytics/data-validation/overview
What changes for Data Validation after models move to the 2026.9 data format, including related objects, value mapping, calculated values, and the removal of project standards.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Use this page when your project is on the 2026.9 data format. Authoring, saving, and result review
still work as in the current product. **Project standards** are not in 2026.9. For check and result
flows that did not change, see [Data Validation](/analytics/data-validation/overview).
This page covers only the differences that change how a check scopes objects, reads properties,
binds a related object (direction, all versus any, multi-hop), maps labels before a comparison,
and tests a calculated value, plus the Project standards change.
## What stays the same
Checks, thresholds, plan caps, BCF export, and result history follow the current guides:
* [Checks](/analytics/data-validation/checks)
* [Viewing Results](/analytics/data-validation/viewing-results)
Authoring still uses a scope and a check. In 2026.9 the editor shows those as short sentences.
A rule can also include a related object.
## Project standards
2026.9 does not include **Project standards**. The Data Validation sidebar shows **Run check** and
**Checks** only. You cannot create, import, or spawn checks from a project standard.
Reuse across the workspace is planned later as a rollup from Checks into workspace standards. That
flow is not available yet. Until it is, author and save checks.
If your team spent time developing Project standards in the previous Data Validation flow, email
[hello@speckle.systems](mailto:hello@speckle.systems).
## Default object scope
In the current product, a new check starts with a global filter that keeps connector BIM objects
and drops proxy and geometry noise. In 2026.9, a new check starts with an **empty** global
scope. The version already contains user-facing objects only, so that starting filter is not
needed.
Do not follow the current [Default scope](/analytics/data-validation/checks#default-scope)
guidance on a 2026.9 version. Leave the global scope empty, then narrow with category, type, or
parameter filters in `WHERE` when you need a tighter rule.
An existing check can return no objects on a 2026.9 version. Preview shows empty counts and an
empty object table. The stored rule still uses the previous-format object filter. That filter is
not shown as a control in the 2026.9 check editor, so you cannot clear it in place. Rebuild the
check: create a new check, run preview, then save.
Unsaved drafts that still carry the old default are cleared when you reopen the editor. Saved
checks keep their stored rules. After save, you can edit name and description only.
## Checks against related objects
In the current product, a rule looks at properties on one object. In 2026.9 you can also check
that object against something it is related to, because those relationships are in the published
version.
Use **Add related object** on the rule. The editor adds a binding row. The name on the left
(`@host`, `@room`, `@connected`) is how later conditions address that object. Pick a
relationship from the list the version actually carries. Friendly labels include **Host
element**, **Sub-elements**, **Room**, **Bounded room**, **Assembly**, and **Connected
element**.
Then choose a property on that related object from the path picker. Related paths show as
`@host · Fire Rating` in the sentence pills.
**Add condition** adds a scope filter above the check. It does not turn the existing check into a
filter.
Rules display as short sentences, for example **category is Doors**. Select a sentence to edit
it.
**Add related object** is disabled when the preview version has no relationship data. The tooltip
reads **This model has no relationship data — re-publish with a current connector to use
related-object rules.** Publish a new version after the project uses the 2026.9 data format.
### Direction, all versus any, and narrowing
The arrow on the binding follows the relation **forward** (for example a door toward its host
wall) or **backward** (a wall toward the elements hosted on it). Click the arrow to reverse it.
**all** (default) means every related object must satisfy the check. **any** means one related
object is enough. That setting applies to the check only. A scope filter on a related path still
keeps a subject when at least one related object matches, regardless of **all** or **any**.
Open **where** and select **Narrow related objects** to keep only some related objects, for
example hosts whose category is Walls. Those filters run on the related object. They do not use
`@` prefixes.
### When there is no related object
A subject with no related object **fails** a check that reads that binding. If a door with no
host should be not applicable instead of a fail, add a scope condition that the related path
exists (for example `@host` is present) before the check.
Examples you can author:
* A door's fire rating must match the wall that hosts it
* If the host wall is fire-rated, the door must be rated. Unrated walls are not applicable, not a
fail (scope on the host rating, then check the door)
* Every door must be hosted on a wall
* Every wall that bounds a room carries a rating (**all**), or at least one of them does
(**any**)
* A door can be checked against a room reached through the host wall: add a second related
object, set **of** to `@host` instead of **this element**, and pick **Bounded room** or
**Room**
You cannot remove a binding while a condition still uses `@name`, or while a later binding
chains from it. Update those references first.
## Map labels before you compare
When a check compares two properties (**Equals property**), 2026.9 can map each side's raw
labels onto shared values before it compares them. This is how `60 MIN` and `1 HR` become the
same rating, and how a `2 HR` wall can require a door of at least `90 MIN`.
Select **Equals property**, pick the other path, then open the comparison control beside it. The
control shows `=` until you add a mapping or change the comparison.
Set **Compare** to **= equals**, **≥ at least**, or **≤ at most**. Under **This value maps** and
**Other value maps**, select **Add mapping**, pick a raw value from the list, and set what it
**maps to**. Unmapped values stay as they are.
Map both sides to the same numbers when the labels should count as equal. Map the other side to a
required minimum and use **≥ at least** when one side must be stricter than the other.
The same mapping works when the other path is on a related object, such as a host wall.
If preview already lists known values, **Seed N known values** on that mapping list fills it so
you do not type every label. Seed one side at a time. The control is hidden when the list is empty
or too large to seed in one step.
## Match a property wherever it sits
A Fire Rating parameter is not always on the same path. Click the path-matching control next to
the path to cycle modes:
* **Exact** (`=`) — the full path from the picker
* **Suffix** (`*.x`) — every property whose path ends with that segment, for example **Fire
Rating**. Every matching value must pass
* **Regex** (`.*`) — a regular expression over the full path
That covers instance and type placements without a separate rule for each path. Path matching is
not available on **Equals property** or **Equals template**. Those operators compare two specific
paths.
## Check a calculated value
Some rules test a number no single property holds, such as a beam's span-to-depth ratio: its length
divided by the depth parsed from a section name like `300x600`. In 2026.9 a rule's scope or check can
test a **computed value** calculated from each object's properties. The usual comparisons apply,
so the threshold stays a value you can edit.
Computed values are created with Speckle Intelligence, available when your workspace plan includes
it. On a self-hosted server, it is available only on Speckle Enterprise Server. For deployment
setup, see [Enterprise license —
Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
Describe the calculation to the assistant to create it.
In the check editor, select **Generate rule**, or ask [Speckle
Intelligence](/next/analytics/intelligence) in **Chat**. Name the inputs, how to combine them,
their units, and the threshold.
Before it offers the rule, the assistant states the range of values, the units, and how many
objects have no value. Confirm the range fits what you asked for.
Accept the rule the assistant proposes, then save the check. The rule shows the computed value
as **Σ** followed by its name.
An example ask:
```text theme={null}
Add a rule: for structural framing, the span-to-depth ratio must be at most 20. Divide Length by the
depth, which is the second number in the type name (300x600 is 300 mm wide and 600 mm deep).
Length is in metres.
```
If you already coloured a model by a calculated value in the 3D viewer, ask the viewer sparkle
**Make this a validation rule: the ratio must be at most 20** to reuse that calculation.
Select the **Σ** subject in the editor to see the description, the unit, and the SQL behind it. The
comparison and threshold stay editable like any other condition.
### Change the calculation
Select the **Σ** subject, then **Edit with AI** to describe the change to the assistant, or **Edit
SQL** to change it yourself. When you edit by hand, you can change the description, the unit, and
the SQL.
Select **Preview** to run the edited SQL in its rule. The preview shows the range of values, how many
objects it checked, and how many have no value. **Apply** stays disabled until the preview has run
on exactly the current SQL, no object has more than one value, and you confirm the description and
unit still match.
The preview checks the calculation runs and gives one value per object. It cannot tell whether the
formula is right. Compare the range with what you expect before you apply.
When you open a failing rule in results, the object table adds a first column with each failing
object's value against the rule, for example `22.5, expected ≤ 20`.
### When there is no value or more than one
An object whose inputs are missing, or do not parse (a type name like `Custom beam`), has **no value**. A
scope condition on the computed value drops it, and the check fails it. The object table shows
`no value, expected ≤ 20`. To treat those objects as not applicable, ask the assistant to check
only objects where the value can be calculated.
The calculation must give one value per object. When it gives more than one for any object the rule
checks, the rule shows **ERROR** with a message such as `3 objects have more than one computed
value`, instead of pass or fail. Ask the assistant to fix the calculation.
Computed values work in a rule's scope and check only, not in the global scope or on a related
object. Each one reads the properties of the model the check runs on.
## Property paths
Always pick property paths from the path picker after a preview model loads. Paths in the current
[Checks](/analytics/data-validation/checks#property-paths) examples, such as
`parameters.Width.value` and `baseline.length`, come from the previous data format and may not
exist on a 2026.9 version.
Typical Revit parameters now sit under `properties.Parameters` (for example type Width under
`properties.Parameters.Type Parameters.Dimensions.Width`). Use the picker for the paths that
exist on your model.
## Versions you can run against
Data Validation preview and saved-check runs need a version in the 2026.9 data format. If preview
does not load, publish a new version after the project has moved to 2026.9. See
[Speckle's data model is changing](/workspaces/data-model-migration).
## FAQ
They are not in 2026.9. Author and save checks instead. A later step is a rollup from Checks
into workspace standards. That is not available yet. If you already built Project standards,
email [hello@speckle.systems](mailto:hello@speckle.systems). See [Project
standards](#project-standards).
The saved rule still uses the previous-format object filter. That filter is not shown in the
2026.9 check editor, and saved checks cannot change rules in place. Create a new check, run
preview, then save. See [Default object scope](#default-object-scope).
No. That step is for the current product. In 2026.9 the object set is already the user-facing
elements. An empty global scope is the correct starting point. Narrow with category, type, or
parameter filters in `WHERE` when you need a tighter rule.
The preview version has no relationship data. The tooltip asks you to re-publish with a current
connector. Publish a new version after the project uses the 2026.9 data format. See [Checks
against related objects](#checks-against-related-objects).
A subject with no related object fails a check that reads that binding. Add a scope condition
that the related path exists if that case should be not applicable. See [When there is no
related object](#when-there-is-no-related-object).
**all** requires every related object to satisfy the check. **any** requires one. Scope filters
on related paths still keep a subject when at least one related object matches. See [Direction,
all versus any, and narrowing](#direction-all-versus-any-and-narrowing).
On the door rule, select **Add related object**, choose **Host element**, then compare the
door's fire rating to the host's fire rating with **Equals property**. Map labels if one side
uses minutes and the other uses hours. See [Map labels before you
compare](#map-labels-before-you-compare).
On an **Equals property** comparison, open the comparison control beside the other path. Under
**This value maps** and **Other value maps**, add a mapping so both labels map to the same
number, such as `60`. Unmapped values stay as they are. See [Map labels before you
compare](#map-labels-before-you-compare).
Click the path-matching control until it shows **suffix** (`*.x`), and set the path to **Fire
Rating**. Every matching value must pass. Click again for regex over the full path. Path
matching is not available on **Equals property**. See [Match a property wherever it
sits](#match-a-property-wherever-it-sits).
The calculation could not read one of the inputs, for example a type name like `Custom beam`
that does not match the expected pattern. Fix the source data, or ask the assistant to handle
that format. See [When there is no value or more than
one](#when-there-is-no-value-or-more-than-one).
Only when a path no longer exists on the new version. Load preview, pick the replacement from
the path picker, and save a new check. Saved checks cannot change rules in place.
# Ask questions about your models with Speckle Intelligence
Source: https://docs.speckle.systems/next/analytics/intelligence
Ask project-scale questions from Chat, ask about the model from the 3D viewer sparkle, and reuse organisation playbooks as skills.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Speckle Intelligence is the assistant in the Speckle web app. You ask in plain language. It reads
the models you have published and replies with answers, charts, and — in the 3D viewer — changes to
what you are looking at.
Use **Chat** in the project sidebar for project-scale questions. It is part of the default project
navigation, so it is available from most screens in a project: the project, models, a model page,
versions, issues, and similar. Use the sparkle in the main 3D viewer when you are looking at models
and want to ask about those loaded models. Reuse organisation playbooks — colour legends, cost and
carbon methods, formulas, house style — as [skills](/next/analytics/intelligence-skills).
There is no equivalent for this Chat surface before 2026.9. For the before-and-after, see
[Compare 2026.8 and 2026.9](/next/whats-new) and
[Speckle's data model is changing](/workspaces/data-model-migration). Dashboards stay on
[Intelligence Dashboards](/analytics/intelligence-dashboards); this page does not cover them.
Speckle Intelligence Chat is available when your workspace plan includes it. On a self-hosted
server, it is available only on Speckle Enterprise Server. For deployment setup, see [Enterprise
license — Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
## How it works
Speckle Intelligence does not upload a Revit, Rhino, Tekla, or other host file to an AI so the AI
can rebuild the model in memory. You publish to Speckle as you do today. The assistant then runs
read-only queries against the Speckle data already stored for those published versions.
That small payload is deliberate. Speckle does not send the whole model into the AI as a giant
blob of text, because that would spend your AI credit — and Speckle's — on tokens the assistant
does not need. It asks for the facts it needs, keeps those results compact, and writes the reply
from them. The goal is to answer from your data without burning budget on a full-model dump.
For versions in the new data format, those queries can cover:
* Object properties on published models
* How parts relate to each other (containment, connectivity, levels, systems, materials)
* Where a model was published from (the source application on the version, and types in the
published data)
* How the project has been updated (who published which version, and what changed between
versions)
* Files in the workspace library, and registered tables such as CSV or XLSX uploads and
warehouse connections (Snowflake, Databricks, Microsoft Fabric), when those exist
The assistant can still be wrong if it skips a query or misreads a result. If the data is not
there, it is instructed to say so rather than invent structure from a host file.
The assistant reads versions in Speckle's new data format. Versions in the old format cannot be
queried, so the assistant says there is no queryable data rather than giving partial answers.
Publish a new version after the project uses the new format, and questions about properties and
about what connects to what or what contains what both work. See [Speckle's data model is
changing](/workspaces/data-model-migration).
This page does not cover how Speckle handles AI providers or data security.
## Where to ask
On day one of 2026.9, Speckle Intelligence has two entry points. They are not the same job.
* **Chat** is in the default project navigation, next to **Browse** at the top of the project
sidebar. Use it from the project, models, a model page, versions, issues, or another project
screen for project-scale questions: the project as a whole, not only the model you have open.
* The sparkle at the top right of the main 3D viewer is for questions about the models loaded in
that viewer.
The **Intelligence** item in the project or workspace sidebar currently opens dashboards, not Chat.
### Ask about the project
Open any screen in the project you want to ask about. You should see **Browse** and **Chat** at
the top of the project sidebar when Chat is available for the workspace.
Select **Chat**. The page title is **Chat**. The empty state reads **Ask about** your project
name, with the prompt to ask in plain language.
Type a question about the project, or pick a suggested starter if one is shown. You can attach a
published model from the composer when the question needs a specific version.
Wait for the reply. You should see a conversation in the Chat history, titled from your
question, and an answer that uses that project's data.
### Ask about the model in the viewer
Open the model in the main 3D viewer. You should see the sparkle control at the top right of the
viewer window when Chat is available.
Select the sparkle. Chat opens beside the model. The empty state reads **Chat with your model**.
The description is **Ask about the models loaded in the viewer, or attach their data to query
it.** The composer placeholder is **Ask about this model…**
Type a question about the models in front of you, or pick a suggested starter. The reply can
change what you see in the viewer as well as answering in the conversation.
If **Chat** or the viewer sparkle is missing, your workspace may not include Speckle Intelligence,
a workspace admin may have turned **AI features** off in workspace settings, or you may need to
choose **Enable AI for this workspace** when the **Enable AI** dialog appears. For the admin
switch, see [Turn AI features off for the
workspace](/next/it-admin/introduction#turn-ai-features-off-for-the-workspace).
## What changes in 2026.9
Today, Speckle Intelligence looks at properties on each object. In 2026.9, Chat queries versions
in the new data format. That is how it reads properties and how parts of a model relate to each
other — what connects to what, and what contains what. Versions still in the old format cannot be
queried.
That is the same change summarised on [Compare 2026.8 and 2026.9](/next/whats-new). Follow
[Speckle's data model is changing](/workspaces/data-model-migration) for dates and what to do next.
## What you can ask
Ask in the same language you would use with a colleague. If you type a question in a given
language, Speckle Intelligence usually keeps replying in that language for the conversation. Use
a [skill](/next/analytics/intelligence-skills) or an [AI
Rule](/next/analytics/intelligence-rules) when the language or dialect must stay fixed even if
someone asks in another language.
In **Chat**, ask at project scale: the project as a whole, not only one open model. Chat can
build a **Report** from a conversation. The viewer sparkle does not create a saved Report. It
answers beside the model with charts and changes to the view. After you save a report, edit sources,
tabs, version comparison, and share links on [Create and share Intelligence
Reports](/next/analytics/reports).
You can share a chat with people on the project so they can view it. They cannot continue that
conversation.
In the **3D viewer**, ask about the model in front of you. Starters can include **What's in this
model?**, **Give me a tour**, **What changed recently?**, and **Color it by category**. If you have
an object selected, you may also see **What is this?**
You can go further than a first overview. Try asks like:
* What models are in this project, and who published recently?
* Compare the two most recent versions and summarise what was added, removed, or changed
* What sits on this level, and what contains those objects?
* What connects to this system?
* Give me a report of quantities by category and level (use **Chat**, not the viewer sparkle)
* How do the models loaded in this viewer relate to each other?
* What is the history of the object I have selected?
* Make this a validation rule: the ratio must be at most 20 (after colouring the model by a
calculated value)
For a full worked example — asking what changed between two versions, then filtering and
colouring the model by how significant each change is — see [Assess model changes with Speckle
Intelligence](/next/workflows/assess-model-changes-with-intelligence).
Model questions need a version in the new data format. Those asks use properties and the
relational layer in the new format.
These are starting asks, not full tutorials. More detailed walkthroughs for Intelligence Chat will
be available soon.
Data Validation has its own assistant in the check editor; that flow stays on the Data Validation
guides.
## Skills
A **skill** is a reusable playbook you invoke with `/skill-name`. Use one for model-aware methods
(colour, cost, carbon, formulas) and for the same kinds of custom instructions you would save in
a generic AI chat tool — tone of voice, meeting notes, non-AEC analysis. Tuned to how your company
works, a skill also spends fewer AI tokens reinventing that wheel on every turn. Create skills in
workspace **Settings**, then **Skills** — including **Draft with AI** when you want Speckle
Intelligence to write the first draft. Arm them in Chat or in the viewer sparkle.
See [Reuse organisation playbooks with Intelligence skills](/next/analytics/intelligence-skills)
for how to create and share a skill, and a catalogue of playbooks teams put in them.
## AI Rules
An **AI Rule** is a standing instruction workspace admins set for every conversation. It applies
without a slash command — currency, language, disclosure, or any must-follow constraint. Members
can read the rules; only admins change them. In Chat, **Workspace rules** shows which enabled
rules apply.
See [Set standing AI Rules for the workspace](/next/analytics/intelligence-rules).
## FAQ
Use **Chat** in the project sidebar for project-scale questions, including building a saved
**Report**. It stays in that navigation on most project screens. Use the sparkle when a model is
open in the 3D viewer and you want to ask about the models loaded there. The sparkle does not
create a saved Report. After you save one, see [Create and share Intelligence
Reports](/next/analytics/reports).
In the product today, **Intelligence** in the workspace and project sidebars opens dashboards.
Speckle Intelligence Chat is a separate surface: **Chat** next to **Browse** in the project
sidebar for project-scale questions, or the sparkle at the top right of the main 3D viewer for
the model in front of you. Dashboards are documented on [Intelligence
Dashboards](/analytics/intelligence-dashboards).
No. You publish to Speckle as you do today, from Revit, Rhino, Tekla, or another supported host.
Intelligence queries that Speckle data. It does not send the host file, or the whole model as a
giant blob of text, to an AI so the AI can rebuild the model from scratch. Keeping what the AI
sees compact is deliberate: it protects your workspace AI credit, and Speckle's cost, instead of
spending tokens on a full-model dump.
Versions in the old format cannot be queried, so the assistant says there is no queryable data
rather than giving partial answers. After the project uses the new format, publish a new
version. See [Speckle's data model is changing](/workspaces/data-model-migration).
Use **New chat** in the Chat history. That starts an empty conversation so the next question is
not mixed with an earlier thread.
No. Viewing a saved report, or changing its interactive inputs, does not spend AI credit. Chat
turns that create or edit the report do. See [Create and share Intelligence
Reports](/next/analytics/reports).
Actions that require **AI credit**, such as conversations, AI-based report editing, and document
parsing, are blocked when the workspace credit has been used up. Stored conversations remain
available. The composer shows that the workspace credit has been used up. To restore access, a
workspace admin must update the workspace plan or contact us.
Put that playbook in a **skill**, share it with **Workspace** visibility, and invoke it with
`/skill-name`. See [Reuse organisation playbooks with Intelligence
skills](/next/analytics/intelligence-skills).
Use an **AI Rule** so the constraint applies on every turn without arming a skill. See [Set
standing AI Rules for the workspace](/next/analytics/intelligence-rules). If you only need that
language for one conversation, ask in that language — Speckle Intelligence usually keeps it.
A workspace admin turns **AI features** off in workspace **Settings**, then **General**. Chat
and the viewer sparkle then disappear for everyone in the workspace until an admin turns the
switch back on. See [Turn AI features off for the
workspace](/next/it-admin/introduction#turn-ai-features-off-for-the-workspace).
# Set standing AI Rules for the workspace
Source: https://docs.speckle.systems/next/analytics/intelligence-rules
Workspace-admin governance for Speckle Intelligence — standing instructions applied to every conversation.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
An **AI Rule** is a standing instruction Speckle Intelligence follows in **every** conversation
in the workspace. You do not type a slash command. Enabled rules shape Chat and the viewer
sparkle on each turn until an admin disables or deletes them.
Use rules for organisation governance: currency, language, safety, disclosure, or any constraint
that must apply even when nobody arms a skill. For opt-in playbooks people invoke with
`/skill-name`, see [Reuse organisation playbooks with Intelligence
skills](/next/analytics/intelligence-skills). For where to ask questions, see [Ask questions about
your models with Speckle Intelligence](/next/analytics/intelligence).
AI Rules appear when the workspace plan includes Speckle Intelligence Chat. On a self-hosted
server, Chat is available only on Speckle Enterprise Server. For deployment setup, see [Enterprise
license — Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
## When to use a rule
Use a rule when the answer must always follow the same constraint — for example always report
costs in EUR, always write Reports in Turkish, never invent missing quantities, or always name
the version before a total.
Do not use a rule for an optional playbook (colour theme, cost method, carbon factors someone
chooses per ask). That is a **skill**. Do not use a rule for a one-off question.
If you only need the assistant to match the language of your question for that conversation, ask
in that language. Speckle Intelligence usually keeps replying in the same language without a rule.
Use a rule when the language must stay fixed even if someone asks in another language.
Rules are workspace governance. Only **workspace admins** create, edit, enable, disable, or
delete them. Every member can open **Settings → AI Rules** and read the instructions. In Chat, a
**Workspace rules** control shows which enabled rules apply to the conversation.
## Create a rule
As a workspace admin:
Open the workspace, select **Settings**, then **AI Rules**. You should see the heading **AI
Rules** and **New rule** when you can manage rules.
Select **New rule**. Set **Name** to a short label such as `Currency` or `Turkish reports`.
Members see this name in Chat under **Workspace rules**.
In **Instructions**, write what Intelligence must do on every turn — for example **Always report
costs in EUR.** or **When creating or editing a Report, write all narrative in Turkish.** Select
**Create**. You should see the rule on the list, enabled by default.
### Enable, disable, or delete
On the AI Rules list, use the switch to **Enable** or **Disable** a rule without deleting it. A
disabled rule shows a **Disabled** badge and stops applying on the next conversation turn.
Use the row menu to edit or delete. Delete asks for confirmation: conversations stop following
that rule on the next turn. Past replies stay as they were.
A workspace can hold up to 25 rules. Enabled rules share a combined instructions budget, so keep
standing text short. Prefer a focused skill for long playbooks.
## What members see in Chat
When at least one rule is enabled, Chat shows a **Workspace rules** control. Open it to see the
enabled rule names and the note that they are set by workspace admins and applied to every
conversation. Admins also see **Manage**, which opens **Settings → AI Rules**.
Rules apply whether or not a skill is armed. A skill adds an opt-in playbook on top; it does not
turn rules off.
## What to put in a rule
Keep rules short and absolute. Good standing constraints:
* Always report costs in the organisation's currency (for example EUR or GBP).
* When creating or editing a **Report**, write all narrative, titles, and labels in Turkish (or
another language). Keep numbers and units from the data.
* Never invent quantities, rates, or carbon factors. If the data is missing, say so.
* Always name the version, the publisher, and the source application before any total.
* Prefer the organisation's words for coordination issues, levels, and packages.
* Do not give legal, safety, or compliance advice beyond what the published model states.
* Keep replies suitable for external clients (no internal codenames, no unreleased rates).
If the instruction is long, optional, or method-heavy (colour legends, factor tables, full cost
methods), put it in a [skill](/next/analytics/intelligence-skills) and keep the rule as the short
must-follow line.
## Rules and skills
* Standing constraint on every conversation — use an **AI Rule**.
* Opt-in playbook with `/skill-name` — use a **skill**.
* Match the language of this conversation — ask in that language (no rule required).
* One-off question — use Chat with no skill armed.
Create rules in [workspace Settings](/next/workspaces/settings) under **AI Rules**. Create
skills under **Skills**.
## FAQ
Only workspace admins. Members and guests can read the rules list and see **Workspace rules** in
Chat. They cannot create, edit, enable, disable, or delete rules.
No. Rules constrain answers; they do not stop conversations. To make Chat and the viewer sparkle
unavailable to the whole workspace, a workspace admin turns **AI features** off in **Settings**,
then **General**. See [Turn AI features off for the
workspace](/next/it-admin/introduction#turn-ai-features-off-for-the-workspace).
Yes. Enabled rules still apply. The skill adds extra instructions for that message on top of the
rules.
No. Opening a saved report does not re-run Intelligence. Rules apply when Chat creates or edits
content. Ask Chat to revise an existing report if you need it rewritten under a new rule. See
[Create and share Intelligence Reports](/next/analytics/reports).
The AI stops following it on the next conversation turn. The rule stays on the list with a
**Disabled** badge so you can turn it back on later.
A workspace can have at most 25 rules. Enabled rules also share a combined instructions length
limit. Shorten or disable other rules, or move long playbooks into a skill.
Use a **rule** when every Report (and every Chat turn you care about) must be in Turkish without
anyone arming a slash command. Use a **skill** when people opt in with `/turkish-reports` for
some asks only. If you only need Turkish for one conversation, ask in Turkish — Speckle
Intelligence usually keeps that language. See [Reuse organisation playbooks with Intelligence
skills](/next/analytics/intelligence-skills) and [Create and share Intelligence
Reports](/next/analytics/reports).
# Reuse organisation playbooks with Intelligence skills
Source: https://docs.speckle.systems/next/analytics/intelligence-skills
Save reusable instructions for Speckle Intelligence Chat, share them across the workspace, and invoke them with /skill-name.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
A **skill** is a reusable playbook for Speckle Intelligence. You write the instructions once —
how your organisation colours a model, how it prices work, which carbon factors to use, which
words to say, or how replies should sound — and anyone who can see that skill pulls it into a
conversation with `/skill-name`. Speckle Intelligence can also **draft the skill for you**:
describe the playbook in plain language, or save a chat that already works.
Skills live on the workspace. They are how you exercise control at organisation scale without
re-explaining the same rules in every chat. A skill tuned to the way your company does things
also spends fewer AI tokens reinventing that method on every turn: the assistant follows the
playbook instead of rediscovering colour legends, cost codes, carbon factors, house lingo, or
tone of voice from scratch.
Anything you would put in a generic AI chat tool as a custom instruction can be a skill here —
brand voice, meeting notes format, translation rules, non-AEC analysis — as well as model-aware
playbooks that use Speckle data. For where to ask questions, see [Ask questions about your models
with Speckle Intelligence](/next/analytics/intelligence).
Skills appear when the workspace plan includes Speckle Intelligence Chat. On a self-hosted server,
Chat is available only on Speckle Enterprise Server. For deployment setup, see [Enterprise license
— Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
## When to use a skill
Use a skill when the same behaviour should happen again: a colour theme, a cost or carbon method,
a formula, a report shape, a glossary, a tone of voice, or any other repeated instruction you
would save in a generic AI assistant. Share it as **Workspace** visibility when the whole
organisation should be able to invoke it. The more the instructions match how your company
already works, the less each conversation spends credit re-deriving that process.
Do not use a skill for a one-off question. Do not use a skill for a standing constraint that must
apply to **every** conversation without anyone typing `/skill-name`. That always-on layer is [AI
Rules](/next/analytics/intelligence-rules). If you only need the assistant to match the language
of your question, ask in that language — Speckle Intelligence usually keeps replying in the same
language without a skill or a rule.
## Create a skill
Workspace **members** can create skills. Guests can invoke a **Workspace** skill in Chat. They
cannot author one, and they usually do not see **Settings → Skills** — they arm skills from the
Chat **/** menu instead. Workspace admins can edit or delete any skill in the workspace, including
someone else's **Private** skill.
You can author a skill three ways. Use Speckle Intelligence when you want a strong first draft;
you always review the form before anything is saved.
### Draft with AI
Open the workspace, select **Settings**, then **Skills**, then **New skill**. You should see the
**Draft with AI** card above the form.
In **Draft with AI**, describe what the skill should do in plain language — for example **Audit
models for missing fire ratings and report the gaps in a table**, or **Colour the model silver
by classification type**. Select **Draft with AI**.
Speckle fills **Name**, **Description**, **Instructions**, and related fields. Nothing is
created until you save. Edit the draft, set **Visibility** to **Private** or **Workspace**, pin
**Knowledge** if the playbook needs a rate card or factor table, then select **Create skill**.
You should see the skill on the Skills list.
Drafting with AI fills the whole form. If you already typed in the fields, Speckle asks **Replace
your edits?** Choose **Draft anyway** only when you want the AI draft to replace what you entered.
Drafting spends workspace AI credit the same way other Intelligence actions do.
### Save a chat as a skill
In **Chat**, when a conversation already behaves the way you want, select **Save as skill**.
Speckle drafts a skill from that thread and opens the create form. Tighten the instructions, set
visibility, pin knowledge if needed, then save. Use this when you discovered the method in Chat
and want the organisation to reuse it.
### Write the form yourself
You can fill the form without a draft. Set **Name** to a slug such as `embodied-carbon` (Chat
uses `/embodied-carbon`), write **Instructions** in the organisation's method and lingo, choose
**Visibility**, then **Create skill**.
### What you can attach
**Knowledge** pins workspace files and data warehouse tables (Snowflake, Databricks, Microsoft
Fabric) to the skill. When the skill is active, Chat can draw on those sources — a rate card, an
EPD library, a carbon factor sheet, a classification map — without you attaching them every time.
A skill can pin up to 20 knowledge sources. Instructions can be up to 8,000 characters.
## Use a skill in Chat
Skills work in project **Chat** and in the 3D viewer sparkle. Arming a skill applies its
instructions to the **next message** you send. The armed chip clears after that send. It does not
rewrite earlier replies, and it does not stay on for the rest of the thread unless you arm it
again.
Open **Chat** in the project sidebar, or select the sparkle in the main 3D viewer. You should
see the composer.
Select the **/** control (**Use a skill**), or type `/` and the skill name. Pick `/skill-name`.
You should see that skill as a chip above the composer, or as `/skill-name` in the message.
Type the question, or pick a conversation starter the skill provides. You should get a reply
that follows those instructions — colour legend, cost codes, carbon method, tone of voice, or
whatever the playbook defines.
If the organisation needs the same playbook on every project, set the skill to **Workspace**
visibility and tell people the slash name. Chat does not run workspace skills automatically. The
**/** menu also links to **Manage skills** or **Create a skill** when you can author skills.
## What custom skills are for
A skill is organisation control you opt into. The catalogue below mixes model-aware AEC playbooks
with the same kinds of custom instructions people save in generic AI chat tools. It is not a
limit on what you can write. If people keep repeating the same method, lingo, formula, visual
rule, or voice, put it in a skill.
### Tone of voice and non-AEC playbooks
* Write every reply in the company's brand voice: plain, formal, client-facing, or internal — and
keep that voice stable across projects.
* Prefer short bullets for directors, and longer technical prose for engineers, as two separate
skills rather than one vague assistant.
* Translate or localise answers into the workspace language, including how the firm spells product
names and place names.
* Structure meeting notes, action lists, or decision logs the way the PMO template already does.
* Summarise chats for email or a board pack with the firm's subject-line and disclaimer rules.
* Explain Speckle data to a non-technical stakeholder without host jargon, or the reverse for a
specialist audience.
* Apply general analysis habits that are not AEC-specific: always show assumptions, always cite
sources, never invent numbers, always end with three next steps.
* Turn a pinned company handbook, style guide, or FAQ workbook into **Knowledge** so Chat answers
HR, IT, or process questions in the approved words.
* Keep a private personal skill for how you like replies formatted, and a workspace skill for the
organisation voice everyone should use with clients.
### Colour and view the model
* Apply a house colour theme — for example a graduated silver or grey palette by classification
type — with clear rules for which host property to read (category, `speckle_type`, type) when
models come from different applications.
* Colour the loaded model with the company's discipline legend, and keep those colours identical
on every project.
* Colour by coordination status using the organisation's red / amber / green meanings, not a
one-off palette.
* Colour by fire rating, acoustic rating, or accessibility category the way the fire engineer
and the architect already talk about those values.
* Isolate MEP systems with the firm's system names and colours, then restore a clean overview
when the walkthrough is done.
* Hide furniture, entourage, and site context for a coordination view that matches the BIM
execution plan.
* Frame typical elevations or a true-north camera so review screenshots look like the office
standard, not a random orbit.
* Colour by the parameter the firm always uses for phase, zone, or work package, even when the
published property name differs between hosts.
* Offer darker / lighter / reverse / revert adjustments in the instructions so reviewers can tune
the theme without rewriting the skill.
* Apply a client-facing colour theme for workshops, and a denser internal theme for design
reviews, as two separate skills.
### Cost, carbon, and quantities
* Price quantities with the company's unit rates, cost codes, and work breakdown structure, and
say those codes out loud in the reply.
* Turn room or space data into the organisation's area standard — NUA, NIA, NLA, GLA, GIA, GEA,
GBA, or the chargeable area the lease uses — with the same definitions every time.
* Apply the firm's measurement rules: wall centreline versus finished face, doors counted in the
wall or excluded, and which voids count.
* Convert published units into the units the commercial team invoices in, and round the way the
cost plan rounds.
* Estimate embodied carbon (for example A1–A3) by matching model quantities to a pinned factor
table, with category fallbacks and an unmatched list instead of silent guesses.
* Separate A1–A3 product carbon from A4 transport and A5 construction when the firm reports that
way, and refuse to blend them.
* Flag missing rates or missing carbon factors instead of inventing a number, and list the
exclusions in the firm's language.
* Pin a rate-card spreadsheet or a live warehouse table as **Knowledge** so the skill reads this
year's prices, not last year's memory.
* Produce a cost or carbon narrative that uses the commercial team's words: elemental, package,
provisional sum, contingency — not generic AI phrasing.
### Language, classification, and house style
* Use the organisation's names for levels, grids, and storeys, and never swap in a host
application's synonym.
* Map host categories and IFC types onto Uniclass, OmniClass, or the firm's own classification,
and quote the code the document controller expects.
* Call coordination issues what the BEP calls them. If the office says "coordination issue", do
not say "clash".
* Keep spelling, units, and date formats on the regional standard the workspace uses (UK
millimetres and DD/MM/YYYY, or US feet-inches).
* Address the client by the name in the appointment, and describe the scheme the way the bid did.
* Prefer the firm's abbreviations (GA, NBS, RIBA stage, LOD) and expand them only when the
audience is external.
* Never rename parameters the way a general assistant might. Repeat the published Speckle
property names the connectors actually sent.
### Formulas and conversions
* Compute steel tonnage from volume and the firm's density, then apply the agreed section
utilisation or connection allowance.
* Derive door, window, and finish schedules with the takeoff formulas the interior package
already uses.
* Convert between host units, SI, and the commercial unit of measure in one place so every chat
does the same conversion.
* Apply percentage complete, earned value, or design maturity rules the project controls team
already uses.
* Include wastage, lapping, and cutting allowances the estimator uses, and show the allowance as
a line, not a silent fudge.
* Normalise type marks, family names, and type catalogues into the firm's type-naming pattern
before totals.
* Recalculate areas when the skill's measurement rule disagrees with a raw property, and say
which rule won.
### Reports, tours, and output shape
* Structure every commercial reply as executive summary, exclusions, then tables — the same
skeleton the director reads on Friday.
* Build a saved **Report** from Chat that already uses the firm's rate card and carbon method,
then share that report rather than the raw thread. See [Create and share Intelligence
Reports](/next/analytics/reports).
* Lead a client workshop view sequence: site, envelope, typical floor, services, then risks.
* Produce a quantity survey in the column order the cost plan template uses.
* Write a change commentary between two versions using the firm's added / omitted / revised
vocabulary.
* Keep charts in the organisation's preferred breakdown (by level, by Uniclass, by package), not
whatever the model tree happens to be.
### Knowledge files and live data
* Pin the approved rate card, carbon factor workbook, or product library so Chat does not scrape
a random file from the conversation.
* Pin a warehouse table for live commercial data so the skill stays current when finance updates
the sheet.
* Pin a mapping table from host category to company cost code, and instruct the skill to fail
closed when a row is missing.
* Pin specification extracts or employer's information requirements so answers cite the
appointment, not generic good practice.
* Pin a legend image or colour dictionary if the visual standard is easier to describe from that
file than from prose alone.
### Coordination, quality, and safety
* Assess BIM and tabular data readiness for design review, analytics, cost or carbon joins,
compliance, handover, or asset reuse — and report gaps in the firm's checklist order.
* Turn readiness findings into a versioned conditioning **manifest**: proposed transformations
that are explainable, testable, attributable, and reversible, without mutating source data from
the skill itself.
* Preserve an auditable path from every headline finding to the smallest set of calculations or
comparisons needed for the decision (a BIM benchmark style skill).
* Review a version the way the BIM manager reviews it: required parameters present, naming
pattern held, dummy types called out.
* List coordination risks in BEP order (structure vs services, then envelope, then interiors).
* Call out fire-stopping, accessibility clearances, or maintenance access with the language the
compliance lead uses.
* Refuse to answer from versions still in the old data format, and tell the user to publish after
the project uses the new format. See [Speckle's data model is
changing](/workspaces/data-model-migration).
* Always name the version, the publisher, and the source application before any total, so a
forwarded answer is auditable.
### Role, region, and programme
* Give the quantity surveyor package-based totals, and give the architect room-based totals, as
two skills rather than one vague assistant.
* Give the structural engineer utilisation and mass, and give MEP flow, load, and system
membership, without mixing those audiences.
* Switch UK Approved Document language, US code language, or another region's citations by
workspace or by skill, not by hoping the model guesses.
* Hold a RIBA stage, AIA phase, or in-house gate in the instructions so the assistant does not
recommend deliverables that stage does not include.
* Keep a private personal skill for how you like replies formatted, and a workspace skill for
what the organisation allows everyone to use.
If none of those headings fit, write the skill anyway. Model-aware AEC methods and generic chat
habits both belong here. The test is repetition: the organisation wants the same control next
week, on the next project, in the same words and the same voice.
## Skills and AI Rules
* Opt-in playbook people invoke with `/skill-name` — use a **skill**.
* Standing constraint on every conversation — use an [AI Rule](/next/analytics/intelligence-rules).
* Match the language of this conversation — ask in that language (no skill required).
* One-off question — use Chat with no skill armed.
Skills do not replace [workspace Settings](/next/workspaces/settings). Create and share skills
from **Settings**, then **Skills**. Create standing rules under **AI Rules**. Invoke skills from
Chat; rules apply on every turn.
## FAQ
A skill is opt-in. Someone must arm it with `/skill-name` or **Use a skill**. For a rule that
must apply to every conversation in the workspace, use [AI
Rules](/next/analytics/intelligence-rules).
Yes, usually. If you ask in a given language, Speckle Intelligence keeps replying in that
language for the conversation. Use a skill or an AI Rule when the language or dialect must stay
fixed even if someone asks in another language.
Only the owner, and workspace admins. Other members do not see it in the picker. A skill you
want the organisation to use must be **Workspace** visibility.
Guests can invoke a **Workspace** skill in Chat with `/skill-name`. They cannot create, edit, or
delete skills, and they usually do not see **Settings → Skills**. Workspace members can create
skills. Admins can edit or delete any skill in the workspace.
**Skills** in the Settings sidebar appears when you can create skills (workspace member, Chat on
the plan, and [**AI features**
on](/next/it-admin/introduction#turn-ai-features-off-for-the-workspace) in workspace settings).
Guests still arm shared skills from Chat. If Chat itself is missing, see [Ask questions about
your models with Speckle Intelligence](/next/analytics/intelligence).
Those conversations continue without it. The history stays. Later messages no longer receive
those instructions.
On the skill, use **Knowledge**. Pin a workspace file or a warehouse table. When the skill is
active, Chat can query that source. Do not rely on pasting the same sheet into every thread.
Arming a skill does not spend credit by itself. Sending the next Chat message does, the same as
any other turn. A company-tuned skill usually spends less on that turn than reinventing the
method in free-form chat. **Draft with AI** and **Save as skill** spend credit when Speckle
drafts the form. Viewing a saved report still does not spend credit — see [Create and share
Intelligence Reports](/next/analytics/reports).
Yes. On **New skill**, use **Draft with AI** and describe the playbook. Or in Chat, use **Save
as skill** after a conversation that already works. You always review and edit before **Create
skill** saves anything.
# Create and share Intelligence Reports
Source: https://docs.speckle.systems/next/analytics/reports
Edit report layouts, change sources and knobs, use tabs, compare versions, and revoke share links.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
A **Report** is a saved artefact under **Reports** in the project sidebar. Speckle Intelligence
**Chat** builds it from a conversation. The viewer sparkle does not create a Report.
Open the report to edit the layout, change sources and knobs, add tabs, compare versions, and share
a link. Viewing a report, or changing its interactive inputs, does not spend AI credit. Chat turns
that create or edit the report do.
For asking questions and building the first draft in Chat, see [Ask questions about your models with
Speckle Intelligence](/next/analytics/intelligence). This page covers the report after you save it.
Speckle Intelligence Reports are available when your workspace plan includes Intelligence. On a
self-hosted server, they are available only on Speckle Enterprise Server. For deployment setup,
see [Enterprise license —
Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
Reports and [Dashboards](/next/analytics/dashboards) are currently distinct products. Dashboards
are available on all plans and are built manually; Reports are generated from an Intelligence Chat
conversation and require Intelligence. How the two develop alongside each other is something we're
actively working out — if you have a Dashboards workflow that shapes what you'd want from Reports,
[we'd like to hear it](https://speckle.community).
## Create a report
In **Chat**, ask Speckle Intelligence to produce the analysis you want, then save the result as a
**Report**. The sparkle in the 3D viewer cannot create a saved Report.
Open **Reports** in the project sidebar to find saved reports. The breadcrumb reads **Reports**, then
the report name. If the report is missing, the page reads **Report not found.**
## Open and edit
Select a report to open it. Edits stay local until you save. There is no auto-save.
Use **Add** to insert **Chart**, **KPI**, **Table**, **Viewer**, or **Text**. With a section
selected, the widget lands in that section. Otherwise Speckle appends a new section. **Add** opens
Chat and pins the new block so the next message can target it.
The toolbar can show **Saving…**, **Unsaved changes**, **Saved**, or **Save failed**. Use **Save**
to keep changes, or **Discard**. Leaving with unsaved edits prompts **Leave without saving?**
The toolbar also offers **Undo** and **Redo** for layout edits made in the editor. Undo does not
apply to AI turns — those changes land as a new edit state you can undo or discard.
**Share** stays disabled while the report is unsaved. The tooltip reads **Save or discard your
changes to share this report.**
The **⋯** menu (**More actions**) offers **Open source chat** when you created the report from Chat
and you are the creator, plus **Rename** and **Delete**. Delete cannot be undone.
### Properties panel
Clicking any element in the editor opens the **Properties** panel on the right. What you see depends on what is selected.
**Section** — Set **Title**, **Show title**, **Subtitle**, and **Show subtitle**. Drag the section
using the grip in its menu. Convert a plain section to a tabbed section with **Add tabs**, or add
another tab to an existing tabbed section with **Add tab**.
**Row** — Set **Height** (in pixels) to control how tall the row renders. A row can contain
multiple cells side by side.
**Widget** — Set a **Title** (required) and optional **Description**. For charts, you can also
switch the **Chart type** between bar, line, and scatter — available on AI-generated charts only,
not hand-authored ones. Chart **Labels** lets you rename what the chart shows (axis labels, series
names) without affecting the underlying values; leave a field empty to keep the original.
Use Chat on the report page to ask the AI to create or rewrite content — those turns spend AI
credit. Direct edits in the Properties panel do not.
## Change sources and knobs
Reports have two **Inputs** surfaces. Name them both.
### Source chips
The header strip reads **Inputs · N sources** (or **1 source**). Each chip shows a model, version,
project, and alias, or a data source.
When you can edit, open **⋯** on a chip for **Change version…** (dialog title **Change version —
**), **Swap model…**, and **Revert to saved** when the working copy differs from the last
save.
### Section knobs
A section can show **Inputs (n)** with knobs such as a rate or occupancy. **Reset** restores each
knob to its saved default. A knob in one section can drive widgets in another.
Changing knobs does not spend AI credit. Ask Chat to create or rewrite the report if you need new
structure.
## Use tabs
A section can be **tabbed**. Each tab has its own row stack of widgets.
In **Properties** for that section, use **Add tab** or **Add tabs**. Those controls are not in the
widget **Add** menu. Rename a tab by editing its label inline.
## Compare versions
There is no separate **Version diff** button. Bind two source aliases — two versions of one model,
or two models — from the **Inputs** strip. Widgets and Chat can compare those aliases.
You cannot remove a source that a comparison widget still uses. Change the version on a chip to
retarget the comparison.
## Share and revoke a link
Select **Share** to open **Share report**. Access uses the same three rungs as Chat sharing:
* **Private** — only the owner
* **Project** — anyone in the project can open and view. The description reads **Anyone in
can open and view this report.**
* **Public** — a link anyone can open without signing in
Public links warn that anyone with the link can view the report — including the model data behind
its sections. Edits to the report update what the link shows and can read. No sign-in is required.
The public token is scoped to the versions the report's sources bind. Speckle shows the raw token
**once** when you create the link. Optional password and expiry apply at create time.
Turning project sharing off does **not** revoke public links. Those links are independent. Revoke a
public link from **Share report**, or from **Project settings** → **Tokens**.
If you are not the owner, the dialog reads **You're viewing a report someone shared. Only its owner
can change sharing.**
Save before you share. Unsaved edits block **Share**.
Do not rely on switching the report to private to kill a public URL. Revoke the public link.
## FAQ
No. Viewing a saved report, or changing its interactive inputs, does not spend AI credit. Chat
turns that create or edit the report do.
No. Only **Chat** in the project sidebar can build a saved **Report**. The sparkle answers
beside the model. See [Ask questions about your models with Speckle
Intelligence](/next/analytics/intelligence).
Project visibility and public links are independent. Turning project sharing off does not revoke
public links. Revoke the link from **Share report** or **Project settings** → **Tokens**.
Save or discard unsaved changes first. The tooltip reads **Save or discard your changes to share
this report.** You also need permission to share.
Editors with permission see **Add**, **Save**, **Discard**, and source **⋯** actions. Viewers
can open the report and change knobs when inputs exist. Only the owner changes sharing.
Non-owners see **You're viewing a report someone shared. Only its owner can change sharing.**
# Sync models from Trimble Connect
Source: https://docs.speckle.systems/next/connectors/cloud-integrations/trimble-connect
Connect Trimble Connect to Speckle for automatic model synchronization.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Beta
Connect Trimble Connect to Speckle for automatic model synchronization. Pick files from your Trimble
Connect projects, and every new version uploaded to Trimble Connect becomes a new version of a
Speckle model — no manual re-uploads.
The Trimble Connect integration is available on the **Enterprise** plan. To use it on another
plan, [talk to
sales](mailto:sales@speckle.systems?subject=Trimble%20Connect%20Integration%20request).
## How it works
* **Browsing is personal.** You sign in to Trimble Connect with your own Trimble ID to browse
projects, folders, and files. Your Trimble login stays in your browser — Speckle never stores it.
* **Background sync uses a service account.** Trimble Connect does not offer application-level
(machine-to-machine) access, so each Speckle workspace connects one Trimble account as its
**service account**. Speckle encrypts that account's credentials, refreshes them automatically,
and uses them to download new file versions in the background.
* **Changes are detected by polling.** Trimble Connect has no webhooks, so Speckle checks your
synced files for new versions every 5 minutes.
## Prerequisites
* A Speckle workspace on the **Enterprise** plan, and **workspace admin** rights to set up the service
account (any workspace member can browse and create syncs afterwards)
* A Trimble Connect account with access to the projects you want to sync
* A Trimble account to act as the **service account** — use a dedicated account (for example
`speckle-sync@yourcompany.com`) rather than a personal one, so background sync does not break when
someone leaves the team
* The service account must be **invited to every Trimble Connect project** you plan to sync (any
role that can view and download files)
## Connect the service account
Workspace admin only. Do this once per workspace.
Open any project in your workspace. In the project sidebar, go to **Integrations** → **Trimble
Connect**.
In the header, select **Connect service account**. Trimble's sign-in page opens. Sign in with
the **service account** (not your personal account) and approve access.
You return to Speckle. The header shows **Background sync:** with the service account email and
a green indicator.
Speckle encrypts the service account's credentials before storing them. They are used only for
server-side file downloads and background synchronization, and are refreshed automatically so they
never expire from inactivity.
Trimble may skip the login screen if you are already signed in to Trimble in that browser. When
connecting the service account, Speckle always forces the sign-in screen so you can pick the right
account.
## Connect and sync files
Any workspace member can do this after the service account is connected.
Go to **Integrations** → **Trimble Connect** and select **Connect to Trimble**. Sign in with
your own Trimble ID.
Choose a **Trimble Connect project** from the selector in the header. Projects from all Trimble
Connect regions are listed; the region appears next to each project name. Browse the folder tree
and select the files you want to sync. Use search and the source filter to narrow the list.
For Revit files, optionally set a **view name** and **reference point** in the selected-files
panel. See [Revit options](#revit-options).
Select **Create sync**. Each synced file gets its own Speckle model, named after the file and
its folder path. The first version imports immediately. After that, every new version uploaded
to Trimble Connect is picked up automatically.
## Keep models in sync
* Speckle polls your synced folders **every 5 minutes**. A new file version in Trimble Connect
typically appears as a new Speckle version within 5 minutes plus conversion time.
* The **Syncs** panel on the Trimble Connect page shows every sync in the project, its latest
status, and links to the Speckle model.
* You can **pause** and **resume** a sync from the Syncs panel. Paused syncs keep their existing
versions; they stop watching for changes.
* If a synced file is moved or deleted in Trimble Connect, Speckle pauses the sync after a few
failed checks and emails the sync's creator.
## Revit options
### View name
By default, all elements of a Revit model are imported. To import only the elements visible in a
specific 3D view, enter the view's exact name (case-sensitive) when creating the sync.
### Reference point
Choose which Revit coordinate origin the model is aligned to: **Internal Origin** (default),
**Project Base Point**, or **Survey Point and Shared Coordinates**. Use the same setting across
models that need to align in Speckle.
## Supported file formats
In default 2026.9 mode, cloud sync supports the same file formats as file uploads. For available
formats and compatibility-mode restrictions, see
[Supported formats by upload mode](/next/connectors/file-uploads#supported-formats-by-upload-mode).
## FAQ
Yes. Speckle only reads and downloads files from Trimble Connect. It never writes to, modifies,
or deletes anything in your Trimble Connect projects.
Speckle checks for changes every 5 minutes, so expect the new version within 5 minutes plus the
time it takes to convert the file. Speckle syncs the file version current at each check;
versions superseded between checks, or whose sync fails, are not synced later. See [Sync
guarantees](/it-admin/integrations/introduction#sync-guarantees).
Autodesk lets an application access files with its own credentials. Trimble Connect currently
only supports user-level access, so background downloads run as a real Trimble account. Speckle
stores that account's credentials encrypted and rotates them automatically. Trimble has
application-level access on its roadmap; once available, the service account step will go away.
Workspace admins only. Browsing and creating syncs is open to all workspace members.
Yes. Background downloads run as the service account, so it needs access to each project you
sync. Files in projects it cannot access fail to download.
Existing Speckle versions are unaffected, but background sync stops: syncs pause on their next
check and their creators are emailed. Reconnecting a service account resumes them automatically.
Two common causes: the file was moved, renamed, or deleted in Trimble Connect (Speckle pauses
after several failed checks), or the service account's access was revoked or its login expired.
Check the file in Trimble Connect, then reconnect the service account if needed and resume the
sync.
Make sure the Trimble account you signed in with is a member of that project. Projects from
every Trimble Connect region are listed, so region is not the issue.
Yes — create a sync for the file in each Speckle project. Each is tracked independently.
Yes. Deleting the model removes its sync. The file in Trimble Connect is untouched.
## Getting help
Having trouble with the setup? Contact support or ask in the
[Speckle Community forum](https://speckle.community/).
# File uploads in 2026.9
Source: https://docs.speckle.systems/next/connectors/file-uploads
Changes to file uploads in 2026.9, including supported formats, Tekla Structures, Solibri and Vectorworks support, and legacy/compatibility mode, which ends on 1 November 2026.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Check your workspace’s upload mode before uploading a file in 2026.9. Some formats require
legacy/compatibility mode, which ends on **1 November 2026**. Tekla Structures uploads are available
where enabled.
## Breaking change: supported file formats
In 2026.9, the default file upload path no longer accepts **STL**, **3MF**, **3DS**, **AMF**,
**DXF**, **X**, **E57**, **IGS/IGES**, **FBX**, **PLY**, or **SLDPRT**. Earlier upload-format
lists do not apply to this path. **These formats remain supported for direct uploads in
legacy/compatibility mode only until it ends on 1 November 2026.** Move affected workflows to a
supported format before then.
This affects both drag-and-drop uploads in the web app and uploads through the API. Existing
models stay readable; the change affects importing new files and uploading new versions.
## Supported formats by upload mode
Availability depends on the conversion services enabled for your workspace.
In default 2026.9 mode, cloud file sync supports the same file formats as file uploads.
| Format | Extensions | Default 2026.9 mode | Legacy/compatibility mode |
| - | - | - | - |
| IFC | .ifc | Supported | Supported |
| Revit | .rvt | Supported | Supported |
| Navisworks | .nwc, .nwd | Supported | Supported |
| AutoCAD | .dwg | Supported | Supported |
| Rhino | .3dm | Supported | Supported |
| Wavefront | .obj | Supported | Supported |
| SketchUp | .skp | Supported | Supported |
| STEP | .step, .stp | Supported | Supported |
| MicroStation | .dgn | Supported | Not supported |
| Vectorworks | .vwx | Supported | Not supported |
| Solibri | .smc | Supported | Not supported |
| Tekla Structures | .tekla, .db1 | Where enabled | Not supported |
| Other retained formats | .stl, .3mf, .3ds, .amf, .dxf, .x, .e57, .igs, .iges, .fbx, .ply, .sldprt | Not supported | Supported |
In legacy/compatibility mode, cloud sync supports only Revit, Navisworks, AutoCAD and IFC files.
## Additional format: Tekla Structures
2026.9 adds support for Tekla Structures model packages (`.tekla`) and model databases (`.db1`)
on deployments where Tekla uploads are enabled. This is not enabled by default; confirm
availability with Speckle before relying on it for your workflow.
Use the complete model folder when uploading a Tekla model to preserve profiles, shapes, and materials.
A `.tekla` package contains the model folder and its supporting catalogs. A `.db1` file alone can
be converted, but missing catalogs can reduce profile, shape, and material fidelity.
## Upload a Vectorworks file
If your design lives in Vectorworks, you can upload the `.vwx` file to publish it without opening
Vectorworks or installing its connector. For the steps, see
[Upload a file](/connectors/direct-uploads/introduction#upload-a-file). The
[Vectorworks connector](/connectors/third-party/vectorworks) publishes the objects you select; an
upload publishes the 3D content of every design layer.
### Files that convert
Speckle reads files last saved in Vectorworks 2021 through 2027. Files from other releases are not
supported yet.
### What is published
* 3D objects on every design layer, including design layers hidden in the document, grouped by
story, layer, and type.
* Each object's name, type, object properties, and IFC properties. An object whose 3D geometry
Speckle cannot read arrives with its properties and no geometry.
These parts of the document are not published:
* Objects in a class that is hidden in the document.
* Sheet layers.
* 2D-only objects, such as callouts, data tags, and section lines.
Speckle follows the class visibility saved in the file. To include a hidden class, make it visible
and save the file before you upload it.
### If a Vectorworks upload fails
| Message | What to do |
| - | - |
| Vectorworks file version *number* is not supported | Contact Speckle support. |
| This is not a supported Vectorworks document | Check that the file is a Vectorworks document, not another file renamed to `.vwx`. |
| This Vectorworks document uses an unsupported byte order | Contact Speckle support. |
## Upload a Solibri file
Upload a Solibri `.smc` file to publish the whole federation saved in it, without opening Solibri
or installing a connector. You do not need the original IFC files alongside it. For the steps, see
[Upload a file](/connectors/direct-uploads/introduction#upload-a-file). After conversion, open the
model in Speckle to view its geometry and inspect its properties.
### Files that convert
Files saved in Solibri 9.12 and 26.6 have been verified. Speckle attempts to read `.smc` files from
other releases, but their compatibility has not been verified. `.smv` and `.smct` files are not
supported.
### What is published
* The saved federation, grouped by source model and its spatial hierarchy.
* Component geometry with original colours and transparency.
* Source properties, Solibri-derived properties, and saved classification results, including
group, zone and system memberships.
These parts of the document are not published:
* Rule sets and checking results.
* Presentations.
* Issues and BCF data.
An upload reads the data saved in the file; it does not run Solibri checks or re-evaluate
classifications.
Solibri uploads require the default 2026.9 upload mode. Workspaces using legacy/compatibility mode
cannot upload `.smc` files.
### Recover missing Solibri geometry
For the closest match to Solibri's 3D view, save the file with **Save 3D Representations with Model**
enabled under **Settings > 3D** in Solibri. Let the 3D view finish drawing before saving.
When the file has no saved 3D representation for a component, Speckle tries to rebuild its geometry.
This takes longer and can produce approximate geometry. Components that cannot be rebuilt retain
their properties but have no geometry. Open the file in Solibri, enable the setting, let the 3D
view draw, save and upload it again.
### Recover a missing Federated Floor property
A component's **Federated Floor** property is included only if Solibri computed and saved it.
To populate it, open the file in Solibri, group the model tree by **Locations**, and use
**Save As** before uploading again. Some components may still have no federated floor.
### If a Solibri upload fails
If the upload fails, check that the file opens in Solibri and save it again before retrying. Contact
Speckle support if it still fails.
## Update an affected workflow
If your workspace uses legacy/compatibility mode, you can continue uploading the affected
formats until compatibility mode ends on **1 November 2026**. Before moving to the default 2026.9 mode,
export to a supported format or publish through a supported desktop connector. Check the
resulting model before switching your workflow: uploads and connector publishing can produce
different geometry and properties. See [Uploads and connectors produce different
datasets](/connectors/direct-uploads/introduction#uploads-and-connectors-produce-different-datasets).
For the upload steps, see [Upload a file](/connectors/direct-uploads/introduction#upload-a-file).
For programmatic uploads, see [Uploading IFC and other files](/developers/api/guides/file-uploads).
Use the supported formats on this page for 2026.9.
## Temporary compatibility option
Legacy mode, also called compatibility mode, retains direct upload support for the affected
formats when the older conversion service is enabled. Ask Speckle to confirm availability for your workspace. It ends for every workspace on **1 November 2026**. This exception applies
to direct uploads, not cloud sync.
Compatibility mode affects the entire workspace's upload and sync routing; it is not a per-file
fallback. **DGN uploads are not supported in compatibility mode.**
For availability, the **1 November 2026** end date, and how to request it, see
[Compatibility mode for ACC sync and uploads](/next/it-admin/compatibility-mode).
# Explore what an object is connected to
Source: https://docs.speckle.systems/next/connectors/grasshopper/explore
Use the Explore node to find the level, room, host, or definition behind a Speckle Object in Grasshopper.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
When you load a model you get the objects themselves: geometry, properties, materials. The model
also records how those objects relate to each other. Which level a wall sits on, which room it is
in, which block definition an instance uses, what it is hosted on.
The **Explore** node surfaces those connections. Plug in any Speckle Object and it works out which
relationships exist for that object, then creates an output for each one.
Explore supports versions published with 2026.9 onwards, loaded with the current **Load** node.
Versions published before then do not record these connections, so there is nothing for Explore to
show. See [Grasshopper in 2026.9](/next/connectors/grasshopper/introduction#load).
## Use Explore
Inputs:
1. Add a **Speckle Object** input of the thing you want to explore. Speckle Objects, Speckle
Geometry, Block Instances, and Collections are all accepted.
Outputs:
1. An output is created for each relationship the model records for that object. The set of outputs
changes with what you plug in, so a Revit object and a Rhino object produce different ones.
## What you can expect to find
The outputs depend on the source application, because different applications record different
things. Some examples:
* A wall gives you its level, the rooms it bounds, its type properties, and its sub-elements.
* A block instance gives you its definition, and the other placements using that same definition.
* A whole-model Collection gives you every level and its elevation.
* A structural member from ETABS gives you its analysis results.
Explore does not repeat what the object already carries. Material, colour, and Collection path are
already set on the object when it loads, so they are not shown again here.
## Work with analysis results
Structural results come out as several outputs of the same length, one per column of the results:
result type, load case, component, station, step, and value. Each row lines up across all of them.
Use the normal list tools to group them. **Sift Pattern** or **Member Index** on the load case
output pulls out just the results for one case.
Outputs only appear for the columns your model actually uses, so a result type without a station
does not produce a station output.
## Objects Explore cannot read
Some objects have no connections to show:
* Objects from a version published before 2026.9. This is by far the most common reason, because
older versions do not record connections.
* Objects loaded with a **Load (legacy)** node. See [Upgrade Grasshopper to
2026.9](/next/connectors/grasshopper/upgrading).
* Objects from inside a block definition, rather than a placed instance.
* Objects that were never loaded from Speckle.
In these cases Explore shows a remark on the canvas explaining why, and produces no outputs.
Republish the model with the current **Publish** node to create a version Explore can read.
## FAQ
Explore reads a local copy of the model that your machine may have cleared. Reload the model and
Explore picks up its connections again.
The usual cause is a version published before 2026.9, or a **Load (legacy)** node. Republish
with the current Publish node, then load with the current Load node.
# Grasshopper in 2026.9
Source: https://docs.speckle.systems/next/connectors/grasshopper/introduction
Node names, Explore, and extra objects in the 2026.9 Grasshopper connector.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
In 2026.9 the Grasshopper connector keeps existing scripts working. Nodes already on your canvas are
renamed and hidden from the ribbon. They are not deleted, and no wires break.
This page covers only what changed. For publish and load today, see
[How to use Speckle for Grasshopper](/connectors/grasshopper/grasshopper). For the step-by-step
script upgrade, see [Upgrade Grasshopper to 2026.9](/next/connectors/grasshopper/upgrading).
## What changed
* **Load** and **Publish** in the ribbon are the current nodes. Nodes you already placed are renamed
**Load (legacy)** and **Publish (legacy)**. They still run. The same rename applies to **(Sync)
Load** and **(Sync) Publish**.
* **Speckle Data Object** is renamed **Speckle Object**. It is the same node with the same ports.
Existing scripts keep working. Its **Geometries** input does not accept Block Instances, because
a Block Instance is already its own object.
* The current **Speckle Geometry** node has no Name or Properties inputs. Name and properties live
on the **Speckle Object** that contains the geometry. Geometry nodes already on your canvas keep
those inputs and are renamed **Speckle Geometry (legacy)**.
Existing scripts keep working on the renamed nodes. You only need the current Load node if you want
Explore, or if you are loading a version a teammate published with the current Publish node.
## Publish
Versions from the current **Publish** node need an upgraded connector to read. **Publish (legacy)**
can still be read by older connectors. Confirm collaborators have upgraded before you switch. The
two nodes are independent of Load.
## Load
The current Load node can return more objects than before, including property-only elements such as
Revit rooms, levels, and areas. Materials and colours arrive on each object. The **Proxies** output
is not on the current Load node. Model-wide properties such as project information arrive on a
**Properties** output. The current Publish node has a matching optional **Properties** input.
Name and properties live on the **Speckle Object**, not on geometry. A wall is one thing with one
name and one set of properties, even when it has three meshes. If each mesh carried its own copy,
summing a volume across the model would count that wall three times. To set a name or properties,
wire geometry into a **Speckle Object**.
**Explore** is a new node. After you load a 2026.9 version with the current Load node, it surfaces
relationships such as level, room, host, and block definition. See [Explore what an object is
connected to](/next/connectors/grasshopper/explore).
Plug in a Speckle Object, Speckle Geometry, Block Instance, or Collection. Explore creates an output
for each relationship the model records for that object. The set of outputs changes with what you
plug in.
Explore does not repeat what the object already carries. Material, colour, and Collection path are
already set on the object when it loads.
Explore supports versions published with 2026.9 onwards, loaded with the current Load node.
Versions published before then do not record these connections, so there is nothing for Explore to
show. Objects loaded with **Load (legacy)** also produce no Explore outputs. If Explore reports
that nothing is cached for a model that loaded correctly earlier, reload the model.
## FAQ
Wrap your geometry in a **Speckle Object** and set the properties there. If you assign a
**builtInCategory** property, Revit loads it as a generic model with that category. There is
currently no way to create elements that load as native walls or beams in BIM or structural
applications.
No. Nodes are matched by identity, not by name, so renaming them has no effect on your files. No
node turns into a placeholder and no wire is dropped.
The usual cause is a version published before 2026.9, or a **Load (legacy)** node. Republish
with the current Publish node, then load with the current Load node.
It does not. The node in your script is **Speckle Geometry (legacy)** and keeps all of its
inputs. The **Speckle Geometry** in the ribbon is a new node without Name and Properties.
This is a known issue in the 2026.9 viewer. Mesh colours and colours set with the **Color**
input are published correctly, but the viewer does not draw them yet. Loading the model back
into Grasshopper or Rhino keeps the colours. See [3D viewer in
2026.9](/next/3d-viewer/introduction#open-a-model).
# Upgrade Grasshopper to 2026.9
Source: https://docs.speckle.systems/next/connectors/grasshopper/upgrading
What 2026.9 changes for Grasshopper, and how to move an existing script across.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Existing scripts keep working. Nothing on your canvas is deleted. This page is the mechanical path
onto the current Load and Publish nodes.
For publish and load today, see [How to use Speckle for
Grasshopper](/connectors/grasshopper/grasshopper). For node names, Explore, and extra objects, see
[Grasshopper in 2026.9](/next/connectors/grasshopper/introduction). To use Explore after you
switch, see [Explore what an object is connected to](/next/connectors/grasshopper/explore).
Nothing is removed from your canvas and no wires break. The nodes you already have are renamed and
hidden from the ribbon, not deleted.
## When to switch
| If you... | Then... |
| - | - |
| Have scripts that work and you are not changing them | Nothing to do. Leave them alone. |
| Are building a new script | Use the current nodes. The ribbon only offers those. |
| Want the Explore node | Upgrade the script's Load node. |
| Load a version published by an upgraded teammate | Upgrade the script's Load node. |
## Before and after
| On your canvas | After you switch |
| - | - |
| **Load** / **Publish** | Unchanged nodes. They support 2026.9 onwards. |
| **Load (legacy)** / **Publish (legacy)** | Your existing nodes, renamed. They still work, and they are hidden from the ribbon. |
| **Speckle Data Object** | Renamed **Speckle Object**. Same node, same ports. |
| **Speckle Geometry (legacy)** | Your existing Speckle Geometry node. It still works, and it is hidden. |
| **Speckle Geometry** | New. Geometry, colour, and material — no Name or Properties. |
The same rename applies to **(Sync) Load** and **(Sync) Publish**.
| Output | **Load (legacy)** | **Load** |
| - | - | - |
| **Collection** | Data Objects and geometry | Speckle Objects and geometry |
| **Properties** | Model-wide properties | Model-wide properties |
| **Proxies** | Materials, colours, levels, groups | Not available |
## How to switch
Place a **Load** node from the ribbon and move your Speckle Model wire onto it. You should see
the same **Collection** output as before.
Check that nodes reading that Collection still resolve. Objects come through as **Speckle
Objects** rather than Data Objects — the same shape under a new name — so most wires carry
straight over.
Remove the **Load (legacy)** node once nothing is wired to it. The definition should now load
through the current node only.
Confirm collaborators have upgraded first. Switching Publish affects people who load your model,
not just you. If you are not sure, leave Publish on the legacy node and upgrade Load only.
If your script publishes and then reloads in the same definition, use the current nodes for both.
A version created by the current **Publish** node cannot be read by a **Load (legacy)** node.
## Messages you might see
| Message | What it means |
| - | - |
| This Load component is deprecated | You are on a legacy node. Nothing is wrong. Upgrade when convenient. |
| This version was published before Speckle 2026.9.0 | An older version loaded. Grouping and collection paths may differ. |
| This version needs the current Load component | A legacy node hit a version it cannot fully read. Upgrade the node. |
| Versions published by this component can be read by older connectors | A reminder from **Publish (legacy)**. Check collaborators before switching. |
| The name or properties on the incoming geometry were replaced | The Speckle Object's name and properties win. Set them on the object. |
| Replacing the geometry dropped its properties | Swapping a shape does not carry properties across. Use a Speckle Object. |
## FAQ
No. Nodes are matched by identity, not by name, so renaming them has no effect on your files. No
node turns into a placeholder and no wire is dropped.
You can — it is in the same place it always was. The node named **Load (legacy)** is the hidden
one, and the **Load** in the ribbon is the current node.
It is now called **Speckle Object**. Same node, same ports, same behaviour. It no longer shows a
deprecation message on every solve.
Yes. Copy and paste still works. Replace it with the current node so the script does not carry
the older behaviour forward.
No. They are independent. Upgrading Load only is a good first step, because it affects nobody
but you.
# Navisworks in 2026.9
Source: https://docs.speckle.systems/next/connectors/navisworks/introduction
2026.8 is the last desktop connector. Use it for a 200 MB publish. Upload larger NWC and NWD files.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
There is no 2026.9 Navisworks desktop connector. **2026.8** is the last installer.
The 2026.8 connector remains the advised path for a publish around **200 MB**. For larger files,
upload an **NWC** or **NWD**. Speckle parses the file on the server, so you do not sit in Navisworks
watching a publish progress bar.
This page covers only that change. For install, sign-in, and publish in the 2026.8 connector, see
[How to use Speckle for Navisworks](/connectors/navisworks).
## What to do
* **About 200 MB.** Publish from the **2026.8** desktop connector. That is still the advised path
for a publish of that size. Do not wait for a 2026.9 installer.
* **Larger files.** Upload an NWC or NWD. Direct upload follows the general file-size limit of
**2 GB**. ACC sync of Navisworks files has been tested to **6 GB**. See [Direct
uploads](/connectors/direct-uploads/introduction) and [Autodesk Forma Data Management
(ACC)](/connectors/cloud-integrations/acc).
A file uploaded directly and the same model published from the desktop connector do not produce
identical Speckle data. See [Uploads and connectors produce different
datasets](/connectors/direct-uploads/introduction#uploads-and-connectors-produce-different-datasets).
## Data format if you stay on 2026.8
The 2026.8 connector publishes Speckle's current data model — the model in use before 2026.9.
Speckle may convert that published version to the 2026.9 format on the server. Treat that conversion
as best effort, not a guarantee.
* In the web app, versions stay readable either way.
* Speckle Intelligence can query only versions that are already in the 2026.9 format. See [Ask
questions about your models with Speckle Intelligence](/next/analytics/intelligence).
* If you read this data in a script, notebook, Automate function, or other API consumer, follow
[Data model migration for developers](/developers/migration/data-model-migration). Do not assume
the version was converted, and do not assume it stayed on the old model.
NWC and NWD uploads follow the upload path: new format by default, or the old format if the
workspace is in [compatibility mode](/next/it-admin/compatibility-mode) until **1 November 2026**.
Compatibility mode does not apply to desktop-connector publishes.
## Desktop-only workflows
If a file is larger than the upload or ACC sizes above, or you have a workflow that only the
desktop connector can do, tell Speckle in [speckle.community/help](https://speckle.community/help).
Those cases will be noted for consideration. As of now there is no 2026.9 connector.
## FAQ
Use the **2026.8** connector for a publish around **200 MB**, and when you need session filters
such as a selection, saved search, or saved view. Upload an NWC or NWD when the file is larger,
or when you do not want to leave Navisworks open on a publish progress bar.
Yes, on **2026.8**. That is the last installer. There is no 2026.9 package. New publishes from
that connector use the current data model. Server-side conversion to the 2026.9 format is not
guaranteed. If you consume the data in code, see [Data model migration for
developers](/developers/migration/data-model-migration).
Not as of now. If a file above the upload or ACC sizes, or a desktop-only workflow, blocks you,
post in [speckle.community/help](https://speckle.community/help) so it can be considered.
The desktop connector cannot match the throughput of server-side NWC and NWD ingest on large
files. Speckle takes that processing so you do not watch a progress bar in Navisworks. A 200 MB
publish from the 2026.8 connector is still the advised desktop path.
No. The Navisworks connector has always been publish-only. You still cannot load Speckle models
back into Navisworks.
# Connectors in 2026.9
Source: https://docs.speckle.systems/next/connectors/overview
How connectors adopt the new data format in 2026.9. Publish and load stay the same.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If you publish and load through Speckle connectors, your day-to-day work stays the same in 2026.9.
After you update a connector, new publishes use the new format going forward. Most people do not
need to do anything beyond updating their connector. Navisworks has no 2026.9 installer — see
[Navisworks in 2026.9](/next/connectors/navisworks/introduction).
For how to publish and load in each design tool today, see [Connectors](/connectors/overview). For
dates and the full workspace steps, see
[Speckle's data model is changing](/workspaces/data-model-migration). For a compact before-and-after table, see [Compare 2026.8 and 2026.9](/next/whats-new).
This page covers the shared data-format change for connectors. 2026.9 pages exist for Revit,
Grasshopper, Power BI, Navisworks, and Trimble Connect. For any other design tool, use the connector guides under **Current** in the version selector — see [Design tools without a dedicated page](#design-tools-without-a-dedicated-page).
## File uploads in 2026.9
The default upload path accepts fewer file formats in 2026.9. STL and the other affected formats
remain supported for direct uploads in legacy/compatibility mode until it ends on **1 November 2026**. See
[File uploads in 2026.9](/next/connectors/file-uploads) for supported formats and alternatives.
## Design tools in 2026.9
* [Revit in 2026.9](/next/connectors/revit/introduction) — reference points and model placement
* [Grasshopper in 2026.9](/next/connectors/grasshopper/introduction) — node names, Explore, and
script upgrades
* [Power BI in 2026.9](/next/connectors/power-bi/introduction) — Get Tables and the 2026.9 3D visual
* [Navisworks in 2026.9](/next/connectors/navisworks/introduction) — no 2026.9 desktop connector;
**2026.8** is the last installer
* [Trimble Connect in 2026.9](/next/connectors/cloud-integrations/trimble-connect) — new beta cloud
sync (Team and Enterprise)
## Design tools without a dedicated page
These tools have no dedicated 2026.9 page. Update the connector when a 2026.9 build exists.
Publish and load stay the same. How-to remains under **Current** in the version selector — see [Connectors](/connectors/overview).
* ProjectWise on-prem, ProjectWise Cloud, and other cloud integrations without a 2026.9
page (ACC, Databricks, Snowflake)
* DGN / MicroStation / OpenRoads
* Tekla Structural Designer
* Rhino, SketchUp, Archicad, Blender, Civil 3D, Plant 3D, AutoCAD, ETABS, and Tekla Structures
* Third-party connectors (Vectorworks, Karamba3D, ShapeDiver)
A 2026.8-or-earlier connector cannot load data published by a 2026.9-or-newer connector. Check the
receiving connector's supported data format before updating a shared workflow.
Vectorworks does not yet support loading the 2026.9 data format. See [Why can't I load a model
into Vectorworks anymore?](#why-cant-i-load-a-model-into-vectorworks-anymore).
Compatibility mode applies to ACC sync and drag-and-drop uploads. It does not change the data
format published or loaded by desktop connectors.
## What stays the same
You still publish and load through your connectors, and you still view and share in the web app.
Your existing connectors keep working. Models you already published stay readable.
## What changes
After your project moves to the new data format, publish, load, and view are faster. New publishes
from an updated connector use the new format going forward. A 2026.8 Navisworks publish still uses
the pre-2026.9 data format; the server may convert it, but that is not guaranteed.
## What you should do
Update your connector from **1 September 2026**, and no later than **1 November 2026**.
Publish and load as usual after updating. New publishes use the 2026.9 format; existing models
stay readable.
Navisworks has no 2026.9 installer. Keep using the 2026.8 connector for publishes around 200 MB;
upload larger NWC and NWD files. See [Navisworks in
2026.9](/next/connectors/navisworks/introduction).
## FAQ
The model was published from a connector that has already updated to 2026.9 — Revit, Archicad,
or any other design tool once it updates. Vectorworks' current read path doesn't support that
format yet. No 2026.8-or-earlier connector can load data published by a 2026.9-or-newer
connector, so this isn't specific to Vectorworks. Ask the publisher to use a **2026.8**
connector build until Vectorworks ships its own 2026.9 read-side update — download it from
[releases.speckle.systems](https://releases.speckle.systems/).
# Power BI in 2026.9
Source: https://docs.speckle.systems/next/connectors/power-bi/introduction
Load Speckle models with Get Tables, relate on Object Key, and bind the 2026.9 3D visual.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
In **Get Data**, select **Connect to Speckle (Custom)**. Power Query calls the function **Speckle - Get Tables**. These instructions use Power BI connector release **2026.10.0**. The default Simplified layout loads a small Objects table plus Properties and Relations functions, instead of one wide table. The new 3D visual streams geometry from Speckle over a WebSocket and requires WebGPU.
This page covers only what changed. For legacy workflows and unchanged setup guidance, see [How to use Speckle for Power BI](/connectors/power-bi/power-bi). Existing `Speckle.GetByUrl` reports can keep working when their target versions contain JSON data. To follow versions published only in the new data format, see [Upgrade Power BI to 2026.9](/next/connectors/power-bi/migration).
## What changed
`Speckle.GetTables` accepts model, version, federated, and project URLs.
The default **Simplified** layout provides three Navigator entries:
* **Objects** has one row per object. Its standard columns are `Object Key`, `Application ID`, and
`Model Info`. Federated loads also include `Source Model`.
* **Properties** is a function. Pick the properties the report needs from a list, then load a table
that contains `Object Key` and those property columns. This table replaces the single flat table
that `Speckle.GetByUrl` returned.
* **Relations** is a function. Choose outgoing graph relations such as `ON_LEVEL` or `IN_ROOM`, then
load a table that contains `Object Key` and those relation columns.
Create one-to-one relationships from the invoked Properties and Relations tables to Objects using
`Object Key`.
## Functions
| Function | Where it appears | Behavior |
| - | - | - |
| `Speckle.GetTables(url, options)` | **Connect to Speckle (Custom)** in Get Data | Returns the navigation table for the new data model |
| `Properties(paths?, useFullPaths?)` | A Navigator entry bound to each loaded model | Returns `Object Key` plus the selected property columns |
| `Relations(relationNames?)` | A Navigator entry bound to each loaded model | Returns `Object Key` plus selected outgoing relation columns |
| `Speckle.AddProperties(source, propertyPaths, objects?, useFullPaths?)` | M code only | Adds selected properties to Objects. An optional Objects table can filter rows and keep extra columns |
| `Speckle.AddAllProperties(source, objects?, useFullPaths?)` | M code only | Adds every property path to Objects. Intended for property-light models |
| `Speckle.AddRelations(objects, relations, relationTypes, nodes, relationNames)` | M code only | Adds selected outgoing, one-hop relations to an Objects table |
`Properties` and `Relations` are bound to the loaded model. They are not global M functions.
`useFullPaths` is a logical value. `false` uses the shortest unique suffix and `true` uses the full
dotted path. The connector adds numeric suffixes when names still collide at full depth.
`Speckle.AddProperties` and `Speckle.AddAllProperties` take the navigation table returned by
`Speckle.GetTables` as `source`. Their optional Objects override can filter the result and preserve
extra columns.
`Speckle.Objects.*` and `Speckle.Models.*` helpers still exist. They operate on the legacy
`Speckle.GetByUrl` table only. They do not accept the tables returned by `Speckle.GetTables`. For
the retained-function list, see [Upgrade Power BI to
2026.9](/next/connectors/power-bi/migration#retained-functions).
## Load
Select **Get Data**. Search for Speckle, then select **Connect to Speckle (Custom)** and
**Connect**. You should see the **Speckle Model URL** input.
Paste the URL from the web app and select **OK**. Sign in when prompted. You should see Objects,
Properties, and Relations in Navigator.
Select **Transform Data** to open Power Query. Enable load for Objects. Select the Properties
function, pick the properties the report needs in the **Properties** list, then select **OK**
and **Invoke** to create a new query. Do the same with Relations for fields such as levels or
rooms. Enable load for the resulting tables and leave the function queries with load disabled.
After **Close & Apply**, create one-to-one relationships from these tables to Objects using
`Object Key`.
The **All tables** layout also exposes Property Values, Property Paths, Object Types, Type
Properties, the raw Relations table, Relation Types, and Nodes. Use these tables for custom
relationships. Nodes includes `Elevation`, which can sort level names.
The Properties function uses the shortest unique suffix for column names by default. Set **Use
full paths** to `true` to use full dotted paths. If two names still collide at full depth, the
connector adds numeric suffixes such as `.1` and `.2`. Instance values use unprefixed column
names. Type values use the `Type_` prefix.
For Get Tables loads, use the Properties and Relations Navigator functions, and relate on `Object
Key`. Do not pass Get Tables output into `Speckle.Objects.*` or `Speckle.Models.*` helpers.
## View
The new visual has four data fields: **Model Info** (required), **Object Keys** (required), **Object Data (Tooltip)**, and **Color By**. Use `Objects[Object Key]` as the standard object binding. `Application ID` remains supported, but large models require an extra lookup dictionary that can add tens of megabytes to the load.
Geometry no longer passes through the Power BI dataset. The visual streams it from Speckle over a
WebSocket, becomes interactive after the first geometry arrives, and continues loading in the
background.
In the Visualizations pane, select **Import a visual from a file**. Browse to `Documents/Power
BI Desktop/Custom Visuals`, select `Speckle Power BI 3D Visual.pbiviz`, and **Open**. You should
see the imported visual in the pane.
Add the imported visual to your report. Check that its fields include **Object Keys**. The older
visual has **Object IDs**. Drag `Objects[Model Info]` into **Model Info** and `Objects[Object
Key]` into **Object Keys**. Drag tooltip fields into **Object Data (Tooltip)** and the category
field into **Color By**. You should see the first geometry arrive, then the rest stream in the
background.
From 2026.10.0, the new visual has a separate identity and leaves legacy visuals in place on
import. Add the new visual and connect its fields separately. Releases 2026.9.0 through 2026.9.2
shared the legacy identity and could replace legacy visuals on import. Use 2026.10.0 or later to
keep both visuals in a report.
The 3D visual requires WebGPU. There is no fallback. If WebGPU is missing, the visual shows an
error.
Firefox on macOS currently allows eight GPU storage buffer bindings, and the 3D visual needs nine,
so it will not render in Power BI Service reports opened there. Use a Chromium-based browser or
Power BI Desktop instead.
The visual's Edit page supports per-category color overrides and a diagnostics mode. Color overrides
remain fixed when the report theme changes. Diagnostics show streaming statistics and recent events.
The toolbar sits at the bottom center and contains Zoom Extents and Camera. Camera includes
orthographic projection, Move camera on filter, and Top, Front, Left, Back, and Right views.
Resizing the visual rebuilds its render targets.
The visual reads Power BI data in 30,000-row windows. When a real filter is active, it can process
up to one million rows for exact isolation. Above 30,000 filtered rows, the visual keeps the
viewport responsive but disables tooltips and click selection for that selection. Chart-click
highlights on very large models are sampled. Use a slicer when exact isolation matters.
The server must serve 4.0 artifact bundles and advertise a geometry stream. The Power BI client
must run in a WebGPU-capable environment. The visual uses the existing OAuth flow and receives a
limited-scope token for each load. The account's main token does not enter the report.
## FAQ
Invoke the **Properties** function: pick the properties you report on, select **OK**, then
**Invoke**. The result has one row per object, with `Object Key` and one column per property.
For the 3D visual, also load Objects and relate it on `Object Key`, because `Model Info` lives
there. To get it all in one table in M, use `Speckle.AddProperties(Source, {"path.one",
"path.two"})`, where `Source` is the `Speckle.GetTables` result.
The **All tables** layout is on. It adds supporting tables for building custom relationships.
Most reports need only the default **Simplified** layout: set **Table layout** to Simplified
under Advanced options, or remove `[TableLayout = "All tables"]` from the `Speckle.GetTables`
call.
No. Models with many property sets can have hundreds of paths, and every selected path adds a
column and refresh time. Start with the properties the report uses. To add more later, edit the
invoked query's step or invoke Properties again.
Type part of its name in the list's search box, for example a property set name. The list shows
full dotted paths such as `properties.Quantities.Qto_SpaceBaseQuantities.NetFloorArea`. The
resulting column uses the shortest unique end of the path, such as `NetFloorArea`, unless you
set **Use full paths** to `true`.
Select **Connect to Speckle (Custom)**. Power Query calls this function **Speckle - Get
Tables**, but Get Data uses a different label. The **Speckle Model URL** input still accepts
your model URL.
`Object Key` is the standard join and visual binding. `Application ID` still works, but large
models need an extra lookup dictionary that can add tens of megabytes to the load.
`GetTables` reads artifact bundles. JSON-only versions published before a 4.0 connector need
`Speckle.GetByUrl`, or the model must be republished with a 4.0 connector.
No. The installer copies the visual file to your Documents folder. Import that file into your
report yourself; there is no automatic update through AppSource. To move from the legacy visual
to the new one, follow the [migration
steps](/next/connectors/power-bi/migration#add-the-new-3d-visual).
Section box, view modes, edges, ghost mode, and named scene views are unavailable. Filtered-out
objects are hidden. Top, Front, Left, Back, and Right remain available. Exact minimum versions
for Speckle Server, Power BI Desktop, and WebView2 are not confirmed.
Power BI Desktop's embedded WebView2 and Chromium-based browsers viewing Power BI Service both
support the visual. Firefox on macOS does not: it supports WebGPU, but currently allows only
eight GPU storage buffer bindings, and the visual needs nine. Other browsers on macOS allow ten
or more. Windows and Android browsers generally allow sixteen or more. This is a known, accepted
limitation. Firefox may raise the limit later.
Not yet. Filtered-out objects are hidden outright instead of shown dimmed. There is no ghost
icon or equivalent toggle in this release.
This is a known rendering-stability issue in the viewer, not specific to Power BI — the same
viewer now runs in the web app, the Power BI visual, and published reports. It shows up most
often with objects far from the model origin or in models with very large bounds. The fix ships
in the web viewer first, then in the following Power BI visual release, which is not on the same
cadence.
# Upgrade Power BI to 2026.9
Source: https://docs.speckle.systems/next/connectors/power-bi/migration
Pin a broken report, rebuild queries with Get Tables, and add the new 3D visual.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Use these steps when your report needs to load versions published in the new data format. They use Power BI connector release **2026.10.0**, which supports both existing `Speckle.GetByUrl` queries and new `Speckle.GetTables` queries. For the new tables and visual, see [Power BI in 2026.9](/next/connectors/power-bi/introduction). For legacy workflows and unchanged setup guidance, see [How to use Speckle for Power BI](/connectors/power-bi/power-bi).
There is no automatic conversion. Renaming the function in the Advanced Editor is not enough.
From 2026.10.0, the new visual has a separate identity and leaves legacy visuals in place on
import. Releases 2026.9.0 through 2026.9.2 shared the legacy identity and could replace legacy
visuals on import. Use 2026.10.0 or later to keep both visuals in a report.
## When to switch
Your report is at risk if all three statements are true:
* It uses `Speckle.GetByUrl`.
* It points to a model URL instead of a specific version.
* The model will get new versions from a 4.0 authoring connector or an IFC file upload.
Version 4.0 authoring connectors and IFC files uploaded in the web app create artifact-only
versions. The next refresh then fails with
`JsonRepresentationUnavailable`. Pinning to a pre-4.0 version URL is a stopgap. Rebuild the report
if it must keep following new versions.
Reports whose target versions still contain JSON data can keep using `GetByUrl` and the legacy visual. Updating the connector alone does not require a rebuild.
## Before and after
| Before | After |
| - | - |
| Existing query calls `Speckle.GetByUrl` | **Connect to Speckle (Custom)** creates a `Speckle.GetTables` query |
| `Speckle.GetByUrl` returns one flat table | The invoked **Properties** table has one row per object |
| Join and color on `Application ID` | Join and bind on `Object Key` |
| Legacy 3D visual | Separate new visual with **Model Info** and **Object Keys** from Objects |
| JSON-only versions | Artifact bundles published with a 4.0 connector |
| Version contains | `Speckle.GetByUrl` | `Speckle.GetTables` |
| - | - | - |
| JSON and artifacts | Returns the legacy flat table | Returns the new tables |
| JSON only, published before 4.0 | Returns the legacy flat table | Fails and directs the user to `GetByUrl` or republishing |
| Artifacts only, published with 4.0 | Fails and directs the user to `GetTables` | Returns the new tables |
## How to switch
### Pin a broken report
If the report already fails, pin it to the last version published before 4.0. A model whose first
version is artifact-only, such as a new IFC upload, has no version to pin to: go to [Rebuild the
queries](#rebuild-the-queries).
Open Power Query and select the query that calls `Speckle.GetByUrl`. You should see the model
URL in the formula.
Change the model URL to `.../models/MODEL_ID@VERSION_ID` for the last version that still
contains JSON. You should be able to refresh against that version again.
After you pin the URL, the report no longer follows new publishes. Continue the steps below when
the report must pick up new versions.
### Rebuild the queries
Use the existing model URL. Do not rename `GetByUrl` to `GetTables` in the Advanced Editor.
Select **Get Data**, search for Speckle, then select **Connect to Speckle (Custom)** and
**Connect**. Paste the existing model URL in **Speckle Model URL** and sign in when prompted.
You should see Objects, Properties, and Relations in Navigator.
Select **Transform Data** and enable load for Objects. You should see one row per object, with
`Object Key`, `Application ID`, and `Model Info`.
Select the Properties function, pick the columns used by the report in the **Properties** list,
then select **OK** and **Invoke** to create a new query. Do the same with Relations for fields
such as levels or rooms. Enable load for the resulting tables and leave the function queries
with load disabled. After **Close & Apply**, create one-to-one relationships from the resulting
tables to Objects using `Object Key`.
For Navigator layouts, column naming, and M helpers, see [Power BI in
2026.9](/next/connectors/power-bi/introduction).
### Reconnect charts, measures, and slicers
Update every report element that references the old table. Check column names. Shortened property
paths, numeric collision suffixes, and the `Type_` prefix may change table references and
calculations.
### Add the new 3D visual
In the Visualizations pane, select **Import a visual from a file**. Import `Speckle Power BI 3D
Visual.pbiviz` from `Documents/Power BI Desktop/Custom Visuals`. The new visual appears in the
pane alongside the legacy visual.
Add the imported visual to your report. Check that its fields include **Object Keys**. The older
visual has **Object IDs**. Drag `Objects[Model Info]` into **Model Info**, `Objects[Object Key]`
into **Object Keys**, tooltip fields into **Object Data (Tooltip)**, and the previous category
field into **Color By**. Recreate any color rules on the new Edit page. You should see your
model in the new visual.
Refresh the report and test its filters, charts, and tooltips. Confirm that the new visual shows
the expected objects before deleting the legacy visual from that page.
Installing a connector update copies the visual file to your Documents folder. Visuals already
imported into reports do not update automatically through AppSource.
### Remove the old queries
Delete the `GetByUrl` queries after no visual, measure, or relationship references them.
## Retained functions
Existing M functions remain available. Data-shaping helpers still expect the legacy `GetByUrl` table shape. The project issues function is independent of both object data paths.
| Function | Behavior |
| - | - |
| `Speckle.GetByUrl(url, ExpandProperties)` | Keeps its 3.x contract and reads JSON versions only. It is no longer the published Get Data entry |
| `Speckle.GetStructuredData(url)` | Returns the legacy flat objects table from JSON |
| `Speckle.SendToServer(url)` | Downloads the version's raw JSON object tree |
| `Speckle.Objects.Collections` | Shapes legacy `GetByUrl` output |
| `Speckle.Objects.CompositeStructure` | Shapes legacy `GetByUrl` output |
| `Speckle.Objects.MaterialQuantities` | Shapes legacy `GetByUrl` output |
| `Speckle.Objects.Properties` | Shapes legacy `GetByUrl` output |
| `Speckle.Models.Federate` | Operates on legacy `GetByUrl` shapes |
| `Speckle.Models.MaterialQuantities` | Operates on legacy `GetByUrl` shapes |
| `Speckle.Revit.Parameters.ToNameValueRecord` | Operates on legacy `GetByUrl` shapes |
| `Speckle.Utils.ExpandRecord` | Operates on legacy `GetByUrl` shapes |
| `Speckle.Project.Issues(url, getReplies)` | Remains independent of both object data paths and works with old and new reports |
| `Speckle.GetVersion()` | Returns the connector version string, unchanged |
The installer places the data connector in the current user's Documents folder. Reports opened by that user use the installed connector. Existing `GetByUrl` queries still work when their target version contains JSON data.
## FAQ
Invoke the **Properties** function and pick the properties the report uses. The result has one
row per object, with `Object Key` and one column per property. See [Power BI in
2026.9](/next/connectors/power-bi/introduction#faq) for the M alternative.
The version is artifact-only. `GetByUrl` reads JSON data only, so pinning works only for a
version published before 4.0. Models created by a 4.0 connector or a new IFC upload have no such
version. Rebuild the query with **Connect to Speckle (Custom)**.
The downstream query steps still expect the old flat table and will fail. Rebuild the query from
**Connect to Speckle (Custom)**, load Objects, and invoke Properties and Relations.
If the error's `RootObjectId` starts with `bundle.`, the version is artifact-only. Some
connector releases report this as `DirectDownloadFailed` instead of
`JsonRepresentationUnavailable`. The fix is the same: rebuild the query with
`Speckle.GetTables`. Pinning to a version published before 4.0 works only if the model has one;
a new IFC upload does not.
No. Pinning to `.../models/MODEL_ID@VERSION_ID` is a recovery option. If the report must follow
new publishes after a 4.0 authoring connector is in use, rebuild it with `Speckle.GetTables` and
add the new visual.
With 2026.10.0, importing the new file leaves legacy visuals in place. A new visual needs
`GetTables` output before it can display a model. A legacy `GetByUrl` table cannot supply its
required fields. Keep the legacy visual until you have loaded the new tables and checked the
report.
# Revit in 2026.9
Source: https://docs.speckle.systems/next/connectors/revit/introduction
Apply Transform, the placement picker, and how published geometry stays in Revit's internal coordinates.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
In 2026.9, published geometry stays in Revit's internal coordinates. The **Reference Point**
dropdown still selects the datum you want consumers to use. Speckle stores that choice next to the
model as a **model placement**. The viewer and the Revit connector apply it when they load.
This page covers only what changed. For install, sign-in, publish, and load in today's connector,
see [How to use Speckle for Revit](/connectors/revit/revit). For the breaking change and bundle
consumers, see [Upgrade Revit to 2026.9](/next/connectors/revit/migration).
## What changed
One publish can now serve every datum. Collaborators choose their placement on their side instead of
asking you to republish.
The send settings still look the same. What changed is a new **Apply Transform** toggle next to
**Reference Point**, and that the transform is stored separately by default.
A linked model's placement in the host document is never written as the model's reference point.
## Publish
Expand **Settings** at the bottom of the **Select objects** dialog while publishing a new
version. You should see **Reference Point** and a new **Apply Transform** toggle.
Set **Reference Point** to **Internal Origin** (default), **Project Base Point**, **Survey
Point**, or **Shared Coordinates**. Survey Point uses the survey point origin only, with no
rotation. Shared Coordinates includes True North rotation. Use Shared Coordinates when you need
to match a survey or civil model's orientation, not just its origin.
Keep **Apply Transform** off. That is the default, and it is the 2026.9 path: geometry stays in
internal coordinates, and placement is stored separately. You should see the toggle remain off
when you publish.
Turn **Apply Transform** on only if a downstream tool that does not understand model placements
needs the geometry pre-positioned. Once it is on, one publish cannot serve every datum for that
model.
If the selected base point cannot be found in the document, the connector falls back to Internal
Origin. The viewer then shows **Internal Origin (fallback)**.
## Load
The **Reference Point** receive setting is unchanged: **Source** (default), **Internal Origin**,
**Project Base Point**, **Survey Point**, or **Shared Coordinates**. Models published from a 2026.9
connector land in the right place regardless of the sender's **Apply Transform** choice. If the
sender baked the transform, Revit inverts it automatically.
For the receive-setting steps in today's connector, see
[How to use Speckle for Revit](/connectors/revit/revit).
Re-publish once from a 2026.9 connector to unlock the picker and datum switching for an existing
model.
## Placement record
Scripts and SDK consumers read `modelPlacement.*` from `eav.model`, not `referencePointTransform` on
the root object. Apply the placement yourself unless the sender turned **Apply Transform** on.
* `modelPlacement.transform` — Internal Origin to the datum selected on publish. The value is 16
row-major doubles. Translation is in `modelPlacement.units`.
* `modelPlacement.default` — which datum that is (`internalOrigin`, `projectBasePoint`,
`surveyPoint`, `sharedCoordinates`).
* `modelPlacement.options.*` — the transform for every available datum, so a consumer can switch.
* `modelPlacement.appliedToGeometry` — `false` by default. `true` only if the sender turned **Apply
Transform** on. If it is `true`, invert the transform before you place the model.
* `referencePoints.*`, `projectLocation.trueNorthAngle` — raw base-point positions, True North, and
site location.
The viewer applies `modelPlacement.transform` at load time. The placement picker swaps in any of the
`options.*` transforms.
### Source (default)
* 2026.9, Apply Transform off: nothing applied. Geometry is already in internal coordinates.
* 2026.9, Apply Transform on: sender's transform inverted automatically.
* v3 / migrated model: sender's root transform inverted, as before.
### Named receive datums
For **Internal Origin**, **Project Base Point**, **Survey Point**, or **Shared Coordinates**:
* 2026.9, Apply Transform off: your chosen datum transform applied.
* 2026.9, Apply Transform on: sender's transform inverted, then your datum applied.
* v3 / migrated model: as before.
For the numbered consumer steps and the re-publish path, see [Upgrade Revit to
2026.9](/next/connectors/revit/migration).
## View
After you publish, the web viewer lands the model using that placement. A **placement picker** lets
you inspect the same model from any other datum without republishing. The datum you published with
is marked **(published)**.
## FAQ
Check the placement picker. The published datum is marked **(published)**. If it shows
**Internal Origin (fallback)**, the survey point could not be resolved in your document.
Yes. The placement record describes the datums in your document. Collaborators still need
matching shared coordinates for models to overlay. What you no longer need to do in 2026.9 is
republish to change datums.
Publish with **Shared Coordinates**. **Survey Point** uses the survey point origin only, with no
rotation.
# Upgrade Revit to 2026.9
Source: https://docs.speckle.systems/next/connectors/revit/migration
Re-publish once, and switch scripts from root referencePointTransform to bundle modelPlacement.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
This page is the mechanical path onto 2026.9 model placement. For Apply Transform, the placement
picker, and the placement record, see [Revit in
2026.9](/next/connectors/revit/introduction). For install, sign-in, publish, and load in today's connector,
see [How to use Speckle for Revit](/connectors/revit/revit).
## When to switch
Existing v3 models keep working. Re-publish from a 2026.9 connector if you need the viewer placement
picker and named datums. Scripts and SDK consumers that read `referencePointTransform` on the root
object must switch to `modelPlacement.*`.
## Before and after
| Before | After |
| - | - |
| Geometry rewritten so the chosen datum is origin | Geometry stays in Revit internal coordinates |
| Root `referencePointTransform` | Bundle `modelPlacement.*` on `eav.model` |
| Datum kind not recorded | Named datum plus `options.*` for every datum |
| One publish per consumer datum | One publish; consumers pick the datum |
v3 connectors that read 2026.9 models get the old root-object transform rebuilt from the placement
record. When a 2026.9 consumer reads a v3 model, the old root transform is migrated into a placement
record, but the datum kind cannot be recovered.
## How to switch
Use a 2026.9 Revit connector. You should see **Apply Transform** next to **Reference Point** in
publish settings.
Leave **Apply Transform** off, then publish as usual. You should see a new version on the model.
Keep **Apply Transform** off unless a downstream tool that does not understand model placements
needs the geometry pre-positioned.
### Scripts and SDK consumers
Assume the model is already in scope.
1. Read `modelPlacement.*` from `eav.model`.
2. Apply `modelPlacement.transform`. The value is 16 row-major doubles. Translation is expressed in
`modelPlacement.units`.
3. If `modelPlacement.appliedToGeometry` is `true`, invert that transform before you place the
model. Do this regardless of the receive datum.
4. Then apply the receive datum you want, if it is not **Source**.
AutoCAD and Civil 3D follow the same contract.
## FAQ
It is not the placement record in 2026.9. Read `modelPlacement.*` from `eav.model`. See
[Placement record](/next/connectors/revit/introduction#placement-record).
Invert `modelPlacement.transform` before you place the model, then apply your receive datum.
Revit does this automatically. A custom consumer must do the same.
No. They keep working. 2026.9 consumers rebuild a placement record from the old root transform.
Re-publish once from a 2026.9 connector if you need the viewer placement picker and named
datums.
Report it with the model URL and the publish and receive settings on both sides. The viewer's
placement overlay shows world origin, model origin, and each reference point.
# Get element data without object.children in 2026.9
Source: https://docs.speckle.systems/next/developers/api/introduction
What changes for GraphQL and REST in 2026.9, and how to replace object.children filtering.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
This page covers what changes for **GraphQL and REST** consumers in 2026.9 — scripts, low-code
flows (Power Automate, Zapier, n8n), and custom integrations that call the API directly. It is not a
full reference: for everything unaffected, keep using the current
[GraphQL](/developers/api/graphql) and [REST](/developers/api/rest) documentation. For the
conceptual model behind this change and who's affected, see [Object model in
2026.9](/next/developers/object-model/overview) and [Data model migration for
developers](/developers/migration/data-model-migration).
Sending a 2026.9 version's `referencedObject` to the legacy objects endpoints (`/objects/ {projectId}/{objectId}`, `Project.object(id)`) returns **404** with `error:
"BUNDLE_REFERENCE_NOT_FOUND"`. Treat that as "this version uses the new format," never as "empty
model." You can tell which format a version uses from the shape of `referencedObject` itself — see
the table below.
## Tell the two formats apart
| `referencedObject` looks like | Format | `object.children` filtering |
| - | - | - |
| A 32-character hex string (e.g. `c462dbafa995e9d1ee3c60ccb0bc1087`) | Object graph, 2026.8 and earlier | Works as before |
| `bundle...` | 2026.9 columnar format | **Not supported** |
Workspaces in compatibility mode — sometimes called "legacy conversions" or "legacy sync" in
support conversations, same thing — can still produce the first shape for uploads and ACC sync,
until **1 November 2026**. See [Data model migration for
developers](/developers/migration/data-model-migration#whos-affected) for how it's granted. Check
the shape of `referencedObject` on the version you actually have — don't assume based on upload
date or workspace settings alone.
## What's no longer supported
For a version whose `referencedObject` is a bundle reference, these stop working. There is
currently no GraphQL-level replacement for the filtering and selection behavior — see [Get element
data today](#get-element-data-today) for what to use instead.
* **`Object.children(query:, select:, limit:)`** — the server-side filter/select mechanism (for
example, filtering by an IFC type and selecting a handful of property paths in one request).
* **`Object.totalChildrenCount`** — nothing to count against; there is no object tree.
* **`Project.object(id: referencedObject)`** — the reference isn't a content hash, so there's
nothing to look up.
* **Reading geometry as JSON** (the `data` field) — 2026.9 geometry is stored in a binary format
read by the Viewer and SDKs, not served as a JSON payload.
## Get element data today
Prior to 2026.9, one GraphQL query could filter elements by property and select just the fields
you needed:
```graphql Prior to 2026.9 theme={null}
query ($myQuery: [JSONObject!]) {
project(id: "PROJECT_ID") {
object(id: "REFERENCED_OBJECT") {
totalChildrenCount
children(query: $myQuery, select: ["id", "Name"], limit: 100) {
totalCount
objects { id data }
}
}
}
}
```
```json Variables theme={null}
{ "myQuery": [{ "field": "ifcType", "value": "IfcDoor", "operator": "=" }] }
```
```bash 2026.9 theme={null}
curl -X POST \
"https://app.speckle.systems/api/v1/projects/PROJECT_ID/models/MODEL_ID/versions/VERSION_ID/eav/query" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"format": "json"}'
```
The 2026.9 call returns every indexed property on every element, one row per property, rather than
a pre-filtered result:
```json theme={null}
[
{
"objectId": "3a1f...",
"path": "ifcType",
"valueText": "IfcDoor",
"valueNum": null,
"type": "string",
"units": null
},
{
"objectId": "3a1f...",
"path": "name",
"valueText": "Door-01",
"valueNum": null,
"type": "string",
"units": null
}
]
```
To reproduce the "every `IfcDoor`" filter from the query above, filter that array yourself:
Filter the rows for `path == "ifcType"` and `valueText == "IfcDoor"`, and collect their
`objectId` values. The outcome is the id of every door in the model.
Filter the full array again for rows whose `objectId` is one of those ids. The outcome is every
property row belonging to a matching element.
Group the result by `objectId` to get one record per door with its properties. The outcome
matches what `select` used to return, without the size limit.
In a low-code tool, that's two filter steps in sequence — two **Filter array** actions in Power
Automate, two **Filter** nodes in n8n — no custom code, just more steps than the single GraphQL
query it replaces.
There's also a `GET .../versions/{versionId}/eav/download` variant for a plain download, and a `GET
.../models/{modelId}/eav/download` variant that always serves the latest version.
This endpoint doesn't accept filter parameters yet — it always returns the whole properties table,
and you filter after receiving it. That's fine at door-count scale; less fine on a model with
hundreds of thousands of elements.
## Payload size, and moving to the bundle directly
Losing `select` and `query` doesn't break anything — you still end up with the same elements once
you've filtered client-side — but it does change what "minimal" means. The old query returned
exactly the fields you asked for, for exactly the elements that matched. The properties endpoint
above always returns everything: every indexed property, on every element, every time you call it.
That's a fixed cost that scales with model size, not with how much of the model you actually
wanted.
For a one-off check, or a model with a few hundred elements, that's a non-issue. For something that
runs repeatedly against a large model — a nightly flow, a live dashboard — downloading and
re-filtering the full properties table on every run is the wrong shape for the job, even though it
technically works.
At that point, don't look for a lighter version of the JSON export — there isn't one yet (see the
FAQ below). Instead, stop treating the version as "a JSON payload to filter" and start reading its
bundle directly:
```
GET /api/v2/projects/{projectId}/models/{modelId}/versions/{versionId}/artifacts
```
This returns presigned URLs to the version's actual parquet files — the same underlying data the
properties export flattens into JSON, but structured for reading only the columns and rows you
need instead of pulling the whole table every time. This is what the SDK paths below attach to
directly, which is why they scale where the JSON export doesn't.
This only helps if your integration can run code that reads Parquet — it isn't a fit for most
low-code tools, which is exactly the properties-export endpoint's use case instead. Self-hosted
n8n is a partial exception; see the FAQ below.
### From an SDK instead of raw HTTP
For anything that can run real code rather than a low-code flow, an SDK's native path reads the
bundle above directly instead of downloading the full properties table yourself:
* **Python:** see [Assess specklepy breaking
changes](/next/developers/sdks/python/breaking-changes) — `operations.receive3` gives direct,
filtered property access (`model.objects_with("ifcType")`), and the legacy `operations.receive`
keeps working as a compatibility path.
* **.NET:** see [Assess .NET SDK breaking changes](/next/developers/sdks/dotnet/breaking-changes).
## FAQ
Probably not. If the failure happens at upload time — a 400 error, code `UNSUPPORTED_FILE_TYPE`
— you're almost certainly still calling the deprecated `startFileImport` mutation, which the
2026.9 server rejects outright as of 1 September 2026. See [File
uploads](/developers/api/guides/file-uploads#what-developers-need-to-know) to switch to
`startFileIngestion`. This page covers a different symptom: uploads that succeed, but querying
elements back afterward returns null or empty.
The version you queried is in the 2026.9 format — check whether `referencedObject` is a
`bundle.` reference (see [Tell the two formats apart](#tell-the-two-formats-apart)). This isn't
a bug or a temporary gap: server-side filtering over the object tree doesn't apply to that
format. Use the properties endpoint above instead.
Yes, unchanged. Nothing on this page affects a version whose `referencedObject` is a hex string.
The two formats can coexist — check the version you actually have rather than assuming based on
project or workspace.
Probably not. A version's id is reserved as soon as ingestion starts, but the version — and its
properties — don't exist until ingestion actually succeeds, which can take from several seconds
to around a minute depending on the file and rail. Querying immediately after upload will
legitimately show nothing. Poll `Model.latestIngestion.statusData` until it reports
`ModelIngestionSuccessStatus`, then query the version it names — don't fire the read on a timer
or a webhook that isn't actually tied to ingestion completion.
No — it works for any version that already has a properties artifact built, hex-referenced or
bundle-referenced. It 404s with `"Version not found or has no EAV artefact"` if that version
hasn't had one built yet, which is a different failure than "wrong format." Bundle versions
always have one; older versions may or may not, depending on whether they've been through
backfill.
Not documented yet. This page will be updated when that changes. For now, download and filter
client-side.
Compatibility mode affects new uploads and ACC sync going forward; it doesn't change versions
that already exist. Confirm you're querying the version created after compatibility mode was
turned on, and that its `referencedObject` is a hex string, not a `bundle.` reference. See [Data
model migration for developers](/developers/migration/data-model-migration#whos-affected) —
compatibility mode ends **1 November 2026** either way, so plan the move regardless.
Not available today for 2026.9 versions — count distinct `objectId` values in the properties
export, or use an SDK's native accessors (see [From an SDK instead of raw
HTTP](#from-an-sdk-instead-of-raw-http)).
Not natively, in most cases. Power Automate and Zapier have no built-in way to parse Parquet —
that's a platform gap, not something Speckle can route around. n8n is a partial exception: its
Code node can't import external npm packages on n8n Cloud, so the same limitation applies there,
but a self-hosted n8n instance can read Parquet if you enable external modules
(`NODE_FUNCTION_ALLOW_EXTERNAL`) or install a community node built for it — that's a deliberate
setup step, not the default. The properties export endpoint exists specifically so low-code
tools have a plain-JSON option that works everywhere, cloud or self-hosted, with no extra
configuration. See [Payload size, and moving to the bundle
directly](#payload-size-and-moving-to-the-bundle-directly).
Two questions decide it: can your integration run code that reads Parquet, and does it run
repeatedly against a large model? If either answer is no — a low-code flow, a one-off check, a
small model — use the JSON properties export; it's simpler and the overhead doesn't matter. If
both are yes — a script or service running on a schedule against a model with many thousands of
elements — read the bundle directly instead. See [Payload size, and moving to the bundle
directly](#payload-size-and-moving-to-the-bundle-directly).
Yes. Format is decided per version, at the time it's created, not per project or workspace.
Older versions and versions made while compatibility mode was active can be hex-referenced while
newer versions on the same model are bundle-referenced. Always check the version you're actually
reading, never assume from a sibling version or from when the project was created.
# Attaching results to objects in 2026.9
Source: https://docs.speckle.systems/next/developers/automate/attaching-results
AttachResultToObjects and attach_result_to_objects still key by a content-hashed id. What that means for a function run against a 2026.9-format model.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If your function calls `AttachResultToObjects` (.NET) or `attach_result_to_objects` (Python) to flag
individual objects, this page covers what happens when it runs against a model in the 2026.9 data
model. This is a different problem from the results not being visible in the product UI yet — see
[Viewing Automate results in 2026.9](/next/developers/automate/viewing-results) for that. This page
is about the attach call itself, inside your function code.
There is no dedicated 2026.9 path for this API yet. What's below is the current, accidental
behavior of the compatibility projection Automate still receives through — not a supported or
tested contract. Don't build a function that depends on the specifics here holding.
## What the attach call expects
Both SDKs key the objects you pass by `id`, the content-hashed Speckle object id, and carry
`applicationId` alongside it:
```csharp theme={null}
// Speckle.Automate.Sdk — AutomationContext.AttachResultToObjects
Dictionary ids = affectedObjects.ToDictionary(
x => x.id.NotNull($"You can only attach {level} results to objects with an id"),
x => x.applicationId
);
```
```python theme={null}
# speckle_automate — AutomationContext.attach_result_to_objects
ids[o.id] = getattr(o, "applicationId", None)
```
Both raise if the object you pass has no `id`. Bundle-only 2026.9 versions have no content-hashed
id at all — see [Object model in 2026.9](/next/developers/object-model/overview) — so this call
should not work against them.
## Why it doesn't fail today
It doesn't fail, but not because it was built to handle 2026.9 data. The objects your function
receives for a bundle-only version still come back as a `Base` tree through Automate's compatibility
projection, and on that projection `id` is set equal to `applicationId` rather than left as a
content hash. The dictionary above ends up keying an object by its own applicationId
(`ids[applicationId] = applicationId`), which happens to satisfy the `NotNull` check and produce a
result Automate can store.
That's a side effect of a decision made for a different problem (receive fidelity on the
compatibility path), not a designed identity story for Automate. There is no committed spec for
what this API looks like once Automate has its own bundle-era path, and no ticket number to point at
yet.
An object your function constructs itself, or receives with neither `id` nor `applicationId` set,
still throws. This isn't limited to objects you read from the model — check any object you build
in the function before attaching a result to it.
## Results stored before a model was migrated
A run that finished before its model moved to the 2026.9 format stored its results against the
content-hashed `id`. Migration doesn't carry that hash into the migrated data: objects are
identified by `applicationId` only, and an object that had no `applicationId` is keyed as
`spk:` followed by its old hash. The stored results are kept as they were, but the ids inside
them no longer match any object in the migrated model.
Runs made after migration don't have this problem, because they key results by `applicationId`.
Re-run the automation against the migrated version to get results that line up with its objects.
## What to do today
Objects you receive from a 2026.9-format model carry one. Objects your function constructs
itself need `applicationId` set explicitly, or the attach call throws regardless of `id`.
Don't parse, compare, or cache by `id` expecting it to be a stable hash of the object's content.
For a bundle-only version it's the same value as `applicationId`; for an object-graph version
it's still a real hash. Treat `id` as an opaque key either way.
Because this is compatibility-projection behavior and not a designed contract, a future Automate
release can change how identity is keyed here without warning. Don't build tooling that depends
on `id == applicationId` continuing to hold.
## FAQ
No. [Viewing Automate results in 2026.9](/next/developers/automate/viewing-results) is about the
product UI not showing results yet. This page is about the attach call inside your function
succeeding or failing. A function can attach results successfully and still have nothing visible
in the viewer.
Automate's bundle-era story is a separate, not-yet-scoped effort. There's no spec, ADR, or
ticket for it yet. This page will be replaced once one exists.
Not by id. Results stored before migration reference the old content-hashed `id`, which the
migrated model no longer carries. Re-run the automation on the migrated version.
Better to make sure every object you attach a result to carries an `applicationId` before you
call the attach method. Catching the exception hides a real problem: an object your function
built without identity set.
# Automate in 2026.9
Source: https://docs.speckle.systems/next/developers/automate/introduction
Where Speckle Automate lives in the 2026.9 web app — Functions, project Automations, and workspace Automation.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Speckle Automate still runs your code when a new version is published on a model. The 2026.9 web
app no longer uses the old Automations-tab wizard. Functions live on a workspace **Functions**
page. You create an automation from project **Settings → Automations**.
This page covers those surfaces only. Function authoring, testing, and the SDK stay on the
[current Automate documentation](/developers/automate/introduction) until a later 2026.9 developer
pass. For dates and who must act on the data-model change, see [Data model migration for
developers](/developers/migration/data-model-migration).
Running a function against a model in the 2026.9 data model works the same as always, but you
currently can't see the results — not in the viewer, not on the project dashboard. See [Viewing
Automate results in 2026.9](/next/developers/automate/viewing-results) before assuming a run
failed. If your function attaches results to individual objects, see [Attaching results in
2026.9](/next/developers/automate/attaching-results) too — that call has its own, unrelated caveat
for 2026.9-format models.
Speckle Automate is available only on Speckle Enterprise Server. For deployment setup, see
[Enterprise license —
Automate](/developers/server/deployment/enterprise-license#optional-automate).
## Find functions
Functions are not in the workspace sidebar. Open them from a project, or go to
`/workspaces/{slug}/functions`.
Open the project, then **Settings → Automations**. You should see a search field, **View
functions**, and **New Automation**.
Select **View functions**. The workspace **Functions** page opens. You should see a search field
and a **New function** button above a card grid.
**New function** starts the create-function flow. Templates and GitHub authoring still follow
[Create a function](/developers/automate/create-function).
## Create an automation
An automation ties one function to one trigger model in a project.
Open the project, then **Settings → Automations**. You should see the automations table, or **No
automations yet.**
Select **New Automation**. The **Create automation** modal opens with **Function**, **Trigger
model**, and **Automation name**.
Choose a function, choose the model that should trigger a run, name the automation, then select
**Create**. The new row appears in the table. Live automations show **Triggered by** and the
model name.
The table can show a **Disabled** badge and a **Test** badge. Test automations show **Connected
to** instead of **Triggered by**. They are a sandbox for local development. They do not run when
someone publishes a new version, and they do not execute published functions. **Create test
automation** in the modal switches to that path; **Standard automation** switches back.
## Manage functions in workspace Settings
Workspace **Settings → Automation** lists functions that projects in this workspace can use.
The table includes each function name, owner, and ID. The row menu includes **Regenerate
token...** when you are allowed to rotate that function's token.
An in-app **Open docs** control on this page may still point at an old URL. Use the Automate
guides linked above, not that control.
## FAQ
2026.9 moved automations into project **Settings → Automations**. The old wizard screenshots on
[Create Automation](/developers/automate/create-automation) describe the previous UI.
From project **Settings → Automations**, select **View functions**. That route is the workspace
Functions page. **New function** lives there.
A test automation will not run when a new version is published. Open the automation from the
table if you need to change or remove it. Use **Standard automation** in the create modal when
you want a live trigger.
# Viewing Automate results in 2026.9
Source: https://docs.speckle.systems/next/developers/automate/viewing-results
Why the results doughnut icon is missing for models in the 2026.9 data model, and what to do meanwhile.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Automate still runs your functions against models in the 2026.9 data model, exactly as described
in [Who's affected](/developers/migration/data-model-migration#whos-affected). A run against a
2026.9-format model completes, and its results are recorded, the same as any other run.
This page is about seeing results in the product UI. If your function calls
`AttachResultToObjects` or `attach_result_to_objects` inside the run itself, see [Attaching
results in 2026.9](/next/developers/automate/attaching-results) for that separate caveat.
The results indicator in the product UI and a documented API don't cover a model in the 2026.9
format right now. The Dashboards Automate results widget is the way to see them. If you're
troubleshooting a run that appears to have succeeded but produced nothing visible in the viewer,
this is why — it isn't a sign the run failed.
## What's missing
For a model still in the current (pre-2026.9) data model, results show up exactly as described in
[Viewing Automation Results](/developers/automate/viewing-results): a doughnut icon in the 3D
viewer and in the project model list, both opening the same results summary.
For a model in the 2026.9 data model, that icon does not appear in either place. This isn't
specific to any one function or trigger — it applies to any 2026.9-format model with a completed
run.
There's also no documented, supported API to read the results back as a substitute. Automate
functions can write results during a run, but nothing on this site currently documents a query for
reading them back afterwards. (The equivalent gap for Data Validation checks specifically is
tracked separately; an undocumented internal path exists there but carries no stability
guarantee — see [Data Validation](/next/analytics/data-validation/overview) if that's what you're
looking for.)
## Results in dashboards
Dashboards include an **Automate results** widget that shows a version's automation runs and the
results each function recorded, and isolates the objects a result refers to in the dashboard's
3D view. It reads a result's object ids and matches them to objects in the model by
`applicationId`, so it works for the same reason [new runs
do](/next/developers/automate/attaching-results#results-stored-before-a-model-was-migrated) and
not for results stored against a content-hashed `id` before the model was migrated.
## What to do meanwhile
Add the widget to a dashboard on the project and pick the model and version. It lists the runs
and their results, and selecting a result isolates its objects in the 3D view.
If you need to see results for a model that would otherwise land in the 2026.9 format —
through ACC sync or drag-and-drop upload — ask us to switch the workspace into compatibility
mode. It keeps that data in the format the current viewer and dashboard already render
correctly. See [Data model migration for
workspaces](/workspaces/data-model-migration).
Compatibility mode is a bridge, not a fix. It ends for every workspace on **1 November 2026**.
Check the automation's run history from project **Settings → Automations** rather than looking
for the results icon. A completed run with no visible icon is this limitation, not a failure.
## FAQ
Yes, if the run history shows it completed. The results icon and any API read path are what's
missing for 2026.9-format models — the run itself, and what it produced, aren't affected.
Yes, through the Dashboards Automate results widget. The other option is compatibility mode,
which keeps new data in the format the existing viewer supports, so the results icon stays
visible the normal way until compatibility mode ends on **1 November 2026**.
No committed date yet. It depends on the same underlying frontend work covering the 2026.9 data
model more broadly — see [Viewer developer notes in
2026.9](/next/developers/viewer/introduction) for the closest thing to a timeline.
# Building applications on 2026.9
Source: https://docs.speckle.systems/next/developers/building-applications
Where to start if you build an app that reads Speckle data rather than a connector — viewer apps, data-only apps, and why there is no drop-in replacement for the object-graph loaders.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
This page is for people building an **application on top of Speckle data** — a dashboard, an
internal tool, a web app, a service that reacts to versions. Not a connector: you are not
publishing from a host application, you are reading what others published.
In 2026.9 a version's data is a [bundle](/next/developers/building-integrations) of parquet
files rather than an object graph, and that changes how your app gets at it. Which door you
take depends on whether you render.
You import `@speckle/viewer`. Bridge the bundle to the object shape the viewer converts, with
sample code you copy into your app.
You read properties, quantities or geometry without the viewer. Read the bundle directly — the
load mechanics are the same ones every non-SDK consumer uses.
Writing scripts with a Speckle SDK instead? The SDK applies every bundle rule for you. Start from
the [.NET](/next/developers/sdks/dotnet/breaking-changes) or
[Python](/next/developers/sdks/python/breaking-changes) 2026.9 pages.
## My app writes data
Publishing is the harder half. A 2026.9 version is a bundle built and uploaded through an
ingestion, and there is no published JavaScript bundle builder — `@speckle/objectsender`
writes object graphs, and .NET and Python are the only SDKs that build bundles today.
That leaves three honest options, in the order most teams should consider them:
| Option | When it fits |
| - | - |
| Publish from .NET or Python instead | The writing half can move process or language |
| Implement the bundle contract yourself | You must stay in JavaScript, and can carry a parquet writer and the SGEO encoder |
| Ask for compatibility mode | You need time, your consumers still read object graphs, and you can move off by 1 November 2026 |
The wire sequence, the correctness rules and a build order are in [Publish a bundle from your
own code](/next/developers/building-integrations/publish). Read it before choosing the second
option: it is a real piece of work, and unlike the read path there is no worked sample.
## Why there is no drop-in object-graph loader
If your app is built on `@speckle/objectloader` or `@speckle/objectloader2`, the natural ask
is a version of those packages that reads bundles and hands you the same object graph —
`getRootObject()`, `__closure`, `getObjectIterator()`, unchanged. That is not coming, on
purpose.
Emulating that surface would mean rebuilding the object graph that 2026.9 retires: a third
implementation of the receive fidelity contract, kept in step with the .NET and Python SDKs
forever, reconstructing hashes and closures that no longer exist in the data. It would also
teach new apps a shape the platform is moving away from.
What to do instead depends on what you were using the graph for.
| What you used the object graph for | In 2026.9 |
| - | - |
| Reading properties across objects | Read the eav tables. Objects are keyed by `applicationId`, properties by dotted path |
| Walking hierarchy | Read typed relations. Grouping is `IN_COLLECTION` and the scene view, not nested `elements` |
| Getting geometry to draw | Decode SGEO from the geometry shards, or bridge to the viewer |
| Object identity across versions | `applicationId`, not a content hash |
The mechanics for all four are in [Load a bundle in your own
code](/next/developers/building-integrations/load). That page is written for any non-SDK
consumer, which includes you — it is not connector-specific.
A loader that fetches `referencedObject` from the objects endpoints gets a 404 on every 2026.9
version. Dispatch on `Version.schemaVersion` before you fetch, and keep your existing reader for
the older shape.
## What stays the same
Your Speckle token, the GraphQL API for projects, models and versions, webhooks, permissions
and sharing are unchanged. So is the shape of the problem: list a version, fetch its data,
key your app's state to something stable. Only the fetch and the identity changed.
# Building integrations in 2026.9
Source: https://docs.speckle.systems/next/developers/building-integrations
The language-neutral model behind every 2026.9 producer and consumer. The bundle, the bundle builder, and where publish and load go from here.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If you connect a host application or a service to Speckle with your own code rather than a Speckle
SDK, 2026.9 changes what you write and read. There is no root object to serialize and no object
graph to walk. A version is a **bundle**, a set of parquet files, and a **model ingestion** is how
the server turns your upload into a version. This page introduces that model and points to the two
flows built on it. It is the 2026.9 successor of [Data model and build
order](/developers/connectivity/data-and-build).
These pages are for people who build connectors, sync services, and other producers or consumers.
If you write scripts with a Speckle SDK, the SDK applies every rule here for you. Start from the
[.NET](/next/developers/sdks/dotnet/breaking-changes) or
[Python](/next/developers/sdks/python/breaking-changes) 2026.9 pages instead.
Open an ingestion, write and upload a bundle, and wait for the version. The wire sequence, the
correctness rules, and a build order.
List a version's artifacts, download the files you need, and rebuild objects, geometry,
instances, grouping, and appearance from the bundle.
Decide **publish only** or **publish and load** for your host before you start, the same way as
today. See [Publish and load](/developers/connectivity/integration-depth).
## What changes
| Concern | Before 2026.9 | In 2026.9 |
| - | - | - |
| What you write | One root object with nested `elements`, serialized as JSON objects | A bundle: parquet files for objects, properties, geometry, and a graph of nodes and relations |
| Object identity | Content hash per object, plus a `__closure` table on the root | `applicationId` per object. Objects, geometry, and nodes get dense integer keys that are local to one bundle |
| Hierarchy and shared assets | Nested `elements` and Proxy arrays on the root | Grouping containers, structural nodes, and typed [relations](/next/developers/object-model/relations) |
| Geometry | `displayValue` objects in JSON | SGEO binary blobs in a sharded geometry table |
| How a version is created | Upload objects, then call create-version with the root id | Open an ingestion, upload the bundle, complete it. The server creates the version |
| When the version exists | When create-version returns | When the ingestion reaches `success`. Completing the upload is not enough |
| Version metadata | Message and source application on the create call, units on the root | Source application on the ingestion, message on completion, units per object. See [Version metadata](/next/developers/object-model/version-metadata) |
## What a bundle builder is
Every Speckle producer has a component that plays the role the SDKs call a **bundle builder**. In
specklepy and Speckle.Sdk it is `BundleBuilder`. The Archicad connector implements the same role in
C++ without an SDK. It is the producer-side model of a version, and it has four jobs.
* **Intern identities into dense integer keys.** Objects are keyed by `applicationId`. Geometry is
keyed by a value you derive from the owning object, such as `wall-1:g0`. Structural nodes such as
definitions, instances, materials, colors, levels, and grouping containers are keyed by whatever
stable key your host gives them. Each of the three key spaces counts from zero on its own. A
relation's type is what says which space each end refers to.
* **Record typed relations.** Every link is one row of `(relation, source, target, ord)`. Membership,
hosting, connectivity, appearance, and instancing are all relations, never nested fields.
* **Stream rows to parquet as you add them.** Geometry is encoded on the spot and flushed in row
groups, so memory does not grow with the model.
* **Stamp provenance.** The builder writes the `meta` row and the self-describing catalogs from the [bundle spec](https://github.com/specklesystems/speckle-bundle-spec)'s generated constants, never from literals you typed in.
The builder's behaviour is the contract, whichever language you write it in:
* **Get-or-add is interning.** Asking for a key that already exists returns the same handle and
writes nothing. Asking for it again with different attributes is an error. One key means one
node.
* **Add is append.** Geometry, model properties, results, and camera views append a row on every
call.
* **Edges cannot be retracted.** A single-valued relation is write-once. Decide before you write.
* **Units and producer are required up front.** The default unit is stamped on every object, and the
producer slug and version go into `meta` and onto the ingestion.
* **A default scene view is injected** when you declare none, grouping by `IN_COLLECTION`.
* **Build once, then rename.** The builder writes files under a temporary stem and renames them to
the reserved version id once the ingestion hands it out.
## What a bundle contains
A bundle is a flat set of files sharing one stem, the version id, with no manifest file. Consumers
know the filenames from the spec and discover the stem from the `meta` file.
| File | Family | Required | Holds |
| - | - | - | - |
| `{id}.eav.objects.parquet` | eav | Yes | Object identity: `applicationId` to object key |
| `{id}.eav.paths.parquet` | eav | Yes | Interned property paths |
| `{id}.eav.eav.parquet` | eav | Yes | One row per object, path, and value |
| `{id}.eav.types.parquet` and two siblings | eav | Ship them | Type-level properties and the object-to-type link. Write them even when empty |
| `{id}.eav.model.parquet` | eav | No | Model-scoped properties such as the reference point. Omit the file when you have no rows |
| `{id}.eav.structural_results.parquet` | eav | No | Analysis results. Omit when empty |
| `{id}.eav.property_set_definitions.parquet` | eav | No | Authored property set schemas. Omit when empty |
| `{id}.geometries.parquet` and shards | geometry | Yes | SGEO blobs keyed by geometry key. Overflow shards are `{id}.geometries.{n}.parquet` |
| `{id}.envelope.nodes.parquet` | envelope | Yes | Definitions, instances, materials, colors, levels, containers, with fixed columns per kind |
| `{id}.envelope.relations.parquet` | envelope | Yes | `(rel, src, dst, ord)` rows |
| `{id}.envelope.rel_types.parquet` | envelope | Yes | Catalog of relation types and their namespaces, copied from the spec |
| `{id}.envelope.node_kinds.parquet` | envelope | Yes | Catalog of node kinds, copied from the spec |
| `{id}.envelope.meta.parquet` | envelope | Yes | One row: `schema_version`, `produced_by`, `producer_version`, `sdk_name`, `sdk_version` |
| `{id}.envelope.scene_views.parquet` | envelope | No | Grouping projections the viewer offers |
| `{id}.envelope.camera_views.parquet` | envelope | No | Saved cameras |
Two files you do not write:
* **The viewer `.dat`.** The server builds `{id}.viewer.dat` from your bundle after completion. That
build is part of why the version is not visible the moment the upload completes.
* **A manifest or index.** The `meta` file is the anchor. Everything else is found by name.
Optional files are feature-detected by presence. An empty optional table is a bug, not a
placeholder. The three type tables are the exception: the viewer needs all three present to build
its property view, so write them even when they hold no rows.
## Relations you do not need, and relations you add
Two questions come up as soon as a team maps its own domain onto the relational layer. Both have a
short answer.
**You do not have to emit every relation type.** The catalog describes what Speckle can carry, not
what every model must contain. A producer for a host with no levels, no instances, or no MEP
systems emits none of those relations, and a consumer treats the absence as "not in this model",
never as an error. Emit what your domain has and nothing else.
**You can add your own links, with the same guarantee interoperability has always had: none.** The
relation catalog is Speckle's. Its numbers are reserved or retired on purpose, the validator rejects
a relations file that uses a number outside the catalog, and every Speckle consumer ignores edges
it does not know. Custom links therefore go in your own parquet file, uploaded with the bundle under
the same version id, keyed by `applicationId` so they survive across versions. Speckle stores and
lists that file and never reads it. The viewer, Speckle Intelligence, and the SDK receive path will
not see those links, and a receiving application supports them only if you wrote that support. Say
so in your own documentation.
Before you add a file, check which of these three shapes your case is. Most turn out to be the
first two.
* **A relation Speckle already has.** A structural "supports" link, a duct run, a pipe network:
`CONNECTS_TO` with a scope, `IN_SYSTEM`, or `SUBELEMENT` usually carries it, and then every
consumer understands it. Read the [relation catalog](/next/developers/object-model/relations)
first.
* **A grouping, not a link.** A classification hierarchy, a cost breakdown, a zone scheme: put the
code on each object as a property and declare a scene view over that property path. The viewer
then offers your hierarchy as a tree without any new relation.
* **A link to something outside the model.** An issue, a clash, a record in an asset register: when
the other end is not an object in the bundle, an id on the object as a property is the portable
form. Use a custom edge file only when both ends are objects in the same bundle.
If a link is general to a discipline rather than to your tool, propose it as an issue on
[speckle-bundle-spec](https://github.com/specklesystems/speckle-bundle-spec) with the two node
kinds it joins and an example model. Reserved catalog numbers exist so that new relations can be
added for everyone, and a relation in the catalog is the only kind every consumer will understand.
## Open-source producers to learn from
Every Speckle connector is open source. Read them as worked examples of the publish and load rules, in the
language closest to yours. The SDKs are the reference builders; the non-SDK producers show the same
contract implemented from scratch.
| Repository | Language | Hosts | How it publishes in 2026.9 | Read it for |
| - | - | - | - | - |
| [speckle-sharp-sdk](https://github.com/specklesystems/speckle-sharp-sdk) | C# | Any | The reference bundle builder and artifact pipeline under `Speckle.Sdk.Bundles` | The builder contract, sharding, progress and cancellation plumbing |
| [specklepy](https://github.com/specklesystems/specklepy) | Python | Any | The reference bundle builder under `specklepy.bundle`, with the `bundle` extra | A compact builder, the SGEO encoder, the upload client |
| [speckle-archicad](https://github.com/specklesystems/speckle-archicad) | C++ | Archicad | Its own parquet writer and ingestion client. No Speckle SDK | The complete non-SDK producer: column binding, EAV rules, SGEO, upload |
| [speckle-sketchup](https://github.com/specklesystems/speckle-sketchup) | Ruby | SketchUp | Its own envelope writer and ingestion client. No Speckle SDK | A non-SDK producer in a dynamic language |
| [speckle-blender](https://github.com/specklesystems/speckle-blender) | Python | Blender | specklepy's bundle builder | Instancing, per-object geometry keys, world-space display geometry |
| [speckle-sharp-connectors](https://github.com/specklesystems/speckle-sharp-connectors) | C# | Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Plant 3D, ETABS, SAP2000, Tekla Structures, Tekla Structural Designer | Speckle.Sdk's bundle builder and the same v2 upload rail | Reference points, by-layer materials, type-level properties, linked models |
| [speckle-powerbi](https://github.com/specklesystems/speckle-powerbi) | TypeScript | Power BI | Load only | Reading versions and properties without producing |
The user interface is separate from publishing. The shared connector UI,
[speckle-connectors-dui](https://github.com/specklesystems/speckle-connectors-dui) (TypeScript), is
what the .NET desktop connectors, Archicad, and SketchUp embed. Using it is optional. It is worth
reading for two things: the bindings show the read and write interactions a host has to expose,
including how the UI follows an ingestion until the version exists, and reusing it keeps the
experience familiar to people who already use other Speckle connectors. Blender ships its own native
panels instead. Grasshopper has no UI at all and splits publish and load into components.
Navisworks has no 2026.9 desktop connector. Large NWC and NWD files go through [direct
uploads](/connectors/direct-uploads/introduction), where a server-side converter produces the
bundle. Older archived repositories such as the QGIS, Unity, and Excel connectors publish the
object graph of 2026.8 and earlier and are not a pattern for 2026.9.
## FAQ
Yes. It lives at [speckle-bundle-spec](https://github.com/specklesystems/speckle-bundle-spec):
the SQL source that defines every table, relation, and node kind, with the reasoning recorded
beside each entry, plus the code generators, a validator, and conformance fixtures. Generated
constants for C++, C#, Python, and TypeScript are under `generated/`. Pin to a commit, build
against the constants for your language, and run the validator on every bundle in CI. To propose
a relation, open an issue there with the two node kinds it joins and an example model.
Emit nothing for them. An absent relation means "not in this model". Consumers must never treat
a missing relation type as an error.
On every object as a root scalar, on every instance node, in every SGEO header, and per property
row where a value has a unit. There is no bundle-wide unit.
# Load a bundle in your own code
Source: https://docs.speckle.systems/next/developers/building-integrations/load
How a consumer lists a version's artifacts, downloads the bundle, and rebuilds objects, geometry, instances, grouping, and appearance without a Speckle SDK.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If you load versions without a Speckle SDK, there is no root object to download and no graph to
walk. You list the version's artifacts, fetch the parquet files you need from presigned URLs, and
rebuild the model from three key spaces joined by relations. A loader that downloads
`referencedObject` from the objects endpoints gets a 404 on every 2026.9 version. This page
assumes the model from [Building integrations in 2026.9](/next/developers/building-integrations).
## Tell the two formats apart
Any version you are asked to load may still be an object graph from 2026.8 or earlier. Read the version record
first.
| Field | 2026.8 and earlier | 2026.9 bundle |
| - | - | - |
| `Version.schemaVersion` | `null` | `3` |
| `Version.referencedObject` | A content hash | `bundle...` |
| Where the data is | The objects endpoints, as JSON | The artifacts endpoint, as parquet files |
Dispatch on the `bundle.` prefix. Keep your existing reader for the first shape, or refuse it with a
clear message the way the Archicad connector does. Never derive storage paths from the triple in the
reference; resolve the version through the project and version ids you already hold.
## How a load fetches a version
Take the version id from the model URL, or list the model's versions and take the latest. The
outcome is a project, model, and version id, and a version record whose `schemaVersion` is `3`.
Get the version's artifacts endpoint with your Speckle token. The outcome is one entry per file
with a presigned URL and an expiry, and the version's viewer `.dat` in the list.
Get each URL without your Speckle token. Skip the geometry shards when you only read properties,
and skip the `.dat`. The outcome is a bundle directory on local disk.
Open `meta`, `rel_types`, and `node_kinds`. The outcome is the vocabulary this bundle was
written with, including relation namespaces and any ids your reader does not know.
Intern objects, nodes, and geometry by their dense keys, then apply relations in the order the
rules below give. The outcome is objects with properties, geometry, placement, grouping, and
appearance.
Call the mark-received mutation with your application slug. The outcome is a receive recorded
against the version, the way every Speckle connector does.
The SDK tabs run the whole sequence in one call. The GraphQL and REST tabs are the calls a
non-SDK consumer makes, numbered to match the steps.
```graphql theme={null}
# 1. Read the version record and dispatch on its shape.
query VersionShape($projectId: String!, $modelId: String!, $versionId: String!) {
project(id: $projectId) {
model(id: $modelId) {
version(id: $versionId) {
id
schemaVersion
referencedObject
sourceApplication
message
}
}
}
}
# 6. Record the receive.
mutation MarkReceived($input: MarkReceivedVersionInput!) {
versionMutations {
markReceived(input: $input)
}
}
```
```json Variables for MarkReceived theme={null}
{
"input": {
"projectId": "",
"versionId": "",
"sourceApplication": "my-consumer"
}
}
```
```http theme={null}
# 2. List. Use the model-level path without a version id for the latest version.
GET /api/v2/projects/{projectId}/models/{modelId}/versions/{versionId}/artifacts
Authorization: Bearer
200 {
"files": [
{ "name": ".envelope.meta.parquet", "url": "https://...", "expiresAt": "..." },
{ "name": ".geometries.parquet", "url": "https://...", "expiresAt": "..." },
{ "name": ".viewer.dat", "url": "https://...", "expiresAt": "..." }
]
}
404 when the version has no bundle, or the token cannot read the project.
# 3. Download. No Authorization header on presigned URLs.
GET
```
```python theme={null}
from specklepy.api import operations
with operations.receive3(account, project_id, model_id, version_id) as model:
print(model.units, len(model.objects), model.unknown_relations)
for obj in model.objects_with("category"):
print(obj.application_id, obj.name)
```
```csharp theme={null}
using var model = await SpeckleBootstrap.Operations.Receive3(
account, projectId, modelId, versionId, cancellationToken: ct
);
Console.WriteLine($"{model.Units} {model.Objects.Count} {model.UnknownRelations.Count}");
```
Presigned URLs expire after an hour and are not content-addressed. Never persist one. Cache the
downloaded files by version id and revalidate with the ETag when you refetch.
The `.dat` in the listing is the viewer's artifact, and a `geometryStream` field beside the file
list is the viewer's streaming endpoint. Both are internal. Read the parquet files.
## Rules that decide whether your load is correct
The bundle tells you its own vocabulary. Read it, and apply these rules in order.
### Reading the files
* Read columns by name and tolerate columns you do not know. The spec adds columns without moving
the version, so a bundle written before a column existed must still load.
* Read every geometry shard, `{id}.geometries.parquet` and `{id}.geometries.{n}.parquet`. A reader
that opens only shard zero silently drops geometry above the shard cap.
* Treat a relation id or node kind missing from your vocabulary as skip-and-report, never as an
error. A newer producer than your reader is normal.
* Do not gate on `meta.schema_version`. It is provenance, not a compatibility switch.
### Identity
* A bare integer is meaningless without its namespace. The relation type's source and target
namespaces in `rel_types` say whether an end is an object, a node, or a geometry.
* The object's identity is `applicationId`. If you project the bundle into a tree with ids on
nodes, mint synthetic ids per bundle and expect them to differ across versions.
### Properties
* Each row sets exactly one of the string, number, and boolean columns. Coalesce them.
* Rebuild nesting from the dotted path. `properties.Constraints.Base Offset` is a nested
dictionary, not a flat key.
* Merge type-level rows into each object through the object-to-type link. Without that merge, most
type parameters are missing.
* The root scalars `speckle_type`, `name`, `units`, and `type` sit beside `properties.*`, not
under it.
### Geometry
* Verify the CRC and decode by primitive type. See [Geometry encoding in
2026.9](/next/developers/object-model/geometry-encoding).
* `DISPLAY` is what to render. Prefer a `SOLID` only when your host can read its format, and fall
back to the display mesh otherwise. Never draw `CENTERLINE` as a body.
* Convert with the blob's own unit, not a model-wide unit. Objects, instances, and geometry can
disagree.
### Instancing
* Expand a definition once through `DEFINES`, then place it for every `DISPLAY_INSTANCE` edge using
the instance node's transform, 16 row-major doubles in the instance's units. Scale only the
translation when you convert units. `DEFINES_INSTANCE` nests one definition in another.
* An object with no render edge is a carrier for a definition member's properties and grouping.
Skip it when you bake geometry, or the member draws twice, once untransformed at the origin.
`DEFINES_MEMBER` and `PLACES` join carriers to their definition and placement.
### Grouping
* Build the tree from the default scene view. Each tier is either a relation such as `IN_MODEL` or
`IN_COLLECTION`, or a property path such as `category`. With no scene view, group by
`IN_COLLECTION`.
* Nest containers through their parent reference column, not through a relation.
* `IN_COLLECTION` is single-valued. `IN_GROUP` and `IN_SYSTEM` are not.
### Appearance
* Material fills from specific to general: `HAS_MATERIAL` on the geometry, then
`OBJECT_HAS_MATERIAL` on the object, then `NODE_HAS_MATERIAL` on the object's container.
* Color is presentational: `OBJECT_HAS_COLOR` overrides `HAS_COLOR` on the geometry, and
`NODE_HAS_COLOR` is the container default.
* `HAS_COLOR` starts from a geometry or an object, and `ord` says which. Read it before you
resolve the source key.
* Appearance edges are single-valued per source. When a bundle repeats one, the last row wins.
### Placement
* Read `modelPlacement.*` and `referencePoint.*` from the model-scoped properties. When
`appliedToGeometry` is true, stored coordinates are already in that datum. When it is false,
apply the transform yourself. No rows means the internal origin.
## Suggested build order
Authenticate, resolve a version, list its artifacts, and download the non-geometry files. The
outcome is a bundle directory and a passing read of the three catalogs.
Objects by `applicationId`, nested properties from paths, type-level rows merged in. The outcome
is a property table you can filter, before any geometry.
Download the shards, verify CRCs, decode meshes, attach them through `DISPLAY` with the right
units. The outcome is a model that renders untransformed geometry correctly.
Definitions, placements, nested definitions, and carrier suppression. The outcome is repeated
objects in the right places, each drawn once.
The default scene view, container nesting, and the material and color precedence. The outcome is
a tree and a look that match the Speckle viewer.
The reference point contract, then the mark-received call. The outcome is geometry in the right
datum and a receive recorded on the version.
## FAQ
Either the version is an object graph from 2026.8 or earlier, the bundle has not been produced
yet, or your token cannot read the project. Check `schemaVersion` on the version record first,
then the ingestion status if the version is new.
Yes. Geometry is the bulk of a bundle and lives in its own shards. Download the `eav` and
`envelope` files only. The SDKs expose the same choice as an include-geometry option.
Only as a new version, and every publish is one. Files are stored under the reserved version id,
so the server never sees "the same bundle". Your choice is how to make the new file set. Copying
the received files, rewriting the property rows you changed, stamping `meta` with your own
producer, renaming to the new id, and uploading is valid when only values change: keys,
relations, and geometry stay consistent. Build a new bundle when structure changes. Either way
the version's source application is you. Speckle's own parameter editing applies changes in the
host and republishes from there, and a receive-then-`send` round trip through a `Base` tree is
unsupported.
No, but every Speckle connector does. It is how the web app and analytics know a version was
loaded, and it costs one mutation.
Skip its edges and report which ids you skipped. The SDKs surface the same set as unknown
relations. Do not fail the load and do not guess a meaning from the number.
# Publish a bundle from your own code
Source: https://docs.speckle.systems/next/developers/building-integrations/publish
The ingestion sequence a producer drives to create a 2026.9 version, the rules that make a bundle correct, and a build order.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
A publish in 2026.9 is an ingestion you open, a bundle you write and upload, and a version the
server creates when ingestion finishes. Nothing here calls a create-version mutation, and nothing
is visible until the ingestion reaches `success`. This page assumes you know what a bundle and a
bundle builder are from [Building integrations in 2026.9](/next/developers/building-integrations).
## How a publish creates a version
The version id is allocated before any bytes move, and the version is born on the server. Every
Speckle producer follows this sequence.
Call the ingestion create mutation with the model, the source application slug and version, a
progress message, and an idle timeout. The outcome is an ingestion id and a reserved version id.
If the response has no version id, the server predates 2026.9. Stop; this path has no fallback.
Run your conversion and write the files with the reserved id as their stem. The outcome is a
complete bundle on local disk that you can validate before anything is uploaded.
Post the list of bare filenames to the sign endpoint. The outcome is one presigned URL per file.
Put each file to its URL, adding only the headers the sign response asked for, and keep the
returned ETag exactly as received. The outcome is every file in storage and an ETag per
filename.
Post the ETags, the bundle reference as the root id, the object count, and the version message.
The outcome is the server accepting the bundle and starting ingestion. The response echoes the
version id.
Subscribe to ingestion updates or poll until the status is terminal. The outcome on `success` is
a version that answers queries, has a viewer `.dat`, and arrives as `CREATED` on the
`projectVersionsUpdated` subscription.
The SDK tabs run the whole sequence in one call. The GraphQL and REST tabs are the calls a
non-SDK producer makes, numbered to match the steps.
```graphql theme={null}
# 1. Open the ingestion. The response carries the reserved version id.
mutation IngestionCreate($input: ModelIngestionCreateInput!) {
projectMutations {
modelIngestionMutations {
create(input: $input) {
id
versionId
cancellationRequested
}
}
}
}
# While you convert: keep the idle clock alive and read the cancel flag.
mutation IngestionUpdateProgress($input: ModelIngestionUpdateInput!) {
projectMutations {
modelIngestionMutations {
updateProgress(input: $input) {
id
cancellationRequested
}
}
}
}
# On any failure: close the ingestion. failWithInvalid takes ModelIngestionInvalidInput
# (validationMessage) and failWithCancel takes ModelIngestionCancelledInput (cancellationMessage).
mutation IngestionFailWithError($input: ModelIngestionFailedInput!) {
projectMutations {
modelIngestionMutations {
failWithError(input: $input) {
id
}
}
}
}
# 6. After complete: wait for a terminal status.
subscription ProjectModelIngestionUpdated($input: ProjectModelIngestionSubscriptionInput!) {
projectModelIngestionUpdated(input: $input) {
modelIngestion {
id
statusData {
__typename
... on ModelIngestionSuccessStatus {
versionId
}
... on ModelIngestionProcessingStatus {
progressMessage
progress
}
}
}
}
}
```
```json Variables for IngestionCreate theme={null}
{
"input": {
"projectId": "",
"modelId": "",
"progressMessage": "Sending from my-producer 1.0",
"sourceData": {
"sourceApplicationSlug": "my-producer",
"sourceApplicationVersion": "1.0"
},
"maxIdleTimeoutSeconds": 600
}
}
```
```http theme={null}
# 3. Sign. Bare filenames only, no paths.
POST /api/v2/projects/{projectId}/modelingestion/{ingestionId}/uploads/sign
Authorization: Bearer
apollographql-client-version: 1.0
Content-Type: application/json
{ "files": [".envelope.meta.parquet", ".eav.objects.parquet", "..."] }
200 { "uploads": { "": { "url": "https://...", "key": "...", "additionalRequestHeaders": { } } } }
# 4. Upload. One PUT per file, no Authorization header. Send additionalRequestHeaders when present.
PUT
200 ETag: ""
# 5. Complete. ETags verbatim, quotes included.
POST /api/v2/projects/{projectId}/modelingestion/{ingestionId}/uploads/complete
Authorization: Bearer
apollographql-client-version: 1.0
Content-Type: application/json
{
"etags": { "": "\"\"" },
"rootId": "bundle...",
"totalChildrenCount":
```python theme={null}
from specklepy.api import operations
from specklepy.bundle import BundleBuilder, Producer, SendOptions
builder = BundleBuilder(Producer(slug="my-producer", version="1.0"), units="m")
# add objects, containers, levels, and relations on the builder
result = operations.send3(
account, project_id, model_id, builder, SendOptions(message="Nightly export")
)
print(result.version_id, result.bundle_reference)
```
```csharp theme={null}
var producer = new SpeckleApplication
{
HostApplication = "My producer",
HostApplicationVersion = "1.0",
Slug = "my-producer",
SpeckleVersion = "2026.9",
};
using var builder = new BundleBuilder(producer, units: "m");
// add objects, containers, levels, and relations on the builder
var sent = await SpeckleBootstrap.Operations.Send3(
account, projectId, modelId, builder, new SendOptions(Message: "Nightly export"), ct
);
var version = await SpeckleBootstrap.Operations.WaitForVersion(account, sent, cancellationToken: ct);
```
Forward the ETag byte for byte. Object storage wraps it in double quotes and the complete endpoint
compares against that exact value. Stripping the quotes fails the publish with an ETag mismatch
that looks like a server fault.
Close the ingestion on every exit. Report `failWithError`, `failWithInvalid`, or `failWithCancel`
through the ingestion mutations, using a request that cannot itself be cancelled. An ingestion you
leave open sits until the idle timeout fires.
Send the `apollographql-client-version` header on the sign and complete calls. The server records
it on the version for analytics. The publishing application itself comes from the source
application slug and version you gave when opening the ingestion, not from a header.
A single presigned put is limited to 5 GB on S3-compatible storage. Larger files use the multipart
endpoints under the same ingestion path (`uploads/multipart/start`, `complete`, and `abort`)
instead of one put per file.
Progress and cancellation run through the same ingestion. Report progress on a throttle, with at
most one update in flight, and never let a failed report fail the publish. The idle timeout is an
idle clock, reset by each report, so a long conversion must keep reporting. Cancellation is
cooperative in both directions: check your own cancel signal per object and per file, and watch
`cancellationRequested` on every ingestion response.
## Rules that decide whether your bundle is correct
The server checks only that every file arrived with a matching ETag. Correctness is checked by the
bundle spec's validator before upload and by consumers after. These are the rules producers most
often get wrong.
### Identity
* `applicationId` must be stable across publishes and unique per placement. A linked file placed
twice needs a per-placement suffix, or its objects and their topology collide.
* Key geometry on the placed object, never on a shared source block. Two objects sharing one mesh
describe different world-space geometry.
* Derive geometry and definition keys from content, quantized, not from random ids. Random keys make
material bindings fragile and defeat definition deduplication.
* An object with no properties, no geometry, and no relations is noise. Do not intern it.
### Properties
* One value per row: exactly one of the string, number, or boolean columns is set. Numbers must be
finite.
* Paths are dotted. Nesting is rebuilt from the path, never stored as a blob.
* Stamp the root scalars on every object: `speckle_type`, `name`, `units`, and the host's own
`type`.
* Parameters shared by a type go to the type tables once, linked from each object, not repeated
per object.
* Decide a policy for multi-valued properties. The reference walk drops arrays; joining them is a
valid alternative when your host emits them.
### Geometry
* Write the SGEO header exactly: magic, version, primitive type, flags, the spec's unit code, and a
CRC32 over the body, all little-endian. Consumers verify the checksum. See [Geometry encoding in
2026.9](/next/developers/object-model/geometry-encoding).
* `DISPLAY`, `SOLID`, and `CENTERLINE` each keep their own ordinal counter per object.
* Ship the solid beside the display mesh, not instead of it. Foreign hosts read the mesh.
* `CENTERLINE` is never a render edge. A consumer that drew every geometry an object owns would
draw the axis through the duct.
* Shard the geometry table at 1536 MiB of uncompressed content. Shard zero keeps the canonical name.
Readers glob all shards.
### Instancing
* A definition owns its geometry once. Each placement is an instance node with a transform of 16
row-major doubles and its own units.
* When you convert transform units, scale only the translation column. Scaling all sixteen values
resizes the instance.
* Definition members get no render edge. They keep an object row for properties and membership, and
join back through `DEFINES_MEMBER` and `PLACES`. A member with a render edge draws twice.
* Create definition nodes before you walk placements, so a placement never references a nameless
definition.
### Grouping
* Membership is an edge (`IN_COLLECTION`, `IN_MODEL`, `IN_GROUP`, `IN_SYSTEM`). Nesting of
containers is a parent reference on the container node. Do not conflate them.
* Containers are one node kind with a subtype (Layer, Collection, Folder, Model, Group, MEP System,
Network). Key them by full path so two branches with the same leaf name stay distinct.
* The tree a viewer shows is a scene view: a recipe of tiers, some relations and some property
paths. Declare one, or accept the default over `IN_COLLECTION`.
* Emit no container that would end up empty in the view.
### Appearance
* Bind materials to geometry with `HAS_MATERIAL`. The object plane only fills where geometry has
none, and is the only option for a placement.
* `HAS_COLOR` can start from geometry or from an object, and `ord` says which. Without it, the
color lands on an unrelated element.
* Resolve inherited, by-layer appearance yourself. Land the layer's material on the layer node and
on each inheriting object's own geometry.
### Topology
* Emit an edge only when both ends are objects you sent. Resolve cross-object links in a second
pass against your key map.
* `ord` is an ordinal on ordered relations and a scope tag on `CONNECTS_TO`. Read the catalog.
* `HOSTED_ON` runs hosted to host. Ownership (`SUBELEMENT`) wins over hosting when both apply.
* Never reuse a retired relation id, and never invent one.
### Provenance and column binding
* Address parquet columns through the generated constants for your language. A spec column
insertion must be a compile or import error, not a silent row shift. If your parquet library has
no schema object, add an arity assertion yourself.
* Stamp `schema_version` as the spec's semver string from the generated constant, pin the spec by
commit, and write real `produced_by` and `producer_version` values. Leave the SDK columns null when
you use none.
* Treat a dropped row as a failed publish. Count rows written, not rows attempted.
The spec is public at
[speckle-bundle-spec](https://github.com/specklesystems/speckle-bundle-spec). Build against the
generated constants under `generated/` for your language and pin to a commit. The vendored copies
in specklepy, the Archicad connector, and the SketchUp connector show what that looks like, and
every bundle carries its own `rel_types` and `node_kinds` catalogs.
Run the spec's validator on every bundle before you upload, from a checkout of the repository:
```bash theme={null}
npm run validate --
```
It checks file presence, live ids, column sets, and referential integrity. The server checks only
ETags. Close the loop in CI as well: publish to a test project, wait for `success`, receive with
an SDK, and check for unknown relations and decode errors.
## Suggested build order
Use a personal access token or your own OAuth flow. Check the ingestion permission on the model,
then open an ingestion and confirm the reserved version id is present. The outcome is a server
you know accepts bundles.
One object, one mesh, one container, a default scene view, and a correct `meta` row. Run the
spec validator on the directory. The outcome is a bundle that passes before any upload.
Sign, upload, complete, then wait for `success`. Open the version in the web app. The outcome is
a version you can see, with the publishing client attributed correctly.
Stable `applicationId` per placement, root scalars, type-level parameters, a policy for arrays.
The outcome is a property store that filters and aggregates the way your users expect.
SGEO encoding for each primitive you support, solids beside meshes, materials on geometry,
by-layer resolution. The outcome is a model that renders with the right appearance.
Definitions, placements, member suppression, then hosting, containment, and connectivity in a
second pass. The outcome is a model Speckle Intelligence can answer questions about.
Throttled progress, cooperative cancellation inside the upload, failure reporting on every exit,
geometry sharding, and retries with backoff on transient errors. The outcome is a producer that
survives large models and unreliable networks.
## FAQ
For now, yes. Upgraded servers accept it with deprecation warnings and convert it to a bundle
before the version appears, because thousands of existing integrations depend on that path. It
is not the path to build new work on: the server stores the graph and then converts it, and that
support will end. No removal date is set, and one will be announced before it is. A server
without legacy-send support rejects it with `LEGACY_SEND_UNSUPPORTED`. Treat that as the wrong
publish path, not a transient error. See [Publish through model
ingestions](/developers/migration/publish-through-ingestions).
# Developer docs for 2026.9
Source: https://docs.speckle.systems/next/developers/introduction
Preview entry for 2026.9 developer documentation. Coverage is incremental.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
2026.9-specific developer documentation starts here. Pages are added only when 2026.9 differs from
the current developer guides or needs its own write-up. For the current product, see
[Developer documentation](/developers/introduction).
The object model is the first of those differences. Today's
[Data Schema](/developers/data-schema/overview) is a tree-and-Proxy graph. 2026.9 stores properties
in a columnar store and describes containment, connectivity, systems, and materials as typed
relations.
Columnar property store and relational layer versus today's tree-and-Proxy model.
Named relation types that replace Proxies and nested `elements` walks.
Before-and-after Speckle.Sdk examples for the 2026.9 breaking-change surface.
Before-and-after specklepy examples for the 2026.9 breaking-change surface.
JS/TS, GraphQL and REST, Viewer packages, Server, Automate, and custom connectors each have a
holding note in this dropdown until those 2026.9 pages land. Use the developer docs under **Current** in the version selector for those topics.
For dates, compatibility mode, Automate SDK bumps, and who must act, see
[Data model migration for developers](/developers/migration/data-model-migration). For a compact before-and-after table, see [Compare 2026.8 and 2026.9](/next/whats-new).
# Geometry encoding in 2026.9
Source: https://docs.speckle.systems/next/developers/object-model/geometry-encoding
SGEO, the binary geometry a producer writes into a bundle, and the viewer .dat the server builds from it.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If you write geometry into a bundle yourself, you encode it as **SGEO**, a small binary format with
a fixed header and a per-primitive body. Get the header wrong and consumers reject the blob on its
checksum. If you only consume geometry, you either decode SGEO from the bundle's geometry table or
stream the **viewer `.dat`** that the server builds from it. You never write the `.dat`.
Before 2026.9, as the [Geometry Schema](/developers/data-schema/geometry-schema) describes, geometry is a list of
`displayValue` objects in JSON on each DataObject. In 2026.9 it is a table of blobs keyed by a
geometry key, attached to objects through relations. This page covers the blob format, how the table
is laid out, and what the `.dat` is for. For which relations attach geometry to what, see [Display
geometry](/next/developers/object-model/relations#display-geometry).
## Where geometry lives in a bundle
The geometry table is `{versionId}.geometries.parquet`. The writer starts a new shard,
`{versionId}.geometries.{n}.parquet`, before a blob would push the current one past 1536 MiB of
uncompressed content. Shard zero keeps the canonical name, so a small model has one file and a
reader always globs all shards.
Each row holds one blob, its dense integer key, and an id that is the SHA-256 of the blob bytes.
Because every producer encodes SGEO identically, the same mesh from any host gets the same id, and
readers deduplicate on it. A row also carries a `type` label: the SGEO primitive name (`mesh`,
`line`, `curve`, and so on) for display meshes and centerlines, or the host format, such as `3dm`,
for a `SOLID` row that carries the host's own bytes.
Nothing in the geometry table says which object a blob belongs to. That is a relation: `DISPLAY`,
`SOLID`, or `CENTERLINE` from the object, or `DEFINES` from a definition node whose placements
reuse the blob. `DISPLAY`, `SOLID`, and `CENTERLINE` each keep their own ordinal per object, so an
object's first display mesh is ordinal zero whether or not a solid was written before it. `DEFINES`
uses the definition's member ordinal instead, shared with `DEFINES_MEMBER` and `DEFINES_INSTANCE`.
## The SGEO header
Every blob starts with 16 bytes, little-endian.
| Offset | Size | Field | Value |
| - | - | - | - |
| 0x00 | 4 | magic | `SGEO` |
| 0x04 | 1 | version | `1` |
| 0x05 | 1 | primitive type | Code from the table below |
| 0x06 | 2 | flags | Bitfield from the table below |
| 0x08 | 2 | units code | `mm` 1, `cm` 2, `m` 3, `km` 4, `in` 5, `ft` 6, `yd` 7, `mi` 8, other 0 |
| 0x0A | 2 | reserved | `0` |
| 0x0C | 4 | crc | CRC-32 (IEEE, polynomial `0xEDB88320`) of the body bytes only |
| 0x10 | | body | Per primitive type |
Conventions that every encoder follows:
* The body starts 8-byte aligned at `0x10`, and every array of doubles stays 8-aligned. Pairs of
32-bit scalars are padded to keep it so.
* Doubles are IEEE-754 binary64. Face indices and colors are signed 32-bit integers.
* Derived values such as length, area, volume, bounding box, and arc radius are not stored.
Readers recompute them.
* One unit per blob. Sub-objects inside a composite lose their own units.
| Code | Primitive | Code | Primitive |
| - | - | - | - |
| 0 | Mesh | 7 | Points |
| 1 | Line | 8 | Ellipse |
| 2 | Polyline | 9 | Spiral |
| 3 | Polycurve | 10 | Box |
| 4 | Curve | 11 | Region |
| 5 | Arc | 12 | Text |
| 6 | Circle | | |
| Bit | Flag | Applies to |
| - | - | - |
| 0 | Quantized | Reserved, must be 0 in v1 |
| 1 | Closed | Polyline, curve, polycurve |
| 2 | Rational | Curve has non-uniform weights |
| 3 | Periodic | Curve |
| 4 | HasNormals | Mesh |
| 5 | HasUvs | Mesh |
| 6 | HasColors | Mesh, points |
| 7 | HasSizes | Points |
| 8 | HasTrimDomain | Ellipse |
| 9 | ScreenOriented | Text |
| 10 | HasMaxWidth | Text |
## Bodies
The mesh body is the one every producer needs.
| Order | Field | Type | Notes |
| - | - | - | - |
| 1 | vertex count | u32 | Number of vertices, so a third of the coordinate count |
| 2 | face count | u32 | Length of the face list |
| 3 | vertices | f64 × 3n | x, y, z per vertex |
| 4 | faces | i32 × face count | The same flattened face list as `Mesh.faces` in 2026.8 and earlier |
| 5 | normals | f64 × 3n | Only with `HasNormals`, padded to 8 bytes first |
| 6 | uvs | f64 × 2n | Only with `HasUvs`, padded to 8 bytes first |
| 7 | colors | i32 × n | Only with `HasColors` |
Curves follow one rule: the viewer has no NURBS evaluator, so a curve body leads with the display
polyline it renders and trails with the analytical definition (degree, points, weights, knots). A
polycurve or region nests each segment as its own SGEO blob behind an 8-byte prefix (a `u32` length
and a reserved `u32`), padded to 8 bytes. Arcs, circles, and ellipses are stored analytically.
Points and point clouds store coordinates plus optional colors and sizes.
Treat the SDK encoders as the byte-exact reference for every body: `SgeoEncoder` in Speckle.Sdk
(namespace `Speckle.Objects.Utils`) and `sgeo.py` in specklepy. Both ship decoders and round-trip
tests you can port.
The CRC covers the body only, and the geometry id covers the whole blob including the CRC. A
producer that computes either over the wrong span produces blobs that decode nowhere and never
deduplicate against anyone else's.
## The viewer `.dat`
After the upload completes, the server builds `{versionId}.viewer.dat` from the bundle and appends
it to the version's artifact list. The version is not visible until that build finishes. The WebGPU
viewer downloads only the `.dat`, never the parquet files.
The `.dat` is one self-contained binary: a geometry region of chunk-encoded meshes and lines, an
index region of fixed-order sections (meta, primitives, chunks, placements, materials, colors,
realizations), and a 128-byte trailer that maps the file. Its current format expands reusable
definitions into placements and gives each occurrence a realization id, which is what lets the
viewer select one instance of a repeated object. That identity exists only in the `.dat`. The
bundle keeps the compact authored graph.
The `.dat` is an internal artifact of the viewer and the server. Its layout can change without a
bundle spec change. Do not parse it, and do not write one.
## Read geometry from a script
Receive the version as a `Model` through `receive3` / `Receive3`. The SDK decodes SGEO into the
mesh and curve types you already use and exposes them per object. If you read the parquet files
directly through the artifacts endpoint
(`GET /api/v2/projects/{projectId}/models/{modelId}/versions/{versionId}/artifacts`, one presigned
URL per file), decode the blobs with the SDK decoder or a port of it, and read every geometry shard.
## FAQ
Read the parquet geometry table, not the `.dat`. Download every geometry shard from the
artifacts listing and open the files with any parquet library. Each row's blob starts with the
16-byte header on this page: check the magic, read the primitive type and unit code, and verify
the CRC-32 over the body. Then parse the body for that primitive, starting with the mesh layout
above. Port the decoder from `sgeo.py` in specklepy or `SgeoDecoder` in Speckle.Sdk rather than
writing one from the tables alone; both are short and tested. The `.dat` is the viewer's own
build of the same geometry and is not readable by anything else.
No. The `.dat` is built by the server and read only by the Speckle viewer. Its layout is not
published, it can change without a bundle spec change, and it carries viewer-specific
realization ids that mean nothing elsewhere. There is nothing to license because there is
nothing to build against. The portable geometry is the bundle itself: SGEO blobs in the parquet
geometry table, with open-source decoders in specklepy and Speckle.Sdk. To show a model, embed
the Speckle viewer rather than reading its artifact.
No. The solid is the host's own format and only a matching host can read it. Every consumer,
including the viewer, renders the SGEO display mesh. Ship both.
Skip that primitive, keep the owning object, and report the skip. If nothing renderable remains
and there is no solid, mark the object as failed in your results so the report matches the
bundle.
# Object model in 2026.9
Source: https://docs.speckle.systems/next/developers/object-model/overview
Columnar property store and relational layer versus today's tree-and-Proxy model.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If you write scripts, notebooks, or small automations against Speckle object data, 2026.9 changes
the shape of that data. Before 2026.9, the [Data Schema](/developers/data-schema/overview) is a directed tree of
Collections and DataObjects, with [Proxies](/developers/data-schema/proxy-schema) at the Root
Collection for shared resources. 2026.9 replaces that tree-and-Proxy graph with two things working
together: a **columnar property store** for querying and aggregation, and an **explicit relational
layer** for how elements relate.
This page is the conceptual model for that change. Dates, compatibility mode, Automate SDK bumps,
and who must act are on
[Data model migration for developers](/developers/migration/data-model-migration). For a compact
before-and-after table, see [Compare 2026.8 and 2026.9](/next/whats-new).
There is no Proxy type anywhere in 2026.9 bundle data - not renamed, not kept around as a
compatibility shim. No `RenderMaterial`, `Level`, `Group`, `Definition`, `InstanceProxy`, or
`InstanceDefinitionProxy` object exists in the format. Everything a Proxy used to carry is now
either a row in the columnar property store or an edge in the relational layer.
Detached references go with them. Before 2026.9, assigning one object to a detached property on
several parents stored it once and left a `{"speckle_type": "reference", "referencedId": ...}`
pointer in each, resolved through the parent's `__closure`. A bundle has neither: there is no
closure, and no reference object type. An object that several others need is an edge in the
relational layer, and a value that several objects share is one row each in the property store.
Standard connectors, SDKs, and APIs keep working with published data. This page matters if your
script assumes the nested object graph or unpacks Proxies itself, rather than going through
supported accessors. See [Data model migration for
developers](/developers/migration/data-model-migration).
## What's different
| Concern | Before 2026.9 | In 2026.9 |
| - | - | - |
| Object graph | Directed tree: Root Collection → Collections → DataObjects | Flat objects plus typed relations. Hierarchy is not nested `elements` |
| Properties | Nested fields on `Base` / DataObject | Columnar property store: one value per object and property path, built for filter and aggregate |
| Shared resources | Proxies at the root (`objects[]` of `applicationId`s) for materials, levels, groups, instances | Relational layer: typed edges such as `ON_LEVEL`, `HAS_MATERIAL`, `IN_SYSTEM` |
| Version root | `version.referencedObject` is the Root Collection (the commit object) carrying `units`, Proxies, and Info | No root object. Identity stays on the version record; `units` is per object; model-wide facts are model-scoped properties. See [Version metadata](/next/developers/object-model/version-metadata) |
| Topology | Reconstruct from Proxies and nested `elements` | Containment, connectivity, systems, and materials are first-class relations. Speckle Intelligence queries this layer |
| Publishing | Build one root object whose `elements` holds everything, send it, then create a version pointing at its id | Build a bundle with no top, upload it through a model ingestion. The server creates the version when the upload completes |
| Script that walks a tree | Receive, traverse `Collection.elements`, resolve Proxy `objects` | Identify objects, read properties from the columnar store, follow named relations. Do not unpack Proxies to rebuild those links |
Project → Model → Version remains the address of a published snapshot. That addressing system is
unchanged. What lives at a version is what changed.
What a version points at also changed. There is no root object to start from; see [Where version
metadata lives in 2026.9](/next/developers/object-model/version-metadata) for what carries the
version's units, source application, and model-wide facts instead.
## Columnar property store
Before 2026.9, a wall's parameters live on the DataObject (and on nested geometry in `displayValue`). To
total area or filter by type, a script walks the tree and reads nested fields.
In 2026.9, object identity stays stable (`applicationId` from the source application). Attributes
are stored as rows in a columnar property store: each row is one object, one property path, and one
value. That layout is what makes filter, group, and aggregate cheap on large models, instead of
walking a reconstructed graph.
Treat property paths as the public names of attributes (for example a category or type name), not
the JSON shape of a received `Base` object. Nested dictionaries belong to the object graph of
2026.8 and earlier.
This page does not document a query API or SQL surface. Use supported SDK accessors for the data you
already read. If a tool assumes the old object-graph shape and breaks, follow
[Migration guidance](/developers/migration/data-model-migration#migration-guidance).
## Relational layer
Proxies solved overlapping membership without duplicating geometry: a wall could sit on a level, use
a material, and belong to a group by appearing in each Proxy's `objects` list. The collection tree
was still the only hierarchy a traversal walked.
2026.9 puts those links beside the property store as **typed relations**. Containment, connectivity,
systems, and materials are richer than today's Proxies: they include host-to-child containment, MEP
and spatial connectivity, and system membership, not only shared resources at the root.
The named relation types, and how they compare to Proxies, are on
[Relations in 2026.9](/next/developers/object-model/relations).
Speckle Intelligence uses both halves. It looks up properties on objects, and it follows relations
for questions such as what connects to what and what contains what. That behaviour is in the
[comparison table](/next/whats-new).
## Publishing creates the version on the server
Before 2026.9, a publish is a client-side act in two steps. You assemble one [root
object](/developers/data-schema/version-root-object), put every object under its `elements` (with Proxies beside them at the root), send that graph to get its id, then
create a version that points at the id. The root object is both the data and the anchor, and the
client decides the moment the version exists.
In 2026.9 there is no root to assemble and no version to create. A bundle builder holds objects,
grouping containers, and relations side by side, with no top. `send3` / `Send3` opens a model
ingestion, which reserves a version id, uploads the bundle under that id, and completes the
ingestion. The server creates the version at that point, in one step.
Three habits change with this:
* **There is no `elements` to fill.** Membership is a relation such as `IN_COLLECTION` or
`ON_LEVEL`, set on the object, not a list on a parent.
* **Version metadata is given up front.** The source application and message go to the ingestion
when it opens, not onto a root object at the end.
* **The version exists only after ingestion completes.** A publish returns a reserved id. Wait for
the version before you query, link, or receive it.
The full sequence, and where each piece of metadata now lives, is on [Where version metadata lives
in 2026.9](/next/developers/object-model/version-metadata). If you build a producer without a
Speckle SDK, the file set, wire sequence, and bundle rules are on [Building integrations in
2026.9](/next/developers/building-integrations).
## What a script should assume
Resolve project, model, and version the same way you do today. The snapshot you point at is
still a version. The outcome is a published model you can read.
Key objects by source identity (`applicationId`). Read attributes from the columnar property
store by property path. The outcome is values you can filter or aggregate without walking nested
`Base` fields.
Use named relations for level, material, containment, connectivity, and systems. The outcome is
membership and connectivity without unpacking a Proxy's `objects` array. See [Relations in
2026.9](/next/developers/object-model/relations).
If you maintain tooling that reads Speckle data more directly than the standard SDK or API
surface, and it breaks against the new format, pin to **2026.8** while you update. Compatibility
mode for ACC sync and drag-and-drop uploads ends **1 November 2026**. Details: [Data model
migration for developers](/developers/migration/data-model-migration).
## What is not on this page
Connector-specific dumps, SDK before-and-after samples, and a SQL or local query API are separate
work. Until those pages exist, use the [Data Schema](/developers/data-schema/overview) for
the object graph of 2026.8 and earlier, and the [developer migration page](/developers/migration/data-model-migration) for
who is affected in 2026.9.
## FAQ
Yes, for versions published in today's tree-and-Proxy format. Use [Data Schema
overview](/developers/data-schema/overview), [Concepts](/developers/data-schema/concepts), and
[Proxy schema](/developers/data-schema/proxy-schema) for that graph. Use this page for 2026.9
bundle data.
No. Existing connectors, SDKs, and integrations continue to work with published data. Watch
Automate functions that process versions from a connector that only writes the new bundle
format: bump the SDK dependency. See [Who's
affected](/developers/migration/data-model-migration#whos-affected).
That reconstruction is the object graph of 2026.8 and earlier. On 2026.9 data, containment and
shared resources live on the relational layer, and properties live in the columnar store. A
missing nested `elements` tree or Proxy `objects` list is expected. Update the script to
identify objects, read property paths, and follow named relations — or pin to 2026.8 until you
can.
See [Relations in 2026.9](/next/developers/object-model/relations). That page maps today's
Proxies to typed edges such as `ON_LEVEL`, `SUBELEMENT`, `CONNECTS_TO`, and `IN_SYSTEM`.
No. There is no `RenderMaterial`, `Level`, `Group`, `Definition`, `InstanceProxy`, or
`InstanceDefinitionProxy` object in the format - not hidden, not deprecated-but-present. If your
code checks for a Proxy type or a root-level `*Proxies` array on 2026.9 data, it will find
nothing, not an empty compatibility stand-in. See [Relations in
2026.9](/next/developers/object-model/relations) for what carries that information now.
# Relations in 2026.9
Source: https://docs.speckle.systems/next/developers/object-model/relations
Typed containment, connectivity, geometry, systems, and materials — richer than today's Proxies.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Before 2026.9, as the [Data Schema](/developers/data-schema/overview) describes, overlapping membership is a
[Proxy](/developers/data-schema/proxy-schema) at the Root Collection: a material, level, group, or
definition lists `applicationId`s in `objects`. Hierarchy is a tree you walk through
`Collection.elements`.
2026.9 replaces that reconstruction with an **explicit relational layer**: typed, directed edges
between objects (and, where needed, structural nodes such as a level, material, or grouping
container). For the two-store picture, see [Object model in 2026.9](/next/developers/object-model/overview).
Dates and who must act are on
[Data model migration for developers](/developers/migration/data-model-migration).
Proxies themselves do not exist in 2026.9 bundle data - there is no `RenderMaterial`, `Level`,
`Group`, `Definition`, `InstanceProxy`, or `InstanceDefinitionProxy` object anywhere in the format.
The relation types below are what carries that information instead.
## Why relations exist
Proxies let one object participate in several organizations without copying geometry. They do not
encode connectivity, hosting, or room occupancy as a graph a consumer can query. 2026.9 keeps the
"no duplication" rule and makes those links first-class, which is what lets Speckle Intelligence
answer topology questions instead of only looking up properties on each object.
Rooms and MEP spaces that carry geometry are **objects** in this layer, not grouping nodes. A
furniture item's room membership is a relation to that room object (`IN_ROOM`), not a Proxy list on
a container.
## Relation types you will see
Names below are the live vocabulary of the relational layer (the same names the 2026.9 viewer
inspector uses). A connector emits only the types that exist in the source model. Omit a row from
your script's assumptions unless that host actually publishes it.
### Containment and grouping
These replace nested `elements` walks and most organisational Proxies.
| Relation | From → to | Replaces, in 2026.8 and earlier | When you use it |
| - | - | - | - |
| `SUBELEMENT` | object → object | Nested DataObject `elements` (curtain wall → panels, stair → runs) | Host-to-child ownership the property store cannot encode |
| `IN_COLLECTION` | object → grouping container | Collection tree / layer membership | Authored layer, category, or collection membership |
| `IN_MODEL` | object → grouping container | Source-file / federation grouping | Which source model an object came from |
| `IN_GROUP` | object → grouping container | Group Proxy | Authored groups (for example Rhino or AutoCAD). Distinct from layer |
| `IN_ROOM` | object → room object | Not a first-class Proxy graph | Spatial occupancy (furniture, door, window → room) |
| `IN_ASSEMBLY` | member object → assembly object | Host-specific nesting | Fabrication membership (for example Tekla). Distinct from `SUBELEMENT` |
| `ON_LEVEL` | object → level | Level Proxy | Level membership |
| `IN_SYSTEM` | object → system or network container | System / network grouping | MEP system membership. Derived networks use the same relation; the container subtype distinguishes them |
Grouping containers nest among themselves (a collection tree). That nesting is structural. It is not
an object-to-object `elements` walk.
### Connectivity
These are new as a queryable graph. Before 2026.9 there is no Proxy equivalent.
| Relation | From → to | Meaning |
| - | - | - |
| `CONNECTS_TO` | object → object | Directed connectivity. Scope distinguishes MEP flow, opening/room adjacency, and unscoped links |
| `HOSTED_ON` | object → object | Hosted element → host (door or window → wall). Different from `SUBELEMENT` ownership |
| `BOUNDS` | object → object | Bounding wall → room. Room footprint, not occupancy |
`CONNECTS_TO` is directed in the store. Neighborhood queries used by the product treat it as a
one-hop connection in either direction.
### Display geometry
An object's own geometry is a relation too, not a `displayValue` array on the object. The blob
format behind these rows is on [Geometry encoding in
2026.9](/next/developers/object-model/geometry-encoding).
| Relation | From → to | Replaces, in 2026.8 and earlier | When you use it |
| - | - | - | - |
| `DISPLAY` | object → geometry | `displayValue` meshes on the object | The object's own render mesh (walls, in-place elements). Instanced objects use `DISPLAY_INSTANCE` instead |
| `SOLID` | object → geometry | The host solid (for example a Rhino Brep) beside its `displayValue` | A true solid body where the host has one (Rhino, Civil 3D). Reach for it when you need the body, not the tessellated shell |
| `CENTERLINE` | object → geometry | `baseCurve` / `baseLine` on the object | The axis a duct, pipe, conduit, or framing member follows. Routing, length take-off, clash lines, and single-line drawings |
`DISPLAY` is what renders. `SOLID` and `CENTERLINE` are additional shapes for consumers that need
the body or the axis. Do not draw every object-to-geometry relation, or the centerline appears
through the middle of the duct.
### Materials and appearance
| Relation | From → to | Replaces, in 2026.8 and earlier |
| - | - | - |
| `HAS_MATERIAL` | geometry → material | RenderMaterial Proxy bound to display meshes |
| `HAS_COLOR` | geometry → color | Display color, kept distinct from material |
| `OBJECT_HAS_MATERIAL` | object → material | Placement / instance paint when the object carries it |
| `OBJECT_HAS_COLOR` | object → color | Object-plane color override |
| `NODE_HAS_MATERIAL` | container → material | Authored layer or tag material (ByLayer fill) |
| `NODE_HAS_COLOR` | container → color | Authored layer or tag color (ByLayer default) |
Material and color are separate on purpose. An object can carry both. Material fills from specific
to general: geometry `HAS_MATERIAL`, then `OBJECT_HAS_MATERIAL`, then the container's
`NODE_HAS_MATERIAL`. Color is presentational: `OBJECT_HAS_COLOR` overrides geometry `HAS_COLOR`,
and `NODE_HAS_COLOR` is the ByLayer default.
### Instancing
The Definition and Instance Proxies of 2026.8 and earlier become structural nodes plus relations.
| Relation | From → to | Replaces, in 2026.8 and earlier |
| - | - | - |
| `DEFINES` | definition → geometry | Definition Proxy holding shared geometry |
| `DISPLAY_INSTANCE` | object → instance placement | Instance that renders with a transform |
| `DEFINES_INSTANCE` | definition → nested instance | Nested instancing inside a definition |
| `DEFINES_MEMBER` | definition → member object | Definition membership on the object plane (geometry and nested alike) |
| `PLACES` | member object → instance | Association without rendering, so member properties stay reachable |
`DISPLAY_INSTANCE` is the render root. `PLACES` is the same object-to-instance link without
drawing. Do not treat a definition member as a render placement, or it will draw untransformed at
the origin.
## What a script should do instead of unpacking Proxies
Use the same project, model, and version address you use today. The outcome is the snapshot you
intend to read.
Key source elements by `applicationId`, not by their position in a nested `elements` tree. The
outcome is a stable object set across versions. If you are producing data rather than only
reading it, this only holds if every placement's `applicationId` is unique — a repeated id
resolves to the same object, not a second occurrence.
Take attributes from property paths on those objects. See [Columnar property
store](/next/developers/object-model/overview#columnar-property-store). The outcome is values
you can filter without walking nested `Base` fields.
For membership, hosting, or connectivity, use relation names such as `ON_LEVEL`, `SUBELEMENT`,
`CONNECTS_TO`, and `IN_SYSTEM`. The outcome is topology without unpacking a Proxy `objects`
array. That join is already the relation.
This page does not document SQL, a local query API, or copy-paste SDK calls. Those surfaces are
separate. Until they ship in these docs, use supported SDK accessors and the
[developer migration page](/developers/migration/data-model-migration) for breakage and pinning.
## FAQ
No. `HOSTED_ON` is placement on a host (a door on a wall). `SUBELEMENT` is ownership (a curtain
wall owns its panels). A door is hosted on a wall; it is not a component of the wall.
The edge is absent. Scripts should treat a missing relation type as "not in this model," not as
a failed receive. Only hosts that have that concept in the source will emit it.
That walk is the tree of 2026.8 and earlier. On 2026.9 data, use `IN_COLLECTION`, `IN_MODEL`, `ON_LEVEL`, and
`SUBELEMENT` instead of assuming a nested `elements` graph. See [Object model in
2026.9](/next/developers/object-model/overview).
No. Detaching and the `reference` object type belong to the object graph of 2026.8 and earlier;
a bundle has no `__closure` to resolve them through. Publish the shared thing as its own object
and relate the others to it, or, if what you are sharing is really a value rather than an
object, write that value as a property on each object. See [Object model in
2026.9](/next/developers/object-model/overview).
No. It is the authored location curve. A Revit wall's location line follows its Location Line
setting and can be a core or finish face. A point-placed MEP fitting (elbow, tee, cross) has no
location curve, so it emits one `CENTERLINE` segment per connector, ordered by connector.
No, and that is not the intent. Relations describe how finished objects relate: containment,
hosting, connectivity, appearance, instancing. They do not describe how an object was made, and
nothing in Speckle rebuilds geometry from a recipe. What you can carry: the exact body through
`SOLID` in the host's own format, feature parameters as properties, and ownership through
`SUBELEMENT`. A dependency order can travel in your own edge file, which no Speckle consumer
reads. Exchanging modelling history would need operation node kinds in the catalog and a
modelling kernel on the receiving side. Neither exists today.
Yes, as a resolved structure. Parts are objects. A sub-assembly owns its parts through
`SUBELEMENT`, or joins them through `IN_ASSEMBLY` when membership is the point. A part used
many times is one definition placed by instances, each with its own transform, and a repeated
sub-assembly is a nested definition. Part-to-part contact is `CONNECTS_TO`, and exact bodies
ride `SOLID`. What does not travel is the constraint system: mates such as coincident or
concentric, degrees of freedom, and the solver that positions parts from them. You publish the
positions those constraints produced, not the constraints.
Yes. Store custom edges in your own parquet files, not in the relation files Speckle SDKs
generate — rewriting those files mismatches on the next republish, and you own whatever you add.
Do not mint unused numeric relation type ids from the Speckle catalog; Speckle reserves those
numbers for future platform types. Which cases need a custom file, and which are really an
existing relation or a property, is on [Building integrations in
2026.9](/next/developers/building-integrations#relations-you-do-not-need-and-relations-you-add).
They are treated as the same object referenced twice, not as two occurrences.
`applicationId` is not required on every object, but wherever it is relied on as identity — a
`DISPLAY_INSTANCE` or `DEFINES_INSTANCE` placement, a `Flatten().ToDictionary(o =>
o.applicationId)` lookup, any join back to a source element — that identity has to be unique
for the objects relying on it. A producer that reuses an id across two distinct placements
will see the second one silently collapse onto the first: same node, same transform, no error
raised.
# Where version metadata lives in 2026.9
Source: https://docs.speckle.systems/next/developers/object-model/version-metadata
There is no root object in 2026.9 bundle data. What carries a version's identity, source application, units, and model-wide facts instead.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If your script starts from `version.referencedObject`, reads `units` off the root object, or
walks down from it, that script breaks on a 2026.9 version. Bundle data has **no root object**. The
jobs the root object used to do are split across four places: the version record on the server,
the model ingestion that publishes it, a one-row provenance stamp inside the bundle, and
model-scoped properties. This page says where each piece went and what a script reads instead.
Before 2026.9, as the [Data Schema](/developers/data-schema/overview) describes, a version points at one object:
`version.referencedObject` is the id of the [Root
Collection](/developers/data-schema/concepts#root-collection), historically called the commit
object. That object is the entry point for every traversal, and it carries the model-wide facts a
script reads first: `units`, a `name`, the nested `elements` tree,
[Proxies](/developers/data-schema/proxy-schema), and [Info](/developers/data-schema/concepts#info)
such as views and the reference point transform. The version record beside it carries the message,
source application, author, and creation date. [Versions and the root
object](/developers/data-schema/version-root-object) covers that split for 2026.8 and earlier in full.
For how objects relate to each other in 2026.9, see [Relations in
2026.9](/next/developers/object-model/relations). For the two-store picture, see [Object model in
2026.9](/next/developers/object-model/overview).
## What the root object carried, and where it lives now
| Before 2026.9 | In 2026.9 | Read it with |
| - | - | - |
| Version id, message, author, created date on the version record | Unchanged shape on the version record. The record exists only after ingestion completes | `client.version.get` / `client.Version.Get` |
| `sourceApplication` on the version record, set by `version.create` | Set when the ingestion opens, from the producer slug and version you give the bundle builder | `Version.sourceApplication` |
| `referencedObject` is the root object's content hash | A bundle reference: `bundle...`. `Version.schemaVersion` is `3` | Dispatch on the `bundle.` prefix. Never fetch it from the objects endpoints |
| `units` on the root object | A `units` property on every object. The bundle builder requires a default unit and stamps it per object | `Model.units` / `Model.Units`, or the object's own `units` property |
| Root `name` and the nested `elements` tree | No root node. Grouping containers (subtypes Model, Layer, Collection, Folder, Group) joined to objects by `IN_MODEL` and `IN_COLLECTION`. A scene view picks the tree | [Relations](/next/developers/object-model/relations#containment-and-grouping) |
| Proxy lists on the root (materials, levels, groups, instances) | Typed relations | [Relations](/next/developers/object-model/relations) |
| Info: views, `referencePointTransform`, analysis results | Model-scoped properties (`referencePoint.*`, document settings, project information), camera views, and optional structural results | `Model.properties` / `Model.Properties`, `Model.camera_views` / `Model.CameraViews` |
| `totalChildrenCount` and the `__closure` table | The object count is reported when the upload completes. There is no closure table | `Version.totalChildrenCount` |
| Not carried | A `meta` row in the bundle: `schema_version`, `produced_by`, `producer_version`, `sdk_name`, `sdk_version` | `Model.Meta` (.NET). Scripts rarely need it |
Two rows need more detail because scripts depend on them today.
**The bundle reference is a pointer, not a hash.** On a bundle-only version, `referencedObject`
names the version, not its content. Resolve the version through the project and version id you
already hold, then read its data with `receive3` / `Receive3`. The embedded model id is the model
at creation time and goes stale if the version is later moved.
**Units are per object, not per version.** A bundle can carry more than one unit. The SDK's
`Model.units` is the first non-empty unit it finds, which is the builder's default on data you
published yourself. When precision matters, read the object's own `units` property.
## How a publish creates a version
`send3` / `Send3` runs all of this for you. Knowing the sequence explains why the version is not
queryable the moment the call returns, and where each piece of metadata is set.
The SDK creates an ingestion on the model, carrying the producer slug and version from your
bundle builder and the message from your send options. The outcome is an ingestion with a
reserved version id.
The builder writes its parquet files, named by the reserved version id, and stamps the `meta`
row with the spec version and producer. The outcome is a complete bundle on local disk.
Each file goes to storage through a presigned upload. The outcome is every file stored under the
version id.
The SDK completes the ingestion with the bundle reference and the object count. The outcome is
the server creating the version in one step, with `referencedObject` set to the bundle reference
and `schemaVersion` set to `3`.
`send3` returns the reserved id immediately. Poll the ingestion, or call `WaitForVersion` in
.NET, before you query, link, or receive it. The outcome is a version that answers queries and
arrives as `CREATED` on the `projectVersionsUpdated` subscription.
`account`, `project_id` / `projectId`, and `model_id` / `modelId` are already in scope.
```python theme={null}
from specklepy.api import operations
from specklepy.bundle import BundleBuilder, Producer, SendOptions
builder = BundleBuilder(Producer(slug="my-script", version="1.0"), units="m")
# add objects, containers, levels, and relations on the builder
result = operations.send3(
account, project_id, model_id, builder, SendOptions(message="Nightly export")
)
print(result.version_id, result.bundle_reference)
```
```csharp theme={null}
var producer = new SpeckleApplication
{
HostApplication = "My script",
HostApplicationVersion = "1.0",
Slug = "my-script",
SpeckleVersion = "2026.9",
};
using var builder = new BundleBuilder(producer, units: "m");
// add objects, containers, levels, and relations on the builder
var sent = await SpeckleBootstrap.Operations.Send3(
account, projectId, modelId, builder, new SendOptions(Message: "Nightly export"), ct
);
var version = await SpeckleBootstrap.Operations.WaitForVersion(account, sent, cancellationToken: ct);
```
`version.create` / `Version.Create` still exist for object graphs from 2026.8 and earlier, and
they too return a reserved id now. See [Publish through model
ingestions](/developers/migration/publish-through-ingestions) for that path.
Automate functions publish the same way. `CreateNewVersionInProject` accepts a bundle builder and
returns the reserved version id.
## Read version metadata from a script
Read the **version record** for identity and provenance: `message`, `sourceApplication`,
`authorUser`, `createdAt`, `schemaVersion`, and `referencedObject`. This is the same query you run
today.
Read **model-wide facts** from the received `Model`, not from a root object. `units` gives the
default unit. `properties` holds the model-scoped rows: the reference point (`referencePoint.kind`,
`referencePoint.transform`, `referencePoint.units`) and any document settings or project information
the connector published. `camera_views` holds saved cameras.
Legacy `receive` / `Receive2` on a bundle-only version returns a compatibility projection. Its
root is a `Collection` named `Received model` with the constant id `artifact-root`, carrying
`units`, `version = 4`, and rebuilt proxy lists. That root is built when you receive, not stored
in the bundle. Do not key on its id, and do not read a message or source application from it.
## FAQ
No. Start from the version and receive it as a `Model`, then key objects by `applicationId` and
follow relations. A container with subtype `Model` exists as the `IN_MODEL` target, but it is a
grouping tier for federated source files, not a root, and it carries only a name.
They are different numbers. `Version.schemaVersion` is the server's storage-shape flag: `3`
means the version is served as a bundle, `null` means an object graph from 2026.8 or earlier.
`meta.schema_version` inside the bundle is the semver of the bundle format the producer wrote
against. Neither moves when the other does.
It gets a 404 with a `BUNDLE_REFERENCE_NOT_FOUND` body. That is the contract, not empty data.
Dispatch on the `bundle.` prefix and receive the version through the SDK instead.
Add them as model-scoped properties on the bundle builder, not on an object. That is the home
for document settings, project information, and a reference point. Units go on the builder, or
on an individual object when it differs from the default.
# Assess .NET SDK breaking changes
Source: https://docs.speckle.systems/next/developers/sdks/dotnet/breaking-changes
Before-and-after Speckle.Sdk examples so you can judge 2026.9 migration impact.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Use this page to judge what a Speckle.Sdk script must change for 2026.9. It is not a replacement
for the [scripts and notebooks](/developers/sdks/dotnet/getting-started/scripts-and-notebooks)
guide, and it is not a full API reference.
Prior to 2026.9, the path is still **PAT → bootstrap → `Receive2` → `Flatten`**. In 2026.9 that
receive is obsolete. The new path is **`Receive3`**, which returns a disposable `Model` with columnar
properties and typed relations. Dates, compatibility mode, and who must act are on
[Data model migration for developers](/developers/migration/data-model-migration). The object-model
change itself is on [Object model in 2026.9](/next/developers/object-model/overview).
Versions published by a 2026.9 connector need a Speckle.Sdk **2026.9** build. An older SDK 404s on
that data. Versions from older connectors keep working on the SDK you have today. See [Who's
affected](/developers/migration/data-model-migration#whos-affected).
Prefer `Receive3` for new scripts. `Receive2` stays callable on legacy object-graph versions.
## What changes for a script
| Concern | Prior to 2026.9 | In 2026.9 |
| - | - | - |
| Packages | `Speckle.Sdk` plus `Speckle.Objects` | One `Speckle.Sdk` package. `Speckle.Objects` is a type-forwarding shell you can delete |
| Load a version | `Receive2` with `version.referencedObject` | `Receive3` with a model URL or project, model, and version ids. Returns `Speckle.Sdk.Bundles.Model` |
| Filter and count | `Flatten()` then `DataObject.properties` | `model.Objects`, `ObjectsWith`, and `GetString` / `GetDouble` |
| Nested property walk | `properties["x"]` then `["y"]` | Dotted path (`GetString("x.y")`) or `PropertyView.Under` |
| Level, material, groups | Unpack proxy `objects` lists | Accessors on `ModelObject` (`Level`, `Material`, `Collection`) |
| Publish from a script | `Send2` then `Version.Create` | `Send3` with a `BundleBuilder`. The version is visible only after ingestion completes |
| Transports | `Send` / `Receive` with `ITransport` | Frozen legacy surface. A bundle reference through transport `Receive` throws |
## Update packages
Bump every Speckle NuGet reference to the same **2026.9** version, then remove `Speckle.Objects`.
Namespaces stay `Speckle.Objects.*`, so usings do not change.
```bash theme={null}
dotnet add package Speckle.Sdk
```
Then delete the `Speckle.Objects` package reference from the project file. Pin an explicit 2026.9
build rather than a floating range.
Mixing a pre-merge `Speckle.Objects` with a merged `Speckle.Sdk` fails the build. Update every
Speckle package together.
You can drop `typeof(Speckle.Objects.Geometry.Point).Assembly` from `AddSpeckleSdk` unless you
register your own `Base` types. Geometry types now live in `Speckle.Sdk`.
## Load a model
Prior to 2026.9, you resolve `referencedObject` and call `Receive2`. In 2026.9, `Receive2` is marked
`[Obsolete]`. `Receive3` takes the same account and ids and returns a disposable `Model`. Wrap that
`Model` in `using`: it owns the downloaded bundle files on disk until you dispose it.
Auth, bootstrap, and version lookup stay on [scripts and notebooks](/developers/sdks/dotnet/getting-started/scripts-and-notebooks).
The snippets below assume `account`, `client`, `projectId`, `modelId`, and `latest` are already in scope.
```csharp Prior to 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
var root = await SpeckleBootstrap.Operations.Receive2(
client.ServerUrl,
projectId,
latest.referencedObject!,
account.token
);
```
```csharp 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
using var model = await SpeckleBootstrap.Operations.Receive3(
account,
projectId,
modelId,
latest.id
);
```
`Receive3` can also take the model URL the web app copies. Omit `@versionId` to receive the latest
version without a version id.
## Count and filter objects
Prior to 2026.9, you walk a `Base` tree. In 2026.9, objects are a flat list. Properties are path-keyed
(`"category"`, `"Constraints.Base Offset"`). Relations are accessors, not Proxy lists.
```csharp Prior to 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
var counts = root.Flatten()
.OfType()
.GroupBy(d => d.properties.GetValueOrDefault("category") as string ?? "Unknown");
var byLevel = root.Flatten()
.OfType()
.GroupBy(d => d.properties.GetValueOrDefault("level")?.ToString() ?? "Unknown");
var walls = root.Flatten()
.OfType()
.Where(d => d.properties.GetValueOrDefault("category") as string == "Walls");
```
```csharp 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
var counts = model.Objects.GroupBy(o => o.GetString("category") ?? o.Name ?? "Unknown");
var byLevel = model.Objects.GroupBy(o => o.Level?.Name ?? "Unknown");
var walls = model.ObjectsWith("category").Where(o => o.GetString("category") == "Walls");
```
Look up one object with `model.ObjectByApplicationId(id)`. `applicationId` is the only identity a
bundle object has. Do not treat `Base.id` as a content hash on bundle-only versions.
## Replace property walks
Prior to 2026.9, nested parameters are dictionaries you walk: `obj.properties["x"]` then `["y"]`. In
2026.9 those paths are flat. `PropertyView` is the type that replaces that walk.
### PropertyView
`PropertyView` (`Speckle.Sdk.Pipelines.Receive.Artifacts`) is a read-only view over one object's
property rows. Keys are dotted paths (`"Constraints.Base Offset"`), not nested dictionaries. It does
not allocate until you enumerate it. `ModelObject.Properties` is this view; cast to `PropertyView`
when you need the helpers below.
* **`GetString` / `GetDouble` / `GetBool`:** typed lookup. Null if the path is missing or the value
is another type.
* **`Under("Constraints")`:** the subtree under that prefix, with the prefix stripped. No allocation.
* **Indexer / `TryGetValue`:** untyped lookup, same paths as `GetString`.
* **`ToNested()`:** the old nested-dictionary tree. Allocates. Use only when a caller still requires
that shape.
`ModelObject.GetString`, `GetDouble`, and `GetBool` do the same typed lookup with instance, then
type, then root-scalar precedence. Prefer those for a single path. Use `PropertyView.Under` when you
need a group.
```csharp Prior to 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
var wall = root.Flatten().OfType().First();
var groups = wall.properties.GetValueOrDefault("parameters") as Dictionary;
var x = groups?.GetValueOrDefault("x") as Dictionary;
var y = x?.GetValueOrDefault("y");
```
```csharp 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
var wall = model.Objects.First();
var y = wall.GetString("parameters.x.y");
var offset = wall.GetDouble("Constraints.Base Offset");
var constraints = ((PropertyView)wall.Properties).Under("Constraints");
foreach (var kv in constraints)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
```
## Replace Proxy walks
Prior to 2026.9, grouping by level or material means indexing `applicationId`s and resolving proxy `objects`
lists. In 2026.9 those links are typed relations. See
[Relations in 2026.9](/next/developers/object-model/relations) for the names.
```csharp Prior to 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
var byAppId = root.Flatten()
.Where(o => o.applicationId is not null)
.ToDictionary(o => o.applicationId!);
```
```csharp 2026.9 lines icon="https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251" theme={null}
var byLevel = model.Objects.GroupBy(o => o.Level?.Name ?? "Unknown");
var hosted = model.Objects.Where(o => o.Host is not null);
```
A missing nested `elements` tree or Proxy `objects` list on 2026.9 data is expected. Do not unpack
Proxies to rebuild containment.
## What Receive2 still does
`Receive2` remains callable. It is obsolete for a reason.
* **Legacy object-graph versions:** behaviour is unchanged. You still pass `referencedObject` as a
content hash.
* **Bundle-only versions:** `referencedObject` is a bundle reference
(`bundle...`). A 2026.9 SDK dispatches on that prefix and returns a
best-effort `Base` tree (DataObject idiom, `version = 4` on the root). That projection does not
scale to the model sizes the bundle format is built for, and it is not a lossless round trip.
* **Older SDKs:** fetching that `referencedObject` from the objects API returns **404** with upgrade
guidance. Upgrade Speckle.Sdk rather than parsing the 404 body.
Transport-based `Receive` cannot load bundle-only versions. It throws if you pass a bundle
reference.
Receiving a bundle-only version as a `Base` tree and sending it again with `Send2` is not a
supported copy workflow. The tree is a baked view, not the authored graph. Receive, derive new
data, then send remains ordinary supported use.
## Publish from a script
Most analysis scripts only receive. If you currently call `Send2` then `Version.Create`, the
2026.9 replacement is `Send3` with a `BundleBuilder`. `Send2` stays accepted and starts emitting
deprecation warnings. There is no removal date.
`Send3` returns a reserved version id immediately. Queries, the version URL, and the
`version_create` webhook fire when ingestion completes. Poll for the version, or watch the
project's model ingestions, if you need to follow up in the same process.
## FAQ
Upgrade Speckle.Sdk first so versions from a 2026.9 connector do not 404. Versions from older
connectors keep working on `Receive2`. Rewrite to `Receive3` when you need relations, columnar
property lookups, or models that are too large to materialize as a `Base` tree.
`Model` is the bundle itself: objects, path-keyed properties, and relation accessors, with
geometry parsed only when you ask. A `Base` tree inflates every object and mesh into managed
objects. That is why `Receive2` is obsolete for the new format.
Downloaded parquet files stay under a scratch directory until `Dispose` runs. Always wrap the
result in `using`. Objects and already-loaded geometry stay in memory after dispose; geometry
you have not touched yet cannot be read afterwards.
Yes for hash-era data, until that path is retired. It will never carry bundles. Move
server-facing scripts to `Receive3`. See the [send and receive
paths](/developers/sdks/dotnet/concepts/send-and-receive-paths) page for the transport story.
# Assess specklepy breaking changes
Source: https://docs.speckle.systems/next/developers/sdks/python/breaking-changes
Before-and-after specklepy examples so you can judge 2026.9 migration impact.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Use this page to judge what a specklepy script must change for 2026.9. It is not a replacement for
the [specklepy](/developers/sdks/python/introduction) guides, and it is not a full API reference.
Prior to 2026.9, the path is still **PAT → `operations.receive` → walk a `Base` tree**. In 2026.9
that receive is a compatibility shim. The new path is **`operations.receive3`**, which returns a
disposable `Model` with columnar properties and typed relations. Dates, compatibility mode, and who
must act are on
[Data model migration for developers](/developers/migration/data-model-migration). The object-model
change itself is on [Object model in 2026.9](/next/developers/object-model/overview).
Versions published by a 2026.9 connector need specklepy **2026.9.0b3** or later, installed with
the `bundle` extra. An older build 404s on that data. Versions from older connectors keep working
on the specklepy you have today. See [Who's
affected](/developers/migration/data-model-migration#whos-affected).
Prefer `operations.receive3` for new scripts. `operations.receive` stays callable on legacy
object-graph versions, and on bundle-only versions it returns a projected `Base` tree.
## What changes for a script
| Concern | Prior to 2026.9 | In 2026.9 |
| - | - | - |
| Package | `pip install specklepy` | `pip install "specklepy[bundle]==2026.9.0b3"`. The `bundle` extra pulls in pyarrow |
| Load a version | `operations.receive` with `referenced_object` | `operations.receive3` with account plus project, model, and version ids. Returns `Model` |
| Filter and count | Walk `elements`, then `DataObject.properties` | `model.objects`, `objects_with`, and `get_string` / `get_double` |
| Nested property walk | `properties["x"]` then `["y"]` | Dotted path (`get_string("x.y")`) or `PropertyView.under` |
| Level, material, groups | Unpack proxy `objects` lists | Accessors on `ModelObject` (`level`, `host`, `collection`) |
| Publish from a script | `operations.send` then `client.version.create` | `operations.send3` with a `BundleBuilder`. The version is visible only after ingestion completes |
| Transports | `send` / `receive` with `ServerTransport` | Frozen legacy surface. A bundle reference through `receive` needs an authenticated `ServerTransport` |
## Update specklepy first
Install a 2026.9 specklepy that can receive bundle-only versions, including the `bundle` extra.
**2026.9.0b3** is the first public build that does this. A later stable **2026.9.0** replaces the
beta pin; the calls on this page do not change.
```bash theme={null}
pip install "specklepy[bundle]==2026.9.0b3"
```
On a bundle-only version, `version.referenced_object` is a bundle reference such as
`bundle...`, not a content hash. An older specklepy treats that
string as an object id, requests it from the objects API, and receives a 404 whose body tells you
to upgrade.
Do not fetch `version.referenced_object` from the objects REST endpoints. Bundle references always
404 there, including after you upgrade. Pass ids to `operations.receive3`, or pass the reference
to `operations.receive` and let specklepy dispatch.
## Load a model
Prior to 2026.9, you resolve `referenced_object` and call `operations.receive`. In 2026.9,
`operations.receive3` takes an `Account` plus project, model, and version ids and returns a
disposable `Model`. Use it as a context manager: it owns the downloaded bundle files on disk until
the `with` block ends.
Auth and version lookup stay on [Authentication](/developers/sdks/python/getting-started/authentication)
and the [Quickstart](/developers/sdks/python/getting-started/quickstart). The snippets below assume
`account`, `client`, `transport`, `project_id`, `model_id`, and `version_id` are already in scope.
Build `account` with `Account.from_token` and a PAT, or `get_default_account()`.
```python Prior to 2026.9 lines icon="python" theme={null}
version = client.version.get(version_id, project_id)
root = operations.receive(version.referenced_object, remote_transport=transport)
```
```python 2026.9 lines icon="python" theme={null}
with operations.receive3(account, project_id, model_id, version_id) as model:
pass
```
Close the model (leave the `with` block, or call `model.close()`) so the download directory is
deleted. Parsed objects stay usable after close. Geometry you have not touched yet cannot be read
afterwards.
## Count and filter objects
Prior to 2026.9, you walk a `Base` tree. In 2026.9, objects are a flat list. Properties are
path-keyed (`"category"`, `"Constraints.Base Offset"`). Relations are accessors, not Proxy lists.
```python Prior to 2026.9 lines icon="python" theme={null}
from specklepy.objects.data_objects import DataObject
def iter_elements(obj):
yield obj
for child in getattr(obj, "elements", None) or []:
yield from iter_elements(child)
counts = {}
for obj in iter_elements(root):
if not isinstance(obj, DataObject):
continue
key = (obj.properties or {}).get("category") or "Unknown"
counts[key] = counts.get(key, 0) + 1
by_level = {}
for obj in iter_elements(root):
if not isinstance(obj, DataObject):
continue
key = (obj.properties or {}).get("level") or "Unknown"
by_level.setdefault(key, []).append(obj)
walls = [
o
for o in iter_elements(root)
if isinstance(o, DataObject) and (o.properties or {}).get("category") == "Walls"
]
```
```python 2026.9 lines icon="python" theme={null}
counts = {}
for obj in model.objects:
key = obj.get_string("category") or obj.name or "Unknown"
counts[key] = counts.get(key, 0) + 1
by_level = {}
for obj in model.objects:
key = obj.level.name if obj.level else "Unknown"
by_level.setdefault(key, []).append(obj)
walls = [o for o in model.objects_with("category") if o.get_string("category") == "Walls"]
```
Look up one object with `model.object_by_application_id(id)`. `application_id` is the only identity
a bundle object has. Do not treat `Base.id` as a content hash on bundle-only versions.
## Replace property walks
Prior to 2026.9, nested parameters are dictionaries you walk: `obj.properties["x"]` then `["y"]`.
In 2026.9 those paths are flat. `PropertyView` is the type that replaces that walk.
`ModelObject.properties` is already this view.
* **`get_string` / `get_double` / `get_bool`:** typed lookup. `None` if the path is missing or the
value is another type. Instance, then type, then root-scalar precedence.
* **Indexer (`obj["path"]`):** untyped lookup, same precedence.
* **`properties.under("Constraints")`:** the subtree under that prefix, with the prefix stripped.
* **`to_nested()`:** the old nested-dictionary tree. Allocates. Use only when a caller still
requires that shape.
Prefer `get_string` / `get_double` for a single path. Use `under` when you need a group.
The snippets below assume `wall` is already in scope: a `DataObject` from the tree, or a
`ModelObject` from `model.objects`.
```python Prior to 2026.9 lines icon="python" theme={null}
groups = (wall.properties or {}).get("parameters") or {}
x = groups.get("x") or {}
y = x.get("y")
```
```python 2026.9 lines icon="python" theme={null}
y = wall.get_string("parameters.x.y")
offset = wall.get_double("Constraints.Base Offset")
constraints = wall.properties.under("Constraints")
for key, value in constraints.items():
print(f"{key}: {value}")
```
If the script reads `parameter["value"]` or other per-parameter metadata from the received tree,
that shape is gone on bundle-only versions. Read the value from a dotted path, not from a metadata
wrapper.
## Replace Proxy walks
Prior to 2026.9, grouping by level or material means indexing `applicationId`s and resolving proxy
`objects` lists. In 2026.9 those links are typed relations. See
[Relations in 2026.9](/next/developers/object-model/relations) for the names.
```python Prior to 2026.9 lines icon="python" theme={null}
by_app_id = {}
for obj in iter_elements(root):
app_id = getattr(obj, "applicationId", None)
if app_id:
by_app_id[app_id] = obj
```
```python 2026.9 lines icon="python" theme={null}
by_level = {}
for obj in model.objects:
key = obj.level.name if obj.level else "Unknown"
by_level.setdefault(key, []).append(obj)
hosted = [o for o in model.objects if o.host is not None]
```
A missing nested `elements` tree or Proxy `objects` list on 2026.9 data is expected. Do not unpack
Proxies to rebuild containment.
## What operations.receive still does
`operations.receive` remains callable. It is the compatibility path, not the 2026.9 default.
* **Legacy object-graph versions:** behaviour is unchanged. You still pass `referenced_object` as a
content hash.
* **Bundle-only versions:** `referenced_object` is a bundle reference
(`bundle...`). A 2026.9 specklepy dispatches on that prefix and
returns a best-effort `Base` tree (`Model.to_base()`, DataObject idiom, `version = 4` on the
root). That projection does not scale to the model sizes the bundle format is built for, and it
is not a lossless round trip. It needs an authenticated `ServerTransport` for the reference's
project.
* **Older specklepy:** fetching that `referenced_object` from the objects API returns **404** with
upgrade guidance. Upgrade specklepy rather than parsing the 404 body.
Receiving a bundle-only version as a `Base` tree and sending it again with `operations.send` is
not a supported copy workflow. The tree is a baked view, not the authored graph. Receive, derive
new data, then send remains ordinary supported use.
## Publish from a script
Most analysis scripts only receive. If you currently call `operations.send` then
`client.version.create`, the 2026.9 replacement is `operations.send3` with a `BundleBuilder`.
`operations.send` stays accepted and starts emitting deprecation warnings. There is no removal date.
`send3` returns a reserved version id immediately. Queries, the version URL, and the `version_create`
webhook fire when ingestion completes. Poll for the version, or watch the project's model
ingestions, if you need to follow up in the same process.
## FAQ
Upgrade specklepy first so versions from a 2026.9 connector do not 404. Versions from older
connectors keep working on `operations.receive`. Rewrite to `operations.receive3` when you need
relations, columnar property lookups, or models that are too large to materialize as a `Base`
tree.
`Model` is the bundle itself: objects, path-keyed properties, and relation accessors, with
geometry parsed only when you ask. A `Base` tree inflates every object and mesh into Python
objects. That is why `operations.receive` is the compatibility path for the new format, not the
default.
Downloaded parquet files stay under a scratch directory until `close` runs. Always use a `with`
block. Objects and already-loaded geometry stay in memory after close; geometry you have not
touched yet cannot be read afterwards.
Yes for hash-era data, until that path is retired. For bundle-only versions,
`operations.receive` still needs an authenticated `ServerTransport` so it can dispatch to
`receive3`. Move server-facing scripts to `operations.receive3`.
On a bundle-only receive, parameter metadata is collapsed to scalars on dotted paths. Read the
value with `get_double` or `obj["Width"]`, not from a metadata wrapper. That is a contracted
receive-fidelity change, not a bug in your filter.
# JS/TS SDK notes in 2026.9
Source: https://docs.speckle.systems/next/developers/sdks/typescript/introduction
2026.9 notes for the TypeScript and JavaScript SDK are not on this preview yet.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
2026.9 developer notes for the TypeScript and JavaScript SDK are not on this preview yet. Use the
[current JS/TS SDK documentation](/developers/sdks/typescript/introduction) until those notes land.
**Building directly on the Viewer package, or on `@speckle/objectloader` /
`@speckle/objectloader2`?** This is the case most likely to break silently, and it's easy to miss
in the general migration comms because it isn't "just" an SDK or API consumer. Your app stops
getting data as soon as any version you load is a 2026.9 bundle: the objects endpoints return
404\.
Start from [Building applications](/next/developers/building-applications), which routes rendering
apps and data-only apps to the right load path and explains why there is no drop-in
object-graph loader.
Package-level JS/TS notes for 2026.9 are still to come. For the Viewer specifically, see [Building
on the Viewer package in 2026.9](/next/developers/viewer/introduction).
# Server notes in 2026.9
Source: https://docs.speckle.systems/next/developers/server/introduction
2026.9 notes for Speckle Server deployment and self-hosting are not on this preview yet.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
2026.9 developer notes for Speckle Server deployment and self-hosting are not on this preview yet.
Use the [current Server documentation](/developers/server/introduction) until those notes land.
The open-source Speckle Server (core release) does not support 2026.9. If you run the open-source
server, pin your connectors to 2026.8 or earlier. 2026.9 connectors will not work against it.
This does not apply to Speckle Enterprise Server, including deployments on third-party
infrastructure. If you maintain or operate an Enterprise-licensed server, work with your
infrastructure support agent to plan your move to 2026.9.
# Building on the Viewer package in 2026.9
Source: https://docs.speckle.systems/next/developers/viewer/introduction
What changes for apps that import the Viewer package when the data they load becomes a 2026.9 bundle, and the two ways to keep rendering.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If your application imports `@speckle/viewer` and loads Speckle data itself, 2026.9 changes
what it loads. A version's data is no longer an object graph fetched from the objects
endpoints; it is a [bundle](/next/developers/building-integrations) of parquet files. The
published Viewer package has no loader for that shape, so a bundle version renders nothing
and the objects endpoints return 404.
The rest of the package is unaffected. Render pipelines, extensions, filtering, selection,
queries, the world tree and view modes all work the same way, because the change is in where
the data comes from, not in what the viewer does with it once converted.
## Am I affected?
| How your app uses the Viewer | Affected |
| - | - |
| Imports `@speckle/viewer`, constructs a `Viewer` and a loader | Yes |
| Extends render pipelines or extensions, reads render views or batches | Yes |
| Embeds the hosted web viewer in an iframe | No |
| Reads data only through the GraphQL or REST API | No |
## Two ways forward
Add about a dozen files to your app, keep the same published packages, and render bundles.
Sample code and a step-by-step path.
Keeps your workspace's data in the object-graph format, so your app keeps working unchanged
until the mode ends on **1 November 2026**.
Loading bundles is the route that outlasts compatibility mode, which ends on **1 November 2026**. It is sample code
rather than a package release, so you own the copy and it carries a date — see the guide for
what it does and does not cover.
Neither option reaches what 2026.9 makes possible. The bundle format is built for streaming
reads, sharded geometry and querying properties without materialising objects, and both
options here keep your app on the object graph it replaced: compatibility mode by producing
that graph, the bridge by rebuilding it in memory. Getting the performance the format allows
needs a viewer built for it. Plan for that, and treat both of these as ways to keep working
until it arrives.
Compatibility mode is not self-serve, and it ends for every workspace on **1 November 2026**. See
[Compatibility mode](/next/it-admin/compatibility-mode) for how to request it.
## Not the Viewer package
Two nearby cases have their own pages.
* **A data-only JavaScript app** that reads Speckle data without rendering it — built on
`@speckle/objectloader` or `@speckle/objectloader2` — should read the bundle directly
rather than bridging to an object graph. Start from [Building
applications](/next/developers/building-applications).
* **The 3D viewer in the product UI** is not this package. See [3D viewer in
2026.9](/next/3d-viewer/introduction).
## Package reference
Package-level Viewer API documentation is unchanged by 2026.9 and lives in the other
documentation version: [Viewer developer
documentation](/developers/viewer/introduction). The API surface the bridge relies on —
`Loader`, `SpeckleLoader`, `WorldTree`, `loadObject` — is documented there.
# Load a 2026.9 bundle in your Viewer app
Source: https://docs.speckle.systems/next/developers/viewer/loading-bundles
Sample code and a step-by-step path for rendering 2026.9 bundle versions with the published Viewer package, without waiting for a new package release.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
If your app imports `@speckle/viewer` and loads Speckle data itself, a 2026.9 version
gives it nothing to draw: the version's data is a [bundle](/next/developers/building-integrations)
of parquet files, and the objects endpoints return 404. This page is the bridge. You add
about a dozen files to your own app, keep the same two published packages you already
depend on, and the viewer renders bundles.
Nothing is patched and nothing new is published. You read the bundle, project it onto the
object shape the viewer already converts, and hand those objects to the loader.
This is sample code, not a package: it is not versioned and it is not supported the way a release
is. Copy it into your app and own the copy.
## Check whether this affects you
Read a version record before assuming either shape.
| Field | 2026.8 and earlier | 2026.9 bundle |
| - | - | - |
| `Version.schemaVersion` | `null` | `3` |
| `Version.referencedObject` | A content hash | `bundle...` |
| Where the data is | The objects endpoints, as JSON | The artifacts endpoint, as parquet files |
You need this page if your app constructs a viewer and a loader itself. You do not need it
if you embed the hosted web viewer in an iframe, or if you only read data through the
GraphQL or REST API.
Compatibility mode keeps a workspace's data in the object-graph format until the mode ends on **1
November 2026**. See [Compatibility mode](/next/it-admin/compatibility-mode).
## What the bridge does
1. Reads the version record and dispatches on its shape.
2. Lists the version's artifacts and downloads the parquet tables it needs.
3. Decodes SGEO geometry.
4. Projects the bundle onto `Base` objects, with collection, material, colour and instance
proxies.
5. Hands those objects to `ObjectLoader2Factory.createFromObjects` through a
`SpeckleLoader` subclass.
Everything after that is untouched public API. Extensions, filtering, selection, queries,
camera and view modes all behave as they do today, because by the time the viewer sees your
data it is the same kind of object tree it always converted.
The worked sample, including a mini viewer app that loads both shapes of version, is
[examples/viewer-bundle-bridge](https://github.com/specklesystems/speckle-docs-new/tree/main/examples/viewer-bundle-bridge).
It lives in the docs repo, so take that directory on its own rather than cloning the rest:
```bash theme={null}
git clone --filter=blob:none --sparse --depth 1 \
https://github.com/specklesystems/speckle-docs-new.git
cd speckle-docs-new
git sparse-checkout set examples/viewer-bundle-bridge
```
## What the bridge costs you
That last sentence is the trade. The bridge reconstitutes the object graph 2026.9 retires:
it reads the whole bundle, builds `Base` objects in memory, and hands them to the converter
that has always run. So it inherits the load time and memory profile you have today, and it
gives up most of what the bundle format exists to enable — streaming reads, sharded geometry
fetched on demand, dense integer keys, properties queried without materialising objects, and
the server-built viewer artifact.
2026.9 raises the performance ceiling well above what this bridge can reach. Reaching it needs a
viewer built for the format, not a projection back onto the shape it replaced.
Read the bridge as what keeps your app working across the format change, not as where it
should stay. It is at its best on models of the size you render well today; it will not make
a model that is slow now fast, and on very large models it can be worse than the object graph
was, because the whole bundle is read before anything draws.
## 1. Pin both packages exactly
Pin `@speckle/viewer` and `@speckle/objectloader2` to the same version, with no carets. The
bridge depends on internals of neither, but it does depend on `initObjectLoader` being
overridable and on `createFromObjects` existing, and a range would let a future release move
either.
```json package.json theme={null}
{
"dependencies": {
"@speckle/objectloader2": "2.31.14",
"@speckle/viewer": "2.31.14"
}
}
```
You should see both packages resolve to exactly `2.31.14` in your lockfile.
## 2. Add a parquet reader and a zstd codec
A bundle is parquet, and the files are zstd-compressed. Both dependencies go in **your**
`package.json`, not in a Speckle package.
```bash theme={null}
pnpm add hyparquet hyparquet-compressors
```
Read the geometry table with UTF-8 decoding turned **off**. The `content` column is a byte array
with no string logical type, and a reader that decodes every byte array as UTF-8 by default will
mangle every SGEO blob — silently, with no error and no geometry.
You should see a parquet read return rows whose `content` values are byte arrays, not
strings.
Find the tables by matching the end of each file name, never by building it from the version id. A
bundle's files share a stem, but that stem is not always the version — a Revit upload lists as
`acme-b-zz-m3-wa-ar.rvt.envelope.nodes.parquet`, with only the viewer's own artifacts named after
the version.
## 3. Drop in the projection
Copy `src/bridge/` from the sample into your app. `projectBundle` turns the bundle into the
object shape the viewer already converts: a collection tree, one object per `applicationId`
carrying its display geometry, and instance, material and colour proxies.
Rendering is unconditional: geometry, nested instances, grouping, and the full material and
colour precedence from the geometry up through the object to its container. Two things cost
extra reads, so they are options rather than defaults:
```ts theme={null}
projectBundle(bundle, { properties: true, referencePoint: true })
```
`properties` merges each object's instance and type-level rows — the type rows live in their
own table, and without that merge most Revit type parameters are simply absent. `referencePoint`
carries the model's datum onto the root. An app that only draws leaves both off and never
downloads the eav tables at all.
Solids, centrelines and the scene-view tree are carried by the bundle and read by none of this.
Add what your app needs rather than assuming its absence is a bug — the rules for every table and
relation are in [Load a bundle in your own code](/next/developers/building-integrations/load).
You should see `projectBundle` return a root object whose `elements` hold your objects.
## 4. Swap the loader
Both shapes of version end at `viewer.loadObject`. The only difference is which loader gets
there, so dispatch on `schemaVersion` and keep your existing path intact.
Assume `viewer`, the project, model and version ids, and a token are already in scope.
```ts Prior to 2026.9 theme={null}
const loader = new SpeckleLoader(viewer.getWorldTree(), objectUrl, token)
await viewer.loadObject(loader, true)
```
```ts 2026.9 theme={null}
const { loader } = await loadBundleVersion({
tree: viewer.getWorldTree(),
ref: { serverUrl, projectId, modelId, versionId },
token,
project: projectBundle
})
await viewer.loadObject(loader, true)
```
The subclass itself is short, because everything asynchronous happens before construction.
The base class passes `resourceData` through to `initObjectLoader`, which is how the
projected objects reach the override before any subclass field exists — the same route
`SpeckleOfflineLoader` takes.
```ts bundleLoader.ts theme={null}
export class BundleLoader extends SpeckleLoader {
constructor(targetTree: WorldTree, resource: string, root: BaseObject) {
super(targetTree, resource, undefined, undefined, root)
}
protected initObjectLoader(
_resource: string,
_authToken?: string,
_enableCaching?: boolean,
resourceData?: unknown
): ObjectLoader2 {
const root = resourceData as BaseObject | undefined
if (!root) throw new Error('BundleLoader was constructed without a projected root')
return ObjectLoader2Factory.createFromObjects([root])
}
}
```
You should see the model render, and `viewer.getWorldTree()` report nodes.
Your own extensions need no changes. They are created the same way, see the same world tree,
and resolve the same ids. The sample proves that with two the Speckle web app never runs:
`ExplodeExtension`, which ships with the viewer, and a box select written from scratch —
```ts extensions/boxSelect.ts theme={null}
export class BoxSelectExtension extends Extension {
get inject(): Array {
return [SelectionExtension, CameraController]
}
private idsInside(rect: Rect): string[] {
const camera = this.viewer.getRenderer().renderingCamera
const ids = new Set()
this.viewer.getWorldTree().walk((node: TreeNode) => {
const aabb = node.model.renderView?.aabb
if (aabb && inside(project(centre(aabb), camera), rect)) {
ids.add(String(node.model.raw.applicationId))
}
return true
})
return [...ids]
}
}
```
If an extension written from scratch against public API works on bridged data, the Viewer 2
surface works on 2026.9 data generally — not only the paths Speckle exercises.
Read render views across every node, not only `atomic` ones, and expect some to have no bounds.
Render views hang off the nested geometry nodes, so a walk that checks atomic nodes alone finds
nothing on a model that renders perfectly well.
## 5. Rekey your interactions to applicationId
Everything above is copy-paste. This step is your app's own data model, and skipping it is
what makes a load look successful while your saved state silently matches nothing.
Before 2026.9, a Speckle object's identity was a content hash, stable across versions of the
same unchanged element and usable as a durable key. In a bundle there is no hash. **The
object's identity is `applicationId`** — the id the authoring application gave the element.
The bridge sets `id` to `applicationId` on every object, so the ids the viewer hands you in
selection events, queries and filtering state are `applicationId` values.
What that changes:
* **Anything you persisted keyed by object id needs a migration or a rekey.** Saved
selections, comment anchors, your own annotations, cached per-object state. A stored
content hash will not match anything in a bundle.
* **`applicationId` is stable across versions in a way hashes were not.** An element that
moved still carries the same `applicationId`, where its hash changed. For most apps this
is better: your per-object state survives a republish.
* **Synthetic ids are per-bundle.** Anything the projection mints rather than reads —
containers (`coll-7`), definitions (`def-3`), materials (`mat-0`) — is local to one bundle
and will differ in the next version. Never persist those.
* **Duplicate `applicationId` values are possible.** Producers are asked for stable unique
ids and mostly deliver them, but a host with no stable id per element can repeat one.
Decide whether last-wins or a reported conflict is right for your app.
If your app already keys on `applicationId` because it cross-references a host model, this step is
a no-op and you are most of the way done.
You should see selection in your app resolve to the same objects it did before, keyed by
`applicationId`.
## What does not come across
A bundle carries far more than the projection reads, and more than the published viewer can
draw. None of these raise an error, so check the list against what your app promises.
**Relations.** The catalog is in [Relations](/next/developers/object-model/relations). The
projection reads DISPLAY, DEFINES, HAS\_MATERIAL, HAS\_COLOR, DISPLAY\_INSTANCE,
DEFINES\_INSTANCE, IN\_COLLECTION and the object and node appearance relations. Everything else
is carried and unread: solids, centrelines, scene views, levels, rooms, systems, groups,
assemblies, connectivity and hosting. Those rows are in the bundle and available to you —
nothing in the sample reads them into the tree.
**Geometry.** Meshes, lines, polylines and the tessellated curve family render. Text,
points, boxes and regions decode but are not converted. The sample counts what it skipped and
prints the tally, so a model that is mostly annotation looks empty for a stated reason.
**Vocabulary you do not know.** Read columns by name and tolerate columns you do not know, and
treat a relation id or node kind your reader does not recognise as skip-and-report rather than
an error. A producer newer than your copy of the bridge is normal, and the sample surfaces
those ids for you.
Do not gate a load on `meta.schema_version`. It is provenance, not a compatibility switch, and the
spec adds columns without moving it.
The sample is verified against `@speckle/viewer@2.31.14`, `@speckle/objectloader2@2.31.14`
and bundle spec 1.2.0, as of September 2026. Check those pins before assuming a behaviour
still holds.
## Viewer concepts that still apply
The bridge changes where data comes from, not how the viewer works. The loader and tree
documentation still applies:
What a loader is and where it sits in a load.
The abstract `Loader` contract the subclass implements.
What the converter builds, and how to query it.
Earlier viewer migrations, for context on what changed before.
## FAQ
Publishing it would make a third implementation of the receive fidelity contract that Speckle
then has to keep in step with the .NET and Python SDKs. Sample code you own is honest about what
it is: a worked example of documented rules, dated, in your app, under your control.
Yes — pass `includeGeometry: false` and the geometry shards, which are the bulk of a bundle, are
never fetched. The projection here renders, so that leaves it with nothing to draw; it is useful
once you extend the projection to read the eav tables.
Not from another origin. Your app cannot read Speckle's auth cookie — that is the origin model —
and sending the request with `credentials: 'include'` does not help either, because the cookie
is `SameSite=Lax` and so never rides a cross-site fetch. Use a token. A public project needs
none at all: send no `Authorization` header rather than an empty one, which is rejected.
Yes, and you keep the same version. The bridge uses `ObjectLoader2Factory.createFromObjects`
instead of `createFromUrl`, which is why no request goes to the objects endpoints.
Either the version is an object graph from 2026.8 or earlier, the bundle has not been produced
yet, or your token cannot read the project. Check `schemaVersion` on the version record first,
then the ingestion status if the version is new.
Read the bundle directly rather than bridging to an object graph. Start from [Building
applications](/next/developers/building-applications), which points at the load mechanics for
data-only apps.
A nested instance — a definition placed inside another definition — logs this once per nested
placement. The converter looks the node up after the parent definition has already consumed it.
The tree is still correct: the nested definition expands and renders under its transform. What
it does skip is the container-colour fallback for that nested placement.
Give every object you mint a distinct id. The converter counts definition members by
`applicationId` and stops as soon as its count is met, so a duplicate consumes another member's
slot and that member is dropped. This is why the sample suffixes definition geometry rather than
reusing its carrier's id.
Instance placement is not being applied. Check that your projection emits an `InstanceProxy` per
`DISPLAY_INSTANCE` edge with the instance node's transform, and that objects carrying a
definition member's properties are not also drawn — a carrier with geometry draws twice, once
untransformed at the origin.
# Compatibility mode for ACC sync and uploads
Source: https://docs.speckle.systems/next/it-admin/compatibility-mode
Compatibility mode ends for every workspace on 1 November 2026. After that date, ACC sync and drag-and-drop uploads always use the 2026.9 data format.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Scripts and reports that expect the earlier data format can stop working when a workspace moves
to 2026.9. Compatibility mode keeps ACC sync and drag-and-drop uploads in that format while
you update the tools that read the data.
For ACC hub setup that does not change, see [Enable Autodesk Forma Data Management
integration](/it-admin/integrations/acc). For the user-facing migration steps, see [Speckle's data
model is changing](/workspaces/data-model-migration).
Compatibility mode ends for every workspace on **1 November 2026**, whenever it was turned on.
From that date, ACC sync and drag-and-drop uploads write the 2026.9 format. Update dependent
scripts and reports before then.
For changes to accepted file extensions, see [File uploads in 2026.9](/next/connectors/file-uploads).
## When to request it
Compatibility mode is a short bridge while you update downstream tools. It is not a way to stay on
the old format. Request it when all of the following are true:
* The workspace uses **ACC sync** or **drag-and-drop uploads**.
* Something downstream of Speckle depends on the old data format.
* You cannot update that downstream tool before the workspace starts writing the new format.
Do not request it only because connectors are still on an older release. Connector upgrades have
their own window. See [Plan the connector upgrade window](/next/it-admin/connector-updates).
## As a workspace admin
Identify whether any project uses ACC sync or drag-and-drop, and whether any script, custom report,
or other tool reads that data in the old format. If it does, ask Speckle to switch the workspace
into compatibility mode now. It is not self-serve, and it ends on **1 November 2026** regardless of
when it was turned on.
After Speckle confirms the workspace is in compatibility mode, ACC sync and drag-and-drop continue
to write the old format until you ask to leave it, and no later than **1 November 2026**. On that
date every workspace leaves compatibility mode, whether or not downstream tools are ready.
Contact Speckle in-app, through [speckle.community/help](https://speckle.community/help), or via
your Customer Success Advisor.
## As an IT administrator
Compatibility mode does not change ACC hub enablement, firewall allowlists, or connector
installers. Keep using the [IT Setup Guide](/it-admin/setup) for those tasks.
Identify tools that read data from ACC sync and drag-and-drop uploads, and update them before
**1 November 2026**.
## After 1 November 2026
Compatibility mode is switched off for every workspace on this date, and Speckle no longer offers
it. New ACC sync and drag-and-drop
uploads then use the new data format. Downstream tools that still expect the old format can break
until they are updated.
Existing models stay readable. Desktop connector publish and load are not turned off by this date.
# Plan the connector upgrade window
Source: https://docs.speckle.systems/next/it-admin/connector-updates
Update connectors from 1 September 2026, and no later than 1 November 2026.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
2026.9 gives IT teams a dated window to bring connectors current. Update from **1 September
2026**, and complete the estate no later than **1 November 2026**.
For installation, baselines, host support, and the usual update cadence, see [Keep connectors
current across your estate](/it-admin/connector-updates). This page covers only the dated window.
This window is not a freeze of desktop connectors. Existing connectors keep working for publish
and load. Compatibility mode is a separate, temporary option for ACC sync and drag-and-drop only.
See [Compatibility mode for ACC sync and uploads](/next/it-admin/compatibility-mode).
The Navisworks desktop connector is not in this window. **2026.8** is the last installer. There is
no 2026.9 package to roll out. Keep a **200 MB** publish on 2026.8. Move larger NWC and NWD files
to upload (up to **2 GB**) or ACC sync (tested to **6 GB**). A 2026.8 publish uses the current
data model; server-side conversion to the 2026.9 format is not guaranteed. See [Navisworks in
2026.9](/next/connectors/navisworks/introduction).
## Why the dates exist
The window opens on **1 September 2026**. Use it to roll updates as routine maintenance rather
than a last-minute project. The backstop is **1 November 2026**, when compatibility mode for ACC
sync and drag-and-drop ends.
Updating in this period gets the performance improvements sooner. You can update earlier than
**1 September 2026** if your change process allows it. Do not plan to still be rolling out after
**1 November 2026**.
## How this differs from the current cadence
The current IT guidance still applies: stay on the current release, and treat quarterly as the
minimum cadence if you cannot approve every release. For 2026.9, **1 November 2026** is a hard
backstop for this window, not a replacement for that standing process.
After **1 November 2026**, return to the cadence in [Keep connectors current across your
estate](/it-admin/connector-updates).
## As an IT administrator
1. Inventory connector versions across the estate. You should have a single target version before
the pilot starts.
2. Pilot from **1 September 2026** with a small group covering your main host applications. Publish
and load a representative model, including a large or federated one.
3. Roll forward together so teams do not sit on mixed versions through October. You should see one
connector generation across the estate.
4. Finish by **1 November 2026**. Record the baseline so the next update starts from a known
position.
Use the deployment routes already documented: `.exe` for single-user installs, and ZIP for managed
all-user installs. See [Move to a new baseline](/it-admin/connector-updates#move-to-a-new-baseline).
## As a self-hosted server admin
Self-hosted servers are not required to adopt this data model on a fixed date. If your current
connector and server versions work together, pin both, and keep downstream users on that same
combination. See [Self-hosted
servers](/developers/migration/data-model-migration#self-hosted-servers).
## FAQ
No. Existing connectors keep working for publish and load. The date is the end of the upgrade
window, and the end of compatibility mode for ACC sync and drag-and-drop. Plan to be current by
then so you are not rolling out under time pressure.
The deadline is **1 November 2026**. Starting from **1 September 2026** leaves time to pilot and
roll forward together. Compressing the work into late October still meets the date, but it
removes that buffer.
Yes, as the standing process. This window is a dated exception for 2026.9. After **1 November
2026**, follow [Keep connectors current across your estate](/it-admin/connector-updates).
No. There is no 2026.9 Navisworks installer. **2026.8** is the last package. Keep a 200 MB
publish on 2026.8. Move larger files to NWC and NWD upload or ACC sync. See [Navisworks in
2026.9](/next/connectors/navisworks/introduction).
Not as a fixed server-upgrade deadline. Pin the connector-and-server combination that already
works, and keep downstream users on it. See [Self-hosted
servers](/developers/migration/data-model-migration#self-hosted-servers).
# IT administration for 2026.9
Source: https://docs.speckle.systems/next/it-admin/introduction
Dated 2026.9 actions for IT and workspace admins. Coverage is incremental.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Use this section when you manage Speckle for an organisation. It covers the 2026.9 dates that
differ from the current IT guides, the workspace-wide **AI features** switch, and workspace
Settings and Usage for workspace admins. For
firewall rules, SSO, connector installation, and cloud integrations, see the [IT Setup
Guide](/it-admin/setup).
2026.9 does not change how you install connectors, configure SSO, or enable ACC and ProjectWise.
Trimble Connect is new in 2026.9: a workspace admin connects a Trimble **service account** in the
project sidebar — there is no separate ACC Hub-style enablement step. See [Sync models from Trimble
Connect](/next/connectors/cloud-integrations/trimble-connect).
Two dated actions do change:
* **Compatibility mode ends on 1 November 2026.** If ACC sync or drag-and-drop uploads feed a
downstream tool that still needs the old data format, request it now and update that tool before
the end date.
* Plan a **connector upgrade window** from **1 September 2026**, and complete it no later than
**1 November 2026**.
Those actions are not a freeze of desktop connectors. Publish and load continue. Compatibility
mode applies only to ACC sync and drag-and-drop uploads.
## What to do
| Date | What you do | Learn more |
| - | - | - |
| 1 September 2026 | Start rolling connector updates across the estate. | [Plan the connector upgrade window](/next/it-admin/connector-updates) |
| Now | If ACC sync or drag-and-drop still needs the old format, ask Speckle to enable compatibility mode and start updating the downstream tool. | [Compatibility mode](/next/it-admin/compatibility-mode) |
| 1 November 2026 | Compatibility mode ends for every workspace. Finish connector updates. | [Compare 2026.8 and 2026.9](/next/whats-new) |
For the user-facing migration steps, see [Speckle's data model is changing](/workspaces/data-model-migration).
## Turn AI features off for the workspace
Speckle Intelligence is available when the workspace plan includes it. A workspace admin can turn
**AI features** off for the whole workspace from workspace **Settings**, then **General**. Only
workspace admins can change this switch; members and guests can open the General page but cannot
change it.
When AI features are off, **Chat** and the sparkle control in the 3D viewer are unavailable to
everyone in the workspace, so nobody can start a conversation or spend AI credit. Turn the switch
back on the same way to restore access.
The switch is a workspace setting, not a plan change. It applies to one workspace, so if your
organisation runs several workspaces, turn AI features off in each one. To remove Speckle
Intelligence from the plan itself, use **Settings**, then **Billing and plans**, or contact us.
On Speckle Enterprise Server, Intelligence is also controlled by the licence. See [Enterprise
license — Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
Setup, security, support mode, and cloud-integration pages are unchanged. Switch to **Current** in
the version selector for those guides.
As a workspace admin, use [Workspace settings in 2026.9](/next/workspaces/settings) for the live
Settings map, and [Usage statistics in 2026.9](/next/workspaces/usage) for activity, plan limits,
and AI spend. Organisation playbooks for Intelligence are
[skills](/next/analytics/intelligence-skills). Standing constraints on every conversation are [AI
Rules](/next/analytics/intelligence-rules) (admin-managed).
# Enhanced roles and permissions
Source: https://docs.speckle.systems/next/it-admin/roles-and-permissions
Enhanced roles and permissions for 2026.9 are coming soon. Use the current roles documentation until then.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Enhanced roles and permissions for 2026.9 are coming soon. Until that documentation ships, workspace
and project access still follow the current two-level model.
This preview does not yet describe new role types or a new admin screen. Manage members and project
collaborators the same way you do today.
For the roles that apply now, see [Roles and Permissions](/workspaces/roles-and-seats).
# 2026.9
Source: https://docs.speckle.systems/next/welcome
2026.9 documentation. Coverage is incremental and is not a copy of the guides under **Current** in the version selector.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
This is the 2026.9 documentation. It is not a parallel copy of the User Guides, Developers, or IT
Administrators trees. Those pages live under **Current** in the version selector. Start there for
[Welcome](/quickstart/welcome) and any topic that does not have a 2026.9 page yet.
Start with [Compare 2026.8 and 2026.9](/next/whats-new) to see what changes for you.
A missing page is expected. Switch to **Current** in the version selector for the complete guides.
# Compare 2026.8 and 2026.9
Source: https://docs.speckle.systems/next/whats-new
What changes for you in 2026.9, next to how Speckle works today.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Use this page to see what 2026.9 changes for you, next to how Speckle works today. For the
current product, switch to **Current** in the version selector.
If you only publish and load through Speckle connectors, your day-to-day work stays the same. The
rows below are the exceptions and dates that matter. For the full steps, see
[Speckle's data model is changing](/workspaces/data-model-migration).
## What's different
| What you do | Before 2026.9 | In 2026.9 | Learn more |
| - | - | - | - |
| Publish, load, and view | You publish and load through connectors and the web app | You still do. After your project moves to the new data format, those flows are faster | [What to do next](/workspaces/data-model-migration) |
| Models you already published | Your existing models stay readable | They stay readable. New publishes use the new format going forward | [What to do next](/workspaces/data-model-migration) |
| ACC sync or drag-and-drop | Uploads use today's data format | Uploads move to the new format. If another tool still needs the old format, ask Speckle to turn on compatibility mode as a short bridge. It ends for every workspace on **1 November 2026** | [Who should pay closer attention](/workspaces/data-model-migration#who-should-pay-closer-attention) |
| Connector updates | Your current connectors keep working | Update them from **1 September 2026**, and no later than **1 November 2026** | [What you should do now](/workspaces/data-model-migration#what-you-should-do-now) |
| Speckle Intelligence | It looks at properties on each object | It can also see how parts of a model relate to each other — what connects to what, and what contains what | [What to do next](/workspaces/data-model-migration) |
| Data Validation | Checks, plus project standards on Enterprise | Project standards are not in 2026.9. Author checks. A later rollup from Checks to workspace standards is planned | [Data Validation in 2026.9](/next/analytics/data-validation/overview) |
| Open a model in the web app | Current viewer: section box, Selection info, Filters | New viewer: hybrid load, docked **Selection** / **Filters** / **Views**, section planes | [3D viewer in 2026.9](/next/3d-viewer/introduction) |
| View point clouds | Point clouds render in the viewer | Not drawn in the new viewer yet. The data is unchanged and connectors that load it keep working. Support is planned for a later release | [3D viewer in 2026.9](/next/3d-viewer/introduction#open-a-model) |
| View colours and materials | Colours and materials from the source application render in the viewer | Some objects show as grey in the new viewer: Grasshopper colours, Civil 3D surface materials, and models published before the move to 2026.9. The data is unchanged. Publishing again fixes only the last case | [3D viewer in 2026.9](/next/3d-viewer/introduction#open-a-model) |
| Compare model versions | ··· **View changes** on a version opens a dedicated diff view | Deprecated — no diff view in the Models ··· menu. Ask Speckle Intelligence (Chat, the viewer sparkle, or a Report) to detect, report, and assess the impact of what changed | [Worked example](/next/workflows/assess-model-changes-with-intelligence) |
| Navisworks models | Publish from the desktop connector | No 2026.9 desktop connector. **2026.8** is last, and still advised at **200 MB**. Larger NWC/NWD: upload to **2 GB**, ACC tested to **6 GB** | [Navisworks in 2026.9](/next/connectors/navisworks/introduction) |
| Trimble Connect | Not available | New beta cloud sync on the Enterprise plan. Service account for background downloads; personal Trimble ID to browse | [Sync models from Trimble Connect](/next/connectors/cloud-integrations/trimble-connect) |
This table is a summary. Follow the linked page for steps and dates.
Connector-specific changes live under Publishing & Loading: [Revit in
2026.9](/next/connectors/revit/introduction), [Grasshopper in
2026.9](/next/connectors/grasshopper/introduction), [Power BI in
2026.9](/next/connectors/power-bi/introduction), [Navisworks in
2026.9](/next/connectors/navisworks/introduction), and [Trimble
Connect](/next/connectors/cloud-integrations/trimble-connect). Viewer chrome, **Selection**,
**Filters**, and **Views** are on [3D viewer in 2026.9](/next/3d-viewer/introduction). Until a
2026.9 page exists for another topic, use the guides under **Current** in the version selector.
## If you build on Speckle
If you use SDKs, APIs, Automate, or a self-hosted server, see
[Data model migration for developers](/developers/migration/data-model-migration). That page covers
SDK updates, compatibility mode for uploads, and when to stay on 2026.8. Holding notes for the
other developer tabs are under Developers in this preview.
## FAQ
Most people who publish and load through Speckle connectors keep the same workflow. 2026.9
details for Revit, Grasshopper, Power BI, Navisworks, and Trimble Connect are under Publishing &
Loading. Other design tools still use the connector guides under **Current** in the version
selector.
Use the guides under **Current** in the version selector. If there is no 2026.9 page for that
topic yet, the feature is still documented there. It did not go away.
No. Compatibility mode is only for ACC sync and drag-and-drop uploads when another tool still
expects the old format. It ends on **1 November 2026**. See [Who should pay closer
attention](/workspaces/data-model-migration#who-should-pay-closer-attention).
# Assess model changes with Speckle Intelligence
Source: https://docs.speckle.systems/next/workflows/assess-model-changes-with-intelligence
A worked example — ask Chat what changed between two versions, read the report, then filter and colour the model by how significant each change is.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow linked Current documentation for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
The dedicated diff view is gone in 2026.9 — see [Compare Versions](/3d-viewer/compare-versions).
In its place, Speckle Intelligence can detect what changed between two versions, explain it in
plain language, and — because Chat can also drive the viewer — colour the model by how
significant each change is. This page walks through one real conversation end to end, from the
first question to a scene coloured by change severity, so you know what to expect and can reuse
the pattern on your own models.
Speckle Intelligence Chat is available when your workspace plan includes it. On a self-hosted
server, it is available only on Speckle Enterprise Server. For deployment setup, see [Enterprise
license — Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
## What this workflow does
* **Detect** — find the two versions to compare and diff them, without you opening a versions
panel or clicking a diff button.
* **Report** — explain what changed in plain language: scale first, then the categories that
carried the change.
* **Assess impact** — score changed elements by how significant the change is, not just
changed-or-not.
* **Visualise** — colour the model by that score so the impact is visible, not just written down.
## Walkthrough
In Chat or the viewer sparkle, ask a plain question — no version pickers, no diff button:
> What is the most significant change between the last two versions of the model?
Intelligence lists the model's version history, attaches the previous version alongside the
one you have open, and runs a diff between them. You do not see those as separate steps; they
happen as part of answering the question.
The reply is a written summary, not a raw diff dump — scale first, then the categories that
carried the change, in order of significance. On one structural model, the answer read:
> The most significant change is a structural steel re-specification that swept through the
> model. Scale: 6,598 elements modified, 119 added, 105 removed across 66 levels — only \~60%
> of the model was left untouched. Structural Framing (3,873 modifications) shows beam
> families replaced en masse, with cross-sections cleared and rebuilt and the material grade
> upgraded (Steel 43-275 → Steel 345 MPa). Structural Columns (135) and Structural Connections
> (376 modified, 61 added, 22 removed) shifted to match. Walls (1,753 modified) look like a
> secondary effect of the structural resizing rather than an independent edit.
Intelligence reads this from the live model — the same [connected, queryable
data](/next/analytics/intelligence#how-it-works) Chat uses for any other question — not from a
static export or a change log someone maintained by hand.
Keep going the way you would with a colleague:
> Filter to just those elements and colour them by how significant.
You do not have to define "significant" or set a threshold. Intelligence scores each changed
element by how many of its properties moved, buckets that into tiers, and applies a filter and
colour-by in the viewer you are already looking at — the same [Filters
panel](/next/3d-viewer/introduction#filter-and-color) documented for manual use.
The reply names the tiers it chose and what landed in each one:
> 🔴 Major (9+ params changed) — 1,757 elements, the deepest re-specs. 🟠 Moderate (5–8 params)
> — 2,587 elements. 🟡 Minor (1–4 params) — 2,254 elements. 🟢 Added — 119 new elements.
The scene is now filtered to only the changed elements, coloured by tier, so the heaviest
re-specification is visually obvious without you building that filter by hand. From here you
can ask to isolate one tier, break it down by level, or start a new question — the filter
stays until you clear it or ask for something that replaces it.
## Turn this into a skill
If your team asks this shape of question often — after every structural issue, before every
coordination meeting — save the pattern instead of retyping it. [Save the chat as a
skill](/next/analytics/intelligence-skills#save-a-chat-as-a-skill) once the conversation behaves
the way you want, or write the significance tiers and vocabulary into a skill by hand so every
comparison uses the same thresholds and the same added/omitted/revised language. See [Reuse
organisation playbooks with Intelligence skills](/next/analytics/intelligence-skills).
You can also save the conversation as a [Report](/next/analytics/reports) if you want a shareable
artefact rather than a one-off chat — bind the two versions as source aliases and the comparison
becomes something you or a teammate can reopen without re-asking Intelligence.
## FAQ
No. Ask about "the last two versions", or name one if you want an older baseline — for example
"compare this to the version from before the coordination meeting." Intelligence finds and
attaches the versions it needs.
Not fixed. In this example Intelligence scored elements by how many properties changed and split
them into rough tiers. Ask it to explain its thresholds, or tell it to weight geometry changes
over parameter-only changes, if you want a different definition of significant for your project.
Yes. Like every other Intelligence query, comparing versions needs both versions in the new data
format. Versions still in the old format cannot be queried. See [Speckle's data model is
changing](/workspaces/data-model-migration).
Yes — the questions on this page are plain language, not a script. Ask them in Chat or the
viewer sparkle on any project with at least two versions in the new data format.
## See also
* [Ask questions about your models with Speckle Intelligence](/next/analytics/intelligence)
* [Create and share Intelligence Reports](/next/analytics/reports)
* [Reuse organisation playbooks with Intelligence skills](/next/analytics/intelligence-skills)
* [Compare Versions](/3d-viewer/compare-versions) — the deprecated Current-viewer diff view this
replaces
* [3D viewer in 2026.9](/next/3d-viewer/introduction)
# Benchmark a project against your portfolio with Speckle Intelligence
Source: https://docs.speckle.systems/next/workflows/benchmark-against-your-portfolio-with-intelligence
Pin a library of past-project figures as Knowledge so Chat can tell you how the project you're looking at compares, instead of exporting each project by hand to compare them.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow linked Current documentation for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Chat answers questions about one project at a time — see [Ask questions about your models with
Speckle Intelligence](/next/analytics/intelligence#where-to-ask). It is not a portal onto every
project in the workspace. So "how does this compare to our past work" is not something Chat can
answer from the live model alone — it needs a benchmark to compare against. This page pins that
benchmark as Knowledge so the comparison happens inside the conversation you're already having,
instead of a separate export-and-compare step.
Speckle Intelligence Chat is available when your workspace plan includes it. On a self-hosted
server, it is available only on Speckle Enterprise Server. For deployment setup, see [Enterprise
license — Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
This pins a static benchmark library for comparison inside Chat. To watch several **live**
projects side by side in one view instead — combined totals or a difference view, updating as
those projects change — use [Perform portfolio
analysis](/analytics/dashboards/common-workflows#perform-portfolio-analysis) in Dashboards.
## What this workflow does
* **Maintain a benchmark library** — a spreadsheet or warehouse table of figures from past
projects (steel tonnage, embodied carbon, cost per m², whatever your team tracks).
* **Ask in project scope** — Chat reads the project you have open plus the pinned library, and
compares the two.
* **Flag outliers** — ask for what's unusual, not just the raw comparison.
* **Keep the library current** — point Chat at a live warehouse table instead of a static file if
the benchmark should update as finance or a data team refreshes it.
## Walkthrough
Export or maintain a table of the metric you want to benchmark — one row per past project,
with the figure your team already tracks (steel tonnage, embodied carbon per m², cost per
unit). A spreadsheet is fine to start; a Databricks, Snowflake, or Microsoft Fabric table keeps
it current without re-uploading.
Create a skill and pin the library as
[Knowledge](/next/analytics/intelligence-skills#what-you-can-attach). Tell the skill, in your
own language, which column is which project and which is the metric, and how you want the
comparison framed — a ratio, a percentile, or a plain "higher/lower than average". See [Reuse
organisation playbooks with Intelligence skills](/next/analytics/intelligence-skills).
Open the project you want to check, arm the skill, and ask:
> How does this project compare to our past hospital projects on steel tonnage?
Intelligence reads the current project's data plus the pinned library and answers within that
one conversation — you don't export either side by hand.
Keep going:
> Is that difference something worth flagging?
Because the comparison basis is the library you pinned — not a guess — Intelligence can say
where this project sits against it, and say so in the vocabulary your skill defined.
## When you need several live projects in one view
A pinned library is a snapshot — accurate as of when you last updated it. If you want combined
totals or a difference view across several **live** projects that stays current as those projects
change, that is a Dashboards job: [Perform portfolio
analysis](/analytics/dashboards/common-workflows#perform-portfolio-analysis) adds a Model Viewer
per project and uses **Aggregate** or **Compare** mode across them.
## FAQ
Not in one conversation — Chat answers about the project you have open, not several projects at
once. Pin a benchmark library so the comparison basis travels with the skill, or use [Perform
portfolio analysis](/analytics/dashboards/common-workflows#perform-portfolio-analysis) in
Dashboards when you want several live projects in one view instead.
As fresh as you keep it. A static spreadsheet is a snapshot as of upload. Point the skill's
Knowledge at a live warehouse table instead if the benchmark should update automatically.
The current project does. Comparing versions or reading model relationships needs the new data
format. See [Speckle's data model is changing](/workspaces/data-model-migration).
Save the conversation as a [Report](/next/analytics/reports) and share it — viewing a saved
report does not need the viewer to run their own Chat turn.
## See also
* [Ask questions about your models with Speckle Intelligence](/next/analytics/intelligence)
* [Reuse organisation playbooks with Intelligence skills](/next/analytics/intelligence-skills)
* [Create and share Intelligence Reports](/next/analytics/reports)
* [Perform portfolio analysis](/analytics/dashboards/common-workflows#perform-portfolio-analysis)
— the live, multi-project dashboard route
# Workflows in 2026.9
Source: https://docs.speckle.systems/next/workflows/overview
Which workflow guides still apply in 2026.9, and which 2026.9 pages replace changed steps.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Use this hub to pick a workflow guide from the set under **Current** in the version selector and the 2026.9 pages that change how that outcome looks. It does not clone those tutorials. For that set, see
[Workflows](/workflows/overview).
## What still applies
These guides still describe the outcome. Use them for the repeatable pattern. Switch to
the 2026.9 pages below when a step hits the new viewer, Intelligence, or a dump-host connector.
* [Design review](/workflows/design-review-workflow) — saved views, federation, markups, and
issues. In 2026.9 the viewer chrome is different: see [3D viewer in
2026.9](/next/3d-viewer/introduction).
* [Model validation](/workflows/model-validation-workflow) — rules and dashboards. Workspace
**Intelligence** still opens dashboards. Chat is a separate surface: see [Ask questions about
your models with Speckle Intelligence](/next/analytics/intelligence).
* [Grasshopper componentised pipeline](/workflows/grasshopper-componentised-workflow) — modules
that hand off data through Speckle models. Node names, Explore, and script upgrades are on
[Grasshopper in 2026.9](/next/connectors/grasshopper/introduction) and [Upgrade Grasshopper to
2026.9](/next/connectors/grasshopper/upgrading).
* [Revit room data extraction](/workflows/revit-room-data-extraction-specklepy) — pull room data
with SpecklePy. Revit placement and Apply Transform are on [Revit in
2026.9](/next/connectors/revit/introduction).
* [SpecklePy model analytics](/workflows/specklepy-model-data-analytics) — load published data
into DuckDB. Publish and load still follow the connector guides under **Current** unless your host has a
2026.9 page. See [Connectors in 2026.9](/next/connectors/overview).
* [Grasshopper design explorer](/workflows/grasshopper-design-explorer) — compare alternatives from
Grasshopper. Do not treat that page as a 2026.9 rewrite. Explore and related connector changes
live under [Grasshopper in 2026.9](/next/connectors/grasshopper/introduction).
Mapping Speckle data to native families and types is still coming soon in those guides. 2026.9 does not add that tutorial.
## New in 2026.9
Worked examples with no Current equivalent — each shows a real pattern end to end rather than
just listing what's possible:
* [Assess model changes with Speckle Intelligence](/next/workflows/assess-model-changes-with-intelligence)
— ask Chat what changed between two versions, then filter and colour the model by how
significant each change is.
* [Take off quantities and cost with Speckle Intelligence](/next/workflows/take-off-quantities-with-intelligence)
— ask Chat for a priced takeoff against your own pinned rate card.
* [Benchmark a project against your portfolio with Speckle Intelligence](/next/workflows/benchmark-against-your-portfolio-with-intelligence)
— pin a benchmark library as Knowledge so Chat can compare the open project against your past
work.
* [Validate incoming models automatically](/next/workflows/validate-incoming-models-automatically)
— set a Validation Check to auto trigger so every new version is checked against your standard
without anyone starting it by hand.
## What 2026.9 changes in those outcomes
* **Viewing** — hybrid load, docked **Selection** / **Filters** / **Views**, and section planes.
See [3D viewer in 2026.9](/next/3d-viewer/introduction).
* **Intelligence** — Chat in the project sidebar and the viewer sparkle. Workspace **Intelligence**
is still dashboards. See [Ask questions about your models with Speckle
Intelligence](/next/analytics/intelligence).
* **Connectors** — Revit Apply Transform, Grasshopper Explore, and Power BI Get Tables. See
[Connectors in 2026.9](/next/connectors/overview), [Revit in
2026.9](/next/connectors/revit/introduction), [Grasshopper in
2026.9](/next/connectors/grasshopper/introduction), and [Power BI in
2026.9](/next/connectors/power-bi/introduction).
Until a 2026.9 page exists for another step, use the workflow guide under **Current** in the version selector.
## FAQ
2026.9 docs stay sparse. A missing page means use the guide under **Current**, not that the
workflow went away. This hub exists so you can find both.
Yes. The outcome is the same. The 3D viewer chrome around those tools changed. Follow [Design
review](/workflows/design-review-workflow) for the pattern and [3D viewer in
2026.9](/next/3d-viewer/introduction) for the new panels.
That guide still describes the dashboard pattern. Connector steps that changed in 2026.9 are on
[Grasshopper in 2026.9](/next/connectors/grasshopper/introduction), not as a second tutorial
here.
# Take off quantities and cost with Speckle Intelligence
Source: https://docs.speckle.systems/next/workflows/take-off-quantities-with-intelligence
Ask Chat for a quantity takeoff and a priced estimate straight from the model, using your own rate card, instead of building a takeoff dashboard by hand.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow linked Current documentation for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
A quantity takeoff and a priced estimate are usually two separate jobs: count elements in a
dashboard or export, then take that export somewhere else to apply rates. This page asks for both
in one conversation — a takeoff by category, priced against your own rate card, with anything it
can't price flagged instead of guessed.
Speckle Intelligence Chat is available when your workspace plan includes it. On a self-hosted
server, it is available only on Speckle Enterprise Server. For deployment setup, see [Enterprise
license — Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
This is the Chat-driven route. For a dashboard you can pin, share, and keep open on a screen
instead of asking for it each time, build a [quantity takeoff
dashboard](/analytics/dashboards/common-workflows#run-a-quantity-takeoff) instead — the two
approaches read the same published data.
## What this workflow does
* **Take off** — count and total elements by category, level, or any other property, without
building the breakdown in a dashboard first.
* **Price** — apply your own unit rates and cost codes instead of a generic estimate.
* **Flag gaps** — call out anything with no matching rate, rather than inventing a number.
* **Reuse** — pin the rate card once so every future takeoff on any project uses this year's
prices.
## Walkthrough
In workspace **Settings → Skills**, create a skill and pin your rate card (a spreadsheet, or a
live warehouse table if finance keeps it there) as
[Knowledge](/next/analytics/intelligence-skills#what-you-can-attach). Write the unit rates and
cost codes into the skill's instructions in your own language — elemental, package, or
whatever your commercial team already calls them. See [Reuse organisation playbooks with
Intelligence skills](/next/analytics/intelligence-skills).
In Chat, arm the skill with `/skill-name` and ask in plain language:
> Give me a material takeoff, priced against our rate card.
Intelligence reads quantities from the published model — the same [connected, queryable
data](/next/analytics/intelligence#how-it-works) it uses for any other question — and matches
each line against the pinned rate card.
The reply groups quantities the way your skill's instructions say to — by category, level, or
package — with each line priced. Anything without a matching rate is called out separately
instead of estimated, so a missing row is visible rather than silently averaged away.
Keep going in the same conversation:
> What's the impact on cost if steel goes up by 15%?
Intelligence recalculates from the same quantities and the same pinned rates, so the answer
reflects your actual take-off, not a generic assumption.
## Share it beyond the chat
Save the conversation as a [Report](/next/analytics/reports) if you want a shareable artefact —
the takeoff and its pricing stay live against the bound version rather than frozen at the moment
you asked. For a takeoff someone will keep open and re-filter by level or category on a screen,
use the dashboard route in [Run a quantity takeoff](/analytics/dashboards/common-workflows#run-a-quantity-takeoff)
instead; **Element table** there also exports straight to CSV.
## FAQ
Only if you say so. Put your unit and rounding conventions in the skill's instructions —
Intelligence does not know your commercial team's conventions unless you tell it once, in the
skill.
It gets flagged as unmatched rather than priced by guesswork. Write that behaviour into the
skill explicitly (see the [cost, carbon, and quantities skill
catalogue](/next/analytics/intelligence-skills#cost-carbon-and-quantities)) if you want the flag
to use a specific phrase your commercial team recognises.
Yes. Like every other Intelligence query, a takeoff needs a version in the new data format.
Versions still in the old format cannot be queried. See [Speckle's data model is
changing](/workspaces/data-model-migration).
Yes — just ask for the takeoff without pricing, for example "give me a material takeoff" or
"give me a report of quantities by category and level." Pricing only kicks in once you've pinned
a rate card in a skill.
## See also
* [Ask questions about your models with Speckle Intelligence](/next/analytics/intelligence)
* [Reuse organisation playbooks with Intelligence skills](/next/analytics/intelligence-skills)
* [Create and share Intelligence Reports](/next/analytics/reports)
* [Run a quantity takeoff](/analytics/dashboards/common-workflows#run-a-quantity-takeoff) — the
dashboard-based route
# Validate incoming models automatically
Source: https://docs.speckle.systems/next/workflows/validate-incoming-models-automatically
Author a check once and set it to run on every new version, so a consultant, subcontractor, or federated model is validated against your standard at each submission without anyone remembering to run it.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow linked Current documentation for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Checking every incoming model by hand against your own standard doesn't scale — someone has to
remember to run it, on every submission, on every model. A **Validation Check** with **auto**
trigger removes the remembering: author the rule once, and it runs itself against every new
version of the tracked model. This page covers that specific pattern — a standard applied at
every submission milestone, not a one-off spot check.
This is a rules-based check, not a Chat conversation — Data Validation runs the same `WHERE` /
`CHECK` predicates every time rather than reasoning about a question. For plain-language questions
about a model, use [Ask questions about your models with Speckle
Intelligence](/next/analytics/intelligence) instead.
## What this workflow does
* **Encode the standard once** — required parameters, naming pattern, or a value that must match
a related object, as `WHERE` / `CHECK` rules.
* **Run it without anyone asking** — **auto** trigger fires the check against tracked models when
a new version lands, so validation happens at the moment of submission, not whenever someone
remembers.
* **Score the submission** — a pass rate against your threshold, not just a pass/fail per object.
* **Keep a history** — every run is recorded, so you can see whether a consultant's compliance is
improving or not across a project.
## Walkthrough
Open **Run check** in the project sidebar and draft your rules — for example, every element from
the incoming discipline must carry a required parameter, follow a naming pattern, or match a
value on a related host object. See [Checks](/analytics/data-validation/checks) for rule
structure, predicates, and how to check an object against something it's related to.
Run **Preview** against the model you're building the check for. Tighten `WHERE` first if the
scope is too broad — a check that's too loose floods the result with noise nobody will read.
Save the check with **auto** trigger, and set your pass (and optional warn) threshold. From this
point, every new version of the tracked model runs this check without anyone starting it
manually.
Each new version's result appears in **Checks** with its pass rate. A consultant's or
subcontractor's model is validated against your standard the moment it's published — not at
whatever point someone gets around to checking it.
## Reuse the standard elsewhere
A saved check is tied to its tracked models. If the same standard applies on other projects,
recreate it there rather than expecting it to follow a model automatically — 2026.9 does not yet
have a rollup from one project's checks into a workspace-wide standard; see [Project
standards](/next/analytics/data-validation/overview#project-standards) for where that's headed.
## FAQ
No — after saving, only the check's name and description are editable. Tracked models, rules,
predicates, and trigger mode stay frozen. Create a new check for different behaviour. See
[Checks](/analytics/data-validation/checks).
Data Validation preview and saved-check runs need a version in the 2026.9 data format. If a
submission is still on the old format, ask the consultant or subcontractor to publish with a
current connector. See [Speckle's data model is changing](/workspaces/data-model-migration).
Yes, on a version in the 2026.9 data format — add a related object to the rule and pick the
relationship (host element, room, connected element, and others). See [Checks against related
objects](/next/analytics/data-validation/overview#checks-against-related-objects).
The result appears in **Checks** with its pass rate; this page does not cover a separate
notification channel. Build a **Model Validation** or **Property checker** widget into a
dashboard if you want the pass/fail state visible somewhere your team already looks — see [Run a
quick validation check](/analytics/dashboards/common-workflows#run-a-quick-validation-check).
No. A skill is an opt-in playbook someone arms with `/skill-name` in a conversation. A
Validation Check with auto trigger runs on its own schedule (every new version) independent of
Chat. They can complement each other — for example, ask Chat to summarise a check's recent
failures in plain language — but the check itself is authored and run through Data Validation.
## See also
* [Checks](/analytics/data-validation/checks)
* [Data Validation in 2026.9](/next/analytics/data-validation/overview)
* [Viewing Results](/analytics/data-validation/viewing-results)
* [Run a quick validation check](/analytics/dashboards/common-workflows#run-a-quick-validation-check)
— the dashboard-widget route for a spot check rather than a standing gate
# Workspaces in 2026.9
Source: https://docs.speckle.systems/next/workspaces/introduction
Workspace sidebar in 2026.9 — Home, Projects, Intelligence, gated Documents, Team, and Settings.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
The workspace sidebar in 2026.9 lists the live areas of the web app. Workspace cards from before 2026.9 show Home, Projects, Intelligence, Team, and Settings only. This page is the map for what you
actually see. For how workspaces work today, see [Workspaces](/workspaces/introduction).
Workspaces are available on Speckle Cloud and on Speckle Enterprise Server. For self-hosted setup,
see [Enterprise license](/developers/server/deployment/enterprise-license#enable-workspaces).
## Find your way around
The left sidebar is the same on Home and on other workspace pages:
* **Home** — recent activity and a summary of the workspace.
* **Projects** — the projects in this workspace. See [Projects](/workspaces/projects).
* **Intelligence** — workspace dashboards. This is not Chat. Chat lives in the project sidebar,
next to **Browse**. See [Ask questions about your models with Speckle
Intelligence](/next/analytics/intelligence) and [Intelligence
Dashboards](/analytics/intelligence-dashboards).
* **Documents** — the workspace file library. This item appears only when the workspace includes
Speckle Intelligence Chat and the documents library is enabled.
* **Team** — members, invites, and seats. See [Inviting](/workspaces/inviting) and [Roles and
permissions](/workspaces/roles-and-seats).
* **Settings** — workspace-wide controls. The settings map and Usage how-to live under IT
Administrators: [Workspace settings in 2026.9](/next/workspaces/settings) and [Usage statistics
in 2026.9](/next/workspaces/usage).
**Documents** is gated. If you do not see it, the workspace does not have that library yet. It is
not missing from the product.
## Open user settings
Your account is no longer behind a top-right avatar. Open the **workspace switcher**: the workspace
name at the top of the left sidebar. In that menu, select **your name** (the row with your avatar).
That row opens **User settings** on **Profile**. It is a link, not a signed-in label. There is no
separate Profile or Settings item in this menu.
User settings then lists **Profile**, **Notifications**, **Developer** (access tokens and apps),
and **Emails**. The same workspace-switcher menu also has **Theme**, **Copy support reference**,
and **Log out**. Server administrators also see **Server settings**.
Clicking your name is how you open profile settings. Treat it as the call to action, not as a
label that only shows who is logged in.
## What has no 2026.9 page
Projects, models, versions, roles, and inviting still use the guides under **Current** in the version selector. This page does not
clone them.
## FAQ
Workspace **Intelligence** opens dashboards. Chat is in the project sidebar, next to **Browse**.
The sparkle in the 3D viewer is a third entry point for questions about loaded models. See [Ask
questions about your models with Speckle Intelligence](/next/analytics/intelligence).
**Documents** appears only when the workspace includes Speckle Intelligence Chat and the
documents library is enabled. Other sidebar items stay. Ask a workspace admin if you expected to
see it.
Open workspace **Settings** in the left sidebar. The children and who can change them are on
[Workspace settings in 2026.9](/next/workspaces/settings) (IT Administrators). Depth that is
still accurate lives on [Configuration](/workspaces/configuration).
They moved into the workspace switcher. Open the workspace name at the top of the left sidebar,
then select your name. That opens User settings. See [Open user settings](#open-user-settings).
# Workspace settings in 2026.9
Source: https://docs.speckle.systems/next/workspaces/settings
Map of workspace Settings in 2026.9, including who can see each item.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Open workspace **Settings** when a change should apply to the whole workspace. This page is the
live map for workspace admins. [Configuration](/workspaces/configuration) still covers
SSO, SCIM, billing, and roles in depth. This page does not clone those guides.
Workspace members can open most settings pages. Only workspace admins can change workspace-level
controls. Some items appear only when the plan includes them.
Workspaces are available on Speckle Cloud and on Speckle Enterprise Server. For self-hosted setup,
see [Enterprise license](/developers/server/deployment/enterprise-license#enable-workspaces).
## Open Settings
Sign in and open the workspace. The left sidebar shows Home, Projects, Intelligence, Team, and
Settings. You should see **Settings** at the bottom of that list.
Select **Settings**. The sidebar expands to the children below. You should see **General**
first.
## Settings map
Items appear in this order. Hidden items are gated by plan or permission, not missing from the
product.
* **General** — name, short ID, description, logo, and the **AI features** switch. Everyone who
can open Settings sees this page. Only admins can save changes or turn AI features off. See
[Turn AI features off for the workspace](/next/it-admin/introduction#turn-ai-features-off-for-the-workspace).
* **Usage** — activity, plan limits, and AI spend. Workspace admins see this item when the plan
includes usage analytics. See [Usage statistics in 2026.9](/next/workspaces/usage).
* **Project metadata** — fields every project in the workspace can fill out. Workspace admins see
this item when the plan includes project metadata. See [Projects —
metadata](/workspaces/projects#metadata).
* **Project labels** — labels used to organise projects. See [Projects — project
labels](/workspaces/projects#project-labels).
* **Issue labels** — labels used on project issues. See [Issues — labels](/3d-viewer/issues#labels).
* **Tokens** — workspace share tokens for read-only access to models, presentations, or dashboards.
You can revoke one token or **Revoke all**.
* **Security** — discoverability, domain protection, SSO, and related identity controls. See
[Workspace discoverability](/workspaces/workspace-discoverability), [Domain
protection](/workspaces/domain-protection), [SSO](/workspaces/sso), and [SCIM](/workspaces/scim).
* **Billing and plans** — plan, payment, and Customer Portal. See [Billing](/workspaces/billing).
* **Data residency** — where workspace data is stored. See [Data
residency](/workspaces/data-residency).
* **Automation** — functions available to projects in this workspace, including **Regenerate
token...**. Speckle Automate is available only on Speckle Enterprise Server. For setup, see
[Enterprise license —
Automate](/developers/server/deployment/enterprise-license#optional-automate). Where Automate
lives in the 2026.9 web app is on [Automate in 2026.9](/next/developers/automate/introduction).
* **Support** — support sessions. This item appears when Admin Support Mode is enabled. See [Admin
Support Mode](/it-admin/support-mode). On self-hosted servers, that feature is available only on
Speckle Enterprise Server. For setup, see [Enterprise license — Admin Support
Mode](/developers/server/deployment/enterprise-license#optional-admin-support-mode).
* **Skills** — reusable playbooks for Speckle Intelligence Chat. Members see this item when the
plan includes Chat. Create skills here (including **Draft with AI**), then invoke them in Chat
with `/skill-name`. See [Reuse organisation playbooks with Intelligence
skills](/next/analytics/intelligence-skills).
* **AI Rules** — standing instructions Speckle Intelligence follows in every conversation.
Members can open this item; only workspace admins can change rules. See [Set standing AI Rules
for the workspace](/next/analytics/intelligence-rules).
## Project settings stay on the project
Workspace Settings do not replace project settings. Open a project, then **Settings**, for that
project's **General**, **Webhooks**, **Tokens**, and **Automations**. [Configuration](/workspaces/configuration) still describes project visibility, collaborators, and
share tokens.
## FAQ
Those items are gated. **Usage** is for workspace admins on a plan that includes usage
analytics. **Skills** appears when the workspace includes Speckle Intelligence Chat.
**Automation** appears when Automate is available. The rest of Settings still opens.
Workspace admins can change workspace-level settings. Members can view most pages but cannot
apply those changes. See [Roles and permissions](/workspaces/roles-and-seats).
Open **Settings**, then **General**, and turn **AI features** off. Only workspace admins can
change it, and it applies to everyone in the workspace. See [Turn AI features off for the
workspace](/next/it-admin/introduction#turn-ai-features-off-for-the-workspace).
Open **Settings**, then **Security**. The existing guides still apply: [SSO](/workspaces/sso)
and [SCIM](/workspaces/scim).
# Usage statistics in 2026.9
Source: https://docs.speckle.systems/next/workspaces/usage
Workspace Settings → Usage in 2026.9 — activity, plan limits, and AI spend.
Documentation set: Speckle 2026.9. Status: current. Authority: canonical for this version and the coverage on this page.
Applies to version 2026.9, from 2026-09; this is not a rollout date for every deployment.
Authoritative Speckle 2026.9 documentation for the topic covered here. For Speckle 2026.9, prefer this page over conflicting earlier guidance. Follow the linked pages in the other documentation version for unchanged topics and where this page says coverage is not yet available. Respect data-format, deployment, plan, permission, and compatibility limitations stated in the page.
This is 2026.9 documentation. Coverage here is incremental: a page exists only where 2026.9
differs or is newly documented. For any other topic, switch to **Current** (2026.8 and earlier) in
the version selector.
Workspace **Settings → Usage** shows activity, plan limits, and AI spend for this workspace. Use
this page as a workspace admin. The guides under **Current** in the version selector have no equivalent page. The closest are [Configuration](/workspaces/configuration) and [Billing](/workspaces/billing).
If **Usage** is missing from Settings, you do not have that permission or the plan does not include
it. The Settings map is on [Workspace settings in 2026.9](/next/workspaces/settings).
## Open Usage
Sign in, open the workspace, then select **Settings**. You should see **General** at the top of
the Settings list.
Select **Usage**. The heading is **Usage**. The subtitle is "Activity, plan limits and AI spend
for this workspace." You should see a filter bar and section tabs.
## Filter and export
The filter bar applies to the activity sections, not to **Limits**.
* Range: **Last 7 days**, **Last 30 days**, **Last 90 days**, or **Last 12 months**.
* **Projects**, project labels, and **Member** narrow the scope. **Clear** removes that scope.
* Charts and tables include **Download CSV** when there are rows to export.
**Limits** is workspace-wide. Changing the filters does not change that tab.
## Sections
* **Projects** — which projects see the most activity, and how many are active month to month.
* **Users** — the most active people, and how many are active month to month.
* **Integrations** — where versions come from, and which ACC and ProjectWise files are wired to
auto-refresh.
* **Dashboards & Reports** — which projects every dashboard and report is attached to, and who
owns it.
* **AI** — token spend by user, project, purpose, and tool. This tab appears when the workspace
includes Speckle Intelligence Chat. Under a project scope, workspace-level chats drop out.
* **Limits** — what the plan includes and how much of it is used. **Manage plan** opens **Billing
and plans**. See [Billing](/workspaces/billing).
Empty activity sections show "No usage data available" until people start working in the
workspace. If a load fails, the page shows "Couldn't load usage data" and **Try again**.
Speckle Intelligence Chat is available when your workspace plan includes it. On a self-hosted
server, it is available only on Speckle Enterprise Server. For deployment setup, see [Enterprise
license — Intelligence](/developers/server/deployment/enterprise-license#optional-intelligence).
## FAQ
**Limits** is workspace-wide. The filters apply to Projects, Users, Integrations, Dashboards &
Reports, and AI. Use **Manage plan** on the Limits tab to open **Billing and plans**.
**AI** appears when the workspace includes Speckle Intelligence Chat. The other tabs stay. Chat
itself is documented on [Ask questions about your models with Speckle
Intelligence](/next/analytics/intelligence).
No. Before 2026.9, the guides cover billing and configuration, not this analytics surface. Use
[Configuration](/workspaces/configuration) and [Billing](/workspaces/billing) for those topics,
and this page for Usage.