# Engage Commands

Manage Engage campaigns, email templates, and workspaces.

## Workspace Context

Engage commands require a workspace context. You can specify a workspace in two ways (in order of priority):

1. **Command option**: `--workspace <name>`
2. **Session context**: `tdx use engage_workspace <name>`


```bash
# Set workspace for the current session
tdx use engage_workspace "Marketing Team"

# Now all engage commands use this workspace
tdx engage campaigns
tdx engage campaign create --name "Newsletter" --type email

# Override workspace for a single command
tdx engage campaigns --workspace "Sales Team"
```

## Campaigns

### List Campaigns

`campaign list` lists regular campaigns by default. Use `--campaign-type list-campaign` to list workspace-scoped ListCampaign resources instead. The selector is separate from `--type`, which filters the message channel.

```bash
# List regular campaigns (default)
tdx engage campaign list
tdx engage campaigns              # alias for "campaign list"
tdx engage campaign list --campaign-type campaign
tdx engage campaign list --workspace "Marketing Team" --campaign-type campaign

tdx engage campaigns --campaign-type list-campaign --workspace "Marketing Team"  # alias also accepts the selector.

# List ListCampaign resources in a workspace
# Workspace is required; use --workspace or set tdx use engage_workspace
tdx engage campaign list --campaign-type list-campaign --workspace "Marketing Team"
tdx engage campaign list --campaign-type list-campaign --workspace "Marketing Team" --status PLANNED

# Filter regular campaigns by pattern (glob pattern with * and ? wildcards)
tdx engage campaign list "test*"
tdx engage campaign list "*_production"

# Filter by message channel
# ListCampaign currently supports email only
tdx engage campaign list --type email
tdx engage campaign list --campaign-type list-campaign --type email

# Filter regular campaigns by status
tdx engage campaign list --status DRAFT
tdx engage campaign list --status ACTIVE
tdx engage campaign list --status PAUSED
tdx engage campaign list --status COMPLETED

# ListCampaign statuses: DRAFT, PLANNED, ACTIVE, SUSPENDED, FINISHED

# Combine filters
tdx engage campaign list --campaign-type list-campaign --workspace "Marketing" --type email --status ACTIVE

# Limit results
tdx engage campaign list --limit 10
```

| Option | Description |
|  --- | --- |
| `--campaign-type campaign` | List regular campaigns (default; `GET /api/campaigns`) |
| `--campaign-type list-campaign` | List contact-list campaigns (requires a workspace; `GET /api/workspaces/{workspace_id}/list_campaigns`) |
| `--type <type>` | Filter by message channel (`email`, `push`, or `line`; ListCampaign supports `email` only) |


**The workspace is resolved from `--workspace` or the `engage_workspace` session context for ListCampaign.**

### Show Campaign Details

```bash
# Show campaign by name
tdx engage campaign show "My Newsletter Campaign"

# Show campaign by UUID
tdx engage campaign show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Specify workspace when using name
tdx engage campaign show "My Campaign" --workspace "Marketing Team"

# Show full JSON:API response with all fields (emailContent, pushContent, etc.)
tdx engage campaign show "My Campaign" --full
```

### Show ListCampaign Details

```bash
# Workspace is required for ListCampaign resources
tdx engage campaign show "Monthly Newsletter" --campaign-type list-campaign --workspace "Marketing Team"
tdx engage campaign show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4" --campaign-type list-campaign --workspace "Marketing Team" --full
```

Use `--campaign-type list-campaign` for ListCampaign details; regular campaigns remain the default. ListCampaign detail output does not include a Console link.

### Create Campaign

