> ## 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 contain multiple Speckle product experiences. For Speckle 2026.9, prefer /next/ pages for the topics and changes they cover. The navigation label Current does not override this version-specific precedence.
> Coverage under /next/ is incremental. Use Current documentation for unchanged topics and when a /next/ page says coverage is unavailable or explicitly refers you to Current. Do not infer a feature was removed from a missing page.
> Read the page-specific documentation status, applicability, affected guidance and replacement links in agent-only content. Impacted, changed and superseded apply only to the stated scope; they do not mean the entire feature is deprecated. Historical connector guides apply only to the legacy connector described.
> Match guidance to the customer's deployment version, model data format and connector. Respect plan, permission and compatibility restrictions. If an unknown version or data format changes the answer, ask a focused clarification; do not infer deployment version from the date alone.

# Proxy Schemas

> How proxies encode relationships between objects and shared resources

Documentation status: impacted for the changes in Speckle 2026.9 (from 2026-09).

Affected guidance: Tree, nested-property, and Proxy assumptions change for 2026.9 bundle data.

For these changes, read [2026.9 guidance](/next/developers/object-model/overview).

For Speckle 2026.9, prefer the linked guidance only for the affected topics. This page remains applicable to earlier experiences and unchanged guidance. Do not treat the whole page or feature as deprecated.

