--- name: fullstory-data-scoping-decoration version: v2 description: 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. related_skills: - fullstory-page-properties - fullstory-element-properties - fullstory-user-properties - fullstory-analytics-events - fullstory-identify-users - fullstory-getting-started - fullstory-privacy-controls - fullstory-privacy-strategy - fullstory-banking - fullstory-ecommerce - fullstory-gaming - fullstory-healthcare - fullstory-saas - fullstory-travel - fullstory-media-entertainment --- # 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!) ``` ✅ **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 ``` ### 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
``` ### 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 ``` ```javascript // ❌ WRONG: Entity data duplicated on elements ``` **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
...
``` ```html
data-results-count="50" > ``` --- ### 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