Embed API Reference
Updated: October 8, 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:
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:
Button-based types (Popup, Slider) require a <button> element. The user clicks it to open the interview.
Examples
Inline widget:
Popup triggered by a button:
Slider triggered by a button:
Floating chat bubble:
URL Parameters
Pass parameters via the data-perspective-params attribute as comma-separated key=value pairs:
Built-In Parameters
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).
Custom parameters have no application-specific count or key-name length cap. Total URL and request size limits still apply. Values in agent prompts and MCP batch transcripts are sanitized and truncated to 50 characters. Stored values, exports, and webhooks retain the decoded URL values. See Custom parameter limits for details.
The SDK also forwards search parameters from the parent page URL into the embed iframe, except for SDK-owned parameters such as embed, embed_type, theme, and brand.* color keys. Parameters passed explicitly through data-perspective-params or the JavaScript params option override forwarded parent-page values. Custom context values become Input Params, visible in the Input tab of conversation results. For details, see Track Input Params.
Theming
Color Mode
Force a color mode with data-perspective-theme:
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):
Available color tokens:
Other tokens (such as secondary and text) are not applied and are ignored.
Widget Appearance
The inline Widget renders as a centered, framed card by default — bordered, rounded, with a soft shadow and a themed background — so it looks finished without any custom CSS. The widget adapts to how you size its container:
- Drop it in without sizing and it renders as a centered card (default max width
480px, min height640px). - Size the container yourself — an explicit height,
height: 100%, or a flex/grid cell — and it fills that space edge to edge with no framing.
Override the auto-detection and individual card styles with data-perspective-frame, a comma-separated list of key=value pairs:
Every key except layout maps to a --perspective-widget-* CSS custom property (--perspective-widget-max-width, --perspective-widget-min-height, --perspective-widget-radius, --perspective-widget-border, --perspective-widget-shadow, --perspective-widget-bg), so you can also set these from your own stylesheet. layout has no CSS variable — it only controls the card-vs-fill decision. Inline data-perspective-frame values take precedence over stylesheet variables, which take precedence over the built-in defaults.
With the JavaScript API or NPM SDK, pass these as a frame object. The field names match the attribute keys above, except the background field is background (the shorter bg is only an alias accepted inside the data-perspective-frame string):
In React, pass the frame prop to <Widget> with the same fields:
Frame options apply to the inline Widget only. Popup, Slider, Float, and Fullpage embeds manage their own layout.
Advanced Attributes
Use these attributes when you need more control over SDK behavior:
Slider Modes
Sliders support two display modes:
- Overlay - the default. The slider floats above the page with a backdrop, and outside clicks can close it.
- Push - the slider occupies its own column and shifts the page content aside. The rest of the page stays interactive, and outside clicks do not close the slider.
Push mode falls back to overlay on narrow screens where there is not enough room to keep the page usable.
For script embeds:
For the JavaScript API:
For React:
Auto-Open (Popup Only)
Trigger a popup automatically without a user click:
Auto-open triggers:
timeout:N-- Opens the popup afterNmilliseconds.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.
Welcome Teaser (Float Only)
Float embeds can show a welcome teaser — a message bubble that appears above the launcher button after a short delay, along with a chime sound and a notification dot. The teaser text comes from the interview's welcomeMessage.
Control the teaser with a teaser object through the JavaScript API or NPM SDK:
enabled: falseturns off the teaser bubble, the chime sound, and the notification dot entirely.delaysets when the teaser bubble appears, in milliseconds.sound: falsemutes the chime while keeping the teaser bubble.
For script embeds, set the teaser data attributes on the trigger element:
In React, pass the teaser prop to <FloatBubble> (or the useFloatBubble hook):
JavaScript API
For programmatic control, use the global Perspective object:
All creation functions return a handle with unmount(), destroy(), and update() methods. Use unmount() for framework cleanup or route changes. Use destroy() when you intentionally want to close the embed; popup, slider, and float embeds can persist that closed state. Float handles also expose open(), close(), toggle(), and isOpen.
The browser global also exposes Perspective.mount(containerOrSelector, config) for selector-based mounting, Perspective.init(config) for non-widget embed types, Perspective.destroy(researchId), Perspective.destroyAll(), Perspective.autoInit(), Perspective.configure(config), and Perspective.getConfig().
Perspective can also be called as a function with a command name, for example Perspective("autoInit"). Calls made before the script loads are queued and run in order once it does, which is how the Google Tag Manager tag works. See Commands for the list.
Callbacks
NPM JavaScript SDK
For TypeScript or bundler-based apps that do not need React components, install @perspective-ai/sdk:
createWidget accepts an HTMLElement | null. If you want selector-based mounting, use the browser global Perspective.mount("#selector", config) or resolve the element before calling the NPM function.
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
Widget (Inline Embed)
Renders the interview directly inside a container element:
The Widget component accepts all standard div props (className, style, etc.) for sizing and layout, plus a frame prop to control its card appearance — see Widget Appearance.
Popup
Use the usePopup hook for modal overlays:
Slider
Use the useSlider hook for a side panel that slides in from the edge:
Float Bubble
A floating chat bubble anchored to the corner of the page:
Pass a welcomeMessage to show a teaser bubble above the launcher, and a teaser prop to control its timing and chime — see Welcome Teaser.
Fullpage
Takes over the entire viewport:
Auto-Open
Trigger a popup automatically after a delay or on exit intent:
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.
React helpers also include useFloatBubble, useEmbedConfig, and useThemeSync for headless float control, config fetching, and theme synchronization.
Google Tag Manager and Google Analytics
When the page already has a window.dataLayer, as every page running Google Tag Manager does, SDK embeds push what visitors do with the conversation to it. You can use those events to count finished conversations as conversions in Google Analytics. This works the same for the script tag, the npm package, the React package, and the Tag Manager install below.
On a page without one, the SDK sends nothing and doesn't create window.dataLayer. A direct iframe embed doesn't load the SDK, so it doesn't send these events.
Install with Google Tag Manager
You can add a conversation to your site from Tag Manager without changing your site's code. The tag works on regular websites and on single-page apps that change pages without a full reload.
The quickest way to get the tag is from the dashboard. Open your perspective, choose Invite participants > Embed, pick the embed type, and open Google Tag Manager on the Code tab. The copied tag has your perspective's ID and embed settings filled in, and it removes the conversation on pages where it no longer runs (see below).
- In Tag Manager, go to Tags > New > Tag Configuration > Custom HTML.
- Paste the tag from the dashboard. To write it yourself instead, start from the minimal tag below.
- In the minimal tag, replace
RESEARCH_IDon the last line with the ID of your perspective, andfloatwith the embed type you want (see the table below).
If the page already has a data-perspective-* element with the same ID and type, the tag uses it instead of adding another one.
Then choose when the tag runs:
- Add a DOM Ready trigger that fires on all pages, or only on the pages that should show the conversation. Don't use All Pages or another Page View trigger. Those fire before the page's HTML has loaded, so the tag can't find a
data-perspective-*element you placed yourself, and the conversation appears in the wrong place. - If your site changes pages without a full reload, also add a History Change trigger with the same page filter, so the tag runs again on each new page where the conversation belongs.
- Use Preview to check the conversation appears where you expect, then Publish.
The tag can run before the Perspective script finishes downloading. Its calls wait and run once the script is ready. Running the tag again, for example on a History Change, doesn't show the conversation twice. If your app replaced the page content, the conversation moves to the new page, and a float that was open stays open.
On a site that changes pages without a full reload, the tag copied from the dashboard removes the conversation when the visitor moves to a page where it doesn't run again. That's why the History Change trigger is needed: without it, the conversation disappears after the first in-app navigation. The minimal tag doesn't do this, so a float or full-page conversation already on screen stays when the visitor moves to a page your triggers exclude. To remove it there, add a second Custom HTML tag that fires on History Change for those pages:
When the visitor returns to an included page, the first tag shows the conversation again.
Track Conversations in Google Analytics
Each embed pushes these events to the dataLayer:
Each event also carries perspective_research_id and perspective_embed_type (widget, popup, slider, float, or fullpage). In Tag Manager, read them with a Data Layer Variable to break results down by research or embed type.
To count finished conversations as conversions in Google Analytics 4:
- In Tag Manager, create a Custom Event trigger with the event name
perspective_conversation_completed. - Create a Google Analytics: GA4 Event tag that fires on that trigger.
- In Google Analytics, mark the event as a key event.
Avoid Counting Conversions Twice
SDK 1.16.0 and later send these events on their own. If you already send your own Perspective events to Tag Manager, for example a dataLayer.push inside onSubmit or a Custom HTML tag that listens for perspective:* messages, remove that code. Otherwise each conversion is counted twice. If you want to keep your own tracking, turn the built-in events off instead.
The script at https://getperspective.ai/v1/perspective.js always serves the latest 1.x release, so script-tag installs already send these events.
Turn Off the Events
In a React app, configure comes from the core package. Install it next to the React package, at the same version, so it configures the copy the components use:
Settings
configure() (or Perspective("configure", { ... })) accepts these options for Tag Manager:
Commands
Instead of adding an element to the page, a tag or script can call these. Calls made before the script has loaded wait and run in order. Calls made while the page's HTML is still loading wait until it has finished, so mount can find an element further down the page.
After the script loads, Perspective keeps its usual methods, such as Perspective.openPopup() and Perspective.destroy().
Content Security Policy
If your site sends a Content-Security-Policy header, allow https://getperspective.ai in both script-src (the embed script) and frame-src (the conversation iframe).
Tag Manager runs a Custom HTML tag as an inline script, so a policy without 'unsafe-inline' blocks the tag even when https://getperspective.ai is allowed. Google's guide to using Tag Manager with a Content Security Policy explains how to allow it with a nonce instead. If your policy can't allow either, add the script tag to your site's HTML instead of using Tag Manager.
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:
When using a direct iframe, you must add a postMessage listener to handle navigation and lifecycle events:
PostMessage Events
Microphone access in nested iframes
For a Perspective iframe you embed directly, allow="microphone" is all voice mode needs.
It gets more involved when that iframe sits inside another iframe (common with page builders like Wix or Squarespace, or your own wrapper): Permissions Policy only delegates the microphone into a frame when every ancestor iframe allows it. Because the Perspective iframe is a different origin than the wrapper, allow="microphone" is not sufficient on the wrapper — the shorthand grants the microphone only to the wrapper's own origin. Set the wrapper to allow="microphone *" (or allow="microphone https://getperspective.ai" to scope it). The Perspective iframe keeps its own allow="microphone", so once the wrapper delegates across origins, voice mode works.
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
emailandnameso participants skip the identification step. - Prefer
onNavigateover default behavior: In single-page apps, handleonNavigateto use your router instead of a full page reload.