Developer Documentation

Integrate Conclude

Add feedback collection to your app in under 5 minutes. Choose the integration that fits your stack.

React SDK

First-class React and Next.js support via @conclude-fyi/react. Renders natively in your app — no iframe required.

Native rendering

The React SDK renders directly in your app using Shadow DOM for CSS isolation. Screenshots use html2canvas (instant, no browser prompt). Session recordings use rrweb (captures DOM interactions, ~50KB vs 10MB video). No iframe, no permissions dialogs.

Content Security Policy

If your app has a CSP header, add https://www.conclude.fyi to your connect-src directive so the SDK can call the API. See the CSP section below for the full list of required directives.

bash
Content-Security-Policy: connect-src 'self' https://www.conclude.fyi
1

Install

bash
npm install @conclude-fyi/react
# or
pnpm add @conclude-fyi/react
2

Add the Provider

Wrap your app with ConcludeProvider. In Next.js, add a "use client" providers file.

tsx
// app/providers.tsx
"use client";
import { ConcludeProvider } from "@conclude-fyi/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ConcludeProvider
      apiKey={process.env.NEXT_PUBLIC_CONCLUDE_API_KEY!}
      user={{
        id: currentUser.id,
        name: currentUser.name,
        email: currentUser.email,
        company: "Acme Inc",
        plan: "pro",
      }}
    >
      {children}
    </ConcludeProvider>
  );
}
tsx
// app/layout.tsx
import { Providers } from "./providers";

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
3

Add the feedback button

Drop FeedbackButton anywhere — it renders a floating button that opens the feedback panel. Theme, label, position, and enabled modes are all pulled from your dashboard config automatically.

tsx
import { FeedbackButton } from "@conclude-fyi/react";

// Uses your dashboard config (theme, modes, colors):
<FeedbackButton />

// Override specific props if needed:
<FeedbackButton position="bottom-left" label="Bug report" accentColor="#6366f1" />
4

That's it

Your users will see a floating button. When clicked, it opens a native panel where they can:

✏️Write feedback

Title, description, email

📸Screenshot

Instant page capture, no prompt

🎬Session replay

Records interactions for 60s

Enable modes from Boards → Widget Settings in the dashboard. Screenshots and recordings are attached to the feedback item automatically.

Other components

Inline board
tsx
import { FeedbackBoard } from "@conclude-fyi/react";

// Renders the full feedback board inline (uses iframe)
<FeedbackBoard sort="most_voted" />
Custom form
tsx
import { FeedbackForm } from "@conclude-fyi/react";

// Bare form — style it however you want
<FeedbackForm
  onSubmit={(item) => console.log("Submitted:", item)}
  placeholder="What can we improve?"
/>
Programmatic (hook)
tsx
import { useConclude } from "@conclude-fyi/react";

function MyComponent() {
  const { submitFeedback } = useConclude();

  async function handleClick() {
    await submitFeedback({
      title: "Feature request",
      body: "I would love to see..."
    });
  }
}

Script Tag

For any website — no build step, no framework required.

1

Add the script before </body>

html
<script src="https://www.conclude.fyi/conclude-widget.js"></script>
<script>
  Conclude.init({
    apiKey: "pk_live_YOUR_BOARD_KEY",
    // Optional overrides (dashboard config is used by default):
    // position: "bottom-right",
    // label: "Feedback",
    // accentColor: "#8083ff",
    // theme: "dark",
    submitter: {                 // optional: identify the user
      externalId: "user-123",
      name: "Jane Doe",
      email: "jane@acme.com"
    }
  });
</script>

The widget automatically fetches its config from your dashboard (theme, colors, modes, label, position). You can override any setting in the init() call.

REST API

Full API access for custom integrations. Authenticate with your API key.

1

Submit feedback

