# Fullstory Data Scoping Decoration

> Strategic meta-skill for Fullstory data semantic decoration. Teaches when to use page properties vs element properties vs user properties vs events. Provides a deterministic framework for scoping data at the right level to maximize searchability, avoid duplication, and maintain consistency. Essential foundation for proper Fullstory implementation across all API types.

- Skill: `majiayu000/fullstory-data-scoping-decoration-2` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/fullstory-data-scoping-decoration-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/fullstory-data-scoping-decoration-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/majiayu000/fullstory-data-scoping-decoration-2

---


# Universal Data Scoping and Decoration: The Unified Model (v4.0)

> A deterministic strategy for applying high-signal data attributes across web interfaces. Separate user context from page context from element context to maximize efficiency, searchability, and consistency.

---

## Overview

This meta-skill provides the strategic framework for deciding **where** to capture data in Fullstory. Before implementing any Fullstory API, use this guide to determine the appropriate scope for your data.

### The Data Hierarchy

```
┌─────────────────────────────────────────────────────────────┐
│  USER PROPERTIES (FS setIdentity / setProperties)           │
│  Scope: Across all sessions for this user                   │
│  Examples: plan, role, company, signup_date                 │
├─────────────────────────────────────────────────────────────┤
│  PAGE PROPERTIES (FS setProperties type: 'page')            │
│  Scope: Current page until URL path changes                 │
│  Examples: pageName, searchTerm, filters, productId         │
├─────────────────────────────────────────────────────────────┤
│  ELEMENT PROPERTIES (data-fs-properties-schema)             │
│  Scope: Individual element interactions                     │
│  Examples: itemId, position, variant, price                 │
├─────────────────────────────────────────────────────────────┤
│  EVENT PROPERTIES (FS trackEvent)                           │
│  Scope: Single discrete action                              │
│  Examples: orderId, revenue, source, action                 │
└─────────────────────────────────────────────────────────────┘
```

---

## I. Core Principles: Data Scope and Responsibility

### Rule 1: Capture Data at the Highest Relevant Scope

**Why?**

- Reduces duplication
- Improves searchability
- Makes data inheritance work correctly
- Simplifies semantic decoration

**Decision Matrix:**

| Data Characteristic           | Scope to Use       | API                                   |
| ----------------------------- | ------------------ | ------------------------------------- |
| Same for all user sessions    | User Properties    | `setIdentity` / `setProperties(user)` |
| Same for entire page          | Page Properties    | `setProperties(page)`                 |
| Different per element on page | Element Properties | `data-fs-*` attributes                |
| Discrete action/moment        | Event              | `trackEvent`                          |

### Rule 2: Never Duplicate Across Scopes

❌ **BAD**: Product ID on page AND on every element

```javascript
// Page properties
FS('setProperties', {type: 'page', properties: {productId: 'SKU-123'}})

// Also on element (REDUNDANT!)
;<button data-product-id="SKU-123">Add to Cart</button>
```

✅ **GOOD**: Product ID at page level only

```javascript
// Page properties (single source of truth)
FS('setProperties', {type: 'page', properties: {productId: 'SKU-123'}})

// Element just has element-specific data
;<button data-fs-element="Add to Cart Button">Add to Cart</button>
```

### Rule 3: Let Inheritance Work

Fullstory's property inheritance:

- **User properties** → Available on all sessions for that user
- **Page properties** → Available on all elements/events on that page
- **Element properties** → Inherited by child elements, AND child properties bubble up to parent on interaction

**Important: Child → Parent Inheritance**

When a user interacts with a parent element (e.g., clicks a form submit button), Fullstory captures properties from ALL child elements that have been interacted with:

```html
<form data-fs-element="CheckoutForm">
  <select
    data-fs-element="ShippingMethod"
    data-fs-properties-schema='{"value": {"type": "str", "name": "shipping"}}'
  >
    <option value="standard">Standard</option>
    <option value="express">Express</option>
  </select>
  <button type="submit">Place Order</button>
</form>
<!-- When form is submitted, Fullstory captures shipping selection from child -->
```

