Skip to main content
If your script starts from version.referencedObject, reads units off the root object, or walks down from it, that script breaks on a bundle version: one whose referencedObject starts with bundle.. 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. In versions published before 2026.9, as the Data Schema describes, a version points at one object: version.referencedObject is the id of the 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, and 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 covers that split in full. For how objects relate to each other, see Relations. For the two-store picture, see Object model.

What the root object carried, and where it lives now

Two rows need more detail because scripts depend on them. 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.
1

Open a model ingestion

The SDK creates an ingestion on the model, carrying the producer slug and version from your bundle builder and a progress message from your send options. The outcome is an ingestion with a reserved version id.
2

Build the bundle

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.
3

Upload the files

Each file goes to storage through a presigned upload. The outcome is every file stored under the version id.
4

Complete the upload

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.
5

Wait for the version

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.
version.create / Version.Create publish object graphs, a deprecated path that new work does not use. They too return a reserved id; see Publish through model ingestions.
.NET Automate functions publish the same way. CreateNewVersionInProject also accepts a bundle builder and returns the reserved version id. Call WaitForVersion before the run ends.

Read version metadata from a script

Read the version record for identity and provenance: nullable message, sourceApplication, authorUser, createdAt, schemaVersion, referencedObject, and the optional summary. A summary contains facts the producer computed during conversion, grouped into namespaces such as modelHealth; it is not a replacement for model or object properties. This is the same query as for any other version. 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 placement record (modelPlacement.transform, modelPlacement.units, modelPlacement.source, modelPlacement.appliedToGeometry) and any document settings or project information the connector published. camera_views holds saved cameras.
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, version = 4, and rebuilt proxy lists. 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 it is an object graph. 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 the placement record (modelPlacement.*). Units go on the builder, or on an individual object when it differs from the default.
Last modified on October 19, 2026