# CAS Commands

Manage Composable Audience Studio (CAS) resources — zero-copy CDP audiences that run directly on customer Cloud Data Warehouses (Snowflake, Databricks, BigQuery) without moving data into Treasure AI.

## Backend endpoint

`tdx cas` requests are served by cdp-api by default, following the CAS platform's cas-api → cdp-api mergeback. If you need to reach a composable audience that still only exists in the legacy standalone cas-api service, set:

```bash
export TDX_CAS_LEGACY_ENDPOINT=1
```

Note: cas-api's data was never migrated to cdp-api, so composable audiences created against one host are invisible from the other — `tdx cas list` will show a different set of audiences depending on which endpoint is active.

## Commands

### Discovery

| Command | Description |
|  --- | --- |
| [`list`](#list) | List composable audiences |
| [`desc`](#desc) | Describe a composable audience |


### YAML Sync

| Command | Description |
|  --- | --- |
| [`pull`](#pull) | Pull audience + segments to YAML files |
| [`validate`](#validate) | Validate composable audience YAML files locally |
| [`push`](#push) | Push YAML files to CAS API |
| [`journey pull`](#journey-pull) | Pull composable journeys to YAML files |
| [`journey validate`](#journey-validate) | Validate composable journey YAML locally |
| [`journey push`](#journey-push) | Create or update composable journeys from YAML files |


### Operations

| Command | Description |
|  --- | --- |
| [`preview`](#preview) | Preview composable segment query on CDW |
| [`sg list`](#sg-list) | List composable segments for an audience |
| [`sg push`](#sg-push) | Create, update, or delete a single child segment, standalone |
| [`journey list`](#journey-list) | List journeys of a composable audience |
| [`journey view`](#journey-view) | Show composable journey details |


## Typical Usage

```bash
# 1. List composable audiences
tdx cas list

# 2. Pull audience and all segments to YAML files
tdx cas pull "Customer360 Snowflake"
# Creates: cas/customer360-snowflake.yml (audience)
#          cas/high-value-customers.yml (segment)
#          cas/Marketing/newsletter-subs.yml (segment in folder)

# 3. Edit YAML files locally

# 4. Validate before pushing (optional — push validates too, but this is faster to iterate on)
tdx cas validate ./cas/

# 5. Push changes back to CAS API
tdx cas push ./cas/

# 6. Preview a segment query on the CDW
tdx cas preview "High-Value Customers" --audience "Customer360 Snowflake"
```

## Session Context

Set a default composable audience to avoid repeating the name:

```bash
tdx use cas "Customer360 Snowflake"

# Now these commands use the context:
tdx cas sg list           # Lists segments for "Customer360 Snowflake"
tdx cas desc              # Describes "Customer360 Snowflake"
tdx cas pull              # Pulls "Customer360 Snowflake"
```

## How It Works

**Composable Audiences** define customer profiles from tables in your own data warehouse. Unlike Complete CDP audiences (which copy data into Treasure AI), composable audiences query your CDW directly — zero data movement.

Each composable audience has:

- A **master table** (the primary customer table in your CDW)
- **Attributes** (additional columns joined from other tables)
- **Behaviors** (time-series event tables for behavioral segmentation)
- **Segments** (filtered subsets using rules/conditions)
- **Activations** (exports from segments to external systems)


## YAML Format

### Audience YAML

```yaml
name: Customer360 Snowflake
description: Customer data from Snowflake
timezone: America/New_York
allow_activation_behavior: true # optional, default false
max_activation_behavior_row: 5 # optional, 1–20
master:
  connection: '204' # Snowflake: a zero-copy config ID (see note below), not a connector name
  schema: customer_data
  table: customers
  key_column: cdp_customer_id
  # catalog: my-gcp-project   # required for Databricks/BigQuery connections; unused for Snowflake
attributes:
  - name: email_info
    connection: '204' # Snowflake: a zero-copy config ID (see note below), not a connector name
    schema: customer_data
    table: customers
    join:
      table_key: cdp_customer_id
      master_key: cdp_customer_id
    columns:
      - name: email
        type: string
        column: email_address
behaviors:
  - name: purchase_events
    connection: '204' # Snowflake: a zero-copy config ID (see note below), not a connector name
    schema: events
    table: purchase_history
    join:
      table_key: customer_id
      master_key: cdp_customer_id
    time_column: event_timestamp
    columns:
      - name: product_id
        type: string
        column: product_sku
      - name: amount
        type: number
        column: purchase_amount
```

**`connection` differs by platform:**

- **Snowflake**: a federated query config (zero-copy config) ID — a raw ID like `"204"`, never a connector name. This resource is never listed by `tdx connections`, so there's no name to look up; run `tdx cas pull` on an existing Snowflake composable audience to see the exact ID your account uses.
- **Databricks/BigQuery**: a connector name or ID from `tdx connections` (also requires `catalog`), or an authentication ID used to connect to the CDW.


### Segment YAML

```yaml
type: composable_segment
name: High-Value Customers
description: Customers with > $1000 in purchases
folder: Marketing/Campaigns
rule:
  type: And
  conditions:
    - type: Behavior
      behavior: purchase_events
      aggregation: Sum
      column: amount
      operator: GreaterThanOrEqual
      value: 1000
activations:
  - name: Export to Marketing
    connection: salesforce-connection
    connector_config:
      object: Contact
    columns: # required: either `columns` (as here) or `all_columns: true`
      - name: email
        type: string
    schedule:
      type: daily # none | daily | weekly | monthly
      timezone: UTC
      repeat_unit: daily # optional finer-grained repeat control
      repeat_frequency: 1
```

## Command Reference

### list

List all composable audiences.

```bash
tdx cas list [options]
```

| Option | Description |
|  --- | --- |
| `--json` | Output as JSON |
| `--jsonl` | Output as JSON Lines |


### desc

Describe a composable audience showing master table, attributes, and behaviors.

```bash
tdx cas desc [name]
```

Uses session context if `name` is omitted (set via `tdx use cas` or `tdx cas pull`).

### pull

Pull a composable audience and all its segments/activations to local YAML files.

```bash
tdx cas pull [name] [options]
```

| Option | Description |
|  --- | --- |
| `--dir <dir>` | Target directory for YAML files (default: `./cas/<audience-name>/`) |
| `--dry-run` | Show what would be done without writing files |


Uses session context if `name` is omitted. Also sets `composable_audience` context for subsequent commands.

The pull command shows diffs for changed files and prompts for confirmation before writing.

### validate

Validate composable audience YAML files locally, without contacting the CAS API.

```bash
tdx cas validate [target] [options]
```

| Option | Description |
|  --- | --- |
| `--verbose` | Also print each valid file (by default only files with errors/warnings are shown) |


`target` can be a file or directory. If omitted, uses the session context set by `tdx use cas`.

Checks performed:

- **YAML shape**: required fields, types, and enum values (e.g. `master.schema`, `master.table`, attribute/behavior `type`)
- **Semantic rules**: duplicate attribute/behavior names, `catalog` required for Databricks/BigQuery master connections, behaviors need `all_columns: true` or a non-empty `columns` list
- **Connection platform/catalog** (only when the value resolves to a registered connector): whether its platform is supported, and — for Databricks/BigQuery master connections — that `catalog` is set. A `connection:` value is **not** required to appear in your account's connector list (`tdx connections`) — for Snowflake it's typically a federated query config (zero-copy config) ID, and for Databricks/BigQuery it's typically an authentication used to connect to the CDW; both are separate resources from connectors, with their own ID namespaces. An unresolved connection is expected and not flagged.
- **Snowflake connection misuse**: a `connection:` value that matches a *registered connector name* on a Snowflake master/attribute/behavior is always an error — Snowflake needs a raw zero-copy config ID instead (see [YAML Format](#yaml-format) above), and resolving a connector name there would silently send the wrong ID and fail with a confusing error at push time.


This is the same validation `tdx cas push` runs as a pre-flight check before touching the API — `cas validate` exists as a faster local iteration loop, not a separate set of rules. It does **not** verify that TD can actually connect to your CDW or that the referenced schema/table/columns exist there — that only happens server-side during an actual `tdx cas push` (there is no standalone endpoint for a live connectivity check). Composable segment YAML (`type: composable_segment`) files are detected but not deeply validated by this command.

### push

Push local YAML files to the CAS API.

```bash
tdx cas push [target] [options]
```

| Option | Description |
|  --- | --- |
| `--dry-run` | Show what would be done without making changes |
| `-y, --yes` | Skip the confirmation prompt (for CI/CD and other non-interactive usage) |
| `--delete` | Allow removing audience attributes/behaviors not in local YAML (disabled by default — see below) |


`target` can be a file or directory. If omitted, pushes from the current directory. Audience files are pushed first, then segments and activations.

Push first validates all composable audience YAML files (same checks as [`tdx cas validate`](#validate)) and aborts before making any API calls if any file has errors. It then creates a new Composable Parent Segment when no audience with that name exists, or updates the existing one — showing a preview of what would be created/updated first, then prompting for confirmation before writing (pass `--yes` to skip the prompt). In a non-interactive session (e.g. CI/CD, no TTY), push refuses outright with an error unless `--yes` is passed — it never silently assumes confirmation.

A target can only contain **one** composable audience YAML file per push — every segment/activation file in the target attaches to that one audience. If more than one audience file is found, push aborts and lists the conflicting files rather than guessing which audience the segments belong to.

If a segment's `folder:` path doesn't exist yet (e.g. the first push of a brand-new audience), push creates the missing folder(s) automatically, parent folders first.

**Updating an existing audience replaces its full `attributes`/`behaviors` set, not a merge.** The backend does a destroy-and-rebuild of the whole collection whenever the request includes an `attributes` or `behaviors` key, so an attribute/behavior on the server but missing from the local YAML would otherwise be silently deleted. Push detects this ("drift") and refuses before writing anything, naming what would be removed — pass `--delete` to authorize the removal.

**Renaming an attribute/behavior (same source `schema`/`table`/`column`, new `name`) is detected as a rename, not a removal — it doesn't need `--delete`.** Because the backend always rebuilds the whole collection on update, a rename can't be applied truly in place: the attribute/behavior gets a new server-side id every time. Push output reports this explicitly, e.g. `Renamed attribute: "email" → "email_updated" (server id changed 10095 → 10096)` — if anything else (an activation, an external system) is keyed on the old id, update it there too.

### preview

Preview a composable segment query by running it on the CDW.

```bash
tdx cas preview <segment_name> [options]
```

| Option | Description |
|  --- | --- |
| `--audience <name>` | Composable audience name (or use session context) |


### sg list

List composable segments for an audience.

```bash
tdx cas sg list [audience_name]
```

If `audience_name` is omitted, uses the session context set by `tdx use cas`.

### sg push

Create or update a single composable child segment, standalone — without needing a co-located audience YAML file in the same directory.

```bash
tdx cas sg push <file> [options]
```

| Option | Description |
|  --- | --- |
| `--audience <name>` | Composable audience name (or use session context) |
| `--dry-run` | Show what would be done without making changes |
| `-y, --yes` | Skip the confirmation prompt (for CI/CD and other non-interactive usage) |
| `--delete` | Delete the named segment instead of creating/updating it (single segment only — see below; disabled by default) |


Unlike [`push`](#push), which requires an audience YAML file in the same target to resolve the parent, `sg push` resolves the parent audience by name — via `--audience <name>` or the session context set by `tdx use cas`. It creates a new child segment when none exists under that audience (matched by name), or updates the existing one — same idempotent-by-name behavior as `push`. Repeat pushes of the same segment file update in place rather than creating a duplicate.

**`--delete` deletes exactly the one segment named in the file's `name:` field — single-named-target only, not a directory/bulk prune.** This is a different meaning from `push`'s own `--delete` (which authorizes removing audience attributes/behaviors detected as drift) — each `--delete` is scoped to its own command's domain. Passing a directory instead of a file is rejected outright, not silently guessed at. The confirmation prompt names the segment and audience explicitly (not the generic "Push N change(s)?" prompt) since this is a destructive, irreversible action.

```bash
tdx cas sg push high-value-customers.yml --audience "Customer360 Snowflake" --delete
tdx cas sg push high-value-customers.yml --audience "Customer360 Snowflake" --delete --dry-run
tdx cas sg push high-value-customers.yml --audience "Customer360 Snowflake" --delete -y
```

### journey validate

Validate composable journey YAML locally, without authentication, API calls, or remote changes. Use [`tdx journey validate`](/treasure-code/commands/journey#validate) for complete journeys instead.

```bash
tdx cas journey validate [target] [--audience <name>]

# Validate a file or all journey files in a directory
tdx cas journey validate ./cas/my-audience/welcome.yml
tdx cas journey validate ./cas/my-audience
tdx cas journey validate .

# Select a local cas/my-audience directory without a remote lookup
tdx cas journey validate --audience "My Audience"
```

| Argument / option | Description |
|  --- | --- |
| `target` | Local `.yml` / `.yaml` file or directory. Explicit paths do not require audience context or `tdx.json`. |
| `--audience <name>` | Select `cas/<sanitized-name>/` under the current working directory when no target is supplied. This selects a local directory, not a remote audience. |
| `--verbose` | Also show valid files and skipped non-journey files. |


With neither a target nor `--audience`, the command uses the local `composable_audience` context. It never falls back to complete parent-segment context or scans every audience. Supply an explicit path for custom pull locations or audiences pulled by ID: a pull may store files under the ID while saving the returned audience name in context.

Directory validation finds nested journey definitions and skips audience and child-segment files. No YAML files, or no journey documents, is an error. Exit code `0` means local validation passed (warnings may remain); exit code `1` means validation or target resolution failed.

**Checks:**

- Journey structure, step references, activation definitions, and flow rules use the same validation as complete journeys.
- Embedded segment rules use the shared journey YAML format (`attribute` / `operator.value`) written by `tdx cas journey pull` and consumed by `tdx cas journey push`. Standalone composable segment files still use their separate `leftValue` / `operator.rightValue` format.
- Condition waits produce `unsupported_wait_type`; Jump steps produce `unsupported_step_type`.
- Goal jump targets, exit-criteria jump targets (`exit_criteria[*].target`), and multi-version definitions produce `unsupported_feature`.
- Supplied activation String Builder mappings with nonblank `source.string` produce `unsupported_field_type`, including literal-only expressions. Ordinary string-valued source columns are allowed.
- Basic A/B tests are allowed when their existing journey-schema and percentage/flow checks pass. They are not rejected merely for being A/B tests; advanced A/B configuration is not added to the YAML format by this command.


Unsupported-feature messages follow the current backend source, including:

```text
Jump step is not supported for Composable Audience journeys in M1.
Dynamic field construction (String Builder) is not supported for Composable Audience journeys in M1.
```

Raw activation mapping metadata is checked before schema normalization can discard it. Mapping structures that the local YAML format cannot safely represent are rejected rather than silently converted to ordinary columns. This does not add String Builder support or lossless expression round-tripping to pull/push.

Local validation is not server acceptance
The command does not resolve remote `ref:` activations, verify audience/account enablement, check connector capabilities, or determine whether pushing a single portable journey entry would update an existing version or create a new version. Existing journey/bundle IDs alone are not rejected as versioning. Composable journey use requires composable account enablement; successful local validation does not verify that enablement.

### journey list

List journeys of a composable audience — the composable counterpart of [`tdx journey list`](/treasure-code/commands/journey), which only sees Complete CDP audiences.

```bash
tdx cas journey list [pattern] [options]
```

| Option | Description |
|  --- | --- |
| `--audience <name>` | Composable audience name (or use session context) |


`pattern` is an optional glob filter on journey name, same as `tdx journey list`. If `--audience` is omitted, uses the `composable_audience` session context set by `tdx cas pull`.

On an account without composable journey dispatch enabled, this reports that clearly instead of returning a raw error or a silently empty list.

### journey view

Show a composable journey's details.

```bash
tdx cas journey view <name> [options]
```

| Option | Description |
|  --- | --- |
| `--audience <name>` | Composable audience name (or use session context) |


`name` matches the journey by name, same resolution `tdx journey view` uses. If `--audience` is omitted, uses the `composable_audience` session context set by `tdx cas pull`.

### journey pull

Export a composable audience's journeys as `*.journey.yml` files — the composable counterpart of `tdx journey pull`, with the same YAML format and round-trip behavior.

```bash
tdx cas journey pull [name] [options]
```

| Option | Description |
|  --- | --- |
| `--audience <name>` | Composable audience name (or use session context) |
| `--dir <dir>` | Target directory (default: `cas/<audience>`) |
| `--dry-run` | Show what would be written without writing files |
| `-y, --yes` | Skip the confirmation prompt |


Pulls every journey when `name` is omitted. Journeys land next to the audience and segment files `pull` writes — `cas/<audience>/<folder path>/<name>.journey.yml` — and `validate`/`push` skip them. Sets the `composable_audience` session context.

```bash
tdx cas journey pull --audience "Customer360 Snowflake"
tdx cas journey pull "Welcome Series" --audience "Customer360 Snowflake" --dry-run
```

### journey push

Create or update a composable audience's journeys, including their activation steps — the composable counterpart of `tdx journey push`.

```bash
tdx cas journey push [file] [options]
```

| Option | Description |
|  --- | --- |
| `--audience <name>` | Composable audience name (or use session context) |
| `--dir <dir>` | Audience root directory (default: `cas/<audience>`) |
| `--dry-run` | Show the changes without writing anything |
| `-y, --yes` | Skip the confirmation prompt |


Pushes every `*.journey.yml` under `--dir`, or just `file` (which must sit under it — folder paths are read relative to the audience root, the same layout [`journey pull`](#journey-pull) writes). Audience and segment files in the same directory are left to [`push`](#push). Journeys on the server that have no file are reported, never deleted. Pushing an unchanged, freshly pulled file reports it up to date.

```bash
tdx cas journey push --audience "Customer360 Snowflake" --dry-run
tdx cas journey push cas/customer360-snowflake/Welcome/onboarding.journey.yml -y
```

Composable journeys differ from complete ones in four ways:

- A journey needs a folder to live in, because that folder is what tells Treasure AI which audience the journey belongs to. Journeys at the top level use the audience's own root folder; if there isn't one, the command says so and pushes nothing.
- Composable journeys don't support every journey feature yet. A journey that uses an unsupported one stops the push with a message naming the feature. That rejection comes back from the server as the journey is written, so whatever the push had created by then — the folder, the journey, its segments, an activation — is still there. Remove the feature and push again, which reuses what already exists. The refusals in the other bullets are checked before the push starts — `--dry-run` reports them too — and write nothing.
- Only the first stage sets its own `entry_criteria`. Profiles enter every later stage by reaching the milestone of the stage before it, and that is the only entry criteria a composable journey stores. A later stage that asks for anything else is refused — adjust the previous stage's `milestone` instead.
- A composable journey has one definition, not versions. A file with more than one entry under `journeys:` is refused; the journey takes its name from the file's `name`, and `version` is only a label.


Journey orchestration for composable audiences must be enabled for your account.

## CAS vs Complete CDP

| Aspect | Complete (`tdx ps` / `tdx sg`) | Composable (`tdx cas`) |
|  --- | --- | --- |
| Data location | TD PlazmaDB | Customer's Snowflake/Databricks/BigQuery |
| Source reference | `database` + `table` | `connection` + `schema` + `table` |
| Query engine | Presto/Trino on TD | Direct SQL on CDW |
| Activations | `syndications` with `connectionId` | `composable_activations` with CDW-specific export |
| API prefix | `/audiences/`, `/entities/segments/` | `/composable_audiences/`, `/entities/composable_segments/` |