npm package: @onlive.ai/flow-client
Description: > TypeScript client for interacting with multi-step Onlive flows. Provides a type-safe, promise-based API with runtime validation and built-in event tracking.
Latest version: 0.1.234

# @onlive.ai/flow-client

> TypeScript client for interacting with multi-step Onlive flows. Provides a type-safe, promise-based API with runtime validation and built-in event tracking.

[![npm version](https://img.shields.io/npm/v/@onlive.ai/flow-client)](https://www.npmjs.com/package/@onlive.ai/flow-client)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue?logo=typescript)](https://www.typescriptlang.org/)
[![Lit](https://img.shields.io/badge/Lit-3.x-purple?logo=lit)](https://lit.dev/)
[![License: Proprietary](https://img.shields.io/badge/License-Proprietary-red.svg)](https://onlive.site)

## Overview

`@onlive.ai/flow-client` is the headless data layer behind [Onlive interactive flows](https://dev-docs.onlive.site/docs/onlive-flows/overview). It handles API communication, step navigation, field submission, and analytics tracking — without any rendering logic.

Use this package directly when you want to build a fully custom flow UI. If you need ready-made web components, use [`@onlive.ai/flow`](https://www.npmjs.com/package/@onlive.ai/flow), which consumes this client internally.

## Installation

**Prerequisites**: Node.js 18+

```bash
# pnpm (recommended)
pnpm add @onlive.ai/flow-client

# npm
npm install @onlive.ai/flow-client

# yarn
yarn add @onlive.ai/flow-client
```

## Quick Start

```typescript
import { FlowClient } from "@onlive.ai/flow-client";

const client = new FlowClient({
  baseUrl: "https://flow-api.onlive.site",
  flowId: "0123456789abcdef01234567",
  organizationId: "550e8400-e29b-41d4-a716-446655440000",
  lang: "en",
});

client.on("track", (eventType, eventData) => {
  console.log("Track event:", eventType, eventData);
});

const context = await client.firstStep();
console.log(context.step.title);
```

## Package Structure

```
packages/flow-client/
├── client/
│   ├── client.service.ts       # FlowClient class implementation
│   ├── client.types.ts         # Zod schemas and inferred TypeScript types
│   └── client.service.spec.ts  # Unit tests
├── tracking/
│   ├── tracking.service.ts     # Tracking service implementation
│   └── tracking.types.ts       # Tracking-related types and schemas
├── flow.types.ts               # Flow-specific type definitions
├── index.ts                    # Package entry point and exports
└── schema-generator.ts         # JSON schema generation utilities
```

### Exports

```typescript
// Client
export { FlowClient } from "@onlive.ai/flow-client";

// Types
export type { ClientOptions, GetStepOptions, GetStepTrigger } from "@onlive.ai/flow-client";
export type { FlowContext, Step, Flow, Action }               from "@onlive.ai/flow-client";
export type { TrackingOptions }                               from "@onlive.ai/flow-client";
export type { EventData, EventName }                          from "@onlive.ai/flow-client";
```

### Build Formats

| Format     | File          | Use Case                                    |
|------------|---------------|---------------------------------------------|
| ESM        | `index.js`    | Modern bundlers (Vite, Webpack, Rollup)     |
| CommonJS   | `index.cjs`   | Node.js environments                        |
| TypeScript | `index.d.ts`  | TypeScript type definitions                 |

## API Reference

### `FlowClient`

The main class. Instantiate it once per flow session and call its methods to navigate steps.

All constructor options are validated at runtime using [Zod](https://zod.dev/).

#### Constructor

```typescript
new FlowClient(options: ClientOptions)
```

#### `ClientOptions`

| Property      | Type              | Required | Default | Description |
|---------------|-------------------|----------|---------|-------------|
| `baseUrl`     | `string`          | Yes      | —       | Base URL of the Flow API. Must be a valid URL. |
| `flowId`      | `string`          | Yes      | —       | Flow identifier. Must be exactly 24 characters. |
| `organizationId` | `string`       | Yes      | —       | Organization UUID. |
| `lang`        | `string`          | Yes      | —       | BCP 47 language code, 2 characters (e.g. `"en"`, `"es"`). |
| `tracking`    | `TrackingOptions` | No       | —       | Analytics tracking configuration. See [TrackingOptions](#trackingoptions). |
| `devMode`     | `boolean`         | No       | `false` | Enables development mode logging. |

```typescript
import { FlowClient, type ClientOptions } from "@onlive.ai/flow-client";

const client = new FlowClient({
  baseUrl: "https://flow-api.onlive.site",
  flowId: "0123456789abcdef01234567",
  organizationId: "550e8400-e29b-41d4-a716-446655440000",
  lang: "en",
  tracking: {
    enabled: true,
    apiUrl: "https://srvless.onlive.site/tracking",
    emitters: {
      windowEvent: true,
      dataLayer: false,
    },
  },
});
```

---

### Methods

#### `firstStep(options?: GetStepOptions): Promise<FlowContext>`

Initiates the flow and returns the first step.

Sends a `POST` request to `flow/{flowId}/first-step`.

```typescript
const context = await client.firstStep({
  state: { source: "homepage" },
  fields: { prefill_email: "user@example.com" },
});
```

---

#### `getStep(stepId: string, options?: GetStepOptions): Promise<FlowContext>`

Retrieves a specific step by its identifier.

Sends a `POST` request to `flow/{flowId}/steps/{stepId}`.

```typescript
const context = await client.getStep("64abc123def456789012abc3", {
  state: { retryCount: 1 },
});
```

---

#### `nextStep(trigger: GetStepTrigger, options?: GetStepOptions): Promise<FlowContext>`

Advances the flow to the next step based on the user's action.

Sends a `POST` request to `flow/{flowId}/steps/{currentStepId}/action/{actionId}`.

```typescript
const context = await client.nextStep(
  {
    currentStepId: "64abc123def456789012abc3",
    actionId: "submit",
  },
  {
    fields: { email: "user@example.com", phone: "+34600000000" },
  },
);
```

---

#### `on(type: EventType, listener: EventListener): void`

Registers a listener for client events. Currently supports the `"track"` event type, fired whenever the client emits an analytics event.

```typescript
client.on("track", (eventType, eventData) => {
  // eventType: EventName
  // eventData: EventData
  analytics.push({ event: eventType, ...eventData });
});
```

---

### Input Types

#### `GetStepOptions`

Optional payload accepted by `firstStep()`, `getStep()`, and `nextStep()`.

| Property | Type                   | Description |
|----------|------------------------|-------------|
| `state`  | `Record<string, any>`  | Arbitrary state data to pass to the flow engine. |
| `fields` | `Record<string, any>`  | Field values to submit or pre-fill in the step. |

#### `GetStepTrigger`

Required first argument for `nextStep()`.

| Property        | Type     | Description |
|-----------------|----------|-------------|
| `currentStepId` | `string` | Identifier of the currently active step. Must be non-empty. |
| `actionId`      | `string` | Identifier of the action the user triggered. Must be non-empty. |

---

### Response Types

#### `FlowContext`

Returned by all step methods.

| Property | Type               | Description |
|----------|--------------------|-------------|
| `status` | `"ok" \| "error"`  | Result status of the flow operation. |
| `step`   | `Step`             | The current step to render. |
| `flow`   | `Flow`             | Flow configuration and stage metadata. |

#### `Step`

| Property             | Type                                                              | Description |
|----------------------|-------------------------------------------------------------------|-------------|
| `id`                 | `string`                                                          | Unique step identifier. |
| `number`             | `number`                                                          | Optional sequential position in the flow. |
| `title`              | `string`                                                          | Optional display title. |
| `description`        | `string`                                                          | Optional contextual description. |
| `stageId`            | `string`                                                          | Optional identifier of the associated flow stage. |
| `state`              | `Record<string, any>`                                             | Optional state data for the step. |
| `fields`             | `Field[]`                                                         | Optional array of form fields to render. |
| `actions`            | `Action[]`                                                        | Optional array of available actions (buttons). |
| `result.postActions` | `PostAction[]`                                                    | Optional post-actions to execute after step completion (e.g. `redirect`). |
| `result.tracking`    | `StepResultTracking \| StepResultTracking[]`                      | Optional tracking payload(s) for this step result. |
| `error.code`         | `ErrorCode`                                                       | Error code when `status` is `"error"`. |
| `error.message`      | `string`                                                          | Optional human-readable error message. |
| `error.icon`         | `string`                                                          | Optional icon name to display with the error. |
| `error.position`     | `"top" \| "bottom"`                                               | Optional position of the error message. |
| `error.fields`       | `Array<{ name: string; type: string; value: any; error: string }>` | Optional per-field validation errors. |
| `isFinal`            | `boolean`                                                         | When `true`, this is the last step in the flow. |

#### `Flow`

| Property         | Type                                   | Description |
|------------------|----------------------------------------|-------------|
| `id`             | `string`                               | Flow identifier. Exactly 24 characters. |
| `organizationId` | `string`                               | UUID of the owning organization. |
| `updatedAt`      | `string`                               | ISO 8601 datetime of the last update. |
| `stages`         | `Array<{ id: string; name: string }>` | Ordered array of flow stages. |

#### `Action`

Actions describe buttons or interactive controls within a step.

| Property          | Type                                                                              | Required | Description |
|-------------------|-----------------------------------------------------------------------------------|----------|-------------|
| `id`              | `string`                                                                          | Yes      | Unique action identifier. |
| `type`            | `"submit" \| "navigate" \| "link" \| "back" \| "dispatcher" \| "script"`        | Yes      | Determines the behavior when triggered. |
| `label`           | `string`                                                                          | Yes      | Display text for the button. |
| `variant`         | `"primary" \| "secondary" \| "success" \| "danger" \| "warning" \| "info"`      | Yes      | Visual style variant. |
| `position`        | `"top" \| "bottom"`                                                               | No       | Position of the button relative to step content. |
| `url`             | `string`                                                                          | No       | Target URL for `link` type actions. Must be a valid URL. |
| `target`          | `"_self" \| "_blank" \| "_parent" \| "_top"`                                     | No       | Link target attribute. |
| `loadingBehavior` | `"none" \| "spinner" \| "disabled"`                                               | No       | How the button behaves while loading. |
| `prefix`          | `AffixSlot`                                                                       | No       | Icon or text to display before the label. |
| `suffix`          | `AffixSlot`                                                                       | No       | Icon or text to display after the label. |
| `scripts`         | `ActionScript[]`                                                                  | No       | Scripts to execute when the action is triggered. |
| `events`          | `ActionEvent[]`                                                                   | No       | Custom events to emit when the action is triggered. |

#### `ActionScript`

| Property   | Type                       | Required | Description |
|------------|----------------------------|----------|-------------|
| `src`      | `string`                   | Yes      | URL of the script to load. Must be a valid URL. |
| `name`     | `string`                   | Yes      | Identifier for the script. |
| `function` | `string`                   | Yes      | Name of the function to invoke from the script. |
| `preload`  | `boolean`                  | No       | Whether to preload the script before execution. |
| `params`   | `Array<Record<string, any>>` | No     | Parameter objects passed to the script function. |

#### `ActionEvent`

| Property | Type                                                                                                           | Required | Description |
|----------|----------------------------------------------------------------------------------------------------------------|----------|-------------|
| `name`   | `"organization_call" \| "close_panel" \| "retry_first_step" \| "retry_next_step" \| "organization_chat"`      | Yes      | Event name to dispatch. |
| `params` | `Record<string, any>`                                                                                          | No       | Additional parameters for the event. |

#### `AffixSlot`

| Property | Type     | Description |
|----------|----------|-------------|
| `text`   | `string` | Text content to display. |
| `icon`   | `string` | Icon name to display. |

---

### Error Handling

When `status` is `"error"`, inspect `step.error.code` to identify the failure reason.

#### `ErrorCode`

| Code                              | Description |
|-----------------------------------|-------------|
| `INVALID_DATA`                    | The submitted data failed validation. |
| `INVALID_ACTION`                  | The requested action does not exist or is not valid for the current step. |
| `STEP_NOT_FOUND`                  | The requested step does not exist. |
| `UNKNOWN_ERROR`                   | An unexpected error occurred. |
| `APPOINTMENT_INVALID_DATA`        | Calendar appointment data is invalid. |
| `APPOINTMENT_BUSY_SLOT`           | The selected appointment slot is already taken. |
| `APPOINTMENT_OUT_OF_DATE`         | The selected appointment slot is in the past or no longer available. |
| `APPOINTMENT_UNKNOWN_ERROR`       | An unexpected error occurred during appointment processing. |
| `LEAD_INVALID_DATA`               | Lead data failed validation. |
| `LEAD_UNKNOWN_ERROR`              | An unexpected error occurred during lead processing. |
| `CONTENT_INVALID_DATA`            | Content data failed validation. |
| `CONTENT_UNKNOWN_ERROR`           | An unexpected error occurred during content processing. |
| `CONTACT_INVALID_DATA`            | Contact data failed validation. |
| `CONTACT_UNKNOWN_ERROR`           | An unexpected error occurred during contact processing. |
| `LOST_OPPORTUNITY_INVALID_DATA`   | Lost opportunity data failed validation. |
| `LOST_OPPORTUNITY_UNKNOWN_ERROR`  | An unexpected error occurred during lost opportunity processing. |

```typescript
const context = await client.nextStep({ currentStepId, actionId });

if (context.status === "error" && context.step.error) {
  switch (context.step.error.code) {
    case "APPOINTMENT_BUSY_SLOT":
      // Prompt user to select a different time slot
      break;
    case "INVALID_DATA":
      // Highlight invalid fields
      context.step.error.fields?.forEach(({ name, error }) => {
        console.warn(`Field "${name}": ${error}`);
      });
      break;
    default:
      console.error("Flow error:", context.step.error.message);
  }
}
```

---

### `TrackingOptions`

| Property                    | Type               | Default | Description |
|-----------------------------|--------------------|---------|-------------|
| `enabled`                   | `boolean`          | `false` | Whether event tracking is active. |
| `apiUrl`                    | `string`           | —       | URL of the tracking API endpoint. |
| `data`                      | `Partial<EventData>` | —     | Base data merged into every tracked event. |
| `removeExtraneousValues`    | `boolean`          | `true`  | Strips unknown fields from event payloads before sending. |
| `allowBotTraffic`           | `boolean`          | `false` | Whether to track requests identified as bot traffic. |
| `adapters`                  | `string[]`         | `[]`    | Tracking adapter identifiers to activate. |
| `emitters.windowEvent`      | `boolean`          | —       | Emits events as `onlive-analytics-event` window custom events. |
| `emitters.dataLayer`        | `boolean`          | —       | Pushes events to `window.dataLayer` (Google Tag Manager). |
| `emitters.windowPostMessage` | `boolean`         | —       | Sends events via `window.postMessage`. |

---

## Examples

### Complete Flow Navigation

```typescript
import { FlowClient } from "@onlive.ai/flow-client";

const client = new FlowClient({
  baseUrl: "https://flow-api.onlive.site",
  flowId: "0123456789abcdef01234567",
  organizationId: "550e8400-e29b-41d4-a716-446655440000",
  lang: "en",
  tracking: { enabled: true, apiUrl: "https://srvless.onlive.site/tracking" },
});

client.on("track", (eventType, eventData) => {
  console.log("Track:", eventType, eventData);
});

try {
  const { step } = await client.firstStep();

  if (step.isFinal) {
    console.log("Flow completed on the first step.");
  } else {
    const next = await client.nextStep(
      { currentStepId: step.id, actionId: "submit" },
      { fields: { email: "user@example.com" } },
    );

    console.log("Next step:", next.step.title);
  }
} catch (error) {
  console.error("Unexpected error:", error);
}
```

### Resuming a Specific Step

```typescript
const context = await client.getStep("64abc123def456789012abc3", {
  state: { referrer: "email-campaign" },
});

console.log("Step fields:", context.step.fields?.map((f) => f.name));
```

---

## Dependencies

| Package                      | Purpose |
|------------------------------|---------|
| `zod`                        | Runtime schema validation for constructor options and API responses |
| `@onlive.ai/tracker`       | Analytics event types and tracking infrastructure |
| `@onlive.ai/common-121`    | Shared utilities and types |
| `@onlive.ai/calendar`      | Calendar utilities for appointment field handling |

## Development

```bash
# Install dependencies
pnpm install

# Build the package (ESM + CJS)
pnpm run build:package

# Run unit tests
pnpm test

# Run tests with coverage
pnpm run test:coverage

# Remove generated build artifacts
pnpm run clean
```

## Related Packages

| Package | Description |
|---------|-------------|
| [`@onlive.ai/flow`](https://www.npmjs.com/package/@onlive.ai/flow) | Web components for rendering flows (uses this client internally) |
| [`@onlive.ai/calendar`](https://www.npmjs.com/package/@onlive.ai/calendar) | Calendar components for date/appointment fields |

## Documentation

- [Onlive Flow Overview](https://dev-docs.onlive.site/docs/onlive-flows/overview)
- [Custom Flow Implementation](https://dev-docs.onlive.site/docs/onlive-flows/custom-flow)
- [Field Types Reference](https://dev-docs.onlive.site/docs/onlive-flows/fields)

## License

Proprietary software — © Onlive.ai. All rights reserved.

For support, visit the [Onlive documentation](https://dev-docs.onlive.site/) or contact the development team.