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

# Live View Component

> A drop-in React component that attaches to a task run, workflow run, or session you already started and streams it to your end user.

The Live View Component is a pre-built React component that shows your end user a task run, workflow run, or session you've already started. It streams the browser, shows the run's status, and collects any [interaction](/guides/interactions) input.

It never creates a run and never cancels one. You start the work from your server, hand the component an ID, and it streams what happens. Closing it leaves the run going.

<Tip>
  To create or update a credential, use the [Auth Component](/components/auth).
</Tip>

## How it works

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {'darkMode': true, 'background': '#0D100F', 'primaryColor': '#1B1E1D', 'primaryTextColor': '#F6F6F6', 'primaryBorderColor': '#323534', 'lineColor': '#60917E', 'actorBkg': '#1B1E1D', 'actorBorder': '#323534', 'actorTextColor': '#F6F6F6', 'actorLineColor': '#323534', 'signalColor': '#60917E', 'signalTextColor': '#F6F6F6'}}}%%
sequenceDiagram
    participant Backend as Your Server
    participant Frontend as Your Frontend
    participant Component as Live View Component
    participant API as Deck API

    Backend->>API: POST /v2/tasks/{task_id}/run
    API-->>Backend: task run id
    Backend->>API: POST /v2/token
    API-->>Backend: session token
    Backend-->>Frontend: pass token + run id
    Frontend->>Component: Mount <DeckLiveViewComponent />
    Component->>API: Attach to the run
    API-->>Component: Status, screencast, interactions
    Component-->>Frontend: onSuccess({ output })
```

1. **Your server** starts a task run, workflow run, or session, and creates a session token via `POST /v2/token`
2. **Your frontend** mounts `<DeckLiveViewComponent />` with the token and the ID
3. **The component** attaches to it and renders the live browser (hosted at `live.components.deck.co`)
4. **If the run needs input**, the component collects it and submits it
5. **On completion**, the component returns the run's output via `onSuccess`

## Restrictions

* Attaches to an existing task run, workflow run, or session. It never starts one.
* Exactly one of `taskRunId`, `workflowRunId`, or `sessionId`. The component attaches to one thing.
* Closing the component never cancels the run. To stop it, [cancel the task run](/api-reference/task-runs/cancel-a-task-run) or [cancel the workflow run](/api-reference/workflow-runs/cancel-a-workflow-run) from your server.

## Installation

```bash theme={null}
npm install @deckco/live-view
```

```tsx theme={null}
import { DeckLiveViewComponent } from '@deckco/live-view';
```

React 18+ is a peer dependency.

## Quick example

Start a run and create a session token on your server:

<CodeGroup>
  ```typescript Node theme={null}
  const headers = {
    'Authorization': `Bearer ${process.env.DECK_SECRET_KEY}`,
    'Content-Type': 'application/json',
  };

  const run = await fetch('https://api.deck.co/v2/tasks/task_x9y8z7.../run', {
    method: 'POST',
    headers,
    body: JSON.stringify({ credential_id: 'cred_abc123' }),
  });
  const { id: taskRunId } = await run.json();

  const tok = await fetch('https://api.deck.co/v2/token', { method: 'POST', headers });
  const { token } = await tok.json();
  ```

  ```bash curl theme={null}
  curl -X POST https://api.deck.co/v2/tasks/task_x9y8z7.../run \
    -H "Authorization: Bearer $DECK_SECRET_KEY" \
    -H "Content-Type: application/json" \
    -d '{"credential_id": "cred_abc123"}'

  curl -X POST https://api.deck.co/v2/token \
    -H "Authorization: Bearer $DECK_SECRET_KEY"
  ```

  ```python Python theme={null}
  import os, requests

  headers = {"Authorization": f"Bearer {os.environ['DECK_SECRET_KEY']}"}

  run = requests.post(
      "https://api.deck.co/v2/tasks/task_x9y8z7.../run",
      headers=headers,
      json={"credential_id": "cred_abc123"},
  )
  task_run_id = run.json()["id"]

  token = requests.post("https://api.deck.co/v2/token", headers=headers).json()["token"]
  ```
</CodeGroup>

Mount the component in your React app:

```tsx Client theme={null}
<DeckLiveViewComponent
  token={token}
  taskRunId={taskRunId}
  externalId="user_12345"
  onSuccess={({ result, output }) => {
    console.log('Run completed:', result, output);
  }}
  onError={({ type, code, message }) => {
    console.error('Run failed:', code, message);
  }}
  onClose={() => {
    // User dismissed the component.
  }}
