# Personalization Studio Commands

Manage Personalization Studio campaigns — web personalization campaigns that deliver targeted content to audiences based on profile attributes.

## Backend endpoint

`tdx ps p13n` requests are served by the Personalization Studio Config API:

| Site | Endpoint |
|  --- | --- |
| us01 | `personalization-studio-config.us01.treasuredata.com` |
| ap01 | `personalization-studio-config.treasuredata.co.jp` |
| eu01 | `personalization-studio-config.eu01.treasuredata.com` |


Not all sites are supported. If Personalization Studio is unavailable for your site, `tdx ps p13n` reports that and exits.

## Commands

| Command | Description |
|  --- | --- |
| [`list`](#list) | List campaigns for a parent segment |
| [`show`](#show) | Show detail for a single campaign |


## Typical Usage

```bash
# Set parent segment context
tdx use parent_segment "My Audience"

# List campaigns
tdx ps p13n list

# List campaigns with explicit parent segment ID
tdx ps p13n list --parent-segment-id 291115

# List by parent segment name (positional arg)
tdx ps p13n list "My Audience"

# JSON output for scripting
tdx ps p13n list --parent-segment-id 291115 --format json

# Inspect one campaign
tdx ps p13n show 42

# ...including every personalization mapping
tdx ps p13n show 42 --include-personalizations
```

## list

List Personalization Studio campaigns for a parent segment.

```bash
tdx ps p13n list [name] [options]
```

### Arguments

| Argument | Description |
|  --- | --- |
| `name` | Parent segment name (optional — uses context if omitted) |


### Options

| Option | Description |
|  --- | --- |
| `--parent-segment-id <id>` | Filter by parent segment ID (numeric) |
| `--parent-segment <name>` | Filter by parent segment name (global option) |
| `--format json` | Output as JSON array |
| `--format jsonl` | Output as JSON Lines |
| `--format tsv` | Output as TSV |


### Resolution order

The parent segment is resolved in this priority:

1. `--parent-segment-id <id>` (used directly, no lookup)
2. Positional `[name]` argument (resolved to ID)
3. `--parent-segment <name>` global option (resolved to ID)
4. Error with usage hint


### Output

Human-readable output shows each campaign with status indicator:

```
✔ Found 3 campaigns

🚀 Summer Sale Campaign  (launched)
   id: 1 | Jun 1, 2026 – Sep 30, 2026 America/Vancouver

📝 Winter Promo  (draft)
   id: 2 | Dec 1, 2026 – Jan 15, 2027 UTC

⚠️  Old Homepage Test  (stale)
   id: 3 | Jan 1, 2025 – Mar 31, 2025 America/Vancouver
```

Status meanings:

- **launched** 🚀 — Campaign is active and serving personalized content
- **draft** 📝 — Campaign is configured but not yet launched
- **scheduled** 📅 — Campaign has a start date and is ready to launch, but is not yet serving
- **stale** ⚠️ — Campaign was launched but configuration changed since last launch
- **paused** ⏸️ — Campaign was unlaunched and is no longer serving


### Examples

```bash
# List all campaigns for a parent segment by name
tdx ps p13n list "Customer360"

# Use session context (after `tdx use parent_segment "Customer360"`)
tdx ps p13n list

# Machine-readable output for automation
tdx ps p13n list --parent-segment-id 291115 --format json

# Pipe to jq for filtering
tdx ps p13n list --parent-segment-id 291115 --format json | jq '.[] | select(.status == "stale")'
```

## show

Show detail for a single campaign: its status, dates, parent segment, assigned audiences, and personalization count.

```bash
tdx ps p13n show [campaign] [options]
```

### Arguments

| Argument | Description |
|  --- | --- |
| `campaign` | Campaign name or ID (optional — uses context if omitted) |


### Options

| Option | Description |
|  --- | --- |
| `--campaign-id <id>` | Campaign ID (numeric); overrides the positional and context |
| `--parent-segment-id <id>` | Parent segment to resolve a campaign name within (numeric) |
| `--include-personalizations` | Also print the audience → page → spot → creative mapping |
| `--format json` | Output as a single JSON object |
| `--format jsonl` | Output as JSON Lines |
| `--format tsv` | Output as TSV |


### Resolution order

The campaign is resolved in this priority:

1. `--campaign-id <id>`
2. Positional `[campaign]` argument
3. `tdx use p13n_campaign <campaign>` session context
4. Error with usage hint


`--campaign-id` is an ID by definition, so it skips name resolution entirely — a non-numeric
value is rejected rather than looked up. The session context always holds an ID too, because
`tdx use p13n_campaign` resolves at set-time. Only the positional argument can cost a lookup.

A numeric argument is taken as a campaign ID and used directly. Anything else is a campaign
name, and names are only unique within a parent segment — so the parent segment is resolved
first, in this priority:

1. `--parent-segment-id <id>`
2. `--parent-segment <name>`
3. `tdx use parent_segment <name>` session context
4. Error with usage hint


Without any of those, pass the campaign ID instead. If two campaigns in the parent segment
share the name, `show` lists their IDs rather than guessing. An ID that does not exist reports
`Campaign not found: <id>` — the same wording a name that matches nothing gets.

### Output

```
✔ Found campaign 'Summer Sale'

🚀 Summer Sale  (launched)
  id: 42
  description: Homepage hero test
  parent segment: Customer360 (id: 291115)
  dates: Jun 1, 2026 – Sep 30, 2026 America/Vancouver
  key event: 7
  version: 3
  audiences: High Value, Cart Abandoners
  personalizations: 3
  created: 2026-01-01T00:00:00.000Z
  updated: 2026-01-02T00:00:00.000Z
```

The parent segment is shown by name, resolved from CDP. If that lookup fails, the ID is shown on its own.

With `--include-personalizations`, the mapping is appended, grouped by page and content spot:

```
Personalizations (3)

  Homepage https://example.com/
    Spot: Hero
      High Value → creative 5
      (default) → creative 6

  Pricing https://example.com/pricing
    Spot: Sidebar
      Cart Abandoners → creative 7
```

`(default)` marks the campaign's default personalization for a content spot — the creative
shown to profiles that fall into none of the assigned audiences.

### Cost of the count

The Personalization Studio Config API publishes no personalization count, so `show` has to
walk the collection to report one — the count itself is what costs the requests, not the
flag. Pages hold 100 personalizations, so most campaigns cost a single request. Passing a
campaign name rather than an ID adds one more request, to list the parent segment's campaigns.

To keep a plain `show` fast, that walk stops after 5 pages. A campaign with more than 500
personalizations therefore reports a floor rather than a total:

```
  personalizations: 500+ (use --include-personalizations for an exact count)
```

In `--format json` this is explicit: `personalizationCount` holds the number reached and
`personalizationCountTruncated` is `true`.

Passing `--include-personalizations` lifts the 5-page cap, but a hard ceiling of 10,000
personalizations still applies. A campaign above that ceiling still reports a floor, and the
hint says so rather than pointing back at the flag you already passed:

```
  personalizations: 10000+ (capped at 10000 personalizations)
```

### Stale campaigns

A stale campaign was launched, but its configuration changed afterwards, so what it serves is
out of date. `show` calls this out on stderr, which keeps it visible even when stdout is piped:

```
⚠️  This campaign is stale: it was launched, but its configuration has
   changed since. Relaunch it for those changes to take effect.
```

### Examples

```bash
# Show a campaign by ID
tdx ps p13n show 42

# Show a campaign by name, as printed by `tdx ps p13n list`
tdx use parent_segment Customer360
tdx ps p13n show "Summer Sale"

# Or name the parent segment inline
tdx ps p13n show "Summer Sale" --parent-segment-id 291115

# Set the campaign once, then omit it
tdx use p13n_campaign 42
tdx ps p13n show

# Override the context for one call
tdx ps p13n show --campaign-id 43

# Clear the campaign context again
tdx unset p13n_campaign

# Full mapping
tdx ps p13n show 42 --include-personalizations

# Machine-readable detail (a single object, not an array)
tdx ps p13n show 42 --format json | jq '.personalizationCount'

# Every audience assigned to the campaign
tdx ps p13n show 42 --format json | jq -r '.audiences[].name'
```

## Session context

Pin a campaign to the current shell session so `tdx ps p13n` commands can omit it:

```bash
tdx use p13n_campaign <campaign>   # name or ID
tdx unset p13n_campaign            # clear it again
```

`tdx use p13n_campaign` accepts a name or an ID but **stores the ID**. It resolves the value
against the API at set-time, which does two things: it fails immediately on a campaign that
does not exist, and it echoes the campaign's name so you can confirm you pinned the right one.

```
$ tdx use p13n_campaign 42
Session p13n_campaign set to: 42 (Summer Sale) (session: 12345)

$ tdx use p13n_campaign 99999999
Error: Campaign not found: 99999999

Use "tdx ps p13n list" to see available campaigns.
```

The ID is what gets stored because campaign names are unique only within a parent segment.
Storing a name would mean every later `tdx ps p13n` call also needed `parent_segment` in
context to make sense of it; storing the ID keeps those calls free of any lookup.

For the same reason, setting the context **by name** needs a parent segment at set-time —
via the global `--parent-segment <name>` flag or `tdx use parent_segment <name>`. Note that
`--parent-segment-id` is *not* available here: it is a per-command option on `list` and
`show`, not a global one. Setting the context by ID needs nothing.

`tdx unset p13n_campaign` clears only this key; other session context (database, parent
segment, and so on) is left alone. To clear everything at once, use `tdx use --clear`.

Both `tdx use` (with no arguments) and `tdx status` list the current `p13n_campaign`.