> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Flow

Flow configuration is walkerOS's **"configuration as code"** approach. A single JSON file defines your entire event collection pipeline, making it portable, version-controlled, and deployable across environments.

Reference page

This page is the complete Flow configuration reference. If you are new to walkerOS, start with the [Quickstart](/preview/pr-749/docs/getting-started/quickstart.md) and the [GA4 ecommerce tutorial](/preview/pr-749/docs/getting-started/ga4-ecommerce.md), then come back here to look up specific options.

## Configuration structure[​](#configuration-structure "Direct link to Configuration structure")

A flow configuration uses the `Flow.Json` format with two required fields:

1. **`version`** - Schema version (`4`)
2. **`flows`** - Named flow configurations (each a `Flow`)

### Example[​](#example "Direct link to Example")

The examples on this page come from [`flow-complete.json`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.json), a tested ecommerce setup with three flows: `web` in the browser, `server` behind it, and `warehouse` reading the Pub/Sub topic into BigQuery. For a minimal first flow, start with the [Quickstart](/preview/pr-749/docs/getting-started/quickstart.md).

`/flows`

```json
{
  "web": {
    "config": { … },
    "collector": { … },
    "sources": { … },
    "transformers": { … },
    "destinations": { … }
  },
  "server": {
    "config": { … },
    "variables": { … },
    "collector": { … },
    "stores": { … },
    "sources": { … },
    "transformers": { … },
    "destinations": { … }
  },
  "warehouse": {
    "config": { … },
    "sources": { … },
    "destinations": { … }
  }
}
```

