---
title: Widget Installation Guide
description: Install the SupaFeedback widget with a single script tag. Covers triggers, the floating button, survey campaigns, user identification, screenshots, appearance, shipped updates, status-update emails, notifications, and the HTTP submit API.
---

# Widget Installation Guide

Learn how to integrate the SupaFeedback widget into your website or application.

## General Installation

### 1. Get Your Project ID

First, you need the unique ID for the project you want to embed.

- Navigate to the Projects page in your dashboard.
- Select the project you wish to install.
- Open the **Setup** page for that project.
- Your unique **Project ID** will be displayed there. Copy it.

### 2. Add the Script Tag

Paste the SupaFeedback script near the end of your HTML, before the closing body tag.

- Include `<script src="https://www.supafeedback.com/widget.js?pid=YOUR_PROJECT_ID" defer></script>`
- Replace `YOUR_PROJECT_ID` with the ID you copied.
- The script loads once, fetches your project trigger settings, and attaches handlers automatically.

### 3. Choose a Trigger (optional)

On the project Setup page, pick Click or Page load. You can also turn on a floating Feedback button.

- Click (default): visitors open the widget from a `data-sf` element.
- Page load: the popup appears when the page finishes loading, and is closable with ×.
- Page load options: Show once per day, Don't show after submit, and Exclude paths.
- Floating button (optional): inject a built-in chip so the script tag is enough.

### 4. Add a Trigger Button

Any element with the `data-sf` attribute opens the feedback widget. Required for Click mode unless the floating button is on; optional for Page load.

- Add `data-sf` to a button or link.
- Example: `<button data-sf>Give Feedback</button>`
- Click the element to open the widget and send feedback to your inbox.

## Trigger Options

Configure how the widget opens from your project Setup page. Settings are applied automatically by the embed — no snippet changes required.

### Click

Default behavior. Visitors open the widget by clicking any element with `data-sf`, or by pressing Enter or Space when it has focus. Press Escape to close.

### Page load

The popup appears after the page finishes loading, in the corner chosen under Appearance. Visitors can close it with the × icon. Optional toggles:

- **Show once per day** — auto-show at most once per day in the same visitor's browser.
- **Don't show after submit** — stop auto-showing after a successful submission.
- **Exclude paths** — hide the auto-popup, floating button, and announcement card on specific routes. Add one path per line on Setup. Each path must start with `/`. A trailing `/*` excludes that path and its subpages, for example `/login` or `/supa-auth/*`. With no exclusions, the popup, floating button, and announcement card can appear on every page.

The once-per-day limit resets at midnight in the visitor's local time. Clearing browser data or using another browser may show the popup again.

Manual `data-sf` buttons still work on excluded paths.

A published [survey campaign](/docs/survey-campaigns) that matches the current page takes this auto-show slot instead. After that visitor has seen the survey, page-load feedback can appear again.

### Floating button

Optional. From Setup, turn on **Show a floating Feedback button** to inject a built-in chip. The script tag is then enough — you do not need a `data-sf` button.

- By default the button is a round icon. Set a custom label (max 24 characters) to show text beside the icon, for example **Help** or **Report a bug**.
- The button uses your widget primary color and the Widget position corner. Its question mark morphs into a chevron while the widget is open, and clicking it again closes the widget.
- Works with Click and Page load. On excluded paths, the floating button stays hidden.

Existing `data-sf` buttons keep working if you want both entry points.

## Survey Campaigns

Ask one 1–5 rating question at the right moment without replacing the feedback widget. Create a campaign from the project **Surveys** page. The installed script shows it automatically — no snippet changes required.

### Create and publish

- Set an internal name, the visitor-facing question, and labels for 1 and 5 (defaults **Very difficult** and **Very easy**).
- Save as a draft, then **Publish** when you want it live. Archive or delete a campaign to stop showing it.
- Limits: name 80 characters, question 200, each end label 40, and up to 50 target paths.
- Surveys use their own popup, separate from the feedback form.

### Target paths and delay

- Leave target paths blank to show the survey on every page.
- Add one path per line. Each path must start with `/`. A trailing `/*` includes that path and its subpages, for example `/setup` or `/onboarding/*`.
- Optional delay is 0–120 seconds after the page is ready. Use 0 to show immediately.

### How it appears

- A published survey can appear even when the widget is set to Click. You do not need Page load turned on.
- If a survey matches the current page, it takes the automatic slot instead of the page-load feedback popup for that visit.
- When more than one active survey matches, the newest campaign wins.
- Each visitor sees a campaign at most once in the same browser. Closing or submitting marks it seen, so it does not chain into another popup.
- After that visitor has seen the survey, page-load feedback can appear again if it is enabled.
- The floating button and `data-sf` still open the regular feedback form. Surveys use the same theme, primary color, and widget position as the feedback form.

### Responses and results

