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

# UI and Temporal signals

> How Tenant UI actions reach Ntropii Tenant workflows through Ntropii Workspace and Temporal — approve/reject, DATA_TABLE row_action, and refreshed UI state.

Interactive workflow steps (approve bar, **DATA\_TABLE** inline edit/reject, **FILE\_UPLOAD** submit) do **not** mutate tenant Postgres directly from the UI. The browser talks to **Ntropii Tenant** (`api-tenant`); Tenant forwards to **Ntropii Workspace** (`api-workspace`), which signals the **Temporal** workflow. Domain rules (`ntro.subledger`, activities) run inside the worker — validation belongs there, not in thin HTTP layers.

## End-to-end sequence

```mermaid theme={null}
sequenceDiagram
  participant U as Tenant UI (ui-tenant)
  participant T as Ntropii Tenant (api-tenant)
  participant W as Ntropii Workspace (api-workspace)
  participant C as Temporal cluster
  participant WF as Workflow (NtroWorkflow)
  participant A as Activities / domain

  U->>T: POST /v1/tasks/:taskId/action or /row-action (JSON body)
  T->>W: Forward approve/reject or row-action for workspace task id
  W->>C: SignalWorkflow(user_action / row payload)
  C->>WF: Signal delivered (deterministic handlers)
  WF->>A: Optional activity — apply mutation (e.g. ntro.subledger)
  A-->>WF: Result / persistence
  WF-->>C: Updated workflow state, snapshots
  U->>T: Poll WebSocket / GET task UI or next-step
  T->>U: UIState with display_hint + payload (fresh rows / steps)
```

**Transport vs logic**

| Layer                 | Responsibility                                                                 |
| --------------------- | ------------------------------------------------------------------------------ |
| **api-tenant**        | Auth, tenant scope, map HTTP JSON → workspace calls. No ledger business rules. |
| **api-workspace**     | Resolve org/task, route signal to the correct Temporal workflow execution.     |
| **Workflow + domain** | Interpret payload, enforce transitions, call activities / `ntro.subledger`.    |

## Payload contracts (conceptual)

### Step approve / reject (`wait_for_action`)

Used when the workflow blocks on a **single** HITL gate with primary actions.

| Field      | Meaning                                                         |
| ---------- | --------------------------------------------------------------- |
| `type`     | `approved` **or** `rejected` — mirrors UI primary/failure CTAs. |
| `comments` | Optional free-text (workspace may map to `reason`).             |

Tenant UI typically sends **`POST /tasks/:taskId/action`** with `{ "type": "approved" \| "rejected", "comments": "..." }`. Workspace translates into the **`user_action`** waveform your workflow expects alongside **`wait_for_action`**.

### DATA\_TABLE row\_action (`edit_cell` / `reject_row`)

Forwarded by Workspace as a **`user_action`** whose **`kind`** is **`row_action`** (workflow drains these into **`on_row_action`**).

| Field    | Required        | Meaning                                                         |
| -------- | --------------- | --------------------------------------------------------------- |
| `ledger` | Yes             | Subledger name today — e.g. `expenses`.                         |
| `action` | Yes             | `edit_cell` **or** `reject_row`.                                |
| `rowId`  | Yes             | Stable row UUID in the subledger table.                         |
| `field`  | For `edit_cell` | Column key (e.g. `amount_gross`).                               |
| `value`  | For `edit_cell` | New scalar / null — coercion and validation in workflow/domain. |

**Tenant HTTP:** `POST /tasks/:taskId/row-action` with JSON matching the DTO above.

## Replay, idempotency, errors

* **Determinism:** Workflow code must not perform non-deterministic I/O directly — mutations happen in **activities** or signal handlers that only record intent for activities.
* **Idempotency:** Row deletes/edits should be safe if the same signal is replayed or retried (workflow handlers typically gate by row status).
* **Errors:** Failed activities surface as workflow/activity failure or structured UI errors; inline edits show **per-cell** error state while **approve** stays disabled until conflicts resolve — driven by refreshed **`UIState`** from Tenant.

## Cross-links

<CardGroup cols={2}>
  <Card title="Workflows overview" icon="diagram-project" href="/workflows/runbooks/overview">
    `wait_for_action`, `user_action`, `on_row_action`.
  </Card>

  <Card title="Subledgers" icon="table" href="/sdk/subledgers">
    Where persisted rows live after successful signals.
  </Card>
</CardGroup>
