Embed API Reference

Updated: March 30, 2026

The Perspective Embed SDK lets you drop interviews into any web page with a script tag and a data attribute. For more control, use the JavaScript API to create and manage embed instances programmatically.

For the user-facing setup guide (choosing embed types, previewing, copying snippets from the dashboard), see Embed Interviews on Your Website or App.

Quick Start

Add the SDK script and a trigger element to your page:

html
<div data-perspective-widget="RESEARCH_ID"></div><script src="https://getperspective.ai/v1/perspective.js"></script>

Replace RESEARCH_ID with the ID of your perspective (visible in the dashboard URL or returned by the Create Perspective API).

The script loads asynchronously and initializes all trigger elements on the page automatically.

Embed Types

Each embed type uses a different data attribute on the trigger element:

TypeAttributeElementDescription
Widgetdata-perspective-widget<div>Inline embed that renders directly in the page flow.
Popupdata-perspective-popup<button>Modal overlay centered on screen.
Sliderdata-perspective-slider<button>Side panel that slides in from the edge.
Floatdata-perspective-float<div>Floating chat bubble anchored to the corner.
Fullpagedata-perspective-fullpage<div>Full-viewport interview experience.

Button-based types (Popup, Slider) require a <button> element. The user clicks it to open the interview.

Examples

Inline widget:

html
<div data-perspective-widget="RESEARCH_ID"></div>

Popup triggered by a button:

html
<button data-perspective-popup="RESEARCH_ID">  Share your feedback</button>

Floating chat bubble:

html
<div data-perspective-float="RESEARCH_ID"></div>

URL Parameters

Pass parameters via the data-perspective-params attribute as comma-separated key=value pairs:

html
<div  data-perspective-widget="RESEARCH_ID"  data-perspective-params="source=homepage,user_id=123,plan=pro"></div>

Built-In Parameters

ParameterTypeDescription
emailstringPre-fill participant email.
namestringPre-fill participant name.
returnUrlURLRedirect URL after interview completion.
voice"0"Set to "0" to disable voice mode (text-only).
scroll"0"Set to "0" to disable scroll mode.
hideProgress"true"Hide the progress bar.
hideGreeting"true"Hide the welcome greeting at the start.
hideBranding"true"Hide the branding header (logo and company name).
enableFullScreen"true" / "false"Show or hide the fullscreen button. Defaults to "true".

Any unrecognized parameters are treated as custom key-value pairs. They are passed through to webhooks and data exports, making them useful for attribution and tracking (e.g., campaign=summer2024).

Theming

Color Mode

Force a color mode with data-perspective-theme:

html
<div  data-perspective-widget="RESEARCH_ID"  data-perspective-theme="dark"></div>

Accepted values: dark, light, system (default, follows OS preference).

Brand Colors

Override brand colors with data-perspective-brand (light mode) and data-perspective-brand-dark (dark mode):

html
<div  data-perspective-widget="RESEARCH_ID"  data-perspective-brand="primary=#7c3aed,bg=#ffffff"  data-perspective-brand-dark="primary=#a78bfa,bg=#1a1a1a"></div>

Available color tokens:

TokenDescription
primaryPrimary accent color (buttons, links).
secondarySecondary accent color.
bgBackground color override.
textText color override.

Auto-Open (Popup Only)

Trigger a popup automatically without a user click:

html
<div  data-perspective-popup="RESEARCH_ID"  data-perspective-auto-open="timeout:5000"  data-perspective-show-once="session"  style="display:none"></div>

Auto-open triggers:

  • timeout:N -- Opens the popup after N milliseconds.
  • exit-intent -- Opens when the user moves their cursor toward the browser chrome (desktop only).

Show-once options:

  • session -- Show once per browser session.
  • visitor -- Show once per visitor (persisted in localStorage).
  • false -- Show every time.

JavaScript API

For programmatic control, use the global Perspective object:

javascript
// Create an inline widgetconst widget = Perspective.createWidget(container, {  researchId: "RESEARCH_ID",  params: { source: "app", user_id: "123" },  onReady: () => console.log("Loaded"),  onSubmit: () => console.log("Completed"),  onNavigate: (url) => window.location.href = url,  onClose: () => console.log("Closed"),  onError: (err) => console.error(err),});
// Open a popupconst popup = Perspective.openPopup({  researchId: "RESEARCH_ID",  onClose: () => console.log("Closed"),});
// Open a sliderconst slider = Perspective.openSlider({  researchId: "RESEARCH_ID",});
// Create a floating chat bubbleconst float = Perspective.createFloatBubble({  researchId: "RESEARCH_ID",});
// Destroy an instance when donewidget.destroy();

Callbacks