- Visitors tap 1–5 to submit, then can leave an optional comment (up to 2000 characters).
- If you identify the visitor with `data-sf-name` or `identify()`, that name is stored with the response.
- Open **Results** on the Surveys page for shown count, conversion rate, average score, 1–5 mix, comments, page URL, and device context.

## User Identification

Attach a customer name and email to each submission so the inbox shows a real person you can reply to. Visitors without identity still appear with an auto-generated display name.

```html
<button
  data-sf
  data-sf-name="Ada Lovelace"
  data-sf-email="ada@example.com"
>
  Give Feedback
</button>
```

You can also set `data-sf-name` and `data-sf-email` on the `<html>` element for a site-wide default. Use that when visitors open the widget from the floating button, which has no host element of its own.

In a single-page app, call `identify()` after login instead of putting attributes on every button. Later calls merge — you can pass only `name` or only `email`. Call `identify(null)` on logout. Identity is remembered for the current tab.

```javascript
window.SupaFeedback.identify({
  name: currentUser.name,
  email: currentUser.email,
});
```

A `data-sf-name` or `data-sf-email` attribute on the trigger or `<html>` still wins over `identify()`. An attached email also opts the visitor in for status-update emails.

### Custom metadata

Any other key you pass to `identify()` is saved as metadata on each feedback submission and survey response. It shows in the feedback details panel, is searchable in the inbox, and is included in exports and webhooks.

```javascript
window.SupaFeedback.identify({
  name: currentUser.name,
  email: currentUser.email,
  userId: currentUser.id,
  plan: "pro",
  seats: 12,
});
```

Metadata values must be strings, numbers, booleans or `null`. Nested objects and arrays are dropped. You can send up to 30 keys, key names up to 40 characters (letters, digits, `_`, `.`, `-`), and strings up to 500 characters. Later calls merge, and passing a key as `undefined` removes it.

## Screenshots

Every feedback form includes an optional screenshot attachment. No extra embed options are required — capture and upload ship with the widget.

- **Capture screen** — from the camera button beside Send feedback, visitors share their screen so you see exactly what they saw. Browsers that cannot capture (most phones) are asked to upload instead.
- **Upload a file** — PNG, JPEG, or WebP, one screenshot per submission.
- Large images are resized in the browser (longest edge 2560 px) and compressed to 3 MB or less before upload. Source files can be up to 10 MB.
- A compact thumbnail appears beside **Send feedback**, with preview and remove controls.
- Screenshots are private and only visible to people with access to the project in the dashboard inbox.

## Appearance

Customize how the widget looks from your project Setup page. Changes apply automatically — you do not need to update the script tag.

- **Theme** — Light for light websites, or Dark.
- **Primary color** — set buttons and accents with presets or a custom hex value.
- **Widget position** — choose **Bottom right** or **Bottom left**. That corner is used by the page-load popup, survey campaigns, What's new cards, the floating button if it is on, and any open with no `data-sf` host. A real `data-sf` click still opens next to that button.
- **Widget logo** — replace the SupaFeedback logo at the top of the form with your own PNG, JPEG, or WebP image, up to 2 MB. Choose **Use default** to switch back.
- **Widget links** — add up to three links below the form, such as **Contact us**, **Visit changelog**, or **View pricing**. Each has a label (max 60 characters), an `http` or `https` URL, and an icon (envelope, bell, wallet, chat, book, question, external link, or sparkle). Links open in a new tab; empty slots are hidden.

