UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
データ・研究 · SKILL.md

formbricks

formbricks

Formbricks is an open-source survey and experience management platform that shows in-app and website surveys to targeted users and collects their feedback. Use when someone asks to "add surveys to my app", "Formbricks", "in-app feedback", "NPS survey", "user research", "product feedback tool", or "open-source Typeform alternative". Covers the JavaScript SDK…

Independent GitHub snapshot · Automated classification · Original documentation governs

Tterminalskills / skillsApache-2.0

スキルの説明

GitHub

name: formbricks description: >- Formbricks is an open-source survey and experience management platform that shows in-app and website surveys to targeted users and collects their feedback. Use when someone asks to "add surveys to my app", "Formbricks", "in-app feedback", "NPS survey", "user research", "product feedback tool", or "open-source Typeform alternative". Covers the JavaScript SDK, action triggers, user identification, the management API, webhooks and self-hosting. license: Apache-2.0 compatibility: "JavaScript SDK for any web framework (React, Next.js, Vue); React Native, iOS, Android and Flutter SDKs. REST API. Formbricks Cloud or self-hosted with Docker." metadata: author: terminal-skills version: "1.1.0" category: development tags: ["surveys", "feedback", "formbricks", "nps", "user-research"] repository: https://github.com/formbricks/formbricks

Formbricks

Overview

Formbricks is an open-source survey and feedback platform — embed surveys directly in your app, trigger them based on user actions, and collect targeted feedback. Unlike Typeform (generic forms) or Hotjar (page-level), Formbricks targets specific users at the right moment: after checkout, on feature use, at churn risk. NPS, CSAT, feature requests, bug reports — all in-context.

Surveys are built in the Formbricks app (Cloud at app.formbricks.com, or your own instance). Your code does three things: loads the SDK with a Workspace ID, tells Formbricks who the user is, and fires actions that surveys listen for. This skill follows Formbricks 6.0 and @formbricks/js 5.1 (September 2026), where the SDK entry point is setup({ workspaceId, appUrl }); the older init({ environmentId, apiHost }) call and the @formbricks/js/app import no longer exist.

When to Use

  • Collecting in-app feedback (NPS, CSAT, feature requests)
  • Understanding why users churn or don't convert
  • Running product research surveys targeted at specific segments
  • Need an open-source alternative to Typeform/SurveyMonkey

Instructions

Setup

npm install @formbricks/js

Find the two values the SDK needs under Settings → Workspace → Web & Mobile SDK (the docs still call this page "Connect Your App"): the Workspace ID and the SDK Connection URL (https://app.formbricks.com on Cloud). Mobile apps use @formbricks/react-native, the FormbricksSDK Swift package, com.formbricks:android, or the formbricks Flutter package with the same two values.

Self-hosting is no longer a single container: the v6 Compose stack runs the web app, PostgreSQL, Valkey, Formbricks Hub, Cube and SpiceDB. Follow https://formbricks.com/docs/self-hosting/setup/docker to fetch docker-compose.yml with its helper files from the repository's stable branch and to generate the secrets in .env (WEBAPP_URL, NEXTAUTH_SECRET, ENCRYPTION_KEY, POSTGRES_PASSWORD and others), then:

docker compose config >/dev/null      # validates .env and the compose file
docker compose up -d
docker compose ps -a                  # the *-migrate and bootstrap services must have exited 0
curl -fsS http://localhost:3000/health   # {"status":"ok"}

In-App Survey Integration

// app/formbricks.tsx — client component, rendered once in app/layout.tsx inside <Suspense>
"use client";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import formbricks from "@formbricks/js";

export default function FormbricksProvider() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    formbricks.setup({
      workspaceId: process.env.NEXT_PUBLIC_FORMBRICKS_WORKSPACE_ID!,
      appUrl: process.env.NEXT_PUBLIC_FORMBRICKS_APP_URL!, // https://app.formbricks.com or your own host
    });
  }, []);

  useEffect(() => {
    formbricks.registerRouteChange(); // lets page-view and URL-filtered actions fire on client navigation
  }, [pathname, searchParams]);

  return null;
}

In a plain React or Vue app, call formbricks.setup(...) once at startup behind if (typeof window !== "undefined") and call registerRouteChange() from the router's navigation hook. Without a bundler, paste the script tag from the Web & Mobile SDK page; it loads /js/formbricks.umd.cjs from your instance and exposes window.formbricks.

// After login — setUserId must come first, attributes are dropped for anonymous visitors
await formbricks.setUserId("usr_8f2k1");
await formbricks.setEmail("dana.whitfield@lumenlabs.io");
await formbricks.setAttributes({ plan: "pro", signup_date: "2026-01-15", company: "Lumen Labs" });
await formbricks.setLanguage("de"); // ISO code or the alias from Settings → Workspace → Survey Languages

