Documentation

API Reference

Complete API documentation for the WhatIf SDK 📚

API Reference

Complete reference for all classes, methods, and types in the WhatIf SDK.

Class: WhatIf

Main SDK class for initializing and controlling the widget.

Constructor

new WhatIf(projectId: string, baseUrl?: string, theme?: ThemeConfig)

Creates a new WhatIf instance.

Parameters:

NameTypeRequiredDescription
projectIdstring✅Your project ID from the dashboard
baseUrlstring❌API base URL (default: "/api")
themeThemeConfig❌Theme configuration object

Example:

const whatif = new WhatIf('your-app-id', '/api', {
  darkMode: 'auto',
  colors: { primary: '#3b82f6' }
});

Methods

ask()

Display a question widget with intelligent user tracking and question filtering.

ask(questionId: string, options?: AskOptions): Promise<void>

Parameters:

NameTypeRequiredDescription
questionIdstring✅Question ID from dashboard
optionsAskOptions❌User, organization, and context options

Returns: Promise<void>

Intelligent Features:

  • Smart Question Filtering: Backend automatically decides if user should see the question based on response history and question preferences
  • Response Threading: Tracks how individual users' feedback evolves over time across multiple iterations
  • Automatic Iteration Tracking: Maintains response history and enables trend analysis
  • Browser & Device Context: Automatically captures and displays enhanced browser/device information
  • Global Configuration: Set user/org data once during initialization for simplified usage

Example:

await whatif.ask('question-id', {
    user: {
    id: 'user_123',        // Required for smart filtering
    name: 'Sarah Chen',    // Optional - for dashboard display
    email: 'sarah@example.com', // Optional - for dashboard display
    avatar: 'https://example.com/avatar.jpg' // Optional - for dashboard display
  },
    org: {
    id: 'org_456',         // Optional - for organization tracking
    name: 'Acme Corp',     // Required if org provided - for dashboard display
    logo: 'https://example.com/logo.jpg' // Optional - for dashboard display
  },
  context: {
    // Custom metadata - any additional data you want to track
    page: 'checkout',
    feature: 'new-payment-flow',
    userType: 'premium'
  }
});

Automatic Data Collection: WhatIf automatically captures timestamp, browser info, device info, page info, and previous URL with every response - no code required! See the Context Guide for details.

Global Initialization

Set user and organization data once during SDK initialization for simplified usage across your app.

Constructor Options

const whatif = new WhatIf('your-project-id', '/api', {
  user: {
    id: 'user_123',        // Required for smart filtering
    name: 'Sarah Chen',    // Optional - for dashboard display
    email: 'sarah@example.com',
    avatar: 'https://example.com/avatar.jpg'
  },
  org: {
    id: 'org_456',         // Optional - for organization tracking
    name: 'Acme Corp',     // Required if org provided
    logo: 'https://example.com/logo.jpg'
  },
  context: {
    appVersion: '1.2.3',   // Custom metadata
    environment: 'production'
  }
});

Simplified Usage

Once initialized, you can use the simple API:

// Simple call - uses pre-configured user/org data
await whatif.ask('checkout-feedback');

// Override for specific cases
await whatif.ask('admin-feedback', {
  user: { id: 'admin_789', name: 'Admin User' }, // Override user
  context: { page: 'admin-panel' } // Additional context
});

Benefits:

  • ✅ Cleaner Code: No need to pass user/org data with every ask() call
  • ✅ Consistent Context: Same user/org data across all questions
  • ✅ Flexible Overrides: Still customize per call when needed
  • ✅ Backward Compatible: Existing code continues to work

Intelligent Question Filtering

WhatIf now includes smart backend logic that automatically decides whether to show questions to users based on their response history and question preferences.

Key Features:

  • Response History Tracking: The backend remembers which users have answered which questions
  • Question Preferences: Creators can set rules like "re-ask after 30 days" or "re-ask when content updates"
  • Manual Overrides: Creators can manually trigger "Ask Again" to show questions to all users
  • Response Threading: Tracks how individual feedback evolves across multiple interactions

How It Works:

  1. User Identification: Pass user.id in the ask() options
  2. Backend Decision: Server checks response history and preferences
  3. Smart Display: Widget only appears if the user should see the question
  4. Automatic Tracking: All responses are threaded and tracked by iteration

