Skip to content
Docs /SDK Reference

SDK Web Documentation

Complete reference for the StreamBack browser SDK.

Installation

HTML
<script src="https://streamback.tech/sdk/feedback-widget.min.js"></script>
<script>
  FeedbackWidget.init({
    envKey: 'env_your_key_here',
    apiUrl: 'https://api.streamback.tech',
  });
</script>

NPM / Yarn / pnpm (private registry / monorepo only)

The @streamback/sdk-web package is not currently published on the public npm registry. Use the hosted script above unless you have been given private package access or you are installing from the StreamBack monorepo.

BASH
npm install @streamback/sdk-web
# or
yarn add @streamback/sdk-web
# or
pnpm add @streamback/sdk-web

Script-tag usage exposes the legacy global FeedbackWidget for backward compatibility. Module/bundler usage should use import { StreamBack } from '@streamback/sdk-web' when the package is available through your private registry or local workspace.

Initialization

Basic Setup

TYPESCRIPT
import { StreamBack } from '@streamback/sdk-web';

StreamBack.init({
  envKey: 'env_your_key_here',
  apiUrl: 'https://api.streamback.tech',
});

Classic Configuration

TYPESCRIPT
StreamBack.init({
  // Required
  envKey: 'env_your_key_here',

  // Optional
  apiUrl: 'https://api.streamback.tech', // API endpoint
  mode: 'always', // 'always' | 'trigger' | 'never'
  position: 'bottom-right', // 'bottom-right' | 'bottom-left'
  accentColor: '#0ea5e9', // Hex color for branding
  debug: false, // Enable console logging
});

Full Configuration (Invisible Feedback Layer)

TYPESCRIPT
StreamBack.init({
  envKey: 'env_your_key_here',
  apiUrl: 'https://api.streamback.tech',
  accentColor: '#6366f1',
  debug: false,

  // Display configuration
  display: {
    trigger: 'dot', // 'hidden' | 'edge' | 'dot' | 'button'
    position: 'right-center', // 'bottom-right' | 'bottom-left' | 'right-center' | 'left-center'
    autoHideTrigger: 0, // Seconds, 0 = never
    panelWidth: 360,
  },

  // Contextual triggers
  triggers: {
    onError: { enabled: true, delay: 2000, maxPerSession: 3 },
    onFrustration: { enabled: true, clickThreshold: 4, timeWindow: 2000, maxPerSession: 2 },
    onExitIntent: { enabled: true, paths: ['/checkout*', '/billing*'], maxPerSession: 1 },
    onIdle: { enabled: true, duration: 60000, maxPerSession: 1 },
    onFormAbandonment: { enabled: true, formSelectors: ['form'], idleThreshold: 15000 },
    onScrollDepth: { enabled: true, threshold: 80, minTimeOnPage: 10000 },
    onPostAction: { enabled: true, events: ['checkout_complete'], delay: 30000 },
  },

  // Emotion-first flow
  flow: {
    startWith: 'emotion', // 'emotion' | 'text' | 'type'
    requireText: false,
    minTextForBonus: 50,
  },

  // Reward preview
  rewards: {
    format: 'credits', // 'credits' | 'currency'
    customTemplate: '{{amount}} bonus credits',
    showPreview: true,
    previewRange: { min: 1, max: 5 },
  },

  // Smart cooldowns
  cooldowns: {
    afterDismiss: 300000, // 5 min per-type cooldown
    afterSubmit: 86400000, // 24h global cooldown
    maxDismissalsPerSession: 3,
    respectNotNow: true,
  },

  // Custom whisper messages
  whisperMessages: {
    error: 'Something went wrong? Let us know and earn credits!',
    frustration: 'Having trouble? We want to help.',
    idle: 'Got a moment? Share your thoughts and earn rewards.',
    exitIntent: 'Before you go - any feedback for us?',
  },

  // Micro-surveys
  microSurveys: [
    {
      id: 'checkout-ease',
      question: { id: 'q1', text: 'How easy was checkout?', type: 'stars' },
      followUp: true,
      followUpPrompt: 'Tell us more...',
      routes: ['/checkout*'],
      delay: 5000,
      maxPerSession: 1,
    },
  ],
});