// On sign-out, so the next person on this browser is not linked to the previous user
await formbricks.logout();

Anonymous visitors still get surveys from triggers; identification is what enables attribute targeting and shows who answered. Identification and attribute-based targeting are Enterprise Edition features, so a self-hosted instance needs a license for them.

Track Custom Actions (Triggers)

An action does nothing until it exists in Formbricks and a survey lists it as a trigger. Create it under Settings → Workspace → User Actions → Add action, switch the dialog to Code, and give it a key. No-code actions (click, page view, exit intent, 50% scroll, time on page) need no code at all, but work only on the web.

// Fire a code action — the string is the action's key
await formbricks.track("checkout_completed");

// Attach hidden-field values to the response this trigger produces
await formbricks.track("checkout_completed", { hiddenFields: { order_value: 149, plan: "pro" } });

// Or push context once; every survey shown afterwards picks it up (until reload or logout)
await formbricks.setEmbeddedData({ page_type: "checkout", plan: "pro" });
await formbricks.setEmbeddedData({ page_type: null }); // null removes the key

track() takes only { hiddenFields } as its second argument — arbitrary event properties are not stored. A hidden field must be declared on the survey first (editor → Questions tab → Hidden fields); new ids are lowercase with underscores (order_value, not orderValue).

track() returning does not mean a survey appeared — recontact options, the cooldown period and targeting may all say no. Subscribe to lifecycle events to learn what happened:

const stop = formbricks.on("formbricks_survey_shown", ({ surveyId }) => {
  console.log("survey shown", surveyId);
});
formbricks.on("formbricks_response_submitted", ({ surveyId, responseId, finished }) => {
  if (finished) console.log("completed", surveyId, responseId);
});
stop(); // on() returns the unsubscribe function

Other events: formbricks_survey_closed, formbricks_setup_successful, formbricks_action_tracked. The same events are pushed to window.dataLayer for Google Tag Manager.

API Usage

Create a key under the organization dropdown → API Keys; add each workspace it may reach with read, write or manage permission. The value is shown once. Send it in the x-api-key header.

# Check the key (single-workspace keys only; a key with several workspaces gets 400 — use /api/v2/me)
curl -s https://app.formbricks.com/api/v1/management/me -H "x-api-key: $FORMBRICKS_API_KEY"

# Create a code action that surveys can use as a trigger
curl -s -X POST https://app.formbricks.com/api/v1/management/action-classes \
  -H "x-api-key: $FORMBRICKS_API_KEY" -H "Content-Type: application/json" \
  -d "{\"workspaceId\":\"$FORMBRICKS_WORKSPACE_ID\",\"name\":\"Checkout completed\",\"type\":\"code\",\"key\":\"checkout_completed\"}"

# List surveys, then read one survey's responses (limit/skip paginate)
curl -s https://app.formbricks.com/api/v1/management/surveys -H "x-api-key: $FORMBRICKS_API_KEY"
curl -s "https://app.formbricks.com/api/v1/management/responses?surveyId=$SURVEY_ID&limit=50&skip=0" \
  -H "x-api-key: $FORMBRICKS_API_KEY"

POST /api/v1/management/surveys creates a survey, but the document is large: the API reference lists workspaceId, name, type (link or app) and status as required, the server also rejects a body without questions or blocks, and every text is a translation object ("headline": {"default": "How likely are you to recommend us?"}). Build surveys in the editor, or read an existing one with GET /api/v1/management/surveys/{surveyId} and modify that document. An API v2 (/api/v2/management/..., beta) covers responses, contacts and webhooks with the same header.

AI agents can skip the REST API: Formbricks exposes an MCP server at https://app.formbricks.com/api/mcp (or /api/mcp on your instance) with tools such as list_surveys, get_survey and create_survey, authorized through OAuth in the browser.

claude mcp add --transport http formbricks https://app.formbricks.com/api/mcp

Webhooks

curl -s -X POST https://app.formbricks.com/api/v1/webhooks \
  -H "x-api-key: $FORMBRICKS_API_KEY" -H "Content-Type: application/json" \
  -d "{\"workspaceId\":\"$FORMBRICKS_WORKSPACE_ID\",\"name\":\"NPS to Slack\",\"url\":\"https://hooks.lumenlabs.io/formbricks\",\"triggers\":[\"responseFinished\"],\"surveyIds\":[\"$SURVEY_ID\"]}"