Dashboard Controls:

Creators can manage question visibility through the dashboard:

  • Re-asking Rules: Set time-based or update-based re-asking
  • Manual Overrides: Click "Ask Again" to immediately show questions to all users
  • Analytics: View response evolution and engagement metrics

setTheme()

Update the theme configuration at runtime.

setTheme(theme: ThemeConfig): void

Parameters:

NameTypeRequiredDescription
themeThemeConfig✅New theme configuration

Example:

whatif.setTheme({
  darkMode: 'dark',
  colors: { primary: '#10b981' }
});

remove()

Manually close the currently displayed widget.

remove(): void

Example:

whatif.remove();

destroy()

Clean up all SDK resources including event listeners and widgets.

destroy(): void

Always call destroy() when unmounting to prevent memory leaks.

Example:

// Vanilla JS
whatif.destroy();

// React
useEffect(() => {
  return () => whatif.destroy();
}, []);

React SDK

WhatIfProvider

Provider component that initializes and manages the SDK.

function WhatIfProvider(props: WhatIfProviderProps): JSX.Element

Props:

PropTypeRequiredDefaultDescription
projectIdstring✅-Your project ID
baseUrlstring❌"/api"API endpoint
userUser❌-Default user data for all questions
orgOrg❌-Default organization data for all questions
contextRecord<string, unknown>❌-Default custom metadata for all questions
themeThemeConfig❌-Theme configuration
childrenReactNode✅-Child components

Example:

<WhatIfProvider 
  projectId="your-project-id"
  user={{
    id: 'user_123',        // Required for smart filtering
    name: 'Sarah Chen',    // Optional display data
    email: 'sarah@example.com'
  }}
  org={{
    name: 'Acme Corp',     // Required if org provided
    logo: 'https://example.com/logo.jpg'
  }}
  context={{
    appVersion: '1.2.3',   // Custom metadata
    environment: 'production'
  }}
  theme={{ darkMode: 'auto' }}
>
  <App />
</WhatIfProvider>

useWhatIf

Hook that provides access to SDK methods. Automatically uses user/org/context data configured in the WhatIfProvider.

function useWhatIf(): {
  ask: (questionId: string, options?: AskOptions) => Promise<void>;
  setTheme: (theme: ThemeConfig) => void;
}

Returns:

PropertyTypeDescription
askFunctionTrigger a question (uses provider defaults)
setThemeFunctionUpdate theme

Key Features:

  • Automatic Data: Uses user/org/context data from WhatIfProvider
  • Override Support: Pass options to customize per call
  • Smart Filtering: Backend automatically decides question visibility

Examples:

// Simple usage - uses provider configuration
const { ask } = useWhatIf();

<button onClick={() => ask('feedback-question')}>
  Give Feedback
</button>

// Override for specific cases
<button onClick={() => ask('admin-question', {
  user: { id: 'admin-user', name: 'Admin' },
  context: { page: 'admin-panel' }
})}>
  Admin Feedback
</button>

Data Inheritance & User ID Requirement

Provider Data Inheritance

The SDK automatically inherits user/org/context data from the WhatIfProvider, but you can override it per question:

Provider Setup:

<WhatIfProvider
  projectId="project-id"
  user={{ id: 'default-user', name: 'John', email: 'john@example.com', avatar: 'avatar.jpg' }}
  org={{ name: 'Acme Corp', logo: 'logo.png' }}
  context={{ app: 'my-app' }}
>
  {/* All questions inherit this data */}
</WhatIfProvider>

Automatic Inheritance:

const { ask } = useWhatIf();

// ✅ Inherits all provider data
ask('question-id');

Selective Overrides:

const { ask } = useWhatIf();

// ✅ Inherits user/org, overrides context
ask('checkout-question', {
  context: { page: 'checkout', step: 'payment' }
});

// ✅ Overrides user, inherits org/context
ask('admin-feedback', {
  user: { id: 'admin-user', name: 'Admin' }
});

User ID Requirement

⚠️ Critical: Always provide a user.id to enable smart question filtering and prevent repeated questions:

// ✅ Good - enables question filtering
<WhatIfProvider user={{
  id: 'user123',           // Required for filtering
  name: 'John Doe',        // Optional for display
  email: 'john@example.com', // Optional for display
  avatar: 'avatar.jpg'     // Optional for display
}}>
  <App />
