Embeddable browser widget that lets users submit feedback from any web app. Feedback is routed to Obvious where it is triaged, tracked, and optionally auto-fixed by Autobuild.
npm install @obvi/feedback-sdk<script
src="https://cdn.jsdelivr.net/npm/@obvi/feedback-sdk@latest/dist/index.global.js"
data-pub-key="fsk_pub_..."
></script>Or via unpkg:
<script
src="https://unpkg.com/@obvi/feedback-sdk@latest/dist/index.global.js"
data-pub-key="fsk_pub_..."
></script>The script auto-initializes the widget when data-pub-key is present.
import { ObviousFeedback } from "@obvi/feedback-sdk";
const widget = ObviousFeedback.init({
publicKey: "fsk_pub_...",
});
// Later: widget.destroy()<script
src="https://cdn.jsdelivr.net/npm/@obvi/feedback-sdk@latest/dist/index.global.js"
data-pub-key="fsk_pub_..."
data-theme="dark"
data-env="staging"
></script>Pass options to ObviousFeedback.init() or use data-* attributes on the script tag.
| Option | data-* attribute |
Type | Default | Description |
|---|---|---|---|---|
publicKey |
data-pub-key |
string |
Required | Your workspace feedback key. |
apiBaseUrl |
data-api-base-url |
string |
https://api.app.obvious.ai |
Base URL for the Obvious API. |
identityToken |
data-identity-token |
string |
n/a | Signed JWT for verified identity (see below). |
env |
data-env |
string |
production |
Environment label attached to submissions. |
prNumber |
data-pr-number |
number |
n/a | PR number for preview environment routing. |
theme |
data-theme |
'light' | 'dark' | 'system' |
light |
Widget color scheme. |
triggerLabel |
data-trigger-label |
string |
Open feedback |
Tooltip text on the trigger button. |
assistantPosition |
n/a | 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' |
bottom-right |
Corner for the floating trigger. |
redactSelectors |
n/a | string[] |
[] |
CSS selectors for elements to redact from DOM snapshots. |
capturePageContext |
n/a | boolean |
false |
Include a redacted DOM snapshot with submissions. |
captureConsole |
n/a | boolean |
false |
Include recent console logs with submissions. |
captureNetwork |
n/a | boolean |
false |
Include recent network requests with submissions. |
sessionReplayUrlResolver |
n/a | () => string | null | Promise<string | null> |
n/a | Returns a session replay URL (e.g. FullStory, LogRocket). |
visualSuggestions |
n/a | { enabled?: boolean } |
{ enabled: false } |
Let reporters preview narrow visual changes before submit. |
previewOnly |
n/a | boolean |
false |
Show the widget in read-only mode without submitting. |
In your Obvious workspace, go to Autobuild > Settings and create a Feedback SDK key. You'll get:
- A public key (
fsk_pub_...) — used in the browser SDK. - A private key (
fsk_secret_...) — used server-side to sign identity tokens. Store it securely; it is only shown once.
Configure allowed domains to restrict which origins can submit feedback with this key.
To attach verified user information to feedback submissions, sign an identity token on your server using the private key:
import { SignJWT } from "jose";
const identityToken = await new SignJWT({
identity: { email: user.email, name: user.name },
})
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
.setSubject("fsk_pub_...")
.setIssuedAt()
.setExpirationTime("1h")
.sign(new TextEncoder().encode("fsk_secret_..."));Pass the resulting token as identityToken in the SDK config. If the token is missing or invalid, feedback is still accepted but marked as unverified.
The widget uses Shadow DOM and does not inherit host-page styles. Control appearance with:
ObviousFeedback.init({
publicKey: "fsk_pub_...",
theme: "dark", // 'light' (default) | 'dark' | 'system'
});light— Always light. Safe for light-only host pages.dark— Always dark.system— Follows the browserprefers-color-schememedia query. Only use when the host page also follows system preference, otherwise the widget may be invisible against the background.
Override individual tokens on :root or the widget host element:
:root {
--obv-feedback-bg: #fafafa;
--obv-feedback-primary: #0066ff;
--obv-feedback-text: #1a1a1a;
--obv-feedback-border: rgba(0, 0, 0, 0.1);
}Available tokens: --obv-feedback-bg, --obv-feedback-bg-subtle, --obv-feedback-trigger-bg, --obv-feedback-text, --obv-feedback-muted, --obv-feedback-border, --obv-feedback-border-strong, --obv-feedback-primary, --obv-feedback-primary-foreground, --obv-feedback-radius, --obv-feedback-radius-card.
Initialize the widget. Only one instance can be active at a time; calling init again destroys the previous instance.
Returns a handle with:
| Method | Description |
|---|---|
destroy() |
Remove the widget from the page. |
open() |
Programmatically open the feedback card. |
getOpenIssueCount() |
Number of non-terminal issues submitted in this session. |
subscribeToOpenIssueCount(listener) |
Subscribe to open issue count changes. Returns an unsubscribe function. |
Press Cmd/Ctrl + Shift + . to open the feedback card when the widget is active.
Users can attach files (up to 25 MB each, 10 per submission) via drag-and-drop or the file picker in the widget. Attachments are uploaded directly to secure storage via pre-signed URLs.
Visual suggestions are default-off for external consumers. Enable them when you want reporters to select a page element, preview a narrow style change live, and submit that intent with the feedback payload:
ObviousFeedback.init({
publicKey: "fsk_pub_...",
visualSuggestions: { enabled: true },
});Submitted feedback includes context.visualSuggestions with { version: 1, suggestions: [...] }. The SDK only accepts font size, border radius, padding, gap, text color, and background color, and it rejects unsafe CSS syntax before applying or submitting a preview.
Use a local package link to test SDK changes inside the Obvious dashboard before publishing:
cd /Users/alexolyaiy/Repositories/obvious-feedback-sdk
bun install
bun link
bun run devIn the dashboard package:
cd /Users/alexolyaiy/Repositories/obvious/dashboard
bun link @obvi/feedback-sdk --no-saveRestart the dashboard so Vite reloads the linked package. Before committing dashboard dependency changes, remove the local link and depend on the published prerelease package.
The widget tracks submitted issues and shows their current status (received, in progress, resolved, etc.) in the feedback card. Status is refreshed automatically when the card is opened.
The SDK targets ES2020 and uses Shadow DOM, ResizeObserver, and crypto.randomUUID (with fallback). It works in all modern browsers (Chrome, Firefox, Safari, Edge).
If the widget trigger blends into the page background:
- Check the
themesetting. If your page is light-only, usetheme: 'light'(the default), not'system'. - On macOS with auto light/dark mode,
theme: 'system'will switch the widget to dark when the OS is in dark mode, even if the host page stays light.
In browsers that support constructable stylesheets, the widget applies its Shadow DOM CSS with adoptedStyleSheets, which avoids an inline <style> tag. Older browsers fall back to injecting a Shadow DOM <style> tag. If your CSP blocks inline styles in those fallback browsers, add 'unsafe-inline' to style-src for pages that load the widget.
The feedback key's allowed domains must include the hostname where the SDK runs. Check your key configuration in Obvious.
MIT