treasure-data

rt-setup-personalization

Complete workflow to set up RT 2.0 personalization from scratch - validates parent segment RT status, discovers event tables and attributes, configures RT infrastructure (events, attributes, ID stitching), creates personalization service, and deploys the personalization entity with payload. Use when user wants to "create realtime setup and build personalization" or "set up RT personalization end-to-end". Includes automatic validation using rt-personalization-validation skill to prevent common API errors.

treasure-data 19 24 Updated 3mo ago

Resources

1
GitHub

Install

npx skillscat add treasure-data/td-skills/rt-setup-personalization

Install via the SkillsCat registry.

SKILL.md

RT 2.0 Personalization Setup - Complete Workflow

Orchestrates the complete RT personalization setup: parent segment validation → use case discovery → data exploration → RT config → personalization service → personalization entity deployment.

Prerequisites

  • TD CLI installed: tdx
  • Authenticated: tdx auth setup
  • Parent segment created in Data Workbench
  • Master API key with full permissions: export TD_API_KEY=your_key

Workflow Overview

Complete 9-step workflow. Each step must complete successfully before proceeding.

Step 1: Validate Parent Segment ✓
Step 2: Use Case Discovery ✓
Step 3: Explore PS Attributes ✓
Step 4: Discover Event Tables ✓
Step 5: Define ID Stitching ✓
Step 6: Define RT Attributes ✓
Step 7: Configure RT Infrastructure ✓ (SHARED with rt-setup-triggers)
Step 8: Create Personalization Service ✓
Step 9: Create Personalization Entity ✓
  9a. Get Parent Segment Folder
  9b. Get Key Event and Attribute IDs
  9c. Validate Payload (REQUIRED - use rt-personalization-validation skill)
  9d. Create Entity via API
Step 10: Verify & Test ✓

Steps 1-2: Validate & Discover Use Case

See steps/01-02-validate-discovery.md for detailed implementation.

Quick Summary

Step 1: Validate parent segment exists and check RT status

# Check if PS has RT enabled
tdx ps rt list --json | jq '.[] | select(.id=="<ps_id>" or .name=="<ps_name>") | {
  id, name,
  rt_status: .status,
  event_tables: (.event_tables | length),
  key_events: (.key_events | length)
}'

Expected outputs:

  • rt_status: "ok" → RT enabled, proceed to Step 2
  • rt_status: "updating" → Wait for RT to be ready
  • Empty result → PS not RT-enabled. Show error: "RT not enabled for this parent segment. Contact CSM."

Step 2: Ask user about use case (Web Personalization, Cart Recovery, User Profile API, etc.)

Checkpoints:

  • ✓ Parent segment ID confirmed
  • ✓ RT status validated ("ok" or "not_configured")
  • ✓ Use case selected

Steps 3-6: Data Exploration

See steps/03-06-data-exploration.md for detailed implementation.

Quick Summary

Step 3: Explore parent segment batch attributes

# List all RT-enabled parent segments
tdx ps rt list --json

# Show RT-enabled parent segments with status
tdx ps rt list --json | jq '.[] | {
  id, name,
  rt_status: .status,
  event_tables: (.event_tables | length),
  key_events: (.key_events | length)
}'

Step 4: Discover streaming event tables

tdx tables "<event_db>.*" --json | jq -r '.[].name'

Step 5: Define ID stitching keys (td_client_id, email, canonical_id)

Step 6: Define RT attributes based on use case (single, list, counter types)

Checkpoints:

  • ✓ Batch attributes identified
  • ✓ Event tables discovered and selected
  • ✓ ID stitching keys defined
  • ✓ RT attributes planned (types and aggregations)

Step 7: Configure RT Infrastructure

SHARED CONFIGURATION - See steps/07-rt-config.md for complete implementation.

This step is identical for both RT Personalization and RT Triggers.

Quick Summary

7a. Configure Event Tables & Key Events

