# Entity types
URL: https://lsd.tools/docs/tools-entities
Updated: 2026-07-18

> An entity is a creatable, named container in the project — it carries its own parameters and a small pipeline of child steps. Declare one two ways: a builder function whose signature becomes the entity's form and whose body wires the child steps once at creation, or a hand-written Entity subclass for full control. Both appear in the New… menu.

An **entity** is a creatable, named container in a project: it owns a set of **parameters** and a
small pipeline of **child steps** wired together. Users create one from the *New…* menu, and
[drag-drop templates](/docs/tools-panels-overlays-views) build one from dropped files. Declare an
entity type with `@tool.entity`.

## Usage

```python
from lsdtools import Tool

tool = Tool("survey")

@tool.entity(label="Drillholes", icon="lsd-folder-symbolic")
def drillholes(collar: str, survey: str):
    """A creatable Drillholes entity.

    The signature becomes the entity's form; the body runs once, at
    creation, to wire the child steps and returns the terminal step.
    """
    collars = load_csv(path=collar) # a @tool.load node
    desurveyed = desurvey(collars, load_csv(path=survey))
    return desurveyed # the terminal step
# → "Drillholes" appears in the New… menu with a two-field form
```

## The builder function

The most common form. The function's **signature becomes the entity's declarative parameters** —
the form a user fills when creating it — and its **body runs once at creation** to build and wire
the child steps, returning the terminal step of the entity's pipeline. `label` sets the display
name; `icon` sets the tree icon (defaults to `"lsd-folder-symbolic"`).

Inside the body you call your other step factories (`load_csv(...)`, `desurvey(...)`) exactly as you
would when [wiring a pipeline](/docs/tools-shape) — the returned chain becomes the entity's contents.

## The Entity subclass

For full control, decorate a hand-written [`Entity`](/docs/api-tool) subclass. Here the decorator
simply **registers the type** (for attribution and wizard interop) and returns the class unchanged:

```python
from lsdtools import Tool, Entity

tool = Tool("survey")

@tool.entity
class Borehole(Entity):
    """A hand-written entity type — you define its params and children."""
    ...
```

Use this when the declarative builder can't express what you need; otherwise prefer the function.

## Entities at run time

From a [`Context`](/docs/api-context) (in a command, action, or view), reach an entity through the
project and drive it:

```python
e = ctx.project.entity("holes_1")
e.params # its form values
e.run() # run the entity's pipeline
e.output # the terminal Table
e.update(collar="collars_v2.csv") # change a parameter
```

An entity is often the target a [drag-drop template](/docs/tools-panels-overlays-views) constructs —
the template's build function returns the `Entity` a dropped file set produces.

## Related

**Learn** — [Shape steps](/docs/tools-shape) · [Node variants](/docs/tools-variants) · [Contributing UI](/docs/tools-panels-overlays-views)

**API** — [Tool](/docs/api-tool) · [Context](/docs/api-context) · [Table](/docs/api-table)

**Examples** — [Drag-drop import](/docs/ex-import-template) · [A complete package](/docs/ex-full-package)

## FAQ
### What is the difference between an entity and a step?

A step is a single node (load, shape, deliver). An entity is a named container that owns a set of parameters and a small graph of child steps wired together — the unit a user creates from the New… menu and that drag-drop templates build.

### When should I use a builder function versus an Entity subclass?

Use the builder function for the common case — its signature is the form and its body wires the children in a few lines. Reach for an Entity subclass when you need full control over the type's behaviour; the decorator just registers it.
