This is 2026.9 preview documentation. Coverage here is incremental: a page exists only where
2026.9 differs or is newly documented.
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. The object-model
change itself is on Object model in 2026.9.
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
Update specklepy first
Install a 2026.9 specklepy that can receive bundle-only versions, including thebundle 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.
version.referenced_object is a bundle reference such as
bundle.<projectId>.<modelId>.<versionId>, 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.
Load a model
Prior to 2026.9, you resolvereferenced_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
and the 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().
- Prior to 2026.9 (receive)
- 2026.9 (receive3)
Prior to 2026.9
Count and filter objects
Prior to 2026.9, you walk aBase 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
- 2026.9
Prior to 2026.9
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.Noneif 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.
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.
- Prior to 2026.9
- 2026.9
Prior to 2026.9
Replace Proxy walks
Prior to 2026.9, grouping by level or material means indexingapplicationIds 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
- 2026.9
Prior to 2026.9
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_objectas a content hash. - Bundle-only versions:
referenced_objectis a bundle reference (bundle.<projectId>.<modelId>.<versionId>). A 2026.9 specklepy dispatches on that prefix and returns a best-effortBasetree (Model.to_base(), DataObject idiom,version = 4on 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 authenticatedServerTransportfor the reference’s project. - Older specklepy: fetching that
referenced_objectfrom the objects API returns 404 with upgrade guidance. Upgrade specklepy rather than parsing the 404 body.
Publish from a script
Most analysis scripts only receive. If you currently calloperations.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 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
Do I have to rewrite an operations.receive script on day one?
Do I have to rewrite an operations.receive script on day one?
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.Why does receive3 return Model instead of Base?
Why does receive3 return Model instead of Base?
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.What happens if I forget to close Model?
What happens if I forget to close Model?
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.Can I keep using ServerTransport?
Can I keep using ServerTransport?
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.Why did parameter['value'] stop working?
Why did parameter['value'] stop working?
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.