# Add event tables
tdx api "/audiences/<ps_id>/realtime_setting" --type cdp -X PATCH --data '{
  "eventTables": [{"database": "<db>", "table": "<table>"}]
}'

# Create key events
tdx api "/audiences/<ps_id>/realtime_key_events" --type cdp -X POST --data '{
  "name": "<event_name>",
  "databaseName": "<db>",
  "tableName": "<table>",
  "filterRule": {"type": "And", "conditions": []}
}'

7b. Configure ID Stitching

tdx api "/audiences/<ps_id>/realtime_setting" --type cdp -X PATCH --data '{
  "keyColumns": [
    {"name": "td_client_id", "validRegexp": null, "invalidTexts": [], "internal": false}
  ],
  "extLookupKey": "<primary_key>"
}'

7c. Create RT Attributes

# Single attribute
tdx api "/audiences/<ps_id>/realtime_attributes" --type cdp -X POST --data '{
  "name": "<attr_name>",
  "type": "single",
  "realtimeKeyEventId": "<key_event_id>",
  "valueColumn": "<column>",
  "dataType": "string",
  "duration": {"value": 1, "unit": "day"}
}'

# List attribute
tdx api "/audiences/<ps_id>/realtime_attributes" --type cdp -X POST --data '{
  "name": "<list_attr>",
  "type": "list",
  "realtimeKeyEventId": "<key_event_id>",
  "idColumn": "<id_column>",
  "maxItems": 100,
  "aggregations": [{
    "name": "items",
    "identifier": "items",
    "column": "<column>",
    "aggregationType": "distinct_list"
  }],
  "duration": {"value": 60, "unit": "day"}
}'

# Counter attribute
tdx api "/audiences/<ps_id>/realtime_attributes" --type cdp -X POST --data '{
  "name": "<counter_attr>",
  "type": "counter",
  "realtimeKeyEventId": "<key_event_id>",
  "counterType": "total",
  "increment": {"type": "const", "value": 1},
  "duration": {"value": 24, "unit": "hour"}
}'

Wait for RT status "ok":

while [ "$(tdx ps rt list --json | jq -r --arg ps '<ps_id>' '.[] | select(.id==$ps) | .status')" != "ok" ]; do
  echo "Waiting for RT infrastructure..."
  sleep 10
done
echo "✅ RT infrastructure ready"

Checkpoints:

  • ✓ Event tables configured
  • ✓ Key events created
  • ✓ ID stitching configured
  • ✓ RT attributes created
  • ✓ RT status is "ok"

⚠️ Validation & Common Errors

CRITICAL: Personalization entity creation has strict validation rules. The most common error is:

Error: "sections[0].payload.node_id.definition.attribute_payload": ["Attribute payload can't be blank"]

Root cause: Using empty arrays [] instead of null for unused fields.

Quick fix:

// ❌ FAILS
"stringBuilder": []

// ✅ WORKS
"stringBuilder": null

REQUIRED VALIDATION STEP:

At Step 9c, you MUST invoke the rt-personalization-validation skill to validate the payload before making the API call. This is a mandatory step in the workflow, not optional. The validation skill will:

  • Check for empty arrays that should be null
  • Verify payload structure completeness
  • Validate field names and uniqueness
  • Prevent all common API errors

Do not skip Step 9c validation. Proceeding directly to the API call without validation will likely result in errors.


Steps 8-9: Create Personalization

See steps/08-09-personalization.md for detailed implementation.

Quick Summary

Step 8: Create personalization service via tdx

cat > pz_service.yaml << 'EOF'
parent_segment_id: '<ps_id>'
personalization_service:
  name: '<use_case>_personalization'
  trigger_event: '<key_event_name>'
  sections:
    - name: 'Default'
      criteria: ''
      attributes: [last_product_viewed, page_views_24h]
EOF

tdx ps push pz_service.yaml -y

Step 9: Create personalization entity via API (with payload)