Configuration Options

OptionTypeDefaultDescription
envKeystringRequiredYour environment API key
apiUrlstringProduction URLCustom API endpoint
mode'always' | 'trigger' | 'never''always'Feedback layer display mode (classic)
positionstring'bottom-right'Feedback layer position (classic)
accentColorstring'#0ea5e9'Primary brand color
debugbooleanfalseEnable debug logging
displayDisplayConfig-Invisible feedback layer display options
triggersContextualTriggers-Contextual trigger configuration
flowFlowConfig-Emotion-first or custom flow
rewardsRewardDisplayConfig-Reward preview configuration
cooldownsCooldownConfig-Smart cooldown rules
whisperMessagesWhisperMessages-Custom whisper message strings
microSurveysMicroSurveyConfig[]-Auto-scheduled micro-surveys

Display Modes (Classic)

  • always: Feedback layer button is always visible
  • trigger: Feedback layer only opens via StreamBack.open()
  • never: Feedback layer is completely hidden (useful for feature flags)

Trigger Modes (Invisible Feedback Layer)

  • hidden: No visible trigger, open only via API or contextual triggers
  • edge: Minimal 4px edge indicator strip that expands on hover
  • dot: Small 10px dot that expands to icon on hover
  • button: Traditional floating button (legacy mode)

User Identification

Identify User

TYPESCRIPT
StreamBack.identifyUser({
  id: 'user_123', // Required: Unique user ID
  email: 'user@example.com', // Optional but recommended
  name: 'John Doe', // Optional
});

Set Entitlements

Entitlements help with targeting and can be used in policy rules:

TYPESCRIPT
StreamBack.setEntitlements({
  plan: 'pro',
  tier: 'business',
  subscriptionId: 'sub_abc123',
  customerId: 'cus_xyz789',
  // Any custom key-value pairs
  features: ['advanced-reporting', 'api-access'],
});

Reset User

Clear user data (e.g., on logout):

TYPESCRIPT
StreamBack.resetUser();

Feedback Layer Control

Open Feedback Layer

TYPESCRIPT
StreamBack.open();

Close Feedback Layer

TYPESCRIPT
StreamBack.close();

Start Element Selection

Allow users to click on a page element to highlight it:

TYPESCRIPT
StreamBack.startElementSelection();

Check Feedback Layer State

TYPESCRIPT
const isOpen = StreamBack.isOpen(); // boolean

Whispers (Contextual Prompts)

Show a non-modal whisper prompt that appears near the feedback layer trigger:

TYPESCRIPT
// Show a built-in whisper type
StreamBack.whisper('error'); // Error whisper
StreamBack.whisper('frustration'); // Frustration whisper
StreamBack.whisper('idle'); // Idle whisper
StreamBack.whisper('exitIntent'); // Exit intent whisper
StreamBack.whisper('success'); // Success whisper

// Custom whisper with your own message
StreamBack.whisper('custom', 'How did the export go?');

Whispers auto-dismiss after 8 seconds if ignored. Users can click the whisper to open the full feedback panel, or click "Not now" to dismiss with a longer cooldown.

Micro-Surveys

Show a 1-question quick poll:

TYPESCRIPT
StreamBack.showSurvey({
  id: 'checkout-ease',
  question: {
    id: 'q1',
    text: 'How easy was your checkout experience?',
    type: 'stars', // 'stars' | 'yesno' | 'scale' | 'multiple-choice'
  },
  followUp: true, // Show follow-up text input after answer
  followUpPrompt: 'Tell us more about your experience...',
});

Survey Question Types

TypeDescriptionOptions
stars1-5 star ratingNone
yesnoYes / No buttonsNone
scaleNumeric scale (e.g., 1-10)scaleMin, scaleMax, scaleLabels
multiple-choiceSelectable options listoptions: string[]