</WhatIfProvider>

// ❌ Bad - questions will show repeatedly
<WhatIfProvider user={{
  name: 'John Doe'         // Missing id - no filtering possible
}}>
  <App />
</WhatIfProvider>

Why it matters: Without user.id, WhatIf cannot track which users have seen questions, causing the same question to appear every time the page loads.

Changelog

v1.1.0 (Latest)

  • New: Global user/org/context initialization - set once, use everywhere
  • New: Simplified API - ask('question-id') with pre-configured defaults
  • New: Override capability - still customize per call when needed

v1.0.0

  • New: Smart question filtering based on user response history and question preferences
  • New: Response threading and iteration tracking for individual users
  • New: Manual "Ask Again" functionality for dashboard users
  • New: Enhanced browser and device context parsing and display
  • New: Question preferences system (re-asking rules, time-based and update-based filtering)
<WhatIfProvider user={{
  id: 'user-123',
  name: 'John'          // Optional display data
}}
org={{
  name: 'Acme Corp'     // Required if org provided
}}
context={{
  customData: 'value'   // Only custom metadata now
}}>
  <App />
</WhatIfProvider>

## Types

### ThemeConfig

Complete theme configuration object.

```typescript
interface ThemeConfig {
  darkMode?: DarkMode;
  position?: WidgetPosition;
  colors?: ColorPalette;
  typography?: Typography;
  spacing?: Spacing;
  border?: BorderConfig;
  shadow?: ShadowConfig;
  animation?: AnimationConfig;
  customCss?: string;
}

See Theming Guide →

DarkMode

Dark mode strategy.

type DarkMode = 'auto' | 'light' | 'dark' | 'disabled';
  • 'auto' - Auto-detect system preference
  • 'light' - Force light mode
  • 'dark' - Force dark mode
  • 'disabled' - No dark mode support (default)

See Dark Mode Guide →

WidgetPosition

Widget position on screen.

type WidgetPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';

ColorPalette

Color configuration for the widget.

interface ColorPalette {
  primary?: string;
  primaryHover?: string;
  background?: string;
  textPrimary?: string;
  textSecondary?: string;
  border?: string;
  borderHover?: string;
  hoverBackground?: string;
  closeButton?: string;
  closeButtonHover?: string;
  ratingFilled?: string;
  ratingEmpty?: string;
}

All colors accept:

  • Hex: #3b82f6
  • RGB: rgb(59, 130, 246)
  • RGBA: rgba(59, 130, 246, 0.5)
  • CSS variables: var(--primary-color)

Typography

Typography configuration.

interface Typography {
  fontFamily?: string;
  questionSize?: string;
  questionWeight?: string;
  inputSize?: string;
  buttonSize?: string;
  buttonWeight?: string;
}

Size values: Any valid CSS size (16px, 1rem, 1.5em)
Weight values: '400', '500', '600', '700', '800', 'bold', etc.

Spacing

Spacing and layout configuration.

interface Spacing {
  padding?: string;
  gap?: string;
  offset?: string;
}

All values: Any valid CSS size (16px, 1rem, 20px, etc.)

BorderConfig

Border styling configuration.

interface BorderConfig {
  radius?: string;
  buttonRadius?: string;
  inputRadius?: string;
  width?: string;
}

Radius values: '4px', '8px', '12px', '24px', '50%', etc.
Width values: '1px', '2px', etc.

ShadowConfig

Shadow effect configuration.

interface ShadowConfig {
  enabled?: boolean;
  container?: string;
}

Container value: Any valid CSS box-shadow value

Examples:

'0 4px 12px rgba(0,0,0,0.08)'
'0 8px 24px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08)'

AnimationConfig

Animation configuration.

interface AnimationConfig {
  enabled?: boolean;
  duration?: number;
  easing?: string;
}

Duration: Milliseconds (200, 400, 600)
Easing: Any CSS easing function

Common easing functions:

'ease'
'ease-in-out'
'cubic-bezier(0.16, 1, 0.3, 1)'
'cubic-bezier(0.68, -0.55, 0.265, 1.55)' // Bouncy

AskOptions

Options for the ask() method. The user object enables intelligent question filtering and response tracking.

interface AskOptions {
  user?: {
    id: string;        // For tracking/identification
    name?: string;     // For display
    email?: string;    // For display
    avatar?: string;   // For display
  };
  org?: {
    id?: string;       // For identification
    name: string;      // For display
    logo?: string;     // For display
  };
  context?: Record<string, unknown>;  // Custom metadata only
  userFingerprint?: string;           // Fallback identification
}

Properties:

NameTypeDescription
userobjectUser identification and display data
orgobjectOrganization identification and display data
contextRecord<string, unknown>Custom metadata (no longer contains user/org data)
userFingerprintstringFallback anonymous user identifier

User & Organization Objects:

User and organization data are now passed as top-level properties for better API clarity:

// User object (for identification + display)
  user?: {
  id: string;        // Required: Unique user identifier for tracking
  name?: string;     // Optional: User's display name
  email?: string;    // Optional: User's email
  avatar?: string;   // Optional: URL to user's avatar
};
  
// Organization object (for identification + display)
  org?: {
  id?: string;       // Optional: Organization's unique ID
  name: string;      // Required: Organization's display name
  logo?: string;     // Optional: URL to organization's logo
};
  
// Context (pure custom metadata)
context?: {
  page: string;      // Current page
  customMetric: number; // Any custom data
  [key: string]: unknown;
};

Example:

{
  context: {
    // Beautiful user display
    user: {
      name: 'Sarah Chen',
      email: 'sarah@example.com',
      avatar: 'https://example.com/avatar.jpg'
    },
    // Beautiful organization display
    org: {
      name: 'Acme Corp',
      id: 'org_acme_123',
      logo: 'https://example.com/logo.png'
    },
    // Custom context
    userId: 'user-123',
    page: 'checkout',
    plan: 'pro',
    timestamp: Date.now()
  },
  userFingerprint: 'anon-123456'
}

Question

Question object structure (returned from API).

interface Question {
  questionId: string;
  text: string;
  type: 'single_choice' | 'text' | 'rating';
  options?: string[];
  targetElementId?: string;
}

Error Handling

All async methods can throw errors. Always handle them:

try {
  await whatif.ask('question-id');
} catch (error) {
  console.error('Failed to show feedback:', error);
  // Handle error appropriately
}

TypeScript

Full TypeScript support with exported types:

import { 
  WhatIf,
  type ThemeConfig,
  type DarkMode,
  type WidgetPosition,
  type ColorPalette,
  type Typography,
  type Spacing,
  type BorderConfig,
  type ShadowConfig,
  type AnimationConfig,
  type AskOptions,
  type Question
} from '@twenty1tools/whatif';

Browser Compatibility

Supported:

  • Chrome/Edge 90+
  • Firefox 90+
  • Safari 14+
  • iOS Safari 14+
  • Chrome Android 90+

Requirements:

  • ES6+ support
  • Shadow DOM support
  • prefers-color-scheme media query (for auto dark mode)

Migration Guide

From v0.1.x to v0.2.x

No breaking changes! All existing code works as before.

New features:

  • Theme customization
  • Dark mode support
  • Dynamic theme updates
  • setTheme() method
  • destroy() method
// Old code still works
const whatif = new WhatIf('app-id');
whatif.ask('question-id');

// New features available
whatif.setTheme({ darkMode: 'dark' });
whatif.destroy();

Examples

Complete Example

import { WhatIf, type ThemeConfig } from '@twenty1tools/whatif';

const theme: ThemeConfig = {
  darkMode: 'auto',
  position: 'bottom-right',
  colors: {
    primary: '#3b82f6',
    primaryHover: '#2563eb',
  },
  typography: {
    fontFamily: 'Inter, sans-serif',
  },
  border: {
    radius: '16px',
  },
  animation: {
    enabled: true,
    duration: 400,
  }
};

const whatif = new WhatIf('your-app-id', '/api', theme);

// Trigger with context
await whatif.ask('feedback-question', {
  context: {
    userId: 'user-123',
    page: window.location.pathname,
    timestamp: Date.now()
  }
});

// Update theme later
whatif.setTheme({
  darkMode: 'dark',
  colors: { primary: '#10b981' }
});

// Clean up
whatif.destroy();

Need Help?