# Tool.template
URL: https://lsd.tools/docs/api-tool-template
Updated: 2026-07-17

## Parameters

| name | type | default | description |
|---|---|---|---|
| `template_id` | `str` | — | the unique template id |
| `label` | `str` | — | the human name shown in the import wizard (keyword-only, required) |
| `extensions` | `tuple` | — | file extensions this template matches, e.g. `(".csv",)` (keyword-only, required) |
| `roles` | `Any` | `()` | `[FileRole(...)]` declaring the data slots the wizard maps columns into |
| `params` | `type` | `None` | a params class for wizard-collected options |
| `preview` | `str` | `"auto"` | preview mode for the wizard |
| `priority` | `int` | `0` | tie-breaker when multiple templates match the same files |
| `icon` | `str` | `None` | a symbolic icon name |
| `sniff` | `Callable` | `None` | a content check to match files beyond `extensions` |
| `suggests` | `tuple` | `()` | follow-up templates suggested after this one |

## Example

```python
@tool.template("csv-holes", label="Drillholes CSV", extensions=(".csv",))
def build(ctx, files, name=None):
    return drillholes.build(name=name, collar=str(files["collar"]))
```

## Notes

The build function is `(ctx: Context, files, name=None) -> Entity`. `files["role"]` returns the
`Path` for a role and `files.main` is the primary/single file, so it reads naturally. Typically it
calls an `@tool.entity` factory's `.build(...)`. Dropped files are matched by `extensions` and, if
given, `sniff`; `priority` breaks ties.

## See also

**API:** [entity](/docs/api-tool-entity) · [Context](/docs/api-context)

**Guides:** [Entity types](/docs/tools-entities) · [An import template](/docs/ex-import-template)