Auto-Scheduled Surveys

Surveys defined in microSurveys config are auto-scheduled based on delay, routes, and maxPerSession:

TYPESCRIPT
microSurveys: [
  {
    id: 'feature-useful',
    question: { id: 'q1', text: 'Did you find this useful?', type: 'yesno' },
    followUp: true,
    followUpPrompt: 'What would make it better?',
    routes: ['/features*', '#features'], // Path or hash-based routes
    delay: 8000, // Show after 8 seconds
    maxPerSession: 1, // Once per session
  },
];

Pause / Resume

Temporarily disable all contextual triggers:

TYPESCRIPT
StreamBack.pause(); // Disable all triggers
StreamBack.resume(); // Re-enable triggers

Context & State Providers

Context and state providers capture app-specific data when feedback is submitted. This data appears in the console for debugging.

Context Provider

Captures static or semi-static app information:

TYPESCRIPT
StreamBack.registerContextProvider('app', () => ({
  version: '2.1.0',
  environment: process.env.NODE_ENV,
  feature_flags: {
    dark_mode: true,
    new_checkout: false,
  },
  deployment: {
    commit: 'abc123',
    branch: 'main',
  },
}));

State Provider

Captures dynamic application state (Redux, Zustand, etc.):

TYPESCRIPT
import { store } from './store';

StreamBack.registerStateProvider('redux', () => ({
  // Be selective - don't capture sensitive data
  cart_items: store.getState().cart.items.length,
  active_filters: store.getState().filters,
  ui_state: {
    sidebar_open: store.getState().ui.sidebarOpen,
    modal_open: store.getState().ui.modalOpen,
  },
}));

Multiple Providers

You can register multiple providers:

TYPESCRIPT
// App context
StreamBack.registerContextProvider('app', () => ({...}));

// User preferences
StreamBack.registerContextProvider('preferences', () => ({
  theme: localStorage.getItem('theme'),
  language: navigator.language,
}));

// Redux state
StreamBack.registerStateProvider('redux', () => ({...}));

// React Query cache
StreamBack.registerStateProvider('query', () => ({
  queries: queryClient.getQueryCache().getAll().map(q => q.queryKey),
}));

Privacy & Redaction

Screenshot Redaction

The SDK automatically redacts sensitive information in screenshots:

Auto-Detected Fields

Field TypeRedaction
Password inputsReplaced with ••••••••
Email inputsPartial mask: j•••@domain.com
Phone inputsPartial mask: 12•••••89
Credit card fieldsFully redacted
Text inputsDots (based on length)
Textareas[Redacted content]

Custom Redaction Rules

TYPESCRIPT
StreamBack.setRedactionRules({
  // Redact all form inputs (default: true)
  redactInputs: true,

  // Additional CSS selectors to redact
  selectors: ['.sensitive-data', '[data-private]', '.account-balance'],

  // Routes where feedback is disabled
  blockedRoutes: ['/admin/*', '/checkout', '/payment/*'],
});

HTML Attribute

Mark specific elements for redaction:

HTML
<div data-feedback-redact="true">Sensitive content here - will be blurred in screenshots</div>

<!-- Exclude elements from screenshots entirely -->
<div data-feedback-ignore>This won't appear in screenshots</div>

Event Tracking

Automatic Events

The SDK automatically tracks:

Event TypeData Captured
clickElement selector, text, coordinates
route_changeURL, path
window_errorMessage, filename, line, stack
network_errorURL, method, status, duration
console_errorLevel, message

Custom Events

Track application-specific events:

TYPESCRIPT
StreamBack.trackEvent('checkout_started', {
  cart_value: 99.99,
  item_count: 3,
  currency: 'USD',
});

StreamBack.trackEvent('feature_used', {
  feature: 'export-to-csv',
  context: 'dashboard',
});

StreamBack.trackEvent('error_boundary', {
  error: error.message,
  componentStack: errorInfo.componentStack,
});

Event Timeline