> Changed in [v0.19.2](/changelog#v0-19-2): the custom widget title was removed. The form heading now reads "Hi there, we value your feedback!" on every project. Use the widget logo to brand it.

## Feedback Inbox

Triage reports with statuses, AI types, search, filters, and archive, with full visitor context.

### What arrives with each report

- The message, the visitor's name and email when identified, and a private screenshot if one was attached. Anonymous visitors get a random friendly name.
- Page URL, browser, OS, device, screen size, country, and user agent are captured automatically.
- AI labels each report as an **Issue**, **Idea**, or **Other**, so visitors never have to pick a category.
- Urgent reports that match a teammate's critical alert description also get a **Critical** badge.

> Changed in [v0.19.2](/changelog#v0-19-2): the widget no longer asks visitors to choose Issue, Idea, or Other. Every report is labeled by AI instead.

### Statuses and archive

- Move reports through **New**, **Planned**, **In progress**, and **Done**. In progress and Done send status-update emails to the submitter.
- **Archive** puts a report aside without deleting it. Choose **Archived** in the status filter to find it, then change the status to bring it back.

### Search and filters

- Search matches the message, visitor name, email, page URL, and custom metadata.
- Filter by status, or switch between the Inbox and Spam folders.
- **Copy email** copies the visitor's address when a report includes one.
- **Export** downloads the current view as CSV or JSON.

### Internal notes

- Open a report and add a note for your team. Notes are never shown to the submitter and are left out of emails, webhooks, and exports.
- Authors can edit or delete their own notes. The project owner can delete any note. Reports with notes show a note count in the list.

### Keyboard shortcuts

- `j` / `k` move between reports, `Enter` or `o` opens one, and `Esc` closes it. `j` / `k` also step through reports while details are open.
- `1`–`4` set New, Planned, In progress, or Done. `e` archives and `!` toggles spam.
- Press `?` in the inbox to see the full list.

## Spam Protection

New feedback from the widget and the HTTP submit API gets a quick AI check.

- Only clearly unhelpful submissions, such as promotions, scams, and gibberish, move to Spam. Genuine issues, ideas, questions, and short messages stay in the inbox.
- Spam has its own folder, shows why a report was flagged, and never triggers new-feedback emails or browser notifications.
- Choose **Not spam** to restore a report, or **Mark as spam** to move one yourself.
- If the AI check is unavailable, the report stays in the normal inbox.

## Team Members

Share a project from the **Members** tab on Setup.

- The project owner invites people by email. They sign in with their own account to accept.
- Members can use the inbox, surveys, announcements, and widget setup.
- Only the owner can invite, resend or cancel invites, remove members, and delete the project. A member can leave a project at any time.
- Each person chooses their own email notifications: Instant, Daily digest, or Off.

## Notifications

Get in-app and browser alerts, choose Instant, Daily digest, or Off for email, and send critical reports immediately.

### In-app and browser

- **In-app notifications** — a bell in the dashboard navbar with an unread indicator and live inbox updates.
- **Browser notifications** — optional alerts while a SupaFeedback tab is open in the background.

### Email

In Setup → Notifications, each team member picks their own email preference for new feedback:

- **Instant** (default) — one email per report, up to 3 an hour. Anything beyond that is grouped into one follow-up.
- **Daily digest** — one summary a day at the hour you choose, in your time zone.
- **Off** — no new-feedback emails, except critical alerts if they are on.

Every email includes notification settings and unsubscribe links.

### Critical alerts

Urgent reports are emailed immediately, even when your preference is Daily digest or Off. Critical alerts are on by default.

- Under **What counts as critical?**, describe the situations that should alert you (up to 1000 characters). The default covers payment failures, double charges, broken login or checkout, missing data, and anything blocking a customer.
- AI checks each new report against your description. It flags a report only when it clearly matches.
- Critical reports get a **Critical** badge in the inbox and the email. The type stays Issue, Idea, or Other.
- The toggle and description are personal, so each teammate can define critical differently or turn it off.

Reports moved to Spam never trigger email, critical, or browser notifications.

### For submitters (status updates)

Close the loop after someone sends feedback. Inbox statuses are New, Planned, In progress, Done, and Archived.

- Visitors identified with `data-sf-email` or `identify()` are notified automatically. The thank-you screen says we'll email them when this is updated.
- Anonymous visitors see **Get notified when this is updated** and can enter an email inline — no extra popup.
- Submitters get an email when their feedback moves to **In progress** or **Done**. When you mark a report **Done**, add an optional note on the announcement prompt, or use **Draft Done** for an AI suggestion. The note stays editable and is only emailed when you skip or publish that prompt. Leave it blank for the usual generic email.
- The thank-you screen stays open until the visitor closes the widget, so they have time to opt in.

## Shipped updates

Publish a short What's new card on your website without changing the install.

- From the project inbox, click **Announce update**. Add a title (max 80 characters), a short description (max 200), and an optional link, then publish.
- For completed feedback, the AI suggestions beside Title and Short description draft a headline and summary. Review and edit before you publish.
- You can still announce when you mark feedback **Done**. Skip if you only want the usual status email.
- The latest published card appears in the same corner as your widget, above the floating button. Each visitor sees it once and can dismiss it. It never covers a survey and stays hidden on excluded paths.
- Published cards stack in the inbox with **Shown** and **CTR**. Choose **Unpublish** to take one off your site.

## Programmatic Triggers

Open the widget from your own UI without relying on a `data-sf` click.

```javascript
window.SupaFeedback.identify({
  name: currentUser.name,
  email: currentUser.email,
});

// Open the widget (optionally pass a trigger element)
window.SupaFeedback.open();

// Close the widget
window.SupaFeedback.close();
```

## Widget Control API

After the script loads, `window.SupaFeedback` exposes a small API:

| Method | Description |
| --- | --- |
| `open(el?)` | Opens the feedback dialog. Pass an optional trigger element for positioning and identity lookup. |
| `close()` | Closes the feedback dialog. |
| `identify(user?)` | Attach a display name, email and custom metadata to later submissions. Pass only the keys you want to patch. Pass `null` to clear. Remembered for the current tab. |

## HTTP Submit API

Agents and server-side integrations can submit feedback without running `widget.js`. `POST /api/feedback` already powers the widget.

```http
POST /api/feedback
Content-Type: application/json
```

```json
{
  "projectId": "proj_...",
  "message": "The checkout button does nothing",
  "userEmail": "ada@example.com",
  "userName": "Ada Lovelace",
  "pageUrl": "https://example.com/checkout"
}
```

Required fields:

- `projectId` — the project ID from Setup
- `message` — the report text (max 5000 characters)

SupaFeedback uses AI to classify each submission as an issue, idea, or other.

Optional fields:

- `userEmail` — visitor email so you can reply, and so the visitor is emailed when you move the report to In progress or Done
- `userName` — display name (max 40 characters)
- `pageUrl` — `http` or `https` URL of the page being reported
- `screen` — viewport size such as `390x844`
- `metadata` — flat object of custom fields such as `{ "plan": "pro", "userId": "u_1" }` (same limits as `identify()` metadata)

A successful submit returns `{ "ok": true, "id": "...", "notifyToken": "..." }`. Unknown `projectId` values return 404.

The endpoint allows CORS (`Access-Control-Allow-Origin: *`) and is rate limited (30 requests per minute per IP, 60 per project). Over the limit, it returns 429 with a `Retry-After` header in seconds. Every submission gets the same AI spam check as the widget.

Screenshots are accepted only as `multipart/form-data` with a JSON `payload` field and exactly one `screenshot` file (PNG, JPEG, or WebP, max 3 MB). The payload must include `screenshotSource` set to `capture` or `upload`.

To add an email after submitting, send `POST /api/feedback/{id}/notify` with `{ "token": notifyToken, "email": "..." }`. That visitor then gets status-update emails.

## Webhooks & Integrations

Send events to Slack, Discord, Zapier, Make, n8n or your own endpoint, and export data as CSV or JSON.

Open **Setup → Integrations** to connect a webhook, Slack or Discord. Only the project owner can manage integrations. Each one has its own event checkboxes, an on/off switch, a **Send test** button and the result of the last delivery.

- Events: `feedback.created`, `feedback.critical`, `feedback.status_changed` and `survey_response.created`. Spam never fires events.
- **Slack and Discord** use an incoming-webhook URL, so no app install or OAuth is needed. With both New and Critical feedback on, a critical report posts one 🚨 alert instead of two messages.
- **Webhook** POSTs JSON to any https URL, which is how Zapier, Make and n8n connect (use their "catch webhook" trigger). Requests time out after 5 seconds and are retried once on a network error or 5xx.

### Webhook payload

```json
{
  "id": "evt_…",
  "type": "feedback.created",
  "createdAt": "2026-09-27T10:00:00.000Z",
  "project": { "id": "…", "name": "Acme", "slug": "acme" },
  "data": {
    "id": "…",
    "type": "issue",
    "status": "new",
    "message": "The checkout button does nothing",
    "userName": "Ada Lovelace",
    "userEmail": "ada@example.com",
    "pageUrl": "https://example.com/checkout",
    "isCritical": false,
    "metadata": { "plan": "pro", "userId": "u_123" },
    "createdAt": "2026-09-27T10:00:00.000Z",
    "url": "https://www.supafeedback.com/project/acme"
  }
}
```

`feedback.status_changed` adds `previousStatus`. `survey_response.created` sends `surveyId`, `surveyName`, `rating` and `comment` instead of the feedback fields. Internal notes are never included.

### Verifying signatures

Every webhook request has `X-SupaFeedback-Event`, `X-SupaFeedback-Delivery` and `X-SupaFeedback-Signature` headers. The signature is an HMAC-SHA256 of `<t>.<raw body>` using the signing secret shown in Setup. Compute it over the raw request body, before JSON parsing.

```javascript
import { createHmac, timingSafeEqual } from "node:crypto";

// header: X-SupaFeedback-Signature, e.g. "t=1790000000,v1=5f2b…"
export const verifySupaFeedback = (rawBody, header, secret) => {
  const parts = Object.fromEntries(
    header.split(",").map((part) => part.split("=")),
  );
  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return (
    fresh &&
    expected.length === parts.v1?.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
  );
};
```

### Export

Choose **Export** in the inbox toolbar to download feedback as CSV or JSON using the current folder, filters and search. Each survey has **Export responses** too. Exports include metadata and are capped at 50,000 rows. Any project member can export.

## Framework Support

SupaFeedback is a plain script tag, so it works in any stack. Drop the snippet into your root layout or template:

- **Next.js / React** — add the script in your root layout, put `data-sf` on any button, and call `window.SupaFeedback.identify()` after the user signs in.
- **Vue** — load the script in `index.html` and bind the attribute on your trigger.
- **WordPress / HTML** — paste the snippet before `</body>`.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
