Skip to content

Docs

JavaScript SDK

Programmatic widget API: open, identify, events, theming and TypeScript types.

Loading and the early-call queue

The loader is asynchronous. Define this small shim before your own code so calls made early are queued and replayed when the SDK loads:

<script>
  window.FeedbackWidget = window.FeedbackWidget || {
    _q: [],
    open(...args) { this._q.push(['open', args]); },
    onReady(callback) { this._q.push(['onReady', [callback]]); }
  };
  window.FeedbackWidget.onReady(() => {
    window.FeedbackWidget.setTheme('auto');
  });
</script>

Methods

MethodBehavior
init({ publicKey, theme, position, launcher })Programmatic alternative to data attributes. Safe to call repeatedly.
open(tab, options?)Open 'feedback', 'roadmap', 'changelog' or 'surveys'; optionally pass an item or survey id.
close()Close the panel and restore focus to where it was.
toggle(tab?)Toggle the panel open or closed.
identify({ token })Pass a short-lived, server-signed identity token for your logged-in user.
logout()Clear the identified session from widget memory.
setTheme('light' | 'dark' | 'auto')Change the appearance at runtime.
on(event, cb) / off(event, cb)Listen to ready, open, close, vote, submit and surveyComplete.
destroy()Remove the launcher, iframe and all listeners.
onReady(cb)Run a callback once the SDK has initialized.

Events

EventPayload
ready—
open / close{ tab }
vote{ feedbackId, voted }
submit{ feedbackId }
surveyComplete{ surveyId }
const onVote = (e) => console.log('voted', e.feedbackId, e.voted);
window.FeedbackWidget.on('vote', onVote);
// later
window.FeedbackWidget.off('vote', onVote);

Programmatic init

Omit data-workspace and call init yourself. Repeated calls are safe and never create duplicate launchers.

window.FeedbackWidget.init({
  publicKey: 'pub_YOUR_PUBLIC_WORKSPACE_KEY',
  theme: 'auto',          // 'light' | 'dark' | 'auto'
  position: 'right',      // 'right' | 'left'
  launcher: true
});

TypeScript types

Save as feedback-widget.d.ts in your project:

export type FeedbackWidgetTab = 'feedback' | 'roadmap' | 'changelog' | 'surveys';
export type FeedbackWidgetTheme = 'light' | 'dark' | 'auto';

export interface FeedbackWidgetInitOptions {
  publicKey: string;
  theme?: FeedbackWidgetTheme;
  position?: 'left' | 'right';
  launcher?: boolean;
}

export interface FeedbackWidgetOpenOptions {
  /** Open a specific feedback post or survey by its public id. */
  id?: string;
}

export interface FeedbackWidgetEventMap {
  ready: void;
  open: { tab: FeedbackWidgetTab };
  close: { tab: FeedbackWidgetTab };
  vote: { feedbackId: string; voted: boolean };
  submit: { feedbackId: string };
  surveyComplete: { surveyId: string };
}

export interface FeedbackWidgetApi {
  init(options: FeedbackWidgetInitOptions): void;
  open(tab?: FeedbackWidgetTab, options?: FeedbackWidgetOpenOptions): void;
  close(): void;
  toggle(tab?: FeedbackWidgetTab): void;
  /** Pass a short-lived token signed by YOUR server. Never sign in the browser. */
  identify(params: { token: string }): Promise<void>;
  logout(): void;
  setTheme(theme: FeedbackWidgetTheme): void;
  on<K extends keyof FeedbackWidgetEventMap>(event: K, cb: (payload: FeedbackWidgetEventMap[K]) => void): void;
  off<K extends keyof FeedbackWidgetEventMap>(event: K, cb: (payload: FeedbackWidgetEventMap[K]) => void): void;
  destroy(): void;
  onReady(cb: () => void): void;
}

declare global {
  interface Window { FeedbackWidget: FeedbackWidgetApi; }
}
export {};