### Rule 4: Consider Privacy at Each Scope

Different scopes have different privacy implications:

| Scope                  | Privacy Consideration                        | Recommendation                        |
| ---------------------- | -------------------------------------------- | ------------------------------------- |
| **User Properties**    | Persists across sessions, linked to identity | Hash/tokenize PII; use internal IDs   |
| **Page Properties**    | Visible in any session replay on this page   | Mask sensitive page context           |
| **Element Properties** | Captured on interaction                      | Use `fs-exclude` for sensitive inputs |
| **Events**             | Logged with timestamp                        | Never include PII in event properties |

```javascript
// ✅ GOOD: Privacy-conscious scoping
FS('setIdentity', {
  uid: 'usr_abc123', // Internal ID, not email
  properties: {
    plan: 'enterprise',
    account_age_days: 365,
    // NO: email, name, phone
  },
})

FS('setProperties', {
  type: 'page',
  properties: {
    pageName: 'Account Settings',
    section: 'billing',
    // NO: account balance, card details
  },
})
```

> **Reference**: See `fullstory-privacy-controls` for fs-exclude/fs-mask/fs-unmask and `fullstory-privacy-strategy` for comprehensive privacy guidance.

### Rule 5: Anonymous Users Can Have Properties

User properties work for **anonymous users** (before `setIdentity` is called). Properties persist via the `fs_uid` first-party cookie and transfer when the user later identifies:

```javascript
// Anonymous user lands on site
FS('setProperties', {
  type: 'user',
  properties: {
    landing_page: '/pricing',
    referral_source: 'google_ads',
    campaign: 'spring_promo',
  },
})

// Later, user signs up - properties transfer automatically
FS('setIdentity', {
  uid: 'usr_newuser123',
  properties: {
    signup_date: '2024-01-15',
  },
})
// Now user has: landing_page, referral_source, campaign, AND signup_date
```

---

## II. Scope Selection by Scenario

### Scenario A: Single-Entity Detail Pages (1:1)

**Definition**: A page dedicated to one unique entity (product detail, flight itinerary, policy document).

**Strategy**: Entity attributes become **Page Properties**

```javascript
// ✅ CORRECT: Entity data at page level
FS('setProperties', {
  type: 'page',
  properties: {
    pageName: 'Product Detail',
    productId: 'SKU-123',
    productName: 'Wireless Headphones',
    category: 'Electronics',
    price: 199.99,
    inStock: true
  }
});

// ✅ CORRECT: Elements just have element-specific data
<button data-fs-element="Add to Cart">Add to Cart</button>
<button data-fs-element="Buy Now">Buy Now</button>
```

```javascript
// ❌ WRONG: Entity data duplicated on elements
<button
  data-fs-element="Add to Cart"
  data-product-id="SKU-123" // REDUNDANT - already at page level
  data-product-name="Wireless..." // REDUNDANT
>
  Add to Cart
</button>
```

**Why This Works:**

- All interactions inherit product context
- Search by "clicks on Product Detail page where price > 100" works
- No duplication, cleaner implementation

---

### Scenario B: Multi-Entity Listing Pages (1:Many)

**Definition**: Pages showing multiple distinct entities (search results, product grid, job listings).

**Strategy**:

- Search/filter context → **Page Properties**
- Individual item context → **Element Properties**

```javascript
// ✅ CORRECT: Search context at page level
FS('setProperties', {
  type: 'page',
  properties: {
    pageName: 'Search Results',
    searchTerm: 'wireless headphones',
    resultsCount: 50,
    sortBy: 'relevance',
    activeFilters: ['Electronics', 'In Stock'],
  },
})
```