/>
```

The run doesn't have to be fresh. Any run your organization owns works, including one started by a [trigger](/concepts/triggers) or one that has already finished. See [Attaching at any point](#attaching-at-any-point).

## Session token

The component authenticates with a session token (`tk_`), the same kind the [Auth Component](/components/auth#session-token) uses, created with `POST /v2/token`. The same [token lifecycle](/components/auth#token-lifecycle) applies. The component polls the run while it's attached, which keeps the token alive up to its 24-hour limit.

Never pass a secret key (`sk_`) to the browser. The component rejects anything that isn't a `tk_` token at mount, before it loads the iframe or sends a request.

## What to attach to

Pass exactly one. Passing none, or more than one, fails at mount.

| Prop | ID prefix | What the end user sees |
| - | - | - |
| `taskRunId` | `trun_` | One task run. |
| `workflowRunId` | `wrun_` | A workflow run, following whichever step is executing. See [Workflow runs](#workflow-runs). |
| `sessionId` | `sess_` | The browser itself, following whichever of its task runs is live. See [Session mode](#session-mode). |

### Attaching at any point

The component reads the run's current state on attach, so it works whenever it mounts:

| Status at attach | What the component does |
| - | - |
| `queued` | Shows a status view until the run starts. |
| `running` | Shows the run, live or status per `taskRunView`. |
| `interaction_required` | Shows the pending prompt right away and fires `onInteractionRequired`. |
| `review_required` | Shows that the run is waiting for approval. Approval happens outside the component. |
| `canceling` | Shows that the run is stopping. |
| `completed`, `failed`, `canceled` | Fires `onSuccess`, `onError`, or `onCancel` right away and shows the result. |

Attaching is repeatable. Close the component, create a new token, and mount it again later (on the same device or another) and it picks up wherever the run is. A pending interaction keeps its original [5-minute window](/guides/interactions), so an end user who opens the component late has whatever time is left.

### Session mode

A [session](/concepts/sessions) is the browser the runs execute in, not a run itself. Attaching to one streams that browser and follows whichever of its task runs is currently live, so an interaction raised by any of them is answerable in the component.

The component stays attached until the session itself ends. A run in it finishing doesn't end it. `onSuccess` fires when the session completes, and `onError` fires with the last run's error if it fails. A run that fails inside the session doesn't fire `onError`, and `onCancel` never fires. A session that [times out](/concepts/sessions#timeout) after 10 minutes with no task running completes, which fires `onSuccess`.

A session has no result of its own, so `result`, `output`, and `runtimeMs` are all `null` in [`onSuccess`](#onsuccess). Read per-run output from the API instead.

## Component props

| Prop | Type | Description |
| - | - | - |
| `token` | `string` | **Required.** Session token from `POST /v2/token`. |
| `taskRunId` | `string` | The task run to attach to. See [What to attach to](#what-to-attach-to). |
| `workflowRunId` | `string` | The workflow run to attach to. Can't be combined with `taskRunId`. |
| `sessionId` | `string` | The session to attach to. Can't be combined with either run prop. |
| `taskRunView` | `"status" \| "live"` | How the run is displayed while executing. Defaults to `"live"`. Falls back to `"status"` when there's no browser to show, such as while the run is queued. |
| `interactions` | `"inline" \| "external"` | Whether the component collects interaction input. Defaults to `"inline"`. See [Interactions](#interactions). |
| `closeAfterStep` | `string` | Workflow runs only. The step the end user waits for, or `"*"` for the whole run. Defaults to `"*"`. See [Workflow runs](#workflow-runs). |
| `showStepProgress` | `boolean` | Workflow runs only. Shows "Step 2 of 3" on the status view. Defaults to `false`. |
| `externalId` | `string` | Your user ID. Echoed back in every callback except `onError` and `onClose`. Not persisted on the run. |
| `appearance` | `AppearanceConfig` | Full theming control. See [Appearance](#appearance). |
| `language` | `"en-US" \| "fr-CA"` | Language for component-rendered copy. Defaults to `"en-US"`. Strings returned by the source are never translated. |
| `onSuccess` | `(data) => void` | **Required.** Called when the run completes. See [`onSuccess`](#onsuccess). |
| `onStatusChange` | `(data) => void` | Called on attach and on every status transition. |
| `onInteractionRequired` | `(data) => void` | Called when the run needs input. See [Interactions](#interactions). |
| `onCancel` | `(data) => void` | Called when the run is canceled. |
| `onError` | `(data) => void` | Called on failure, or when the component can't attach. See [Error handling](#error-handling). |
| `onClose` | `() => void` | Called when the user dismisses the component. Its presence also shows the X unless `showCloseButton` overrides it. |
| `showCloseButton` | `boolean` | Show or hide the persistent close (X) icon, independently of `onClose`. Defaults to `onClose != null`. |

`closeAfterStep` and `showStepProgress` apply to workflow runs. Passing either without `workflowRunId` fails at mount rather than being silently ignored.

The component does not own its lifecycle. Your app does. When `onClose` fires, you decide what happens.

## Callbacks

`onStatusChange`, `onInteractionRequired`, `onSuccess`, and `onCancel` carry the run's identity, so you can correlate with your own records and with Deck-side logs:

| Field | Description |
| - | - |
| `taskRunId` | The task run, or `null` when attached to a workflow run or session. |
| `workflowRunId` | The workflow run, or `null` otherwise. |
| `sessionId` | The session in play, once resolved. |
| `externalId` | Whatever you passed in. |

### `onSuccess`

Called when the run reaches `completed`.

```tsx theme={null}
onSuccess={({ result, output, runtimeMs, taskRunId }) => {
  if (result === 'failure') {
    showRetry();
  } else {
    save(output);
  }
}}
```

| Field | Type | Description |
| - | - | - |
| `result` | `"success" \| "failure" \| "unknown" \| null` | The run's own result. `null` for a workflow run and a session. |
| `output` | `Record<string, unknown> \| null` | The run's output. `null` for a workflow run and a session. |
| `runtimeMs` | `number \| null` | How long the run took. `null` for a workflow run and a session. |

<Warning>
  A run can complete with `result: "failure"`: the task finished, and its own logic reported failure without erroring. Check `result`; don't treat `onSuccess` alone as success.
</Warning>

For a workflow run, read per-step output from `GET /v2/workflow-runs/{id}`.

### `onStatusChange`

Called once on attach with the run's current status, then on every transition: `queued`, `running`, `interaction_required`, `review_required`, `canceling`, `completed`, `failed`, `canceled`. Terminal statuses also fire `onSuccess`, `onError`, or `onCancel`.

### `onCancel`

Called when the run reaches `canceled`, canceled out of band through the API or the [Console](https://console.deck.co). It does not fire when the end user dismisses the component.

### `onError`

```typescript theme={null}
onError: (data: {
  type: string;
  code: string;
  message: string;
}) => void;
```

`type` is the error category. `code` is the machine-readable identifier. `message` is human-readable and may change. Switch on `type` and `code`, not `message`. See [Error handling](#error-handling).

### `onClose`

Called when the end user dismisses the component: the close (X) icon, the Escape key, or the Close button on a result screen.

## Interactions

When a run needs something only your end user can supply (an MFA code, a security question, a choice of account), it raises an [interaction](/guides/interactions).

| Value | Behavior |
| - | - |
| `"inline"` | Default. The component renders the prompt and submits the answer. `onInteractionRequired` still fires so you can react. |
| `"external"` | The component shows the run but renders no form. You collect the answer and [submit it through the API](/guides/interactions) yourself. |

Use `"external"` when the person watching must not be the one answering. For example, a support agent following a customer's run should not be typing that customer's MFA code.

`onInteractionRequired` carries the identity fields plus the run's [`interaction`](/guides/interactions) object (`type`, `message`, `fields`). For a workflow run or session, `taskRunId` is the run that raised it, which is the run to submit the answer to.

A rejected answer isn't an error. The component shows the message under the prompt and the end user retries in place.

## Workflow runs

Pass `workflowRunId` to follow a whole [workflow](/guides/workflows) run across its steps. A common case is picking up after the [Auth Component](/components/auth#after-the-component-closes) closes: a later step pauses for MFA, and you mount the Live View Component on the same workflow run so the end user can answer it.

```tsx theme={null}
<DeckLiveViewComponent
  token={token}
  workflowRunId="wrun_abc123"
  closeAfterStep="fetch_statements"
  showStepProgress
  onSuccess={({ workflowRunId }) => {
    // fetch_statements finished. Later steps keep running.
  }}
  onError={handleError}