Triggers are responseCreated, responseUpdated and responseFinished. Each delivery is a POST with the body { "webhookId", "event", "data": { ...response, "survey": { "title", "type", "status", "createdAt", "updatedAt" } } } and Standard Webhooks headers. The signing secret (whsec_…) is shown when the webhook is created; verify every request:

// verify-formbricks.ts — Standard Webhooks signature check
import crypto from "node:crypto";

export function verifyFormbricksWebhook(rawBody: string, headers: Record<string, string>) {
  const id = headers["webhook-id"];
  const timestamp = headers["webhook-timestamp"];
  const received = headers["webhook-signature"]; // "v1,<base64>"
  if (!id || !timestamp || !received) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; // replay window: 5 minutes

  const secret = Buffer.from(process.env.FORMBRICKS_WEBHOOK_SECRET!.replace(/^whsec_/, ""), "base64");
  const expected = crypto.createHmac("sha256", secret).update(`${id}.${timestamp}.${rawBody}`).digest("base64");
  const given = received.replace(/^v1,/, "");
  return given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}

Examples

Example 1: Add NPS survey after onboarding

User prompt: "Add an NPS survey that appears after a user completes onboarding."

  1. In Formbricks: create the survey from the NPS template, set Survey Type to Website & App Survey, add a code action with the key onboarding_completed as its trigger, declare a hidden field plan, and publish it.
  2. In the app: add FormbricksProvider from Instructions to app/layout.tsx, identify the user after login, and fire the action where onboarding ends:
// app/onboarding/done/page.tsx
"use client";
import { useEffect } from "react";
import formbricks from "@formbricks/js";

export default function OnboardingDone() {
  useEffect(() => {
    formbricks.track("onboarding_completed", { hiddenFields: { plan: "pro" } });
  }, []);
  return <h1>You're all set</h1>;
}
  1. Open the page as https://app.lumenlabs.io/onboarding/done?formbricksDebug=true. The console logs the setup and the tracked action, the survey slides in, and the Web & Mobile SDK page shows "Formbricks SDK is connected" (click Re-check; it does not poll). A new survey or trigger can take up to a minute to reach the browser because the SDK's configuration request is cached.

Example 2: Feature request collection

User prompt: "Let users submit feature requests from inside the app and send each one to our backend."

  1. Build a survey with a single-select category question and an open-text question; trigger it with a code action feedback_button_clicked, and under Visibility & Recontact set both Keep showing while conditions match and Ignore Cooldown Period — with only the first, the button goes quiet for the cooldown period after any survey was shown.
  2. Wire the button: <button onClick={() => formbricks.track("feedback_button_clicked")}>Give feedback</button>.
  3. Register a webhook for responseFinished (the curl call under Webhooks) and verify each delivery with verifyFormbricksWebhook. A delivery looks like (abridged — the response also carries createdAt, meta, contact, tags and more):
{
  "webhookId": "cm8x2k4tq0007l508u1d3hz9e",
  "event": "responseFinished",
  "data": {
    "id": "cm8x31w2v000dl508f0a7qk1m",
    "surveyId": "cm8wz6n3a0002l508c4r9t2yb",
    "finished": true,
    "data": { "category": "Integrations", "request": "Export responses to BigQuery" },
    "survey": { "title": "Feature requests", "type": "app", "status": "inProgress" }
  }
}

The keys inside data.data are the question ids, which you can rename in the editor before the survey is published.

Guidelines

  • In-app surveys > email surveys — Formbricks reports 6–10x higher conversion rates for app surveys than for email surveys
  • Trigger on actions — show surveys at the right moment, not randomly
  • Target specific segments — pro users, churning users, new signups
  • NPS + follow-up — always ask "why" after the score
  • Survey not showing? Three gates apply after a trigger fires: the survey's recontact option (default: show only once), the workspace cooldown period (default 7 days since the user saw any survey) and targeting. While testing, set Ignore Cooldown Period and Keep showing while conditions match, then set them back
  • Identify before attributes — setEmail and setAttributes before setUserId are dropped with a console error; attribute keys are capped at 150 per workspace
  • Strict CSP — allow your appUrl in script-src and connect-src, and call formbricks.setNonce(nonce) before setup() so survey styles pass style-src
  • Workspace ID, not Environment ID — environmentId still works in setup() and the API but is deprecated
  • API keys are server-side secrets — never ship one to the browser; the SDK needs only the public Workspace ID. Scopes are fixed at creation: to change them, delete the key and create a new one
  • Webhooks to private addresses are blocked on self-hosted instances unless DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS=1 is set — leave it off on a public server
  • Self-host for privacy — data stays on your infrastructure and the core is AGPLv3. Back up the PostgreSQL volume and .env together: the encryption key lives in .env
  • Close the loop — respond to feedback, users notice
保存したスキル