Skip to main content
This is 2026.9 preview documentation. Coverage here is incremental: a page exists only where 2026.9 differs or is newly documented.
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 guide, and it is not a full API reference. Prior to 2026.9, the path is still PAT → bootstrap → Receive2Flatten. 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. The object-model change itself is on Object model in 2026.9.
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.
Prefer Receive3 for new scripts. Receive2 stays callable on legacy object-graph versions.

What changes for a script

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.
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. The snippets below assume account, client, projectId, modelId, and latest are already in scope.
Prior to 2026.9
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.
Prior to 2026.9
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.
Prior to 2026.9

Replace Proxy walks

Prior to 2026.9, grouping by level or material means indexing applicationIds and resolving proxy objects lists. In 2026.9 those links are typed relations. See Relations in 2026.9 for the names.
Prior to 2026.9
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.<projectId>.<modelId>.<versionId>). 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 version_created webhooks 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 page for the transport story.
Last modified on September 2, 2026