Getting started

Put an indifeed feedback board on your site in minutes. No setup wizard, no onboarding call — drop in a link and start listening.

Include page context (optional)

Help your team understand where feedback came from. The browser SDK captures the current page at click time, strips credentials, query strings, and fragments, and sends your extra context in the board URL fragment. No API key or user profile is needed.

Use the npm module

npm install @indifeed/web

Call openFeedback from your button’s click handler. The same module works in any JavaScript framework and includes TypeScript types:

import { openFeedback } from "@indifeed/web";

function giveFeedback() {
  openFeedback("acme", {
    context: {
      screen: "project-settings",
      app_version: "2.4.1"
    }
  });
}

This opens https://acme.indifeed.app/new in the current tab. Pass destination: "board" to let users browse first, or captureSource: false to omit automatic page capture. An explicit context.source_url overrides the current page and is sanitized in the same way. Invalid options or context throw before navigating.

Enhance ordinary links

Prefer HTML? Add data attributes to a real link, then explicitly initialize link enhancement. Its href still works without JavaScript:

<a href="https://acme.indifeed.app/new"
   data-indifeed-board="acme"
   data-indifeed-screen="project-settings">
  Give feedback
</a>

<script defer src="https://indifeed.dev/sdk.js"></script>
<script>
  window.addEventListener("DOMContentLoaded", function () {
    window.indifeed("initFeedbackLinks");
  });
</script>

For an npm app, call initFeedbackLinks() from @indifeed/web once after mounting instead. It returns a cleanup function. Links added later are handled too; repeated initialization keeps the first options until cleanup and does not add duplicate handlers. Optional attributes are data-indifeed-destination="board" and data-indifeed-capture-source="false". Programmatic options can also be passed to initFeedbackLinks. Modified clicks, downloads, and links targeting another tab keep their normal behavior without context enhancement; invalid link configuration falls back to the ordinary link.

The script SDK also supports window.indifeed("openFeedback", { board: "acme", context: { screen: "settings" } }) and the same destination and captureSource options.

For a custom integration, the fragment has the form #context=<percent-encoded JSON>. It is removed before the board starts analytics or error reporting. Invalid incoming context is discarded without blocking access to the board; no context is inferred from the browser referrer.

What you can include

  • A flat object with up to 20 properties and at most 4 KiB of normalized UTF-8 JSON. The encoded fragment field must be at most 16 KiB before decoding.
  • Nonempty property names of up to 64 characters (Unicode code points). Values can be strings, finite numbers, booleans, or null; nested objects and arrays are not supported.
  • Optional source_url: an absolute HTTP or HTTPS URL with a host, up to 2,048 characters. Credentials, query strings, and fragments are stripped. Control characters and invalid text are rejected.

Lifetime and visibility

Context stays in the board’s tab for up to 30 minutes, including browsing, reloads, and same-tab sign-in. It attaches to the next successful feedback submission only, then clears. A new context-bearing link replaces it; an invalid explicit link clears it. Users can preview or remove it before posting. Failed submissions retain it for retry; removal, explicit sign-out, expiry, or a different board or established user identity clears it. Opening a different tab or device does not continue the handoff.

Saved context is visible only to board staff, including private JSON exports and authorized agent feedback-detail reads. It is not shown on public feedback, broadcast in public realtime events, or used as identity or authorization.

Choose safe values

URL paths and custom values can still contain personal data or secrets. Only send information you intend to share with your board staff. Disable automatic source capture with captureSource: false or data-indifeed-capture-source="false" if your paths are sensitive. The SDK never collects a profile or browser referrer automatically. Fragment transport keeps context out of the initial HTTP request, but copied links and scripts on the page can still read it. Context is unverified customer-supplied metadata, not proof of identity.

Submitting through the API

POST /api/feedback/requests accepts the same optional context object alongside title and body, using the existing session and CSRF requirements. Raw API context JSON is capped at 64 KiB before parsing, and the saved normalized object must still fit within 4 KiB. Numbers are normalized to browser-style finite numbers. Omitted, null, or empty context saves no metadata. Invalid API context returns a validation error rather than creating feedback. Non-staff responses omit the saved context, including the creation response.

whoami is optional

Your board works perfectly in guest mode — many teams never add it. The whoami handoff only pays off when you want feedback attributed to your real users, for example because you plan to require a known user before posting.

Identify your users (optional)

To attach feedback to your app's logged-in users instead of guests, pass the user's public profile to indifeed from their browser using the indifeed SDK.

Allow your app's origin

Add your app's origin (for example https://app.example.com) as an allowed origin. In Admin → Products → Configure → Participation, copy the API key (app_pk_...). The key is public — it is not a secret.

If you regenerate the API key, the old key stops working immediately. Update the key in your SDK configuration below with the new value.

Hand off the profile

With the npm module, call whoami after your user is signed in and check the returned HTTP response:

import { whoami } from "@indifeed/web";

const response = await whoami({
  board: "acme",
  key: "app_pk_...",
  profile: {
    id: currentUser.id,
    email: currentUser.email,
    displayName: currentUser.name,
    avatar: currentUser.avatarUrl
  }
});
if (!response.ok) {
  // Handle the failed handoff in your app.
}

Or use the script SDK on every page (or only the entry page), after the user is signed in:

<script>
  window.indifeed = window.indifeed || function () {
    (window.indifeed.q = window.indifeed.q || []).push(arguments);
  };

  window.indifeed("whoami", {
    board: "acme",
    key: "app_pk_...",
    profile: {
      id: currentUser.id,
      email: currentUser.email,
      displayName: currentUser.name,
      avatar: currentUser.avatarUrl
    }
  });
</script>
<script async src="https://indifeed.dev/sdk.js"></script>

profile.id must be the user's stable identifier in your system. The profile improves attribution — requests, votes, and comments are linked to that user — but it does not grant staff or admin access, and indifeed does not verify it. Treat it as helpful context, not proof of identity.

Require known users (optional)

Once whoami is working, you can disable guest participation in Admin → Apps. Guests can still view the board, but only identified users can create requests, vote, comment, or watch. A message of your choosing is shown to blocked guests.

Set this up only after the handoff is live in your app, or your users will be locked out of posting.