CallbackArgumentsDescription
onReady--Embed has loaded and is ready for interaction.
onSubmit--Participant completed the interview.
onNavigateurl: stringEmbed requests a page navigation. If not provided, defaults to window.location.href (full page reload).
onClose--User closed the embed (popup, slider, or float).
onErrorerror: ErrorAn error occurred during the embed lifecycle.

React SDK

For React and Next.js apps, use @perspective-ai/sdk-react instead of the script tag. It provides typed components and hooks with full lifecycle control.

Installation

bash
npm install @perspective-ai/sdk-react

Widget (Inline Embed)

Renders the interview directly inside a container element:

tsx
import { Widget } from "@perspective-ai/sdk-react";
function FeedbackWidget() {  return (    <Widget      researchId="RESEARCH_ID"      params={{ source: "app", user_id: "123" }}      onReady={() => console.log("Loaded")}      onSubmit={() => console.log("Completed")}    />  );}

The Widget component accepts all standard div props (className, style, etc.) for sizing and layout.

Use the usePopup hook for modal overlays:

tsx
import { usePopup } from "@perspective-ai/sdk-react";
function FeedbackButton() {  const { open, isOpen } = usePopup({    researchId: "RESEARCH_ID",    onSubmit: () => console.log("Completed"),  });
  return <button onClick={open}>Give Feedback</button>;}

Slider

Use the useSlider hook for a side panel that slides in from the edge:

tsx
import { useSlider } from "@perspective-ai/sdk-react";
function SliderTrigger() {  const { open } = useSlider({ researchId: "RESEARCH_ID" });  return <button onClick={open}>Open Survey</button>;}

Float Bubble

A floating chat bubble anchored to the corner of the page:

tsx
import { FloatBubble } from "@perspective-ai/sdk-react";
function App() {  return <FloatBubble researchId="RESEARCH_ID" onSubmit={handleSubmit} />;}

Fullpage

Takes over the entire viewport:

tsx
import { Fullpage } from "@perspective-ai/sdk-react";
function InterviewPage() {  return <Fullpage researchId="RESEARCH_ID" />;}

Auto-Open

Trigger a popup automatically after a delay or on exit intent:

tsx
import { useAutoOpen } from "@perspective-ai/sdk-react";
function AutoSurvey() {  useAutoOpen({    researchId: "RESEARCH_ID",    trigger: { type: "timeout", delay: 5000 },    showOnce: "session",  });  return null;}

React Callbacks

All components and hooks accept the same callback props as the JavaScript API: onReady, onSubmit, onNavigate, onClose, and onError. See the Callbacks table above for details.

Direct Iframe Embed

If you cannot use the SDK (e.g., in environments that restrict third-party scripts), embed the interview directly in an iframe:

html
<iframe  id="perspective-iframe"  src="https://getperspective.ai/i/RESEARCH_ID?email=user@example.com&returnUrl=https://example.com/thanks"  style="width: 100%; height: 600px; border: none;"  allow="microphone"></iframe>

When using a direct iframe, you must add a postMessage listener to handle navigation and lifecycle events:

javascript
var iframe = document.getElementById("perspective-iframe");var allowedOrigin = new URL(iframe.src).origin;
window.addEventListener("message", (event) => {  if (event.origin !== allowedOrigin) return;  if (!event.data?.type?.startsWith("perspective:")) return;
  switch (event.data.type) {    case "perspective:ready":      // Embed loaded and ready      break;    case "perspective:submit":      // Interview completed      break;    case "perspective:redirect":      // Navigate to the URL provided by the embed      window.location.href = event.data.url;      break;    case "perspective:close":      // User closed the embed      break;    case "perspective:resize":      // Adjust iframe height: event.data.height      iframe.style.height = event.data.height + "px";      break;    case "perspective:error":      // Handle error: event.data.error      break;  }});

PostMessage Events

EventDataDescription
perspective:ready--Embed loaded and ready.
perspective:submit--Interview completed.
perspective:redirect{ url }Embed wants to navigate. You must handle this.
perspective:close--User closed the embed.
perspective:resize{ height }Embed content height changed.
perspective:error{ error }An error occurred.

Add allow="microphone" to the iframe if the interview uses voice mode.

Best Practices

  • Load the script once: If you have multiple embeds on the same page, include the SDK script tag only once. It discovers all trigger elements automatically.
  • Use the SDK over iframes: The SDK handles responsive sizing, navigation, theming, and lifecycle events. Direct iframes require you to manage all of this yourself.
  • Pass user identity via params: If your users are already authenticated, pass email and name so participants skip the identification step.
  • Prefer onNavigate over default behavior: In single-page apps, handle onNavigate to use your router instead of a full page reload.