ListCampaigns are created through the YAML workflow in [Push Campaigns](#push-campaigns), using `--campaign-type list-campaign`.

**Note:** Workspace is required for creating campaigns. Set it via `tdx use engage_workspace` or `--workspace`.

```bash
# Create an email campaign (workspace from session)
tdx engage campaign create --name "Monthly Newsletter" --type email

# Create with explicit workspace
tdx engage campaign create --name "Monthly Newsletter" --type email \
  --workspace "Marketing Team"

# Create with description
tdx engage campaign create --name "Monthly Newsletter" --type email \
  --description "Monthly newsletter for subscribers"

# Create with segment targeting (by path)
tdx engage campaign create --name "VIP Campaign" --type email \
  --segment "My Audience/VIP Users"

# Create with email sender configuration
tdx engage campaign create --name "Monthly Newsletter" --type email \
  --email-sender-id "sender-uuid-123" \
  --json-columns "email,name,preferences"

# Create with delivery schedule
tdx engage campaign create --name "Morning Newsletter" --type email \
  --start-at "2024-01-15T09:00:00" \
  --timezone "Asia/Tokyo"

# Create a push notification campaign
tdx engage campaign create --name "Flash Sale Alert" --type push
```

### Update Campaign

```bash
# Update campaign name
tdx engage campaign update "Monthly Newsletter" --name "Weekly Newsletter"

# Update campaign description
tdx engage campaign update "Monthly Newsletter" --description "Updated description"

# Update segment targeting
tdx engage campaign update "My Campaign" \
  --segment "New Audience/Premium Users"

# Update delivery schedule
tdx engage campaign update "My Campaign" \
  --start-at "2024-02-01T10:00:00" \
  --timezone "UTC"

# Update email sender configuration
tdx engage campaign update "My Campaign" \
  --email-sender-id "new-sender-uuid"

# Specify workspace when using name
tdx engage campaign update "My Campaign" --workspace "Marketing" --name "New Name"
```

### Delete Campaign

```bash
# Delete campaign by name (prompts for confirmation)
tdx engage campaign delete "Old Campaign"

# Delete campaign by UUID
tdx engage campaign delete "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Skip confirmation prompt
tdx engage campaign delete "Old Campaign" --yes

# Specify workspace when using name
tdx engage campaign delete "My Campaign" --workspace "Marketing"
```

### Campaign Lifecycle Commands

```bash
# Launch a campaign (changes status from DRAFT to ACTIVE)
tdx engage campaign launch "Monthly Newsletter"
tdx engage campaign launch "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Pause a running campaign (changes status from ACTIVE to PAUSED)
tdx engage campaign pause "Monthly Newsletter"

# Resume a paused campaign (changes status from PAUSED to ACTIVE)
tdx engage campaign resume "Monthly Newsletter"

# Duplicate a campaign (creates a copy in DRAFT status)
tdx engage campaign duplicate "Monthly Newsletter"

# Preview a ListCampaign launch (no email is sent)
tdx engage campaign launch "Monthly Newsletter" --campaign-type list-campaign --workspace "Marketing Team" --dry-run

# Launch a ListCampaign after reviewing the audience source and confirming
# This starts delivery to the contact list and cannot be undone
tdx engage campaign launch "Monthly Newsletter" --campaign-type list-campaign --workspace "Marketing Team"
```

ListCampaign launch requires a workspace. `--dry-run` shows the source contact-list table without calling the launch API. A live launch requires interactive confirmation; use `--yes` only after reviewing the target audience because delivery begins for the contact list.

### Pull Campaigns

Export campaigns from Engage to local YAML + HTML files. This is the starting point for the YAML-based campaign workflow.

```bash
# Pull all campaigns from a workspace
tdx engage campaign pull "Marketing Team"
tdx engage campaign pull --workspace "Marketing Team"

# Pull using session context
tdx use engage_workspace "Marketing Team"
tdx engage campaign pull

# Pull a specific campaign by name
tdx engage campaign pull "Marketing Team" --name "Monthly Newsletter"

# Pull only email campaigns
tdx engage campaign pull "Marketing Team" --type email

# Preview what would be written without writing files
tdx engage campaign pull "Marketing Team" --dry-run

# Skip confirmation prompt
tdx engage campaign pull "Marketing Team" --yes
```

| Option | Description |
|  --- | --- |
| `--workspace <name>` | Workspace name |
| `--name <name>` | Pull specific campaign by name |
| `--type <type>` | Filter by campaign type (`email`, `push`, `line`) |
| `--dry-run` | Show what would be written without writing |
| `-y, --yes` | Skip confirmation prompt |


Pulled files are written to `campaigns/<workspace-slug>/` under the current directory. Each campaign produces a YAML file and optionally an HTML file (if the campaign has an HTML email override).

### Push Campaigns

Push local YAML files to the Engage API. Regular campaigns are matched by name and updated or created. Use `--campaign-type list-campaign` for ListCampaign YAML; existing DRAFT ListCampaigns are updated, and missing ones are created as drafts.

```bash
# Push a regular campaign file
tdx engage campaign push path/to/campaign.yaml

# Validate and preview ListCampaign draft changes without applying them
tdx engage campaign validate path/to/list-campaign.yaml --campaign-type list-campaign
tdx engage campaign push path/to/list-campaign.yaml --campaign-type list-campaign --dry-run

# Apply after reviewing the dry-run (prompts for confirmation unless --yes is set)
tdx engage campaign push path/to/list-campaign.yaml --campaign-type list-campaign
```

A ListCampaign YAML definition uses `type: list_campaign` and includes its contact list, source-column mapping, email content, and sender. The `email.template` must reference an existing template with `ref:`; HTML and optional plaintext files are read relative to the YAML file.

```yaml
type: list_campaign
name: Monthly Newsletter
contact_list:
  database_name: marketing
  table_name: subscribers
source_columns:
  - key: email
    sql_name: email
    type: string
email:
  template: "ref:Newsletter Template"
  subject: "Hello {{ first_name }}"
  html_file: newsletter.html
  plaintext_file: newsletter.txt # optional
  sender_id: sender-uuid
# Optional:
enable_utm_tracking: true
```

The `email` source column must have `sql_name: email` and `type: string`. `enable_utm_tracking` is optional. Push updates only an existing ListCampaign in `DRAFT` status. `--dry-run` resolves references and shows the planned create/update operations but does not write changes; applying requires interactive confirmation or `--yes`.

| Option | Description |
|  --- | --- |
| `--campaign-type campaign` | Push regular campaign YAML (default) |
| `--campaign-type list-campaign` | Push ListCampaign YAML (`type: list_campaign`) |
| `--workspace <name>` | Workspace name (overrides tdx.json and session context) |
| `--dry-run` | Show what would be applied without applying |
| `-y, --yes` | Skip confirmation prompt |


**Workspace resolution order:** `--workspace` option > `tdx.json` (`engage_workspace` field) > session context.

For regular email campaigns, the `email:` YAML section may include `preheader` for inbox preview text. It supports Liquid tags, is limited to 220 characters by the API, and accepts `null` to clear an existing value.

### Validate Campaigns

Validate regular campaign YAML files locally, or select ListCampaign YAML with `--campaign-type list-campaign`. Validation is local and does not call the API.

```bash
# Validate all regular campaign files in the current directory
tdx engage campaign validate

# Validate a specific regular campaign file
tdx engage campaign validate campaign.yaml

# Validate a ListCampaign definition
tdx engage campaign validate path/to/list-campaign.yaml --campaign-type list-campaign

# Show all files including valid ones
tdx engage campaign validate --verbose
```

| Option | Description |
|  --- | --- |
| `--campaign-type campaign` | Validate regular campaign YAML (default) |
| `--campaign-type list-campaign` | Validate ListCampaign YAML locally |
| `--verbose` | Show all files including valid ones |


For API-level validation of regular campaigns (resolving references), use `tdx engage campaign push --dry-run`. ListCampaign push `--dry-run` also resolves its template reference and previews planned draft changes.

### Check Campaign Readiness

Check an email campaign's workspace delivery configuration before launch. This check is read-only; it does not create or update campaigns or send email.

```bash
tdx engage campaign readiness campaign.yaml
tdx engage campaign readiness path/to/campaigns/ --workspace Marketing
```

Workspace resolution order is `--workspace` > `tdx.json` (`engage_workspace`) > session context. For email campaigns, readiness checks that the workspace has an assigned email send domain, that `email.sender_id` identifies a sender profile available in the workspace, and that the sender's email domain is assigned to the workspace. Non-email campaigns are reported as not applicable.

This check confirms workspace assignments only. It does not verify DNS/domain activation, audience eligibility, consent or suppression results, or API-level campaign references. Use `tdx engage campaign push --dry-run` separately to resolve and validate campaign references. The `validate` command above remains local-only. Use `--json` or `--jsonl` for a machine-readable report with per-file results and summary counts.

Valid non-email campaigns are reported as not applicable and do not fail a LINE-only readiness run. Local validation failures or blocked email prerequisites return a non-zero exit status.

Without a target, the command scans the current directory, or the discovered `tdx.json` project root when `engage_workspace` is set.

## Email Templates

### List Templates

```bash
# List all email templates (uses workspace from session context)
tdx engage template list
tdx engage templates              # alias for "template list"

# List templates in a specific workspace
tdx engage template list --workspace "Marketing Team"

# Filter templates by pattern
tdx engage template list "welcome*"
tdx engage template list "*_newsletter"

# Limit results
tdx engage template list --limit 20
```

### Show Template Details

```bash
# Show template by name
tdx engage template show "Welcome Email"

# Show template by UUID
tdx engage template show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Specify workspace when using name
tdx engage template show "Welcome Email" --workspace "Marketing"

# Show full JSON:API response with all fields (htmlTemplate, beefreeJson, etc.)
tdx engage template show "Welcome Email" --full
```

### Create Template

**Note:** Workspace is required for creating templates. Set it via `tdx use engage_workspace` or `--workspace`.

```bash
# Create an email template (workspace from session)
tdx engage template create --name "Welcome Email" \
  --subject "Welcome to our service!" \
  --preheader "Start exploring your new account today." \
  --html "<html><body><h1>Welcome!</h1></body></html>"

# Create with explicit workspace
tdx engage template create --name "Welcome Email" \
  --subject "Welcome to our service!" \
  --html "<html><body><h1>Welcome!</h1></body></html>" \
  --workspace "Marketing Team"

# Create from an HTML file
tdx engage template create --name "Welcome Email" \
  --subject "Welcome to our service!" \
  --html-file ./templates/welcome.html

# Create with plaintext version
tdx engage template create --name "Welcome Email" \
  --subject "Welcome to our service!" \
  --html "<html><body><h1>Welcome!</h1></body></html>" \
  --plaintext "Welcome to our service!"
```

### Update Template

```bash
# Update template name
tdx engage template update "Welcome Email" --name "New Welcome Email"

# Update template subject or inbox preview text
tdx engage template update "Welcome Email" --subject "New Subject Line"
tdx engage template update "Welcome Email" --preheader "A concise preview for inboxes"

# Update template HTML
tdx engage template update "Welcome Email" --html "<html><body><h1>Updated!</h1></body></html>"

# Update template HTML from file
tdx engage template update "Welcome Email" --html-file ./templates/updated.html

# Update by UUID
tdx engage template update "01968a3a-ae17-7ef6-9b45-f1a0af1224b4" --name "New Name"

# Specify workspace when using name
tdx engage template update "Welcome Email" --workspace "Marketing" --name "New Name"
```

### Delete Template

```bash
# Delete template by name (prompts for confirmation)
tdx engage template delete "Old Template"

# Delete template by UUID
tdx engage template delete "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Skip confirmation prompt
tdx engage template delete "Old Template" --yes

# Specify workspace when using name
tdx engage template delete "My Template" --workspace "Marketing"
```

## In-Browser Messages

Manage workspace-scoped popup and inline web messages as local YAML definitions. The workflow supports drafts only: it does not launch, pause, finish, or delete messages.

### List and Show

```bash
# List messages in the current workspace
# The plural form is an alias for "in-browser-message list"
tdx engage in-browser-message list
tdx engage in-browser-messages

# Filter by glob pattern or type
tdx engage in-browser-message list "summer*" --workspace "Marketing Team"
tdx engage in-browser-message list --type popup
tdx engage in-browser-message list --type inline

# Show by exact name or UUID
tdx engage in-browser-message show "Summer Promo" --workspace "Marketing Team"
tdx engage in-browser-message show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4" --workspace "Marketing Team"

# Show the full JSON:API resource, including HTML and BeeFree state
tdx engage in-browser-message show "Summer Promo" --full
```

### Pull and Validate

`pull` writes definitions to `in-browser-messages/<workspace-slug>/`. Each workspace directory contains `tdx.json`, a YAML file per message, its HTML companion file, and—for popups returned with editor state—a BeeFree JSON companion file.

```text
in-browser-messages/marketing-team/
├── tdx.json
├── summer-promo.yaml
├── summer-promo.html
├── summer-promo.json
├── homepage-banner.yaml
└── homepage-banner.html
```

```bash
# Pull all messages, preview file changes, or select a subset
tdx engage in-browser-message pull "Marketing Team"
tdx engage in-browser-message pull "Marketing Team" --dry-run
tdx engage in-browser-message pull "Marketing Team" --type popup --yes
tdx engage in-browser-message pull "Marketing Team" --name "Summer Promo" --yes

# Validate one definition or every in-browser-message YAML file below a directory
tdx engage in-browser-message validate ./in-browser-messages/marketing-team
# Include valid and skipped files in the output
tdx engage in-browser-message validate ./in-browser-messages/marketing-team --verbose
```

For pull, the workspace is resolved in this order: positional workspace, `--workspace`, nearby `tdx.json`, then the session context. Pull shows a diff for changed files, does not write during `--dry-run`, and prompts before writing unless `--yes` is provided. Pulling an empty workspace still creates or updates its `tdx.json` and sets the workspace context.

### YAML Format

Popup messages keep their rendered HTML and optional BeeFree editor state in companion files:

```yaml
# summer-promo.yaml
type: in_browser_message
name: Summer Promo
message_type: popup
description: Seasonal popup modal
workspace: Marketing Team

parent_segment:
  id: segment-uuid
  name: Customers
personalization:
  id: personalization-uuid
  name: Hero Section

html_file: summer-promo.html
beefree_json_file: summer-promo.json
appearance:
  position: center
```

Inline messages require a CSS selector and cannot specify popup-only fields:

```yaml
# homepage-banner.yaml
type: in_browser_message
name: Homepage Banner
message_type: inline
workspace: Marketing Team
html_file: homepage-banner.html
css_selector: "#promo-banner-container"
```

All messages require `name`, `message_type`, and `html_file`. `message_type` is either `popup` or `inline`. Inline messages require `css_selector`; it cannot be `*` or begin with `>`, `+`, or `~`. Popups cannot set `css_selector`. Inline definitions cannot set `appearance` or `beefree_json_file`.

When specified, `parent_segment` and `personalization` must each contain the complete `id` and `name` metadata returned by Engage. `appearance` is the backend-shaped popup appearance configuration. Omit it to preserve the server's default appearance settings. The HTML and BeeFree paths must be safe relative paths contained within the YAML definition directory.

### Push Drafts

`push` discovers `type: in_browser_message` YAML files, validates them locally, and matches messages by exact trimmed name within the selected workspace. It creates missing messages as drafts and updates matching drafts.

```bash
# Preview create/update actions without API writes
tdx engage in-browser-message push ./in-browser-messages/marketing-team --dry-run

# Push one definition or an entire directory
tdx engage in-browser-message push ./in-browser-messages/marketing-team/summer-promo.yaml --yes
tdx engage in-browser-message push ./in-browser-messages/marketing-team --yes

# Override workspace stored in tdx.json
tdx engage in-browser-message push ./in-browser-messages/marketing-team --workspace "Marketing Team" --yes
```

For push, workspace resolution is `--workspace`, then the nearest `tdx.json` `engage_workspace`, then the session context. `--dry-run` reads companion files and reports the intended create/update actions without sending POST or PATCH requests. Normal push prompts for confirmation unless `--yes` is supplied.

Message type is immutable: a local popup cannot replace an existing inline message (or the reverse). Only `DRAFT` messages can be updated. Resolve these conditions in Engage before pushing; this command intentionally does not include lifecycle or deletion operations.

## Landing Pages

Landing Pages use a local static-site workflow. A page belongs to an Engage workspace and requires a Web Domain that has been attached to that workspace. The normal workflow is:

```bash
tdx engage domain create --domain-name pages.example.com
tdx engage domain attach pages.example.com --workspace "Marketing Team"
tdx engage landing-page init "Spring Launch" --domain pages.example.com --workspace "Marketing Team"
cd landing-pages/spring-launch
tdx engage landing-page validate
tdx engage landing-page push --dry-run
tdx engage landing-page push --yes
tdx engage landing-page publish "Spring Launch" --workspace "Marketing Team"
```

### Web Domains

Web Domains are account-scoped custom subdomains used to host Landing Pages. Create a domain and configure the DNS records shown by `show`; deployment must be active before a Landing Page can be published. Attach the domain to every workspace that will use it.

```bash
# Discover domain status and required DNS records
tdx engage domain list
tdx engage domain show pages.example.com

# Register and attach a domain
tdx engage domain create --domain-name pages.example.com --display-name "Marketing pages"
tdx engage domain attach pages.example.com --workspace "Marketing Team"

# Change display metadata or re-deploy a suspended domain
tdx engage domain update pages.example.com --display-name "Campaign pages"
tdx engage domain deploy pages.example.com

# Remove an unused association or domain
tdx engage domain detach pages.example.com --workspace "Marketing Team"
tdx engage domain delete pages.example.com --yes
```

### Local Project Format

`init` creates a project directory containing `tdx.json`, a `landing-page.yaml` definition, and a root `site/index.html` entrypoint. It does not overwrite an existing directory.

```text
landing-pages/spring-launch/
├── tdx.json
├── landing-page.yaml
└── site/
    └── index.html
```

```yaml
type: landing_page
name: Spring Launch
description: Spring campaign site
slug: campaigns/2026/spring
domain: ref:pages.example.com
site_dir: site
```

The definition supports only `type`, `name`, optional `description`/`slug`, `domain`, and `site_dir`. A slug uses lowercase letters, numbers, hyphens, and slash-separated path segments. The static site must contain `index.html`; it permits HTML, CSS, JavaScript, common image/font files, JSON, text, XML, and web manifests. Uploads are limited to 1,000 files, 20 MB per file, and 100 MB in total.

### Commands

```bash
# Create and locally validate a project
tdx engage landing-page init "Spring Launch" --domain pages.example.com --workspace "Marketing Team"
tdx engage landing-page validate [landing-page.yaml]

# Inspect pages in the workspace
tdx engage landing-page list --workspace "Marketing Team"
tdx engage landing-page list "spring*" --status DRAFT --workspace "Marketing Team"
tdx engage landing-page show "Spring Launch" --workspace "Marketing Team"

# Upload a local static site as a draft; it never publishes automatically
tdx engage landing-page push [landing-page.yaml] --workspace "Marketing Team" --dry-run
tdx engage landing-page push [landing-page.yaml] --workspace "Marketing Team" --yes

# Publish or unpublish explicitly
tdx engage landing-page publish "Spring Launch" --workspace "Marketing Team"
tdx engage landing-page unpublish "Spring Launch" --workspace "Marketing Team"

# Delete after unpublishing
tdx engage landing-page delete "Spring Launch" --workspace "Marketing Team" --yes
```

### Push a Local Site

`push` accepts a path to `landing-page.yaml`, not a ZIP archive or an HTML file. When the path is omitted, it uses `./landing-page.yaml`. The YAML `site_dir` identifies the directory to upload and must contain `<site_dir>/index.html` at its root. Workspace resolution is `--workspace`, then the nearest project `tdx.json`, then the session context.

```bash
# From a pulled or initialized project directory
tdx engage landing-page validate landing-page.yaml
tdx engage landing-page push landing-page.yaml --dry-run
tdx engage landing-page push landing-page.yaml --yes

# Or pass a definition path from another directory
tdx engage landing-page push ./landing-pages/spring-launch/landing-page.yaml --workspace "Marketing Team" --yes
```

### Pull a Landing Page

`pull` always retrieves the Landing Page configuration as `tdx.json` and `landing-page.yaml`. For a published page, it also reads the current published upload attempt's `webAssets` manifest and downloads every file in that manifest from the page's public URL, including files that are not referenced by HTML or CSS. For a draft or content-less page, it creates a configuration-only project with an empty `site/` directory; it does not retrieve a draft upload attempt or draft site files.

`pull` does not execute JavaScript, crawl navigation links or iframes, or retrieve historical versions. A published page with an invalid or unavailable published manifest fails rather than silently replacing the local site with a configuration-only project.

```bash
# Preview a pull without changing local files
tdx engage landing-page pull "Spring Launch" --workspace "Marketing Team" --dry-run

# Replace the project directory after confirmation
tdx engage landing-page pull "Spring Launch" --workspace "Marketing Team"

# Replace a chosen directory without confirmation
tdx engage landing-page pull "Spring Launch" --workspace "Marketing Team" --dir ./spring-launch --yes
```

Pull replaces the destination project directory, so files that are not in the pulled project are removed. Public-site crawler requests must **not** send TD API authorization headers, OAuth credentials, cookies, or any private session data.

## Workspaces

### List Workspaces

```bash
# List all workspaces
tdx engage workspace list
tdx engage workspaces              # alias for "workspace list"

# Filter workspaces by pattern
tdx engage workspace list "marketing*"
tdx engage workspace list "*_production"

# Limit results
tdx engage workspace list --limit 10
```

### Show Workspace Details

```bash
# Show workspace by name
tdx engage workspace show "Marketing Team"

# Show workspace by UUID
tdx engage workspace show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Show full JSON:API response with all fields (workspaceConfig, ownerUser, etc.)
tdx engage workspace show "Marketing Team" --full
```

### Create Workspace

```bash
# Create a workspace
tdx engage workspace create --name "Marketing Team"

# Create with description
tdx engage workspace create --name "Marketing Team" \
  --description "Workspace for marketing campaigns"
```

### Update Workspace

```bash
# Update workspace name
tdx engage workspace update "Marketing Team" --name "Marketing Department"

# Update workspace description
tdx engage workspace update "Marketing Team" --description "Updated description"

# Update by UUID
tdx engage workspace update "01968a3a-ae17-7ef6-9b45-f1a0af1224b4" --name "New Name"
```

### Delete Workspace

```bash
# Delete workspace by name (prompts for confirmation)
tdx engage workspace delete "Old Workspace"

# Delete workspace by UUID
tdx engage workspace delete "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Skip confirmation prompt
tdx engage workspace delete "Old Workspace" --yes
```

### Set Workspace Context

```bash
# Set workspace for the current session (alias for "tdx use engage_workspace")
tdx engage workspace use "Marketing Team"

# This is equivalent to:
tdx use engage_workspace "Marketing Team"
```

### Assign Workspace Resources

Parent Segments and Email Domains are stored in Workspace Configuration. These commands accept a resource name or ID and use the explicit `--workspace` option or the current `engage_workspace` session context.

```bash
# Assign or remove a Parent Segment
tdx engage workspace parent-segment attach "Customer 360" --workspace "Marketing Team"
tdx engage workspace parent-segment detach "Customer 360" --workspace "Marketing Team"

# Assign or remove a verified email send domain
tdx engage workspace email-domain attach "mail.example.com" --workspace "Marketing Team"
tdx engage workspace email-domain detach "mail.example.com" --workspace "Marketing Team" --yes
```

Detach operations prompt for confirmation; use `--yes` to skip the prompt. The Engage API can reject detaching a resource while active campaigns or senders still use it.

Sender Profiles can only be assigned to a Workspace when the profile is created. The Delivery API rejects attempts to change or clear `workspaceId` on an existing profile, so tdx does not provide Sender Profile attach/detach commands.

**Session Context:** Parent Segment or Email Domain operations can omit `--workspace` after running `tdx use engage_workspace <name>`.

## LINE Sender Accounts

LINE sender accounts are the LINE Official Account credentials used to send LINE messages. Manage them before creating LINE campaigns.

### List LINE Sender Accounts

```bash
# List all LINE sender accounts
tdx engage line-sender-account list

# Filter by pattern (glob wildcards supported)
tdx engage line-sender-account list "my-brand*"

# Filter by workspace
tdx engage line-sender-account list --workspace "Marketing Team"
```

### Show LINE Sender Account Details

```bash
# Show by name
tdx engage line-sender-account show "My LINE Account"

# Show by UUID
tdx engage line-sender-account show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"

# Specify workspace when using name
tdx engage line-sender-account show "My LINE Account" --workspace "Marketing Team"
```

## LINE Imagemap Images

Before creating a LINE imagemap campaign, upload the image to receive a `baseUrl`.

### Upload an Image

```bash
# Upload a JPEG image
tdx engage line-imagemap-image upload ./banner.jpg

# Upload a PNG image
tdx engage line-imagemap-image upload ./banner.png
```

**Constraints (enforced by LINE):**

| Constraint | Value |
|  --- | --- |
| Format | JPEG or PNG |
| Max file size | 10 MB |
| Min width | 1040 px |


**Example output:**

```
baseUrl: https://profile.line-scdn.net/0h...

Resized URLs:
  240px : https://profile.line-scdn.net/0h.../240
  300px : https://profile.line-scdn.net/0h.../300
  460px : https://profile.line-scdn.net/0h.../460
  700px : https://profile.line-scdn.net/0h.../700
 1040px : https://profile.line-scdn.net/0h.../1040

Use baseUrl in your campaign YAML:
  line:
    messages:
      - type: imagemap
        base_url: "https://profile.line-scdn.net/0h..."
```

Copy the `baseUrl` value into your campaign YAML `line.messages[].base_url` field.

All Engage commands support name-based selection in addition to UUIDs:

- **By Name**: `tdx engage campaign show "My Campaign"` - searches by exact name match
- **By UUID**: `tdx engage campaign show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4"` - direct ID lookup


When multiple resources share the same name across workspaces, use `--workspace` to narrow the search.

## Output Formats

All commands support standard tdx output formats:

```bash
# JSON output (default)
tdx engage campaign list

# Table format
tdx engage campaign list --format table

# TSV format for scripting
tdx engage campaign list --format tsv

# JSONL format for streaming
tdx engage campaign list --format jsonl
```

## Always-On Campaigns

Always-on campaigns run continuously (no scheduling) and support a different lifecycle from regular campaigns: DRAFT → LIVE → PAUSED → FINISHED.

All always-on campaign commands require a workspace context (`--workspace` or `tdx use engage_workspace`).

### List Always-On Campaigns

```bash
# List all always-on campaigns
tdx engage always-on-campaign list --workspace "Marketing Team"
tdx engage always-on-campaigns --workspace "Marketing Team"  # alias

# Filter by pattern
tdx engage always-on-campaign list "welcome*" --workspace "Marketing Team"

# Filter by type or status
tdx engage always-on-campaign list --workspace "Marketing Team" --type email
tdx engage always-on-campaign list --workspace "Marketing Team" --status LIVE

# Combine filters
tdx engage always-on-campaign list --workspace "Marketing" --type email --status DRAFT
```

### Show Always-On Campaign Details

```bash
# Show campaign by name
tdx engage always-on-campaign show "My Campaign" --workspace "Marketing Team"

# Show campaign by UUID
tdx engage always-on-campaign show "01968a3a-ae17-7ef6-9b45-f1a0af1224b4" --workspace "Marketing Team"

# Show full JSON:API response
tdx engage always-on-campaign show "My Campaign" --workspace "Marketing Team" --full
```

### Create Always-On Campaign

```bash
# Create an email always-on campaign
tdx engage always-on-campaign create --workspace "Marketing Team" \
  --name "Welcome Flow" --type email

# Create with description and audience targeting
tdx engage always-on-campaign create --workspace "Marketing Team" \
  --name "Welcome Flow" --type email \
  --description "Automated welcome emails" \
  --segment "My Audience/New Users"

# Create a push campaign with UTM tracking
tdx engage always-on-campaign create --workspace "Marketing Team" \
  --name "Daily Digest" --type push \
  --enable-utm-tracking
```

### Update Always-On Campaign

```bash
# Update name or description
tdx engage always-on-campaign update "Welcome Flow" --workspace "Marketing Team" \
  --name "New Welcome Flow"

tdx engage always-on-campaign update "Welcome Flow" --workspace "Marketing Team" \
  --description "Updated description"

# Update audience targeting
tdx engage always-on-campaign update "Welcome Flow" --workspace "Marketing Team" \
  --segment "My Audience/Premium Users"
```

### Delete Always-On Campaign

Only DRAFT campaigns can be deleted.

```bash
# Delete with confirmation prompt
tdx engage always-on-campaign delete "Draft Campaign" --workspace "Marketing Team"

# Skip confirmation
tdx engage always-on-campaign delete "Draft Campaign" --workspace "Marketing Team" --yes
```

### Always-On Campaign Lifecycle Commands

```bash
# Launch (DRAFT → LIVE)
tdx engage always-on-campaign launch "Welcome Flow" --workspace "Marketing Team"

# Pause (LIVE → PAUSED)
tdx engage always-on-campaign pause "Welcome Flow" --workspace "Marketing Team"

# Resume (PAUSED → LIVE)
tdx engage always-on-campaign resume "Welcome Flow" --workspace "Marketing Team"

# Finish permanently (prompts for confirmation)
tdx engage always-on-campaign finish "Welcome Flow" --workspace "Marketing Team"
tdx engage always-on-campaign finish "Welcome Flow" --workspace "Marketing Team" --yes
```

### Duplicate Always-On Campaign

```bash
# Duplicate with a new name (required)
tdx engage always-on-campaign duplicate "Welcome Flow" --workspace "Marketing Team" \
  --name "Welcome Flow Copy"
```

### Validate Always-On Campaign YAML Files

Validate YAML files locally without authentication. Useful for checking syntax and required fields before pushing.

```bash
# Validate YAML files in a directory
tdx engage always-on-campaign validate ./campaigns

# Verbose mode shows all files including valid ones
tdx engage always-on-campaign validate ./campaigns --verbose

# Validate specific file
tdx engage always-on-campaign validate campaign.yaml
```

### Pull Always-On Campaigns to YAML

Export always-on campaigns from Engage API to local YAML files for editing.

```bash
# Pull all campaigns from a workspace
tdx engage always-on-campaign pull --workspace "Marketing Team"

# Pull campaigns with filter by name pattern
tdx engage always-on-campaign pull --workspace "Marketing Team" --name "welcome*"

# Pull campaigns by type (email, push, line, sms)
tdx engage always-on-campaign pull --workspace "Marketing Team" --type email

# Dry-run mode shows what would be pulled without writing files
tdx engage always-on-campaign pull --workspace "Marketing Team" --dry-run
```

**Output directory structure:**

```
always-on-campaigns/
├── Marketing Team/
│   ├── tdx.json                          # Workspace config
│   ├── welcome-email.yaml                # Campaign definition
│   ├── welcome-email.html                # Email HTML template (if email type)
│   ├── welcome-email.txt                 # Email plaintext (optional)
│   └── daily-push.yaml                   # Push campaign
```

### Push Always-On Campaigns from YAML

Create or update always-on campaigns from local YAML files.

```bash
# Push a campaign YAML file (creates new or updates existing by name match)
tdx engage always-on-campaign push campaign.yaml --workspace "Marketing Team"

# Push all campaigns in a directory
tdx engage always-on-campaign push ./campaigns --workspace "Marketing Team"

# Dry-run mode shows what would be applied without creating/updating
tdx engage always-on-campaign push campaign.yaml --workspace "Marketing Team" --dry-run

# Skip confirmation prompts
tdx engage always-on-campaign push campaign.yaml --workspace "Marketing Team" --yes
```

**YAML File Format — Always-On Campaign:**

Always-on campaigns support email, push, LINE, and SMS content types. Unlike one-off campaigns, they run continuously without scheduling.

```yaml
type: always_on_campaign
name: Welcome Series
campaign_type: email
description: Automated welcome emails
workspace: Marketing Team # optional if set via --workspace
audience: ref:New Users # ref: prefix required

email:
  subject: 'Welcome to {{company}}!'
  preheader: 'Everything you need to get started'
  template: ref:Welcome Template # ref: prefix required
  html_file: welcome.html # relative to YAML file
  sender_id: ref:Sender ID # ref: prefix required, required for launch

# For push campaigns:
push:
  title: Daily Digest
  message: Check out today's updates
  image_url: https://example.com/image.png

# For LINE campaigns:
line:
  line_sender_account_id: ref:LINE Sender
  messages:
    - type: textV2
      text: Hello {{profile.first_name}}!

# For SMS campaigns (Always-On specific):
sms:
  sender_id: ACME # SMS sender ID
  text: Your code is {{code}}
  send_window_timezone: Asia/Tokyo # always-on: required for launch
  send_window_start: '09:00:00' # always-on: required for launch
  send_window_end: '21:00:00' # always-on: required for launch
  send_window_blocked_weekdays: # optional
    - Sunday
    - Saturday

# Optional sections:
utm:
  enabled: true
  id: campaign-001
  medium: email
  source: treasuredata
```

`email.preheader` is optional inbox preview text. It supports Liquid tags, is limited to 220 characters by the API, and accepts `null` to clear an existing value.

**Content Types Supported:**

| Type | Required fields | Email template | Launch requirements |
|  --- | --- | --- | --- |
| `email` | `subject`, `html_file`, `template` | HTML file in same directory | `sender_id` |
| `push` | `title`, `message` | — | (none) |
| `line` | `line_sender_account_id`, `messages` | — | (none) |
| `sms` | `sender_id`, `text` | — | `sender_id`, `text`, `send_window_timezone`, `send_window_start`, `send_window_end` |


### Always-On Campaign Status Reference

| Status | Description |
|  --- | --- |
| `DRAFT` | Campaign is being prepared, not yet launched |
| `LIVE` | Campaign is actively running |
| `PAUSED` | Campaign is temporarily paused |
| `FINISHED` | Campaign has been permanently completed |


## Campaign Status Reference

| Status | Description |
|  --- | --- |
| `DRAFT` | Campaign is being prepared, not yet launched |
| `ACTIVE` | Campaign is currently running |
| `PAUSED` | Campaign is temporarily paused |
| `COMPLETED` | Campaign has finished execution |


## Campaign Types

| Type | Description |
|  --- | --- |
| `email` | Email marketing campaigns |
| `push` | Push notification campaigns |
| `line` | LINE messaging campaigns |


## LINE Campaign YAML Reference

LINE campaigns use a `line:` section in the campaign YAML. Two message types are supported.

### textV2 — Text Message

```yaml
type: campaign
name: My LINE Campaign
campaign_type: line

line:
  line_sender_account_id: '<sender-uuid>' # From: tdx engage line-sender-account list
  messages:
    - type: textV2
      text: 'Hello {{profile.first_name}}! Check out our latest offer.'
```

### imagemap — Image with Tappable Areas

Upload the image first with `tdx engage line-imagemap-image upload`, then use the returned `baseUrl`:

```yaml
type: campaign
name: My LINE Imagemap Campaign
campaign_type: line

line:
  line_sender_account_id: '<sender-uuid>'
  messages:
    - type: imagemap
      base_url: 'https://profile.line-scdn.net/0h...' # From: tdx engage line-imagemap-image upload
      alt_text: 'Tap to explore our offers'
      base_size:
        width: 1040
        height: 1040
      actions:
        # URI action — opens a URL
        - type: uri
          link_uri: 'https://example.com/offer'
          label: 'View Offer'
          area: { x: 0, y: 0, width: 1040, height: 1040 }
```

**Action types:**

| Type | Required fields | Description |
|  --- | --- | --- |
| `uri` | `link_uri`, `area` | Opens a URL when tapped |
| `message` | `text`, `area` | Sends `text` as a message from the user *(not yet supported)* |
| `clipboard` | `clipboard_text`, `area` | Copies `clipboard_text` to the user's clipboard *(not yet supported)* |


The `uri` action supports an optional `label` field.

**`base_size` constraints:** `width` must be `1040`. `height` can be any value, but LINE recommends a 1:1 ratio (1040×1040).

**Limits:** Up to 5 messages per `line:` section; up to 50 actions per imagemap message.

### Workflow

```bash
# 1. Find your LINE sender account
tdx engage line-sender-account list --workspace "Marketing Team"

# 2. Upload imagemap image (if using imagemap type)
tdx engage line-imagemap-image upload ./banner.jpg

# 3. Write campaign YAML (use baseUrl from step 2)

# 4. Validate locally
tdx engage campaign validate my-line-campaign.yaml

# 5. Push to API (dry-run first)
tdx engage campaign push my-line-campaign.yaml --dry-run
tdx engage campaign push my-line-campaign.yaml --yes
```

## Media (DAM)

Manage images and assets in the Engage Digital Asset Management (DAM) storage.

```bash
# List root directory
tdx engage media ls

# List a specific folder
tdx engage media ls uploads/images

# Find recent matching files, newest first
tdx engage media ls uploads/images --name hero --node-type media_file --date-range last_30_days --sort=-updatedAt

# Page through files uploaded by a specific user
# --limit is a global option; DAM accepts values from 1 through 100
tdx engage media ls --uploaded-by "Admin User" --limit 20 --offset 20

# Upload an image (JPEG, PNG, GIF supported)
tdx engage media upload ./banner.jpg
tdx engage media upload ./banner.jpg --path uploads/images
tdx engage media upload ./banner.jpg --path uploads --conflict replace

# Create a directory
tdx engage media mkdir uploads/images

# Delete a file
tdx engage media rm uploads/images/banner.jpg

# Delete a directory (must be empty)
tdx engage media rmdir uploads/images
```

### Filtering and pagination for `media ls`

`tdx engage media ls [path]` supports server-side filtering, sorting, and pagination:

| Option | Values | Description |
|  --- | --- | --- |
| `--name <text>` | Any text | Case-insensitive partial match on asset or folder name. |
| `--node-type <type>` | `media_file`, `media_folder` | Restrict results to files or folders. |
| `--uploaded-by <name>` | Any text | Case-insensitive partial match on the uploader name. This filter returns files only. |
| `--date-range <range>` | `last_7_days`, `last_30_days`, `all` | Restrict results by upload date. |
| `--sort <order>` | `updatedAt`, `-updatedAt`, `name`, `-name` | Sort results. Prefix with `-` for descending order; use `--sort=-updatedAt` when the value starts with `-`. |
| `--limit <number>` | `1`–`100` | Maximum matching items to return. This is a global option. |
| `--offset <number>` | Non-negative integer | Number of matching items to skip before returning results. |


JSON and JSONL output include the API's `paginationMeta.total` and page navigation links, in addition to the directory metadata and listed items.

### Conflict strategies for `media upload`

| Strategy | Behavior |
|  --- | --- |
| `ask` (default) | Fails with HTTP 409 if the file already exists |
| `replace` | Overwrites the existing file |
| `keep` | Auto-renames to avoid conflict (e.g. `image_20240427103045.jpg`) |