```html
<!-- ✅ CORRECT: Item-specific data on elements -->
<div
  data-product-id="SKU-123"
  data-product-name="Wireless Headphones"
  data-price="199.99"
  data-position="1"
  data-fs-properties-schema='{
    "data-product-id": {"type": "str", "name": "productId"},
    "data-product-name": {"type": "str", "name": "productName"},
    "data-price": {"type": "real", "name": "price"},
    "data-position": {"type": "int", "name": "position"}
  }'
  data-fs-element="Product Card"
>
  ...
</div>
```

```html
<!-- ❌ WRONG: Search context duplicated on elements -->
<div
  data-product-id="SKU-123"
  data-search-term="wireless headphones"  <!-- REDUNDANT - page level -->
  data-results-count="50"                  <!-- REDUNDANT - page level -->
>
```

---

### Scenario C: User Attributes

**Definition**: Data about WHO the user is (not what they're doing).

**Strategy**: Use **User Properties** via `setIdentity` or `setProperties(user)`

```javascript
// ✅ CORRECT: User attributes at user level
FS('setIdentity', {
  uid: user.id,
  properties: {
    displayName: user.name,
    email: user.email,
    plan: 'enterprise',
    role: 'admin',
    companyName: user.company,
    signupDate: user.createdAt,
  },
})
```

```javascript
// ❌ WRONG: User attributes in page/element properties
FS('setProperties', {
  type: 'page',
  properties: {
    userPlan: 'enterprise', // WRONG SCOPE - use user properties
    userRole: 'admin', // WRONG SCOPE
  },
})
```

---

### Scenario D: Discrete Actions (Events)

**Definition**: Something that **happened** at a point in time.

**Strategy**: Use **trackEvent** with action-specific properties

```javascript
// ✅ CORRECT: Action captured as event
FS('trackEvent', {
  name: 'Product Added to Cart',
  properties: {
    quantity: 2, // Action-specific
    addedFrom: 'quick-view', // Action-specific
    // productId inherited from page properties
    // userId inherited from user properties
  },
})
```

```javascript
// ❌ WRONG: Trying to track actions via properties
FS('setProperties', {
  type: 'user',
  properties: {
    lastAddedProduct: 'SKU-123', // Events shouldn't be properties
    lastAddedQuantity: 2,
    lastAddedTime: Date.now(),
  },
})
```

---

## III. Decision Flowchart

```
START: You have data to capture
          │
          ▼
    Is this data about WHO the user is?
    (plan, role, company, signup date)
          │
    YES ──┴── NO
     │         │
     ▼         ▼
  USER      Is this a discrete action/moment?
  PROPS     (purchase, signup, feature used)
               │
         YES ──┴── NO
          │         │
          ▼         ▼
       EVENT    Is this data the same for the entire page?
                (search term, product on detail page)
                      │
                YES ──┴── NO
                 │         │
                 ▼         ▼
              PAGE      ELEMENT
              PROPS     PROPS
```

---

## IV. Common Patterns by Industry

> **Detailed guidance**: See industry-specific skills for comprehensive implementation patterns.

### E-commerce (`fullstory-ecommerce`)

| Data Point           | Scope            | Implementation        |
| -------------------- | ---------------- | --------------------- |
| User's loyalty tier  | User Property    | `setIdentity`         |
| Search term          | Page Property    | `setProperties(page)` |
| Product ID (on PDP)  | Page Property    | `setProperties(page)` |
| Product ID (in grid) | Element Property | `data-fs-*`           |
| Cart value           | Page Property    | `setProperties(page)` |
| Purchase completed   | Event            | `trackEvent`          |

### SaaS (`fullstory-saas`)

| Data Point             | Scope            | Implementation        |
| ---------------------- | ---------------- | --------------------- |
| User role              | User Property    | `setIdentity`         |
| Team/org ID            | User Property    | `setIdentity`         |
| Dashboard being viewed | Page Property    | `setProperties(page)` |
| Report ID in list      | Element Property | `data-fs-*`           |
| Feature used           | Event            | `trackEvent`          |
| Setting changed        | Event            | `trackEvent`          |

### Media & Entertainment (`fullstory-media-entertainment`)

| Data Point             | Scope            | Implementation        |
| ---------------------- | ---------------- | --------------------- |
| Subscriber status      | User Property    | `setIdentity`         |
| Content category       | Page Property    | `setProperties(page)` |
| Content ID (on detail) | Page Property    | `setProperties(page)` |
| Related content IDs    | Element Property | `data-fs-*`           |
| Video played           | Event            | `trackEvent`          |
| Content shared         | Event            | `trackEvent`          |

### Banking & Finance (`fullstory-banking`)

| Data Point         | Scope            | Implementation                      |
| ------------------ | ---------------- | ----------------------------------- |
| Account type       | User Property    | `setIdentity`                       |
| Current section    | Page Property    | `setProperties(page)`               |
| Transaction type   | Page Property    | `setProperties(page)`               |
| Account selector   | Element Property | `data-fs-*` (ID only, mask details) |
| Transfer completed | Event            | `trackEvent` (no amounts)           |
| MFA step           | Event            | `trackEvent`                        |

### Gaming (`fullstory-gaming`)

| Data Point               | Scope            | Implementation                |
| ------------------------ | ---------------- | ----------------------------- |
| Player tier (VIP status) | User Property    | `setIdentity`                 |
| Game lobby section       | Page Property    | `setProperties(page)`         |
| Active game ID           | Page Property    | `setProperties(page)`         |
| Game tile in grid        | Element Property | `data-fs-*`                   |
| Wager placed             | Event            | `trackEvent` (for compliance) |
| Game launched            | Event            | `trackEvent`                  |

### Healthcare (`fullstory-healthcare`)

| Data Point                      | Scope            | Implementation                 |
| ------------------------------- | ---------------- | ------------------------------ |
| User role (patient/provider)    | User Property    | `setIdentity`                  |
| Section (appointments, records) | Page Property    | `setProperties(page)`          |
| Flow step                       | Page Property    | `setProperties(page)`          |
| Form field (non-PHI only)       | Element Property | `data-fs-*` + `fs-exclude`     |
| Appointment scheduled           | Event            | `trackEvent` (no PHI)          |
| Form submitted                  | Event            | `trackEvent` (completion only) |

### Travel & Hospitality (`fullstory-travel`)

| Data Point               | Scope            | Implementation        |
| ------------------------ | ---------------- | --------------------- |
| Loyalty tier             | User Property    | `setIdentity`         |
| Search criteria          | Page Property    | `setProperties(page)` |
| Selected flight/hotel    | Page Property    | `setProperties(page)` |
| Flight option in results | Element Property | `data-fs-*`           |
| Booking completed        | Event            | `trackEvent`          |
| Ancillary added          | Event            | `trackEvent`          |

---

## V. Anti-Patterns to Avoid

### Anti-Pattern 1: Everything as User Properties

```javascript
// ❌ BAD: Transient state as user property
FS('setProperties', {
  type: 'user',
  properties: {
    currentPage: '/checkout', // Should be page property
    cartItems: 5, // Should be page property
    lastClickedButton: 'submit', // Should be event
  },
})
```

### Anti-Pattern 2: Everything as Events

```javascript
// ❌ BAD: State as events
FS('trackEvent', {name: 'User Is Premium', properties: {}}) // User property
FS('trackEvent', {name: 'Page Has 5 Results', properties: {}}) // Page property
```

### Anti-Pattern 3: Duplicating Hierarchy

```javascript
// ❌ BAD: Same data at multiple levels
FS('setIdentity', {uid: '123', properties: {plan: 'premium'}})
FS('setProperties', {type: 'page', properties: {userPlan: 'premium'}}) // DUP
FS('trackEvent', {name: 'Click', properties: {userPlan: 'premium'}}) // DUP
```

### Anti-Pattern 4: Over-Granular Element Properties

```javascript
// ❌ BAD: Too much data on elements when page context would work
<button
  data-page-name="Checkout"          // Should be page property
  data-user-id="123"                 // Should be user property
  data-cart-total="99.99"            // Should be page property
  data-fs-element="Submit">
```

---

## VI. Implementation Checklist

Before implementing, answer these questions:

- [ ] **Who is this data about?**
  - The user → User Properties
  - The page/context → Page Properties
  - A specific element → Element Properties
  - An action → Event

- [ ] **How long is this data relevant?**
  - Entire user lifetime → User Properties
  - This page view → Page Properties
  - This interaction → Element/Event Properties

- [ ] **Is this data already available at a higher scope?**
  - If yes → Don't duplicate, let inheritance work
  - If no → Add at appropriate scope

- [ ] **Can this data be searched/segmented?**
  - User properties → Segment users
  - Page properties → Find sessions with this page context
  - Element properties → Find clicks on elements with this data
  - Events → Funnel analysis, conversion tracking

---

## VII. Related Skills

### Core API Skills

| API                 | Skill Document                 |
| ------------------- | ------------------------------ |
| User Identification | `fullstory-identify-users`     |
| User Properties     | `fullstory-user-properties`    |
| Page Properties     | `fullstory-page-properties`    |
| Element Properties  | `fullstory-element-properties` |
| Events              | `fullstory-analytics-events`   |
| Privacy Controls    | `fullstory-privacy-controls`   |

### Industry Skills

| Industry              | Skill Document                  |
| --------------------- | ------------------------------- |
| Banking & Finance     | `fullstory-banking`             |
| E-commerce & Retail   | `fullstory-ecommerce`           |
| Gaming                | `fullstory-gaming`              |
| Healthcare            | `fullstory-healthcare`          |
| B2B SaaS              | `fullstory-saas`                |
| Travel & Hospitality  | `fullstory-travel`              |
| Media & Entertainment | `fullstory-media-entertainment` |

### Strategic Skills

| Topic            | Skill Document               |
| ---------------- | ---------------------------- |
| Getting Started  | `fullstory-getting-started`  |
| Privacy Strategy | `fullstory-privacy-strategy` |
| Stable Selectors | `fullstory-stable-selectors` |

---

## VIII. Version History

- **4.0** (Current)
  - Added YAML front matter for skill metadata
  - Integrated with core skill documents
  - Added decision flowchart
  - Expanded industry-specific patterns
  - Added explicit anti-patterns section
  - Aligned format with element-properties skill
- **3.0**
  - Generalized naming and examples
  - Added explicit Good/Bad implementation guides
  - Renamed properties for universal application

- **2.0**
  - Merged Scoping and Element Naming into one unified document

---

## Key Takeaways for Agent

When helping developers with data scoping:

1. **Always ask first**:
   - What data are you trying to capture?
   - Is this about the user, the page, an element, or an action?
   - Will this data be the same across multiple elements?
   - Is this data sensitive? What privacy level is needed?

2. **Common mistakes to watch for**:
   - User data in page properties
   - Page-level data duplicated on elements
   - Actions captured as properties instead of events
   - Same data at multiple scopes
   - PII in user properties without hashing
   - Forgetting that anonymous users can have properties

3. **Golden rules**:
   - Capture at the HIGHEST relevant scope
   - Let inheritance do the work (both parent→child AND child→parent)
   - User → Page → Element → Event (hierarchy)
   - Don't duplicate across scopes
   - Consider privacy at every scope

4. **Quick decision**:
   - WHO = User Properties (works for anonymous users too!)
   - WHERE = Page Properties
   - WHAT (specific item) = Element Properties
   - WHAT HAPPENED = Events

5. **Privacy quick reference**:
   - Sensitive user data → Hash/tokenize or use internal IDs
   - Sensitive page context → Consider masking
   - Sensitive elements → Use `fs-exclude`
   - Sensitive events → Never include PII in properties

---

_This meta-skill document provides strategic guidance for Fullstory data semantic decoration. Refer to individual API skill documents for detailed implementation examples._

