Skip to main content
This page describes the root object of versions published before 2026.9. Bundle data has no root object; see Where version metadata lives.
A script that looks for the publish message on the root object, or for units on the version, finds nothing. The two live in different records. The Version is the record the server keeps: who published, when, from which application, and which object to fetch. The root object is the top of the data package itself: the file name, the units, the element tree, and the proxies. This page shows how the two connect and how to read each one.

Resolve a version to its root object

Every version carries a referencedObject field. Its value is the object id of the root of that version’s data package. Fetch the version, read the id, then receive that object.
  1. Get the version. List a model’s versions to take the latest, or fetch one by id.
  2. Read referencedObject. It is a plain object id, the same kind of id every object in the package has.
  3. Receive that id. The SDK downloads the root and every object it references, and returns the root as a Base.
The root object is a Root Collection whenever a connector published the version. Scripts that publish their own data can send any Base as the root. Check speckle_type before you assume a Collection.
referencedObject is null when the version is beyond your workspace’s version history limit. There is nothing to receive for such a version.

Bundle versions have no root object

A bundle version, the format 2026.9 connectors and file uploads publish, has no root object to resolve. Its referencedObject is a bundle reference, bundle.<projectId>.<modelId>.<versionId>, and its schemaVersion is 3 (an object graph has null). Dispatch on the bundle. prefix before you receive:
  • Do not fetch the reference from the objects endpoints. It returns 404 with BUNDLE_REFERENCE_NOT_FOUND. Receive the version with receive3 / Receive3 instead, using the project and version ids you already hold.
  • Read version facts from the version record. The message, author, source application, and creation date stay on the version; see What lives on the Version.
  • Read model-wide facts from the received model. units is per object, and the reference point, document settings, and camera views are model-scoped properties. There is no root name, elements tree, or Proxy array.
Where version metadata lives maps each field of the root object to where it lives in bundle data.
Receive a bundle version with receive3 / Receive3, not receive / Receive2. The older calls return a compatibility projection built at receive time: a Collection named Received model with the constant id artifact-root. Do not key on its id, and do not read a message or source application from it.

What lives on the Version

The server stores these fields when the version is created. Read them from the version record. They are not present on the root object. The .NET and Python SDK Version types expose every row except totalChildrenCount and parents. Query those two fields with the GraphQL API when you need them.

What lives on the root object

The root object is a Base. Connectors publish it as a Collection and set the fields below on it before sending. Only name and elements are declared on the Collection class. The rest are dynamic properties, so read them by key.
A dynamic property that a connector did not set is absent. Read every root field with a fallback instead of assuming it exists.
The stored JSON of the root also carries a __closure table that lists every descendant object. The SDK deserializers consume it while receiving, so it is not present on the Base you get back.

The units convention

Connectors write the source document’s unit to units on the root object. The value is one of the SDK unit strings: mm, cm, m, km, in, ft, yd, mi, or none. Treat it as the default unit for the whole package. Geometry objects also carry their own units field. A connector normally writes the same value to both. When you convert coordinates, read the unit from the geometry object you are converting, and use the root value as the fallback when a geometry object has none.
Scripts that publish a bare Base often omit units on the root. Do not assume metres when it is missing.

Read each in a script

Both snippets assume client, project_id / projectId, and model_id / modelId are already in scope, and that you have authenticated with a personal access token. The Python snippet also assumes a ServerTransport named transport. The .NET snippet assumes account and an IOperations named operations.
Example
You should see one line with the publish message and author, and one line with the document name, element count, and unit.

Set each field in the right place when you publish

New work publishes a bundle instead: give the source application and message to the model ingestion when it opens; see Where version metadata lives. The deprecated object-graph publish keeps the split between root object and version record:
  • Set name, units, and elements on the root Base before you send it. Set them as dynamic properties on a Collection to match what connectors publish.
  • Pass message, sourceApplication, and totalChildrenCount in the create-version input after the send returns the root object id. The call returns a reserved id: the version exists once the server finishes converting it. See Publish through model ingestions.
See Version (.NET) and VersionResource (Python) for the create call.

FAQ

No. A version’s referencedObject is fixed at creation. Updating a version changes only its message. Send the new root and create a new version for it.
The root object and its children stay in the project’s object store, but nothing references them. They are orphaned and reachable only by object id.

Next steps

Core Concepts

Root Collection, Proxies, and Info in detail

Traversal Recipes

Walk elements once you hold the root

Object Schema

Fields on every object below the root
Last modified on October 19, 2026