# StepContext.emit
URL: https://lsd.tools/docs/api-context-emit
Updated: 2026-07-18

## Parameters

| name | type | default | description |
|---|---|---|---|
| `topic` | `str` | — | the event topic — prefer the topic enums, not raw strings |
| `**data` | `Any` | — | keyword payload delivered to subscribers |

## Example

`emit` is for **live, mid-run** signals a view or the run report listens for — a progress notice, a
"file written" event, a training epoch:

```python
@tool.deliver
def export(t: Table, ctx: StepContext) -> None:
    path = write_parquet(t, ctx.output_dir)
    ctx.emit("file/written", path=path, rows=len(t))
```

## Notes

Routes through the canonical sync-to-async bridge and is **best-effort** — a bad handler never fails
the step.

`emit` is **not** how a deliver *shows* a result. To put a chart, a geometry layer, or a legend in the
viewer, a deliver **returns a [`ViewerPayload`](/docs/api-viewerpayload)** — the framework then persists
it and emits its `viewer/...` topic for you, and reconstructs it on reopen (which a bare `emit` does
not):

```python
@tool.deliver
def grades_chart(t: Table) -> ChartPayload:
    ys = [r["grade"] for r in t.to_pylist()]
    return ChartPayload(kind="bar", title="Grades",
                        series=[{"name": "grade", "x": list(range(len(ys))), "y": ys}])
```

Use the topic enums (`ViewerEvents`, …) rather than hardcoded strings.

## See also

**API:** [progress](/docs/api-context-progress) · [set_output](/docs/api-context-set-output) · [events](/docs/api-context-events) · [StepContext](/docs/api-context)

**Guides:** [Deliver steps](/docs/tools-deliver) · [Rendering & payloads](/docs/tools-rendering)