# Generate unique payload node ID
PAYLOAD_NODE_ID=$(uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]')

# Get folder ID
FOLDER_ID=$(curl -s "https://api-cdp.treasuredata.com/audiences/<ps_id>/folders" \
  -H "Authorization: TD1 ${TD_API_KEY}" | jq -r '.[0].id')

# Create entity
curl -X POST 'https://api-cdp.treasuredata.com/entities/realtime_personalizations' \
  -H "Authorization: TD1 ${TD_API_KEY}" \
  -H 'Content-Type: application/vnd.treasuredata.v1+json' \
  --data "{
    \"attributes\": {
      \"audienceId\": \"<ps_id>\",
      \"name\": \"<use_case>_personalization\",
      \"sections\": [{
        \"name\": \"Default_Section\",
        \"entryCriteria\": {
          \"keyEventCriteria\": {
            \"keyEventId\": \"<key_event_id>\",
            \"keyEventFilters\": {\"type\": \"And\", \"conditions\": []}
          }
        },
        \"payload\": {
          \"$PAYLOAD_NODE_ID\": {
            \"type\": \"ResponseNode\",
            \"definition\": {
              \"attributePayload\": [
                {\"realtimeAttributeId\": \"<attr_id>\", \"outputName\": \"last_product\"}
              ]
            }
          }
        }
      }]
    },
    \"relationships\": {
      \"parentFolder\": {\"data\": {\"id\": \"<folder_id>\", \"type\": \"folder-segment\"}}
    }
  }"

Checkpoints:

  • ✓ Service created
  • ✓ Entity deployed
  • ✓ Visible in Console
  • ✓ API endpoint available

Step 10: Verification

See steps/10-verification.md for complete verification checklist.

Verification Checklist

After setup completes, verify:

# 1. RT status is "ok"
tdx ps rt list --json | jq -r --arg ps "<ps_id_or_name>" '.[] | select(.id==$ps or .name==$ps) | .status'
# Expected: "ok"

# 2. Key events exist
tdx api "/audiences/<ps_id>/realtime_key_events" --type cdp | jq '.data | length'
# Expected: > 0

# 3. RT attributes exist
tdx api "/audiences/<ps_id>/realtime_attributes?page[size]=100" --type cdp | jq '.data | length'
# Expected: > 0

# 4. Configuration deployed
curl -s "https://api-cdp.treasuredata.com/entities/parent_segments/<ps_id>/realtime_personalizations" \
  -H "Authorization: TD1 ${TD_API_KEY}" | jq '.data | length'
# Expected: > 0

# 5. API endpoint responds
curl -X GET "https://${REGION}.p13n.in.treasuredata.com/audiences/<ps_id>/personalizations/<pz_id>?td_client_id=test_user" \
  -H "Authorization: TD1 ${TD_API_KEY}"
# Expected: JSON with attributes (not 404)

Console URL:

https://console-next.${REGION}.treasuredata.com/app/ps/<ps_id>/e/<pz_id>/p/de

If any check fails, review the corresponding setup step.


Summary

This orchestrator ensures all checkpoints are met:

  • Parent segment validated
  • Use case discovered
  • Data explored (batch attributes, event tables, IDs)
  • RT infrastructure configured (events, attributes, stitching)
  • Personalization service created
  • Personalization entity deployed
  • API endpoint tested

For RT Triggers (Journeys) instead, use the rt-setup-triggers skill which shares Steps 1-7.

Error Handling

RT not enabled:

❌ RT 2.0 is not enabled for this parent segment.
→ Contact your CSM to enable RT 2.0.

No RT-enabled parent segments:

❌ No RT-enabled parent segments found in your account.
→ Contact your CSM to enable RT 2.0 for a parent segment.

RT status "updating":

⚠️  RT configuration is updating. Waiting for status "ok"...
→ This typically takes 30-90 seconds.

Missing event tables:

❌ No streaming event tables found in database "<db>".
→ Verify database name or check if events are being ingested.