One file holds three flows: web in the browser, server behind it, warehouse reading the topic. [Guide chapter `tour`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#tour-one-file-one-event)

## Config syntax: `package:` vs `code:`[​](#config-syntax-package-vs-code "Direct link to config-syntax-package-vs-code")

When configuring sources and destinations, you'll see two different syntaxes depending on your operating mode.

### Bundled mode (JSON with CLI)[​](#bundled-mode-json-with-cli "Direct link to Bundled mode (JSON with CLI)")

Use `package:` with a string reference. The CLI downloads and bundles the npm package:

```json
"sources": {
    "browser": {
      "package": "@walkeros/web-source-browser",
      "config": { "settings": { "pageview": true } }
    }
}
```

### Integrated mode (TypeScript with startFlow)[​](#integrated-mode-typescript-with-startflow "Direct link to Integrated mode (TypeScript with startFlow)")

Use `code:` with a direct import reference:

```typescript
import { startFlow } from '@walkeros/collector';
import { sourceBrowser } from '@walkeros/web-source-browser';

const flow = await startFlow({
  sources: {
    browser: {
      code: sourceBrowser,
      config: { settings: { pageview: true } }
    }
  }
});
```

### Quick reference[​](#quick-reference "Direct link to Quick reference")

| Property   | Mode       | Value                      | When to Use                       |
| ---------- | ---------- | -------------------------- | --------------------------------- |
| `package:` | Bundled    | `"@walkeros/..."` (string) | CLI resolves and bundles from npm |
| `code:`    | Integrated | `sourceBrowser` (import)   | Direct code reference in your app |

Both achieve the same result. The difference is whether the CLI bundles the code for you (Bundled) or you import it directly (Integrated).

See [Operating Modes](/preview/pr-749/docs/getting-started/modes.md) for more details on choosing your approach.

## Flow configuration (Flow)[​](#flow-configuration-flow "Direct link to Flow configuration (Flow)")

Each flow in `flows` is a `Flow` that defines runtime behavior. Per-flow build/runtime metadata sits inside `config` (`Flow.Config`).

### Platform[​](#platform "Direct link to Platform")

Platform is set explicitly via `config.platform`:

`/flows/web/config/platform`: `"web"`. platform decides how a flow is built: a browser bundle or a server artifact. [Guide chapter `tour`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#tour-one-file-one-event)

It sits in the flow's `config` next to the other build and runtime metadata:

`/flows/web/config`

```json
{
  "platform": "web",
  "url": "$env.SITE_URL:https://www.example.com",
  "settings": {
    "windowCollector": "walkerCollector"
  },
  "observe": {
    "url": "$env.OBSERVE_URL:https://app.walkeros.io/observe",
    "binding": "$env.OBSERVE_BINDING:obs-web"
  },
  "bundle": {
    "packages": {
      "@walkeros/collector": {
        "version": "4.8.0"
      },
      "@walkeros/web-source-cmp-usercentrics": {
        "version": "4.8.0"
      },
      "@walkeros/web-source-session": {
        "version": "4.8.0"
      },
      "@walkeros/web-source-datalayer": {
        "version": "4.8.0"
      },
      "@walkeros/web-source-browser": {
        "version": "4.8.0"
      },
      "@walkeros/web-destination-gtag": {
        "version": "4.8.0"
      },
      "@walkeros/web-destination-api": {
        "version": "4.8.0"
      }
    },
    "env": {
      "ENVIRONMENT": "production"
    }
  }
}
```

config holds what the build needs: platform, url, settings, bundle, observe. [Guide chapter `tour`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#tour-one-file-one-event)

**Platform options:**

* `"platform": "server"` - Node.js server environment (HTTP endpoints, cloud functions)
* `"platform": "web"` - Browser environment (client-side tracking)

The CLI automatically applies platform-specific build defaults:

* **Web**: IIFE format, ES2020 target
* **Server**: ESM format, Node20 target

The bundle is written to stdout by default. Use `-o` to write to a file (e.g., `-o ./dist/walker.js` for web, `-o ./dist/bundle.mjs` for server).

### Bundle[​](#bundle "Direct link to Bundle")

Build-time configuration lives under `flows.<name>.config.bundle`. The `packages` field specifies npm packages to download and bundle, and `overrides` pins transitive dependency versions (npm `overrides` semantics):

```json
{
  "config": {
    "platform": "web",
    "bundle": {
      "packages": {
        "@walkeros/web-source-browser": {
          "version": "^4",
          "imports": ["sourceBrowser"]
        },
        "@walkeros/web-destination-api": {
          "imports": ["destinationAPI"]
        }
      },
      "overrides": {
        "@amplitude/analytics-types": "2.11.1"
      }
    }
  }
}
```

**`packages` properties:**

* **`version`** - An exact npm version, a semver range, or a tag. Defaults to `"latest"`. A major range such as `"^4"` picks up new minor and patch releases without jumping to the next major version. `walkeros validate` warns about packages that set neither `version` nor `path`, and `--strict` turns that warning into a failing exit code.
* **`imports`** - Array of named exports to import
* **`path`** - Local filesystem path (takes precedence over `version`)

**`overrides`** is a `Record<string, string>`: for each named package, the version to use for any transitive reference. Direct package specs always win over overrides; overrides only substitute transitive dependencies during resolution.

For development or custom packages, use `path` to reference a local directory:

```json
{
  "config": {
    "platform": "web",
    "bundle": {
      "packages": {
        "@my/custom-destination": {
          "path": "./my-destination",
          "imports": ["myDestination"]
        }
      }
    }
  }
}
```

See [Local Packages](/preview/pr-749/docs/apps/cli.md#local-packages) in the CLI documentation for more details.

### Step-level package versions[​](#step-level-package-versions "Direct link to Step-level package versions")

When configuring individual steps (sources, transformers, or destinations), the `package` field accepts an optional inline version suffix:

```json
{
  "sources": {
    "browser": {
      "package": "@walkeros/web-source-browser@^4",
      "config": { "settings": { "pageview": true } }
    }
  }
}
```

If the same package is pinned in `config.bundle.packages` to a different version, the bundle-level pin takes precedence and the inline version is ignored (with a build warning).

### Sources[​](#sources "Direct link to Sources")

Sources capture events from various inputs. Each source needs:

* **`package`** - The npm package (required for package-based sources, optional when using `code:`)
* **`config`** - Source-specific settings

```json
{
  "sources": {
    "http": {
      "package": "@walkeros/server-source-express",
      "config": {
        "settings": {
          "paths": ["/collect"],
          "cors": true
        }
      }
    }
  }
}
```

See [Sources documentation](/preview/pr-749/docs/sources.md) for all available options.

### Destinations[​](#destinations "Direct link to Destinations")

Destinations receive processed events and send them to analytics tools, databases, or APIs:

```json
{
  "destinations": {
    "bigquery": {
      "package": "@walkeros/server-destination-gcp",
      "config": {
        "settings": {
          "projectId": "my-project",
          "datasetId": "analytics",
          "tableId": "events"
        },
        "mapping": {
          "page": {
            "view": {
              "name": "page_view"
            }
          }
        }
      }
    },
    "console": {
      "package": "@walkeros/destination-demo",
      "config": {
        "settings": {
          "name": "Debug Logger"
        }
      }
    }
  }
}
```

**Configuration options:**

* **`settings`** - Destination-specific configuration (API keys, endpoints, etc.)
* **`mapping`** - Event transformation rules (see [Mapping documentation](/preview/pr-749/docs/mapping.md))
* **`consent`** - Required consent states
* **`policy`** - Processing rules

**Consent gating:**

Gate a destination on consent with `config.consent`. The destination initializes once one of the listed consent keys is granted; until then its events wait in a queue and are delivered after the grant, during the same run. A denial keeps them waiting:

```json
{
  "destinations": {
    "ga4": {
      "package": "@walkeros/web-destination-gtag",
      "config": {
        "consent": { "marketing": true },
        "settings": { "ga4": { "measurementId": "G-DEMO123456" } }
      }
    }
  }
}
```

`require` is a separate mechanism that waits for collector events, not for granted consent (see [Deferred activation with `require`](#deferred-activation-with-require)).

See [Destinations documentation](/preview/pr-749/docs/destinations.md) for all available options.

### Transformers[​](#transformers "Direct link to Transformers")

Transformers process events between sources and destinations. They validate, enrich, or redact events in the pipeline.

```json
{
  "transformers": {
    "fingerprint": {
      "package": "@walkeros/server-transformer-fingerprint",
      "config": {
        "settings": { "salt": "$env.FINGERPRINT_SALT" }
      }
    }
  }
}
```

**Configuration options:**

* **`package`** - The npm package (required for package-based steps, optional when using `code:`)
* **`import`** - Named export of `package` to use (optional; the package's default export otherwise)
* **`code`** - Inline code object `{ push, type?, init? }`, used instead of `package` (see [Inline Code](#inline-code-without-packages))
* **`config`** - Transformer-specific configuration
* **`env`** - Environment-specific settings
* **`before`** - Pre-transformer chain (runs before this transformer's push)
* **`next`** - Next transformer in chain (omit to end chain)
* **`variables`** - Transformer-level variable overrides

**Chaining transformers:**

Link transformers together using `next`:

```json
{
  "transformers": {
    "enrich": {
      "code": {
        "type": "enrich",
        "push": "$code:(event) => ({ event: { ...event, data: { ...event.data, enriched: true } } })"
      },
      "next": "fingerprint"
    },
    "fingerprint": {
      "package": "@walkeros/server-transformer-fingerprint"
    }
  }
}
```

**Explicit chain control with arrays:**

For explicit control over the order, use an array instead of a string. The array is the backbone of the chain: its entries run in order, and a member's own `next` runs right after that member before the array continues:

```json
{
  "sources": {
    "http": {
      "package": "@walkeros/server-source-express",
      "next": ["fingerprint", "enrich", "redact"]
    }
  },
  "transformers": {
    "fingerprint": { "package": "@walkeros/server-transformer-fingerprint" },
    "enrich": {
      "code": {
        "type": "enrich",
        "push": "$code:(event) => ({ event: { ...event, data: { ...event.data, enriched: true } } })"
      }
    },
    "redact": {
      "code": {
        "type": "redact",
        "push": "$code:(event) => ({ event: { ...event, user: { ...event.user, email: undefined } } })"
      }
    }
  }
}
```

| Syntax                              | Behavior                                                                           |
| ----------------------------------- | ---------------------------------------------------------------------------------- |
| `"next": "fingerprint"`             | Runs `fingerprint`, then follows each transformer's own `next`                     |
| `"next": ["fingerprint", "enrich"]` | Runs the array in order; a member's own `next` is inserted right after that member |

**Connecting to sources, the collector, and destinations:**

* **Source preprocessing**: Use `before` on a source for consent-exempt preprocessing (runs before the source's push)
* **Source chain**: Use `next` on a source to route its events through transformers before the collector
* **Collector chain**: Use `next` on the collector to run one chain for every event, after the collector completed it and before the destination fan-out
* **Destination chain**: Use `before` on a destination to run transformers for that destination only
* **Post-push chain**: Use `next` on a destination to process events after the destination push completes

The server flow of the complete example uses the source, collector and destination positions, shown step by step on the [Routing](/preview/pr-749/docs/getting-started/flow/routing.md#where-transformers-run) page.

All chain fields (`before` and `next`, including `collector.next`) accept the same route grammar: a transformer id, an array, or a route config (see [Conditional routing](/preview/pr-749/docs/getting-started/flow/routing.md#conditional-routing)).

See [Transformers documentation](/preview/pr-749/docs/transformers.md) for available transformers and custom transformer guide.

### Connection rules[​](#connection-rules "Direct link to Connection rules")

Sources, transformers, the collector and destinations connect through chain fields (`before` and `next`). The connection table, chain resolution, route operators and dropping events with `stop` are on the [Routing](/preview/pr-749/docs/getting-started/flow/routing.md) page.

### Deferred activation with `require`[​](#deferred-activation-with-require "Direct link to deferred-activation-with-require")

Sources and destinations support `require` to wait until specific collector events have fired:

* A **destination** with `require` is not initialized and receives no events until then. Its events are queued and delivered once `require` is met, unless the destination sets `queue: false`: then it receives only the events pushed after that.
* A **source** with `require` initializes and pushes events right away. `require` only holds back the collector events its `on()` handler receives (such as `consent` or `run`).

For the browser source, `require: ["consent"]` holds back the page view and element triggers such as `load`, `impression` or `visible` until the first consent choice, granted or denied. `click` and `submit` triggers are captured from the start.

```json
{
  "sources": {
    "browser": {
      "package": "@walkeros/web-source-browser",
      "config": { "require": ["consent"] }
    }
  },
  "destinations": {
    "api": {
      "package": "@walkeros/web-destination-api",
      "config": {
        "require": ["consent"],
        "settings": { "url": "https://collect.example.com/events" }
      }
    }
  }
}
```

For `require: ["consent"]` the collector needs a consent state: the `collector.consent` default or a `walker consent` command. Any state counts, granted or denied, so with a `consent` default set, `require` is met at startup. `require` does not check consent values; `config.consent` does.

### Inline Code (without packages)[​](#inline-code-without-packages "Direct link to Inline Code (without packages)")

For simple one-liner logic, define sources, transformers, or destinations inline without creating a package.

Use `code` object instead of `package`:

**Inline Transformer:**

```json
{
  "transformers": {
    "enrich": {
      "code": {
        "type": "enricher",
        "push": "$code:(event) => ({ event: { ...event, data: { ...event.data, timestamp: Date.now() } } })"
      },
      "config": {}
    }
  }
}
```

**Inline Destination:**

```json
{
  "destinations": {
    "logger": {
      "code": {
        "type": "console-logger",
        "push": "$code:(event, context) => { context.logger.info(event.name, event.data); }"
      },
      "config": {}
    }
  }
}
```

**Code object properties:**

* **`push`** - The push function with `$code:` prefix (required)
* **`type`** - Optional instance type identifier
* **`init`** - Optional init function with `$code:` prefix

**Rules:**

* Use `package` OR `code`, never both
* `config` stays separate (same as package-based)
* `$code:` prefix outputs raw JavaScript at bundle time

### Collector[​](#collector "Direct link to Collector")

The collector processes events from sources and routes them to destinations:

```json
{
  "collector": {
    "globals": {
      "environment": "production"
    },
    "consent": {
      "functional": true
    },
    "next": ["bot", "session"]
  }
}
```

**Options:**

* **`run`** - Whether to start the collector automatically (default: `true`)
* **`globals`** - Properties added to every event
* **`consent`** - Default consent state
* **`next`** - Collector chain: a `Route` that runs once per event, after the collector completed it and before the destination fan-out. Every destination receives what it produces; a `stop` here reaches no destination, and each `many` copy is delivered to the destinations on its own. Per-destination work stays on `destination.before`

See [Collector documentation](/preview/pr-749/docs/collector.md) for complete options.

### Web-specific options[​](#web-specific-options "Direct link to Web-specific options")

For browser bundles, you can configure the collector's window variable name via `config.settings`:

```json
{
  "config": {
    "platform": "web",
    "settings": {
      "windowCollector": "walkerCollector"
    }
  }
}
```

**Properties:**

* **`windowCollector`** - Global variable name for collector instance (default: `"walkerOS"`). The bundle assigns the collector to this one global, whether you build with `walkeros bundle` or deploy it as a hosted flow. Accepts a literal or a `$var`/`$env` reference; the resolved value must be a JavaScript identifier and not a reserved name: `elb`, `elbLayer`, `window`, `document`, `location`, `self`, `top`, `parent`, `frames`, `globalThis`, `__proto__`, `constructor`, `prototype`.

The `window.elb` function is owned by the browser source: set `elb` in the browser source's `config.settings` to change its global name (default: `"elb"`). If the deprecated `config.settings.windowElb` is set, the CLI forwards its value to the browser source's `settings.elb` and logs a warning; if that source already sets `elb`, `windowElb` is ignored.

## Multi-flow configuration[​](#multi-flow-configuration "Direct link to Multi-flow configuration")

One file holds several flows. The complete example splits by platform (`web`, `server`, `warehouse`, see [Example](#example)); the same works per environment, for example `development` and `production`.

Build specific flows using the CLI:

```bash
# Build the web flow
walkeros bundle flow.json --flow web

# Build the server flow
walkeros bundle flow.json --flow server

# Build all flows (--all needs an output directory, one subfolder per flow)
walkeros bundle flow.json --all -o dist/all
```

### Cross-flow references with `$flow`[​](#cross-flow-references-with-flow "Direct link to cross-flow-references-with-flow")

Flows in the same file can reference each other's `config` block via `$flow.<name>.<path>`. The most common case is linking a web flow's destination to a server flow's URL. In the complete example the web `collect` destination sends to the server flow:

`/flows/web/destinations/collect/config/settings/url`: `"$flow.server.url"`. $flow\.server.url points the browser at the server flow; it is whole-string only. [Guide chapter `references`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#references-values-from-outside-the-file)

Cross-flow refs read values from another flow's `config` block (`platform`, `url`, `settings.*`), keeping web and server flows in sync without duplication. The bundler resolves `$flow.server.url` to the server flow's `config.url` at build time. Validate resolves the reference the same way the bundle does: an unknown flow or a missing key is an error in both. An empty `config.url` fails the schema check in `bundle` and in `validate` without `--flow`; `validate --flow web` does not check the server flow and passes. An empty value under `settings` resolves to an empty string.

## Dynamic patterns[​](#dynamic-patterns "Direct link to Dynamic patterns")

Flow configurations support these dynamic patterns for reusable, environment-aware configs (see [Reference syntax](/preview/pr-749/docs/guides/reference-syntax.md) for every reference type):

### `$var.name` - Config Variables[​](#varname---config-variables "Direct link to varname---config-variables")

Reference variables defined in `variables` for shared values across your config:

`/variables`

```json
{
  "currency": "EUR",
  "gcp": {
    "project": "$env.GCP_PROJECT:demo-project",
    "dataset": "$env.BQ_DATASET:walkeros",
    "topic": "$env.PUBSUB_TOPIC:walkeros-events"
  }
}
```

Root variables are shared by all flows; a flow or a step can override them. [Guide chapter `references`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#references-values-from-outside-the-file)

`/flows/web/destinations/ga4/config/policy/data.currency/1/value`: `"$var.currency"`. A whole-string $var keeps the variable type. [Guide chapter `references`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#references-values-from-outside-the-file)

`/flows/warehouse/sources/pubsub/config/setup/deadLetterPolicy/deadLetterTopic`: `"projects/$var.gcp.project/topics/walkeros-dead-letter"`. Inside a longer string $var is replaced in place, here in a full topic name. [Guide chapter `references`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#references-values-from-outside-the-file)

Variables can be defined at three levels (higher specificity wins):

1. **Step level** (`variables` on a source, transformer, destination, or store) - Highest priority
2. **Flow level** (`flows.<name>.variables`) - Middle priority
3. **Root level** (top-level `variables`) - Lowest priority

### `$env.NAME` - Environment Variables[​](#envname---environment-variables "Direct link to envname---environment-variables")

Reference environment variables with optional defaults:

`/variables/gcp/project`: `"$env.GCP_PROJECT:demo-project"`. $env.NAME:default works out of the box and takes the environment value when set. [Guide chapter `references`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#references-values-from-outside-the-file)

`/flows/server/transformers/fingerprint/config/settings/salt`: `"$env.FINGERPRINT_SALT"`. $env without a default must be set: a secret salt never gets a public default. [Guide chapter `references`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#references-values-from-outside-the-file)

**Syntax:**

* `$env.GA4_ID` - Required. A web flow reads the value at bundle time, and the bundle fails if it is not set. A server flow reads `process.env` when the bundle runs; an unset variable resolves to `undefined`. Inside a longer string, it becomes the text `undefined`.
* `$env.GA4_ID:default` - Uses "default" if not set

Why only `$env` supports defaults

Environment variables are external and unpredictable - they might not be set in all environments. Config variables (`$var`) are explicitly defined, so a missing one indicates a configuration error.

### `$code:` - Inline JavaScript[​](#code---inline-javascript "Direct link to code---inline-javascript")

Embed JavaScript code directly in JSON config values:

```json
{
  "destinations": {
    "api": {
      "package": "@walkeros/web-destination-api",
      "config": {
        "mapping": {
          "product": {
            "view": {
              "data": {
                "map": {
                  "item_id": {
                    "fn": "$code:(event) => String(event.data.id).toUpperCase()"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

The `$code:` prefix is stripped during bundling, outputting raw JavaScript:

```typescript
// Bundler output
{
  item_id: {
    fn: (event) => String(event.data.id).toUpperCase()
  }
}
```

A `fn` receives the value being mapped (here the whole event, inside a `loop` the item) and returns the result, so it reads the field itself. A `fn` next to a `key` never runs: each value has one producer.

**Use cases:**

* **`fn:` callbacks** - Transform values in mapping
* **`condition:` predicates** - Conditional event processing
* **Custom logic** - Any inline function or expression

**Syntax notes:**

* Multi-line code: Use JSON escapes (`\n`, `\"`)
* Scope: Same as bundled code (access to imports and variables)
* Errors: esbuild rejects syntax errors at build time. The code must be plain JavaScript (TypeScript annotations fail the bundle) and is not type-checked.

## Type hierarchy[​](#type-hierarchy "Direct link to Type hierarchy")

walkerOS uses a clear type hierarchy:

```
┌─────────────────────────────────────────────────────────────────┐

│  Flow.Json (config file)                                        │

│  ├── version: 4                                                 │

│  ├── variables?: { currency: "EUR" }                            │

│  └── flows:                                                     │

│       └── default: Flow                                         │

└─────────────────────────────────────────────────────────────────┘

                              │

                              │ CLI resolves $var, $env, $flow, $contract, $secret

                              ▼

┌─────────────────────────────────────────────────────────────────┐

│  Flow (resolved flow)                                           │

│  ├── config: { platform, url?, settings?, bundle? }             │

│  ├── sources: { ... }                                           │

│  ├── transformers: { ... }                                      │

│  ├── destinations: { ... }                                      │

│  ├── stores: { ... }                                            │

│  └── collector: { next?, globals?, ... }                        │

└─────────────────────────────────────────────────────────────────┘

                              │

                              │ CLI bundles and transforms

                              ▼

┌─────────────────────────────────────────────────────────────────┐

│  Collector.InitConfig (runtime)                                 │

│  Passed to startFlow() at runtime                               │

└─────────────────────────────────────────────────────────────────┘
```

* **`Flow.Json`** - Root config file format for CLI
* **`Flow`** - Single flow configuration (with optional `config`, `sources`, `destinations`, etc.)
* **`Flow.Config`** - Per-flow config block (`platform`, `url`, `settings`, `bundle`, `observe`)
* **`Collector.InitConfig`** - Runtime type passed to `startFlow()`

## Complete example[​](#complete-example "Direct link to Complete example")

The canonical, tested flow is [`packages/cli/examples/flow-complete.json`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.json), shown in parts on this page and across the docs. Its [guide](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md) teaches it in 15 chapters. `@walkeros/cli/examples` exports the file and its feature manifest.

Every flow declares its steps in named maps. The web flow's sources, and the server flow's transformers and destinations:

`/flows/web/sources`

```json
{
  "usercentrics": { … },
  "session": { … },
  "dataLayer": { … },
  "browser": { … }
}
```

Sources turn whatever happens (a CMP decision, a dataLayer push, a click) into events. [Guide chapter `tour`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#tour-one-file-one-event)

`/flows/server/transformers`

```json
{
  "ga4Decode": { … },
  "ga4Consent": { … },
  "file": { … },
  "dedup": { … },
  "fingerprint": { … },
  "bot": { … },
  "enrich": { … },
  "loadUser": { … },
  "sessionSave": { … },
  "sessionLoad": { … },
  "validate": { … },
  "eventFilter": { … },
  "pseudonymize": { … }
}
```

Transformers are named hops that validate, enrich, filter or remember events. [Guide chapter `tour`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#tour-one-file-one-event)

`/flows/server/destinations`

```json
{
  "pubsub": { … },
  "meta": { … },
  "piwikpro": { … },
  "datamanager": { … }
}
```

Destinations fan out: every event reaches each destination that accepts it. [Guide chapter `tour`](https://github.com/elbwalker/walkerOS/blob/main/packages/cli/examples/flow-complete.md#tour-one-file-one-event)

## Programmatic usage[​](#programmatic-usage "Direct link to Programmatic usage")

You can also use configuration programmatically with the `startFlow` function:

```typescript
import { startFlow } from '@walkeros/collector';
import { sourceExpress } from '@walkeros/server-source-express';
import { destinationDemo } from '@walkeros/destination-demo';

const { collector, elb } = await startFlow({
  sources: {
    http: {
      code: sourceExpress,
      config: {
        settings: { paths: ['/collect'], port: 8080 }
      }
    }
  },
  destinations: {
    console: {
      code: destinationDemo,
      config: {
        settings: { name: 'Logger' }
      }
    }
  },
});

// Collector is now running and ready to process events
```

See the [Collector documentation](/preview/pr-749/docs/collector.md) for complete API reference.

## Next steps[​](#next-steps "Direct link to Next steps")

* **[CLI](/preview/pr-749/docs/apps/cli.md)** - Learn how to bundle and test flows
* **[Runtime](/preview/pr-749/docs/apps/runtime.md)** - Run built flows in containers
* **[Sources](/preview/pr-749/docs/sources.md)** - Explore available event sources
* **[Destinations](/preview/pr-749/docs/destinations.md)** - Configure analytics destinations
* **[Mapping](/preview/pr-749/docs/mapping.md)** - Transform events for destinations
