> ## Documentation Index
> Fetch the complete documentation index at: https://docs.speckle.systems/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These docs describe Speckle as it works today, including everything released in 2026.9. Treat page content as current unless the page or section says it applies only to versions published before 2026.9.
> Content scoped to versions published before 2026.9 (the object graph and root object, Proxies, Receive2, operations.receive, object.children) applies only to model versions in that earlier data format. For versions published in 2026.9, answer from the Object model pages and the SDK migration guides.
> The What's new pages summarise what changed in 2026.9 and link to the page that documents each change. Answer from the linked page rather than from the summary. Legacy (v2) connectors are retired; send questions about them to the community help forum.
> Match guidance to the customer's deployment, model data format, connector, plan and permissions. Self-hosted servers on a release before 2026.9 and compatibility-mode workflows follow the Compatibility mode page. If an unknown version or data format changes the answer, ask one focused question; do not infer the deployment version from the date alone.

# Load a bundle in your own code

> How a consumer lists a version's artifacts, downloads the bundle, and rebuilds objects, geometry, instances, grouping, and appearance without a Speckle SDK.

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 bundle version. This page
assumes the model from [Build integrations on the bundle
format](/developers/building-integrations/bundles).

## Tell the two formats apart

Any version you are asked to load may still be an object graph, for example one published before
2026.9 or in compatibility mode. Read the version record first.

| Field | Object graph (before 2026.9) | Bundle (2026.9) |
| - | - | - |
| `Version.schemaVersion` | `null` | `3` |
| `Version.referencedObject` | A content hash | `bundle.<projectId>.<modelId>.<versionId>` |
| 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

<Steps>
  <Step title="Resolve the 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`.
  </Step>

  <Step title="List the artifacts">
    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.
  </Step>

  <Step title="Download the files you need">
    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.
  </Step>

  <Step title="Read the catalogs first">
    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.
  </Step>

  <Step title="Rebuild the model">
    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.
  </Step>

  <Step title="Mark the version received">
    Call the mark-received mutation with your application slug. The outcome is a receive recorded
    against the version, the way every Speckle connector does.
  </Step>
</Steps>

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.

<Tabs>
  <Tab title="GraphQL">
    ```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": "<projectId>",
        "versionId": "<versionId>",
        "sourceApplication": "my-consumer"
      }
    }
    ```
  </Tab>

  <Tab title="REST">
    ```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 <token>

    200 {
      "files": [
        { "name": "<versionId>.envelope.meta.parquet", "url": "https://...", "expiresAt": "..." },
        { "name": "<versionId>.geometries.parquet", "url": "https://...", "expiresAt": "..." },
        { "name": "<versionId>.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 <presigned url>
    ```
  </Tab>

  <Tab title="Python">
    ```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)
    ```
  </Tab>

  <Tab title=".NET">
    ```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}");
    ```
  </Tab>
</Tabs>

<Note>
  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.
</Note>

<Note>
  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.
</Note>

## 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

* Find each table by the end of its file name (`.envelope.meta.parquet`, `.eav.objects.parquet`,
  and so on), never by building the name from the version id. A server-converted upload can use
  the source file name as the stem.
* 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, `.geometries.parquet` and `.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](/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_MATERIAL` and `HAS_COLOR` start from geometry. Older bundles can carry an instance or object
  source on them, tagged by `ord`, so keep a geometry-first fallback for those.
* Appearance edges are single-valued per source. When a bundle repeats one, the last row wins.

### Placement

* Read `modelPlacement.*` 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

<Steps>
  <Step title="List and download">
    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.
  </Step>

  <Step title="Read properties only">
    Objects by `applicationId`, nested properties from paths, type-level rows merged in. The outcome
    is a property table you can filter, before any geometry.
  </Step>

  <Step title="Decode display meshes">
    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.
  </Step>

  <Step title="Expand instances">
    Definitions, placements, nested definitions, and carrier suppression. The outcome is repeated
    objects in the right places, each drawn once.
  </Step>

  <Step title="Apply grouping and appearance">
    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.
  </Step>

  <Step title="Apply placement and mark received">
    The placement contract, then the mark-received call. The outcome is geometry in the right datum
    and a receive recorded on the version.
  </Step>
</Steps>

## FAQ

<AccordionGroup>
  <Accordion title="What happens if the artifacts listing returns 404?">
    Either the version is an object graph, 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.
  </Accordion>

  <Accordion title="Can I read properties without downloading geometry?">
    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.
  </Accordion>

  <Accordion title="Can I edit a received bundle's values and publish it again?">
    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.
  </Accordion>

  <Accordion title="Do I have to mark the version received?">
    No, but every Speckle connector does. It is how the web app and analytics know a version was
    loaded, and it costs one mutation.
  </Accordion>

  <Accordion title="What if the bundle uses a relation my reader does not know?">
    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.
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.