/>
```

`closeAfterStep` uses the same step names as the Auth Component, but defaults to `"*"` (the whole run), since this component usually attaches partway through.

| Outcome | Callback |
| - | - |
| The close step finishes, or is skipped | `onSuccess` |
| A step at or before the close step fails | `onError` with that step's error |
| A step after the close step fails | No callback. Watch [events](/events/events-reference). |
| The run is canceled | `onCancel` |

* **Statuses.** `onStatusChange` reports task run statuses, not the workflow run's own. A `paused` workflow reports the paused step's `interaction_required` or `review_required`.
* **Interactions.** Any step up to the close step can prompt the end user, inline or `external`.
* **Live view.** `taskRunView: "live"` follows whichever step is running.

## Appearance

The component accepts the same [`appearance`](/components/auth#appearance) object as the Auth Component: `theme`, `variables`, and `layout`.

```tsx theme={null}
<DeckLiveViewComponent
  token={token}
  taskRunId={taskRunId}
  appearance={{
    theme: 'dark',
    variables: { colorPrimary: '#5b5bd6', borderRadius: 8 },
    layout: { screencastFit: 'contain' },
  }}
  onSuccess={handleSuccess}
/>
```

One key is specific to this component:

| Key | Type | Description |
| - | - | - |
| `layout.screencastFit` | `"contain" \| "cover"` | How the browser view fits its frame. `"contain"` (default) fits the whole viewport; `"cover"` fills the frame and crops. `taskRunView: "live"` only. |

In React, the component renders in a fixed 480px-high frame, and taller content scrolls inside it.

## Error handling

All errors surface through `onError` with a uniform `{ type, code, message }` shape.

### Validation errors

Raised by the component before any network request, from checking the token and the prop combination. These checks don't run when you [embed by URL](#embed-by-url): an invalid token is only logged to the browser console, and if more than one ID is passed, the component uses `workflowRunId`, then `sessionId`, then `taskRunId`.

| Type | Code | Description |
| - | - | - |
| `request` | `token_invalid` | Token is missing, malformed, or not a session token (`tk_`). Also fires if the API later rejects the token as expired. |
| `request` | `task_run_id_required` | No attach target was passed, or `taskRunId` isn't a `trun_` ID. |
| `request` | `workflow_run_id_invalid` | `workflowRunId` isn't a `wrun_` ID, or was combined with `taskRunId`. |
| `request` | `workflow_run_id_required` | `closeAfterStep` or `showStepProgress` was passed without a `workflowRunId`. |
| `request` | `session_id_invalid` | `sessionId` isn't a `sess_` ID, or was combined with another attach target. |

### Runtime errors

| Type | Code | Description |
| - | - | - |
| `request` | `task_run_not_found` | The task run doesn't exist, or this token can't see it. |
| `request` | `workflow_run_not_found` | The workflow run doesn't exist, or this token can't see it. |
| `request` | `session_not_found` | The session doesn't exist, or this token can't see it. |
| `request` | `workflow_step_not_found` | `closeAfterStep` names a step the workflow doesn't have. The message lists the real step names. |
| `api` | `stream_unavailable` | The component lost contact with the API after repeated retries. |

When the run fails, `onError` receives the run's own error unchanged, the same `type`, `code`, and `message` that `GET /v2/task-runs/{id}` returns (for example `auth_invalid` or `interaction_timeout`). A run that failed without an error reports `task_failed`.

After any error, the component stops following the run. It doesn't cancel the run or end the session. Short network drops are retried inside the component and never reach `onError`. To follow the run again, create a new token and remount.

## Embed by URL

For clients that can't run React, load the component's URL in a `WebView` (for example, in a React Native app) or an `<iframe>` and pass configuration as query parameters:

```bash theme={null}
https://live.components.deck.co/?token=tk_abc123&taskRunId=trun_abc123&externalId=user_12345
```

Each parameter does what its [prop](#component-props) does.

| Parameter | Maps to prop | Notes |
| - | - | - |
| `token` | `token` | **Required.** |
| `taskRunId` | `taskRunId` | Pass exactly one of `taskRunId`, `workflowRunId`, or `sessionId`. |
| `workflowRunId` | `workflowRunId` | |
| `sessionId` | `sessionId` | |
| `closeAfterStep` | `closeAfterStep` | A step name, or `*` for the whole run. |
| `showStepProgress` | `showStepProgress` | `true` shows step progress. Anything else leaves it off. |
| `externalId` | `externalId` | Echoed back in results. |
| `taskRunView` | `taskRunView` | `live` (default) or `status`. |
| `interactions` | `interactions` | `inline` (default) or `external`. |
| `appearance` | `appearance` | URL-encoded JSON. Malformed JSON is ignored. |
| `theme` | `appearance.theme` | Shorthand for `appearance={{ theme }}`. Ignored if `appearance` is also provided. |
| `language` | `language` | `en-US` (default) or `fr-CA`. |
| `showCloseButton` | `showCloseButton` | `true` renders the close (X) icon and enables Escape-to-close. Defaults to `false` when omitted. |

### Configuring via postMessage

Instead of URL parameters, load the bare origin (`https://live.components.deck.co/`), wait for the `READY` message, then post an `INIT` message with the full configuration, the same handshake as the [Auth Component](/components/auth#configuring-via-postmessage):