bash
curl -X POST https://www.conclude.fyi/api/v1/widget/feedback \
  -H "Content-Type: application/json" \
  -H "x-conclude-api-key: pk_live_YOUR_BOARD_KEY" \
  -d '{
    "title": "Feature request",
    "body": "I would love dark mode",
    "submitterExternalId": "user-123",
    "submitterEmail": "jane@acme.com"
  }'

The board is derived from your API key — no boardId needed in the body.

2

Get board with feedback

bash
curl https://www.conclude.fyi/api/v1/widget/boards/your-board-id \
  -H "x-conclude-api-key: pk_live_YOUR_BOARD_KEY"
3

Vote on feedback

bash
curl -X POST https://www.conclude.fyi/api/v1/widget/votes \
  -H "Content-Type: application/json" \
  -H "x-conclude-api-key: pk_live_..." \
  -d '{
    "feedbackItemId": "item-uuid",
    "submitterExternalId": "user-123"
  }'

Authentication

API Keys

Find your keys in Settings → API Keys in the Conclude dashboard.

pk_live_Publishable key — safe for browser code. Use in the widget script, React SDK, and client-side API calls.
sk_live_Secret key — server-side only. Full workspace access. Never expose in client code, HTML, or version control.

User Identification

Pass user info to connect feedback to your customers. The id field is your internal user ID — we use it to track votes and prevent duplicates. Email and name are optional but recommended for notifications.

Framework Guides

Step-by-step setup for popular frameworks.

Next.js (App Router)

1

Install the SDK

bash
npm install @conclude-fyi/react
2

Create a client component

tsx
// app/feedback-widget.tsx
"use client";
import { ConcludeProvider, FeedbackButton } from "@conclude-fyi/react";

export function FeedbackWidget() {
  return (
    <ConcludeProvider
      apiKey={process.env.NEXT_PUBLIC_CONCLUDE_API_KEY!}
      user={{
        id: currentUser.id,
        name: currentUser.name,
        email: currentUser.email,
      }}
    >
      <FeedbackButton />
    </ConcludeProvider>
  );
}
3

Add to your layout

tsx
// app/layout.tsx
import { FeedbackWidget } from "./feedback-widget";

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <FeedbackWidget />
      </body>
    </html>
  );
}

No frame-src CSP needed — the SDK renders natively, not in an iframe. If your app has a connect-src CSP directive, add https://www.conclude.fyi so the SDK can fetch your widget config and submit feedback.

React (Vite / CRA)

1

Install

bash
npm install @conclude-fyi/react
2

Add to your App component

tsx
// src/App.tsx
import { ConcludeProvider, FeedbackButton } from "@conclude-fyi/react";

function App() {
  return (
    <ConcludeProvider
      apiKey={import.meta.env.VITE_CONCLUDE_API_KEY}
      user={{
        id: user.id,
        name: user.name,
        email: user.email,
      }}
    >
      <div className="app">
        {/* Your app content */}
      </div>
      <FeedbackButton />
    </ConcludeProvider>
  );
}

That's it — no iframe or routing config. If your app sends a CSP header, remember to allow https://www.conclude.fyi in connect-src.

Vanilla JavaScript / Any Website

No build step, no framework. Just paste this before </body>.

html
<script src="https://www.conclude.fyi/conclude-widget.js"></script>
<script>
  Conclude.init({
    apiKey: "pk_live_YOUR_BOARD_KEY",
    // All optional - uses dashboard config by default:
    // position: "bottom-right",
    // label: "Feedback",
    // accentColor: "#8083ff",
    submitter: {
      externalId: "user-123",
      name: "Jane Doe",
      email: "jane@acme.com"
    }
  });
</script>

The script fetches your widget config from the dashboard (theme, colors, modes, label). Override any option in the init() call. On mobile, the button shows just an icon.

Widget Configuration

Customize the widget appearance and behavior from your board settings. The widget automatically fetches its config on load.

Available options

