tdx CAS - Composable Audience Studio Management
Composable audiences never copy data into Treasure Data — they query the customer's own Snowflake/Databricks/BigQuery directly. This is the main reason CAS YAML differs from standard tdx ps/tdx sg YAML: every table reference needs a connection, and child segment rules use a different condition shape.
Pushing composable audiences safely — read this first
All CAS writes go through the typed tdx cas commands. Never create or update a composable audience/segment/activation with raw tdx api HTTP calls — the request shapes below have real footguns that the typed command handles for you.
- Audience updates are a full replace of
attributes/behaviors, not a merge. If you push a YAML that's missing an attribute/behavior the server currently has,tdx cas pushrefuses before writing anything and names exactly what would be removed. This is intentional — it is not a bug to work around. Either update the local YAML to include the missing field (pull first if it might be stale:tdx cas pull), or, if removing it is genuinely intended, re-run with--delete. Never assume--deleteis safe to add reflexively just to make the refusal go away — confirm with the user first if you didn't write the YAML yourself. - Renaming an attribute/behavior (same source
schema/table/column, newname) does NOT need--delete.tdx cas pushdetects this as a rename, not a removal, and applies it directly. Under the hood the backend recreates the attribute/behavior with a new server-side id every time (it can't rename in place) — push output reports this explicitly, e.g.Renamed attribute: "email" → "email_updated" (server id changed 10095 → 10096). If anything else is keyed on that id (an activation, an external system), it needs to be updated separately;tdx cas pushhas no way to know about or update such references. - Repeat pushes are idempotent by name. Pushing the same audience/segment/activation name again updates the existing one — it does not create a duplicate. There's no need to check existence yourself before pushing.
- No confirmation prompt on drift or errors. Both the drift refusal above and any other push failure exit non-zero with a specific message (never a generic "push failed"). Read the message — it names the exact field/attribute/behavior involved.
- If a typed command seems not to exist for what you need, ask the user — do not fall back to raw
tdx api.
Core Commands
tdx cas list # List composable audiences
tdx cas desc <name> # Describe a composable audience
tdx cas pull <name> [--dir <dir>] # Pull audience + segments to YAML
tdx cas validate <file_or_dir> # Validate YAML locally (no API calls)
tdx cas push <file_or_dir> # Preview, confirm, then push
tdx cas push <file_or_dir> --dry-run # Preview only, no writes
tdx cas push <file_or_dir> -y # Skip confirmation (CI/CD)
tdx cas push <file_or_dir> --delete # Also apply a detected attribute/behavior removal
tdx cas sg push <segment_file> --audience <name> # Push one child segment standalone
tdx cas sg push <segment_file> --audience <name> --delete # Delete that one named segment
tdx cas preview <segment_name> --audience <name> # Preview a segment query on the CDW
tdx cas push always previews first (dry-run pass showing create/update counts and any drift), prompts for confirmation on a real write unless -y, and refuses outright in non-interactive mode without -y.
Audience YAML
name: Customer360 Snowflake
description: Customer data from Snowflake
timezone: UTC
master:
connection: '<federated_query_config_id>' # Snowflake: raw zero-copy config ID, not a connector name
schema: PUBLIC
table: CUSTOMERS
key_column: CDP_CUSTOMER_ID
attributes:
- name: email
connection: '<federated_query_config_id>'
schema: PUBLIC
table: CUSTOMERS
join:
table_key: CDP_CUSTOMER_ID
master_key: CDP_CUSTOMER_ID
column: EMAIL
type: string # Must match the real CDW column type — see Connection resolution below
behaviors:
- name: purchases
connection: '<federated_query_config_id>'
schema: PUBLIC
table: PURCHASE_HISTORY
join:
table_key: CDP_CUSTOMER_ID
master_key: CDP_CUSTOMER_ID
time_column: TIMESTAMP
columns: # required: this OR `all_columns: true`
- name: amount
type: number
column: AMOUNT
Attribute/behavior type must match the real CDW column type (string, number, boolean, date, timestamp, string_array, number_array) — a string-typed attribute over a numeric CDW column is rejected by the API (400) even though it's syntactically valid YAML. If unsure, pull an existing audience over the same table and copy its types.
Every audience needs at least one attribute. tdx cas validate/push reject an audience with an empty or missing attributes: list — the backend requires at least one.
all_columns: true for a behavior only works if that behavior already has it set server-side. The backend rejects all_columns: true for a brand-new behavior, or when flipping an existing behavior from explicit columns: to all_columns: true — this only succeeds on a behavior that was already created with it (e.g. an older audience predating this restriction). For any new or changed behavior, use an explicit columns: list instead; don't reach for all_columns: true as a shortcut. (Note: this is different from segment activations below, which do support all_columns: true freely.)
Connection resolution — differs by CDW platform
The connection: field is a single generic value in the YAML, but what it means depends on the platform:
- Snowflake: a federated query config (zero-copy config) ID — a raw numeric ID, never a connector name. This resource is never listed by
tdx connections, so there's no name to look up;tdx cas pullon an existing Snowflake audience shows the exact ID in use. Passing a value that happens to match a registered connector name on a Snowflake field is always a mistake —tdx cas validate/pushcatch this and fail fast rather than silently misrouting the ID. - Databricks/BigQuery: a connector name or ID from
tdx connections, plus a requiredcatalog:field alongsideconnection:in the same block (master, or per-attribute/behavior).
Child segment YAML — different rule DSL from tdx sg
Composable segment conditions use leftValue (the attribute) and operator (the comparison, which itself nests type, not, and rightValue) — not the attribute/operator: {type, value} shape standard tdx sg segments use. rightValue is not a sibling of leftValue/operator; it nests inside operator, as shown below. Get this wrong and the API returns a 400 naming exactly which field is missing/invalid.
leftValue.name must exactly match the parent audience's attribute/behavior name: field — not the underlying CDW column, and not a lowercased or otherwise reformatted version of it. For example, if the audience attribute is name: STATE (mapped to CDW column STATE), the rule must use leftValue: { name: STATE }, not name: state. This match is case-sensitive. There is no client- or push-time validation for this — composable segment rules have no schema (unlike composable audience YAML), so a mismatched name doesn't error at all: the segment pushes successfully, then silently matches zero rows and shows a blank rule in the Console UI. If a pushed segment's rule looks blank in Console or a preview returns zero rows unexpectedly, check leftValue.name against the audience's actual attribute/behavior names (tdx cas pull the parent audience to see them) before assuming anything else is wrong.
type: composable_segment
name: High Value Customers
folder: Marketing/VIP # optional — created automatically if it doesn't exist yet
rule:
type: And
conditions:
- type: Value
exclude: false
leftValue:
name: cdp_customer_id # the attribute name, nested under leftValue
operator:
type: Equal # Equal and other comparison types available
not: false
rightValue: "some-value" # the comparison value, as a string
activations:
- name: Export to Marketing
connection: salesforce-connection
all_columns: true # or `columns:` — one of the two is required
schedule:
type: daily # none | daily | weekly | monthly
timezone: UTC
Nested And/Or condition groups are supported here, same as standard tdx sg rules (nesting there only produces a validator warning about the Console UI's SQL preview, not a rejection — segment execution isn't affected either way).
For activations[].connector_config fields, don't guess the field names — run tdx connection schema <connector_type> first (see the connector-config skill) to discover them for the specific connector.
type: composable_segment at the top level is mandatory, exact literal — not just documentation, it's how tdx tells this file apart from a composable audience file. Omit it and the file gets validated against the audience schema instead, producing a confusing error about an unrelated field (e.g. a missing master:) rather than anything about the segment itself. If you see an error naming master or other audience-only fields while pushing something you intend as a segment, check type: first.
Composable segment YAML currently has no schema validation ahead of push (unlike composable audience YAML, which does). A missing rule: reaches the backend and comes back as a raw database error, not a clean client-side message — if you hit an unfamiliar low-level error pushing a segment, suspect a missing/malformed rule block first before assuming something else is wrong.
Pushing a single child segment standalone
tdx cas sg push <segment_file> --audience <name> creates or updates one child segment without needing a co-located audience.yml in the same directory — the parent audience is resolved by name (via --audience, or the session context set by tdx use cas <name>). Same idempotent-by-name behavior as tdx cas push: pushing the same segment name again updates it in place, never creates a duplicate.
tdx cas sg push high-value-customers.yml --audience "Customer360 Snowflake"
tdx cas sg push high-value-customers.yml --audience "Customer360 Snowflake" --dry-run
tdx cas sg push high-value-customers.yml --audience "Customer360 Snowflake" -y
Use this instead of tdx cas push <dir> when you only need to manage one segment file independently — e.g. a CI/CD pipeline that touches one segment at a time, or when the parent audience is managed elsewhere and shouldn't be re-pushed alongside every segment change.
Deleting a single child segment
tdx cas sg push <segment_file> --audience <name> --delete deletes exactly the one segment named in that file's name: field. This is single-named-target delete only — not a directory/bulk prune. Passing a directory with --delete is rejected outright, not silently interpreted as "delete everything not in this directory."
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
This --delete means something different from tdx cas push's own --delete (which authorizes removing audience attributes/behaviors detected as drift) — each is scoped to its own command's domain. Don't assume --delete always means the same thing across cas push and cas sg push. The confirmation prompt names the segment and audience explicitly since this is destructive and irreversible — never skip the confirmation (-y) on a segment you didn't write the YAML for yourself without confirming with the user first.
Legacy endpoint toggle
tdx cas requests go to the current default CAS backend host. If a composable audience only exists on the older, standalone legacy host (rare — most accounts are fully migrated), set TDX_CAS_LEGACY_ENDPOINT=1 before running the command. The two hosts do not share data — an audience visible on one is invisible on the other, so only toggle this if tdx cas list genuinely doesn't show an audience you expect to find.
Common Issues
| Issue | Solution |
|---|---|
| Push refuses with "would remove attribute(s)/behavior(s) ..." | Intentional drift protection — see "Pushing composable audiences safely" above. Update the YAML or use --delete deliberately. |
CONNECTION_NOT_FOUND |
Check the connection: value against tdx connections (Databricks/BigQuery) or re-pull the audience to confirm the Snowflake zero-copy config ID (Snowflake). |
| 400 with a Snowflake connection that matches a connector name | You used a connector name where a zero-copy config ID is required — pull an existing Snowflake audience to see the correct ID format. |
400 naming a type mismatch on an attribute/behavior column |
The YAML type: doesn't match the real CDW column type — check the source table's actual column type. |
catalog is required |
Databricks/BigQuery connections need catalog: set alongside connection: in the same block. |
Segment rule 400 naming leftValue/rightValue/operator |
Composable segment rules use a different shape than standard tdx sg — see "Child segment YAML" above, don't reuse a standard segment's rule YAML as-is. |
| Segment pushes fine but shows a blank rule in Console / preview returns 0 rows | leftValue.name doesn't exactly match the audience's attribute/behavior name: (case-sensitive) — there's no validation to catch this. tdx cas pull the parent audience and check the real attribute/behavior names. |
cas sg push fails naming master or another audience-only field |
The file is missing type: composable_segment at the top level and got validated as an audience file instead. Add the field — this isn't about the field the error names. |
cas sg push --delete against a directory is rejected |
Single-named-target delete only — point it at the one segment file to delete, not a directory. |
| Unfamiliar low-level/database error pushing a segment | Composable segment YAML has no schema validation ahead of push — check for a missing or malformed rule: block first. |
all_columns: true rejected for a behavior |
Only works on a behavior that already has it set server-side — use an explicit columns: list for any new or changed behavior instead. |
| Non-interactive mode error | Add -y: tdx cas push -y <path> |
An audience you expect isn't in tdx cas list |
It may only exist on the other host — try again with TDX_CAS_LEGACY_ENDPOINT=1. |
Related Skills
- connector-config - Discover
connector_configfields per connector type for activations - segment - Standard (non-composable) child segment management — note the different rule DSL
- parent-segment - Standard (non-composable) parent segment management