```js theme={null}
iframe.contentWindow.postMessage(
  {
    type: 'INIT',
    payload: {
      token: 'tk_abc123',
      taskRunId: 'trun_abc123',
      // Send the whole shape; use null for anything you're not setting.
      workflowRunId: null,
      sessionId: null,
      closeAfterStep: null,
      showStepProgress: false,
      externalId: null,
      taskRunView: 'live',
      interactions: 'inline',
      appearance: null,
      language: 'en-US',
      showCloseButton: false,
    },
  },
  'https://live.components.deck.co',
);
```

<Warning>
  Pick one channel. Configure via URL parameters **or** via `postMessage`, not both.
</Warning>

### Receiving results

Without React callbacks, the component delivers results as `{ type, payload }` messages, through `window.ReactNativeWebView.postMessage` in a React Native `WebView` or `window.postMessage` to the parent of an `<iframe>`. Switch on `type` and ignore any you don't handle:

| `type` | Payload | Meaning |
| - | - | - |
| `READY` | None | Component loaded. |
| `STATUS_CHANGED` | The [`onStatusChange`](#onstatuschange) payload | Status on attach and on each transition. |
| `INTERACTION_REQUIRED` | The [`onInteractionRequired`](#interactions) payload | The run needs input. |
| `SUCCESS` | The [`onSuccess`](#onsuccess) payload | The run completed. |
| `CANCELED` | The [`onCancel`](#oncancel) payload | The run was canceled. |
| `ERROR` | `{ type, code, message }` | Failure. See [Error handling](#error-handling). |
| `CLOSE` | None | User dismissed the component. |
| `RESIZE` | `{ height }` | Content height, for sizing the `WebView` or `<iframe>`. |


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