OptionTypeDefaultDescription
theme"light" | "dark" | "auto""dark"Widget color theme. "auto" matches the user's system preference.
accentColorstring"#8083ff"Primary color for buttons and accents (hex).
buttonLabelstring"Feedback"Text shown on the floating feedback button.
buttonPosition"bottom-right" | "bottom-left""bottom-right"Where the floating button appears.
modes("text" | "screenshot" | "recording")[]["text"]Enabled feedback modes. Multiple modes show a selector before the form.
emailField"required" | "optional" | "hidden""optional"Controls the email field visibility in the feedback form.
logoUrlstring?Custom logo URL shown in the widget header.

Feedback modes

Text

Standard text form with title, description, email, and image attachments.

Screenshot

React SDK: Instant page capture via html2canvas — no prompt. Script tag: Browser Screen Capture API.

Recording

React SDK only. rrweb session replay — captures DOM interactions for 60s. Lightweight JSON (~50KB), playable in dashboard.

Configure via dashboard

Go to Boards → [Your Board] → Settings → Widget to configure all options visually. Changes take effect immediately — the widget fetches its config on each load.

Configure via API

bash
curl -X PATCH https://www.conclude.fyi/api/internal/boards/{boardId}/widget-config \
  -H "Content-Type: application/json" \
  -H "Cookie: __session=..." \
  -d '{
    "theme": "auto",
    "accentColor": "#6366f1",
    "modes": ["text", "screenshot", "recording"],
    "emailField": "required"
  }'

Content Security Policy (CSP)

If your app doesn't send a CSP header, skip this section — the widget works out of the box. If you do, add the following entries so the SDK can reach Conclude.

DirectiveValueRequired by
connect-srchttps://www.conclude.fyiAll SDKs — API requests
img-srchttps://www.conclude.fyi blob: data:All SDKs — screenshots & logos
media-srchttps://www.conclude.fyi blob:Recording mode (playback)
script-srchttps://www.conclude.fyiScript tag users only
frame-srchttps://www.conclude.fyiiframe embed users only

React SDK

Only needs connect-src, img-src, and (if using recording mode) media-src. No frame-src or script-src required — the SDK is installed from npm and renders natively in your React tree.

Full CSP header (React SDK)

HTTP Header

text
Content-Security-Policy: connect-src 'self' https://www.conclude.fyi; img-src 'self' https://www.conclude.fyi blob: data:; media-src 'self' https://www.conclude.fyi blob:;

Next.js (next.config.ts)

ts
async headers() {
  return [{
    source: "/(.*)",
    headers: [{
      key: "Content-Security-Policy",
      value: [
        "connect-src 'self' https://www.conclude.fyi",
        "img-src 'self' https://www.conclude.fyi blob: data:",
        "media-src 'self' https://www.conclude.fyi blob:",
      ].join("; "),
    }],
  }];
}

Meta tag

html
<meta http-equiv="Content-Security-Policy" content="connect-src 'self' https://www.conclude.fyi; img-src 'self' https://www.conclude.fyi blob: data:; media-src 'self' https://www.conclude.fyi blob:;">

Script tag / iframe users

Also add script-src (to load conclude-widget.js) and frame-src (for the widget iframe overlay):

text
Content-Security-Policy:
  connect-src 'self' https://www.conclude.fyi;
  script-src 'self' https://www.conclude.fyi;
  frame-src 'self' https://www.conclude.fyi;
  img-src 'self' https://www.conclude.fyi blob: data:;
  media-src 'self' https://www.conclude.fyi blob:;

Symptoms if CSP is blocking the widget:

  • Network tab: net::ERR_BLOCKED_BY_CSP on fetches to www.conclude.fyi
  • Console:"Refused to connect to 'https://www.conclude.fyi/...' because it violates the Content Security Policy directive: "connect-src ...""
  • Script tag: "Refused to frame www.conclude.fyi" → missing frame-src
  • Screenshots broken: missing img-src blob: or data:
  • Recording playback broken: missing media-src blob:

Local development

If you're testing against localhost:3000, include both:

text
connect-src 'self' http://localhost:3000 https://www.conclude.fyi

Need help? Reach out at support@conclude.fyi