All events (automatic and custom) are captured in a timeline that's submitted with feedback. This helps developers understand what led to the issue.

Event Listeners

Available Events

TYPESCRIPT
// Feedback layer opened
StreamBack.on('open', () => {
  analytics.track('streamback_opened');
});

// Feedback layer closed
StreamBack.on('close', () => {
  console.log('Feedback layer closed');
});

// Feedback submitted (before API call)
StreamBack.on('submit', (data) => {
  console.log('Submitting:', data);
});

// Feedback sent successfully
StreamBack.on('success', () => {
  showNotification('Thanks for your feedback!');
});

// Feedback failed
StreamBack.on('error', (error) => {
  showNotification('Failed to send feedback. Please try again.');
  console.error('Feedback error:', error);
});

// Element selected via point-to-element
StreamBack.on('element-selected', (elementInfo) => {
  console.log('Selected element:', elementInfo);
});

// Whisper shown
StreamBack.on('whisper-shown', (data) => {
  console.log('Whisper shown:', data);
});

// Whisper dismissed
StreamBack.on('whisper-dismissed', (data) => {
  console.log('Whisper dismissed:', data);
});

// Whisper clicked (user engaged)
StreamBack.on('whisper-clicked', (data) => {
  console.log('Whisper clicked:', data);
});

Remove Listeners

TYPESCRIPT
const handler = () => console.log('Opened');
StreamBack.on('open', handler);

// Later...
StreamBack.off('open', handler);

Emotion-First Flow

When flow.startWith is set to 'emotion', the feedback panel opens with a 5-emoji picker instead of a text box:

  1. Step 1: User selects an emotion (Awful / Bad / Okay / Good / Great) - 1 tap
  2. Step 2: Category pills appear (Bug / Idea / Praise / Question / Other) + text area expands
  3. Step 3: User can submit with just emotion (no text required), or add details for higher rewards
TYPESCRIPT
flow: {
  startWith: 'emotion',     // Show emoji picker first
  requireText: false,       // Allow submit without text
  minTextForBonus: 50,      // Chars needed for "detailed" reward tier
}

The reward preview dynamically updates: "Quick feedback: 1 credit | With details: up to 5 credits"

Reward Preview

Show potential reward amounts before submission to increase engagement:

TYPESCRIPT
rewards: {
  format: 'credits',                    // 'credits' | 'currency'
  customTemplate: '{{amount}} bonus credits',
  showPreview: true,                    // Show range in whispers + panel
  previewRange: { min: 1, max: 5 },    // Min/max possible reward
}

When enabled:

  • Whispers include reward hint: "Let us know and earn 1-5 credits!"
  • Panel header shows: "Earn up to 1-5 credits for detailed feedback"
  • Success state shows actual awarded amount

Smart Cooldowns

Prevent user annoyance with session-aware rate limiting:

TYPESCRIPT
cooldowns: {
  afterDismiss: 300000,          // 5 min cooldown per trigger type after dismiss
  afterSubmit: 86400000,         // 24h global cooldown after submission
  maxDismissalsPerSession: 3,    // Suppress all triggers after 3 dismissals
  respectNotNow: true,           // "Not now" = longer cooldown than X dismiss
}

Cooldown data is stored in sessionStorage (per-session) and localStorage (cross-session). Clearing storage resets cooldowns.

Mobile Bottom Sheet

On viewports under 768px, the feedback panel automatically renders as a bottom sheet:

  • Full-width panel slides up from the bottom
  • Rounded top corners with drag handle
  • Max height 70% of viewport
  • Touch-friendly targets (min 44px)
  • Swipe down to dismiss

No configuration needed -- this is automatic based on screen size.

Framework Integration

React

TSX
// src/feedback.ts
import { StreamBack } from '@streamback/sdk-web';

export function initFeedback() {
  StreamBack.init({
    envKey: process.env.REACT_APP_FEEDBACK_KEY!,
    mode: 'always',
  });
}