This page explains how [Proxies](/developers/data-schema/overview#core-glossary) encode relationships between objects and shared resources. Proxies solve a fundamental problem: how to let objects participate in multiple organizational systems without duplicating data. Here you'll learn how proxies reference objects, how [Definition](/developers/data-schema/overview#core-glossary) and [Instance](/developers/data-schema/overview#core-glossary) patterns work, how transforms propagate, and why proxies matter for deduplication, versioning, and performance.

## What Are Proxies?

**Proxies** are relationship containers stored at the Root Collection level. They encode connections between:

* Objects and shared resources (materials, levels, groups)
* Objects and other objects (group memberships, instance definitions)

Proxies allow a single object to be:

* On "Level 1" (spatial organization)
* In "Exterior Walls" group (functional grouping)
* Using "Concrete" material (visual property)
* Part of "Block A" definition (instance relationship)

All without creating multiple copies of the object.

## Proxy Structure

All proxies follow this pattern:

```json theme={null}
{
  "speckle_type": "Objects.Organization.RenderMaterial",
  "applicationId": "material-123",
  "name": "Concrete",
  "value": {
    "color": [128, 128, 128],
    "opacity": 1.0
  },
  "objects": ["obj-app-id-1", "obj-app-id-2", "obj-app-id-3"]
}
```

### Key Fields

* **`speckle_type`** - Identifies the proxy type
* **`applicationId`** - Unique identifier for this proxy
* **`name`** - Human-readable name
* **`value`** - The actual resource data (material, level, etc.)
* **`objects`** - Array of `applicationId`s of objects that use this resource

## Common Proxy Types

### RenderMaterial

Encodes material assignments:

```json theme={null}
{
  "speckle_type": "Objects.Organization.RenderMaterial",
  "applicationId": "mat-concrete",
  "name": "Concrete",
  "value": {
    "color": [128, 128, 128],
    "opacity": 1.0,
    "metallic": 0.0,
    "roughness": 0.5
  },
  "objects": ["wall-1", "wall-2", "column-1"]
}
```

### Level

Encodes level/floor associations:

```json theme={null}
{
  "speckle_type": "Objects.BuiltElements.Level",
  "applicationId": "level-1",
  "name": "Level 1",
  "value": {
    "elevation": 0.0,
    "name": "Level 1"
  },
  "objects": ["wall-1", "door-1", "window-1"]
}
```

### Group

Encodes group memberships:

```json theme={null}
{
  "speckle_type": "Objects.Organization.Group",
  "applicationId": "group-exterior",
  "name": "Exterior Walls",
  "value": {
    "name": "Exterior Walls"
  },
  "objects": ["wall-1", "wall-2", "wall-3"]
}
```

### Definition

Stores geometry for instances (blocks, components). The following diagram illustrates how multiple [Instance](/developers/data-schema/overview#core-glossary) objects reference a single [Definition](/developers/data-schema/overview#core-glossary) proxy:

```mermaid theme={null}
graph TD
    A[Definition Proxy<br/>at Root Collection] --> B["applicationId: 'block-door'"]
    A --> C["name: 'Standard Door'"]
    A --> D["value: {<br/>  displayValue: [Mesh, ...]<br/>}<br/>Geometry stored once"]

    E[Instance Objects<br/>in Collections] --> F[Instance 1]
    E --> G[Instance 2]
    E --> H[Instance 3]

    F --> I["definitionId: 'block-door'<br/>References Definition"]
    F --> J["transform: matrix for position 1"]

    G --> K["definitionId: 'block-door'<br/>References same Definition"]
    G --> L["transform: matrix for position 2"]

    H --> M["definitionId: 'block-door'<br/>References same Definition"]
    H --> N["transform: matrix for position 3"]

    I -.references.-> A
    K -.references.-> A
    M -.references.-> A
```

The Definition stores the geometry once; each Instance references it and applies its own transform. This enables geometry reuse without duplication.

```json theme={null}
{
  "speckle_type": "Objects.Organization.Definition",
  "applicationId": "block-door",
  "name": "Standard Door",
  "value": {
    "displayValue": [
      {
        "speckle_type": "Objects.Geometry.Mesh",
        "vertices": [...],
        "faces": [...]
      }
    ]
  },
  "objects": ["instance-1", "instance-2", "instance-3"]
}
```

### Color

Encodes color assignments (CAD workflows):

```json theme={null}
{
  "speckle_type": "Objects.Organization.Color",
  "applicationId": "color-red",
  "name": "Red",
  "value": {
    "hex": "#FF0000"
  },
  "objects": ["line-1", "line-2"]
}
```

## How Proxies Reference Objects

Proxies reference objects by **`applicationId`**, not `id`:

```mermaid theme={null}
graph LR
    A[Object] --> B["applicationId: 'wall-123'"]

    C[RenderMaterial Proxy] --> D["objects: ['wall-123', 'wall-456']"]

    B -.referenced by.-> D
```

To find which objects use a material:

1. Get the RenderMaterial proxy
2. Read its `objects` array
3. Find objects with matching `applicationId`

To find which material an object uses:

1. Get the object's `applicationId`
2. Search all RenderMaterial proxies for one containing that `applicationId` in `objects`

<Accordion title="Why use applicationId instead of id for references?">
  `applicationId` is stable within a model version and represents the source application's identifier. `id` is content-based and changes if the object's data changes. For relationships that should persist even when object data changes, `applicationId` is the right choice.

  Also, `applicationId` is what the source application uses, making it easier to map back to the original data.
</Accordion>

## Proxy Location

All proxies are stored at the **Root Collection** level:

```mermaid theme={null}
graph TD
    A[Root Collection] --> B[Collections<br/>hierarchy]
    A --> C[Proxies<br/>at root level]
    A --> D[Info]

    B --> E[Objects]

    C --> F[RenderMaterial array]
    C --> G[Level array]
    C --> H[Group array]
    C --> I[Definition array]
```

This centralized storage makes it easy to:

* Find all proxies of a type
* Resolve references efficiently
* Avoid duplication

## Multiple Relationships

A single object can be referenced by multiple proxies:

```mermaid theme={null}
graph TD
    A["Object<br/>applicationId: 'wall-1'"]

    B[Level proxy<br/>'Level 1'] -.references.-> A
    C[Group proxy<br/>'Exterior Walls'] -.references.-> A
    D[RenderMaterial proxy<br/>'Concrete'] -.references.-> A
    E[Definition proxy<br/>'Wall Type A'] -.references.-> A
```

## Nested Proxies and Transform Propagation

**Nested proxies** occur when a Definition proxy's `value` contains objects that themselves reference other proxies. For example, a Definition might contain a DataObject that uses a RenderMaterial proxy.

Transform propagation works as follows:

* **Instance transforms** apply to the geometry stored in the Definition proxy
* When resolving an Instance, you:
  1. Find the Definition proxy by `definitionId` (using `applicationId`)
  2. Extract the geometry from `definition.value.displayValue`
  3. Apply the Instance's transform matrix to position/orient the geometry
* **Nested instances**: If a Definition contains Instances, transforms compose—the nested Instance's transform is applied relative to the parent Instance's transform

Conceptually, transforms flow from Instance → Definition → Geometry, positioning the geometry in world space based on the Instance's placement.

## Why Proxies Matter: Deduplication, Versioning, and Performance

Proxies provide three critical benefits:

**Deduplication**: Shared resources (materials, levels, definitions) are stored once and referenced many times. A single RenderMaterial proxy can reference hundreds of objects, avoiding duplication of material data. Similarly, a Definition proxy stores geometry once for many Instances.

**Versioning**: Because proxies reference objects by `applicationId` (which is stable across versions), relationships persist even when object content changes. A wall's material assignment remains linked even if the wall's geometry is modified.

**Performance**: Storing shared resources once reduces data size and transfer time. Instead of duplicating material properties on every object, a single proxy contains the material and references all objects that use it. This is especially important for Definition proxies—geometry stored once can be reused by many Instances without duplication.

These benefits make proxies essential for efficient, maintainable data models, especially in workflows with many repeated elements (blocks, components, shared materials).

## Connector-Specific Proxies

Different connectors use different proxy types:

* **Revit**: RenderMaterial, Level, Group
* **Rhino**: RenderMaterial, Color, Group, Definition
* **AutoCAD**: RenderMaterial, Color, Group, Definition
* **Civil3D**: RenderMaterial, Color, Group, Definition, PropertySetDefinition
* **Etabs**: Section, Material (specialized)

See [connector pages](/developers/data-schema/connector-index) for details.

## Reference Objects (Non-Proxy)

In addition to proxies, Speckle supports direct reference objects that point to other objects by ID. These are used for different purposes than proxies:

### ObjectReference

A reference (`speckle_type: "reference"`; the .NET class is `Speckle.Sdk.Models.ObjectReference`, formerly `Speckle.Core.Models.ObjectReference`) is a non-proxy reference object that points at another object by its `id`:

```json theme={null}
{
  "speckle_type": "reference",
  "referencedId": "object-hash-id"
}
```

Fields:

* **`referencedId`** (required) - The object `id` (hash) of the referenced object
* **`closure`** (optional) - Dictionary mapping closure information

Unlike proxies, which reference by `applicationId` and are stored at root level, a reference points by `id` and can appear anywhere in the object graph.

#### How references get created

You don't build references by hand. The serializer writes one whenever you **detach** a property, and at the same time records the referenced `id` in the parent's `__closure`. That closure entry is what lets the object be found again on receive. Assign the same object to a detached property on several parents, and it is stored once and referenced from each:

<CodeGroup>
  ```python specklepy theme={null}
  spec = Base()
  spec.name = "Type A glazing unit"
  spec.uValue = 1.2

  # "@" marks the property detached; add_detachable_attrs({"specification"})
  # does the same for a property you don't want to prefix.
  element_a["@specification"] = spec
  element_b["@specification"] = spec

  operations.send(base=root, transports=[server_transport])
  ```

  ```csharp .NET theme={null}
  var spec = new Base { ["name"] = "Type A glazing unit", ["uValue"] = 1.2 };

  // "@" marks the property detached, as in specklepy.
  elementA["@specification"] = spec;
  elementB["@specification"] = spec;
  ```
</CodeGroup>

The specification object is stored once, both elements carry a reference to it, and `operations.receive` resolves each one back to the full object.

<Note>
  specklepy has no `ObjectReference` class — `ObjectReference` is the .NET type name. In Python,
  detach the property as above rather than building a reference object yourself.
</Note>

#### References in the 3D viewer

A reference is a pointer, and the viewer's selection panel shows it as one. Select an object whose property holds a reference, and that property lists the reference's own fields — `referencedId`, `speckle_type`, `closure` — not the referenced object's properties. To read those, select the referenced object, which appears in the model tree in its own right.

So if you want a value visible on every object in the viewer, set it on every object. References save storage; they do not copy properties onto the objects that point at them.

### InstanceProxy

`InstanceProxy` is a specialized proxy class for block/component instances (e.g., Rhino blocks, AutoCAD block references):

```json theme={null}
{
  "speckle_type": "Speckle.Core.Models.Instances.InstanceProxy",
  "definitionId": "block-definition-app-id",
  "transform": [4x4 matrix],
  "units": "meters",
  "maxDepth": 10
}
```

Fields:

* **`applicationId`** (required, must be unique per instance) - Identifies this specific placement. Reusing an id across two instances resolves them to the same one - the second placement's transform is dropped, not applied.
* **`definitionId`** (required) - The `applicationId` of the Definition proxy that contains the geometry
* **`transform`** (required) - 4x4 transformation matrix for the instance placement
* **`units`** (required) - Units of the host application file
* **`maxDepth`** (required) - Maximum depth for nested instance resolution

`InstanceProxy` objects are used to represent instances in the object graph. To get the geometry for an instance:

1. Get the `InstanceProxy` object
2. Use its `definitionId` to find the corresponding Definition proxy
3. Access `definition_proxy.value.displayValue` for the geometry

## Proxy Invariants

1. **Proxies are at root level** - Always stored in Root Collection, never nested
2. **References use applicationId** - Always `applicationId`, never `id`
3. **No circular references** - Proxies don't reference other proxies
4. **Value is the resource** - The `value` field contains the actual resource data
5. **objects is a list** - Can reference zero or more objects
6. **applicationId is not mandatory, but where it is relied on as identity it must be unique** - a Proxy's own `applicationId`, and any object's `applicationId` used to resolve a placement (for example `InstanceProxy`), is read as that object's identity. Two objects sharing one are treated as the same object referenced twice, not as two occurrences, and this is not validated - a collision collapses silently rather than raising an error.

<Accordion title="Can an object be in multiple groups?">
  Yes! An object's `applicationId` can appear in multiple Group proxy `objects` arrays. This allows
  objects to belong to multiple functional groups simultaneously. The same applies to materials,
  levels, and other proxy types.
</Accordion>

## Conceptual Capability

After reading this page, you understand how Proxies encode relationships between objects and shared resources: how proxies reference objects by `applicationId`, how Definition and Instance patterns enable geometry reuse, how transforms propagate from Instances to geometry, and why proxies matter for deduplication, versioning, and performance. You recognize that proxies solve the overlapping hierarchies problem by storing relationships separately, allowing objects to participate in multiple organizational systems without duplication. You're ready to explore connector-specific behaviors and extensions.

## Next Steps

* **[Connector Index](/developers/data-schema/connector-index)** - Connector-specific proxy usage
* **[Object Schema](/developers/data-schema/object-schema)** - Review object structure


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