// src/App.tsx
import { useEffect } from 'react';
import { useAuth } from './hooks/useAuth';
import { initFeedback } from './feedback';

function App() {
  const { user } = useAuth();

  useEffect(() => {
    initFeedback();
  }, []);

  useEffect(() => {
    if (user) {
      StreamBack.identifyUser({
        id: user.id,
        email: user.email,
        name: user.name,
      });
    } else {
      StreamBack.resetUser();
    }
  }, [user]);

  return <YourApp />;
}

Vue

TYPESCRIPT
// src/plugins/feedback.ts
import { StreamBack } from '@streamback/sdk-web';

export default {
  install(app) {
    StreamBack.init({
      envKey: import.meta.env.VITE_FEEDBACK_KEY,
      mode: 'always',
    });

    app.config.globalProperties.$feedback = StreamBack;
  },
};

// src/main.ts
import feedbackPlugin from './plugins/feedback';
app.use(feedbackPlugin);

Next.js

TSX
// src/app/providers.tsx
'use client';

import { useEffect } from 'react';
import { StreamBack } from '@streamback/sdk-web';

export function FeedbackProvider({ children }) {
  useEffect(() => {
    StreamBack.init({
      envKey: process.env.NEXT_PUBLIC_FEEDBACK_KEY!,
      mode: 'always',
    });
  }, []);

  return children;
}

// src/app/layout.tsx
import { FeedbackProvider } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <FeedbackProvider>{children}</FeedbackProvider>
      </body>
    </html>
  );
}

Angular

TYPESCRIPT
// src/app/feedback.service.ts
import { Injectable } from '@angular/core';
import { StreamBack } from '@streamback/sdk-web';

@Injectable({ providedIn: 'root' })
export class FeedbackService {
  init() {
    StreamBack.init({
      envKey: environment.feedbackKey,
      mode: 'always',
    });
  }

  identifyUser(user: User) {
    StreamBack.identifyUser({
      id: user.id,
      email: user.email,
    });
  }

  open() {
    StreamBack.open();
  }
}

// src/app/app.component.ts
export class AppComponent implements OnInit {
  constructor(private feedback: FeedbackService) {}

  ngOnInit() {
    this.feedback.init();
  }
}

TypeScript Support

The SDK is written in TypeScript and includes full type definitions:

TYPESCRIPT
import {
  StreamBack,
  type StreamBackConfig,
  type UserIdentity,
  type RedactionRules,
} from '@streamback/sdk-web';

const config: StreamBackConfig = {
  envKey: 'env_xxx',
  mode: 'always',
  position: 'bottom-right',
};

StreamBack.init(config);

Troubleshooting

Feedback Layer Not Appearing

  1. Check that envKey is correct and valid
  2. Ensure mode is not set to 'never'
  3. Check for CSS conflicts (z-index issues)
  4. Enable debug: true to see console logs

Screenshots Not Working

  1. Ensure no CORS issues with images on the page
  2. Check that html2canvas is loading correctly
  3. Some browser extensions may interfere

Events Not Tracking

  1. Enable debug: true to see event logs
  2. Check that the SDK is initialized before user interactions
  3. Verify network requests in DevTools

User Not Identified

  1. Call identifyUser() after user authentication
  2. Ensure user ID is a string, not a number
  3. Check that resetUser() isn't being called

Best Practices

  1. Initialize Early: Call init() as early as possible in your app lifecycle
  2. Identify Users: Always identify users when possible for better feedback quality
  3. Use Context Providers: Provide relevant app context for easier debugging
  4. Don't Over-Track: Only track meaningful events, not every click
  5. Test Redaction: Verify sensitive data is properly redacted in screenshots
  6. Handle Errors: Listen to the error event and show user-friendly messages

Browser Support

BrowserVersion
Chrome60+
Firefox60+
Safari12+
Edge79+
iOS Safari12+
Chrome Android60+

Security

  • All API communication uses HTTPS
  • Screenshots are uploaded directly to secure storage
  • PII is automatically redacted before transmission
  • User data is never logged or stored client-side