# Create Display Source: https://formbricks.com/docs/api-reference/client-api--display/create-display /api-reference/openapi.json post /api/v1/client/{workspaceId}/displays Create a new display for a valid survey ID. If a userId is passed, the display is linked to the user. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Get Workspace State Source: https://formbricks.com/docs/api-reference/client-api--environment/get-workspace-state /api-reference/openapi.json get /api/v1/client/{workspaceId}/environment Retrieves the workspace state to be used in Formbricks SDKs. Note - Environments are deprecated. Use Workspace/workspaceId terminology. **Cache Behavior**: This endpoint uses server-side caching with a **5-minute TTL (Time To Live)**. Any changes to surveys, action classes, workspace settings, or other workspace data will take up to 5 minutes to reflect in the API response. This caching is implemented to improve performance for high-frequency SDK requests. # Create Response Source: https://formbricks.com/docs/api-reference/client-api--response/create-response /api-reference/openapi.json post /api/v1/client/{workspaceId}/responses Create a response for a survey and it's fields with the user's responses. The userId & meta here is optional. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Update Response Source: https://formbricks.com/docs/api-reference/client-api--response/update-response /api-reference/openapi.json put /api/v1/client/{workspaceId}/responses/{responseId} Update an existing response for example when you want to mark a response as finished or you want to change an existing response's value. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Create or Identify User Source: https://formbricks.com/docs/api-reference/client-api--user/create-or-identify-user /api-reference/openapi.json post /api/v1/client/{workspaceId}/user Endpoint for creating or identifying a user within the specified workspace. If the user already exists, this will identify them and potentially update user attributes. If they don't exist, it will create a new user. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Generate API Key Source: https://formbricks.com/docs/api-reference/generate-key Here is how you can generate an API key which gives you full access to the Formbricks Management API. Keep it safe! ## Generate API Key Click on **API Keys** in the breadcrumbs at the top left corner of the page. Your organization name is a dropdown menu. Click on the "Add API Key" button to open the creation modal. 1. Give your API key a descriptive label (e.g., "GitHub Integration", "PostHog", "Slack") 2. Configure **Workspace Access** — this controls access to data inside a Workspace (surveys, responses, contacts, webhooks, and so on): * Add each Workspace this key should reach by clicking "Add Permission" * Choose the permission level for each (read, write, or manage) * There is no "all Workspaces" option — a key can only reach the Workspaces you add here 3. Configure **Organization Access** — this controls organization administration (members and teams) and does **not** grant access to Workspace data: * Set read and/or write access as needed After creating the key, you'll see the API key value displayed once. Copy it immediately and store it in a secure location. For security reasons, you won't be able to view the complete API key again. Store your API key safely! Anyone who has your API key has access based on the permissions you've granted. For security reasons, the complete API key value cannot be viewed again after creation. ## API Key Access Scopes An API key has two independent access scopes. They control different families of endpoints and do not overlap: | Scope | What it grants | Example endpoints | | ----------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------- | | **Workspace Access** (per Workspace: read / write / manage) | Access to data and configuration inside the selected Workspaces | `/api/v2/management/surveys`, `/responses`, `/contacts`, `/webhooks` | | **Organization Access** (read / write) | Organization administration only | `/api/v2/organizations/{organizationId}/users`, `/teams`, `/api/v2/me` | **Organization Access is not a shortcut for "all Workspaces".** It only grants access to organization-administration endpoints (members and teams). To read or write survey and response data, add each Workspace explicitly under Workspace Access. A key with only Organization Access and no Workspaces cannot access any Workspace data. Access scopes are fixed when the key is created. Afterwards you can only rename the key's label — to change its Workspaces or permissions, delete the key and create a new one. On Formbricks Cloud, organization user-management endpoints (creating and updating members) are not available. Organization Access is primarily intended for self-hosted instances. ## Delete API Key If you need to revoke access for an API key: 1. Go to **Settings → Organization → API Keys** page 2. Find the key you wish to revoke and click the trash icon 3. Confirm the deletion in the dialog that appears Your API key will stop working immediately after deletion. ## API Key Permission Levels API Keys have three permission levels that can be assigned per Workspace: * **Read**: Can only perform read operations (GET requests) * **Write**: Can perform read and write operations (GET, POST, PUT, PATCH requests) * **Manage**: Has full access including delete operations (all request types) These permission levels apply to **Workspace Access**. **Organization Access** is separate: it uses read/write access for organization administration only. See [API Key Access Scopes](#api-key-access-scopes) for how the two scopes differ. # Health Check Source: https://formbricks.com/docs/api-reference/health/health-check /api-reference/openapi.json get /health Formbricks' Health Check endpoint to make sure the App & the Database are connected with each other & up! # Create Action Class Source: https://formbricks.com/docs/api-reference/management-api--action-class/create-action-class /api-reference/openapi.json post /api/v1/management/action-classes Create Action Class from the UI or from the API! You can read more about them [here](https://formbricks.com/docs/in-app-surveys/actions). # Delete Action Class Source: https://formbricks.com/docs/api-reference/management-api--action-class/delete-action-class /api-reference/openapi.json delete /api/v1/management/action-classes/{actionClassId} Deletes an existing action class # Get Action Class by ID Source: https://formbricks.com/docs/api-reference/management-api--action-class/get-action-class-by-id /api-reference/openapi.json get /api/v1/management/action-classes/{actionClassId} Fetch an Action Class by it's ID # Get All Action Classes Source: https://formbricks.com/docs/api-reference/management-api--action-class/get-all-action-classes /api-reference/openapi.json get /api/v1/management/action-classes Fetches all the action classes available in the workspace # Get All Contact Attribute Keys Source: https://formbricks.com/docs/api-reference/management-api--contact-attribute-keys/get-all-contact-attribute-keys /api-reference/openapi.json get /api/v1/management/contact-attribute-keys Retrieve a list of all contact attribute keys defined in the current workspace # Get Contact Attribute Key by ID Source: https://formbricks.com/docs/api-reference/management-api--contact-attribute-keys/get-contact-attribute-key-by-id /api-reference/openapi.json get /api/v1/management/contact-attribute-keys/{contactAttributeKeyId} Retrieve a specific contact attribute key by its ID. This returns detailed information about how a particular attribute is defined in your workspace. # Get All Contact Attributes Source: https://formbricks.com/docs/api-reference/management-api--contact-attributes/get-all-contact-attributes /api-reference/openapi.json get /api/v1/management/contact-attributes Retrieve a list of all contact attributes in the current workspace # Get All Contacts Source: https://formbricks.com/docs/api-reference/management-api--contacts/get-all-contacts /api-reference/openapi.json get /api/v1/management/contacts Retrieve a list of all contacts for the current workspace # Get Contact by ID Source: https://formbricks.com/docs/api-reference/management-api--contacts/get-contact-by-id /api-reference/openapi.json get /api/v1/management/contacts/{contactId} Retrieve a specific contact by its ID # Me Source: https://formbricks.com/docs/api-reference/management-api--me/me /api-reference/openapi.json get /api/v1/management/me Fetches the product associated with the passed API key # Create Response Source: https://formbricks.com/docs/api-reference/management-api--response/create-response /api-reference/openapi.json post /api/v1/management/responses Create a user response using the management API. This will trigger the response pipeline, including webhooks, integrations, follow-up emails, and other configured actions. # Delete Response Source: https://formbricks.com/docs/api-reference/management-api--response/delete-response /api-reference/openapi.json delete /api/v1/management/responses/{responseId} Delete a response with its ID # Get Response by ID Source: https://formbricks.com/docs/api-reference/management-api--response/get-response-by-id /api-reference/openapi.json get /api/v1/management/responses/{responseId} Fetch the entire response object based on its ID # Get Survey Responses Source: https://formbricks.com/docs/api-reference/management-api--response/get-survey-responses /api-reference/openapi.json get /api/v1/management/responses Pass the surveyId as a Query Parameter & only fetch responses for a specific survey that you have access to. # Update Response Source: https://formbricks.com/docs/api-reference/management-api--response/update-response /api-reference/openapi.json put /api/v1/management/responses/{responseId} Update an existing user response with new data. This will trigger the response pipeline, including webhooks, integrations, follow-up emails (if the response is marked as finished), and other configured actions. # Upload Public File Source: https://formbricks.com/docs/api-reference/management-api--storage/upload-public-file /api-reference/openapi.json post /api/v1/management/storage API endpoint for uploading public files. Uploaded files are public and accessible by anyone. This endpoint requires authentication and enforces a hard limit of 5 MB for all uploads. It accepts a JSON body with fileName, fileType, workspaceId, and optionally allowedFileExtensions to restrict file types. On success, it returns a signed URL for uploading the file to S3. # Create Survey Source: https://formbricks.com/docs/api-reference/management-api--survey/create-survey /api-reference/openapi.json post /api/v1/management/surveys Create a survey with granular settings, questions, their types, welcomeCard, thankYouCard, languages, etc right from this API. We'd however recommend you to do this via our Formbricks App with the UI for a better visual experience. # Delete Survey Source: https://formbricks.com/docs/api-reference/management-api--survey/delete-survey /api-reference/openapi.json delete /api/v1/management/surveys/{surveyId} Delete a Survey by its ID. Please use this only when extrememly necessary. # Get All Surveys Source: https://formbricks.com/docs/api-reference/management-api--survey/get-all-surveys /api-reference/openapi.json get /api/v1/management/surveys Fetches all existing surveys. Each survey includes both `questions` (the legacy structure) and `blocks` (the newer structure). `questions` is always derived from `blocks` for backwards compatibility, so integrations may read either field. # Get SingleUse Links Source: https://formbricks.com/docs/api-reference/management-api--survey/get-singleuse-links /api-reference/openapi.json get /api/v1/management/surveys/{surveyId}/singleUseIds Generates multiple single use survey links for a survey based on its id. # Get Survey by ID Source: https://formbricks.com/docs/api-reference/management-api--survey/get-survey-by-id /api-reference/openapi.json get /api/v1/management/surveys/{surveyId} Fetch a survey object based on its ID. The response includes both `questions` (the legacy structure) and `blocks` (the newer structure). `questions` is always derived from `blocks` for backwards compatibility, so integrations may read either field. # Update Survey Source: https://formbricks.com/docs/api-reference/management-api--survey/update-survey /api-reference/openapi.json put /api/v1/management/surveys/{surveyId} Update an existing survey with new properties. This is also what we'd recommend you to do from the UI for a better visual feedback. # Create Webhook Source: https://formbricks.com/docs/api-reference/management-api--webhook/create-webhook /api-reference/openapi.json post /api/v1/webhooks Create a new webhook right from the API and see it active right away! # Delete Webhook Source: https://formbricks.com/docs/api-reference/management-api--webhook/delete-webhook /api-reference/openapi.json delete /api/v1/webhooks/{webhookId} Delete an existing webhook by provifding it's ID. It will immediately apply and not trigger again. # Get All Webhooks Source: https://formbricks.com/docs/api-reference/management-api--webhook/get-all-webhooks /api-reference/openapi.json get /api/v1/webhooks Retrieve a list of all webhooks for the current workspace # Get Webhook Source: https://formbricks.com/docs/api-reference/management-api--webhook/get-webhook /api-reference/openapi.json get /api/v1/webhooks/{webhookId} Get a specific webhook object based on its ID # REST API Source: https://formbricks.com/docs/api-reference/rest-api Formbricks provides two APIs: the Public Client API for frontend survey interactions and the Management API for backend management tasks. Formbricks offers two types of APIs: the **Public Client API** and the **Management API**. Each API serves a different purpose, has *different* authentication requirements, and provides access to different data and settings. ## Public Client API The **Public Client API** is used by our SDKs and doesn’t require authentication, making it ideal for client-side interactions without exposing sensitive data. We currently have the following Client API methods exposed and below is their documentation attached in Postman: * [Displays API](/docs/api-reference/client-api--display/create-display) - Mark a survey as displayed or link a display to a response for a person. * [Responses API](/docs/api-reference/client-api--response/create-response) - Create & Update a Response for a Survey. ## Management API The **Management API** gives full access to all data and settings available in your Formbricks account. It requires a personal API Key for authentication, which you can generate and manage in the Settings section of the Formbricks app. We currently have the following Management API methods exposed and below is their documentation attached in Postman: * [Action Class API](/docs/api-reference/management-api--action-class/get-all-action-classes) - Create, List, and Delete Action Classes * [Contact Attribute Keys API](/docs/api-reference/management-api--contact-attribute-keys/get-all-contact-attribute-keys) - List Contact Attribute Keys * [Contacts API](/docs/api-reference/management-api--contacts/get-all-contacts) - List Contacts * [Me API](/docs/api-reference/management-api--me/me) - Retrieve Account Information * [Response API](/docs/api-reference/management-api--response/get-survey-responses) - List, List by Survey, Update, and Delete Responses * [Survey API](/docs/api-reference/management-api--survey/get-all-surveys) - List, Create, Update, generate multiple suId, and Delete Surveys * [Webhook API](/docs/api-reference/management-api--webhook/get-all-webhooks) - List, Create, and Delete Webhooks *** **Need help?** Reach out in [GitHub Discussions](https://github.com/formbricks/formbricks/discussions). # Test API Key Source: https://formbricks.com/docs/api-reference/test-key Here is how you can test your API key to make sure it is working. To test if your API key is working, you can use the following request: ### Mandatory Headers | Name | x-Api-Key | | --------------- | ------------------------ | | **Type** | string | | **Description** | Your Formbricks API key. | ### Request ```bash cURL theme={null} GET - /api/v1/me curl --location \ 'https://app.formbricks.com/api/v1/me' \ --header \ 'x-api-key: ' ``` ### Response ```bash 200 (Success) theme={null} { "id": "cll2m30r70004mx0huqkitgqv", "createdAt": "2023-08-08T18:04:59.922Z", "updatedAt": "2023-08-08T18:04:59.922Z", "type": "production", "workspace": { "id": "cll2m30r60003mx0hnemjfckr", "name": "My Workspace" }, "appSetupCompleted": false, "websiteSetupCompleted": false, } ``` ```bash 401 (Not Authenticated) theme={null} Not authenticated ``` # Create Display Source: https://formbricks.com/docs/api-v2-reference/client-api--display/create-display /api-v2-reference/openapi.yml post /client/{workspaceId}/displays Create a new display for a valid survey ID. If a userId is passed, the display is linked to the user. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Upload Private File Source: https://formbricks.com/docs/api-v2-reference/client-api--file-upload/upload-private-file /api-v2-reference/openapi.yml post /client/{workspaceId}/storage API endpoint for uploading private files. Uploaded files are kept private so that only users with access to the specified Workspace can retrieve them. The endpoint validates the survey ID, file name, and file type from the request body, and returns a signed URL for S3 uploads along with a local upload URL. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Create Response Source: https://formbricks.com/docs/api-v2-reference/client-api--response/create-response /api-v2-reference/openapi.yml post /client/{workspaceId}/responses Create a response for a survey and its fields with the user's responses. The userId & meta here is optional. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Update Response Source: https://formbricks.com/docs/api-v2-reference/client-api--response/update-response /api-v2-reference/openapi.yml put /client/{workspaceId}/responses/{responseId} Update an existing response for example when you want to mark a response as finished or you want to change an existing response's value. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Create or Identify User Source: https://formbricks.com/docs/api-v2-reference/client-api--user/create-or-identify-user /api-v2-reference/openapi.yml post /client/{workspaceId}/user Endpoint for creating or identifying a user within the specified Workspace. If the user already exists, this will identify them and potentially update user attributes. If they don't exist, it will create a new user. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Get Workspace State Source: https://formbricks.com/docs/api-v2-reference/client-api--workspace/get-workspace-state /api-v2-reference/openapi.yml get /client/{workspaceId}/environment Retrieves the Workspace state to be used in Formbricks SDKs. Note - Environments are deprecated. Use Workspace/workspaceId terminology. **Cache Behavior**: This endpoint uses server-side caching with a **5-minute TTL (Time To Live)**. Any changes to surveys, action classes, Workspace settings, or other Workspace data will take up to 5 minutes to reflect in the API response. This caching is implemented to improve performance for high-frequency SDK requests. # Health Check Source: https://formbricks.com/docs/api-v2-reference/health/health-check /api-v2-reference/openapi.yml get /health Check the health status of critical application dependencies including database and cache. # API v2 Reference (Beta) Source: https://formbricks.com/docs/api-v2-reference/introduction Formbricks offers two types of APIs: the **Public Client API** and the **Management API**. Each API serves a different purpose, has *different* authentication requirements, and provides access to different data and settings. ### API Key Setup Checkout the [API Key Setup](/docs/api-reference/rest-api) to access the Management APIs with an API Key. If you’ve forked the collection and are running it, update the `apiKey` and `workspaceId` in the collection variables with your values. The API still accepts `environmentId` as a deprecated alias for backward compatibility. We also provide post-run scripts to help auto-assign variables when running scripts. Need more help? Visit our [Website](https://formbricks.com/) or join our [Discord](https://formbricks.com/discord)! # Create a contact attribute key Source: https://formbricks.com/docs/api-v2-reference/management-api--contact-attribute-keys/create-a-contact-attribute-key /api-v2-reference/openapi.yml post /management/contact-attribute-keys Creates a contact attribute key in the database. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Delete a contact attribute key Source: https://formbricks.com/docs/api-v2-reference/management-api--contact-attribute-keys/delete-a-contact-attribute-key /api-v2-reference/openapi.yml delete /management/contact-attribute-keys/{id} Deletes a contact attribute key from the database. # Get a contact attribute key Source: https://formbricks.com/docs/api-v2-reference/management-api--contact-attribute-keys/get-a-contact-attribute-key /api-v2-reference/openapi.yml get /management/contact-attribute-keys/{id} Gets a contact attribute key from the database. # Get contact attribute keys Source: https://formbricks.com/docs/api-v2-reference/management-api--contact-attribute-keys/get-contact-attribute-keys /api-v2-reference/openapi.yml get /management/contact-attribute-keys Gets contact attribute keys from the database. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Update a contact attribute key Source: https://formbricks.com/docs/api-v2-reference/management-api--contact-attribute-keys/update-a-contact-attribute-key /api-v2-reference/openapi.yml put /management/contact-attribute-keys/{id} Updates a contact attribute key in the database. # Create a contact Source: https://formbricks.com/docs/api-v2-reference/management-api--contacts/create-a-contact /api-v2-reference/openapi.yml post /management/contacts Creates a single contact in the database. This endpoint expects a top-level `attributes` object. For bulk uploads, use `PUT /management/contacts/bulk`, which expects `contacts[].attributes[]` instead. Each contact must have a valid email address in the attributes. All attribute keys must already exist in the Workspace. The email is used as the unique identifier along with the Workspace. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Upload Bulk Contacts Source: https://formbricks.com/docs/api-v2-reference/management-api--contacts/upload-bulk-contacts /api-v2-reference/openapi.yml put /management/contacts/bulk Uploads contacts in bulk. This endpoint expects the bulk request shape: `contacts` must be an array, and each contact item must contain an `attributes` array of `{ attributeKey, value }` objects. Unlike `POST /management/contacts`, this endpoint does not accept a top-level `attributes` object. Each contact must include an `email` attribute in its `attributes` array, and that email must be valid. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Create a response Source: https://formbricks.com/docs/api-v2-reference/management-api--responses/create-a-response /api-v2-reference/openapi.yml post /management/responses Creates a response in the database. This will trigger the response pipeline, including webhooks, integrations, follow-up emails, and other configured actions. # Delete a response Source: https://formbricks.com/docs/api-v2-reference/management-api--responses/delete-a-response /api-v2-reference/openapi.yml delete /management/responses/{id} Deletes a response from the database. # Get a response Source: https://formbricks.com/docs/api-v2-reference/management-api--responses/get-a-response /api-v2-reference/openapi.yml get /management/responses/{id} Gets a response from the database. # Get responses Source: https://formbricks.com/docs/api-v2-reference/management-api--responses/get-responses /api-v2-reference/openapi.yml get /management/responses Gets responses from the database. # Update a response Source: https://formbricks.com/docs/api-v2-reference/management-api--responses/update-a-response /api-v2-reference/openapi.yml put /management/responses/{id} Updates a response in the database. This will trigger the response pipeline, including webhooks, integrations, follow-up emails (if the response is marked as finished), and other configured actions. # Get personalized survey link for a contact Source: https://formbricks.com/docs/api-v2-reference/management-api--surveys--contact-links/get-personalized-survey-link-for-a-contact /api-v2-reference/openapi.yml get /management/surveys/{surveyId}/contact-links/contacts/{contactId}/ Retrieves a personalized link for a specific survey. # Get survey links for contacts in a segment Source: https://formbricks.com/docs/api-v2-reference/management-api--surveys--contact-links/get-survey-links-for-contacts-in-a-segment /api-v2-reference/openapi.yml get /management/surveys/{surveyId}/contact-links/segments/{segmentId} Generates personalized survey links for contacts in a segment. # Create a webhook Source: https://formbricks.com/docs/api-v2-reference/management-api--webhooks/create-a-webhook /api-v2-reference/openapi.yml post /management/webhooks Creates a webhook in the database. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Delete a webhook Source: https://formbricks.com/docs/api-v2-reference/management-api--webhooks/delete-a-webhook /api-v2-reference/openapi.yml delete /management/webhooks/{id} Deletes a webhook from the database. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Get a webhook Source: https://formbricks.com/docs/api-v2-reference/management-api--webhooks/get-a-webhook /api-v2-reference/openapi.yml get /management/webhooks/{id} Gets a webhook from the database. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Get webhooks Source: https://formbricks.com/docs/api-v2-reference/management-api--webhooks/get-webhooks /api-v2-reference/openapi.yml get /management/webhooks Gets webhooks from the database. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Update a webhook Source: https://formbricks.com/docs/api-v2-reference/management-api--webhooks/update-a-webhook /api-v2-reference/openapi.yml put /management/webhooks/{id} Updates a webhook in the database. Note - Environments are deprecated. Use Workspace/workspaceId terminology. # Me Source: https://formbricks.com/docs/api-v2-reference/me/me /api-v2-reference/openapi.yml get /me Fetches the Workspaces and organizations associated with the API key. # Create a team Source: https://formbricks.com/docs/api-v2-reference/organizations-api--teams/create-a-team /api-v2-reference/openapi.yml post /organizations/{organizationId}/teams Creates a team in the database. # Delete a team Source: https://formbricks.com/docs/api-v2-reference/organizations-api--teams/delete-a-team /api-v2-reference/openapi.yml delete /organizations/{organizationId}/teams/{id} Deletes a team from the database. # Get a team Source: https://formbricks.com/docs/api-v2-reference/organizations-api--teams/get-a-team /api-v2-reference/openapi.yml get /organizations/{organizationId}/teams/{id} Gets a team from the database. # Get teams Source: https://formbricks.com/docs/api-v2-reference/organizations-api--teams/get-teams /api-v2-reference/openapi.yml get /organizations/{organizationId}/teams Gets teams from the database. # Update a team Source: https://formbricks.com/docs/api-v2-reference/organizations-api--teams/update-a-team /api-v2-reference/openapi.yml put /organizations/{organizationId}/teams/{id} Updates a team in the database. # Create a user Source: https://formbricks.com/docs/api-v2-reference/organizations-api--users/create-a-user /api-v2-reference/openapi.yml post /organizations/{organizationId}/users Create a new user in the database.
Only available for self-hosted Formbricks. # Get users Source: https://formbricks.com/docs/api-v2-reference/organizations-api--users/get-users /api-v2-reference/openapi.yml get /organizations/{organizationId}/users Gets users from the database.
Only available for self-hosted Formbricks. # Update a user Source: https://formbricks.com/docs/api-v2-reference/organizations-api--users/update-a-user /api-v2-reference/openapi.yml patch /organizations/{organizationId}/users Updates an existing user in the database.
Only available for self-hosted Formbricks. # Create a Workspace team Source: https://formbricks.com/docs/api-v2-reference/organizations-api--workspace-teams/create-a-workspace-team /api-v2-reference/openapi.yml post /organizations/{organizationId}/workspace-teams Creates a Workspace team in the database. # Delete a Workspace team Source: https://formbricks.com/docs/api-v2-reference/organizations-api--workspace-teams/delete-a-workspace-team /api-v2-reference/openapi.yml delete /organizations/{organizationId}/workspace-teams Deletes a Workspace team from the database. # Get Workspace teams Source: https://formbricks.com/docs/api-v2-reference/organizations-api--workspace-teams/get-workspace-teams /api-v2-reference/openapi.yml get /organizations/{organizationId}/workspace-teams Gets workspace teams from the database. # Update a Workspace team Source: https://formbricks.com/docs/api-v2-reference/organizations-api--workspace-teams/update-a-workspace-team /api-v2-reference/openapi.yml put /organizations/{organizationId}/workspace-teams Updates a Workspace team in the database. # Get roles Source: https://formbricks.com/docs/api-v2-reference/roles/get-roles /api-v2-reference/openapi.yml get /roles Gets roles from the database. # Contribute to Formbricks Source: https://formbricks.com/docs/development/contribution/contribution How to contribute to Formbricks We’re excited that you want to contribute to Formbricks! There are many ways to help, including reporting issues, fixing bugs, adding new features, or improving documentation. #### How to Contribute * **Issues:** Found a bug? Facing deployment problems? Have user feedback? Report an issue for the fastest response. * **Feature Requests:** Have an idea? Open an issue, tag it as an **Enhancement**, and clearly describe the issue you're solving. * **Pull Requests (PRs):** Fork the repo, make your changes, and submit a PR. * For small fixes with 1-5 lines of code changes, go ahead! * For bigger changes, we currently don't have the capacity to facilitate them. #### Talk to Us First We highly recommend engaging with us on [**GitHub Discussions**](https://github.com/formbricks/formbricks/discussions) before submitting contributions. This helps improve the chances of your PR being accepted while avoiding unnecessary work. #### Contributor License Agreement (CLA) To keep Formbricks sustainable, we require a **CLA** from all contributors. Once you open a PR, our **CLA bot** will prompt you to sign the agreement. We can only merge contributions after the CLA is signed. #### Setting Up Your Development Environment You can set up your environment using: * [**Gitpod**](/docs/development/local-setup/gitpod) * [**GitHub Codespaces**](/docs/development/local-setup/github-codespaces) * [**Local Machine Setup**](/docs/development/local-setup) For junior developers, **Gitpod or GitHub Codespaces** are recommended as they allow you to start coding in minutes. # Setup SAML with Identity Providers Source: https://formbricks.com/docs/development/guides/auth-and-provision/setup-saml-with-identity-providers This guide explains the settings you need to use to configure SAML with your Identity Provider. Once configured, obtain an XML metadata file and use it to configure SAML in Formbricks. ### SAML Registration with Identity Providers This guide explains the settings you need to use to configure SAML with your Identity Provider. Once configured, obtain an XML metadata file and use it to configure SAML in Formbricks. > **Note:** Please do not add a trailing slash at the end of the URLs. Create them exactly as shown below. **Assertion consumer service URL / Single Sign-On URL / Destination URL:** [https://app.formbricks.com/api/auth/saml/callback](https://app.formbricks.com/api/auth/saml/callback) **Entity ID / Identifier / Audience URI / Audience Restriction:** [https://saml.formbricks.com](https://saml.formbricks.com) > **Note:** [https://saml.formbricks.com](https://saml.formbricks.com) is hardcoded in Formbricks — do not replace it with your instance URL. It is the fixed SP Entity ID and must match exactly as shown in SAML assertions. **Response:** Signed **Assertion Signature:** Signed **Signature Algorithm:** RSA-SHA256 **Assertion Encryption:** Unencrypted **NameID Format:** EmailAddress **Application username:** email **Mapping Attributes / Attribute Statements:** * Name claim: If your IdP has a `name` claim, set the following claims to populate the name field: | Name | Name Format | Value | | ---- | ----------- | --------- | | name | Basic | user.name | Many IdPs do not have a `name` claim. If not, you can use different claims to populate the name field. The order of precedence is `name` -> **other options** -> `email`. **Other options:** | Name | Name Format | Value | | --------- | ----------- | --------------------------- | | firstName | Basic | **FIRST\_NAME\_EQUIVALENT** | | lastName | Basic | **LAST\_NAME\_EQUIVALENT** | Refer to the table below for the different claims you can use for each IdP. | IdP | FIRST\_NAME\_EQUIVALENT | LAST\_NAME\_EQUIVALENT | | ----------------------------- | --------------------------------- | --------------------------------- | | Okta | user.firstName | user.lastName | | Microsoft Entra ID (Azure AD) | user.givenName | user.surname | | Google Workspace | user.given\_name / user.firstName | user.family\_name / user.lastName | | OneLogin | user.FirstName / user.first\_name | user.LastName / user.last\_name | | Auth0 | user.given\_name | user.family\_name | | JumpCloud | user.firstname | user.lastname | Above provided claims may differ based on your configuration and the IdP you are using. Please refer to the documentation of your IdP for the correct claims. ### SAML With Okta For example, in Okta, once you create an account, you can click on Applications on the sidebar menu: * **Single Sign-On URL**: `https:///api/auth/saml/callback` or `http://localhost:3000/api/auth/saml/callback` (if you are running Formbricks locally) * **Audience URI (SP Entity ID)**: `https://saml.formbricks.com` (hardcoded; do not replace with your instance URL) That's it. Now when you try to login with SSO, your application on Okta will handle the authentication. # GitHub Codespaces Source: https://formbricks.com/docs/development/local-setup/github-codespaces How to set up Formbricks in a GitHub Codespaces environment ### GitHub Codespaces Setup This guide outlines how to set up Formbricks in a **GitHub Codespaces** environment. **Requirements:** * A GitHub Codespace that has support for Node.JS, pnpm, and Docker. **Steps:** 1. **Open your repository in GitHub Codespaces. If needed, clone the repository:** ```bash theme={null} git clone https://github.com/formbricks/formbricks && cd formbricks ``` 2. **Setup NodeJS with nvm (if not already configured):** ```bash theme={null} nvm install && nvm use ``` 3. **Install the dependencies:** ```bash theme={null} pnpm install ``` 4. **Create a development `.env` file and generate the required secrets:** ```bash theme={null} pnpm dev:setup ``` 5. **Generate the Next.js AGENTS.md file (optional, for AI-assisted development):** This step generates an `AGENTS.md` file at the repository root that provides Next.js documentation context for AI coding assistants (e.g. Cursor, GitHub Copilot). It runs `npx @next/codemod agents-md` under the hood. Re-run it whenever you upgrade Next.js. ```bash theme={null} pnpm agents:update ``` 6. **Launch the development setup:** ```bash theme={null} pnpm go ``` Use the Codespaces port forwarding to access Formbricks at [http://localhost:3000](http://localhost:3000). Make sure your Codespaces port configuration is set to allow access to the app. # Gitpod Source: https://formbricks.com/docs/development/local-setup/gitpod How to set up Formbricks in a Gitpod workspace ### Gitpod Setup This guide explains how to set up Formbricks in a **Gitpod** workspace. **Requirements:** * A Gitpod workspace with Node.JS, pnpm, and Docker support. **Steps:** 1. **Open the repository in Gitpod. The workspace typically clones the repo automatically. If not:** ```bash theme={null} git clone https://github.com/formbricks/formbricks && cd formbricks ``` 2. **Setup NodeJS with nvm:** ```bash theme={null} nvm install && nvm use ``` 3. **Install dependencies:** ```bash theme={null} pnpm install ``` 4. **Create a development `.env` file and generate the required secrets:** ```bash theme={null} pnpm dev:setup ``` 5. **Generate the Next.js AGENTS.md file (optional, for AI-assisted development):** This step generates an `AGENTS.md` file at the repository root that provides Next.js documentation context for AI coding assistants (e.g. Cursor, GitHub Copilot). It runs `npx @next/codemod agents-md` under the hood. Re-run it whenever you upgrade Next.js. ```bash theme={null} pnpm agents:update ``` 6. **Run the development setup:** ```bash theme={null} pnpm go ``` Access the running app via the forwarded port (typically [http://localhost:3000](http://localhost:3000) inside Gitpod). Check your Gitpod settings to ensure Docker is enabled if required. # Linux Source: https://formbricks.com/docs/development/local-setup/linux How to set up Formbricks on a Linux machine ### Local Machine Setup - Linux This guide is recommended for advanced users setting up Formbricks on a **Linux** machine. Here are the requirements for setting up Formbricks on Linux: * Node.JS (v20 recommended) * [pnpm](https://pnpm.io/) * [Docker](https://www.docker.com/) (to run PostgreSQL/MailHog) **Steps:** 1. **Clone the repository and move into the directory:** ```bash theme={null} git clone https://github.com/formbricks/formbricks && cd formbricks ``` 2. **Setup NodeJS with nvm:** ```bash theme={null} nvm install && nvm use ``` 3. **Install NodeJS packages via pnpm:** ```bash theme={null} pnpm install ``` 4. **Create a development `.env` file and generate the required secrets:** ```bash theme={null} pnpm dev:setup ``` 5. **Generate the Next.js AGENTS.md file (optional, for AI-assisted development):** This step generates an `AGENTS.md` file at the repository root that provides Next.js documentation context for AI coding assistants (e.g. Cursor, GitHub Copilot). It runs `npx @next/codemod agents-md` under the hood. Re-run it whenever you upgrade Next.js. ```bash theme={null} pnpm agents:update ``` 6. **Start the development setup:** ```bash theme={null} pnpm go ``` You can now access Formbricks at [http://localhost:3000](http://localhost:3000). Create a new account on first login as no default account is available. # Mac Source: https://formbricks.com/docs/development/local-setup/mac How to set up Formbricks on a Mac machine ### Local Machine Setup - Mac This guide is recommended for advanced users setting up Formbricks on a **Mac** machine. **Requirements:** * Node.JS (v20 recommended) * [pnpm](https://pnpm.io/) * [Docker](https://www.docker.com/) **Steps:** 1. **Clone the repository and change directory:** ```bash theme={null} git clone https://github.com/formbricks/formbricks && cd formbricks ``` 2. **Setup NodeJS with nvm:** ```bash theme={null} nvm install && nvm use ``` 3. **Install NodeJS packages with pnpm:** ```bash theme={null} pnpm install ``` 4. **Create a development `.env` file and generate the required secrets:** ```bash theme={null} pnpm dev:setup ``` 5. **Generate the Next.js AGENTS.md file (optional, for AI-assisted development):** This step generates an `AGENTS.md` file at the repository root that provides Next.js documentation context for AI coding assistants (e.g. Cursor, GitHub Copilot). It runs `npx @next/codemod agents-md` under the hood. Re-run it whenever you upgrade Next.js. ```bash theme={null} pnpm agents:update ``` 6. **Start the development setup:** ```bash theme={null} pnpm go ``` Visit [http://localhost:3000](http://localhost:3000) to access Formbricks. Ensure you create a new account at first login. # Windows Source: https://formbricks.com/docs/development/local-setup/windows How to set up Formbricks on a Windows machine ### Local Machine Setup - Windows This guide is intended for **Windows** users. For the best experience, use **WSL2** since pure Windows is not fully supported. **Requirements:** * Node.JS (v20 recommended) via WSL2 * [pnpm](https://pnpm.io/) * [Docker](https://www.docker.com/) (ensure Docker Desktop is installed with WSL2 integration enabled) **Steps (Using WSL2):** 1. **Open your WSL2 terminal and clone the repository:** ```bash theme={null} git clone https://github.com/formbricks/formbricks && cd formbricks ``` 2. **Setup NodeJS with nvm in WSL2:** ```bash theme={null} nvm install && nvm use ``` 3. **Install packages using pnpm:** ```bash theme={null} pnpm install ``` 4. **Create a development `.env` file and generate the required secrets:** ```bash theme={null} pnpm dev:setup ``` 5. **Generate the Next.js AGENTS.md file (optional, for AI-assisted development):** This step generates an `AGENTS.md` file at the repository root that provides Next.js documentation context for AI coding assistants (e.g. Cursor, GitHub Copilot). It runs `npx @next/codemod agents-md` under the hood. Re-run it whenever you upgrade Next.js. ```bash theme={null} pnpm agents:update ``` 6. **Start the development setup:** ```bash theme={null} pnpm go ``` Access Formbricks at [http://localhost:3000](http://localhost:3000). If you run into conflicts, ensure any local services (like PostgreSQL) are stopped. # Overview Source: https://formbricks.com/docs/development/overview Learn how to setup formbricks locally and build custom integrations and services. Welcome to the Development section of Formbricks! This guide is designed to help you get started with setting up the repository locally, contributing to the Formbricks codebase, and customizing it to suit your needs. Whether you're a seasoned developer or just getting started, you'll find valuable information on how to: * **Set Up Locally**: Step-by-step instructions to clone the repository, install dependencies, and run Formbricks on your local machine. * **Contribute**: Guidelines on how to contribute to the codebase, including coding standards, submitting pull requests, and collaborating with other developers. * **Customize**: Tips and tricks for customizing Formbricks to better fit your specific use cases, including modifying components and extending functionality. Dive in and start building with Formbricks today! # File and Directory Org. Source: https://formbricks.com/docs/development/standards/organization/file-and-directory-organization This document outlines the structure and organization of files and directories in the Formbricks codebase, providing guidelines for maintaining consistency and clarity across the repository. ## Repository Structure Overview The Formbricks codebase follows a monorepo structure using pnpm workspaces, with two main directories: * `apps/` - Contains full applications * `packages/` - Contains shared libraries and utilities ## Apps Directory The `apps/` directory contains complete applications: ### apps/web/ * Main Formbricks web application (Next.js) * Primary application with full feature set * Uses App Router architecture * Contains environment-specific settings and configurations ### apps/demo/ * Demo application (Next.js) * Showcases Formbricks in-product surveying functionality * Used for testing and demonstration purposes ### apps/demo-react-native/ * React Native demo app (React Native) * Demonstrates mobile integration capabilities * Example implementation for React Native ### apps/storybook/ * Component documentation * Visual documentation of UI components * Testing environment for isolated components ## Packages Directory The `packages/` directory contains shared libraries and utilities: ### packages/js-core/ * Contains core functionality for in-product surveys * Shared logic between different SDK implementations * Base classes and utilities ### packages/js/ * JavaScript SDK for browser-based applications * Used for running surveys on websites and web apps (browser) * Public NPM package ### packages/react-native/ * React Native SDK * Used to run surveys in mobile apps built with React Native / Expo * Includes native platform adaptations ### packages/lib/ * Shared business logic * Shared utilities and helpers ### packages/types/ * TypeScript type definitions * Zod schemas for validation ### packages/database/ * Database schemas and migrations * Prisma schema definitions * Migration management ### packages/surveys/ * Survey-specific functionality * Survey rendering logic and UI components * Survey state management ## Module Organization ### Core Module Structure Each feature module follows a consistent structure: ``` modules/ └── ee/ ├── insights/ │ ├── components/ │ ├── experience/ │ └── types/ └── contacts/ ├── segments/ └── components/ ``` ## Adding New Code ### New Features When adding new features, follow these guidelines: 1. **Determine Scope**: * Complete application → `apps/` * Shared library → `packages/` * Feature for existing app → appropriate module in `apps/web/modules/` 2. **Module Creation**: Create a new module with the standard structure: ``` modules/ └── your-feature/ ├── components/ # React components ├── lib/ # Business logic ├── types/ # TypeScript types ├── actions.ts # Server actions └── route.ts # API routes ``` 3. **Component Organization**: * Base UI components → `modules/ui/components/` * Feature-specific components → `modules/[feature]/components/` ## Best Practices ### Code Organization * Keep modules focused and single-purpose * Maintain clear separation between UI and business logic * Use proper TypeScript interfaces and types ### File Structure * Group related files in descriptive directories * Use consistent naming patterns * Keep files focused and modular ### Module Independence * Minimize dependencies between modules * Share common utilities through appropriate packages * Maintain clear module boundaries ### Documentation * Document complex logic and APIs as laid out in the [Documentation](/docs/development/standards/practices/documentation) section * Keep documentation current with code changes ## Testing Organization ### Test File Location * Test files should be located alongside the code they test * Use `.test.ts` or `.spec.ts` suffix for test files * Example: `user-service.test.ts` for `user-service.ts` ### Test Directory Structure ``` feature/ ├── tests/ # Test directory (if grouping tests) ├── components/ # Feature components └── lib/ # Business logic ``` ## Configuration Files ### Root Level Configuration * `.eslintrc.js` - ESLint configuration * `tsconfig.json` - TypeScript configuration * `package.json` - Package metadata and scripts * `.env` - Environment variables ### Package Level Configuration Each package maintains its own configuration files: * `package.json` - Package-specific dependencies and scripts * `tsconfig.json` - Package-specific TypeScript settings * `.eslintrc.js` - Package-specific linting rules ## Version Control ### Git Organization * `.gitignore` - Specifies ignored files and directories * `.github/` - GitHub specific configurations and workflows * `CHANGELOG.md` - Documents version changes * `LICENSE` - License information ## Conclusion Following these organizational patterns ensures: * Consistent code structure across the repository * Easy navigation and maintenance * Clear separation of concerns * Scalable architecture for future growth Remember to maintain these patterns when adding new code to keep the codebase organized and maintainable. # Modules & Components Source: https://formbricks.com/docs/development/standards/organization/module-component-structure In this document we outline the best practices for organizing modules and components across the codebase. Clear separation of concerns between modules (which encompass business logic and domain-specific functionality) and components (reusable UI elements and building blocks) promotes clarity, scalability, and maintainability. ## Introduction Our codebase follows a modular architecture with two primary organizational units: 1. **UI Components**: Reusable components in the `modules/ui/components` directory 2. **Feature Modules**: Domain-specific functionality organized by feature in the `modules` directory ## Module Organization ### Core Module Structure Modules are organized by feature and can be found in the `modules` directory. Each module typically includes: ``` modules/ └── feature-name/ ├── components/ # Feature-specific components ├── lib/ # Business logic and utilities ├── types/ # TypeScript types ├── actions.ts # Server actions └── route.ts # API routes (if needed) ``` ### Enterprise Edition (EE) Modules Enterprise features are organized in a dedicated `modules/ee` directory: ``` modules/ └── ee/ ├── insights/ │ ├── components/ │ ├── experience/ │ └── types/ └── contacts/ ├── segments/ └── components/ ``` ## Component Organization ### UI Component Structure UI components are centralized in `modules/ui/components` and follow a consistent structure: ``` modules/ui/components/ └── component-name/ ├── index.tsx # Main component implementation ├── stories.tsx # Storybook stories └── components/ # Sub-components (if needed) ``` ### Component Types 1. **Base Components**: Fundamental UI elements like Button, Input, Modal 2. **Composite Components**: More complex components that combine base components 3. **Feature-Specific Components**: Components tied to specific features ### Feature Module Example A feature module with its components and business logic: ``` modules/survey/ ├── components/ │ ├── question-form-input/ │ └── template-list/ ├── editor/ │ └── components/ ├── lib/ │ └── utils.ts └── types/ └── index.ts ``` ## Best Practices 1. **Component Organization** * Keep components focused and single-purpose * Use proper TypeScript interfaces for props * Implement Storybook stories for UI components 2. **Module Structure** * Organize by feature domain * Separate business logic from UI components * Use proper type definitions 3. **Code Sharing** * Share common utilities through the ui/lib directory * Maintain clear boundaries between modules * Use proper imports with aliases (@/modules/...) # Naming Conventions Source: https://formbricks.com/docs/development/standards/organization/naming-conventions This section outlines the guidelines for naming conventions across the codebase, ensuring consistency and clarity. ## Files and Directories ### General Files * Use lowercase and hyphen-separated names (kebab-case) for files and directories * ✅ `user-profile.ts` * ❌ `UserProfile.ts` * Group related files in directories with descriptive plural names * ✅ `components`, `services`, `utils` * ❌ `component`, `util` ### Special Files * Configuration files should follow framework conventions * ✅ `next.config.mjs`, `tailwind.config.js` * Test files should mirror source files with `.test` or `.spec` suffix * ✅ `user-service.test.ts` for `user-service.ts` * Database migration files should include timestamp and description * ✅ `20241017124431_add_documents_and_insights.sql` ## Code Symbols ### Variables and Functions * Use camelCase for variables and function names * ✅ `fetchUserData`, `isLoading`, `handleSubmit` * ❌ `FetchUserData`, `is_loading` * Boolean variables should use is/has/should prefix * ✅ `isVerifyEmailEnabled`, `hasPermission`, `shouldDisplay` * Async functions should use verb prefixes suggesting async * ✅ `fetchData`, `createUser`, `updateProfile` ### Classes and Types * Use PascalCase for: * Classes: `Config`, `Client`, `ResponseAPI` * Interfaces: `TSurveySummaryResponse`, `ApiConfig` * Type aliases: `TResponseData` * Prefix types with T and interfaces with I (when helpful) * ✅ `TStats`, `TResponseData`, `IApiConfig` * Enum names should be PascalCase, values in UPPER\_SNAKE\_CASE ```typescript theme={null} enum WorkspaceFeatureKeys { FREE = "free", STARTUP = "startup", SCALE = "scale", } ``` ### Constants * Use UPPER\_SNAKE\_CASE for constant values * ✅ `API_TIMEOUT`, `MAX_RETRIES`, `CONTAINER_ID` * Use PascalCase for constant references/objects * ✅ `ErrorCodes`, `Config` ### Database Models * Use PascalCase singular form for model names * ✅ `Survey`, `Response`, `Document` * Use camelCase for field names * ✅ `createdAt`, `workspaceId`, `isSpam` * Use snake\_case for database column names * ✅ `created_at`, `updated_at` ### Components * Use PascalCase for React components and their files * ✅ `SurveyCard.tsx`, `UserProfile.tsx` * Component-specific types should be prefixed with component name * ✅ `SurveyCardProps`, `UserProfileData` ### API and Endpoints * Use kebab-case for API endpoints * ✅ `/api/user-profile`, `/api/survey-responses` * Use camelCase for query parameters * ✅ `/api/surveys?pageSize=10&sortOrder=desc` ## Schema and Validation * Prefix Zod schemas with Z * ✅ `ZSurvey`, `ZDocument`, `ZInsight` * Use descriptive names for validation schemas * ✅ `ZUpdateDocumentAction`, `ZGenerateDocumentObjectSchema` ## Error Handling * Suffix error classes with "Error" * ✅ `ValidationError`, `DatabaseError`, `AuthenticationError` * Use descriptive names for error types * ✅ `SURVEY_NOT_FOUND`, `INVALID_RESPONSE` ## Best Practices * Keep names descriptive but concise * Be consistent within each context * Follow existing patterns in the codebase * Use full words instead of abbreviations unless widely accepted * ✅ `configuration` vs ❌ `config` (except in standard terms) * ✅ `id`, `url` (standard abbreviations are acceptable) By following these conventions, we maintain consistency and clarity across the codebase, making it more maintainable and easier to understand for all team members. # Code Formatting Source: https://formbricks.com/docs/development/standards/practices/code-formatting Standards for consistent code formatting across the Formbricks codebase ## Core Configuration ### Prettier Config Formbricks uses a standardized Prettier configuration defined in `@formbricks/config-prettier`: ```javascript theme={null} module.exports = { bracketSpacing: true, bracketSameLine: true, singleQuote: false, jsxSingleQuote: false, trailingComma: "es5", semi: true, printWidth: 110, arrowParens: "always", importOrder: [ // Mocks must be at the top as they contain vi.mock calls "(.*)/__mocks__/(.*)", "server-only", "", "^@formbricks/(.*)$", "^~/(.*)$", "^@/(.*)$", "^[./]", ], importOrderSeparation: false, importOrderSortSpecifiers: true, }; ``` Key configurations: * 110 characters line width * Double quotes for strings * ES5 trailing commas * Always use parentheses for arrow functions * Strict import ordering ### Import Order All imports follow a strict ordering: 1. Mocks (for testing) 2. Server-only imports 3. Third-party modules 4. Internal `@formbricks/*` modules 5. Local aliases (\`\~/\*) 6. Relative imports ### TypeScript Config * Strict TypeScript checking enabled * Consistent use of `tsconfig.json` extending from `@formbricks/config-typescript` * Example configuration: ```json theme={null} { "compilerOptions": { "allowImportingTsExtensions": true, "isolatedModules": true, "noEmit": true, "resolveJsonModule": true, "strict": true }, "extends": "@formbricks/config-typescript/js-library.json", "include": ["src", "package.json"] } ``` ### JSDoc Comments Required for public APIs and complex functions: ```typescript theme={null} /** * Creates a new user * @param {string} name - User's name * @returns {Promise} The created user object */ function createUser(name: string): Promise { // implementation } ``` ### Error Handling Standardized error handling using the ErrorHandler class: ```typescript theme={null} export class ErrorHandler { private static instance: ErrorHandler | null; private handleError: (error: unknown) => void; public customized = false; public static initialized = false; private constructor(errorHandler?: (error: unknown) => void) { if (errorHandler) { this.handleError = errorHandler; this.customized = true; } else { this.handleError = (error) => { Logger.getInstance().error(JSON.stringify(error)); }; } } // ... additional methods } ``` ## Enforcement Tools ### Pre-commit Hooks Using Husky and lint-staged for automated checks: ```json theme={null} { "lint-staged": { "(apps|packages)/**/*.{js,ts,jsx,tsx}": [ "prettier --write" ], "*.json": [ "prettier --write" ], "packages/database/schema.prisma": [ "prisma format" ] } } ``` The hook formats staged files only — ESLint is not part of it. Lint violations are caught by `pnpm lint` and the "Run Linters" CI job. ### ESLint Configuration ESLint runs on the flat config format. Every package and app has its own `eslint.config.mjs` that imports one of the composable tiers exported by `@formbricks/eslint-config`: | Import | Use for | | --------------------------------------- | --------------------------------------------- | | `@formbricks/eslint-config/library` | Node/TypeScript packages | | `@formbricks/eslint-config/react` | React packages (react, react-hooks, jsx-a11y) | | `@formbricks/eslint-config/react-hooks` | React packages that only need the hooks rules | | `@formbricks/eslint-config/next` | Next.js applications | Every tier builds on `@formbricks/eslint-config/base`, which holds the shared turbo, prettier, vitest and unused-vars conventions. It exports named building blocks rather than a ready-made config, so packages import one of the tiers above instead of `base` directly. Each tier is an array of flat config objects, so a package either re-exports it directly or spreads it and appends its own overrides: ```javascript theme={null} // packages//eslint.config.mjs import library from "@formbricks/eslint-config/library"; export default library; ``` ```javascript theme={null} // apps/web/eslint.config.mjs import next from "@formbricks/eslint-config/next"; export default [ { ignores: [".next/**", "public/**"] }, ...next, { rules: { "react-hooks/exhaustive-deps": "warn" } }, ]; ``` Use the extensionless subpaths above — they are the only ones the package exports. ### Continuous Integration * Automated formatting checks in CI/CD pipeline * SonarCloud integration for code quality analysis * Coverage requirements for tests ## VS Code Integration ### Required Extensions * `esbenp.prettier-vscode` - Prettier formatting * `dbaeumer.vscode-eslint` - ESLint integration * `bradlc.vscode-tailwindcss` - Tailwind CSS support ### Recommended Settings ```json theme={null} { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "typescript.tsdk": "node_modules/typescript/lib" } ``` ## Best Practices 1. **Consistent Formatting** * Always run `pnpm format` before committing * Use VS Code's format on save feature * Follow the established import order 2. **Type Safety** * Enable strict TypeScript checks * Use explicit type annotations when necessary * Avoid using `any` type 3. **Code Organization** * Keep files focused and modular * Group related functionality * Use clear, descriptive names 4. **Documentation** * Document complex logic * Use JSDoc for public APIs * Keep comments current with code changes These standards ensure consistency across the Formbricks codebase while maintaining high code quality and developer productivity. # Documentation Source: https://formbricks.com/docs/development/standards/practices/documentation Standards for documenting code and features in the Formbricks codebase ## Overview At Formbricks, we maintain strict documentation standards to ensure code clarity, maintainability, and ease of use for both internal developers and external contributors. ### Contribute to Documentation We use Mintlify to maintain our documentation. You can find more information about how to contribute to the documentation in the [README](https://github.com/formbricks/formbricks/blob/main/docs/README.md) file. ## Code Documentation ### Function Documentation 1. **Complex Logic Documentation** * All functions with complex logic must include JSDoc comments * Document parameters, return types, and potential side effects * Example: ```typescript theme={null} /** Creates a new user and initializes their preferences @param {string} name - User's full name @param {UserOptions} options - Configuration options for user creation @returns {Promise} The created user object @throws {ValidationError} If name is invalid */ async function createUser(name: string, options: UserOptions): Promise { // implementation } ``` 2. **TypeScript Ignore Comments** * When using `@ts-ignore` or `@ts-expect-error`, always include a comment explaining why * Example: ```typescript theme={null} // @ts-expect-error -- Required for dynamic function calls void window.formbricks.setup(...args); ``` ### API Documentation 1. **API Endpoints** * All new API endpoints must be documented in the OpenAPI specification * Include request/response schemas, authentication requirements, and examples * Document both Client API and Management API endpoints * Place API documentation in the `docs/api-reference` directory 2. **Authentication** * Clearly specify authentication requirements * Document API key usage and permissions * Include error scenarios and handling ### Feature Documentation * All new features must include a feature documentation file * Document the feature's purpose, usage, and implementation details * Include code examples and best practices ## Working with Mintlify We use Mintlify to write our documentation. ### File Structure 1. **MDX Files** * All documentation files must use the `.mdx` extension * Include frontmatter with required metadata: ```markdown theme={null} --- title: "Document Title" description: "Brief description of the content" icon: "appropriate-icon" --- ``` 2. **Navigation** * Add new pages to the appropriate section in `docs/mint.json` * Follow the existing navigation structure * Include proper redirects if URLs change ### Content Guidelines 1. **Writing Style** * Use clear, concise language * Break content into logical sections with proper headings * Include practical examples and code snippets * Use Mintlify components for notes, warnings, and callouts: ```markdown theme={null} Important information goes here ``` 2. **Media and Assets** * Store images in the appropriate `/images` subdirectory * Use descriptive alt text for all images * Optimize images for web delivery * Use relative paths for internal links 3. **Code Examples** * Specify the language for all code blocks * Include comments for complex code snippets * Use proper indentation and formatting ## Validation 1. **Local Testing** * Test documentation locally using Mintlify CLI: ```bash theme={null} mintlify dev ``` 2. **Review Process** * Documentation changes require peer review * Verify all links and references work * Ensure proper formatting and rendering These documentation requirements ensure that our codebase remains maintainable, accessible, and well-documented for both current and future developers. # Error Handling Source: https://formbricks.com/docs/development/standards/practices/error-handling Standards for handling errors across the Formbricks codebase ## Overview At Formbricks, we follow consistent error handling patterns to ensure reliability, debuggability, and maintainability across our codebase. This document outlines our standard approaches to error handling. ## Core Principles 1. **Type Safety**: Use typed errors and results 2. **Meaningful Messages**: Provide clear, actionable error messages 3. **Proper Propagation**: Handle or propagate errors appropriately 4. **Logging**: Ensure errors are properly logged for debugging 5. **Recovery**: Implement graceful fallbacks where possible ## Standard Error Types We maintain a set of standardized error types for different scenarios: ```typescript theme={null} export interface ApiErrorResponse { code: | "not_found" | "gone" | "bad_request" | "internal_server_error" | "unauthorized" | "method_not_allowed" | "not_authenticated" | "forbidden" | "network_error"; message: string; status: number; url: URL; details?: Record; responseMessage?: string; } ``` ## Error Handling Patterns ### API Error Handling For API endpoints: ```typescript theme={null} export const GET = async (request: Request) => { try { const authentication = await authenticateRequest(request); if (!authentication) return responses.notAuthenticatedResponse(); const data = await fetchData(authentication.workspaceId!); return responses.successResponse(data); } catch (error) { if (error instanceof DatabaseError) { return responses.badRequestResponse(error.message); } throw error; } }; ``` ### Client-Side Error Handling For client-side operations: ```typescript theme={null} const handleOperation = async () => { const result = await performAction(); if (!result.ok) { logger.error(`Operation failed: ${result.error.message}`); toast.error("Operation failed. Please try again."); return; } // Process successful result processResult(result.data); }; ``` ## Best Practices 1. **Never Swallow Errors** * Always handle or propagate errors * Log errors appropriately for debugging * Use error boundaries in React components 2. **Type Safety** * Use typed error responses * Leverage TypeScript for compile-time error checking * Define clear error interfaces 3. **Error Messages** * Include relevant context in error messages * Make messages actionable for developers * Use consistent error formatting 4. **Error Recovery** * Implement fallback behaviors where appropriate * Gracefully degrade functionality when possible * Provide user feedback for recoverable errors 5. **Documentation** * Document expected errors in function JSDoc * Include error handling in code examples * Keep error handling documentation up to date ## Testing Error Scenarios Always include error case testing: ```typescript theme={null} describe("fetchEnvironmentState()", () => { test("returns err(...) on network error", async () => { const mockNetworkError = { code: "network_error", message: "Timeout", responseMessage: "Network fail", }; const result = await fetchEnvironmentState(); expect(result.ok).toBe(false); if (!result.ok) { expect(result.error.code).toBe(mockNetworkError.code); expect(result.error.message).toBe(mockNetworkError.message); } }); }); ``` These standards ensure consistent, reliable error handling across the Formbricks codebase while maintaining good developer experience and system reliability. # Code Reviews Source: https://formbricks.com/docs/development/standards/qa/code-reviews This document outlines our comprehensive code review process at Formbricks. We maintain high quality, consistency, and best practices within our codebase through this structured approach. ## Initial Checks via GitHub Workflows When a Pull Request (PR) is opened, our automated GitHub workflows perform several critical checks: > **Note:** At Formbricks, all feature changes must be submitted through Pull Requests - direct pushes to protected branches are not allowed. * **PR Title Validation**: Ensures clear, descriptive titles following our conventions * **Build Verification**: Confirms that the build process completes successfully * **Test Suite**: Runs all automated tests to catch potential regressions * **Translation Check**: Identifies any missing translation keys * **Linting**: Enforces our coding standards and style guidelines These checks must pass before proceeding to manual review. ## Reviewer Assignment Once all automated checks pass successfully: * A team member with relevant expertise is assigned as the reviewer * The reviewer is notified automatically through GitHub * Priority is given based on PR urgency and complexity ## Functional Testing and UI Review The assigned reviewer performs thorough testing: ### Functionality Testing * Verifies that new features work as specified * Tests edge cases and error scenarios * Ensures proper error handling * Checks integration with existing features ### UI Review (if applicable) * Validates visual consistency * Checks responsive behavior * Ensures accessibility standards * Reviews user interaction flows ## Feedback Process If improvements are needed: * Reviewers add inline comments on GitHub * Feedback is specific and actionable * Code suggestions are provided when helpful * Comments follow our constructive feedback guidelines ## In-depth Code Review Our code review process utilizes both human expertise and AI assistance: ### Manual Review * Code structure and organization * Design patterns and best practices * Performance considerations * Security implications * Documentation completeness ### AI-Assisted Review We leverage advanced AI tools for additional insights: * **Code Rabbit**: Automated code analysis and suggestions * **Sonarqube**: Code quality metrics and potential issues ## Resolution and Merge Final steps before merging: * All review comments must be addressed * Required changes are implemented * At least one reviewer approval is required * All conversations must be resolved * CI/CD pipeline passes again * PR is merged into the target branch ## Best Practices * Keep PRs focused and reasonably sized * Respond to review comments promptly * Use the "Request Changes" feature when necessary * Mark conversations as resolved when addressed * Use approve/request changes workflow appropriately ## Conclusion Our code review process combines automated checks, manual review, and AI assistance to ensure high-quality code contributions. This multi-layered approach helps maintain code quality while promoting knowledge sharing within the team. Remember: Code reviews are not just about finding issues—they're opportunities for learning and improving our codebase together. # Testing Methodology Source: https://formbricks.com/docs/development/standards/qa/testing-methodology How we test Formbricks to ensure reliability, performance, and high-quality code. ## Overview At Formbricks, we maintain a rigorous testing strategy to ensure high-quality code and reliable functionality. Our testing approach is standardized and integrated into our development workflow. ## Testing Structure ### Unit Testing with Vitest We use Vitest as our primary testing framework. All unit tests follow these conventions: 1. **File Location and Naming** * Test files are co-located with the source files they test * Test files use the `.test.ts` extension (e.g., `utils.test.ts` tests `utils.ts`) 2. **Test Organization** ```typescript theme={null} import { describe, expect, test } from "vitest"; describe("ComponentName or FeatureName", () => { describe("functionName or scenario", () => { // We use the `test` function instead of `it` throught our test suite test("should describe expected behavior", () => { // test implementation }); }); }); ``` 3. **Coverage Requirements** * Minimum 85% code coverage requirement * Coverage is tracked using V8 provider * Coverage reports include: * Text summaries * HTML reports * LCOV reports ### End-to-End Testing with Playwright E2E tests are located in `apps/web/playwright/` and focus on critical user workflows. ## Testing Setup ### Configuration Our Vitest configuration (`vite.config.ts`) includes: ```typescript theme={null} test: { exclude: ['playwright/', 'node_modules/'], setupFiles: ['../../packages/lib/vitestSetup.ts'], coverage: { provider: 'v8', reporter: ['text', 'html', 'lcov'], reportsDirectory: './coverage', }, } ``` ### Test Utilities Common test utilities are available in `vitestSetup.ts`: * Mock implementations for commonly used functions * Test lifecycle hooks (beforeEach, afterEach) * Validation test helpers ## Best Practices 1. **Test Independence** ```typescript theme={null} beforeEach(() => { vi.resetModules(); vi.resetAllMocks(); }); afterEach(() => { vi.clearAllMocks(); }); ``` 2. **Mocking** * Use Vitest's built-in mocking utilities * Mock external dependencies and services * Example: ```typescript theme={null} vi.mock("@formbricks/database", () => ({ prisma: { user: { create: vi.fn(), findUnique: vi.fn(), }, }, })); ``` 3. **Assertions** * Write clear, specific assertions * Test both success and error cases * Example: ```typescript theme={null} expect(result.ok).toBe(true); expect(result.data).toEqual(expectedData); expect(async () => await invalidCall()).rejects.toThrow(ValidationError); ``` ## Quality Assurance Process 1. **Continuous Integration** * Automated test suite execution on pull requests * Coverage reports generation * Test results reporting 2. **New Features** * Must include corresponding unit tests * Must maintain or improve coverage metrics * Must include relevant E2E tests for user-facing features # Framework Usage Source: https://formbricks.com/docs/development/standards/technical/framework-usage Guidelines on how Formbricks utilizes Next.js, Tailwind CSS, and Prisma ORM for efficient development and performance. ## Introduction This document outlines the framework usage guidelines for Formbricks, which is built primarily on Next.js (with heavy use of Server-Side Rendering through the App Router), Tailwind CSS, and Prisma ORM. ## Next.js ### App Router and Server Components * Use the Next.js App Router with the /app directory for building the application. * Leverage React Server Components, which are the default in the App Router. * Perform server-side data fetching directly within page.tsx files using async function components. ### Data Fetching in page.tsx * Implement data fetching logic directly in your page.tsx components. This allows for server-side rendering without needing separate data fetching methods like getServerSideProps. * Utilize React Suspense and streaming features if using advanced asynchronous patterns. ### Server Actions for Mutations * Define Server Actions in dedicated action.ts files. * Server actions serve as entry points for mutations and delegate to service layers that perform Prisma operations and other business logic. * This approach promotes separation of concerns and maintainability by keeping form actions and mutations decoupled from UI components. ### Error Handling and Performance * Integrate error boundaries and loading states appropriately within your App Router structure. * Optimize performance with Next.js built-in features such as dynamic imports, the Image component, and code splitting. ## Tailwind CSS We follow the Tailwind framework as intended. Please have a look at the [Tailwind CSS Guidelines](https://tailwindcss.com/docs/styling-with-utility-classes) for more information. ## Prisma ORM We're working with Prisma as our ORM. Please have a look at the [Prisma ORM Documentation](https://www.prisma.io/docs/orm) for more information. # Language-Specific Source: https://formbricks.com/docs/development/standards/technical/language-specific-conventions This document outlines the language-specific conventions for the Formbricks codebase, providing guidelines for writing code in TypeScript/JavaScript. ## TypeScript Our codebase uses the `@vercel/style-guide` ESLint configurations for consistent code quality. ### ESLint Configuration We maintain three primary ESLint configurations for different package types: 1. **Library Configuration** (for packages): ```javascript theme={null} extends: [ "@vercel/style-guide/eslint/node", "@vercel/style-guide/eslint/typescript" ] ``` 2. **React Configuration** (for React applications): ```javascript theme={null} extends: [ "@vercel/style-guide/eslint/node", "@vercel/style-guide/eslint/typescript", "@vercel/style-guide/eslint/browser", "@vercel/style-guide/eslint/react", "@vercel/style-guide/eslint/next" ] ``` 3. **Next.js Configuration** (for Next.js applications): ```javascript theme={null} extends: [ "@vercel/style-guide/eslint/node", "@vercel/style-guide/eslint/typescript", "@vercel/style-guide/eslint/browser", "@vercel/style-guide/eslint/react", "@vercel/style-guide/eslint/next" ] ``` ### Key Conventions 1. **TypeScript Usage** * Strict TypeScript checking enabled * Explicit type annotations when necessary * Proper interface and type naming (prefix with T for types, I for interfaces when helpful) * No use of `any` type unless absolutely necessary 2. **Imports/Exports** * Follow strict import ordering: 1. Mocks (for testing) 2. Server-only imports 3. Third-party modules 4. Internal `@formbricks/*` modules 5. Local aliases (`~/*`) 6. Relative imports 3. **Error Handling** * Use typed error responses * Proper error propagation * Consistent error message formatting * Implement error boundaries in React components 4. **Async/Await** * Prefer async/await over raw promises * Proper error handling in async functions * Use Promise.all for parallel operations 5. **React Specific** * Functional components with TypeScript * Proper use of hooks * Consistent prop typing * Server Components by default in Next.js App Router ### Code Formatting We use Prettier with specific configurations: ```javascript theme={null} { bracketSpacing: true, bracketSameLine: true, singleQuote: false, jsxSingleQuote: false, trailingComma: "es5", semi: true, printWidth: 110, arrowParens: "always" } ``` ## Swift (iOS SDK) Our iOS SDK follows Swift best practices. ### Swift Configuration The iOS SDK requires the following: * **Swift Version**: 5.7+ * **Platform**: iOS 16.6+ * **Package Manager**: Swift Package Manager and CocoaPods support * **ARC**: Automatic Reference Counting enabled **Package.swift Configuration:** ```swift theme={null} // swift-tools-version:5.7 platforms: [ .iOS(.v16) ], ``` **CocoaPods Configuration:** ```ruby theme={null} s.platform = :ios, "16.6" s.swift_version = "5.7" s.requires_arc = true ``` ### Key Conventions 1. **Access Control Strategy** * `public`: SDK public API surface only * `internal`: Internal SDK communication and shared components * `private`: Implementation details within specific classes * Strategic use of `private(set)` for read-only public properties 2. **Architecture Patterns** * **Singleton Pattern**: Main SDK class (`Formbricks`) with static interface * **Manager Pattern**: Specialized managers (`UserManager`, `SurveyManager`, `PresentSurveyManager`) * **Builder Pattern**: Configuration objects (`FormbricksConfig.Builder`) * **Protocol-Oriented Programming**: Service protocols for dependency injection and testing 3. **Error Handling** * Custom error enums with descriptive cases (`FormbricksSDKErrorType`) * Error types conform to `LocalizedError` protocol * Structured error propagation with completion handlers * Defensive programming with guard statements and early returns 4. **Naming Conventions** * Classes: PascalCase (`FormbricksConfig`, `UserManager`) * Properties/Methods: camelCase (`workspaceId`, `setUserId`) * Constants: camelCase with descriptive names * Protocol names: Descriptive with "Protocol" suffix (`FormbricksServiceProtocol`) 5. **Code Organization** * `// MARK:` comments for logical section separation * Extensions for related functionality grouping * Consistent file structure with models, managers, networking, and views 6. **Model Design** * Prefer `struct` for data models and value types * Use `class` for reference types and managers * Implement `Codable` for JSON serialization/deserialization * Immutable properties where possible (`let` over `var`) 7. **Security & Validation** * HTTPS enforcement for all network requests * URL validation before network operations * Input validation with descriptive error messages * Secure data handling practices 8. **Asynchronous Operations** * `OperationQueue` for network operations * Completion handlers for async operations * Network connectivity checking with `NWPathMonitor` * Thread-safe operations with proper queue management ### Code Formatting We follow standard Swift formatting conventions: **Key Formatting Rules:** ```swift theme={null} // Class definitions @objc(Formbricks) public class Formbricks: NSObject { // Property declarations with access control static internal var isInitialized: Bool = false private let userManager: UserManager // Method signatures with clear parameter labels @objc public static func setup(with config: FormbricksConfig, force: Bool = false) // Guard statements for early returns guard !isInitialized else { let error = FormbricksSDKError(type: .sdkIsAlreadyInitialized) Formbricks.logger?.error(error.message) return } // Enum cases with descriptive names public enum FormbricksSDKErrorType: Int { case sdkIsNotInitialized case invalidAppUrl case networkError } ``` ## Kotlin (Android SDK) Our Android SDK codebase with Kotlin follows modern Android development practices and Kotlin conventions. ### Key Conventions 1. **Package Structure** * Logical grouping by functionality (api, model, network, manager, webview) * Clear separation of concerns across packages 2. **Kotlin Language Features** * **Object singletons** for stateless utilities and managers (`Formbricks`, `Logger`, `SDKError`) * **Data classes** for models with automatic equals/hashCode/toString (`Survey`, `User`) * **Sealed classes** for representing restricted hierarchies * **Extension functions** for utility methods (`Guard.kt`, `DateExtensions.kt`) * **Coroutines** for asynchronous operations with proper context switching 3. **Error Handling** * Centralized error definitions in `SDKError` object * Use of `Result` type for API responses * Proper exception propagation with meaningful error messages * Consistent error logging through centralized `Logger` 4. **Android-Specific Patterns** * `@Keep` annotations for ProGuard/R8 compatibility on public APIs * Proper lifecycle management in fragments and view models * Use of `FragmentManager` for UI components * Network security configuration for HTTPS enforcement 5. **Async/Await Pattern** * Prefer coroutines with `suspend` functions over callbacks * Use `withContext(Dispatchers.IO)` for network operations * Implement retry logic with `delay()` for robust API calls * Proper error handling in async functions 6. **API and Network Layer** * Retrofit for HTTP client with Gson converter * OkHttp interceptors for logging and security * Proper timeout configurations * Result-based API responses with retry mechanisms ### Code Formatting We use Android Studio's default Kotlin formatting ### Data Modeling 1. **Serialization** * Kotlinx Serialization for modern JSON handling * Gson annotations for backward compatibility: `@SerializedName` * Consistent nullable and non-nullable field declarations 2. **Data Classes** * Immutable data structures where possible * Proper use of nullable types (`String?`) * Clear property naming and documentation # Troubleshooting Source: https://formbricks.com/docs/development/support/troubleshooting Here, you'll find help with common issues. ## "The app doesn't work after Prisma migration" If the app doesn’t work after a Prisma migration, clear your browser’s storage and reload the page. This will force the app to fetch data from the server again. ![prisma](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738108186/image_dwm9hp.jpg) ## "I ran 'pnpm i' but there seems to be an error with the packages" If you run `pnpm i` and get an error with the packages, try running `pnpm clean` followed by `pnpm i` again. This often solves the problem. ## "I get a full-screen error with cryptic strings" This usually happens when the Formbricks Widget isn't correctly or completely built. ```bash theme={null} pnpm build --filter=@formbricks/js // Run the app again pnpm dev ``` ## "My machine struggles with the repository" Since we're working with a monorepo structure, the repository can get quite big. If you're having trouble working with the repository, try the following: ```bash helloWorld.js theme={null} pnpm dev --filter=@formbricks/web... ``` It’s better to use a single terminal with `pnpm dev` rather than having multiple open (one with the Formbricks app and one with the demo). ## Error: "Uncaught (in promise) SyntaxError: Unexpected token !DOCTYPE ... is not valid JSON"![Syntax Error](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738109837/image_wbxv8k.jpg) If you see this error, it happens when the person connected to the widget is deleted. To fix it, log out of the test person and reload the page.![Reset person](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738110212/image_nvkpku.jpg) # API Gateway Source: https://formbricks.com/docs/development/technical-handbook/api-gateway Gateway auth architecture for proxied service APIs ### Gateway Model Formbricks gateway auth is split into three layers: * A shared gateway-auth core authenticates the caller, normalizes the original request, and dispatches to a service authorizer. * Provider adapters translate ingress-specific auth requests into the shared shape. Envoy uses `/api/envoy-auth/[...path]`; Traefik uses `/api/traefik-auth/[...path]`. * Service authorizers own service-specific authorization. FeedbackRecords is the first registered service authorizer. ### Tokens Session-authenticated browser callers should use `/api/v3/gateway/token` with `{ "service": "feedbackRecords" }`. The token identifies the user for the gateway only; every proxied request still runs through gateway authorization. `/api/v3/feedbackRecords/token` remains a compatibility alias. ### Provider Adapters Envoy and Traefik do not send auth subrequests in the same format, so they stay as thin adapters. Envoy derives the original path from the auth request path. Traefik derives it from `X-Forwarded-Method` and `X-Forwarded-Uri`. Both adapters reuse the same gateway-auth core and FeedbackRecords authorizer. # Cube Tenant Isolation Source: https://formbricks.com/docs/development/technical-handbook/cube-tenant-isolation Threat model and controls for XM analytics tenant isolation in Cube ## Context XM analytics reads Hub feedback records through Cube. Hub stores all tenants in a shared `feedback_records` table and uses `tenant_id` to separate rows. Workspace access is the application authorization boundary. In the current Hub schema, `tenant_id` stores the authorized FeedbackDirectory ID, so every Cube query must be scoped to a directory that the authenticated workspace can access before data leaves Cube. ## Threat Model The main risk is cross-tenant read access through an unscoped Cube query. The attacker could be a regression in server code, an AI-generated query that includes malicious filters, a copied static token, or a direct request to Cube from inside the deployment network. The controls assume query bodies are attacker-influenced. Tenant identity is never trusted from the query JSON. ## Enforcement Flow Server actions and server components authorize workspace access in the Next.js app. The app validates the Cube query and rejects any `FeedbackRecords.tenantId` member supplied by users, saved charts, or AI output, including filters, dimensions, time dimensions, and order clauses. The app mints a short-lived JWT per Cube request with `tenantId`, `feedbackDirectoryId`, `workspaceId`, `organizationId`, `userId`, `scope`, `iss`, `aud`, `jti`, and `exp` claims. Cube verifies the JWT and exposes the claims through `securityContext`. Cube `queryRewrite` rejects missing tenant context, rejects caller-supplied tenant member usage, and appends `FeedbackRecords.tenantId = securityContext.tenantId` to every query. ## Audit Evidence The app records a sanitized `cubeQuery` audit event for each Cube query attempt, keyed by the JWT `jti`. Cube also emits a structured audit log line from `queryRewrite` with tenant, feedback directory, workspace, organization, user, request ID, source, and queried member names. Raw filter values are intentionally omitted from both logs. ## Operational Notes `CUBEJS_API_SECRET` is a signing secret, not an access token. Do not pass it to clients or reuse it as a bearer token. Set `CUBEJS_JWT_ISSUER` and `CUBEJS_JWT_AUDIENCE` consistently for the web app and Cube so Cube rejects tokens minted for any other audience or issuer. Set `CUBEJS_DEFAULT_API_SCOPES=meta,data` for Cube deployments so GraphQL, SQL, and orchestration APIs are not exposed unless explicitly needed. Network isolation for Cube remains recommended, but JWT-backed `queryRewrite` is the mandatory data boundary. # Database Model Source: https://formbricks.com/docs/development/technical-handbook/database-model Overview of the Formbricks database schema and relationships Formbricks uses PostgreSQL as its primary database and [Prisma](https://www.prisma.io/) as the Object-Relational Mapping (ORM) tool. The database schema is designed to support multi-tenancy, survey management, and response collection while maintaining data isolation between organizations. ## Entity Relationship Diagram The following diagram shows the core entities and their relationships in the Formbricks database: ```mermaid theme={null} erDiagram Organization ||--o{ Workspace : "has many" Organization ||--o{ Membership : "has many" Organization ||--o{ Team : "has many" Organization ||--o{ Invite : "sends" Workspace ||--o{ Language : "supports" Workspace ||--o{ WorkspaceTeam : "has" Workspace ||--o{ Survey : "contains" Workspace ||--o{ Contact : "tracks" Workspace ||--o{ ActionClass : "defines" Workspace ||--o{ ApiKeyWorkspace : "has" Workspace ||--o{ Integration : "configures" Survey ||--o{ Response : "receives" Survey ||--o{ Display : "tracks" Survey ||--o{ SurveyTrigger : "configured by" Survey ||--o{ SurveyAttributeFilter : "filtered by" Survey ||--o{ SurveyLanguage : "translated to" Contact ||--o{ Response : "provides" Contact ||--o{ Display : "sees" Contact ||--o{ ContactAttribute : "has" User ||--o{ Membership : "belongs to" User ||--o{ Account : "has" User ||--o{ TeamUser : "member of" Team ||--o{ TeamUser : "includes" Team ||--o{ WorkspaceTeam : "has access to" ApiKey ||--o{ ApiKeyWorkspace : "scopes" Response ||--o{ TagsOnResponses : "tagged with" Tag ||--o{ TagsOnResponses : "applied to" ActionClass ||--o{ SurveyTrigger : "triggers" ContactAttributeKey ||--o{ ContactAttribute : "defines" ContactAttributeKey ||--o{ SurveyAttributeFilter : "used in" ``` ## Core Models ### Organization & Workspace Management 1. **Organization** * Top-level entity for multi-tenancy * Contains multiple Workspaces and team members * Manages billing and whitelabel settings 2. **Workspace** * Main grouping mechanism for surveys and related configuration * Belongs to a single organization * Controls branding and display settings 3. **WorkspaceTeam** * Maps teams to Workspaces * Controls Workspace-level access permissions * Enables scoped collaboration across teams ### Survey Management 1. **Survey** * Central model for questionnaires * Configurable with multiple questions and display rules * Supports multiple languages and targeting filters 2. **Response** * Records user answers to surveys * Links to contact information when available * Supports tagging for analysis 3. **ActionClass** * Defines triggering points for surveys * Can be code-based or no-code configurations * Links surveys to specific user actions ### User Management 1. **User** * Represents system users (admins/team members) * Manages authentication and preferences * Can belong to multiple organizations 2. **Contact** * Represents survey respondents * Stores attributes for targeting * Tracks survey displays and responses 3. **Team** * Groups users within organizations * Controls Workspace access permissions * Facilitates collaborative survey management ## Data Isolation The database schema ensures proper data isolation through: 1. **Organization-level isolation** * Each organization has its own Workspaces * Users can belong to multiple organizations * Resources are scoped to organizations 2. **Workspace-level isolation** * Independent surveys, contacts, action classes, and integrations * Isolated API key Workspace assignments * Workspace-specific configuration and styling defaults 3. **Team-based access isolation** * Teams are linked to Workspaces through `WorkspaceTeam` * Permissions are scoped per Workspace * Access can be delegated without exposing all organization resources ## Schema Management The database schema is managed through Prisma, which provides: * Type-safe database client * Automatic migration generation * Schema versioning * Data validation * Query optimization For detailed information about specific models and their properties, refer to the [schema.prisma](https://github.com/formbricks/formbricks/blob/main/packages/database/schema.prisma) file in the repository. ## PostgreSQL Database Tables Formbricks stores all data in PostgreSQL tables. Here's a comprehensive list of all database tables and their purposes: | Table Name | Description | | --------------------- | ------------------------------------------------------------------------- | | Account | Stores third-party authentication accounts (OAuth) and their tokens | | ActionClass | Defines triggerable events that can launch surveys | | ApiKey | Stores API authentication keys for organization-level access | | ApiKeyWorkspace | Maps API keys to the Workspaces they can access | | Contact | Records information about user that can receive and respond to surveys | | ContactAttribute | Stores attribute values for contacts (e.g., role, company size) | | ContactAttributeKey | Defines available attribute types for contacts | | DataMigration | Tracks the status of database schema migrations | | Display | Records when and to whom surveys were shown | | Workspace | Main container for surveys, contacts, actions, integrations, and settings | | Integration | Stores configuration for third-party service integrations | | Invite | Manages pending invitations to join organizations | | Language | Defines supported languages for multi-lingual surveys | | Membership | Links users to organizations with specific roles | | Organization | Top-level container for Workspaces and team management | | Response | Stores survey responses and associated metadata | | Segment | Defines groups of contacts based on attributes | | Survey | Stores survey configurations, questions, and display rules | | SurveyAttributeFilter | Defines targeting rules for surveys based on contact attributes | | SurveyFollowUp | Configures automated actions based on survey responses | | SurveyLanguage | Links surveys to their supported languages | | SurveyTrigger | Connects surveys to their triggering actions | | Tag | Stores labels for categorizing responses | | TagsOnResponses | Junction table linking tags to responses | | Team | Groups users within organizations | | TeamUser | Links users to teams with specific roles | | WorkspaceTeam | Manages team access permissions to Workspaces | | User | Stores user account information and preferences | | Webhook | Manages webhook endpoints for event notifications | ## Schema Reference For detailed information about the database schema, including: * Complete field definitions * Relationships between tables * Enums and custom types * Field constraints and indices * JSON field structures Please refer to the [Prisma schema file](https://github.com/formbricks/formbricks/blob/main/packages/database/schema.prisma) in our GitHub repository. The schema.prisma file contains extensive documentation comments for each model, making it the authoritative reference for the database structure. # MCP Server Source: https://formbricks.com/docs/development/technical-handbook/mcp-server Configure and use the Formbricks v3 Surveys and Workflows MCP server ## Overview The Formbricks MCP server exposes v3 Surveys and Workflows tool surfaces for AI agents. It runs inside the Formbricks web app at `/api/mcp` and reuses the same v3 API authentication, authorization, rate limiting, response handling, and audit logging paths as the REST routes. OAuth 2.1 is the preferred authentication path for MCP clients. Formbricks API keys remain supported as a backwards-compatible fallback for local development, self-hosters, and clients that do not support MCP OAuth yet. Looking for step-by-step setup guides for Claude Code, the Claude apps, and Codex? See [Connect AI agents (MCP)](/docs/platform/mcp/overview). This handbook covers the technical internals. ## Endpoint Use the streamable HTTP MCP endpoint on the Formbricks app: ```text theme={null} https://app.formbricks.com/api/mcp ``` For local development: ```text theme={null} http://localhost:3000/api/mcp ``` The route supports `POST` requests, runs with the Next.js Node.js runtime, and sets private no-store response headers. Browser-origin MCP requests must come from the configured Formbricks app origin. OAuth clients discover the protected resource metadata here: ```text theme={null} https://app.formbricks.com/.well-known/oauth-protected-resource/api/mcp ``` The metadata advertises `/api/mcp` as the protected resource and points clients to the Better Auth issuer under `/api/auth`. ## Authentication ### OAuth 2.1 OAuth clients use Authorization Code + PKCE with Dynamic Client Registration. The MCP protected resource is: ```text theme={null} https://app.formbricks.com/api/mcp ``` The authorization server issuer is: ```text theme={null} https://app.formbricks.com/api/auth ``` Discovery endpoints: | Endpoint | Purpose | | -------------------------------------------------- | ------------------------------------ | | `/api/auth/.well-known/oauth-authorization-server` | Better Auth authorization metadata | | `/api/auth/.well-known/openid-configuration` | OpenID Connect metadata | | `/.well-known/oauth-authorization-server/api/auth` | RFC 8414 path-insertion alias | | `/.well-known/openid-configuration/api/auth` | RFC 8414 OpenID path-insertion alias | | `/.well-known/oauth-protected-resource` | MCP protected resource metadata | | `/.well-known/oauth-protected-resource/api/mcp` | MCP resource-specific metadata | Supported scopes: | Scope | Use | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `surveys:read` | `list_surveys`, `get_survey`, `validate_survey` | | `surveys:write` | `create_survey`, `patch_survey`, `delete_survey` | | `workflows:read` | `list_workflows`, `get_workflow`, `list_workflow_runs`, `get_workflow_run`, `test_workflow` | | `workflows:write` | `create_workflow`, `patch_workflow`, `duplicate_workflow`, `delete_workflow`, and the enable/disable/archive/unarchive mutations | | `feedbackRecords:read` | `list_feedback_datasets`, `list_feedback_records`, `count_feedback_records`, `get_feedback_record`, `search_feedback_records`, `find_similar_feedback_records` | | `feedbackRecords:write` | `create_feedback_record`, `create_feedback_records`, `update_feedback_record`, `delete_feedback_record` | | `openid` | OpenID Connect subject interoperability | | `profile` | User profile claims | | `email` | User email claim | | `offline_access` | Refresh tokens for MCP clients | **The rule:** every MCP tool is registered through `registerScopedTool`, which takes the required scope(s) as a mandatory argument and runs the gate before the handler. Read tools declare `:read`; every mutating tool declares `:write`. A tool cannot be registered without declaring a scope, so a new tool inherits enforcement by construction — the gate returns a `403 insufficient_scope` before the handler touches a v3 operation. Tools that more than one scope group legitimately reaches pass `{ anyOf: [...] }` instead of a plain list, which keeps them on the same registration path rather than a hand-rolled gate. The one current exception is the feedback-record tools, which centralise the gate in their own shared read/write handler factories and register through `server.registerTool` directly. Every one of their handlers is still gated, but the guarantee is by convention there rather than by construction — converging them onto `registerScopedTool` is a known follow-up. **There is no single mandatory baseline scope.** Authentication requires *at least one* resource scope (`MCP_RESOURCE_SCOPES`), so a workflows-only or `feedbackRecords:read`-only grant is a legitimate MCP client. `list_workspaces` — the workspaceId-discovery prerequisite for every resource tool — therefore gates on any resource read scope rather than one specific one; its result is derived from the caller's own memberships and key grants, so admitting any read scope exposes nothing extra. The authorization server supports all of the above, but the MCP protected-resource metadata (`/.well-known/oauth-protected-resource/api/mcp`) advertises only the resource scopes an MCP client needs — `surveys:read`, `surveys:write`, `workflows:read`, `workflows:write`, `feedbackRecords:read`, `feedbackRecords:write`, and `offline_access`. Clients derive their Dynamic Client Registration scopes from that list, so `offline_access` must be advertised there for clients to be issued refresh tokens (`openid`/`profile`/`email` are OIDC scopes the MCP resource does not require). Adding a resource scope breaks already-connected OAuth clients until they re-register. The authorization server validates `/authorize` against the scopes the **client** registered with, so a client registered before the change requests the newly advertised scope and is rejected with `invalid_scope` — it does not fall back to the scopes it already holds, and re-consenting does not help because the same `client_id` is reused. The client has to run Dynamic Client Registration again: remove and re-add the MCP server (for example `claude mcp remove ` then `claude mcp add …`), or delete its `oauthClient` row so the next connection re-registers. Ship a release note whenever this list changes. OAuth scopes gate MCP tool categories at the token layer. Actual workspace access is still evaluated at tool execution through the existing v3 authorization checks for the signed-in Formbricks user, so a token scope and a workspace role are independent gates — a call must satisfy both. Scope groups are **independent**: a token authenticates as long as it holds at least one resource scope, so a `feedbackRecords:read`-only grant is valid and simply can't reach the survey tools. `list_workspaces` is the shared discovery tool and accepts either read scope. ### API-key fallback API-key MCP access remains supported. Authenticate with a Formbricks API key in a request header: ```http theme={null} Authorization: Bearer fbk_... ``` or: ```http theme={null} x-api-key: fbk_... ``` Do not pass credentials in the query string. The MCP route rejects query credential names such as `api_key`, `x-api-key`, `access_token`, `token`, and `authorization` case-insensitively. API key permissions are enforced through the same workspace access checks as the v3 REST API: | Tool | Minimum Workspace Permission | | ------------------------------- | ---------------------------------------------------------------------------------------------------- | | `list_surveys` | `read` | | `get_survey` | `read` | | `create_survey` | `write` or `manage` | | `validate_survey` | `read` — a dry run; it validates a create or patch payload without writing | | `patch_survey` | `write` or `manage` | | `delete_survey` | `write` or `manage` | | `list_workflows` | `read` | | `get_workflow` | `read` | | `list_workflow_runs` | `read` | | `get_workflow_run` | `read` | | `test_workflow` | `read` — a dry run; it validates the definition and resolves its trigger references, creating no run | | `create_workflow` | `write` or `manage` | | `patch_workflow` | `write` or `manage` | | `duplicate_workflow` | `write` or `manage` | | `delete_workflow` | `write` or `manage` | | `enable_workflow` | `write` or `manage` | | `disable_workflow` | `write` or `manage` | | `archive_workflow` | `write` or `manage` | | `unarchive_workflow` | `write` or `manage` | | `list_feedback_datasets` | `read` | | `list_feedback_records` | `read` | | `count_feedback_records` | `read` | | `get_feedback_record` | `read` | | `search_feedback_records` | `read` | | `find_similar_feedback_records` | `read` | | `create_feedback_record` | `write` or `manage` | | `create_feedback_records` | `write` or `manage` | | `update_feedback_record` | `write` or `manage` | | `delete_feedback_record` | `write` or `manage` | Synthesized MCP scopes for an API key: `surveys:read` + `workflows:read` + `feedbackRecords:read` always; the matching `:write` scopes when the key has `write` or `manage` on any workspace. The scope gate and the per-workspace access check above are independent — a `:write` tool requires **both** the `:write` scope on the token and write/manage on the target workspace. Store fallback MCP API keys as environment variables or client secrets. Do not commit API keys into MCP client config files. ## Local Setup Start Formbricks and prepare the database: ```bash theme={null} pnpm db:up pnpm db:migrate:dev pnpm --filter @formbricks/web dev ``` Restart the dev server after changing tool registrations. The MCP server is built once at module scope by `createMcpHandler`, so hot reload does not pick up an added, removed or renamed tool — `tools/list` keeps serving the previous set until the process restarts. Verify OAuth discovery: ```bash theme={null} curl -sS http://localhost:3000/.well-known/oauth-protected-resource/api/mcp curl -sS http://localhost:3000/.well-known/oauth-authorization-server/api/auth ``` OAuth-capable MCP clients should dynamically register a public client and request `resource` equal to the MCP URL: ```text theme={null} http://localhost:3000/api/mcp ``` Manual Dynamic Client Registration smoke test: ```bash theme={null} curl -sS -X POST http://localhost:3000/api/auth/oauth2/register \ -H "Content-Type: application/json" \ --data '{ "client_name": "MCP Inspector", "redirect_uris": ["http://127.0.0.1:6274/oauth/callback"], "token_endpoint_auth_method": "none", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "scope": "openid profile email offline_access surveys:read surveys:write workflows:read workflows:write feedbackRecords:read feedbackRecords:write" }' ``` This passes `scope` explicitly, so it does **not** reproduce a real client's behavior — real MCP clients register with the scopes from the protected-resource metadata's `scopes_supported` (`surveys:read surveys:write workflows:read workflows:write feedbackRecords:read feedbackRecords:write offline_access`). Because the authorization server validates the `/authorize` request against the client's **registered** scopes, a client that registers with a narrower set than it later requests (e.g. it registers surveys-only but requests `offline_access`) is rejected with `invalid_scope`. To smoke-test the real path, omit `scope` and let the client adopt the advertised set, or pass exactly what the metadata advertises. For the same reason, the `scope` in the MCP endpoint's 401 `WWW-Authenticate` challenge is exactly the metadata's `scopes_supported`, not a subset — a client that hits the 401 before fetching the metadata uses the challenge string as its registration scope. The two are derived from one constant (`MCP_CHALLENGE_SCOPE`) and a test asserts they stay equal; when they diverged, every new client failed its first connect with `invalid_scope` and only succeeded on retry. The consent screen is served at `/account/authorize`. Users can revoke approved MCP clients from `/account/settings/authorized-apps`. For API-key fallback, create an API key in the Formbricks app with access to the target workspace. Use the least privileged permission needed by the agent workflow. Verify that the MCP endpoint can list tools: ```bash theme={null} export FORMBRICKS_MCP_API_KEY="fbk_..." curl -sS -X POST http://localhost:3000/api/mcp \ -H "Accept: application/json, text/event-stream" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $FORMBRICKS_MCP_API_KEY" \ --data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' ``` ## Codex Configuration ### OAuth Add the HTTP MCP server. Include `--oauth-resource` so the OAuth access token is audience-bound to the MCP resource URL: ```bash theme={null} codex mcp add formbricks-local \ --url http://localhost:3000/api/mcp \ --oauth-resource http://localhost:3000/api/mcp ``` Authenticate and request the scopes the agent needs: ```bash theme={null} codex mcp login formbricks-local \ --scopes openid,profile,email,offline_access,surveys:read,surveys:write,workflows:read,workflows:write,feedbackRecords:read,feedbackRecords:write ``` For read-only usage, omit the `:write` scopes: ```bash theme={null} codex mcp login formbricks-local \ --scopes openid,profile,email,offline_access,surveys:read,workflows:read,feedbackRecords:read ``` Scope groups are independent — a token needs at least one resource scope, but not all of them. To use only the feedback-record tools, request `feedbackRecords:read` (plus `feedbackRecords:write` to create) and omit the survey scopes. Codex opens the browser to Formbricks, completes Dynamic Client Registration, and stores the OAuth client and tokens in Codex's credential store. The user approves the requested scopes on the Formbricks consent screen. Equivalent Codex config for the server entry: ```toml theme={null} [mcp_servers.formbricks-local] url = "http://localhost:3000/api/mcp" oauth_resource = "http://localhost:3000/api/mcp" ``` Verify the registration: ```bash theme={null} codex mcp list codex mcp get formbricks-local ``` Then ask Codex to use the configured server: ```text theme={null} Use the formbricks-local MCP server to list surveys for workspace clxx1234567890123456789012. ``` ### API-key fallback Store the API key in the shell environment: ```bash theme={null} export FORMBRICKS_MCP_API_KEY="fbk_..." ``` For Codex Desktop on macOS, persist the value in the launch environment and restart Codex Desktop: ```bash theme={null} launchctl setenv FORMBRICKS_MCP_API_KEY "fbk_..." ``` Register the local MCP server with a bearer-token environment variable: ```bash theme={null} codex mcp add formbricks-local-api-key \ --url http://localhost:3000/api/mcp \ --bearer-token-env-var FORMBRICKS_MCP_API_KEY ``` ## Claude Configuration ### Claude Code #### OAuth Add the remote HTTP MCP server: ```bash theme={null} claude mcp add --transport http formbricks-local http://localhost:3000/api/mcp ``` Run `/mcp` and authenticate `formbricks-local`. Claude Code discovers the MCP protected-resource metadata, registers a public client whose scopes are taken from that metadata's `scopes_supported` (`surveys:read surveys:write workflows:read workflows:write feedbackRecords:read feedbackRecords:write offline_access`), and launches the browser-based OAuth flow — there is no scope-entry step. You approve the requested scopes on the Formbricks consent screen. A `:write` tool requires both the matching `:write` scope on the token and write/manage permission on the target workspace at execution time (see [Authentication](#authentication)). Verify it: ```bash theme={null} claude mcp list claude mcp get formbricks-local ``` For project-shared Claude Code configuration, use `.mcp.json` without static credentials: ```json theme={null} { "mcpServers": { "formbricks-local": { "type": "http", "url": "http://localhost:3000/api/mcp" } } } ``` If the installed Claude Code version does not start the OAuth flow for remote HTTP servers, use the API-key fallback below until that client is updated. #### API-key fallback Set the API key: ```bash theme={null} export FORMBRICKS_MCP_API_KEY="fbk_..." ``` Add the HTTP MCP server: ```bash theme={null} claude mcp add --transport http formbricks-local-api-key http://localhost:3000/api/mcp \ --header "Authorization: Bearer $FORMBRICKS_MCP_API_KEY" ``` For a project-shared fallback configuration, keep the API key outside git: ```json theme={null} { "mcpServers": { "formbricks-local-api-key": { "headers": { "Authorization": "Bearer ${FORMBRICKS_MCP_API_KEY}" }, "type": "http", "url": "http://localhost:3000/api/mcp" } } } ``` Claude Code will fail to parse this fallback config if `FORMBRICKS_MCP_API_KEY` is not set. ### Claude Desktop If the installed Claude Desktop build supports remote HTTP MCP server entries, add the same server definition to the Claude Desktop config file and restart Claude Desktop. Prefer the OAuth entry without headers when supported; use the API-key header entry only as a fallback. macOS config path: ```text theme={null} ~/Library/Application Support/Claude/claude_desktop_config.json ``` Example config: ```json theme={null} { "mcpServers": { "formbricks-local": { "type": "http", "url": "http://localhost:3000/api/mcp" } } } ``` Fallback config with an API key: ```json theme={null} { "mcpServers": { "formbricks-local-api-key": { "headers": { "Authorization": "Bearer ${FORMBRICKS_MCP_API_KEY}" }, "type": "http", "url": "http://localhost:3000/api/mcp" } } } ``` Set the API key in the Desktop launch environment before opening Claude Desktop when using the fallback config: ```bash theme={null} launchctl setenv FORMBRICKS_MCP_API_KEY "fbk_..." ``` If a Claude Desktop version only supports stdio MCP servers, use Claude Code for the HTTP endpoint or add a local stdio-to-HTTP bridge. ## Tool Responses Tool results include both `structuredContent` and a text content item containing the same JSON string. Successful results mirror the v3 REST response body and add `requestId` for correlation. Errors are returned as MCP error tool results with a structured v3 problem payload: ```json theme={null} { "error": { "code": "bad_request", "detail": "Invalid survey document", "invalid_params": [ { "name": "blocks.0.elements.0.headline", "reason": "Required" } ], "requestId": "req_...", "status": 400, "title": "Bad Request" } } ``` ## Available Tools ### list\_surveys Lists surveys in one workspace. The tool is read-only and idempotent. Input: ```json theme={null} { "cursor": "opaque-cursor-from-previous-response", "filter": { "name": { "contains": "feedback" }, "status": { "in": ["draft", "inProgress"] }, "type": { "in": ["link"] } }, "includeTotalCount": true, "limit": 20, "sortBy": "updatedAt", "workspaceId": "clxx1234567890123456789012" } ``` Output: ```json theme={null} { "data": [ { "createdAt": "2026-04-21T10:00:00.000Z", "creator": { "name": "Ada Lovelace" }, "id": "clsv1234567890123456789012", "name": "Product Feedback", "responseCount": 0, "status": "draft", "type": "link", "updatedAt": "2026-04-21T11:00:00.000Z", "workspaceId": "clxx1234567890123456789012" } ], "meta": { "limit": 20, "nextCursor": null, "totalCount": 1 }, "requestId": "req_..." } ``` ### get\_survey Gets one survey by ID. The tool is read-only and idempotent. Input: ```json theme={null} { "lang": ["en-US"], "surveyId": "clsv1234567890123456789012" } ``` `lang` is optional. When supplied, it filters translatable survey fields to the requested language codes or configured aliases. ### create\_survey Creates a block-based link survey using the v3 survey document contract. The tool writes data and is not idempotent. Input: ```json theme={null} { "blocks": [ { "elements": [ { "headline": { "de-DE": "Was sollen wir verbessern?", "en-US": "What should we improve?" }, "id": "satisfaction", "required": true, "type": "openText" } ], "name": "Main Block" } ], "defaultLanguage": "en-US", "endings": [], "hiddenFields": { "enabled": false }, "languages": [ { "code": "de-DE", "enabled": true } ], "metadata": { "cx_operation": "enterprise_onboarding", "title": { "de-DE": "Produktfeedback", "en-US": "Product Feedback" } }, "name": "Product Feedback Survey", "status": "draft", "variables": [], "welcomeCard": { "enabled": true, "headline": { "de-DE": "Willkommen", "en-US": "Welcome" } }, "workspaceId": "clxx1234567890123456789012" } ``` Output uses the same survey resource shape as `GET /api/v3/surveys/{surveyId}` and includes the MCP `requestId`. ### validate\_survey Validates a create or patch payload without writing survey changes. The tool is read-only and idempotent; workspace access is still checked when `workspaceId` is present, at `read` level — matching the `surveys:read` scope the tool declares. Create validation input: ```json theme={null} { "data": { "blocks": [ { "elements": [ { "headline": { "en-US": "What should we improve?" }, "id": "satisfaction", "required": true, "type": "openText" } ], "name": "Main Block" } ], "defaultLanguage": "en-US", "name": "Product Feedback Survey", "workspaceId": "clxx1234567890123456789012" }, "operation": "create" } ``` Patch validation input: ```json theme={null} { "data": { "name": "Updated Product Feedback Survey" }, "operation": "patch", "surveyId": "clsv1234567890123456789012" } ``` Validation failures return `200` with `valid: false` and structured `invalid_params`, matching `POST /api/v3/surveys/validate`. ### patch\_survey Updates a survey by ID using the v3 survey patch contract. The tool writes data, can be destructive, and is not idempotent. Omitted top-level fields are preserved. Provided top-level objects and arrays replace that whole subtree, so omitted nested entries inside a provided subtree can be removed. The patch tool does not deep-merge nested objects and does not implement JSON Patch. For agent workflows, fetch the current survey first, modify only the intended top-level fields, run `validate_survey` with `operation: "patch"`, and then submit the same patch with `patch_survey`. Input: ```json theme={null} { "data": { "metadata": { "cx_operation": "enterprise_onboarding", "title": { "en-US": "Updated Product Feedback" } }, "name": "Updated Product Feedback" }, "surveyId": "clsv1234567890123456789012" } ``` Output uses the same survey resource shape as `GET /api/v3/surveys/{surveyId}` and includes the MCP `requestId`. ### delete\_survey Deletes one survey by ID. The tool is destructive, writes data, and is not idempotent. Input: ```json theme={null} { "surveyId": "clsv1234567890123456789012" } ``` The v3 REST delete operation returns `204 No Content`. The MCP tool result contains the `requestId` so callers can correlate the audit trail: ```json theme={null} { "requestId": "req_..." } ``` ### Workflow tools Workflow tools operate on the v3 Workflows API (trigger → condition → action automations). They reuse the same authentication, authorization, audit logging, and error mapping as the survey tools. #### list\_workflows Lists workflows in one workspace. Read-only and idempotent. Input: ```json theme={null} { "workspaceId": "clxx1234567890123456789012", "limit": 20, "cursor": "opaque-cursor-from-previous-response", "sortBy": "updatedAt", "filter": { "name": { "contains": "welcome" }, "status": { "in": ["draft", "enabled"] } } } ``` Output mirrors the v3 list envelope: `{ "data": [...], "meta": { "limit", "nextCursor" }, "requestId" }`. Omitting `filter.status.in` returns every status except archived. #### get\_workflow Gets one workflow by ID. Read-only and idempotent. Unknown or cross-workspace ids return `403` (never `404`) so existence is not leaked. ```json theme={null} { "workflowId": "clwf1234567890123456789012" } ``` #### list\_workflow\_runs Lists workflow runs for a workspace, newest first. Read-only and idempotent. ```json theme={null} { "workspaceId": "clxx1234567890123456789012", "limit": 20, "workflowId": "clwf1234567890123456789012", "responseId": "clrs1234567890123456789012", "filter": { "status": { "in": ["completed"] }, "isDryRun": false } } ``` Only `workspaceId` is required. Omit `filter.isDryRun` to return both real and dry runs. #### get\_workflow\_run Gets one workflow run with its ordered step logs. Read-only and idempotent. ```json theme={null} { "runId": "clrn1234567890123456789012" } ``` #### test\_workflow Dry-runs a workflow: validates its live definition would execute and resolves the trigger's survey + ending cards. No run is persisted and no side effects occur; the result reports `{ ok, problems }`. Annotated read-only (no world mutation). **Drafts are testable** — checking the setup before going live is the point of a dry run — so a workflow can be tested straight after `create_workflow`. Only **archived** workflows are rejected, with `422 invalid_workflow_state`, since they are soft-deleted. ```json theme={null} { "workflowId": "clwf1234567890123456789012" } ``` #### create\_workflow Creates a workflow, always as a draft (only `enable_workflow` makes it live). Writes data; not idempotent. Takes the full v3 create payload: ```json theme={null} { "workspaceId": "clxx1234567890123456789012", "name": "Notify on churn survey completion", "definition": { "schemaVersion": 1, "entryNodeId": "trigger", "trigger": { "id": "trigger", "type": "trigger", "triggerType": "response.completed", "config": { "surveyId": "clsv1234567890123456789012", "endingCardIds": ["ending-1"] } }, "nodes": [], "edges": [] } } ``` #### patch\_workflow Updates a workflow (v3 PATCH contract: top-level partial merge, no deep merge). Destructive; not idempotent. `definition` edits are only accepted while the workflow is draft or disabled. ```json theme={null} { "workflowId": "clwf1234567890123456789012", "data": { "name": "Renamed workflow" } } ``` #### duplicate\_workflow Duplicates a workflow as a new draft with empty run and version history. Writes data; not idempotent. ```json theme={null} { "workflowId": "clwf1234567890123456789012", "name": "Copy of workflow" } ``` `name` is optional; if omitted the server picks a non-conflicting copy name. #### Lifecycle tools `delete_workflow`, `enable_workflow`, `disable_workflow`, `archive_workflow`, and `unarchive_workflow` each take only a workflow id: ```json theme={null} { "workflowId": "clwf1234567890123456789012" } ``` * `enable_workflow` — validates executability, snapshots an immutable version, and makes the workflow live. A non-executable definition is refused with `422 workflow_not_executable`. Once live, the workflow runs on matching survey responses and can send emails. * `disable_workflow` — stops future runs. * `archive_workflow` / `unarchive_workflow` — soft archive and restore (to draft). * `delete_workflow` — the v3 delete returns `204 No Content`; the MCP result contains the `requestId`. ### list\_feedback\_datasets Lists the active feedback datasets assigned to a workspace. Read-only and idempotent. Use the returned `id` as `datasetId` for the other feedback-record tools. Input: ```json theme={null} { "workspaceId": "clxx1234567890123456789012" } ``` Output: ```json theme={null} { "data": [{ "id": "clfd1234567890123456789012", "name": "Support" }], "meta": { "nextCursor": null, "totalCount": 1 }, "requestId": "req_..." } ``` ### list\_feedback\_records Lists feedback records in a workspace's feedback dataset. Read-only and idempotent, with opaque cursor pagination. `datasetId` is optional when the workspace has exactly one active dataset (the common case) and required when it has more than one. Input: ```json theme={null} { "cursor": "opaque-cursor-from-previous-response", "datasetId": "clfd1234567890123456789012", "field_group_id": "grp_1", "field_id": "q1", "field_type": "text", "limit": 50, "since": "2026-01-01T00:00:00Z", "source_id": "svy_1", "source_type": "survey", "submission_id": "sub_1", "until": "2026-12-31T23:59:59Z", "user_id": "user_1", "value_id": "opt_1", "workspaceId": "clxx1234567890123456789012" } ``` All filters are optional, match exactly, and combine with AND. They mirror the Hub's own `GET /v1/feedback-records` parameters, and each is named after the record field it filters — so filtering by a `user_id` you just read in a response is spelled the same way. An unknown filter key is rejected with a 422 rather than ignored, since a silently dropped filter would widen the result set without saying so. | Filter | Selects | | ----------------- | -------------------------------------------------------------------- | | `source_type` | a kind of source, e.g. `survey`, `review`, `call_notes` | | `source_id` | one survey/form/ticket | | `field_type` | one field type (`text`, `nps`, `rating`, …) | | `field_id` | all answers to one question | | `field_group_id` | one grouped question (ranking, matrix, grid) | | `submission_id` | the sibling answers given in one submission | | `user_id` | everything one end user submitted | | `value_id` | everyone who picked one particular choice | | `since` / `until` | a `collected_at` range (must fall between 1970-01-01 and 2080-12-31) | The workspace/dataset/pagination parameters (`workspaceId`, `datasetId`, `limit`, `cursor`) stay camelCase: they name nothing in the record. Output mirrors the Hub feedback-record shape and paginates with `meta.nextCursor`. `meta` also names the dataset that was searched, so an empty `data` array unambiguously means that dataset holds no matching records — a caller that let the dataset auto-resolve does not need a second call to find out which one it was: ```json theme={null} { "data": [ { "collected_at": "2026-04-21T10:00:00.000Z", "dataset_id": "clfd1234567890123456789012", "field_id": "q1", "field_type": "text", "id": "019fa338-f494-7384-b34e-01739783d280", "source_type": "survey", "submission_id": "…", "value_text": "Great support!" } ], "meta": { "datasetId": "clfd1234567890123456789012", "datasetName": "Support", "limit": 50, "nextCursor": null }, "requestId": "req_..." } ``` ### count\_feedback\_records Counts the feedback records matching a filter set, without fetching them — for "how many" questions that would otherwise mean paging through records. Read-only and idempotent. Takes the same filters as `list_feedback_records` (no `limit`/`cursor`), because the Hub documents its count endpoint as accepting the same query parameters as its list endpoint; both go through one mapper in `lib/operations.ts`, so a count always describes the same set as the equivalent list. Input: ```json theme={null} { "since": "2026-01-01T00:00:00Z", "user_id": "user_1", "workspaceId": "clxx1234567890123456789012" } ``` Output is the total and the dataset it came from — **no record content**, which is the point: a caller asking "how many" never pulls end-user text into its context to find out. ```json theme={null} { "data": { "count": 42, "dataset_id": "clfd1234567890123456789012", "dataset_name": "Support" }, "requestId": "req_..." } ``` ### get\_feedback\_record Gets one feedback record by its Hub UUID. Read-only and idempotent. The record must belong to a feedback dataset assigned to the workspace; otherwise the tool returns a generic authorization error and never reveals whether a record id exists in another tenant. Input: ```json theme={null} { "feedbackRecordId": "019fa338-f494-7384-b34e-01739783d280", "workspaceId": "clxx1234567890123456789012" } ``` ### create\_feedback\_record Creates a feedback record in a workspace's feedback dataset. Writes data and is not idempotent, and requires `feedbackRecords:write` (API keys need `write` or `manage`). The tenant is derived from the resolved feedback dataset and is never taken from the request body. `submission_id` is optional — a UUID is generated when omitted. Input: ```json theme={null} { "field_id": "note", "field_label": "Call summary", "field_type": "text", "source_type": "call_notes", "value_text": "Customer asked for SSO and a longer trial.", "workspaceId": "clxx1234567890123456789012" } ``` Output is the created feedback record (Hub shape) plus the MCP `requestId`. ### create\_feedback\_records Creates several feedback records in one call — the batch form of `create_feedback_record`, for imports. Writes data, is not idempotent, and requires `feedbackRecords:write`. Between 1 and 50 records per call. The Hub has no bulk-create endpoint (its only bulk write is the delete-by-user erasure path), so this fans out to one Hub create per record, in parallel. Two consequences are deliberate: * **Validation is all-or-nothing.** Every record is validated before any is written, so a malformed batch is rejected without leaving half of it stored. `invalid_params` names the offending index, e.g. `records.3.value_text`. * **Partial success is reported, not hidden.** If the Hub rejects some records — a duplicate `(submission_id, field_id)`, say — the created ones are returned and `meta.failures` accounts for the rest by index, so only those need retrying. If *nothing* could be created, the upstream failure is returned as the response instead, because an empty success would read as "there was nothing to do". The 50-record cap is an amplification bound as much as a payload one: one authorized request must not become an unbounded burst of upstream writes. **A batch is not a submission.** Each record without a `submission_id` gets its own generated one, exactly as in the single-record case — so several answers that belong to the same submission (a survey response, or a call with both a rating and a comment) must carry the *same* `submission_id`, set by the caller. Omit it and they are stored as unrelated submissions, which nothing downstream can distinguish from the intended shape. Input: ```json theme={null} { "records": [ { "field_id": "note", "field_type": "text", "source_type": "call_notes", "value_text": "Asked for SSO." }, { "field_id": "score", "field_type": "nps", "source_type": "call_notes", "value_number": 9 } ], "workspaceId": "clxx1234567890123456789012" } ``` Output: ```json theme={null} { "data": [{ "dataset_id": "clfd…", "id": "019fa338-…", "value_text": "Asked for SSO." }], "meta": { "created": 1, "datasetId": "clfd1234567890123456789012", "datasetName": "Support", "failed": 1, "failures": [{ "detail": "duplicate record for (tenant_id, submission_id, field_id)", "index": 1 }], "requested": 2 }, "requestId": "req_..." } ``` Per-record failure text goes through the same relay rules as a whole-request failure: a Hub 4xx explains itself, anything else becomes a fixed message. Each record actually created produces its own `created` audit event — N creations are N events, not one summary. ### update\_feedback\_record Corrects the value of an existing feedback record. Writes data, is annotated `destructiveHint: true` (it overwrites a stored value; the previous one survives only in the audit log), and requires `feedbackRecords:write`. Only the fields sent are changed; at least one is required. Updatable — the Hub's own mutable set: `value_text`, `value_number`, `value_boolean`, `value_date`, `value_id`, `user_id`, `language`, `metadata`. The schema is `.pick()`ed from the create fields, so bounds can't drift between creating and correcting a record. The `value_*` field being set must be one the record's `field_type` accepts — the same table `create` enforces (`text` takes `value_text`, `nps`/`rating`/`number` take `value_number`, `categorical` takes `value_text` and/or `value_id`, and so on). Because `field_type` is immutable it is not part of the patch, so this is checked against the *stored* record and therefore reported after the ownership check, as a 422 naming the offending field. Without it a patch could assemble what `create` rejects: putting `value_number` on a `text` record would leave both a text and a number set, with nothing to say which one the record means. The Hub does not enforce this itself. **Not** updatable: a record's provenance — `source_type`, `source_id`, `source_name`, `field_id`, `field_type`, `field_label`, `field_group_*`, `submission_id`, `collected_at`. Correcting those means deleting the record and creating it again. The derived enrichment fields (`sentiment`, `sentiment_score`, `emotions`, `value_text_translated`, `translation_lang_key`) are not accepted from callers either — they are the Hub's to compute. `metadata` is the one field that is **replaced, not merged** — the Hub assigns it wholesale, so adding a key means sending the existing ones too. Everything else is left untouched when omitted. Ownership is verified before the update, the same way as for get and delete: `PATCH /{id}` is another Hub endpoint that derives the tenant from the stored record. A record deleted in the window between that check and the write returns the same generic authorization error, not a 502 — nothing was updated and the service is fine. Input: ```json theme={null} { "feedbackRecordId": "019fa338-f494-7384-b34e-01739783d280", "value_text": "Actually the export works fine, it was the filter that confused me", "workspaceId": "clxx1234567890123456789012" } ``` Output is the updated record. **Editing the text resets what was derived from it.** Per the Hub's contract, changing `value_text` clears `sentiment`, `sentiment_score`, `emotions`, `value_text_translated` and `translation_lang_key` and queues re-enrichment; changing `language` re-queues the translation pair only. The response reflects the **cleared** state, so those fields being absent right after an update means "being recomputed", not "none". Changing `value_text` (or a field label) also re-queues the embedding, so semantic search catches up asynchronously — and clearing a record's text removes its embedding, making it unsearchable. ### delete\_feedback\_record Permanently deletes one feedback record. Writes data, is not idempotent, is annotated `destructiveHint: true`, and requires `feedbackRecords:write` (API keys need `write` or `manage`). The record and its derived embedding are removed with no soft delete, so this cannot be undone — the audit log entry keeps the deleted record as its `oldObject` and is the only remaining trace. Ownership is verified before the delete: the record must belong to a feedback dataset assigned to the workspace, and a record in another tenant is refused with the same generic authorization error as an unknown id, without deleting anything. Single-record only — there is no bulk delete tool. Input: ```json theme={null} { "feedbackRecordId": "019fa338-f494-7384-b34e-01739783d280", "workspaceId": "clxx1234567890123456789012" } ``` Returns `204 No Content` on success (the tool result carries only the `requestId`). ### search\_feedback\_records Searches a workspace's feedback dataset semantically: the query is embedded and compared to record embeddings by cosine similarity, so it matches meaning rather than keywords. Read-only and idempotent. Input: ```json theme={null} { "cursor": "opaque-cursor-from-previous-response", "limit": 10, "minScore": 0.5, "query": "checkout is confusing", "workspaceId": "clxx1234567890123456789012" } ``` Output is scored matches, best first — record ids with the embedded text, not full records. Pass a `feedback_record_id` to `get_feedback_record` for the rest of a record: ```json theme={null} { "data": [ { "feedback_record_id": "019fa338-f494-7384-b34e-01739783d280", "field_label": "What can we improve?", "score": 0.82, "value_text": "I couldn't figure out how to pay" } ], "meta": { "datasetId": "clfd1234567890123456789012", "datasetName": "Support", "limit": 10, "minScore": 0.5, "nextCursor": null }, "requestId": "req_..." } ``` ### find\_similar\_feedback\_records Finds the records most similar to a given one, by embedding distance — useful for gauging how widely a piece of feedback is echoed. Read-only and idempotent. Same output shape as `search_feedback_records`; the anchor record is excluded from its own results. Input: ```json theme={null} { "feedbackRecordId": "019fa338-f494-7384-b34e-01739783d280", "limit": 10, "minScore": 0.5, "workspaceId": "clxx1234567890123456789012" } ``` Ownership of the anchor record is verified before any neighbour is fetched (see [Feedback Records And The Hub](#feedback-records-and-the-hub)). Both search tools need embeddings, which are optional in the Hub. Without `EMBEDDING_PROVIDER` and `EMBEDDING_MODEL` configured on **both** the Hub API and the Hub worker they return `503` with that instruction as the detail. Embedding is also asynchronous and only covers records that have text, so a record created moments ago is not searchable yet; `find_similar_feedback_records` reports that as a `409` rather than an empty result, distinguishing "still being embedded, retry" from "no text, so there is no embedding to wait for" — the latter also covers text cleared by an update. `limit` (1–100, default 10) and `minScore` (0–1, default 0.5) are validated by Formbricks rather than passed straight through: the Hub silently coerces out-of-range values to its own defaults, which would return something other than what was asked for. The default `minScore` of 0.5 is Formbricks' own — the Hub defaults to 0.7, which is strict enough that a fair paraphrase often falls just below it. ## Relationship to V3 Workflows API Like the survey tools, workflow tools run no custom database queries — each calls the shared, framework-agnostic `@formbricks/workflows` handlers used by the v3 REST routes: | MCP Tool | REST Contract | | -------------------- | ----------------------------------------------- | | `list_workflows` | `GET /api/v3/workflows` | | `get_workflow` | `GET /api/v3/workflows/{workflowId}` | | `list_workflow_runs` | `GET /api/v3/workflows/runs` | | `get_workflow_run` | `GET /api/v3/workflows/runs/{runId}` | | `test_workflow` | `POST /api/v3/workflows/{workflowId}/test` | | `create_workflow` | `POST /api/v3/workflows` | | `patch_workflow` | `PATCH /api/v3/workflows/{workflowId}` | | `duplicate_workflow` | `POST /api/v3/workflows/{workflowId}/duplicate` | | `delete_workflow` | `DELETE /api/v3/workflows/{workflowId}` | | `enable_workflow` | `POST /api/v3/workflows/{workflowId}/enable` | | `disable_workflow` | `POST /api/v3/workflows/{workflowId}/disable` | | `archive_workflow` | `POST /api/v3/workflows/{workflowId}/archive` | | `unarchive_workflow` | `POST /api/v3/workflows/{workflowId}/unarchive` | ## Relationship to V3 Surveys API The MCP server does not run custom survey database queries. Each tool calls the shared server-only v3 survey operations used by the REST routes: | MCP Tool | REST Contract | | ----------------- | ----------------------------------- | | `list_surveys` | `GET /api/v3/surveys` | | `get_survey` | `GET /api/v3/surveys/{surveyId}` | | `create_survey` | `POST /api/v3/surveys` | | `validate_survey` | `POST /api/v3/surveys/validate` | | `patch_survey` | `PATCH /api/v3/surveys/{surveyId}` | | `delete_survey` | `DELETE /api/v3/surveys/{surveyId}` | When the v3 OpenAPI contract changes, update the MCP schemas and this page together. The hand-maintained v3 OpenAPI spec lives at `docs/api-v3-reference/openapi.yml`. ## Feedback Records And The Hub Feedback-record tools do not map to a v3 REST route. They call the shared server-only Hub service (`@/modules/hub/service`) — the same client the Unify Feedback UI uses — and enforce the same `feedbackDirectories` Enterprise entitlement. A feedback record lives in the Formbricks Hub, addressed by an opaque tenant id. Every tool resolves that tenant server-side: it authorizes the caller's access to `workspaceId`, checks the `feedbackDirectories` license, then maps the workspace to its assigned feedback dataset — the `FeedbackDirectory` id **is** the Hub tenant id, and it is surfaced to callers as `dataset_id`. Assignment is a join table, but the application enforces at most one *non-archived* directory per workspace, so in practice `datasetId` can be omitted and the single active dataset is used. It becomes required only if a workspace somehow has several active directories, in which case the tools return `400` rather than guessing. A tenant id is never accepted from tool input. Three Hub endpoints — get, delete, and similar — take a bare record id and derive the tenant from the stored record, delegating record-level authorization to the product. So `get_feedback_record`, `delete_feedback_record` and `find_similar_feedback_records` all retrieve the record first and verify its tenant — the named dataset when one was given, otherwise any dataset the workspace owns — before acting. A foreign record and an unknown record produce the same generic authorization error, so record ids cannot be probed across tenants. `search_feedback_records` instead has the resolved tenant injected into the Hub query, like `create_feedback_record`. ## Limitations * OAuth is user-delegated only. Machine-to-machine client credentials for MCP are not supported. * OAuth access tokens must be JWTs audience-bound to `/api/mcp`; opaque MCP access token introspection is not accepted by the MCP route. * API-key authentication remains supported for compatibility and fallback use. * The MCP server exposes only the survey, workflow and feedback-record operations listed on this page. * Survey and workflow tool coverage depends on the current v3 Surveys and Workflows REST endpoint coverage; feedback-record tool coverage depends on the Hub feedback-records API. * `enable_workflow` makes a workflow live and can trigger email sending; treat it as a high-impact mutation when granting agents write access. * `create_workflow` requires a complete workflow `definition` (schema version, trigger, entry node); it is created as a draft and only `enable_workflow` can make it live. * Feedback-record tools require the `feedbackDirectories` Enterprise entitlement and a feedback directory assigned to the workspace; without either they return 403 / 422. * A feedback record's **provenance** is immutable: `update_feedback_record` changes values, users, language and metadata, but not which source, question or submission a record belongs to, nor when it was collected. The derived enrichment fields are the Hub's to compute and are never accepted from callers. * `delete_feedback_record` deletes one record at a time. The Hub's bulk and delete-by-user endpoints are deliberately not exposed: they are erasure operations whose blast radius does not belong on an agent surface. There is deliberately no batch *delete* to match `create_feedback_records`. * Counting and filtering cover the Hub's own filter set only. There is no aggregation by sentiment or emotion, and semantic search cannot be narrowed by source or date — the Hub's search endpoint takes only a query and a tenant. Those need Hub-side support first. * The search tools require an embedding model configured on the Hub API **and** the Hub worker; without one they return 503. Embedding is asynchronous and covers only records with text. * `create_survey` creates link surveys only. In-app survey creation and distribution settings are not part of the current v3 create operation. * `patch_survey` follows the v3 PATCH contract: top-level partial document updates only, no JSON Patch, and no nested deep merge. Provided top-level objects and arrays replace their whole subtree and can remove omitted nested entries. * `validate_survey` validates payload shape and references; it does not create languages, survey versions, or surveys. * Query-string credentials are rejected; use headers for API keys. * Large request bodies are rejected before the MCP handler using the same body-size policy as v3 APIs. ## Implementation Files * `apps/web/app/api/mcp/route.ts` * `apps/web/modules/mcp/auth.ts` * `apps/web/modules/mcp/server.ts` * `apps/web/modules/mcp/tools/surveys.ts` * `apps/web/modules/mcp/tools/workspaces.ts` * `apps/web/modules/mcp/tools/feedback-records.ts` * `apps/web/modules/mcp/tools/schemas.ts` * `apps/web/modules/mcp/tools/workflows.ts` * `apps/web/modules/mcp/tools/workflow-schemas.ts` * `apps/web/app/api/v3/surveys/lib/operations.ts` * `apps/web/app/api/v3/workflows/lib/context.ts` (adapter to the `@formbricks/workflows` handlers) * `apps/web/app/api/v3/feedbackRecords/lib/operations.ts` * `apps/web/app/api/v3/feedbackRecords/lib/access.ts` (tenant resolution + per-record ownership guard) * `apps/web/app/api/v3/feedbackRecords/lib/errors.ts` (Hub error → v3 problem mapping) # Solution Overview Source: https://formbricks.com/docs/development/technical-handbook/overview High-level architecture and technical details of the Formbricks platform The Formbricks platform is designed with a modern, unified architecture that prioritizes developer experience and performance. This overview outlines the system's architecture, key technology choices, and how different components interact. ## System Architecture Formbricks uses a unified architecture with Next.js at its core, serving both the frontend and backend capabilities. Here's an interactive overview of the system: ```mermaid theme={null} graph TD subgraph Users MU[Mobile User] WU[Web User] LSU[Link Survey User] AD[Admin] end subgraph Client Applications MA[Mobile App\niOS/Android SDK] SW[Survey Widget\nJavaScript SDK] end subgraph Formbricks Platform NS[Next.js Server\nAPI Routes + SSR\nBusiness Logic] subgraph Storage DB[(PostgreSQL\nSurveys & Data)] RC[(Redis\nPerformance Cache)] FS[File Storage\nUploads] end end MU --> |Uses| MA WU --> |Interacts| SW LSU --> |Direct Access| NS AD --> |Manages| NS MA --> |REST API| NS SW --> |REST API| NS NS --> |Cache Data| RC NS --> |Read/Write| DB NS --> |File Upload| FS style DB fill:#00C4B8,color:#ffffff style RC fill:#00C4B8,color:#ffffff style FS fill:#00C4B8,color:#ffffff style NS fill:#0D9373,color:#ffffff style MA fill:#07C983,color:#ffffff style SW fill:#07C983,color:#ffffff classDef user fill:#666,color:#fff,stroke:#333 class MU,WU,LSU,AD user ``` ### Key Components #### Users * **Mobile Users**: Access surveys through native mobile applications * **Web Users**: Interact with embedded survey widgets on websites * **Link Survey Users**: Access surveys directly through shared URLs * **Admins**: Manage surveys and view responses through the web dashboard #### Client Applications * **Survey Widget**: A JavaScript component that can be embedded in web applications * **Mobile Application**: Native iOS/Android apps that integrate with the Formbricks SDK * **Web Dashboard**: The admin interface for managing surveys and viewing responses #### Core Platform * **Next.js Server**: The central component that handles: * Frontend rendering and SSR * API routes for client communication * Business logic implementation * Survey display and response collection #### Data Storage * **PostgreSQL Database**: Primary data store for: * Survey definitions * Response data * User and organization information * **Redis Cache**: Performance optimization through: * Caching frequently accessed data * Reducing database load * Improving response times ### Communication Flow 1. **Client Interaction** * Web users interact with embedded survey widgets * Mobile users engage with surveys through native apps * Link survey users access surveys directly via URLs * Admins manage the system through the web dashboard 2. **Data Flow** * Clients communicate with the Next.js server via REST APIs * Survey data is fetched and responses are submitted over HTTPS * The server processes requests and interacts with the database using Prisma ORM * Frequently accessed data is cached in Redis for performance 3. **Data Storage** * Persistent data is stored in PostgreSQL * Frequently accessed data is cached in Redis * File attachments are handled through the configured storage provider ## Technology Stack * **Frontend**: React, TailwindCSS, Radix UI * **Backend**: Next.js App Router with API Routes * **Database**: PostgreSQL with Prisma ORM * **Caching**: Redis * **API**: REST with OpenAPI specification * **Testing**: Playwright for E2E, Vitest for unit tests # Tenant Separation Source: https://formbricks.com/docs/development/technical-handbook/tenant-separation How Formbricks handles tenant separation on Formbricks Cloud Formbricks Cloud is designed to support multiple tenants, each with their own set of surveys and data. This page describes the different ways to configure and use Formbricks for multi-tenancy. This documentation only applies to Formbricks Cloud instances. On-premise deployments handle tenant separation differently based on your specific setup and requirements. ## Multi-Tenancy in Formbricks Cloud Formbricks Cloud is architected as a true multi-tenant system where each organization contains one or more isolated Workspaces while sharing the underlying infrastructure. This approach allows multiple organizations to use the platform independently without visibility into each other's data. ### Organizational Structure ```mermaid theme={null} graph TD Organization[Organization] --> Workspace1[Workspace 1] Organization --> Workspace2[Workspace 2] Organization --> WorkspaceN[Workspace n] Workspace1 --> Survey1[Surveys] Workspace1 --> Contact1[Contacts] Workspace1 --> Action1[Action Classes] Workspace1 --> ApiKeyScope1[API Key Scopes] Workspace1 --> Webhook1[Webhooks] Workspace1 --> Integration1[Integrations] Workspace1 --> Segment1[Segments] Workspace2 --> Survey2[Surveys] Workspace2 --> Contact2[Contacts] Workspace2 --> Action2[Action Classes] Workspace2 --> ApiKeyScope2[API Key Scopes] Workspace2 --> Webhook2[Webhooks] Workspace2 --> Integration2[Integrations] Workspace2 --> Segment2[Segments] ``` The multi-tenant architecture in Formbricks Cloud is built around three key levels of isolation: ### 1. Organization-Level Isolation * Each tenant is represented by an Organization entity * Organizations are completely separated from each other * Users can be members of multiple organizations simultaneously * All resources and data belong exclusively to a single organization * Role-based access control (RBAC) with specific roles: * Owner: Full access to all organization resources * Manager: Can manage members and billing * Member: Basic access to organization resources * Billing: Access to billing information only * Billing and subscription management occurs at the organization level ### 2. Workspace-Level Isolation Within each organization, further isolation is maintained through Workspaces: * Organizations can have multiple Workspaces to separate different applications or product lines * Complete separation of resources between Workspaces: * Surveys and responses * Contacts and attributes * Action classes and triggers * API key scopes and integrations * Webhooks and notifications * Segments and targeting rules * Workspace-specific configuration, branding, and style settings * Language settings and customizations are Workspace-specific ### 3. Team-Based Access Isolation Team access is scoped per Workspace: * Teams can be granted access to specific Workspaces via Workspace-team mapping * Permissions are applied at the Workspace level for finer-grained access control * Users can collaborate in one Workspace without being granted access to all organization resources ## Data Security in the Multi-Tenant Model Formbricks ensures data security in its multi-tenant Workspace model through: * **Database Layer Isolation**: * Foreign key constraints enforce data isolation * Each resource is scoped to its organization * Cascading deletes maintain referential integrity * **Authentication Boundaries**: * Role-based access control (RBAC) * Session management per organization * Multi-factor authentication support * **API Access Control**: * API keys are scoped to specific Workspaces * Rate limiting per organization * Request validation and sanitization * **Resource Quotas**: * Organization-level limits based on billing plan This multi-layered approach to tenant separation ensures that organizations can safely use Formbricks Cloud with complete data privacy and isolation from other tenants. # AI Features Source: https://formbricks.com/docs/platform/features/ai-features How AI features are organized, hosted, and controlled in Formbricks. AI features are part of the [Enterprise Edition](/docs/self-hosting/advanced/license). Formbricks ships a single organization-wide toggle that turns on AI-powered helpers across the app: **Settings → Organization → General → Smart functionality (AI)**. ## AI Principles 1. **Always optional**: AI is disabled until your organization enables. You can run Formbricks fully without AI. 2. **We separate AI**: We distinguish between: * **Smart Functionality**: helps teams build and operate faster (for example [AI Survey Translation](/docs/surveys/general-features/multi-language-surveys#translate-with-ai) or [AI Chart Creation](/docs/unify-feedback/dashboards-charts#ai-builder)). * **Data AI**: features that work directly with your feedback data (creating embeddings for Unify Feedback semantic search). ### Privacy-first and self-hosted where possible We prioritize self-hosted AI, especially for capabilities that process customer feedback data. Formbricks supports AI in self-hosted and on-premise environments, and we prefer open-weight models whenever feasible. For Unify Feedback semantic search, we host the embeddings model ourselves. This means feedback data is not shared with third-party model providers for that capability, and your collected feedback/response data is never used as AI training input. ## Model Hosting Status * **Current Smart Functionality model**: Gemini 3.5 Flash hosted on Google Cloud Platform in Germany. * **Current Embeddings model**: Alibaba GTE embeddings hosted by Formbricks in Germany. * **In progress**: evaluation of self-hosted Kimi 2.5 to replace Gemini 3.5 Flash for Smart Functionality. ## AI Features by Category ### Smart Functionality * **[AI Survey Translation](/docs/surveys/general-features/multi-language-surveys#translate-with-ai)**: auto-translate survey questions, options, and prompts into enabled languages. * **[AI Chart Creation](/docs/unify-feedback/dashboards-charts#ai-builder)**: describe a chart in natural language and Formbricks generates the underlying query. ### Data AI * **Embeddings creation**: create embeddings for feedback records so they can be used for semantic search, clustering, and retrieval. ## Permissions Only **Owners** and **Managers** can change the AI toggle. Other roles see a read-only state. # Email Branding Source: https://formbricks.com/docs/platform/features/email-customization Branding the emails that are sent to your respondents. **Self-Hosting Requirements**: Uploading custom organization logos for emails requires file upload storage to be configured. If you're self-hosting Formbricks, make sure to [configure file uploads](/docs/self-hosting/configuration/file-uploads) before using this feature. Email branding is a white-label feature that allows you to customize the email that is sent to your users. You can upload a logo of your company and use it in the email. Email branding is part of the Formbricks [Enterprise Edition](/docs/self-hosting/advanced/license). Only the Owner and Managers of the organization can modify the logo. ## How to upload a logo 1. Go to **Settings → Organization → General**. 2. You will see a card called **Email Customization**. Email Customization Settings 3. Upload a logo of your company. Logos must be 5 MB or less. 4. Click on the **Save** button. Updated Logo ## Viewing the Logo in the Email You can click on the **Send test email** button to get a test email with the logo. Email Sample ## Use Cases * **White-labeling**: You can use this feature to white-label your emails to your users. * **Branding**: You can use this feature to add your logo to your emails to increase brand recognition. # Activepieces Source: https://formbricks.com/docs/platform/features/integrations/activepieces A step-by-step guide to integrating Formbricks with Activepieces. Activepieces is a versatile tool to automate workflows between Formbricks and numerous applications. Here's how to set it up. Ensure your survey is finalized before setting up Activepieces. Any changes in the survey will require additional adjustments in the workflow. ## Step 1: Setup your survey incl. `questionId` for every question Set up the `questionId`s of your survey questions before publishing. Update Question ID *Update the Question ID field in every question card under Advanced Settings.* Already published? Duplicate survey You can only update the questionId before publishing the survey. If already published, simply duplicate it. Duplicate Survey ## Step 2: Setup Activepieces Visit [Activepieces](https://activepieces.com) to start a new Flow. Create New Flow Search for `Formbricks` and choose the event to trigger the flow, we will choose `Response Finished` here Search Formbricks ## Step 3: Connect Formbricks with Activepieces Click on `Create connection`: Create Connection Enter the Formbricks API Host and API Key. API Host is by default set to [https://app.formbricks.com](https://app.formbricks.com) but can be modified for self-hosting instances. Learn how to get an API Key from the [API Key tutorial](/docs/api-reference/rest-api#how-to-generate-an-api-key). Configure Connection ## Step 4: Select Survey Choose from your created surveys: Select Survey ## Step 5: Send a test response You need a test response for Activepieces setup. Click on Test trigger and submit a test response in the connected Formbricks survey to see the data in Activepieces. Test Trigger If the test response is successful, you will see the data in Activepieces. Success Response ## Step 6: Set up Google Sheet Decide on the desired action for the data. Here, we'll send submissions to a Google Sheet. Add Google sheet step to your flow and configure it as follows: Choose "Add a Row" for the action. Authenticate with Google and select the spreadsheet you want to add the data to. Select Google sheet Specify the fields you want to add to the spreadsheet. Match Data A new row gets added to the spreadsheet for every response: Result # Airtable Source: https://formbricks.com/docs/platform/features/integrations/airtable A step-by-step guide to integrate Airtable with Formbricks Cloud. The Airtable integration allows you to automatically send responses to an Airtable of your choice. If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/docs/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance. ## Formbricks Cloud 1. Click on the `Configuration` tab in the left sidebar and then click on the `Integrations` tab and click on the `connect` button under the `Airtable` card. Formbricks Integration Tab 1. Now click on the "Connect with Airtable" button to authenticate yourself with Airtable. Connect Formbricks with Airtable 1. You will now be taken to a page where you need to add and grant access to the base you want to use for the integration. Add and grant access to airtable base 1. Once you add and grant access to your base, you will be taken back to Formbricks Cloud and see the connected status as below: Formbricks is now connected with Airtable Before the next step, make sure that you have a Formbricks Survey with at least one question and an Airtable base with at least one table in the Airtable account you integrated. 1. Now click on the "Link New Table" button to link an Airtable with Formbricks and a modal will open up. Link Formbricks with an Airtable base 1. Select the Base and table you want to link with Formbricks and the Survey. On doing so, you will be asked with what questions' responses you want to feed in Airtable. Select the questions and click on the "Save" button. Select question to link with Airtable 1. On submitting, the modal will close and you will see the linked table in the list of linked tables. List of linked tables Congratulations! You have successfully linked an Airtable with Formbricks. Now whenever a response is submitted for the linked survey, it will be automatically added to the linked Airtable. Still struggling or something not working as expected? [Join us on Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! ## Remove Integration with Airtable To remove the integration with Airtable, 1. Visit the Integrations tab in your Formbricks Cloud dashboard. 2. Select "Manage" button in the Airtable card. 3. Click on the "Connected with `" just before the "Link new Table" button. 4. It will now ask for a confirmation to remove the integration. Click on the "Delete" button to remove the integration. You can always come back and connect again with the same Airtable Account. Delete Airtable Integration with Formbricks Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Google Sheets Source: https://formbricks.com/docs/platform/features/integrations/google-sheets The Google Sheets integration allows you to automatically send responses to a Google Sheet of your choice. If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/docs/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance. ## Connect Google Sheets 1. Click on the `Configuration` tab in the left sidebar and then click on the `Integrations` tab and click on the `connect` button under the `Google Sheets` card. Formbricks Integrations Tab 1. Now click on the "Connect with Google" button to authenticate yourself with Google. Connect Formbricks with your Google 1. You will now be taken to the Google OAuth page where you can select the Google account you want to use for the integration. 2. Once you have selected the account and completed the authentication process, you will be taken back to Formbricks Cloud and see the connected status as below: Formbricks is now connected with Google Before the next step, make sure that you have a Formbricks Survey with at least one question and a Google Sheet in the Google account you integrated. 1. Now click on the "Link New Sheet" button to link a Google Sheet with Formbricks and a modal will open up. Link Formbricks with a Google Sheet 1. Enter the spreadsheet URL for the Google Sheet you want to link with Formbricks and the Survey. On doing so, you will be asked with what questions' responses you want to feed in the Google Sheet. Select the questions and click on the "Link Sheet" button. Select question to link with Google Sheet 1. On submitting, the modal will close and you will see the linked Google Sheet in the list of linked Google Sheets. List of linked Google Sheets Congratulations! You have successfully linked a Google Sheet with Formbricks. Now whenever a response is submitted for the linked survey, it will be automatically added to the linked Google Sheet. ## Remove Integration with Google Account To remove the integration with Google Account, 1. Visit the Integrations tab in your Formbricks Cloud dashboard. 2. Select **Manage Sheets** button in the Google Sheets card. 3. Click on the **Delete Integration** button. 4. It will now ask for a confirmation to remove the integration. Click on the **Delete** button to remove the integration. You can always come back and connect again with the same Google Account. Delete Google Integration with Formbricks ## What info do you need? * Your **Email ID** for authentication (We use this to identify you) * Your **Google Sheets Names and IDs** (We fetch this to list and show you the options of choosing a sheet to integrate with) * Write access to **selected Google Sheet** (The google sheet you choose to integrate it with, we write survey responses to it) For the above, we ask for: 1. **User Email**: To identify you (that's it, nothing else, we're opensource, see this in our codebase [here](https://github.com/formbricks/formbricks/blob/main/apps/web/app/api/google-sheet/callback/route.ts#L47C17-L47C25)) 2. **Google Spreadsheet API**: To write to the spreadsheet you select (that's it, nothing else, we're opensource, see this method in our codebase [here](https://github.com/formbricks/formbricks/blob/main/packages/lib/googleSheet/service.ts#L70)) We store as little personal information as possible. Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # HubSpot Source: https://formbricks.com/docs/platform/features/integrations/hubspot Learn how to integrate Formbricks with HubSpot to automatically create or update contacts when survey responses are submitted. Formbricks doesn't have a native HubSpot integration yet. This guide shows you how to connect Formbricks with HubSpot using automation platforms (Make.com, n8n) or custom webhooks. ## Prerequisites Before setting up the integration, you'll need: 1. **A Formbricks account** with at least one survey that collects email addresses 2. **A HubSpot account** with API access 3. **HubSpot API credentials** (see authentication options below) 4. **Automation service** like Make, n8n, or ActivePieces (for no-code methods) ### HubSpot Authentication Options HubSpot offers two main ways to authenticate API requests: #### Option A: OAuth 2.0 via Public App (Recommended) OAuth is the recommended approach for production integrations. When using Make.com or n8n, they handle OAuth authentication for you through their built-in HubSpot connectors. #### Option B: Legacy Private Apps (Simple Setup) For custom webhook handlers, you can use a Legacy Private App which provides a static access token. While marked as "legacy," these apps remain fully supported by HubSpot. To create a Legacy Private App: Go to your HubSpot account **Settings** and navigate to **Integrations** → **Private Apps**. Click **Create a private app** and give it a name (e.g., "Formbricks Integration"). Under **Scopes**, add `crm.objects.contacts.write` and `crm.objects.contacts.read`. Click **Create app** and copy the access token. For more information on HubSpot's authentication options, see the [HubSpot Authentication Overview](https://developers.hubspot.com/docs/guides/apps/authentication/intro-to-auth). *** ## Method 1: Using Make.com (Recommended for No-Code) Before starting, ensure your survey has clear `questionId` values set. You can only update these before publishing. If your survey is already published, duplicate it and update the question IDs in the copy. Make sure your survey has meaningful `questionId` values for each question. This makes mapping responses to HubSpot fields easier. Update Question ID Go to [Make.com](https://make.com) and create a new scenario. Search for **Formbricks** and select it as your trigger, then choose **Response Finished** as the trigger event. Search Formbricks Click **Create a webhook**, enter your Formbricks API Host (default: `https://app.formbricks.com`), add your Formbricks API Key (see [API Key Setup](/docs/api-reference/rest-api#how-to-generate-an-api-key)), and select the survey you want to connect. Enter API Key Click the **+** button after the Formbricks trigger, search for **HubSpot**, choose **Create or Update a Contact** as the action, and connect your HubSpot account. Map the Formbricks response fields to HubSpot contact properties: | HubSpot Field | Formbricks Field | | --------------- | ------------------------------------- | | Email | `data.email` (your email question ID) | | First Name | `data.firstName` (if collected) | | Last Name | `data.lastName` (if collected) | | Custom Property | Any other `data.*` field | You can also map metadata: `meta.country`, `meta.userAgent.browser`, `survey.title`. Submit a test response to your Formbricks survey, verify the contact appears in HubSpot, and turn on your Make scenario. *** ## Method 2: Using n8n (Self-Hosted Option) The Formbricks n8n node is available as a community node. Install it via **Settings** → **Community Nodes** → install `@formbricks/n8n-nodes-formbricks`. Create a new workflow in n8n, add the **Formbricks** trigger node, connect it with your Formbricks API Key and host, select **Response Finished** as the event, and choose your survey. Add Formbricks Trigger Add a new node and search for **HubSpot**, select **Create/Update Contact** as the operation, and connect your HubSpot account (n8n supports both OAuth and access token authentication). In the HubSpot node, map the fields: ``` Email: {{ $json.data.email }} First Name: {{ $json.data.firstName }} Last Name: {{ $json.data.lastName }} ``` For custom HubSpot properties, use the **Additional Fields** section to add mappings like `survey_source`, `response_id`, and `submission_date`. Click **Listen for event** in the Formbricks trigger, submit a test survey response, verify the data flows through to HubSpot, and activate your workflow. *** ## Method 3: Using Webhooks (Custom Integration) For maximum flexibility, you can use Formbricks webhooks with a custom endpoint that calls the HubSpot API directly. This approach is ideal for developers who want full control. This method requires a HubSpot access token. You can use a Legacy Private App token (simplest) or implement OAuth 2.0 for production applications. Go to **Configuration** → **Integrations** in Formbricks, click **Manage Webhooks** → **Add Webhook**, enter your endpoint URL, select **Response Finished** as the trigger, and choose the surveys to monitor. Integrations Tab Your webhook handler needs to: * **Receive the Formbricks webhook** - Accept POST requests with the survey response payload * **Extract contact data** - Parse the email and other fields from `data.data` (keyed by your `questionId` values) * **Call the HubSpot API** - Use the [HubSpot Contacts API](https://developers.hubspot.com/docs/api/crm/contacts) to create or update contacts * **Handle duplicates** - HubSpot returns a 409 error if a contact with that email exists; search and update instead You can deploy using serverless functions (Vercel, AWS Lambda, Cloudflare Workers), traditional servers, or low-code platforms. For webhook signature verification, see the [Webhooks documentation](/docs/platform/features/integrations/webhooks). Deploy your webhook handler to a publicly accessible URL, add the URL to your Formbricks webhook configuration, submit a test survey response, and verify the contact appears in HubSpot. *** ## Troubleshooting ### Contact Not Created in HubSpot 1. **Check the email field**: Ensure your survey has an email question and you're mapping the correct `questionId` 2. **Verify API token**: Make sure your HubSpot access token has the required scopes (`crm.objects.contacts.write` and `crm.objects.contacts.read`) 3. **Check for duplicates**: HubSpot returns a 409 error if a contact with that email already exists ### Webhook Not Triggering 1. Verify the webhook URL is publicly accessible 2. Check that **Response Finished** trigger is selected 3. Ensure the survey is linked to the webhook ### Testing Your Integration 1. Use a unique test email for each test 2. Check HubSpot's **Contacts** page after submitting a response 3. Review your webhook handler logs for errors *** Still struggling or something not working as expected? [Join our GitHub Discussions](https://github.com/formbricks/formbricks/discussions) and we're happy to help! # Make Source: https://formbricks.com/docs/platform/features/integrations/make Discover how to seamlessly integrate Formbricks with Make.com. Dive into our comprehensive guide to set up scenarios, connect with a plethora of apps, and send your survey data to more than 1000 platforms. Make is a powerful tool to send information between Formbricks and thousands of apps. Here's how to set it up. Nailed down your survey?? Any changes in the survey cause additional work in the *Scenario*. It makes sense to first settle on the survey you want to run and then get to setting up Make. ## Step 1: Setup your survey incl. `questionId` for every question Set up the `questionId`s of your survey questions before publishing. Update Question ID *Update the Question ID field in every question card under Advanced Settings.* Already published? Duplicate survey You can only update the questionId before publishing the survey. If already published, simply duplicate it. Duplicate Survey ## Step 2: Setup Make.com Visit [Make.com](https://make.com) to start a new scenario. Create New Scenario Search for `Formbricks`: Search Formbricks Choose the event to trigger the Scenario: Select Triggers ## Step 3: Connect Formbricks with Make Click "Create a webhook": Create Webhook Enter the Formbricks API Host and API Key. API Host is by default set to [https://app.formbricks.com](https://app.formbricks.com) but can be modified for self-hosting instances. Learn how to get an API Key from the [API Key tutorial](/docs/api-reference/rest-api#how-to-generate-an-api-key). Enter API Key ## Step 4: Select Survey Choose from your created surveys: Select Survey ## Step 5: Send a test response You need a test response for Make setup. Submit Test Response ## Step 6: Set up Google Sheet Decide on the desired action for the data. Here, we'll send submissions to a Google Sheet: Add Module Choose "Add a Row" for the action: Select Action Specify the spreadsheet details and match the Formbricks data: Select Fields A new row gets added to the spreadsheet for every response: Result # n8n Source: https://formbricks.com/docs/platform/features/integrations/n8n Integrate Formbricks with n8n for a streamlined workflow experience. The Formbricks n8n node is currently only available in the n8n self-hosted version as a community node. To install it go to "Settings" -> "Community Nodes" and install @formbricks/n8n-nodes-formbricks. n8n allows you to build flexible workflows focused on deep data integration. And with sharable templates and a user-friendly UI, the less technical people on your team can collaborate on them too. Unlike other tools, complexity is not a limitation. So you can build whatever you want — without stressing over budget. Hook up Formbricks with n8n and you can send your data to 350+ other apps. Here is how to do it. ## Step 1: Setup your survey incl. `questionId` for every question Nailed down your survey? Any changes in the survey cause additional work in the n8n node. It makes sense to first settle on the survey you want to run and then get to setting up n8n. When setting up the node your life will be easier when you change the `questionId`s of your survey questions. You can only do so **before** you publish your survey. Update Question ID *In every question card in the Advanced Settings you find the Question ID field. Update it so that you'll recognize the response tied to this question.* Already published? Duplicate survey You can only update the questionId when the survey was not yet published. Already published it? Just **duplicate it** to update the questionIds. Duplicate Survey ## Step 2: Setup your n8n workflow Go to [n8n.io](https://n8n.io) and create a new workflow. Search for “Formbricks” to get started: Add Formbricks Trigger ## Step 3: Connect Formbricks with n8n Now, you have to connect n8n with Formbricks via an API Key: Create new credential button Click on Create New Credential button to add your host and API Key Add host and api key Now you need an API key. Please refer to the [API Key Setup](/docs/api-reference/rest-api#how-to-generate-an-api-key) page to learn how to create one. Once you copied it in the API Key field, hit Save button to test the connection and save the credentials. Successful Connection ## Step 4: Select Event Next, you can choose the event you want to trigger the node on. You can select multiple events: Select Event Here, we are adding `Response Finished` as an event, which will trigger when the survey has been filled out. ## Step 5: Select Survey Next, you can choose from all the surveys you have created in this environment. You can select multiple surveys: Select Survey Here, we are selecting two surveys. Selected Surveys ## Step 6: Test your trigger In order to set up n8n you'll need a test response in the selected survey. This allows you to select the individual values of each response in your workflow. Submit Test Response Next, click on Listen for event button. Listen for event Then, go to the survey which you selected. Fill it out, and wait for the particular event to trigger (in this case it's `Response Finished`). Once the event is triggered you will see the response that you filled out in the survey. Test Response Success Now you have all the data you need at hand. The next steps depend on what you want to do with it. In this tutorial, we will send submissions to a discord channel: ## Step 7: Add discord to your workflow Click on the plus and search `Discord`. Add Discord Fill in the `Webhook URL` and the `Content` that you want to receive in the respective discord channel. Next, click on `Execute Node` button to test the node. Fill Discord Details Once the execution is successful, you'll receive the content in the discord channel. Discord Response # Notion Source: https://formbricks.com/docs/platform/features/integrations/notion The notion integration allows you to automatically send responses to a Notion database of your choice. If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/docs/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance. ## Formbricks Cloud 1. Click on the `Configuration` tab in the left sidebar and then click on the `Integrations` tab and click on the `connect` button under the `Notion` card. Formbricks Integrations Tab 1. Now click on the "Connect with Notion" button to authenticate yourself with Notion. Connect Formbricks with your Notion account 1. You will now be taken to the Notion OAuth page where you can select the Notion account you want to use for the integration 2. Once you have selected the account and databases and completed the authentication and authorization process, you will be taken back to Formbricks Cloud and see the connected status as below: Formbricks is now connected with Notion Before the next step, make sure that you have a Formbricks Survey with at least one question and a Notion database in the Notion account you integrated. 1. Now click on the "Link New Database" button to link a Notion database with Formbricks and a modal will open up. Link Formbricks with a Notion database 1. Select the Notion database you want to link with Formbricks and the Survey. On doing so, you will be asked to map formbricks' survey questions with selected databases' column. Complete the mapping and click on the "Link Database" button. Question to notion database column mapping 1. On submitting, the modal will close and you will see the linked Notion database in the list of linked Notion databases. List of linked notion databases Congratulations! You have successfully linked a Notion database with Formbricks. Now whenever a response is submitted for the linked survey, it will be automatically added to the linked Notion database. ## Setup in self-hosted Formbricks Enabling the Notion Integration in a self-hosted environment requires a setup using Notion account and changing the environment variables of your Formbricks instance. 1. Sign up for a [Notion](https://www.notion.so/) account, if you don't have one already. 2. Go to the [my integrations](https://www.notion.so/my-integrations) page and click on **New integration**. 3. Fill up the basic information like **Name**, **Logo** and click on **Submit**. 4. Now, click on **Distribution** tab on the sidebar. A text will appear which will ask you to make the integration public. Click on that toggle button. A form will appear below the text. 5. Now provide it the details such as requested. Under **Redirect URIs** field: * If you are running formbricks locally, you can enter `http://localhost:3000/api/v1/integrations/notion/callback`. * Or, you can enter `https:///api/v1/integrations/notion/callback` 6. Once you've filled all the necessary details, click on **Submit**. 7. A screen will appear which will have **Client ID** and **Client secret**. Copy them and set them as the environment variables in your Formbricks instance as: * `NOTION_OAUTH_CLIENT_ID` - OAuth Client ID * `NOTION_OAUTH_CLIENT_SECRET` - OAuth Client Secret Voila! You have successfully enabled the Notion integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in the [Formbricks Cloud](#formbricks-cloud) section to link a Notion database with Formbricks. ## Remove Integration with Notion Account To remove the integration with Slack Workspace, 1. Visit the Integrations tab in your Formbricks Cloud dashboard. 2. Select "Manage" button in the Slack card. 3. Click on the "Delete Integration" button. 4. It will now ask for a confirmation to remove the integration. Click on the "Delete" button to remove the integration. You can always come back and connect again with the same Notion Account. Delete Notion Integration with Formbricks Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Third-party Integrations Source: https://formbricks.com/docs/platform/features/integrations/overview Configure third-party integrations with Formbricks Cloud. At Formbricks, we understand the importance of integrating with third-party applications. We have step-by-step guides to configure our third-party integrations with Formbricks Cloud. For automations that stay inside Formbricks — without a third-party tool — use [Workflows](/docs/workflows/overview) to trigger actions like sending an email when a response is completed. If you are on a self-hosted instance, you will need to configure these integrations manually. Please follow the guides [here](/docs/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance. * [ActivePieces](/docs/platform/features/integrations/activepieces) (Open Source): Automate workflows with ActivePieces no-code automation tool * [Airtable](/docs/platform/features/integrations/airtable): Automatically send responses to an Airtable of your choice. * [Google Sheets](/docs/platform/features/integrations/google-sheets): Automatically send responses to a Google Sheet of your choice. * [HubSpot](/docs/platform/features/integrations/hubspot): Create or update HubSpot contacts automatically when survey responses are submitted. * [Make](/docs/platform/features/integrations/make): Leverage Make's powerful automation capabilities to automate your workflows. * [n8n](/docs/platform/features/integrations/n8n)(Open Source): Automate workflows with n8n's no-code automation tool * [Notion](/docs/platform/features/integrations/notion): Automatically send responses to a Notion database of your choice. * [Slack](/docs/platform/features/integrations/slack): Automatically send responses to a Slack channel of your choice on response events. * [Wordpress](/docs/platform/features/integrations/wordpress)(Open Source): Automatically integrate your Formbricks surveys with your Wordpress website. * [Zapier](/docs/platform/features/integrations/zapier): Connect Formbricks with 2000+ apps on Zapier. *** If you have any questions or need help with any of the integrations or even want a new integration, please reach out to us on [Github Discussions](https://github.com/formbricks/formbricks/discussions). # Slack Source: https://formbricks.com/docs/platform/features/integrations/slack The slack integration allows you to automatically send responses to a Slack channel of your choice. If you are on a self-hosted instance, you will need to configure this integration separately. Please follow the guides [here](/docs/self-hosting/configuration/integrations) to configure integrations on your self-hosted instance. ## Formbricks Cloud 1. Click on the `Configuration` tab in the left sidebar and then click on the `Integrations` tab and click on the `connect` button under the `Slack` card. Formbricks Integrations Tab 1. Now click on the "Connect with Slack" button to authenticate yourself with Slack. Connect Formbricks with your Slack Workspace 1. You will now be taken to the Slack OAuth page where you can select the Slack channel you want to link with Formbricks and click on the "Allow" button. Slack OAuth Page 1. Once you have selected the account and completed the authentication process, you will be taken back to Formbricks Cloud and see the connected status as below: Formbricks is now connected with Slack Before the next step, make sure that you have a Formbricks Survey with at least one question and a Slack channel in the Slack workspace you integrated. 1. In order to make your channel available in channel dropdown, you need to add formbricks integration bot to the channel you want to link. You can do this by going to channel settings -> Integrations -> Add apps -> Search for "Formbricks" -> Select the bot -> Add. Click on three dot at top right of the channel Select Edit Settings Navigate to Integrations Add Formbricks Bot 1. Now click on the "Link channel" button to link a Slack channel with Formbricks and a modal will open up. Link Formbricks with a Slack Channel 1. Select the channel you want to link with Formbricks and the Survey. On doing so, you will be asked to select the questions' responses you want to feed in the Slack channel. Select the questions and click on the "Link Channel" button. Select question to link with Slack Channel 1. On submitting, the modal will close and you will see the linked Slack channel in the list of linked Slack channels. List of linked Slack Channels Congratulations! You have successfully linked a Slack channel with Formbricks. Now whenever a response is submitted for the linked survey, it will be automatically sent to the linked Slack channel. ## Setup in self-hosted Formbricks Enabling the Slack Integration in a self-hosted environment requires a setup using slack workspace account and changing the environment variables of your Formbricks instance. If you are running Formbricks locally: You need to use `https` instead of `http` for the redirect URI. * You can update the `go` script in your `apps/web/package.json` to include the `--experimental-https` flag. The command will look like:
```bash theme={null} "go": next dev --experimental-https -p 3000 ``` * You also need to update the .env file in the `apps/web` directory to include the `WEBAPP_URL` as `https://localhost:3000` instead of `http://localhost:3000`. * You also need to run the terminal in admin mode to run the `go` script(to acquire the SSL certificate). You can do this by running the terminal as an administrator or using the `sudo` command in Unix-based systems.
1. Create a Slack workspace if you don't have one already. 2. Go to the [Your apps](https://api.slack.com/apps) page and **Create New App**. 3. Click on **From Scratch** and provide the **App Name** and select your workspace in **Pick a workspace to develop your app in:** dropdown. Click on **Create App**. 4. Go to the **OAuth & Permissions** tab on the sidebar and add the following **Bot Token Scopes**: * `channels:read` * `groups:read` * `chat:write` * `chat:write.public` * `chat:write.customize` 5. Add the **Redirect URLs** under **OAuth & Permissions** tab. You can add the following URLs: * If you are running formbricks locally, you can enter `https://localhost:3000/api/v1/integrations/slack/callback`. * Or, you can enter `https:///api/v1/integrations/slack/callback` 6. Now, click on **Install to Workspace** and **Allow** the permissions. 7. Go to the **Basic Information** tab on the sidebar and copy the **Client ID** and **Client Secret**. Copy them and set them as the environment variables in your Formbricks instance as: * `SLACK_CLIENT_ID` - OAuth Client ID * `SLACK_CLIENT_SECRET` - OAuth Client Secret 1. Now, you need to enable the public distribution of your app. Go to the **Basic Information** tab and click on the **Manage distribution** button and click on the "Distribute App". 2. Scroll down to the **Share your app with other workspaces** section, complete the checklist and click on the **Activate public distribution** button. ### By now, your environment variables should include the below ones: * `SLACK_CLIENT_ID` * `SLACK_CLIENT_SECRET` Voila! You have successfully enabled the Slack integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in the [Formbricks Cloud](#formbricks-cloud) section to link a Slack workspace with Formbricks. ## Remove Integration with Slack Workspace To remove the integration with Slack Workspace, 1. Visit the Integrations tab in your Formbricks Cloud dashboard. 2. Select "Manage" button in the Slack card. 3. Click on the "Delete Integration" button. 4. It will now ask for a confirmation to remove the integration. Click on the "Delete" button to remove the integration. You can always come back and connect again with the same Slack Workspace. Delete Slack Integration with Formbricks Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Webhooks Source: https://formbricks.com/docs/platform/features/integrations/webhooks With webhooks, you can receive real-time HTTP notifications when specific objects change in your Formbricks environment. This allows you to stay updated and trigger automated actions based on these events. ### **Webhook Triggers** Webhooks are configured to send notifications based on trigger events. The available triggers include: * **`responseCreated`** * **`responseUpdated`** * **`responseFinished`** *** ## **Creating Webhooks** You can create webhooks either through the **Formbricks App UI** or programmatically via the **Webhook API**. ## **Creating Webhooks via UI** * **Log in to Formbricks** and click on the `Configuration` tab in the left sidebar and then click on the `Integrations` tab. Step one * Click on **Manage Webhooks** & then **Add Webhook** button: ![Step two](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738094259/j4a92z2q43twgamogpny.webp) * Add your webhook listener endpoint and test it to make sure the endpoint is reachable and accepts `POST` requests. ![Step three](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738094617/image_kubsnz.jpg) * Now add the triggers you want to listen to and the surveys! * That’s it! Your webhooks will now start receiving data as soon as it arrives! ![Step five](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738094816/image_xvrel1.jpg) ## **Creating Webhooks via API** Use our documented methods on the **Creation**, **List**, and **Deletion** endpoints of the Webhook API mentioned in our [API v2 playground](https://formbricks.com/docs/api-v2-reference/management-api-%3E-webhooks/get-webhooks). ## Testing Webhooks Locally If you want to test a webhook consumer running on your machine before deploying it, you can expose your local endpoint with [ngrok](https://ngrok.com/docs/universal-gateway/http). Run your local endpoint on a port like `3000`. Create a public HTTPS URL for your local service, for example with `ngrok http http://localhost:3000`. Paste the ngrok URL into your webhook endpoint, click **Test Endpoint**, and then save the webhook once the endpoint is reachable. To avoid sending unwanted test responses to production workflows, create a separate test survey in your Workspace and use that survey copy in your development workflow while validating the webhook setup. ### Allowing Internal URLs (Self-Hosted Only) By default, Formbricks blocks webhook URLs that point to private or internal IP addresses (e.g. `localhost`, `192.168.x.x`, `10.x.x.x`) to prevent [SSRF attacks](https://owasp.org/www-community/attacks/Server-Side_Request_Forgery). If you are self-hosting Formbricks and need to send webhooks to internal services, you can set the following environment variable: ```sh theme={null} DANGEROUSLY_ALLOW_WEBHOOK_INTERNAL_URLS=1 ``` Only enable this on trusted, self-hosted environments. Enabling this on a publicly accessible instance exposes your server to SSRF risks. If you encounter any issues or need help setting up webhooks, feel free to reach out to us on [GitHub Discussions](https://github.com/formbricks/formbricks/discussions). 😃 *** ## Webhook Security with Standard Webhooks Formbricks implements the [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks) specification to ensure webhook requests can be verified as genuinely originating from Formbricks. ### Webhook Headers Every webhook request includes the following headers: | Header | Description | Example | | ------------------- | ---------------------------------------------------- | -------------------------------------- | | `webhook-id` | Unique message identifier | `019ba292-c1f6-7618-aaf2-ecf8e39d1cc7` | | `webhook-timestamp` | Unix timestamp (seconds) when the webhook was sent | `1704547200` | | `webhook-signature` | HMAC-SHA256 signature (only if secret is configured) | `v1,K3Q2bXlzZWNyZXQ=` | ### Signing Secret When you create a webhook (via the UI or API), Formbricks automatically generates a unique signing secret for that webhook. The secret follows the Standard Webhooks format: `whsec_` followed by a base64-encoded random value. **Via UI:** After creating a webhook, the signing secret is displayed immediately. Copy and store it securely—you can also view it later in the webhook settings. **Via API:** The signing secret is returned in the webhook creation response. This secret is used to generate the HMAC signature included in each webhook request, allowing you to verify the authenticity of incoming webhooks. ### Signature Verification The signature is computed as follows: ``` signed_content = "{webhook-id}.{webhook-timestamp}.{body}" signature = base64(HMAC-SHA256(secret, signed_content)) header_value = "v1,{signature}" ``` ### Validating Webhooks To validate incoming webhook requests: 1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers 2. Verify the timestamp is within an acceptable tolerance (e.g., 5 minutes) to prevent replay attacks 3. Decode the secret by stripping the `whsec_` prefix and base64 decoding the rest 4. Compute the expected signature using HMAC-SHA256 with the decoded secret 5. Compare your computed signature with the received signature (after stripping the `v1,` prefix) ### Node.js Verification Functions ```javascript theme={null} const crypto = require("crypto"); const WEBHOOK_TOLERANCE_IN_SECONDS = 300; // 5 minutes /** * Decodes a Formbricks webhook secret (whsec_...) to raw bytes */ function decodeSecret(secret) { const base64 = secret.startsWith("whsec_") ? secret.slice(6) : secret; return Buffer.from(base64, "base64"); } /** * Verifies the webhook timestamp is within tolerance * @throws {Error} if timestamp is too old or too new */ function verifyTimestamp(timestampHeader) { const now = Math.floor(Date.now() / 1000); const timestamp = parseInt(timestampHeader, 10); if (isNaN(timestamp)) { throw new Error("Invalid timestamp"); } if (Math.abs(now - timestamp) > WEBHOOK_TOLERANCE_IN_SECONDS) { throw new Error("Timestamp outside tolerance window"); } return timestamp; } /** * Computes the expected signature for a webhook payload */ function computeSignature(webhookId, timestamp, body, secret) { const signedContent = `${webhookId}.${timestamp}.${body}`; const secretBytes = decodeSecret(secret); return crypto.createHmac("sha256", secretBytes).update(signedContent).digest("base64"); } /** * Verifies a Formbricks webhook request * @param {string} body - Raw request body as string * @param {object} headers - Object containing webhook-id, webhook-timestamp, webhook-signature * @param {string} secret - Your webhook secret (whsec_...) * @returns {boolean} true if valid * @throws {Error} if verification fails */ function verifyWebhook(body, headers, secret) { const webhookId = headers["webhook-id"]; const webhookTimestamp = headers["webhook-timestamp"]; const webhookSignature = headers["webhook-signature"]; if (!webhookId || !webhookTimestamp || !webhookSignature) { throw new Error("Missing required webhook headers"); } // Verify timestamp const timestamp = verifyTimestamp(webhookTimestamp); // Compute expected signature const expectedSignature = computeSignature(webhookId, timestamp, body, secret); // Extract signature from header (format: "v1,{signature}") const receivedSignature = webhookSignature.split(",")[1]; if (!receivedSignature) { throw new Error("Invalid signature format"); } // Use constant-time comparison to prevent timing attacks const expectedBuffer = Buffer.from(expectedSignature, "utf8"); const receivedBuffer = Buffer.from(receivedSignature, "utf8"); if ( expectedBuffer.length !== receivedBuffer.length || !crypto.timingSafeEqual(expectedBuffer, receivedBuffer) ) { throw new Error("Invalid signature"); } return true; } module.exports = { verifyWebhook, decodeSecret, computeSignature, verifyTimestamp }; ``` **Usage:** ```javascript theme={null} // In your webhook handler, use the raw body (not parsed JSON) try { verifyWebhook(rawBody, req.headers, process.env.FORMBRICKS_WEBHOOK_SECRET); const payload = JSON.parse(rawBody); // Process verified webhook... } catch (error) { // Verification failed - reject the request console.error("Webhook verification failed:", error.message); } ``` Always use the **raw request body** (as a string) for signature verification, not the parsed JSON object. Parsing and re-stringifying can change the formatting and break signature validation. ### Using Standard Webhooks Libraries You can also use the official [Standard Webhooks libraries](https://github.com/standard-webhooks/standard-webhooks#libraries) available for various languages: * **Node.js**: `npm install standardwebhooks` * **Python**: `pip install standardwebhooks` * **Go, Ruby, Java, Kotlin, PHP, Rust**: See the [Standard Webhooks GitHub](https://github.com/standard-webhooks/standard-webhooks) *** ## Example Webhook Payloads We provide the following webhook payloads, `responseCreated`, `responseUpdated`, and `responseFinished`. ### Response Created Example of Response Created webhook payload: ```json theme={null} { "data": { "contact": null, "contactAttributes": null, "createdAt": "2025-07-24T07:47:29.507Z", "data": { "welcome_card_cta": "clicked" }, "displayId": "displayId", "endingId": null, "finished": false, "id": "responseId", "language": "en", "meta": { "country": "DE", "url": "https://app.formbricks.com/s/surveyId", "userAgent": { "browser": "Chrome", "device": "desktop", "os": "macOS" } }, "singleUseId": null, "survey": { "createdAt": "2025-07-20T10:30:00.000Z", "status": "inProgress", "title": "Customer Satisfaction Survey", "type": "link", "updatedAt": "2025-07-24T07:45:00.000Z" }, "surveyId": "surveyId", "tags": [], "ttc": { "welcome_card_cta": 2154.700000047684 }, "updatedAt": "2025-07-24T07:47:29.507Z", "variables": {} }, "event": "responseCreated", "webhookId": "webhookId" } ``` ### Response Updated Example of Response Updated webhook payload: ```json theme={null} { "data": { "contact": null, "contactAttributes": null, "createdAt": "2025-07-24T07:47:29.507Z", "data": { "visit_reason": "Just browsing", "welcome_card_cta": "clicked" }, "displayId": "displayId", "endingId": null, "finished": false, "id": "responseId", "language": "en", "meta": { "country": "DE", "url": "https://app.formbricks.com/s/surveyId", "userAgent": { "browser": "Chrome", "device": "desktop", "os": "macOS" } }, "singleUseId": null, "survey": { "createdAt": "2025-07-20T10:30:00.000Z", "status": "inProgress", "title": "Customer Satisfaction Survey", "type": "link", "updatedAt": "2025-07-24T07:45:00.000Z" }, "surveyId": "surveyId", "tags": [], "ttc": { "visit_reason": 3855.799999952316, "welcome_card_cta": 2154.700000047684 }, "updatedAt": "2025-07-24T07:47:33.696Z", "variables": {} }, "event": "responseUpdated", "webhookId": "webhookId" } ``` ### Response Finished Example of Response Finished webhook payload: ```json theme={null} { "data": { "contact": null, "contactAttributes": null, "createdAt": "2025-07-24T07:47:29.507Z", "data": { "newsletter_consent": "accepted", "welcome_card_cta": "clicked" }, "displayId": "displayId", "endingId": "endingId", "finished": true, "id": "responseId", "language": "en", "meta": { "country": "DE", "url": "https://app.formbricks.com/s/surveyId", "userAgent": { "browser": "Chrome", "device": "desktop", "os": "macOS" } }, "singleUseId": null, "survey": { "createdAt": "2025-07-20T10:30:00.000Z", "status": "inProgress", "title": "Customer Satisfaction Survey", "type": "link", "updatedAt": "2025-07-24T07:45:00.000Z" }, "surveyId": "surveyId", "tags": [], "ttc": { "_total": 4947.899999035763, "newsletter_consent": 2793.199999988079, "welcome_card_cta": 2154.700000047684 }, "updatedAt": "2025-07-24T07:47:56.116Z", "variables": {} }, "event": "responseFinished", "webhookId": "webhookId" } ``` # Wordpress Source: https://formbricks.com/docs/platform/features/integrations/wordpress Target specific visitors with a survey on your WordPress page using Formbricks for free. Show survey on specific page or on button click. To run a targeted survey on your WordPress website, Formbricks is the way to go! With our generous free plan and open source tech, you get everything you need to get started and keep full control over your data. ## TLDR 1. Install the Formbricks WordPress plugin 2. Create a [free Formbricks account](https://app.formbricks.com/auth/signup) 3. Find and copy the `workspaceId` 4. Copy the `workspaceId` into the right field in the plugin settings 5. Create survey on trigger “New Session” to test it ## Step 1: Install the Formbricks WordPress plugin As long as the Formbricks plugin is in review, please download it from our [GitHub repository directly.](https://github.com/formbricks/wordpress) Run targeted website survye on any WordPress site ## Step 2: Create a Formbricks Account This is super straight forward: Go to [app.formbricks.com/auth/signup](https://app.formbricks.com/auth/signup), create the account, verify your email and you’re in! When you see this screen, you’re there: Free HotJar survey alternative open source ## Step 3: Find and copy the workspaceId Go to **Settings → Workspace → Connect Your App** where you’ll find your workspaceId: Run targeted surveys for free on WordPress pages ## Step 4: Copy the workspaceId to the WordPress Plugin Settings In your WordPress instance, go to the Formbricks Plugin settings and copy the workspaceId in the correct field: Free and open source HotJar survey on WordPress page Then click the button at the bottom to see if the connection worked. If you don’t use our Cloud, you also have to update the API Host Great! ## Step 5: Create survey on trigger “New Session” Now that all is setup, we create a survey to display an example survey. Create a survey and open survey editor. Keep the content for now, click on the Settings tab: Free and open source HotJar survey on WordPress page Here we do three things: 1. Change survey type to **App Survey** or **Website Survey**(for public facing) 2. Select trigger “New Session” 3. Publish Open Source survey on targeted website wordpress When you see this page, you did it! Run free an open source targeted survey on any page ## Step 6: Reload your page to check out your survey 🤓 You did it! Reload the WordPress page and your survey should appear! ## Doesn't work? If you have any questions or need help, feel free to reach out to us on [Github Discussions](https://github.com/formbricks/formbricks/discussions) # Zapier Source: https://formbricks.com/docs/platform/features/integrations/zapier Master the integration of Formbricks with Zapier using our detailed guide. Seamlessly connect your surveys to 5000+ apps, automate data transfers, and enhance feedback management. Start optimizing your workflow today. Nail down your survey first? Any changes in the survey cause additional work in the Zap. It makes sense to first settle on the survey you want to run and then get to setting up Zapier. ## Step 1: Setup your survey incl. `questionId` for every question When setting up the Zap your life will be easier when you change the `questionId`s of your survey questions. You can only do so **before** you publish your survey. Update Question ID *In every question card in the Advanced Settings you find the Question ID field. Update it so that you’ll recognize the response tied to this question.* Already published? Duplicate survey You can only update the questionId when the survey was not yet published. Already published it? Just **duplicate it** to update the questionIds. Duplicate Survey ## Step 2: Send a test response In order to set up Zapier you’ll need a test response. This allows you to select the individual values of each response in your Zap. Submit Test Response ## Step 3: Setup your Zap Go to [zapier.com](https://zapier.com) and create a new Zap. Search for “Formbricks” to get started: Add New Zap Then, choose the event you want to trigger the Zap on: Choose Event ## Step 4: Connect Formbricks with Zapier Now, you have to connect Zapier with Formbricks via an API Key: Connect with Formbricks - 1 Connect with Formbricks - 2 Now you need an API key. Please refer to the [API Key Setup](/docs/api-reference/rest-api#how-to-generate-an-api-key) page to learn how to create one. Once you copied it in the newly opened Zapier window, you will be connected: Successful Connection ## Step 5: Select Survey Next, you can choose from all the surveys you have created in this environment: Select Survey ## Step 6: Test your trigger Once you hit “Test” you will see the three most recent submissions for this survey. If you don’t have any submissions in the survey, submit one to continue setting up your Zap: Test Submission *Now you're happy that you updated the questionId's* ## Step 7: Set up your Zap Now you have all the data you need at hand. The next steps depend on what you want to do with it. In this tutorial, we will send submissions to a Slack channel: Slack Channel Message In the action itself we can determine the data and layout of the message. Here, we only choose the submission data. You can also refer to the meta data of the submission and the [attributes](/docs/surveys/website-app-surveys/user-identification) of the person who submitted the survey. Slack Message We now receive a notifcation in our Slack channel whenever a Churn survey is completed: Zapier Message # Styling Theme Source: https://formbricks.com/docs/platform/features/styling-theme Keep the survey styling consistent over all surveys with a Styling Theme. Customize colors, fonts, buttons, inputs, and more to match your brand. **Self-Hosting Requirements**: Uploading custom background images and brand logos requires file upload storage to be configured. If you're self-hosting Formbricks, make sure to [configure file uploads](/docs/self-hosting/configuration/file-uploads) before using these features. Keep the survey styling consistent over all surveys with a Styling Theme. Customize colors, fonts, buttons, inputs, and other styling options to match your brand's aesthetic. ## Configuration In the left sidebar, open **Settings → Workspace → Appearance**: Appearance ## Survey styling The Survey styling section gives you granular control over every text and input element in your survey. Expand the **Survey styling** panel to find collapsible sub-sections for Headlines & Descriptions, Inputs, Buttons, and Options. ### Headlines & Descriptions Fine-tune how question headlines, descriptions, and upper labels appear: | Property | Description | | --------------------------- | ------------------------------------------------------------ | | **Headline Color** | Color of the question headline text | | **Description Color** | Color of the question description text | | **Headline Font Size** | Font size for headlines (in `px` or any CSS unit) | | **Description Font Size** | Font size for descriptions | | **Headline Font Weight** | Numeric font weight for headlines (e.g. `400`, `600`, `700`) | | **Description Font Weight** | Numeric font weight for descriptions | | **Upper Label Color** | Color of the small labels above input fields | | **Upper Label Font Size** | Font size for upper labels | | **Upper Label Font Weight** | Numeric font weight for upper labels | ### Inputs Control the appearance of text inputs, textareas, and other form fields: | Property | Description | | ----------------------- | ------------------------------------------------------------------ | | **Input Color** | Background color of input fields | | **Input Border Color** | Border color of input fields | | **Input Text Color** | Color of text typed into inputs | | **Border Radius** | Corner roundness of input fields (in `px` or any CSS unit) | | **Height** | Height of input fields | | **Font Size** | Font size of text inside inputs | | **Padding X** | Horizontal padding inside inputs | | **Padding Y** | Vertical padding inside inputs | | **Placeholder Opacity** | Opacity of placeholder text (`0` to `1`) | | **Shadow** | CSS box-shadow value for inputs (e.g. `0 1px 2px rgba(0,0,0,0.1)`) | ### Buttons Customize the submit and navigation buttons: | Property | Description | | --------------------- | ----------------------------------- | | **Button Background** | Background color of buttons | | **Button Text** | Text color of buttons | | **Border Radius** | Corner roundness of buttons | | **Height** | Height of buttons | | **Font Size** | Font size of button text | | **Font Weight** | Numeric font weight for button text | | **Padding X** | Horizontal padding inside buttons | | **Padding Y** | Vertical padding inside buttons | ### Options Style the select options in single-select, multi-select, and similar question types: | Property | Description | | ---------------------- | --------------------------------- | | **Option Background** | Background color of option items | | **Option Label Color** | Text color of option labels | | **Border Radius** | Corner roundness of option items | | **Padding X** | Horizontal padding inside options | | **Padding Y** | Vertical padding inside options | | **Font Size** | Font size of option text | ## Card Styling Adjust the look of the survey card container: | Property | Description | | ------------------------- | ----------------------------------------------------------------------- | | **Roundness** | Corner roundness of the survey card (in `px` or any CSS unit) | | **Card Background Color** | Background color of the survey card | | **Card Border Color** | Border color of the survey card | | **Add Highlight Border** | Adds a distinct colored border for emphasis (app surveys only) | | **Card Arrangement** | Layout mode for stacking cards: **Simple**, **Straight**, or **Casual** | ### Progress Bar When the progress bar is visible (toggle **Hide Progress Bar** to control it), you can customize: | Property | Description | | ------------------------ | -------------------------------------- | | **Track Background** | Background color of the progress track | | **Indicator Background** | Fill color of the progress indicator | | **Track Height** | Height of the progress bar track | ## Background Styling Customize the survey background with static colors, animations, or images (upload your own or pick from Unsplash). Background styling is only available for Link Surveys. | Property | Description | | ---------------------- | ---------------------------------------------------------------- | | **Color** | Pick any color for the background | | **Animation** | Add dynamic animations to enhance user experience | | **Upload** | Use a custom uploaded image (5 MB max) | | **Image** | Choose from Unsplash's gallery (attribution shown automatically) | | **Background Overlay** | Adjust the background's opacity / brightness | ## Add Brand Logo Customize your survey with your brand's logo. Brand logos are only visible on Link Survey pages. In **Settings → Workspace → Appearance**, scroll down to the **Logo Upload** box. Logo upload box Upload your logo. Logos must be 5 MB or less. Upload logo If you've uploaded a transparent image and want to add a background to it, enable the toggle and select a color. Logo background color Remember to save your changes! Save changes The logo settings apply across all Link Survey pages. ## Overwrite Styling Theme You can allow overwriting the styling theme for individual surveys to create unique styles per survey: Allow overwrite toggle In the survey editor, a **Styling** tab will appear where you can overwrite the default styling theme. See the [Custom Styling](/docs/surveys/general-features/overwrite-styling) guide for details. ## CSS Variables Reference Under the hood, every styling property maps to a CSS variable prefixed with `--fb-`. For App & Website Surveys, you can override these directly in your global CSS file (e.g., `globals.css`) by targeting the `#fbjs` selector. See the full [CSS Variables Reference](/docs/surveys/general-features/overwrite-styling#overwrite-css-styles-for-app--website-surveys) in the Custom Styling guide. # User Management Overview Source: https://formbricks.com/docs/platform/features/user-management Manage organization members, roles, teams, and security settings to control access and collaboration in your Formbricks organization. Formbricks provides comprehensive user management capabilities to help you control access, organize teams, and secure your organization. This section covers everything you need to know about managing users, roles, and permissions. ## Key concepts Formbricks uses a flexible permission system with multiple layers: * **Organization roles** - Control access across the entire organization * **Team roles** - Provide granular permissions within specific teams * **Workspace permissions** - Fine-tune access to individual Workspaces * **Security features** - Protect accounts with two-factor authentication Advanced user management features are part of the [Enterprise Edition](/docs/self-hosting/advanced/license). The Community Edition and Free/Startup Cloud plans support unlimited organization members with Owner permissions. ## Get started Learn about organization-level roles and how they control access to teams, Workspaces, and data across your Formbricks organization. Understand team-level roles and Workspace permissions that enable granular access control within teams and Workspaces. Learn how to invite new members to your organization individually or in bulk, and manage invitation workflows. Secure your account with an additional layer of protection using time-based codes from authenticator apps and backup codes. # Invite members Source: https://formbricks.com/docs/platform/features/user-management/invite-members Learn how to invite new members to your organization individually or in bulk, and manage invitation workflows. Add new members to your Formbricks organization to collaborate on surveys and manage Workspaces together. You can invite members individually or in bulk using CSV uploads. ## Prerequisites To invite members, you need: * **Owner** or **Manager** role in the organization * Valid email addresses for the people you want to invite ## Individual invitations Use this method when inviting a few people or when you need to carefully control each invitation. ### Steps to invite individual members Go to **Settings → Organization → Teams**, where members are managed. Access Control Tab Click on the `Add member` button: Add member Button Position In the modal, add the Name, Email and Role of the organization member you want to invite: Individual Invite Modal Tab Click the invite button to send the invitation email. ### What happens next 1. Formbricks sends an email to the organization member with an invitation link 2. The organization member can accept the invitation or create a new account by clicking on the link 3. Once accepted, they'll have access based on the role you assigned ## Bulk invitations Use bulk invitations when you need to invite many people at once, such as when onboarding an entire team or department. ### Steps to invite members in bulk Go to **Settings → Organization → Teams**, where members are managed. Click on the `Add member` button: Add member Button Position In the modal, switch to `Bulk Invite`. You can download an example .CSV file to fill in the Name, Email and Role of the organization members you want to invite: Individual Invite Modal Tab The CSV file should include three columns: * **Name**: Full name of the person * **Email**: Valid email address * **Role**: One of the available organization roles (Owner, Manager, Billing, Member) Upload the filled .CSV file and invite the organization members in bulk ✅ ### What happens next * Formbricks sends an email to each organization member in the CSV * Each member can accept the invitation or create a new account by clicking on the link * All invitations are processed simultaneously ## Managing invitations ### Pending invitations * View pending invitations under **Settings → Organization → Teams** * Resend invitations if needed * Cancel pending invitations before they're accepted ### Invitation status Monitor the status of your invitations: * **Pending**: Invitation sent but not yet accepted * **Accepted**: User has joined the organization * **Expired**: Invitation has expired and needs to be resent # Organizations and roles Source: https://formbricks.com/docs/platform/features/user-management/organizations-and-roles Understand organization-level roles and how they control access to teams, Workspaces, and data across your Formbricks organization. Organization-level roles apply to all teams and Workspaces within your Formbricks organization. These roles provide broad permissions that determine what users can do across the entire organization. Access Roles is a feature of the [Enterprise Edition](/docs/self-hosting/advanced/license). In the **Community Edition** and on the **Free** and **Startup** plan in the Cloud you can invite unlimited organization members as `Owner`. ## Role hierarchy Here are the different access permissions, ranked from highest to lowest access: 1. **Owner** - Full organizational control 2. **Manager** - Management access with some restrictions 3. **Billing** - Billing and payment management only 4. **Member** - Basic access to assigned Workspaces ### Role Permissions and Privilege Escalation Prevention To prevent privilege escalation, the following rules apply: * **Owners** can: * Invite users as owners, managers, or members * Assign roles up to owner level * **Managers** can: * Invite users only as members * Assign roles up to member only, not manager or owner * **Members** cannot: * Invite users * Assign roles ## Organization-level roles All users and their organization-level roles are listed under **Settings → Organization → Teams**. Users can hold any of the following org-level roles: ### Owner * Have full access to the organization, its data, and settings * Can perform Team Admin actions without needing to join the team * Can manage all aspects of the organization including billing, integrations, and member management ### Manager * Have full management access to all teams and Workspaces * Can manage the organization's membership (but can only invite or assign users as members) * Can perform Team Admin actions without needing to join the team * Cannot change other organization settings like billing or delete the organization ### Billing * Can manage payment and compliance details in the organization * Have access to billing settings and subscription management * Cannot access other organizational data or settings ### Member * Can view most data in the organization and act in the Workspaces they are members of * Cannot create or join Workspaces on their own and need to be assigned by owners or managers * Have limited permissions that depend on their Workspace-level access ## Detailed permissions matrix | | Owner | Manager | Billing | Member | | ---------------------------------- | ----- | ------- | ------- | ------ | | **Organization** | | | | | | Update organization | ✅ | ❌ | ❌ | ❌ | | Delete organization | ✅ | ❌ | ❌ | ❌ | | Add new member | ✅ | ✅ | ❌ | ❌ | | Delete member | ✅ | ✅ | ❌ | ❌ | | Update member access | ✅ | ✅ | ❌ | ❌ | | Update billing | ✅ | ✅ | ✅ | ❌ | | **Workspace** | | | | | | Create Workspace | ✅ | ✅ | ❌ | ❌ | | Update Workspace name | ✅ | ✅ | ❌ | ✅\*\* | | Update Workspace recontact options | ✅ | ✅ | ❌ | ✅\*\* | | Update look & feel | ✅ | ✅ | ❌ | ✅\*\* | | Update survey languages | ✅ | ✅ | ❌ | ✅\*\* | | Delete Workspace | ✅ | ✅ | ❌ | ❌ | | **Surveys** | | | | | | Create new survey | ✅ | ✅ | ❌ | ✅\* | | Edit survey | ✅ | ✅ | ❌ | ✅\* | | Delete survey | ✅ | ✅ | ❌ | ✅\* | | View survey results | ✅ | ✅ | ❌ | ✅ | | **Response** | | | | | | Delete response | ✅ | ✅ | ❌ | ✅\* | | Add tags on response | ✅ | ✅ | ❌ | ✅\* | | Edit tags on response | ✅ | ✅ | ❌ | ✅\* | | Download survey responses (CSV) | ✅ | ✅ | ❌ | ✅\* | | **Actions** | | | | | | Create action | ✅ | ✅ | ❌ | ✅\* | | Update action | ✅ | ✅ | ❌ | ✅\* | | Delete action | ✅ | ✅ | ❌ | ✅\* | | **API keys** | | | | | | Create API key | ✅ | ✅ | ❌ | ✅\*\* | | Update API key | ✅ | ✅ | ❌ | ✅\*\* | | Delete API key | ✅ | ✅ | ❌ | ✅\*\* | | **Tags** | | | | | | Create tags | ✅ | ✅ | ❌ | ✅\* | | Update tags | ✅ | ✅ | ❌ | ✅\* | | Delete tags | ✅ | ✅ | ❌ | ✅\*\* | | **Contacts** | | | | | | Delete contact | ✅ | ✅ | ❌ | ✅\* | | **Integrations** | | | | | | Manage integrations | ✅ | ✅ | ❌ | ✅\* | \* - for the read & write permissions team members \*\* - for the manage permissions team members ## Best practices * **Principle of least privilege**: Assign users the minimum role necessary for their responsibilities * **Regular audits**: Periodically review organization members and their roles * **Owner role**: Limit the number of owners to reduce security risk * **Manager role**: Use for team leads who need to manage Workspaces but not organizational settings # Teams and roles Source: https://formbricks.com/docs/platform/features/user-management/teams-and-roles Learn about team-level roles and workspace permissions that enable granular access control within teams and workspaces. Team-level roles provide more granular permissions within specific teams and workspaces. These roles work alongside organization-level roles to create a flexible permission system. Team-level roles are a feature of the [Enterprise Edition](/docs/self-hosting/advanced/license). In the Community Edition, all members are Organisation-level "Owners". ## Understanding the role hierarchy Formbricks uses a two-tier permission system: 1. **Organization-level roles** - Apply across all teams and workspaces 2. **Team-level roles** - Apply within specific teams and workspaces ### How roles interact * Organization-level roles (Owner, Manager) can override team-level restrictions * Team-level roles provide granular control for specific teams * Workspace permissions further refine what users can do within individual workspaces ## Team-level roles ### Team Admins * Have additional permissions to manage their team's membership and workspaces * Can add or remove team members * Can create and manage workspaces within their team * Can assign workspace-level permissions to team members * These permissions are granted at the team-level and don't apply to teams where they're not a Team Admin ### Team Contributors * Can view and act on surveys and responses within their assigned workspaces * Cannot manage team membership or create new workspaces * Permissions depend on their workspace-level access (Read, Read & Write, or Manage) ## Workspace-level permissions Within each workspace, team members can have one of three permission levels: ### Read * Read access to all resources (except settings) in the workspace * Can view surveys, responses, and analytics * Cannot create, edit, or delete surveys * Cannot modify workspace settings ### Read & Write * Read & write access to all resources (except settings) in the workspace * Can create, edit, and delete surveys * Can manage responses and tags * Can download survey data * Cannot modify workspace settings or manage integrations ### Manage * Read & write access to all resources including settings in the workspace * Full workspace control including settings * Can manage API keys and integrations * Can configure workspace-level settings like recontact options and styling * Can manage workspace tags and actions ## Permission examples ### Scenario 1: Marketing Team Member * **Organization role**: Member * **Team role**: Team Contributor * **Workspace permission**: Read & Write * **Can do**: Create and edit surveys, view responses, download data * **Cannot do**: Change workspace settings, manage team membership ### Scenario 2: Team Lead * **Organization role**: Member * **Team role**: Team Admin * **Workspace permission**: Manage * **Can do**: Everything within their team including managing members and workspace settings * **Cannot do**: Access other teams, change organization settings ### Scenario 3: Department Manager * **Organization role**: Manager * **Team role**: N/A (org role overrides) * **Workspace permission**: N/A (org role provides access) * **Can do**: Access all teams and workspaces, manage organization membership * **Cannot do**: Change organization-level settings like billing # Two-factor authentication Source: https://formbricks.com/docs/platform/features/user-management/two-factor-auth Secure your account with an additional layer of protection using time-based codes from authenticator apps and backup codes. Two-factor authentication (2FA) adds an extra layer of security to user accounts by requiring a second form of verification in addition to the password. This significantly reduces the risk of unauthorized access even if passwords are compromised. Two-factor authentication is part of the [Enterprise Edition](/docs/self-hosting/advanced/license). ## Prerequisites To use two-factor authentication, users must: * Have an account with email-based authentication (third-party login providers like Google SSO are not compatible with 2FA) * Have a TOTP-compatible authenticator app installed on their device (such as Google Authenticator, Authy, or 1Password) ## Setting up Two-factor authentication Users can enable 2FA from their profile: 1. Navigate to **Settings → Account → Your Profile** via the menu in the lower right corner 2. In the **Security** section, toggle the **Two-factor authentication** switch 3. Follow the setup wizard: **Step 1: Confirm Password** * Enter your current password to verify your identity **Step 2: Scan QR Code** * Use your authenticator app to scan the displayed QR code * Alternatively, manually enter the provided secret key into your authenticator app **Step 3: Verify Setup** * Enter the 6-digit code from your authenticator app to confirm the setup **Step 4: Save Backup Codes** * **Important**: Save the 10 backup codes in a secure location * These codes can be used to access your account if you lose access to your authenticator device * Each backup code can only be used once Store your backup codes in a secure location. If you lose access to both your authenticator device and backup codes, you will need administrator assistance to regain access to your account. ## Logging in with Two-factor authentication Once 2FA is enabled, the login process requires an additional step: 1. Enter your email and password as usual 2. When prompted, enter either: * A 6-digit code from your authenticator app, or * One of your backup codes (use format: xxxxx-xxxxx or just the 10-character code) ## Managing Two-factor authentication ### Disabling 2FA To disable two-factor authentication: 1. Go to **Settings → Account → Your Profile** and scroll to the **Security** section 2. Toggle off the **Two-factor authentication** switch 3. Confirm by entering either: * Your password and a TOTP code from your authenticator app, or * Your password and a backup code When 2FA is disabled, all associated backup codes are permanently deleted for security reasons. ### Re-enabling 2FA If you need to set up 2FA again (for example, after getting a new device): 1. Follow the same setup process described above 2. New backup codes will be generated 3. Old backup codes (if any existed) will be invalidated # Introduction Source: https://formbricks.com/docs/platform/introduction Welcome to the Formbricks Documentation! ## Welcome to the Formbricks Documentation! Formbricks is a versatile open-source platform for collecting and analyzing feedback from customers, users, and employees through targeted surveys. Whether you need simple forms or complex experience management solutions, Formbricks scales with your needs. Formbricks is an end-to-end experience management (XM) suite: **Ask** with surveys, **Analyze** with Unify Feedback and dashboards, and **Act** with Workflows. This guide covers everything you need to set up, use, and develop with Formbricks. You'll find step-by-step instructions, feature explanations, and best practices. It also includes advanced docs on extending Formbricks using its self-hosted option, APIs, and SDKs. Learn how to use Formbricks' XM & Surveys to collect feedback from your customers, users, and employees. Learn how to self-host Formbricks on your infrastructure. Learn how to use Formbricks' API to CRUD various resources programmatically. Warm up with the Formbricks code base to make changes to the platform. # Connect AI Agents (MCP) Source: https://formbricks.com/docs/platform/mcp/overview Let AI assistants like Claude and Codex read and manage your Formbricks surveys and feedback records through the Model Context Protocol (MCP), authorized securely with your own login. Formbricks ships a **Model Context Protocol (MCP) server** that lets AI assistants — Claude Code, the Claude apps, Codex, and other MCP-capable clients — work with your workspace: list and read surveys, create link surveys, update or delete them, and read or add Unify Feedback records. The agent goes through the same APIs and permission checks you use in the app, so it always respects your permissions. You connect a client **with your own Formbricks login over OAuth 2.1** — there are no API keys to generate, copy, or paste. You approve the connection once on a consent screen, and you can revoke it at any time. Prefer to use a long-lived token instead (for scripts, CI, or a client that doesn't support OAuth yet)? Formbricks API keys still work as a fallback — see the [MCP server technical handbook](/docs/development/technical-handbook/mcp-server). ## The MCP server URL Point your client at your Formbricks app's `/api/mcp` endpoint: | Instance | MCP server URL | | ---------------- | ------------------------------------------ | | Formbricks Cloud | `https://app.formbricks.com/api/mcp` | | Self-hosted | `https:///api/mcp` | ## How the connection works You don't need to understand the details to connect, but here's what happens under the hood when you add the server to a client: 1. **Discovery** — the client reads the server's OAuth metadata to find where to sign in. 2. **Registration** — the client registers itself automatically using Dynamic Client Registration (DCR). There's no client ID or secret for you to manage. 3. **Sign in & consent** — your browser opens to Formbricks. You log in (if you aren't already) and **approve** the requested access on a consent screen. 4. **Token** — the client receives a short-lived access token it sends on every request, plus a refresh token so it can stay connected without asking you to sign in again. The whole flow uses Authorization Code + PKCE, the standard the [MCP authorization spec](https://modelcontextprotocol.io/specification/draft/basic/authorization) requires. ## What the agent can do The MCP server exposes survey, workflow and feedback-record tools, grouped into scopes you approve on the consent screen. Read tools require the matching `:read` scope; tools that create, update, or delete require the `:write` scope: | Scope | Tools | What it allows | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `surveys:read` | `list_surveys`, `get_survey`, `validate_survey` | Read surveys and validate survey documents | | `surveys:write` | `create_survey`, `patch_survey`, `delete_survey` | Create, update, and delete surveys | | `workflows:read` | `list_workflows`, `get_workflow`, `list_workflow_runs`, `get_workflow_run`, `test_workflow`¹ | Read workflows and their runs | | `workflows:write` | `create_workflow`, `patch_workflow`, `duplicate_workflow`, `delete_workflow`, `enable_workflow`, `disable_workflow`, `archive_workflow`, `unarchive_workflow` | Create, update, and delete workflows | | `feedbackRecords:read` | `list_feedback_datasets`, `list_feedback_records`, `count_feedback_records`, `get_feedback_record`, `search_feedback_records`, `find_similar_feedback_records` | Read, count and search feedback records in a workspace's feedback dataset | | `feedbackRecords:write` | `create_feedback_record`, `create_feedback_records`, `update_feedback_record`, `delete_feedback_record` | Create, correct and delete feedback records | ¹ `test_workflow` is a dry-run (no changes are saved), so it only needs the `workflows:read` scope — but because it evaluates the live workflow definition, it requires **write or manage** permission on the workspace. A read-only member will get a 403 even though the scope matches. Scope groups are independent — a client can be granted feedback-record access without survey access, or the other way around. `list_workspaces` is available to any read scope, since every other tool needs a workspace id. The feedback-record tools work with **Unify Feedback** data and need the Enterprise Edition (`feedbackDirectories`), plus a feedback dataset assigned to the workspace. Without the entitlement or a directory, those tools return an authorization or validation error while the survey tools keep working. `delete_feedback_record` permanently deletes a record — there is no undo and no trash. Grant `feedbackRecords:write` only to clients you want to be able to change or delete feedback, and keep read-only agents on `feedbackRecords:read`. Corrections via `update_feedback_record` are also irreversible — the previous value is kept only in the audit log. Feedback records can be filtered by source, question, submission, end user, chosen option and date range, and `count_feedback_records` answers "how many" without fetching the records themselves — so an agent can report totals without pulling anyone's feedback text into the conversation. The two search tools match by meaning rather than keywords ("checkout is confusing" finds "I couldn't figure out how to pay"), and `find_similar_feedback_records` shows how widely one piece of feedback is echoed by others. They rely on the feedback service having an embedding model configured; on self-hosted installs without one they report that they are unavailable. Newly created records become searchable a short while after they arrive, and records without text are never searchable. Scopes are only a coarse gate. **Access is always bounded by your Formbricks workspace role**: even if a client holds a `:write` scope, a call only succeeds if you have write/manage permission on the target workspace. An agent can never do more than you can. ## Prerequisites * A **Formbricks account** with access to the workspace whose surveys you want to work with. Read-only tasks need `read`; creating or editing surveys needs `write` or `manage`. * The **MCP server URL** for your instance (see the table above). * An **MCP client** that supports remote HTTP servers with OAuth — see the [setup guides](/docs/platform/mcp/setup) for Claude Code, the Claude apps, and Codex. **Self-hosting?** The OAuth provider is built in and enabled automatically — there's nothing extra to turn on. It just needs your instance to be served from a correct, public **HTTPS** base URL (set via `WEBAPP_URL` / `BETTER_AUTH_URL`). The discovery metadata and browser redirects are built from that URL, so an `http://localhost` or misconfigured origin will break the OAuth flow for remote clients. See the [self-hosting configuration](/docs/self-hosting/configuration/environment-variables). ## Next steps Copy-paste guides for Claude Code, the Claude apps (custom connectors), and Codex — plus how to manage and revoke access. For the tool schemas, response formats, discovery endpoints, and the API-key fallback, see the [MCP server technical handbook](/docs/development/technical-handbook/mcp-server). Survey tools map to the [v3 Surveys API](/docs/development/technical-handbook/mcp-server#relationship-to-v3-surveys-api) they wrap; feedback-record tools read and write [Unify Feedback data](/docs/development/technical-handbook/mcp-server#feedback-records-and-the-hub). # Set Up Your MCP Client Source: https://formbricks.com/docs/platform/mcp/setup Connect Claude Code, the Claude apps, or Codex to the Formbricks MCP server over OAuth — plus how to manage and revoke access. These guides connect your MCP client to Formbricks using OAuth (no API keys). They use the Formbricks Cloud URL `https://app.formbricks.com/api/mcp`; if you self-host, swap in your own `https:///api/mcp`. For how the flow works and prerequisites, see [Connect AI agents (MCP)](/docs/platform/mcp/overview). ## Claude Code Claude Code runs on your machine and performs the OAuth handshake itself. ```bash theme={null} claude mcp add --transport http formbricks https://app.formbricks.com/api/mcp ``` In a Claude Code session, run `/mcp`, select **formbricks**, and choose **Authenticate**. Your browser opens to Formbricks; sign in if needed and **approve** the requested access on the consent screen. Claude Code registers itself automatically and stores the token — there's no scope to type in. ```bash theme={null} claude mcp list claude mcp get formbricks ``` Then ask Claude to use it, e.g. *"Use the formbricks MCP server to list my surveys."* To share the server definition with a project (no credentials committed), add it to `.mcp.json` at the repo root and authenticate with `/mcp` on first use: ```json theme={null} { "mcpServers": { "formbricks": { "type": "http", "url": "https://app.formbricks.com/api/mcp" } } } ``` ## Claude apps (custom connectors) Use this for **claude.ai** and **Claude Desktop**, where Formbricks is added as a custom connector. Go to **Settings → Connectors** (**Customize → Connectors**) and click **Add custom connector**. Enter `https://app.formbricks.com/api/mcp` and confirm. Leave the optional OAuth client fields blank — Formbricks registers the connector automatically. Claude opens Formbricks in your browser. Sign in and **approve** the requested access. The connector then shows as connected, and you can enable it per chat from the **+** menu. **Two things to know about the Claude apps:** * **Custom connectors depend on your Claude plan.** They're available on Free, Pro, Max, Team, and Enterprise, but the Free plan is limited to a single connector. On **Team/Enterprise**, an **organization owner** must add the connector under **Organization settings → Connectors** first; members then connect to it individually. * **The connection runs from Anthropic's servers, not your machine.** A custom connector therefore **cannot reach a `localhost` server** — the MCP URL must be a **public HTTPS** address. Formbricks Cloud works out of the box; if you self-host, expose your instance on a public HTTPS domain (a tunnel such as ngrok/cloudflared works for testing). To connect a local dev server, use **Claude Code** instead — it runs locally. ## Codex Include `--oauth-resource` so the access token is audience-bound to the MCP resource: ```bash theme={null} codex mcp add formbricks \ --url https://app.formbricks.com/api/mcp \ --oauth-resource https://app.formbricks.com/api/mcp ``` ```bash theme={null} codex mcp login formbricks ``` Codex opens the browser to Formbricks, registers itself, and you **approve** on the consent screen. For read-only access, restrict the scopes: ```bash theme={null} codex mcp login formbricks --scopes surveys:read,workflows:read,feedbackRecords:read,offline_access ``` Scope groups are independent — request only the ones you need (for example `feedbackRecords:read,offline_access` for feedback data alone). ```bash theme={null} codex mcp list codex mcp get formbricks ``` The equivalent `~/.codex/config.toml` entry: ```toml theme={null} [mcp_servers.formbricks] url = "https://app.formbricks.com/api/mcp" oauth_resource = "https://app.formbricks.com/api/mcp" ``` ## Manage and revoke access Every client you authorize appears under **Settings → Account → Authorized Apps** (`/account/settings/authorized-apps`), showing the client name, granted scopes, and when it was approved. Click **Revoke** to immediately cut off a client's access; it will need to be re-authorized to connect again. Tokens are short-lived by design: an access token lasts **15 minutes** and the client refreshes it silently in the background for up to **30 days** (the refresh window). After that — or after you revoke — the client re-runs the sign-in and consent flow. Approving the write scopes (`surveys:write`, `workflows:write`, `feedbackRecords:write`) is re-confirmed periodically for safety. ## Troubleshooting Custom connectors connect from Anthropic's cloud, which can't reach `localhost` or a private network. Use a **public HTTPS** MCP URL (Formbricks Cloud, or a tunnel to a self-hosted instance), or connect a local server with **Claude Code** instead. The client cached a registration that no longer exists on the server (e.g. the server data was reset). Clear the client's stored credentials for the server and re-authenticate — in Claude Code, remove and re-add the server (`claude mcp remove formbricks` then `claude mcp add …`) and run `/mcp` again. The client requested a scope it isn't registered for. Reconnect from scratch so it re-registers with the scopes the server advertises (`surveys:read`, `surveys:write`, `workflows:read`, `workflows:write`, `feedbackRecords:read`, `feedbackRecords:write`, `offline_access`). Usually a cookie or stale-session issue. Make sure you're signed in to Formbricks in the same browser the client opens, and that third-party-cookie blocking or a private window isn't discarding the session. If it persists, revoke the app under **Settings → Account → Authorized Apps**, clear the client's stored credentials for the server, and reconnect. Self-hosters: an incorrect base URL (see below) also causes repeated redirects. Scopes don't override your workspace role. Creating, editing, or deleting surveys or workflows requires `write` or `manage` permission on that workspace — ask a workspace owner/manager to grant it. `test_workflow` also needs `write`/`manage` even though it's a read-scoped dry-run, because it evaluates the live workflow definition. A `:write` tool likewise 403s if your token was only granted the matching `:read` scope; reconnect and approve write access. OAuth discovery and redirects are built from your configured base URL. Serve Formbricks from a correct, public **HTTPS** origin and set `WEBAPP_URL` (and `BETTER_AUTH_URL`) to it. An `http://localhost` or mismatched origin breaks the flow for remote clients. See the [environment variables](/docs/self-hosting/configuration/environment-variables) reference. *** Need the tool schemas, response shapes, or the API-key fallback? See the [MCP server technical handbook](/docs/development/technical-handbook/mcp-server). # Open-Source Source: https://formbricks.com/docs/platform/open-source Open-source Experience Management. Free & open source. The open core of the Formbricks software is available under the AGPLv3 license. This means you can use, modify, and distribute the software as long as you adhere to the [terms of the license.](https://www.tldrlegal.com/license/gnu-affero-general-public-license-v3-agpl-3-0) The open-source version of Formbricks is free to use, even for commercial purposes. Over time, all survey features will remain part of the free Community Edition. Formbricks also offers a more advanced Enterprise Edition with additional features and support. | Advantage | Open Source Software | Proprietary Software | | --------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------- | | **Data Privacy** | Self-host for maximum control over data | Dependent on third party data processor. | | **Cost** | Often free or significantly lower cost. | Typically, requires a purchase or subscription. | | **Customizability** | Code can be modified to meet specific needs. | Limited customization, restricted to developer's features. | | **Security** | Frequent community reviews identify vulnerabilities quickly. | Security updates depend on vendor's schedule and interest. | | **Flexibility** | Supports a wide range of applications and integrations. | Designed for specific environments and integrations. | | **Community Support** | Large, active communities offer free support and resources. | Paid customer support with limited community help. | | **Innovation** | Fosters rapid innovation through community contributions. | Innovations depend on vendor's vision and development team. | | **Licensing** | Permissive licenses allow broad usage and modification. | Strict licensing with limited redistribution rights. | | **Independence** | Not dependent on a single vendor or developer. | Vendor lock-in can limit future choices. | | **Transparency** | Full visibility into the code base and development. | Closed-source, code is hidden from users. | | **Interoperability** | Supports open standards, ensuring interoperability. | Often requires additional software or plugins for compatibility. | # What is Formbricks? Source: https://formbricks.com/docs/platform/what-is-formbricks Get to know Formbricks and its capabilities. Formbricks is an open-source (AGPLv3) survey platform built to collect feedback from anyone—users, customers, or employees—on any platform. With Formbricks, you can replace many existing survey tools: * **Standalone surveys (share via link):** Replace Google Forms, Typeform or any other link survey tool [with Formbricks Form Builder](https://formbricks.com/open-source-form-builder). Use lots of question types and comprehensive customizations. * **Scalable website surveys:** Even if you have millions of website visitors, Formbricks lets you run well-timed and anonymously targeted [surveys on any public website.](https://formbricks.com/website-survey) * **Highly targeted app surveys:** Identify known users with Formbricks and enrich their profiles with attributes and specific actions. Build cohorts for [highly targeted in-app surveys.](https://formbricks.com/in-app-survey) The survey platform is **mostly free, even for commercial use**. Over time, all survey features will stay part of the free Community Edition. ## An end-to-end XM Suite Formbricks covers the full experience-management loop — from collecting feedback to acting on it — in one platform: Collect feedback with link, website, and in-app surveys built in a powerful, fully customizable builder. Unify feedback from every source into one store, then visualize it with charts and shareable dashboards. Turn responses into action with Workflows that trigger emails and other automations — no code required. ### Formbricks – The Experience Management (XM) Suite To support the development of our open-source platform, we’ve created a premium offering: the **Formbricks XM Suite**. * **What is XM?** Experience Management (XM) involves collecting, analysing, and reporting feedback from stakeholders (like customers or employees) to understand and improve their experience with your organisation. * **Why XM Matters** Helping businesses, governments, and nonprofits understand their users' experiences leads to better services and happier people. Formbricks XM provides the data needed to make decisions that put people first. * **How XM Works in Formbricks** Formbricks XM simplifies experience management. It focuses only on what’s needed to measure specific experiences, with easy-to-use templates, reports, and best practices. We have spent a lot of time and energy building out the open-source survey platform which powers the above. Stick around to see how Formbricks XM Apps will empower everyone to think and work human-centric. [Try Formbricks Cloud ☁️ ](https://app.formbricks.com/) # AI Features Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/ai-features Enable AI-powered helpers like survey translation and AI chart creation. A single organization toggle unlocks AI-assisted survey translation and AI chart creation across the app. Requires `AI_PROVIDER`, `AI_MODEL`, and the matching provider configuration on the instance. ## Kubernetes Helm The Formbricks Helm chart can deploy a bundled Qwen/vLLM runtime for Smart functionality. This path is disabled by default and requires GPU-capable Kubernetes nodes. ```yaml theme={null} llm: enabled: true ``` When `llm.enabled` is true, the chart deploys the vLLM router and Qwen serving engine. By default it also points the Formbricks app at the in-cluster OpenAI-compatible endpoint by injecting the required `AI_*` environment variables. Set `llm.autoConfigureApp=false` if you want the chart to deploy Qwen/vLLM but prefer to configure the app provider manually. ## Docker Compose The Docker stack can deploy the same Qwen/vLLM runtime through an opt-in Compose profile. This path is disabled by default and requires a GPU-capable Docker host with the NVIDIA Container Toolkit installed. ```bash theme={null} COMPOSE_PROFILES=qwen AI_PROVIDER=openai-compatible AI_MODEL=qwen3-14b-awq AI_OPENAI_COMPATIBLE_BASE_URL=http://vllm:8000/v1 AI_OPENAI_COMPATIBLE_PROVIDER_NAME=vllm AI_OPENAI_COMPATIBLE_SUPPORTS_STRUCTURED_OUTPUTS=1 ``` If you use the optional taxonomy service and want it to share the bundled Qwen runtime, start Docker Compose with `COMPOSE_PROFILES=qwen,taxonomy` and point `TAXONOMY_LLM_BASE_URL` at `http://vllm:8000/v1`. ## External Providers Keep `llm.enabled=false` when you use Google Vertex, Azure, AWS Bedrock, or your own OpenAI-compatible runtime. Configure those providers with `deployment.env` in your Helm values or with environment variables in your deployment platform. Read the full guide: [AI Features](/docs/platform/features/ai-features). # Audit Logging Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/audit-logging Enable comprehensive audit logs for your Formbricks instance. Audit logs record **who** did **what**, **when**, **from where**, and **with what outcome** across your Formbricks instance. *** ## Benefits of audit logging * **Compliance readiness** — Many regulatory frameworks such as GDPR and SOC 2 require immutable records of user activity. * **Security investigation support** — Audit logs provide clear visibility into user and system actions, helping teams respond quickly and confidently during security incidents. * **Operational accountability** — Track changes across the system to answer common questions like "*who modified this?*" or "*when was this deleted?*". *** ## Enabling audit logging 1. Set the following environment variables in your deployment (Docker Compose, Kubernetes, etc.): ```bash title=".env" theme={null} # --- Audit logging --- AUDIT_LOG_ENABLED=1 AUDIT_LOG_GET_USER_IP=1 # set to 1 to include user IP address in audit logs, 0 to omit (default: 0) ``` 2. Redeploy your containers. 3. Confirm you can see audit logs in the output of your containers. Audit logs are printed to **stdout** as JSON Lines format, making them easily accessible through your container logs or log aggregation systems. *** ## Understanding the log format Audit logs are **JSON Lines** (one JSON object per line). A typical entry looks like this: ```json theme={null} {"level":"audit","time":1749207302158,"pid":20023,"hostname":"formbricks-node-1","name":"formbricks","actor":{"id":"cm90t4t7l0000vrws5hpo5ta5","type":"api"},"action":"created","target":{"id":"cmbkov4dn0000vrg72i7oznqv","type":"webhook"},"timestamp":"2025-06-06T10:55:02.145Z","organizationId":"cm8zovtbm0001vr3efa4n03ms","status":"success","ipAddress":"unknown","apiUrl":"http://localhost:3000/api/v1/webhooks","changes":{"id":"cmbkov4dn0000vrg72i7oznqv","name":"********","createdAt":"2025-06-06T10:55:02.123Z","updatedAt":"2025-06-06T10:55:02.123Z","url":"https://eoy8o887lmsqmhz.m.pipedream.net","source":"user","workspaceId":"cm8zowv0b0009vr3ec56w2qf3","environmentId":"cm8zowv0b0009vr3ec56w2qf3","triggers":["responseCreated","responseUpdated","responseFinished"],"surveyIds":[]}} ``` Key fields: | Field | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `level` | Log level, always `"audit"` for audit events | | `time` | Unix timestamp in milliseconds | | `pid` | Process ID of the logging instance | | `hostname` | Hostname of the server generating the log | | `name` | Application name, typically `"formbricks"` | | `timestamp` | ISO‑8601 time of the action | | `actor` | User or API key responsible (object with `id` and `type`) | | `action` | Constant verb‑noun string (`survey.updated`, `login.failed`, …) | | `target` | The resource affected (object with `id` and `type`) | | `status` | `success` or `failure` | | `organizationId` | Organization identifier where the action occurred | | `ipAddress` | User IP address, present only if `AUDIT_LOG_GET_USER_IP=1`, otherwise `"unknown"` | | `apiUrl` | (Optional) API endpoint URL if the logs was generated through an API call | | `eventId` | (Optional) Available on error logs. You can use it to refer to the system log with this eventId for more details on the error | | `changes` | (Optional) Only the fields that actually changed (sensitive values redacted) | *** ## Centralized logging and compliance Formbricks audit logs are designed to work with modern centralized logging architectures: * **Stdout delivery**: Logs are written to stdout for immediate collection by log forwarding agents * **Centralized integrity**: Log integrity and immutability are handled by your centralized logging platform (ELK Stack, Splunk, CloudWatch, etc.) * **Platform-level security**: Access controls and tamper detection are provided by your logging infrastructure * **SOC2 compliance**: Most SOC2 auditors accept centralized logging without application-level integrity mechanisms ## Additional details * **Redacted secrets:** Sensitive fields (e‑mails, access tokens, passwords…) are replaced with `"********"` before being written. * **Failure events count:** Both successful *and* failed operations are logged. * **Single source of truth:** The same logs power the `Formbricks` UI and API endpoints. * **Scope limitation:** For now, **only events triggered inside the `Formbricks` application** are audited. This means: * **Embed and Link Surveys** are **not** included in the audit logs. * **Survey responses** created via the client API or client-side SDKs are **not** audited. *** # Contact management & segments Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/contact-management-segments Create and manage contacts and attribute-based segments with Formbricks. Contacts are helpful if you want to assign response to specific contacts or users of your mobile application. Also, if you'd like to do [attribute-based targeting](/docs/surveys/website-app-surveys/attribute-based-targeting) of specific cohorts or segments of your user base you need this feature. # Dashboards & Charts Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/dashboards Visualize Feedback Records with charts and group them onto shareable dashboards. Build Area, Bar, Line, Pie, and Big Number charts on top of any Feedback Dataset, then arrange them on dashboards to share with your team. Read the full guide: [Dashboards & Charts](/docs/unify-feedback/dashboards-charts). # Hide 'Powered by Formbricks' signature Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/hide-powered-by-formbricks Hide our brand signature for a more white-labeled experience. Remove the 'Powered by Formbricks' signature in both Link Surveys and In-product surveys as well as on emails sent out via Formbricks. # OAuth & SSO Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/oidc-sso Configure Single Sign-On with your Formbricks instance. Implement enterprise-grade authentication for your survey platform with [Open ID connect, Azure AD OAuth, Google OAuth, and SAML](/docs/self-hosting/configuration/auth-sso) # SAML SSO Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/saml-sso Configure Single Sign-On with SAML for your Formbricks instance. Implement enterprise-grade authentication for your survey platform using [SAML SSO](/docs/self-hosting/configuration/auth-sso/saml-sso#saml-sso). # Teams & Roles (RBAC) Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/team-access Granularly control which users have access to specific Workspaces and surveys. Each User can be a member of any number of Teams. Each Team can have Read, Write or Manage access to any number of Workspaces. In the Community Edition, each User has Admin rights. In the Enterprise Edition, you can granularly assign Roles and Memberships to users. Here are [all details of user management](/docs/platform/features/user-management) # Unify Feedback Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/unify-feedback Consolidate feedback from surveys, CSVs, and APIs into one normalized store. Unify Feedback brings survey responses, CSV uploads, and API-ingested records into the same normalized model under organization-scoped Feedback Datasets. Workspaces can be granted access to specific datasets. Read the full guide: [Unify Feedback overview](/docs/unify-feedback/overview). # White-label Follow-ups Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/whitelabel-email-follow-ups Add you own logo to emails sent to respondents With the Enterprise Edition, you can customize the [Follow-up Emails](/docs/platform/features/email-customization) sent to respondents. Emails sent to the coworkers of your organisation (e.g. invitations to join a Formbricks team) remain Formbricks branded. # Workflows Source: https://formbricks.com/docs/self-hosting/advanced/enterprise-features/workflows Automate response-driven tasks with triggers, filters, and actions. Workflows automate tasks in response to events in Formbricks. You choose a trigger, narrow down which events qualify with optional filters, and run an action such as sending an email. Read the full guide: [Workflows overview](/docs/workflows/overview). # License Source: https://formbricks.com/docs/self-hosting/advanced/license License for Formbricks The Formbricks core source code is licensed under AGPLv3 and available on GitHub. Additionally, we offer features for bigger organisations & enterprises under a separate, paid Enterprise License. This assures the long-term sustainability of the open-source codebase. All free features are listed [below](#what-features-are-free%3F). Want to get your hands on the Enterprise Edition? [Request a free Enterprise Edition Trial](https://app.formbricks.com/s/trvp8tzy5uvsps9rc9qi9l9w?delivery=onpremise\&source=docs) License to build a fully functioning Proof of Concept. ## Enterprise Edition Additional to the AGPLv3 licensed Formbricks core, the Formbricks repository contains code licensed under our [Enterprise License](https://github.com/formbricks/formbricks/blob/main/apps/web/modules/ee/LICENSE). This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. ## When do I need an Enterprise License? | | Community Edition | Enterprise Edition | | --------------------------------------------------- | ----------------- | ------------------ | | Self-host for commercial purposes | ✅ | ✅ | | Fork codebase, make changes, release under AGPLv3 | ✅ | ✅ | | Fork codebase, make changes, **keep private** | ❌ | ✅ | | Unlimited responses | ✅ | Pay per response | | Unlimited surveys | ✅ | ✅ | | Unlimited users | ✅ | ✅ | | Workspaces | 1 | Pay per workspace | | Use all [free features](#what-features-are-free%3F) | ✅ | ✅ | | Use [paid features](#what-features-are-free%3F) | ❌ | Pay per feature | ## Open Core Licensing ### The AGPL Formbricks Core The Formbricks core application is licensed under the [AGPLv3 Open Source License](https://github.com/formbricks/formbricks/blob/main/LICENSE). The core application is fully functional and includes everything you need to design & run link surveys, website surveys and in-app surveys. You can use the software for free for personal and commercial use. You're also allowed to create and distribute modified versions as long as you document the changes you make incl. date and **publish your complete code under the AGPLv3 license as well.** ### The Enterprise Edition Additional to the AGPL licensed Formbricks core, this repository contains code licensed under an Enterprise license. The [code](https://github.com/formbricks/formbricks/tree/main/apps/web/modules/ee) and [license](https://github.com/formbricks/formbricks/blob/main/apps/web/modules/ee/LICENSE) for the enterprise functionality can be found in the `/apps/web/modules/ee` folder of this repository. This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://app.formbricks.com/s/trvp8tzy5uvsps9rc9qi9l9w?delivery=onpremise\&source=docs) to unlock it. Want to get your hands on the Enterprise Edition? [Request a free Enterprise Edition Trial](https://app.formbricks.com/s/trvp8tzy5uvsps9rc9qi9l9w?delivery=onpremise\&source=docs) License to build a fully functioning Proof of Concept. ## White-Labeling Formbricks and Other Licensing Needs We offer Formbricks white-labeled in some cases. [Please send us an email with a deployment description and we'll get back to you.](mailto:hola@formbricks.com). ## Why charge for Enterprise Features? The Enterprise Edition allows us to fund the development of Formbricks sustainably. It guarantees that the open-source surveying infrastructure we're building will be around for decades to come. ## What features are free? | Feature | Community Edition | Enterprise Edition | | ---------------------------------------------- | ----------------- | ------------------ | | Unlimited surveys | ✅ | ✅ | | Full API Access | ✅ | ✅ | | All SDKs | ✅ | ✅ | | Website & App surveys | ✅ | ✅ | | Link surveys | ✅ | ✅ | | Email embedded surveys | ✅ | ✅ | | Advanced logic | ✅ | ✅ | | Custom styling | ✅ | ✅ | | Custom URL | ✅ | ✅ | | Recall information | ✅ | ✅ | | All question types | ✅ | ✅ | | Multi-media backgrounds | ✅ | ✅ | | Partial responses | ✅ | ✅ | | File upload | ✅ | ✅ | | Hidden fields | ✅ | ✅ | | Single-use links | ✅ | ✅ | | Pin-protected surveys | ✅ | ✅ | | Webhooks | ✅ | ✅ | | Email follow-ups | ✅ | ✅ | | Multi-language surveys | ✅ | ✅ | | Multi-language UI | ✅ | ✅ | | All integrations (Slack, Zapier, Notion, etc.) | ✅ | ✅ | | Domain Split Configuration | ✅ | ✅ | | Cluster Hosting via Formbricks Helm Chart | ✅ | ✅ | | Hide "Powered by Formbricks" | ❌ | ✅ | | Whitelabel email follow-ups | ❌ | ✅ | | Teams & access roles | ❌ | ✅ | | Contact management & segments | ❌ | ✅ | | Quota Management | ❌ | ✅ | | Workflows | ❌ | ✅ | | Unify Feedback Inbox | ❌ | ✅ | | Feedback Datasets | ❌ | ✅ | | Insights Dashboards | ❌ | ✅ | | Audit Logs | ❌ | ✅ | | OIDC SSO (AzureAD, Google, OpenID) | ❌ | ✅ | | SAML SSO | ❌ | ✅ | | Spam protection (ReCaptchaV3) | ❌ | ✅ | | Two-factor authentication | ❌ | ✅ | | Custom 'Workspace' count | ❌ | ✅ | | White-glove onboarding | ❌ | ✅ | | Support SLAs | ❌ | ✅ | Questions? [Send us an email](mailto:johannes@formbricks.com) or [book a call with us.](https://cal.com/johannes/license) # License Activation Source: https://formbricks.com/docs/self-hosting/advanced/license-activation How to activate your Formbricks Enterprise License To unlock Formbricks Enterprise Edition features, you need to activate your Enterprise License Key. Follow these steps to activate your license: Add your Enterprise License Key as an environment variable in your deployment: ```bash theme={null} ENTERPRISE_LICENSE_KEY= ``` * Add your Enterprise License Key after `ENTERPRISE_LICENSE_KEY=` with the key you received from Formbricks. * How you set environment variables depends on your deployment (Docker, Kubernetes, .env file, etc.). After setting the environment variable, **restart your Formbricks instance** to apply the changes. To verify if your license is active, visit **Settings → Organization → Enterprise License** to check the confirmation screen. Your Formbricks instance performs a daily license check to verify your Enterprise License. If you're deploying Formbricks behind a firewall or have network restrictions, you need to whitelist the following: * **Domain**: `ee.formbricks.com` * **URL**: `https://ee.formbricks.com/api/licenses/check` * **Protocol**: HTTPS (Port 443) * **Method**: POST * **Frequency**: Every 24 hours The license check includes a 3-day grace period. If the check fails temporarily, your instance will continue using cached license information for up to 3 days. If you have questions or need assistance with network configuration, please reach out to [hola@formbricks.com](mailto:hola@formbricks.com). # Migration Source: https://formbricks.com/docs/self-hosting/advanced/migration Formbricks Self-hosted version migration ## v5.3 Formbricks v5.3 is a **drop-in upgrade for most self-hosted instances**. There are no breaking API, webhook, or authentication changes, and every database migration is additive and runs automatically on startup. The only self-hosting-relevant changes are a few new optional variables. The main thing worth setting before you deploy is `TRUSTED_PROXY_HOP_COUNT` for your proxy setup; everything else is optional. All new environment variables are optional and defaulted — none of them block startup. You can upgrade to v5.3 without changing any environment variables and everything will run. ### Client IP Resolution Behind a Proxy (recommended) v5.3 resolves the client IP from the `X-Forwarded-For` chain using a configurable number of trusted proxy hops: * `TRUSTED_PROXY_HOP_COUNT` — optional, **defaults to `1`**. Set it to the number of trusted proxies / load balancers in front of Formbricks (e.g. an ingress plus a CDN = `2`). An explicit `0` trusts no proxy at all: the runtime exposes no socket peer address, so the client IP then resolves to a fixed "untrusted" placeholder that rate limiting and response audit logging record in place of a real address. Leave it at `1` (or your real hop count) unless you deliberately want to stop capturing client IPs. Setting `TRUSTED_PROXY_HOP_COUNT` **higher** than your real hop count lets a client spoof its IP by sending a forged `X-Forwarded-For` header. Set it to the exact number of proxies in front of the app — no higher. The default of `1` is correct for a single reverse proxy / ingress. ### Renamed Configuration * **Scheduling variables renamed.** `NEXT_PUBLIC_SURVEY_SCHEDULING_*` → `SURVEY_SCHEDULING_*` (the `NEXT_PUBLIC_` prefix was dropped). This only affects you if you ran a v5.3 release candidate that used the old names; a clean v5.2 → v5.3 upgrade never set them. ### Optional New Configuration None of the following is required to run v5.3. Add them only if you want the associated feature. #### Survey Scheduling The new survey-scheduling feature runs on a fixed daily local time. All three are optional and defaulted: * `SURVEY_SCHEDULING_TIME_ZONE` — a valid IANA time zone. Default `Europe/Berlin`. * `SURVEY_SCHEDULING_LOCAL_HOUR` — default `0`. * `SURVEY_SCHEDULING_LOCAL_MINUTE` — default `0`. #### Password Breach Check * `PASSWORD_HIBP_CHECK_DISABLED` — set to `"1"` to disable the "Have I Been Pwned" breached-password check on sign-up and password reset (`"0"` or unset keeps it on). ### Analytics (Cube) Two Cube-related deltas in v5.3. Neither changes the Cube schema, so there is **no schema refresh to run**: * **`CUBEJS_EXTERNAL_DEFAULT` now defaults to `false`** (in both `docker/docker-compose.yml` and the Helm chart). Cube Store is used only when you explicitly opt into external pre-aggregations — a safe default for most self-hosters. Set it back to `true` only if you run external pre-aggregations. * **The Hub image moves to `0.8.1`.** The Helm chart pins `0.8.1`. Docker Compose instead resolves the Hub image through `${HUB_IMAGE_REF:-:latest}`, so a plain `docker compose pull` fetches whatever `:latest` currently is — set `HUB_IMAGE_REF=:0.8.1` in `docker/.env` before pulling for a deterministic upgrade. ### Upgrade Steps 1. Back up your database. 2. Set `TRUSTED_PROXY_HOP_COUNT` to the number of proxies in front of your instance (default `1`). 3. Update your deployment assets to v5.3 **before** pulling images — `docker compose pull` reads your local Compose file, so fetch the v5.3 `docker-compose.yml` first (Helm users use the v5.3 chart). For a deterministic Hub image on Compose, set `HUB_IMAGE_REF=:0.8.1` in `docker/.env`. 4. Pull and restart (`docker compose pull && docker compose down && docker compose up -d`, or `helm upgrade` with the new chart / tag). Migrations apply automatically on startup. 5. Smoke test: sign in (including 2FA), create and schedule a survey, and open the Feedback Records dashboards / charts to confirm analytics render correctly. ### Rollback The v5.3 migrations are additive and are not read by v5.2, so rolling back the application images / Helm values to v5.2 is safe without touching the database. Restore a database backup only if you specifically need to undo the schema additions — and note that doing so discards any data written after the backup was taken, so perform it within a maintenance window. ## v5.2 Formbricks v5.2 migrates authentication from NextAuth to **Better Auth**. For most self-hosted instances this is a drop-in upgrade, but **if you use SSO (Azure AD / Entra ID, generic OIDC, or SAML) you must re-register the OAuth callback URL at your identity provider before the first v5.2 restart**, or SSO sign-in will fail with a redirect-URI mismatch. All users are signed out once when you upgrade to v5.2. Better Auth takes over session handling and there is no dual-read from the old NextAuth sessions, so the cutover is a one-time forced re-login. Passwords are preserved — existing credential hashes are migrated automatically, so no password reset is needed. ### SSO Callback URL Change (action required for SSO users) Better Auth serves the OAuth2/OIDC callback at a new path: * old (NextAuth): `/api/auth/callback/{provider}` * new (Better Auth): `/api/auth/oauth2/callback/{providerId}` Update the redirect / callback URIs at your identity provider to the new path. Replace `https://your-domain.com` with your instance URL: | Provider | Where to update | New callback URL | | ----------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- | | Azure AD / Entra ID | Azure portal -> App registration -> Authentication -> Redirect URIs | `https://your-domain.com/api/auth/oauth2/callback/azuread` | | Generic OIDC | At your OIDC provider | `https://your-domain.com/api/auth/oauth2/callback/openid` | | SAML (BoxyHQ / Jackson) | The Jackson connection's `redirect_uri` | `https://your-domain.com/api/auth/oauth2/callback/saml` | **Google and GitHub sign-in do not change.** They use Better Auth's built-in social providers, whose callback path (`/api/auth/callback/{provider}`) is unchanged — nothing to do there. For SAML, only the Jackson connection's `redirect_uri` changes. The IdP-side ACS endpoint (`/api/auth/saml/callback`) is unchanged, so you do **not** need to reconfigure your IdP's SAML app. Leave the old `/api/auth/callback/*` redirect URIs registered through the transition. Keeping them in place is harmless and makes a rollback to v5.1 clean. ### Authentication Environment Variables Better Auth introduces two optional variables. **Neither is required** for a standard upgrade — both fall back to your existing NextAuth configuration: * `BETTER_AUTH_SECRET` — cookie/session signing secret. Falls back to `NEXTAUTH_SECRET` when unset. If you set it explicitly it must be at least 32 characters. Keeping the `NEXTAUTH_SECRET` fallback is recommended so the value the forward-auth proxy already verifies with stays consistent. * `BETTER_AUTH_URL` — the auth base URL. Falls back to `NEXTAUTH_URL` (and then `WEBAPP_URL`) when unset. If your instance already sets `NEXTAUTH_SECRET` and `NEXTAUTH_URL` (all v5.1 instances do), you can upgrade without adding any new auth variables. **Removed:** `DISABLE_ACCOUNT_DELETION_SSO_CONFIRMATION` no longer exists. Remove it from your environment if you set it; it now has no effect. ### Optional New Configuration None of the following is required to run v5.2. Add them only if you want the associated feature. #### Hub Enrichment (sentiment, emotion, translation) Hub `>= 0.8.0` can classify sentiment and emotion and translate feedback. These are **off** unless you set a provider and model, and they run in both the Hub API and the hub-worker, so set the variables on **both** services. Bump your Hub image to `>= 0.8.0` (via `HUB_IMAGE_REF`) to pick up these features. * `SENTIMENT_PROVIDER`, `SENTIMENT_MODEL`, `SENTIMENT_PROVIDER_API_KEY` * `EMOTIONS_PROVIDER`, `EMOTIONS_MODEL`, `EMOTIONS_PROVIDER_API_KEY` * `TRANSLATION_PROVIDER`, `TRANSLATION_MODEL`, `TRANSLATION_PROVIDER_API_KEY`, `TRANSLATION_DEFAULT_LANGUAGE` Supported providers: `openai`, `google` (AI Studio, needs `*_PROVIDER_API_KEY`), and `google-gemini` (Vertex, needs `*_GOOGLE_CLOUD_PROJECT` / `*_LOCATION` plus Application Default Credentials). A provider set **without** its required credentials crash-loops both Hub containers on startup — it does not degrade gracefully. Set the credentials, or leave the provider unset. #### AI Taxonomy (beta) An optional standalone taxonomy service, enabled in Docker Compose with `COMPOSE_PROFILES=taxonomy`. It shares an internal token with Hub: * `TAXONOMY_SERVICE_URL` (the internal URL to the taxonomy service) * `HUB_INTERNAL_API_TOKEN`, `TAXONOMY_SERVICE_TOKEN` (strong random secrets) * `TAXONOMY_LLM_PROVIDER` / `TAXONOMY_LLM_MODEL` / `TAXONOMY_LLM_BASE_URL` / `TAXONOMY_LLM_API_KEY` (`openai-compatible` by default; `bedrock` and `vertex-gemini` also supported) * `TAXONOMY_IMAGE_REF` to pin a released image (e.g. `:v0.1.0`) Use `COMPOSE_PROFILES=qwen,taxonomy` to back it with the bundled Qwen/vLLM service. #### Bundled Qwen / vLLM Runtime Docker Compose can now run a bundled OpenAI-compatible LLM for self-hosted AI features with `COMPOSE_PROFILES=qwen`. Tunable via `QWEN_VLLM_IMAGE`, `QWEN_MODEL_ID`, `QWEN_SERVED_MODEL_NAME`, `QWEN_MAX_MODEL_LEN`, `QWEN_MAX_NUM_SEQS`, `QWEN_GPU_MEMORY_UTILIZATION`, `QWEN_VLLM_HOST`, and `QWEN_VLLM_PORT`. #### OpenTelemetry Log Export * `OTEL_LOGS_ENABLED=1` — export Pino application logs via OTLP (off by default; also needs `OTEL_EXPORTER_OTLP_ENDPOINT`). ### Upgrade Steps v5.2 requires no manual database migration step beyond the standard automatic Prisma migrations on startup (the credential-account backfill runs automatically). The Helm chart is unchanged from v5.1. 1. **If you use SSO**, add the new `/api/auth/oauth2/callback/{providerId}` redirect URIs at your IdP (see the table above), keeping the old ones in place. 2. Back up your database. 3. Pull the v5.2 images and restart (`docker compose pull && docker compose down && docker compose up -d`, or `helm upgrade` with the new tag). 4. After startup, sign in again (the one-time forced re-login) and verify each configured SSO provider completes sign-in. ### Rollback Because the old `/api/auth/callback/*` redirect URIs are left in place, rolling back to v5.1 is clean: revert your image tags / Helm values, and the previous NextAuth callback path works again. Restore the database backup only if you need to undo schema changes. ## v5 Formbricks v5 changes the self-hosted runtime contract. If you are upgrading an existing Formbricks 4.x deployment, review this section before starting the new version. ### What Changes In v5 * **Formbricks Hub is now mandatory** for self-hosted Formbricks v5 deployments. * **Cube is now part of the baseline stack** alongside Hub. Docker, one-click, and Helm all bundle Cube by default; `CUBEJS_API_SECRET` is required. Operators can disable the bundled Cube deployment in Helm to use an external cluster instead. * **Edge rate limiting is now required** for specific public and API-key routes. Those routes are no longer throttled inside the application server. * **AI features are configured at the instance level** via `AI_*` environment variables. Formbricks v5 removes application-level rate limiting for several routes that are now expected to be protected by Envoy Gateway or an equivalent edge rate limiter. If your self-hosted instance does not already have equivalent edge protection, add it before exposing the v5 stack. ### Before You Upgrade Before you restart your instance on Formbricks v5: * back up your database * identify your current deployment type: one-click, manual Docker Compose, or Kubernetes/Helm * confirm Redis/Valkey and your file storage setup are already healthy from your v4 baseline * identify whether file uploads use external S3-compatible storage or a legacy bundled MinIO service * budget approximately \~500 MB additional RAM headroom for the bundled Cube container (dashboards and analysis are part of the baseline now) * decide whether this instance needs optional AI features * verify whether you already run Envoy Gateway or another equivalent edge rate limiter for the covered routes ### Required Config And Infrastructure Changes #### Formbricks Hub Formbricks v5 expects Hub to be part of the self-hosted stack. * `HUB_API_KEY` is required * `HUB_API_URL` must point to the Hub service the Formbricks app can reach * `HUB_DATABASE_URL` is optional; when unset, Hub can share the same PostgreSQL database as Formbricks * bundled Docker and Helm assets run Hub database migrations automatically during startup or upgrade, and the migration steps are idempotent Hub-specific source code and standalone deployment assets live in the [Formbricks Hub repository](https://github.com/formbricks/hub). For a normal Formbricks v5 upgrade, use that repository as reference material only; the canonical migration steps stay in these Formbricks docs. #### Edge Rate Limiting Formbricks v5 splits rate limiting across two layers: * Envoy Gateway, or an equivalent edge rate limiter, for covered public and API-key routes * the application server for the remaining session-authenticated routes, server actions, and uncovered APIs Keep Redis/Valkey enabled for the remaining application-enforced limits. For the covered route groups and exact thresholds, use the [rate-limiting guide](/docs/self-hosting/advanced/rate-limiting) as the source of truth. #### AI Features AI features are optional and only need instance-level configuration if you want to enable the related enterprise functionality. * `AI_PROVIDER` and `AI_MODEL` are the base settings * `AI_PROVIDER=aws` requires `AI_AWS_REGION`, `AI_AWS_ACCESS_KEY_ID`, and `AI_AWS_SECRET_ACCESS_KEY` * `AI_PROVIDER=google` requires `AI_GOOGLE_CLOUD_PROJECT` and `AI_GOOGLE_CLOUD_LOCATION`; credentials are optional overrides when Application Default Credentials are unavailable * `AI_PROVIDER=azure` requires `AI_AZURE_API_KEY` and either `AI_AZURE_BASE_URL` or `AI_AZURE_RESOURCE_NAME` * `AI_PROVIDER=openai-compatible` requires `AI_OPENAI_COMPATIBLE_BASE_URL`; the LLM GA v1 self-hosted path is Qwen served by vLLM * Helm operators can set `llm.enabled: true` to deploy the bundled Qwen/vLLM runtime * Docker Compose operators can set `COMPOSE_PROFILES=qwen` to deploy the bundled Qwen/vLLM runtime * Keep the bundled runtime disabled when you configure Google Vertex, Azure, AWS Bedrock, or an external OpenAI-compatible endpoint manually #### Cube Cube is part of the baseline Formbricks v5 stack. * the Docker, one-click, and Helm deployments all bundle the `cube` service by default * the Formbricks app requires `CUBEJS_API_URL` and `CUBEJS_API_SECRET`; the install/dev-setup scripts generate the secret automatically for new installs * Helm operators who want to run an external Cube cluster can set `cube.enabled: false` and provide their own endpoint via `deployment.env.CUBEJS_API_URL` * if you run Cube yourself, you may also need to override `CUBEJS_DB_*` values for the Cube service ### Upgrade Steps By Deployment Type **1. Back up your database** From the `formbricks` directory created by the installer: ```bash theme={null} cd formbricks docker compose exec postgres pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v5_$(date +%Y%m%d_%H%M%S).dump ``` If your PostgreSQL service name differs, run docker compose ps first and adjust the command. From the directory that contains your `docker-compose.yml`: ```bash theme={null} docker compose exec postgres pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v5_$(date +%Y%m%d_%H%M%S).dump ``` If your service is not named postgres, use docker compose ps to find the correct name. If you are using the in-cluster PostgreSQL released by the Helm chart: ```bash theme={null} kubectl exec -n formbricks formbricks-postgresql-0 -- pg_dump -Fc -U formbricks -d formbricks > formbricks_pre_v5_$(date +%Y%m%d_%H%M%S).dump ``` If you use a managed PostgreSQL service, create a provider snapshot or run `pg_dump` directly against the external host before continuing. **2. Update your deployment config and restart on v5** The one-click `./formbricks.sh update` command only pulls new images. It does **not** rewrite your existing `formbricks/docker-compose.yml`, so you must merge the v5 stack changes before the first v5 restart. ```bash theme={null} curl -fsSL -o formbricks/docker-compose.v5.yml https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/docker-compose.yml ``` Then compare `formbricks/docker-compose.v5.yml` with your existing `formbricks/docker-compose.yml` and merge the v5 additions: * add a non-empty `HUB_API_KEY` and reuse the same value wherever your deployment resolves Hub auth * keep `HUB_API_URL` at `http://hub:8080` unless Hub runs elsewhere * include the bundled `formbricks-migrate`, `hub-migrate`, and `hub` services * sync `formbricks/cube/cube.js` and `formbricks/cube/schema/FeedbackRecords.js` from the current release and ensure `formbricks/.env` contains `CUBEJS_API_SECRET` (Cube is part of the baseline stack in v5) * if your older setup still uses bundled MinIO for uploads, review that storage path separately before the first v5 restart; newer self-hosting updates move the bundled object-storage path to RustFS, while external S3-compatible storage keeps the same `S3_*` app contract * add any `AI_*` variables you need * if you prefer to run an external Cube instance, point `CUBEJS_API_URL` at it and provide the matching `CUBEJS_API_SECRET`; otherwise the bundled `cube` service runs against the local Postgres After the compose file is updated and your edge rate limiter is in place: ```bash theme={null} ./formbricks.sh update ``` Pull the current production compose file and merge the v5 changes into your existing deployment config before only updating images: ```bash theme={null} curl -fsSL -o docker-compose.v5.yml https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/docker-compose.yml ``` At minimum, confirm: * `HUB_API_KEY` is configured and the same value is available wherever your deployment resolves Hub auth * `HUB_API_URL` points to the Hub service the app can reach * the compose stack includes `formbricks-migrate`, `hub-migrate`, and `hub` * the v5 stack also includes `cube`, `cube/cube.js`, and `cube/schema/FeedbackRecords.js`, with `CUBEJS_API_SECRET` available through your `.env` or shell environment * if your legacy Compose file still includes bundled MinIO for uploads, treat that as a separate storage review when comparing files; newer bundled storage guidance uses RustFS, while external S3-compatible storage keeps the same `S3_*` app contract * any `AI_*` variables you need are set * if you prefer to run an external Cube instance, point `CUBEJS_API_URL` at it and supply the matching `CUBEJS_API_SECRET`; otherwise the bundled `cube` service runs against the local Postgres Then restart the stack: ```bash theme={null} docker compose pull docker compose down docker compose up -d ``` The v5 Docker Compose stack bundles Hub and Cube. Keep the bundled `cube/` config files in sync with `docker-compose.yml` when you update this path. Upgrade using the current Formbricks chart: ```bash theme={null} helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \ -n formbricks \ -f values.yaml ``` Before running the upgrade, confirm these v5 expectations in your values: * keep `hub.enabled=true`; Hub is mandatory in v5 * if you want the bundled Envoy path, configure `envoy.enabled=true` and then choose: * `envoy.controller.enabled=true` for the bundled controller mode * `envoy.controller.enabled=false` when the cluster already has a compatible Envoy Gateway controller * if you use bundled Envoy rate limiting, enable a dedicated backend with `envoyRedis.enabled=true` * if you already have an equivalent edge rate limiter outside the chart, keep that protection in place * `CUBEJS_API_SECRET` is provided (Cube is bundled by default at `cube.enabled: true`; set to `false` and point `CUBEJS_API_URL` at your own endpoint if you prefer an external Cube cluster) ### Post-Upgrade Verification After the upgrade: * confirm the Formbricks app starts and `GET /health` returns successfully * confirm the Hub service is healthy and reachable from the Formbricks app * verify any Hub-backed connector or feedback flows you use * verify covered routes are rate-limited at the edge layer * verify AI features only if you configured the required `AI_*` variables * verify dashboards and analysis flows against the bundled (or external) Cube endpoint ### Troubleshooting And Rollback Common upgrade issues: * **Missing `HUB_API_KEY`**: Formbricks cannot authenticate to Hub, and Hub itself may fail to start * **Bad `HUB_API_URL`**: the app starts, but Hub-backed features fail because the service cannot be reached * **No edge rate limiter in front of covered routes**: the v5 deployment runs, but those routes are no longer protected by the legacy in-app limiter * **Missing AI provider configuration**: AI features remain unavailable until `AI_PROVIDER`, `AI_MODEL`, and the matching provider settings are set correctly * **Missing `CUBEJS_API_SECRET`** (or unreachable Cube endpoint): the Formbricks app fails env validation at boot, or — if env vars are present but Cube is unreachable — dashboards and analysis queries fail while the rest of the app stays healthy * **Cube healthcheck fails with `wget: not found`**: older v5 Docker Compose files used `wget` for the bundled Cube healthcheck, but `cubejs/cube:v1.6.6` does not include it. Sync the Cube healthcheck from the current Docker Compose file so it checks `/readyz` with Node instead. If you need to roll back: * restore the pre-upgrade database backup if schema or data changes require it * revert your image tags or Helm release values to the last known-good Formbricks 4.x version * revert compose or Helm configuration changes introduced for the v5 rollout **Workspaces and Environment IDs** Environment IDs were deprecated to simplify SDK setup around a single Workspace ID while keeping existing integrations backward compatible during migration. **Website & App Surveys SDK: Migrate to Workspace ID** Formbricks v5 also includes the Environment ID to Workspace ID transition for SDK setup. If you still rely on a legacy Environment ID, read the [Migrate to Workspace ID guide](/docs/surveys/website-app-surveys/workspace-id-migration). ## v4.7 Formbricks v4.7 introduces **typed contact attributes** with native `number` and `date` data types. This enables comparison-based segment filters (e.g. "signup date before 2025-01-01") that were previously not possible with string-only attribute values. ### What Happens Automatically When Formbricks v4.7 starts for the first time, the data migration will: 1. Analyze all existing contact attribute keys and infer their data types (`text`, `number`, or `date`) based on the stored values 2. Update the `ContactAttributeKey` table with the detected `dataType` for each key 3. **If your instance has fewer than 1,000,000 contact attribute rows**: backfill the new `valueNumber` and `valueDate` columns inline. No manual action is needed. 4. **If your instance has 1,000,000 or more contact attribute rows**: the value backfill is skipped to avoid hitting the migration timeout. You will need to run a standalone backfill script after the upgrade. Most self-hosted instances have far fewer than 1,000,000 contact attribute rows (a typical setup with 100K contacts and 5-10 attributes each lands around 500K-1M rows). If you are below the threshold, the migration handles everything automatically and you can skip the manual backfill step below. ### Steps to Migrate **1. Backup your Database** Before running these steps, navigate to the `formbricks` directory where your `docker-compose.yml` file is located. ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v4.7_$(date +%Y%m%d_%H%M%S).dump ``` If you run into "**No such container**", use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you are using the **in-cluster PostgreSQL** deployed by the Helm chart: ```bash theme={null} kubectl exec -n formbricks formbricks-postgresql-0 -- pg_dump -Fc -U formbricks -d formbricks > formbricks_pre_v4.7_$(date +%Y%m%d_%H%M%S).dump ``` If your PostgreSQL pod has a different name, run `kubectl get pods -n formbricks` to find it. If you are using a **managed PostgreSQL** service (e.g. AWS RDS, Cloud SQL), use your provider's backup/snapshot feature or run `pg_dump` directly against the external host. **2. Upgrade to Formbricks v4.7** ```bash theme={null} # Pull the latest version docker compose pull # Stop the current instance docker compose down # Start with Formbricks v4.7 docker compose up -d ``` ```bash theme={null} helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \ -n formbricks \ --set deployment.image.tag=v4.7.0 ``` The Helm chart includes a migration Job that automatically runs Prisma schema migrations as a PreSync hook before the new pods start. No manual migration step is needed. **3. Check the Migration Logs** After Formbricks starts, check the logs to see whether the value backfill was completed or skipped: ```bash theme={null} docker compose logs formbricks | grep -i "backfill" ``` ```bash theme={null} # Check the application pod logs kubectl logs -n formbricks -l app.kubernetes.io/name=formbricks --tail=200 | grep -i "backfill" ``` If the Helm migration Job ran, you can also inspect its logs: ```bash theme={null} kubectl logs -n formbricks job/formbricks-migration ``` If you see a message like `Skipping value backfill (X rows >= 1000000 threshold)`, proceed to step 4. Otherwise, the migration is complete and no further action is needed. **4. Run the Backfill Script (large datasets only)** If the migration skipped the value backfill, run the standalone backfill script inside the running Formbricks container: ```bash theme={null} docker exec formbricks node packages/database/dist/scripts/backfill-attribute-values.js ``` Replace `formbricks` with your actual container name if it differs. Use `docker ps` to find it. ```bash theme={null} kubectl exec -n formbricks deploy/formbricks -- node packages/database/dist/scripts/backfill-attribute-values.js ``` If your Formbricks deployment has a different name, run `kubectl get deploy -n formbricks` to find it. The script will output progress as it runs: ``` ======================================== Attribute Value Backfill Script ======================================== Fetching number-type attribute keys... Found 12 number-type keys. Backfilling valueNumber... Number backfill progress: 10/12 keys (48230 rows updated) Number backfill progress: 12/12 keys (52104 rows updated) Fetching date-type attribute keys... Found 5 date-type keys. Backfilling valueDate... Date backfill progress: 5/5 keys (31200 rows updated) ======================================== Backfill Complete! ======================================== valueNumber rows updated: 52104 valueDate rows updated: 31200 Duration: 42.3s ======================================== ``` Key characteristics of the backfill script: * **Safe to run while Formbricks is live** -- it does not lock the entire table or wrap work in a long transaction * **Idempotent** -- it only updates rows where the typed columns are still `NULL`, so you can safely run it multiple times * **Resumable** -- each batch commits independently, so if the process is interrupted you can re-run it and it picks up where it left off * **No timeout risk** -- unlike the migration, this script runs outside the migration transaction and has no time limit **5. Verify the Upgrade** * Access your Formbricks instance at the same URL as before * If you use contact segments with number or date filters, verify they return the expected results * Check that existing surveys and response data are intact *** ## v4.0 **Important: Migration Required** Formbricks 4 introduces additional requirements for self-hosting setups and makes a dedicated Redis cache as well as S3-compatible file storage mandatory. Formbricks 4.0 is a **major milestone** that sets up the technical foundation for future iterations and feature improvements. This release focuses on modernizing core infrastructure components to improve reliability, scalability, and enable advanced features going forward. ### What's New in Formbricks 4.0 **🚀 New Enterprise Features:** * **Quotas Management**: Advanced quota controls for enterprise users **🏗️ Technical Foundation Improvements:** * **Enhanced File Storage**: Improved file handling with better performance and reliability * **Improved Caching**: New caching functionality improving speed, extensibility and reliability * **Database Optimization**: Removal of unused database tables and fields for better performance * **Future-Ready Architecture**: Standardized infrastructure components for upcoming features ### What This Means for Your Self-Hosting Setup These improvements in Formbricks 4.0 also make some infrastructure requirements mandatory going forward: * **Redis** for caching * **RustFS or S3-compatible storage** for file uploads These services are already included in the updated one-click setup for self-hosters, but existing users need to upgrade their setup. More information on this below. ### Why We Made These Changes We know this represents more moving parts in your infrastructure and might even introduce more complexity in hosting Formbricks, and we don't take this decision lightly. As Formbricks grows into a comprehensive Survey and Experience Management platform, we've reached a point where the simple, single-service approach was holding back our ability to deliver the reliable, feature-rich product our users demand and deserve. By moving to dedicated, professional-grade services for these critical functions, we're building the foundation needed to deliver: * **Enterprise-grade reliability** with proper redundancy and backup capabilities * **Advanced features** that require sophisticated caching and file processing * **Better performance** through optimized, dedicated services * **Future scalability** to support larger deployments and more complex use cases without the need to maintain two different approaches We believe this is the only path forward to build the comprehensive Survey and Experience Management software we're aiming for. ### Migration Steps for v4.0 Additional migration steps are needed if you are using a self-hosted Formbricks setup that uses either local file storage (not S3-compatible file storage) or doesn't already use a Redis cache. ### Legacy one-click v4.0 upgrade For historical v4.0 upgrades, our original one-click migration script is still available: ```bash theme={null} # Download the latest script curl -fsSL -o migrate-to-v4.sh \ https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/migrate-to-v4.sh # Make it executable chmod +x migrate-to-v4.sh # Launch the guided migration ./migrate-to-v4.sh ``` This script guides you through the steps for the infrastructure migration and does the following: * Adds a Redis service to your setup and configures it * Adds a bundled MinIO service to your setup, configures it, and migrates local files to it * Pulls the latest Formbricks image and updates your instance ### Manual Setup If you use a different setup to host your Formbricks instance, you need to make sure to make the necessary adjustments to run Formbricks 4.0. #### Redis Formbricks 4.0 requires a Redis instance to work properly. Please add a Redis instance to your Docker setup, your K8s infrastructure, or however you are hosting Formbricks at the moment. Formbricks works with the latest versions of Redis as well as Valkey. You need to configure the `REDIS_URL` environment variable and point it to your Redis instance. #### S3-compatible storage To use file storage (e.g., file upload questions, image choice questions, custom survey backgrounds, etc.), you need to have S3-compatible file storage set up and connected to Formbricks. Formbricks supports multiple storage providers (among many other S3-compatible storages): * AWS S3 * Digital Ocean Spaces * Hetzner Object Storage * Custom RustFS server Please make sure to set up a storage bucket with one of these solutions and then link it to Formbricks using the following environment variables: ``` S3_ACCESS_KEY: your-access-key S3_SECRET_KEY: your-secret-key S3_REGION: us-east-1 S3_BUCKET_NAME: formbricks-uploads S3_ENDPOINT_URL: https://files.yourdomain.com # not needed for AWS S3 S3_FORCE_PATH_STYLE: 1 # set this only for custom endpoints like RustFS, MinIO, or LocalStack; omit for AWS S3 ``` #### Upgrade Process **1. Backup your Database** **Critical Step**: Create a complete database backup before proceeding. Formbricks 4.0 will automatically remove unused database tables and fields during startup. ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v4.0_$(date +%Y%m%d_%H%M%S).dump ``` If you run into "**No such container**", use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. **2. Upgrade to Formbricks 4.0** Pull the latest Docker images and restart the setup (example for docker-compose): ```bash theme={null} # Pull the latest version docker compose pull # Stop the current instance docker compose down # Start with Formbricks 4.0 docker compose up -d ``` **3. Automatic Database Migration** When you start Formbricks 4.0 for the first time, it will **automatically**: * Detect and apply required database schema updates * Remove unused database tables and fields * Optimize the database structure for better performance No manual intervention is required for the database migration. **4. Verify Your Upgrade** * Access your Formbricks instance at the same URL as before * Test file uploads to ensure S3 storage integration works correctly. Check the [File Upload Troubleshooting](/docs/self-hosting/configuration/file-uploads#troubleshooting) section if you face any issues. * Verify that existing surveys and data are intact * Check that previously uploaded files are accessible ### v3.3 With Formbricks 3.0, we introduced an automatic data migration system. If you're using a version older than **v2.7**, you must upgrade step-by-step through earlier versions (e.g. **2.4 → 2.5 → 2.6 → 2.7**). Once you reach **v2.7**, you can upgrade directly to **any v3.x or future release** without needing intermediate migrations. ### Steps to Migrate This guide is for users who are **self-hosting Formbricks** using the **one-click setup**. If you have a different setup, you may need to adjust the commands accordingly. Before running these steps, **navigate to the `formbricks` directory** where your `docker-compose.yml` file is located. 1. **Backup your Database**: Backing up your database is crucial before upgrading. Use the following command to create a backup: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v3.3_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “**No such container**”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. 1. Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` 1. Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` 1. Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` When you start the latest version of **Formbricks**, it will **automatically detect and apply any required data migrations** during startup. You don’t need to run any manual migration steps or download separate migration images. * Access your updated instance Once the containers are running, simply go to the **same URL** as before to access your updated **Formbricks** instance. That’s it! This new process ensures your **Formbricks** setup stays up to date with the latest schema changes as soon as you run the updated container. ### v3.2 With Formbricks 3.0, we introduced an automatic data migration system. If you're using a version older than **v2.7**, you must upgrade step-by-step through earlier versions (e.g. **2.4 → 2.5 → 2.6 → 2.7**). Once you reach **v2.7**, you can upgrade directly to **any v3.x or future release** without needing intermediate migrations. ### Steps to Migrate This guide is for users who are **self-hosting Formbricks** using the **one-click setup**. If you have a different setup, you may need to adjust the commands accordingly. Before running these steps, **navigate to the `formbricks` directory** where your `docker-compose.yml` file is located. 1. **Backup your Database**: Backing up your database is crucial before upgrading. Use the following command to create a backup: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v3.2_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “**No such container**”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. 1. Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` 1. Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` 1. Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` When you start the latest version of **Formbricks**, it will **automatically detect and apply any required data migrations** during startup. You don’t need to run any manual migration steps or download separate migration images. * Access your updated instance Once the containers are running, simply go to the **same URL** as before to access your updated **Formbricks** instance. That’s it! This new process ensures your **Formbricks** setup stays up to date with the latest schema changes as soon as you run the updated container. ### v3.1 With Formbricks 3.0, we introduced an automatic data migration system. If you're using a version older than **v2.7**, you must upgrade step-by-step through earlier versions (e.g. **2.4 → 2.5 → 2.6 → 2.7**). Once you reach **v2.7**, you can upgrade directly to **any v3.x or future release** without needing intermediate migrations. ### Steps to Migrate This guide is for users who are **self-hosting Formbricks** using the **one-click setup**. If you have a different setup, you may need to adjust the commands accordingly. Before running these steps, **navigate to the `formbricks` directory** where your `docker-compose.yml` file is located. 1. **Backup your Database**: Backing up your database is crucial before upgrading. Use the following command to create a backup: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v3.1_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “**No such container**”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. 1. Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` 1. Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` 1. Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` When you start the latest version of **Formbricks**, it will **automatically detect and apply any required data migrations** during startup. You don’t need to run any manual migration steps or download separate migration images. * Access your updated instance Once the containers are running, simply go to the **same URL** as before to access your updated **Formbricks** instance. That’s it! This new process ensures your **Formbricks** setup stays up to date with the latest schema changes as soon as you run the updated container. ## v3.0 **Important: Do Not Upgrade to 3.0 If You Need SSO, User Identification, or Cluster Support** With **Formbricks 3.0**, we're making changes to ensure long-term sustainability while still supporting open source. While the **Community Edition** has gained [new features](https://formbricks.com/blog/formbricks-3-0), some [advanced capabilities](https://formbricks.com/docs/self-hosting/license) are now part of the **Enterprise Edition**. ⚠️ **No Downgrade Option:** If you upgrade to **3.0** and run the data migration, **you cannot revert to 2.7.2**. If you rely on **SSO, user identification, or cluster support**, either **stay on version 2.7.x** or reach out to us on [**GitHub Discussions**](https://github.com/formbricks/formbricks/discussions) **for a custom quote**. This major release brings a better approach to **data migrations**. * If you're using **Formbricks v2.7 or later**, you can upgrade **directly** to **v3.x** and future versions. * If you're on a version **older than v2.7**, you **must upgrade step-by-step** (e.g. **2.4 → 2.5 → 2.6 → 2.7**) before moving to v3.x. ### Steps to Migrate This guide is for users **self-hosting** Formbricks with the **one-click setup**. If you're using a different setup, you might adjust the commands. * Navigate to the Formbricks Directory Before running any steps, go to the directory where your `docker-compose.yml` file is located. * Backup Your Database Backing up your database is **crucial** before upgrading. Use the following command to create a backup: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v3.0_$(date +%Y%m%d_%H%M%S).dump ``` **Note:** If you see a “No such container” error, run `docker ps` to find your actual container name (e.g. `formbricks_postgres_1`). Tip: If you want to store the backup as a `.sql` file, remove the `-Fc` (custom format) option. If you need to restore it later, use `psql` with an empty Formbricks database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` When you start the latest version of Formbricks, it will automatically detect and run any necessary data migrations during startup. There is no need to run any manual migration steps or pull any separate migration images. * Access your updated instance That’s it! Once the containers are up and running, simply navigate to the same URL as before to access your fully migrated Formbricks instance. ## v2.7 AI Features & Database Update: This release lays the groundwork for upcoming AI features, now in private beta. Formbricks requires the `pgvector` extension in PostgreSQL. One-Click Setup Users: Use the `pgvector/pgvector:pg15` image instead of `postgres:15-alpine` to ensure compatibility. Formbricks v2.7 includes all the features and improvements developed by the community during Hacktoberfest 2024. Additionally, this release introduces an advanced team-based access control system, which is available in the Formbricks Enterprise Edition. ### Additional Updates If you previously used organisation-based access control (an enterprise feature) and the `DEFAULT_ORGANIZATION_ROLE` environment variable, update its value to one of these roles: `owner`, `manager`, or `member`. Read more about the new roles in the [documentation](/docs/platform/features/user-management). ### Steps to Migrate This guide is for users who are self-hosting Formbricks using our one-click setup. If you are using a different setup, you might adjust the commands accordingly. To run all these steps, please navigate to the Formbricks folder where your `docker-compose.yml` file is located. * **Backup your Database:** This is a crucial step. Please make sure to backup your database before proceeding with the upgrade. You can use the following command to backup your database. ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.7_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “No such container”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you want to store the backup as an \*.sql file, remove the `-Fc` option. To restore, use `psql` with an empty Formbricks database. * If you use an older `docker-compose.yml` file from the one-click setup, modify it to use the `pgvector/pgvector:pg15` image instead of `postgres:15-alpine`: ```yaml theme={null} services: postgres: image: pgvector/pgvector:pg15 volumes: - postgres:/var/lib/postgresql/data environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres ports: - 5432:5432 ``` * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance and remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find the Docker network name for your Postgres database, run `docker network ls`. ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:latest && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.7" \ ghcr.io/formbricks/data-migrations:v2.7.0 ``` The command above will migrate your data to the latest schema. This step is important to update your existing data to the new structure. The changes will only be made if the script runs successfully. You can run the script multiple times safely. * That's it! Once the migration is finished, you can **access your Formbricks instance** at the same URL as before. ## v2.6 Formbricks v2.6 introduces advanced logic jumps for surveys, allowing more complex branching logic, including variables, conditions, and more. This release also includes numerous bug fixes, significant performance improvements for website and app surveys, and enhanced stability. This release includes the final step of deprecating server-side action tracking (previously used for segment filtering by performed actions). The migrations will delete all tracked actions from the database. If you still need these action records, ensure you export them before upgrading. ### Steps to Migrate This guide is for users who are self-hosting Formbricks using our one-click setup. If you're using a different setup, you might need to adjust the commands accordingly. To run these steps, navigate to the folder where your `docker-compose.yml` file is located. * **Backup your Database:** This is an important step. Make sure to back up your database before proceeding with the upgrade. You can use this command to back up your database: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.6_$(date +%Y%m%d_%H%M%S).dump ``` If you get a "No such container" error, run `docker ps` to find your container name, for example, `formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario, you will need to use `psql` then with an empty `formbricks` database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find the Docker network name for your Postgres database, run the command: ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:latest && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.6" \ ghcr.io/formbricks/data-migrations:v2.6.0 ``` The command above will migrate your data to the latest schema. This is an important step to update your existing data to the new structure. Changes will only be made if the script runs successfully. You can run the script multiple times without issues. * Once the migration is complete, **you can access your Formbricks instance** at the same URL as before. ## v2.5 Formbricks v2.5 lets you visualise responses in a data table format. This release also brings a few bug fixes and performance improvements. This release fixes the inconsistency of CTA and consent question values when a question is skipped. The value will now be set to an empty string instead of "dismissed" to maintain consistency with other questions. ### Steps to Migrate This guide is for users self-hosting Formbricks with our one-click setup. If you're using a different setup, you may need to adjust the commands. To begin, navigate to the folder where your `docker-compose.yml` file is located. * **Backup your Database**: This step is important. Please make sure to back up your database before upgrading. You can use the following command to back up your database: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.5_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “No such container”, use `docker ps` to find your container name, e.g.`formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find your Docker Network name for your Postgres Database, find it using `docker network ls` ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:latest && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.5" \ ghcr.io/formbricks/data-migrations:v2.5.3 ``` The command will migrate your data to the latest schema, ensuring your existing data is updated to the new structure. Changes will only be made if the script runs successfully, and you can safely run the script multiple times. * Once the migration is complete, **you can access your Formbricks instance** at the same URL as before. ## v2.4 Formbricks v2.4 lets you create multiple endings for your surveys and choose which one users see based on logic jumps. This version also includes bug fixes and performance improvements. This release will remove support for advanced targeting (enterprise targeting for app surveys) using actions (e.g. targeting users who triggered action x 3 times in the last month). Actions can still be used as triggers, but they will no longer be stored on the server to improve Formbricks' overall performance. ### Steps to Migrate This guide is for users who are self-hosting Formbricks with our one-click setup. If you're using a different setup, you may need to adjust the commands accordingly. To follow these steps, go to the folder where your `docker-compose.yml` file is located. 1. **Backup your Database**: This step is important. Please make sure to back up your database before upgrading. You can use the following command to back up your database: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.4_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “No such container”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as a `*.sql` file, remove the `-Fc` (custom format) option. In case of a restore scenario, you will need to use `psql` then with an empty `formbricks` database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` Now let's migrate the data to the latest schema: To find your Docker Network name for your Postgres Database, find it using `docker network ls` ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:v2.4.3 && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.4" \ ghcr.io/formbricks/data-migrations:v2.4.3 ``` The command above will migrate your data to the latest schema. This is an important step to update your data structure. Changes are only applied if the script runs successfully. You can run the script multiple times without issues. * That's it! Once the migration is done, you can access your Formbricks instance at the same URL as before. ### Additional Updates * The `CRON_SECRET` environment variable is now required to enhance the security of the internal cron APIs. Please ensure that this variable is set in your environment or `docker-compose.yml`. You can generate a secure secret using `openssl rand -hex 32` ## v2.3 Formbricks v2.3 introduces new colour options for rating questions, enhanced multi-language support for Chinese (Simplified & Traditional), and includes several bug fixes and performance improvements. ### Steps to Migrate You only need to run the data migration if you have surveys set up in Chinese (`zh`). If you don't have any surveys in Chinese, you can skip the migration step. This guide is for users self-hosting Formbricks with our one-click setup. If you're using a different setup, adjust the commands as needed. To proceed, go to the folder where your `docker-compose.yml` file is located. * **Backup your Database**: This is a crucial step. Please make sure to backup your database before proceeding with the upgrade. You can use the following command to backup your database: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.3_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “**No such containe**r”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find your Docker Network name for your Postgres Database, find it using `docker network ls` ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:v2.3.0 && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.3" \ ghcr.io/formbricks/data-migrations:v2.3.0 ``` The command above will migrate your data to the latest schema, which is essential for updating your data structure. Changes will only be made if the script runs successfully. You can safely run the script multiple times. * Once the migration is complete, **you can access your Formbricks instance** at the same URL as before. ### Additional Updates The feature to create short URLs in Formbricks is now deprecated. Existing short URLs will continue to work for now, but it is recommended to use long URLs instead. Support for short URL redirects will be removed in a future release. ## v2.2 Formbricks v2.2 introduces XM research presets, providing a brand-new product onboarding experience. Our goal is to make user research "obviously easy" across different industries, starting with Software-as-a-Service and E-Commerce. ### Steps to Migrate This guide is for users who are self-hosting Formbricks with our one-click setup. If you're using a different setup, you may need to adjust the commands accordingly. To run all these steps, please navigate to the `formbricks` folder where your `docker-compose.yml` file is located. 1. **Backup your Database**: This is a crucial step. Please make sure to backup your database before proceeding with the upgrade. You can use the following command to backup your database: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.2_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “No such container”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find your Docker Network name for your Postgres Database, find it using `docker network ls` ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:v2.2 && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.2" \ ghcr.io/formbricks/data-migrations:v2.2 ``` The command above will migrate your data to the latest schema. This is an important step to update your existing data. Changes will only be made if the script runs successfully. You can safely run the script multiple times. * Once the migration is complete, **you can access your Formbricks instance** at the same URL as before. ### Changes in Environment Variables * The `ONBOARDING_DISABLED` environment variable is now deprecated. We have replaced the user onboarding with a product onboarding process that only runs when creating a new product. ## v2.1 Formbricks v2.1 introduces more options for creating No-Code Actions and improves the self-hosting experience with a new onboarding process for fresh instances. To improve the user experience and simplify setup for self-hosting instances, we are transitioning to a single organization model. This change allows self-hosts to manage their instance centrally and more easily control access. A new permissions system will also be introduced soon, providing more granular control over projects and resources within an organization. If you've previously created multiple organizations, you can still switch between them in the UI, but creating new organizations will no longer be possible. ### Steps to Migrate This guide is for users self-hosting Formbricks using our one-click setup. If you are using a different setup, you may need to adjust the commands accordingly. To follow these steps, please navigate to the Formbricks folder where your `docker-compose.yml` file is located. * **Backup your Database**: This is an essential step. Please ensure that you back up your database before proceeding with the upgrade. You can use the following command to back it up: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.1_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “No such container”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as a `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers. ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find your Docker network name for the Postgres database, run the following `docker network ls` ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:v2.1.0 && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.1" \ ghcr.io/formbricks/data-migrations:v2.1.0 ``` Once the migration script runs successfully, your data will be migrated to the latest schema. The script is designed to run safely multiple times, so you don’t need to worry if it’s executed more than once. After completing the migration, you can continue to access your Formbricks instance at the same URL as before. * That's it! Once the migration is complete, you can **now access your Formbricks instance** at the same URL as before. ### Changes in Environment Variables * **SIGNUP\_DISABLED** is now deprecated. Self-hosting instances have signup disabled by default, and new users can only be invited by the organization owner or admin. * **DEFAULT\_TEAM\_ID** has been renamed to **DEFAULT\_ORGANIZATION\_ID**. * **DEFAULT\_TEAM\_ROLE** has been renamed to **DEFAULT\_ORGANIZATION\_ROLE**. ## v2.0 Formbricks v2.0 introduces significant features such as **Multi-Language Surveys** and **Advanced Styling for Surveys**, aimed at improving the flexibility and appearance of your surveys. Additionally, the release includes several optimisations, bug fixes, and smaller improvements to ensure a smoother user experience. If you are upgrading to Formbricks v2.0, follow this guide to ensure a smooth migration of your existing Formbricks instance without encountering issues or build errors. This upgrade requires a **data migration**. Please make sure to back up your database before proceeding. Follow the steps below to upgrade your Formbricks instance to v2.0. If you've used the Formbricks Enterprise Edition with a free beta license key, your instance will be downgraded to the Community Edition 2.0. You can find all license details on the [license page](https://formbricks.com/docs/self-hosting/license). We are moving from DockerHub to GitHub Packages for our images. If you are still pulling the images from DockerHub please change `image: formbricks/formbricks:latest` to `image:ghcr.io/formbricks/formbricks:latest` in your `docker-compose.yml` file. ### Steps to Migrate This guide is for users who are self-hosting Formbricks using our one-click setup. If you are using a different setup, you might adjust the commands accordingly. To run all these steps, please navigate to the `formbricks` folder where your `docker-compose.yml` file is located. 1. **Backup your Database**: This is an essential step. Please ensure that you back up your database before proceeding with the upgrade. You can use the following command to back it up: ```bash theme={null} docker exec formbricks-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v2.0_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “No such container”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as an `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. * Pull the latest version of Formbricks: ```bash theme={null} docker compose pull ``` * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers with the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find your Docker Network name for your Postgres Database, find it using `docker network ls` ```bash theme={null} docker pull ghcr.io/formbricks/data-migrations:v2.0.3 && \ docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v2.0" \ ghcr.io/formbricks/data-migrations:v2.0.3 ``` The above command will migrate your data to the latest schema. This is a crucial step to migrate your existing data to the new structure. Only if the script runs successful, changes are made to the database. The script can safely run multiple times. * That's it! Once the migration is complete, you can **now access your Formbricks instance** at the same URL as before. ### App Surveys with @formbricks/js With this upgrade, we now dynamically fetch the package from our API endpoint and have updated the package entry points to support both app and website surveys. This ensures that you always have the latest version of the package (v2.0.0+). **Old approach:** (v1.6.5) ```ts theme={null} import formbricks from "@formbricks/js"; formbricks.init({ environmentId: "", apiHost: "", userId: "", // optional }); ``` **New approach:** (v2.0.0) Website surveys: ```ts theme={null} import formbricks from "@formbricks/js/website"; formbricks.init({ environmentId: "", apiHost: "", // userId is not supported here }); ``` App surveys: ```ts theme={null} import formbricks from "@formbricks/js/app"; formbricks.init({ environmentId: "", apiHost: "", userId: "", // required }); ``` ## v1.6 Formbricks v1.6 introduces key features like Advanced Targeting, Segmentation, on-the-fly survey triggers, and stability improvements. This update also changes some environment variables. Follow this guide to upgrade your Formbricks instance to v1.6 smoothly. This upgrade requires a data migration. Ensure you back up your database before proceeding. Follow the steps below to upgrade your Formbricks instance to v1.6. ### Steps to Migrate This guide is for users self-hosting Formbricks with our one-click setup. If you're using a different setup, adjust the commands accordingly. Navigate to the Formbricks folder containing your `docker-compose.yml` file to run the steps. * **Backup your Database**: This is a crucial step. Please make sure to backup your database before proceeding with the upgrade. You can use the following command to backup your database: ```bash theme={null} docker exec formbricks-quickstart-postgres-1 pg_dump -Fc -U postgres -d formbricks > formbricks_pre_v1.6_$(date +%Y%m%d_%H%M%S).dump ``` If you run into “No such container”, use `docker ps` to find your container name, e.g. `formbricks_postgres_1`. If you prefer storing the backup as a `*.sql` file remove the `-Fc` (custom format) option. In case of a restore scenario you will need to use `psql` then with an empty `formbricks` database. * Stop the running Formbricks instance & remove the related containers: ```bash theme={null} docker compose down ``` * Restarting the containers will automatically pull the latest version of Formbricks: ```bash theme={null} docker compose up -d ``` * Now let's migrate the data to the latest schema: To find your Docker Network name for your Postgres Database, find it using `docker network ps` ```bash theme={null} docker run --rm \ --network=formbricks_default \ -e DATABASE_URL="postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" \ -e UPGRADE_TO_VERSION="v1.6" \ ghcr.io/formbricks/data-migrations:v1.6.1 ``` The above command will migrate your data to the latest schema. This is a crucial step to migrate your existing data to the new structure. Only if the script runs successful, changes are made to the database. The script can safely run multiple times. 1. That's it! Once the migration is complete, you can **now access your Formbricks instance** at the same URL as before. #### Restoring the database after a failed upgrade ```bash theme={null} docker exec -i formbricks-quickstart-postgres-1 pg_restore --clean -U postgres -v -d formbricks < formbricks_pre_v1.6_.dump ``` Replace the path to `formbricks_pre_v1.6_.dump` with the exact path to your `.dump` file. This will wipe the database and restore from the `.dump` file. ### App Surveys with `@formbricks/js` If you're using the `@formbricks/js` package, update it to version \~1.6.5 to access the latest features and improvements. Currently, the package needs to be pinned to `~1.6.5`, see [this issue](https://github.com/formbricks/formbricks/issues/2273). ### Upgrade and pin the client package ```bash theme={null} npm install @formbricks/js@~1.6.5 ``` ### Deprecated Environment Variables | Environment Variable | Comments | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GITHUB_AUTH_ENABLED` | Was used to enable GitHub OAuth, but from v1.6, you can just set the `GITHUB_ID` and `GITHUB_SECRET` environment variables. | | `GOOGLE_AUTH_ENABLED` | Was used to enable Google OAuth, but from v1.6, you can just set the `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` environment variables. | | `AZUREAD_AUTH_ENABLED` | Was used to enable AzureAD OAuth, but from v1.6, you can just set the `AZUREAD_CLIENT_ID`, `AZUREAD_CLIENT_SECRET` & `AZUREAD_TENANT_ID` environment variables. | ## v1.2 Formbricks v1.2 introduces new features for Link Surveys and enhances security. However, there are some breaking changes related to environment variables. This guide will help you upgrade your Formbricks instance to v1.2 smoothly. ### New Environment Variables | Environment Variable | Required | Recommended Generation | Comments | | -------------------- | -------- | ---------------------- | ---------------------------------- | | `ENCRYPTION_KEY` | true | `openssl rand -hex 32` | Needed for 2 Factor Authentication | ### Deprecated / Removed Environment Variables | Environment Variable | Comments | | -------------------- | --------------------------------------------------------------------------- | | `SURVEY_BASE_URL` | The `WEBAPP_URL` is now used to determine the survey base URL in all places | ## v1.1 Formbricks v1.1 introduces new features and improvements but includes breaking changes with environment variables. This guide will help you upgrade your Formbricks instance to v1.1 without data loss. ### Renamed Environment Variables | till v1.0 | v1.1 | | ----------------------------------------- | ----------------------------- | | `NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED` | `EMAIL_VERIFICATION_DISABLED` | | `NEXT_PUBLIC_PASSWORD_RESET_DISABLED` | `PASSWORD_RESET_DISABLED` | | `NEXT_PUBLIC_SIGNUP_DISABLED` | `SIGNUP_DISABLED` | | `NEXT_PUBLIC_INVITE_DISABLED` | `INVITE_DISABLED` | | `NEXT_PUBLIC_PRIVACY_URL` | `PRIVACY_URL` | | `NEXT_PUBLIC_TERMS_URL` | `TERMS_URL` | | `NEXT_PUBLIC_IMPRINT_URL` | `IMPRINT_URL` | | `NEXT_PUBLIC_GITHUB_AUTH_ENABLED` | `GITHUB_AUTH_ENABLED` | | `NEXT_PUBLIC_GOOGLE_AUTH_ENABLED` | `GOOGLE_AUTH_ENABLED` | | `NEXT_PUBLIC_WEBAPP_URL` | `WEBAPP_URL` | | `NEXT_PUBLIC_IS_FORMBRICKS_CLOUD` | `IS_FORMBRICKS_CLOUD` | | `NEXT_PUBLIC_SURVEY_BASE_URL` | `SURVEY_BASE_URL` | Please note that their values and the logic remains exactly the same. Only the prefix has been deprecated. The other environment variables remain the same as well. ### Deprecated Environment Variables * **`NEXT_PUBLIC_VERCEL_URL`**: Was used as deployment URL fallback (used instead of `WEBAPP_URL`), but from v1.1, you can just set the `WEBAPP_URL` environment variable. * **`RAILWAY_STATIC_URL`**: Was used as Railway Static URL (used instead of `WEBAPP_URL`), but from v1.1, you can just set the `WEBAPP_URL` environment variable. * **`RENDER_EXTERNAL_URL`**: Was used as an external URL to Render (used instead of `WEBAPP_URL`), but from v1.1, you can just set the `WEBAPP_URL` environment variable. * **`HEROKU_APP_NAME`**: Was used to build the App name on a Heroku hosted webapp, but from v1.1, you can just set the `WEBAPP_URL` environment variable. * **`NEXT_PUBLIC_WEBAPP_URL`**: Was used for the same purpose as `WEBAPP_URL`, but from v1.1, you can just set the `WEBAPP_URL` environment variable. * **`PRISMA_GENERATE_DATAPROXY`**: Was used to tell Prisma that it should generate the runtime for Dataproxy usage. But its officially deprecated now. ### Helper Shell Script For a seamless migration, below is a shell script for your self-hosted instance that will automatically update your environment variables to be compliant with the new naming conventions. ### Docker & Single Script Setup Now that these variables can be defined at runtime, you can append them inside your `x-environment` in the `docker-compose.yml` itself. For a more detailed guide on these environment variables, please refer to the [Environment Variables](/docs/self-hosting/configuration/environment-variables) page. ```yaml docker-compose.yml theme={null} version: "3.3" x-environment: &environment environment: # The url of your Formbricks instance used in the admin panel WEBAPP_URL: # Required for next-auth. Should be the same as WEBAPP_URL NEXTAUTH_URL: # PostgreSQL DB for Formbricks to connect to DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/formbricks?schema=public" # NextJS Auth # @see: https://next-auth.js.org/configuration/options#nextauth_secret # You can use: `openssl rand -hex 32` to generate one NEXTAUTH_SECRET: # PostgreSQL password POSTGRES_PASSWORD: postgres # Email Configuration MAIL_FROM: MAIL_FROM_NAME: SMTP_HOST: SMTP_PORT: SMTP_SECURE_ENABLED: SMTP_USER: SMTP_PASSWORD: # Uncomment the below and set it to 1 to disable Email Verification for new signups # EMAIL_VERIFICATION_DISABLED: # Uncomment the below and set it to 1 to disable Password Reset # PASSWORD_RESET_DISABLED: # Uncomment the below and set it to 1 to disable Signups # SIGNUP_DISABLED: # Uncomment the below and set it to 1 to disable loging in with email # EMAIL_AUTH_DISABLED: # Uncomment the below and set it to 1 to disable Invites # INVITE_DISABLED: # Uncomment the below and set a value to have your own Privacy Page URL on the signup & login page # PRIVACY_URL: # Uncomment the below and set a value to have your own Terms Page URL on the auth and the surveys page # TERMS_URL: # Uncomment the below and set a value to have your own Imprint Page URL on the auth and the surveys page # IMPRINT_URL: # Uncomment the below and set to 1 if you want to enable GitHub OAuth # GITHUB_AUTH_ENABLED: # GITHUB_ID: # GITHUB_SECRET: # Uncomment the below and set to 1 if you want to enable Google OAuth # GOOGLE_AUTH_ENABLED: # GOOGLE_CLIENT_ID: # GOOGLE_CLIENT_SECRET: ``` If you have any questions or require help, feel free to reach out to us on [**GitHub Discussions**](https://github.com/formbricks/formbricks/discussions). 😃[ ](https://formbricks.com/docs/developer-docs/rest-api) # Rate Limiting Source: https://formbricks.com/docs/self-hosting/advanced/rate-limiting Current request rate limits in Formbricks Formbricks applies request rate limits to protect against abuse and keep API usage fair. Starting with Formbricks v5, rate limiting is split across two layers: * Envoy Gateway for public and API-key routes that can be enforced at ingress * The application server for remaining session-authenticated routes, server actions, and other flows Envoy does not currently cover Formbricks v5 removes application-level rate limiting for several routes that are now expected to be protected by Envoy Gateway. If you self-host Formbricks without Envoy or an equivalent edge rate limiter, those routes will no longer be throttled by the application server after upgrading. For the full self-hosted upgrade checklist, use this page together with the [v5 migration guide](/docs/self-hosting/advanced/migration#v5). Rate limits are scoped by identifier, depending on the endpoint and enforcement layer: * IP hash (for unauthenticated/client-side routes and public actions) * API key ID (for Envoy-managed and app-managed authenticated API calls) * User ID (for authenticated session-based calls and server actions) * Organization ID (for follow-up email dispatch) When a limit is exceeded, the API returns `429 Too Many Requests`. ## v5 Migration Note Before upgrading to Formbricks v5: * deploy Envoy Gateway or an equivalent edge rate limiter for the covered routes below * keep application Redis/Valkey enabled for the remaining app-enforced limits * expect covered routes to emit gateway `429`s instead of the legacy app JSON `429`s For the current source of truth on covered routes and thresholds, use this page together with your deployment configuration and the [v5 migration guide](/docs/self-hosting/advanced/migration#v5). ## Envoy-Managed Limits These limits are expected to be enforced at the gateway layer in Formbricks v5 and later: | **Route Group** | **Limit** | **Window** | **Identifier** | | ------------------------------------------------------------------------ | ------------ | ---------- | -------------- | | `POST /api/auth/callback/credentials` | 40 requests | 1 hour | IP hash | | `POST /api/auth/callback/token` | 10 requests | 1 hour | IP hash | | `GET, POST, PUT, PATCH, DELETE /api/v1/management/*` (API key auth only) | 100 requests | 1 minute | API key ID | | `POST /api/v1/management/storage` (API key auth only) | 5 requests | 1 minute | API key ID | | `GET, POST, PUT, PATCH, DELETE /api/v1/webhooks/*` (API key auth only) | 100 requests | 1 minute | API key ID | | `GET /api/v1/client/[workspaceId]/environment` | 100 requests | 1 minute | IP hash | | `POST /api/v1/client/[workspaceId]/responses` | 100 requests | 1 minute | IP hash | | `PUT /api/v1/client/[workspaceId]/responses/[responseId]` | 100 requests | 1 minute | IP hash | | `POST /api/v1/client/[workspaceId]/displays` | 100 requests | 1 minute | IP hash | | `POST /api/v1/client/[workspaceId]/user` | 100 requests | 1 minute | IP hash | | `POST /api/v1/client/[workspaceId]/storage` | 5 requests | 1 minute | IP hash | | `POST /api/v2/client/[workspaceId]/responses` | 100 requests | 1 minute | IP hash | | `PUT /api/v2/client/[workspaceId]/responses/[responseId]` | 100 requests | 1 minute | IP hash | | `POST /api/v2/client/[workspaceId]/displays` | 100 requests | 1 minute | IP hash | | `POST /api/v2/client/[workspaceId]/storage` | 5 requests | 1 minute | IP hash | | `DELETE /storage/[workspaceId]/public/[fileName]` (API key auth only) | 5 requests | 1 minute | API key ID | | `DELETE /storage/[workspaceId]/private/[fileName]` (API key auth only) | 5 requests | 1 minute | API key ID | Session-authenticated `/api/v1/management/*`, `/api/v1/management/me`, `/api/v1/management/storage`, and `DELETE /storage/...` requests are **not** covered by the current Envoy policies and remain app-enforced. ## App-Enforced Limits These are the limits that still run inside the Formbricks application server: | **Config** | **Limit** | **Window** | **Identifier** | **Used For** | | ----------------------------- | ------------ | ---------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `auth.signup` | 30 requests | 60 minutes | IP hash | Signup server action | | `auth.forgotPassword` | 5 requests | 60 minutes | IP hash | Forgot password server action | | `auth.verifyEmail` | 10 requests | 60 minutes | IP hash | Resend verification server action | | `api.v1` | 100 requests | 1 minute | Session user ID | Session-authenticated v1 management routes, `/api/v1/management/me`, and `/api/v1/integrations/*` | | `api.v2` | 100 requests | 1 minute | API key ID | Authenticated v2 API wrapper outside the Envoy-managed `/api/v2/client/*` subset | | `api.v3` | 100 requests | 1 minute | API key ID or session user ID | v3 API wrapper | | `api.client` | 100 requests | 1 minute | IP hash | Uncovered client routes such as `GET /api/v1/client/og`, `GET /api/v2/client/[workspaceId]/environment`, and `POST /api/v2/client/[workspaceId]/user` | | `storage.upload` | 5 requests | 1 minute | Session user ID | Session-authenticated `POST /api/v1/management/storage` | | `storage.delete` | 5 requests | 1 minute | Session user ID | Session-authenticated `DELETE /storage/[workspaceId]/[accessType]/[fileName]` | | `actions.emailUpdate` | 3 requests | 60 minutes | User ID | Profile email update action | | `actions.surveyFollowUp` | 50 requests | 60 minutes | Organization ID | Survey follow-up email processing | | `actions.sendLinkSurveyEmail` | 10 requests | 60 minutes | IP hash | Link survey email send action | | `actions.licenseRecheck` | 5 requests | 1 minute | User ID | Enterprise license recheck action | ## Explicit Envoy Exclusions The current Envoy policy set explicitly excludes these routes: * `GET /api/v1/client/og` (still covered by the app-level `api.client` limiter) * `GET /api/v2/health` (not rate-limited) * `OPTIONS` requests (not rate-limited) ## 429 Response Shape Application-generated v1 `429`s return: ```json theme={null} { "code": "too_many_requests", "details": {}, "message": "Maximum number of requests reached. Please try again later." } ``` Application-generated v2/v3 `429`s return: ```json theme={null} { "error": { "code": 429, "message": "Too Many Requests" } } ``` Envoy-generated `429`s are gateway responses and should include an `x-envoy-ratelimited` header. Their exact body shape is not the same stability contract as the in-app JSON responses above. ## Disabling Rate Limiting For self-hosters, rate limiting can be disabled if necessary. We strongly recommend keeping it enabled in production. Set: ```bash theme={null} RATE_LIMITING_DISABLED=1 ``` After changing this value, restart the server. This setting disables only the **application-level** limiter. It does **not** disable Envoy rate-limit policies. ## Operational Notes * Redis/Valkey is required for the application-level limiter (`REDIS_URL`). * If you deploy Envoy rate limiting, use a dedicated Redis/Valkey backend for Envoy instead of sharing the app cache. * If application Redis is unavailable at runtime, app rate-limiter checks currently fail open (requests are allowed through without enforcement). * Client storage upload rate limits count signed upload URL issuance, not successful object creation in S3-compatible storage. * Authentication failure audit logging uses a separate throttle (`shouldLogAuthFailure()`) and is intentionally **fail-closed**: when Redis is unavailable or errors occur, audit log entries are **skipped entirely** rather than written without throttle control. This prevents spam while preserving the hash-integrity chain required for compliance. In other words, if Redis is down, no authentication-failure audit logs will be recorded—requests themselves are still allowed (fail-open rate limiting above), but the audit trail for those failures will not be written. # Authentication Behavior Source: https://formbricks.com/docs/self-hosting/auth-behavior Learn how authentication and user invitation work in self-hosted Formbricks deployments. ## Overview In self-hosted Formbricks, user management and authentication can be customized using environment variables. By default, self-hosted instances have user signup disabled, and only organization owners or admins can invite new users. The behavior of the authentication and invitation flow can be further controlled using the following environment variables: * `AUTH_SKIP_INVITE_FOR_SSO` * `AUTH_SSO_DEFAULT_TEAM_ID` ## License Requirement for Role Management and SSO Behavior To control advanced role management features and environment-based SSO behavior, your self-hosted Formbricks instance must have a valid enterprise license. ## Environment Variables ### `AUTH_SKIP_INVITE_FOR_SSO` * **Type:** Boolean (0 or 1) * **Default:** 0 (invite required) * **Description:** * When set to `1`, users who sign up via SSO (Single Sign-On) providers (such as Google, Azure AD, SAML, or OIDC) can create an account without requiring an invitation. * When set to `0` (default), all users—including those signing up via SSO—must be invited by an organization owner or admin before they can create an account. * **Use case:** * Set this to `1` if you want to allow anyone with access to your SSO provider to join your Formbricks instance without a manual invite. * Keep it at `0` for stricter access control, where only invited users can join, regardless of SSO. ### `AUTH_SSO_DEFAULT_TEAM_ID` * **Type:** String (Team ID, a valid cuid) * **Default:** None (must be set if you want to use default team assignment) * **Description:** * When a new user is invited or signs up (if allowed), they will automatically be added to the team with the ID specified in this variable. * This is useful for onboarding users into a default team, ensuring they have access to relevant Workspaces and resources immediately after joining. * **Use case:** * Set this to the ID of your default team to streamline onboarding for new users. * If not set, users will not be automatically assigned to any team upon signup or invite acceptance. ## Example `.env` Configuration ```env theme={null} # Allow SSO users to join without invite AUTH_SKIP_INVITE_FOR_SSO=1 # Automatically assign new users to this team AUTH_SSO_DEFAULT_TEAM_ID=team-123 ``` Refer to the [Environment Variables documentation](./configuration/environment-variables) for a full list and details. *** For more information on SSO setup, see: * [Google OAuth](./configuration/auth-sso/google-oauth) * [Azure AD OAuth](./configuration/auth-sso/azure-ad-oauth) * [Open ID Connect](./configuration/auth-sso/open-id-connect) * [Keycloak OIDC](./configuration/auth-sso/keycloak-oidc) * [SAML SSO](./configuration/auth-sso/saml-sso) Formbricks does not support special characters, such as Cyrillic, in account email addresses to avoid technical, compatibility, and security issues. Additionally, universal support for such addresses is still limited. # Azure AD OAuth Source: https://formbricks.com/docs/self-hosting/configuration/auth-sso/azure-ad-oauth Configure Microsoft Entra ID (Azure AD) OAuth for secure Single Sign-On with your Formbricks instance. Use enterprise-grade authentication for your survey platform. AzureAD OAuth is part of the Formbricks [Enterprise Edition](/docs/self-hosting/advanced/license) ### Microsoft Entra ID Do you have a Microsoft Entra ID Tenant? Integrate it with your Formbricks instance to allow users to log in using their existing Microsoft credentials. This guide will walk you through the process of setting up an Application Registration for your Formbricks instance. ### Requirements * A Microsoft Entra ID Tenant populated with users. [Create a tenant as per Microsoft's documentation](https://learn.microsoft.com/en-us/entra/fundamentals/create-new-tenant). * A Formbricks instance running and accessible. * The callback URI for your Formbricks instance: `{WEBAPP_URL}/api/auth/oauth2/callback/azuread` **Upgrading from v5.1 or earlier?** This path changed in v5.2. The old URI was `{WEBAPP_URL}/api/auth/callback/azure-ad`. Add the new one under **Redirect URIs** in your app registration or logins will fail. See the [migration guide](/docs/self-hosting/advanced/migration) for the full list of changed callback URLs. ## How to connect your Formbricks instance to Microsoft Entra * Login to the [Microsoft Entra admin center](https://entra.microsoft.com/). * Go to **Applications** > **App registrations** in the left menu. ![first](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250153/image_tobdth.jpg) * Click the **New registration** button at the top. ![second](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250228/image_dmz75t.jpg) * Name your application something descriptive, such as `Formbricks SSO`. ![third](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250292/image_rooa3w.jpg) * If you have multiple tenants/organizations, choose the appropriate **Supported account types** option. Otherwise, leave the default option for *Single Tenant*. ![fourth](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250542/image_nyndzo.jpg) * Under **Redirect URI**, select **Web** for the platform and paste your Formbricks callback URI (see Requirements above). ![fifth](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250776/image_s3pgb6.jpg) * Click **Register** to create the App registration. You will be redirected to your new app's *Overview* page after it is created. * On the *Overview* page, under **Essentials**: * Copy the entry for **Application (client) ID** to populate the `AZUREAD_CLIENT_ID` variable. * Copy the entry for **Directory (tenant) ID** to populate the `AZUREAD_TENANT_ID` variable. ![sixth](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250876/image_dj2vi5.jpg) * From your App registration's *Overview* page, go to **Manage** > **Certificates & secrets**. ![seventh](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250913/image_p4zknw.jpg) * Make sure you have the **Client secrets** tab active, and click **New client secret**. ![eighth](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738250973/image_kyjray.jpg) * Enter a **Description**, set an **Expires** period, then click **Add**. You will need to create a new client secret using these steps whenever your chosen expiry period ends. ![ninth](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738251467/image_bkirq4.jpg) * Copy the entry under **Value** to populate the `AZUREAD_CLIENT_SECRET` variable. Microsoft will only show this value to you immediately after creation, and you will not be able to access it again. If you lose it, simply create a new secret. ![tenth](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738251234/image_jen6tp.jpg) * Update these environment variables in your `docker-compose.yml` or pass it like your other environment variables to the Formbricks container. You must wrap the `AZUREAD_CLIENT_SECRET` value in double quotes (e.g., "THis\~iS4faKe.53CreTvALu3"\`) to prevent issues with special characters. An example `.env` for Microsoft Entra ID in Formbricks would look like this: ```yml Formbricks Env for Microsoft Entra ID SSO theme={null} AZUREAD_CLIENT_ID=a25cadbd-f049-4690-ada3-56a163a72f4c AZUREAD_TENANT_ID=2746c29a-a3a6-4ea1-8762-37816d4b7885 AZUREAD_CLIENT_SECRET="THis~iS4faKe.53CreTvALu3" ``` * Restart your Formbricks instance. * You're all set! Users can now sign up & log in using their Microsoft credentials associated with your Entra ID Tenant. # Google OAuth Source: https://formbricks.com/docs/self-hosting/configuration/auth-sso/google-oauth Configure Google OAuth for secure Single Sign-On with your Formbricks instance. Implement enterprise-grade authentication for your survey platform with Google credentials. Google OAuth is part of the Formbricks [Enterprise Edition](/docs/self-hosting/advanced/license) ### Google OAuth Integrating Google OAuth with your Formbricks instance allows users to log in using their Google credentials, ensuring a secure and streamlined user experience. This guide will walk you through the process of setting up Google OAuth for your Formbricks instance. ### Requirements * A Google Cloud Platform (GCP) account * A Formbricks instance running ### How to connect your Formbricks instance to Google * Navigate to the [GCP Console](https://console.cloud.google.com/). * From the projects list, select a project or create a new one. * If the **APIs & services** page isn't already open, open the console left side menu and select **APIs & services**. * On the left, click **Credentials**. * Click **Create Credentials**, then select **OAuth client ID**. * If this is your first time creating a client ID, configure your consent screen by clicking **Consent Screen**. * Fill in the necessary details and under **Authorized domains**, add the domain where your Formbricks instance is hosted. * Select the application type **Web application** for your project and enter any additional information required. * Ensure to specify authorized JavaScript origins and authorized redirect URIs. ``` Authorized JavaScript origins: {WEBAPP_URL} Authorized redirect URIs: {WEBAPP_URL}/api/auth/callback/google ``` * To integrate the Google OAuth, you have two options: either update the environment variables in the docker-compose file or directly add them to the running container. * In your Docker setup directory, open the `.env` file, and add or update the following lines with the `Client ID` and `Client Secret` obtained from Google Cloud Platform: ```sh theme={null} GOOGLE_CLIENT_ID=your-client-id-here GOOGLE_CLIENT_SECRET=your-client-secret-here ``` * Alternatively, you can add the environment variables directly to the running container using the following commands (replace `container_id` with your actual Docker container ID): ```sh theme={null} docker exec -it container_id /bin/bash export GOOGLE_CLIENT_ID=your-client-id-here export GOOGLE_CLIENT_SECRET=your-client-secret-here exit ``` Restarting your Docker containers may cause a brief period of downtime. Plan accordingly. * Once the environment variables have been updated, it's crucial to restart your Docker containers to apply the changes. This ensures that your Formbricks instance can utilize the new Google OAuth configuration for user authentication. * Navigate to your Docker setup directory where your `docker-compose.yml` file is located. * Run the following command to bring down your current Docker containers and then bring them back up with the updated environment configuration. # Keycloak OIDC Source: https://formbricks.com/docs/self-hosting/configuration/auth-sso/keycloak-oidc Configure Keycloak as an OpenID Connect (OIDC) provider for secure Single Sign-On with your self-hosted Formbricks instance. Keycloak OIDC is part of the Formbricks [Enterprise Edition](/docs/self-hosting/advanced/license) Integrating Keycloak with your Formbricks instance allows users to log in using their existing Keycloak credentials via OpenID Connect. This guide walks you through setting up a Keycloak OIDC client and configuring Formbricks to use it, including automatic user provisioning into a default team. ### Requirements * A running Keycloak instance with a configured realm and users. * A self-hosted Formbricks instance with a valid [Enterprise license](/docs/self-hosting/advanced/license). * The Formbricks callback URI: `{WEBAPP_URL}/api/auth/oauth2/callback/openid` **Upgrading from v5.1 or earlier?** This path changed in v5.2. The old URI was `{WEBAPP_URL}/api/auth/callback/openid` (without `/oauth2`). Add the new one to your Keycloak client's **Valid redirect URIs** or logins will fail. See the [migration guide](/docs/self-hosting/advanced/migration) for the full list of changed callback URLs. ## Setting up Keycloak OIDC * Log in to your Keycloak admin console. * Select the realm you want to use (e.g., `production`). * Navigate to **Clients** in the left menu and click **Create client**. * Set **Client type** to `OpenID Connect`. * Choose a **Client ID** (e.g., `formbricks`) and click **Next**. * Enable **Client authentication** (this makes the client "confidential" and generates a client secret). * Under **Authentication flow**, ensure **Standard flow** is enabled. * Click **Next**. Formbricks uses PKCE (Proof Key for Code Exchange) for added security. Modern Keycloak versions (21+) support PKCE automatically for confidential clients. No additional PKCE configuration is needed. * Under **Valid redirect URIs**, add your Formbricks callback URI: ``` https://your-formbricks-domain.com/api/auth/oauth2/callback/openid ``` Replace `your-formbricks-domain.com` with your actual Formbricks URL. * Click **Save**. * After saving, go to the **Credentials** tab of your newly created client. * Copy the **Client secret** value — you will need this for the Formbricks configuration. The OIDC issuer URL follows this pattern: ``` https://your-keycloak-domain.com/realms/your-realm-name ``` For example, if your Keycloak runs at `login.example.com` and your realm is called `production`: ``` https://login.example.com/realms/production ``` You can verify the issuer URL by navigating to `https://your-keycloak-domain.com/realms/your-realm-name/.well-known/openid-configuration` in your browser. It should return a JSON document with the OIDC discovery endpoints. Add the following environment variables to your Formbricks `.env` file or `docker-compose.yml`: ```yml Formbricks Env for Keycloak OIDC theme={null} OIDC_CLIENT_ID=formbricks OIDC_CLIENT_SECRET=your-client-secret-here OIDC_ISSUER=https://your-keycloak-domain.com/realms/your-realm-name OIDC_DISPLAY_NAME="Keycloak Login" OIDC_SIGNING_ALGORITHM=RS256 ``` | Variable | Description | | ------------------------ | ------------------------------------------------------------------------------------------------------------------ | | `OIDC_CLIENT_ID` | The Client ID you chose in Keycloak (e.g., `formbricks`). | | `OIDC_CLIENT_SECRET` | The client secret from the **Credentials** tab. | | `OIDC_ISSUER` | Your Keycloak realm URL (see step above). | | `OIDC_DISPLAY_NAME` | The label shown on the Formbricks login button. | | `OIDC_SIGNING_ALGORITHM` | Optional. Keycloak and Formbricks both default to `RS256`. Only set this if your realm uses a different algorithm. | * Restart your Formbricks instance. * The login page should now display a button with your chosen `OIDC_DISPLAY_NAME`. * Click it to authenticate through Keycloak. To disable email/password login entirely and force all users through Keycloak, set `EMAIL_AUTH_DISABLED=1`. This hides the email/password form on the login page. ## Automatic User Provisioning By default, new SSO users must be invited by an organization admin before they can access Formbricks. To allow Keycloak users to join automatically and be assigned to a team, configure the following additional environment variables: ```yml Automatic User Provisioning theme={null} AUTH_SKIP_INVITE_FOR_SSO=1 AUTH_SSO_DEFAULT_TEAM_ID=your-team-id-here ``` | Variable | Description | | -------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `AUTH_SKIP_INVITE_FOR_SSO` | Set to `1` to let SSO users create an account without a manual invite. | | `AUTH_SSO_DEFAULT_TEAM_ID` | The ID of the team that new SSO users are automatically added to. The organization is determined from this team. | ### Finding Your Team ID 1. Log in to Formbricks as an admin. 2. Go to **Settings → Organization → Teams**. 3. Click **Manage Team** on the team you want to use as the default. 4. The Team ID is displayed in the modal. The Teams feature requires the **Role-Based Access Control (RBAC)** add-on on your Enterprise license. If you don't see the Teams section under **Settings → Organization**, contact [Formbricks support](mailto:support@formbricks.com) to enable it. ### How It Works When `AUTH_SKIP_INVITE_FOR_SSO=1` and `AUTH_SSO_DEFAULT_TEAM_ID` are both set: 1. A user authenticates through Keycloak for the first time. 2. Formbricks creates a new user account. 3. The user is automatically added as a **member** of the organization that owns the specified team. 4. The user is assigned to the default team, granting them access to all Workspaces associated with that team. No manual invitation or admin action is required. ## Full Configuration Example A complete `.env` configuration for Keycloak OIDC with automatic user provisioning: ```yml Complete Keycloak OIDC Configuration theme={null} OIDC_CLIENT_ID=formbricks OIDC_CLIENT_SECRET=your-client-secret-here OIDC_ISSUER=https://login.example.com/realms/production OIDC_DISPLAY_NAME="Company Login" OIDC_SIGNING_ALGORITHM=RS256 AUTH_SKIP_INVITE_FOR_SSO=1 AUTH_SSO_DEFAULT_TEAM_ID=your-team-id-here # Optional: disable email/password login to force Keycloak-only authentication # EMAIL_AUTH_DISABLED=1 ``` ## Troubleshooting This means the user was added to the organization but not assigned to a team. Verify that: * `AUTH_SSO_DEFAULT_TEAM_ID` is set correctly (the env variable name is `AUTH_SSO_DEFAULT_TEAM_ID`, not `DEFAULT_TEAM_ID`). * The Team ID is a valid cuid that exists in your database. * The team is linked to a Workspace via **Workspace Teams** settings. You can verify the team exists by checking your database: ```sql theme={null} SELECT id, name, "organizationId" FROM "Team" WHERE id = 'your-team-id'; ``` Formbricks determines the organization automatically from the `AUTH_SSO_DEFAULT_TEAM_ID` — you do not need to set a separate organization ID. If this error appears: * Verify the team specified in `AUTH_SSO_DEFAULT_TEAM_ID` exists and belongs to the correct organization. * Confirm your Enterprise license includes the RBAC feature (required for Teams). * Ensure the organization has an active billing plan that supports access control. * Confirm that `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`, and `OIDC_ISSUER` are all set. * Ensure your Formbricks instance has a valid Enterprise license. * Restart the Formbricks container after adding the environment variables. * Verify the **Valid redirect URI** in your Keycloak client matches exactly: `{WEBAPP_URL}/api/auth/oauth2/callback/openid` * If you upgraded from v5.1 or earlier, note the path changed in v5.2 — the old `/api/auth/callback/openid` URI no longer works on its own. * Check that the OIDC issuer URL is reachable from your Formbricks server. You can test with: ```bash theme={null} curl https://your-keycloak-domain.com/realms/your-realm-name/.well-known/openid-configuration ``` # Open ID Connect Source: https://formbricks.com/docs/self-hosting/configuration/auth-sso/open-id-connect Configure Open ID Connect for secure Single Sign-On with your Formbricks instance. Implement enterprise-grade authentication for your survey platform with Open ID Connect. OpenID Connect is part of the Formbricks [Enterprise Edition](/docs/self-hosting/advanced/license) Integrating your own OIDC (OpenID Connect) instance with your Formbricks instance allows users to log in using their OIDC credentials, ensuring a secure and streamlined user experience. Please follow the steps below to set up OIDC for your Formbricks instance. * Configure your OIDC provider & get the following variables: * `OIDC_CLIENT_ID` * `OIDC_CLIENT_SECRET` * `OIDC_ISSUER` * `OIDC_SIGNING_ALGORITHM` Make sure the Redirect URI for your OIDC Client is set to `{WEBAPP_URL}/api/auth/oauth2/callback/openid`. **Upgrading from v5.1 or earlier?** This path changed in v5.2. The old URI was `{WEBAPP_URL}/api/auth/callback/openid` (without `/oauth2`). Add the new one at your OIDC provider or logins will fail. See the [migration guide](/docs/self-hosting/advanced/migration) for the full list of changed callback URLs. * Update these environment variables in your `docker-compose.yml` or pass it directly to the running container. An example configuration for a FusionAuth OpenID Connect in Formbricks would look like: ```yml Formbricks Env for FusionAuth OIDC theme={null} OIDC_CLIENT_ID=59cada54-56d4-4aa8-a5e7-5823bbe0e5b7 OIDC_CLIENT_SECRET=4f4dwP0ZoOAqMW8fM9290A7uIS3E8Xg29xe1umhlB_s OIDC_ISSUER=http://localhost:9011 OIDC_DISPLAY_NAME=FusionAuth OIDC_SIGNING_ALGORITHM=HS256 ``` * Set an environment variable `OIDC_DISPLAY_NAME` to the display name of your OIDC provider. * Restart your Formbricks instance. * You're all set! Users can now sign up & log in using their OIDC credentials. # SAML SSO Source: https://formbricks.com/docs/self-hosting/configuration/auth-sso/saml-sso Configure SAML Single Sign-On (SSO) for secure enterprise authentication with your Formbricks instance. SAML SSO is part of the Formbricks [Enterprise Edition](/docs/self-hosting/advanced/license) ## Overview Formbricks supports SAML Single Sign-On (SSO) to enable secure, centralized authentication. With SAML, organizations can integrate their existing Identity Provider (IdP) infrastructure for streamlined access management. Formbricks internally uses BoxyHQ's SAML Jackson to manage SAML connections. SAML Jackson is a service provided by BoxyHQ that manages SAML connection details and validates assertions. It is part of the Formbricks server. To learn more about SAML Jackson, please refer to the [BoxyHQ SAML Jackson documentation](https://boxyhq.com/docs/jackson/deploy). ## How SAML works in Formbricks SAML (Security Assertion Markup Language) is an XML-based standard for exchanging authentication and authorization data between an Identity Provider (IdP) and Formbricks. Here's how the integration works with BoxyHQ Jackson embedded into the flow: 1. **Login Initiation:**\ The user clicks `Continue with SAML SSO` on Formbricks. 2. **Configuration Retrieval via BoxyHQ:**\ Formbricks requests the SAML connection details from BoxyHQ Jackson. BoxyHQ securely stores and manages the IdP configuration, including endpoints, certificates, and other metadata. 3. **Redirection:**\ With the configuration details from BoxyHQ, Formbricks redirects the user to the IdP’s login page (e.g., Okta). 4. **Authentication:**\ The user authenticates directly with the IdP. 5. **SAML Response:**\ Upon successful authentication, the IdP sends a signed SAML response back to Formbricks via the user’s browser. 6. **Validation via BoxyHQ:**\ BoxyHQ Jackson validates the SAML assertion—verifying the signature and extracting user details—before sending the validated data back to Formbricks. 7. **Access Granted:**\ Formbricks logs the user in using the verified information. ## SAML Auth Flow Sequence Diagram Below is a sequence diagram illustrating the complete SAML authentication flow with BoxyHQ Jackson integrated: ```mermaid theme={null} sequenceDiagram participant U as User participant FB as Formbricks (SP) participant BHQ as BoxyHQ Jackson participant OK as Okta (IdP) Note over FB,BHQ: (Setup phase, done beforehand)
1. Admin configures SAML metadata in Formbricks
2. BoxyHQ stores & manages SAML connection details U->>FB: Clicks “Continue with SAML SSO" FB->>BHQ: Request SAML connection details BHQ->>FB: Returns SAML configuration (IdP info) FB->>OK: Redirect user to Okta (SAML Auth Request) OK->>U: Prompts user for credentials U->>OK: Submits credentials OK->>FB: Sends SAML Assertion (Callback URL) FB->>BHQ: Validates assertion & extracts user info BHQ->>FB: Returns validated user data FB->>U: Logs user into Formbricks ``` ## Setting Up SAML SSO To configure SAML SSO in Formbricks, follow these steps: Configure a dedicated database for SAML by setting the `SAML_DATABASE_URL` environment variable in your `docker-compose.yml` file (e.g., `postgres://postgres:postgres@postgres:5432/formbricks-saml`). If you're using a self-signed certificate for Postgres, include the `sslmode=disable` parameter. Create a SAML application in your IdP by following your provider's instructions([SAML Setup](/docs/development/guides/auth-and-provision/setup-saml-with-identity-providers)) Provision users in your IdP and configure access to the IdP SAML app for all your users (who need access to Formbricks). Keep the XML metadata from your IdP handy for the next step. Create a file called `connection.xml` in your self-hosted Formbricks instance's `formbricks/saml-connection` (use `formbricks/apps/web/saml-connection` for development) directory and paste the XML metadata from your IdP into it. Please create the directory if it doesn't exist. Your metadata file should start with a tag like this: `<...>` or ``. Please remove any extra text from the metadata. Restart Formbricks to apply the changes. You can do this by running `docker compose down` and then `docker compose up -d`. We don't support multiple SAML connections yet. You can only have one SAML connection at a time. If you change the `connection.xml` file, your existing SAML connection will be overwritten. # CDN Source: https://formbricks.com/docs/self-hosting/configuration/cdn Use a CDN in front of public Formbricks traffic without serving stale survey assets after upgrades. ### Why a CDN is recommended A CDN is recommended for production self-hosting when you expose Formbricks surveys publicly. It helps you: * reduce latency for survey respondents in different regions * absorb traffic spikes on public survey links and embedded surveys * terminate TLS and use provider features such as DDoS protection, WAF rules, and bot filtering * serve static assets such as `/_next/*`, `/css/*`, and `/js/surveys.umd.cjs` closer to end users You can run Formbricks without a CDN, but it is usually the better production setup once surveys are reachable from the public internet. ## Recommended topology The cleanest setup is to put the **public survey domain** behind the CDN and keep the **admin domain** separate: ```bash theme={null} # Private admin domain WEBAPP_URL=https://admin.formbricks.example.com NEXTAUTH_URL=https://admin.formbricks.example.com # Public survey domain PUBLIC_URL=https://surveys.example.com ``` In this setup: * `PUBLIC_URL` handles survey traffic, embed traffic, and public assets * `WEBAPP_URL` handles the admin UI, authentication, and other private routes Please refer to the [Domain Configuration](/docs/self-hosting/configuration/domain-configuration) guide first if you have not set up a dedicated public domain yet. ## What the CDN should do Configure your CDN to proxy requests to your Formbricks origin while preserving the original `Host` header and HTTPS scheme. As a baseline, the CDN should: * cache static assets and respect origin cache headers * forward query strings * bypass cache when cookies or authorization headers are present * avoid rewriting paths or stripping prefixes Do not enable a blanket "cache everything" rule for all Formbricks routes. That can cause stale survey pages, stale client state, broken authentication flows, or outdated JavaScript bundles after an upgrade. ## What is safe to cache In most setups, you should only cache static public assets aggressively: * `/_next/*` * `/js/*` * `/css/*` * `/images/*` * `/fonts/*` * `/icons/*` * `/favicon.ico` * `/storage/{workspaceId}/public/*` If you place the full public domain behind a CDN, keep HTML and API traffic proxied but not force-cached unless you have a very deliberate invalidation strategy. ## What should bypass cache These routes should generally not be cached: * admin routes served from `WEBAPP_URL` * authentication routes such as `/api/auth/*` * public client API routes such as `/api/v1/client/*` and `/api/v2/client/*` * survey HTML routes such as `/s/*`, `/c/*`, and `/p/*` unless your CDN strictly honors short origin cache headers This is especially important if your CDN vendor offers features like "Cache Everything", "Edge Cache TTL", or "Ignore origin headers". Those features can easily keep old Formbricks responses alive longer than intended. ## Important detail for upgrades Formbricks serves the survey runtime bundle from a stable public path: ```text theme={null} /js/surveys.umd.cjs ``` That bundle is used by the website/app survey SDK. If your CDN keeps serving an older copy after you deploy a new Formbricks version, respondents may receive outdated survey code even though the rest of the app has already been updated. This is the most common CDN-related upgrade issue for self-hosters: the application is updated, but the CDN still serves cached JavaScript from the previous release. ## Upgrade checklist Every time you update Formbricks, include CDN cache handling in your rollout: Update Formbricks as usual by following the [Migration Guide](/docs/self-hosting/advanced/migration) or your normal deployment process. Purge the CDN cache for at least `/js/*`, `/_next/*`, and `/css/*`. If your CDN supports targeted purges, make sure `/js/surveys.umd.cjs` is included every time. Open a survey or embedded survey through the public domain and verify in the browser network tab that the JavaScript assets are fetched fresh from the updated release. Browser caches can hide CDN issues. During verification, use a hard refresh or disable the browser cache in DevTools to confirm the new assets are really being served. ## Practical setup guidance Most CDN providers can be configured safely with the following approach: 1. Put only the public Formbricks domain behind the CDN. 2. Forward all requests to your reverse proxy or load balancer. 3. Cache static asset paths only. 4. Bypass cache for authenticated traffic, API traffic, and survey HTML. 5. Add a purge step to every Formbricks deployment or upgrade. If you use a single-domain deployment, the same advice still applies, but you need more careful cache rules because admin and public traffic share the same hostname. ## Troubleshooting stale content If users still see outdated surveys or broken embeds after an upgrade, check the following: * the CDN is not overriding origin cache headers with a long fixed TTL * `/js/surveys.umd.cjs` has been purged or revalidated * the public domain in your SDK configuration points to `PUBLIC_URL`, not the admin domain * the browser cache is cleared during verification * your reverse proxy is forwarding the correct `Host` and `X-Forwarded-Proto` headers If problems remain, temporarily bypass the CDN for the public domain. If the issue disappears immediately, the CDN cache policy is the first place to investigate. # Custom SSL Certificate Source: https://formbricks.com/docs/self-hosting/configuration/custom-ssl Using Formbricks One-Click Setup with a Custom SSL Certificate. Formbricks One-Click setup already comes with a valid SSL certificate using **Let's Encrypt**. This guide is only if you already have a valid SSL certificate that you need to use due to company policy or other requirements. Formbricks' One-Click setup automatically creates an SSL certificate using **Let's Encrypt**. However, some setups need a custom SSL certificate. This is common for intranets or systems with special certificate rules that use an internal or custom certificate authority (CA). ### Step 1: Navigate to the Formbricks Folder Move into the `formbricks/` directory: ```bash theme={null} cd formbricks ``` ### Step 2: Create a Folder for SSL Certificates Create a folder called `certs` and place your SSL certificate files inside: ```bash theme={null} mkdir certs # Move your SSL certificate files to the certs folder mv /path/to/your/fullchain.crt certs/ mv /path/to/your/cert.key certs/ ``` ### Step 3: Understand SSL Certificate Files * **fullchain.crt** – Your SSL certificate, including the full certificate chain. * **cert.key** – The private key used to encrypt data. ### Step 4: Set Correct File Permissions Ensure the certificate files have the right permissions: ```bash theme={null} sudo chown root:root certs/* sudo chmod 600 certs/* ``` ### Step 5: Update `traefik.yaml` Modify the file to define HTTP and HTTPS settings: ```yaml traefik.yaml theme={null} entryPoints: web: address: ":80" http: redirections: entryPoint: to: websecure scheme: https permanent: true websecure: address: ":443" providers: docker: watch: true exposedByDefault: false file: directory: /etc/traefik/dynamic ``` ### Step 6: Create `certs-traefik.yaml` Create a `certs-traefik.yaml` file that specifies the path to your custom SSL certificate and key. ```yaml certs-traefik.yaml theme={null} tls: certificates: - certFile: /certs/fullchain.crt keyFile: /certs/cert.key ``` ### Step 7: Update `docker-compose.yml` Modify the configuration to enforce SSL. The rest of the configuration should remain the same as the One-Click setup: ```yaml docker-compose.yml theme={null} services: formbricks: restart: always image: ghcr.io/formbricks/formbricks:latest depends_on: - postgres labels: - "traefik.enable=true" # Enable Traefik for this service - "traefik.http.routers.formbricks.rule=Host(`my-domain.com`)" # Use your actual domain or IP - "traefik.http.routers.formbricks.entrypoints=websecure" # Use the websecure entrypoint (port 443 with TLS) - "traefik.http.routers.formbricks.tls=true" # Enable TLS - "traefik.http.services.formbricks.loadbalancer.server.port=3000" # Forward traffic to Formbricks on port 3000 ports: - 3000:3000 volumes: - uploads:/home/nextjs/apps/web/uploads/ <<: *environment traefik: image: "traefik:v3.6.4" restart: always container_name: "traefik" depends_on: - formbricks ports: - "80:80" - "443:443" - "8080:8080" volumes: - ./traefik.yaml:/traefik.yaml - ./acme.json:/acme.json - /var/run/docker.sock:/var/run/docker.sock:ro - ./certs:/certs - ./certs-traefik.yaml:/etc/traefik/dynamic/certs-traefik.yaml ``` ## Summary * **Navigate to the Formbricks folder** * **Create a `certs/` folder** and move your certificate files inside. * **Ensure you have the correct certificate files** (`fullchain.crt` and `cert.key`). * **Update file permissions** for security. * **Modify `traefik.yaml`** to handle HTTPS. * **Create `certs-traefik.yaml`** to point to your certificate files. * **Update `docker-compose.yml`** to use your custom SSL certificate. This setup ensures that Formbricks securely communicates using your own SSL certificate. 🚀 If you have any questions or require help, feel free to reach out to us on [**GitHub Discussions**](https://github.com/formbricks/formbricks/discussions). 😃 # Custom Subpath Source: https://formbricks.com/docs/self-hosting/configuration/custom-subpath Serve Formbricks from a custom URL prefix when you cannot expose it on the root domain. Custom subpath deployments are currently under internal review. If you need early access, please reach out via [GitHub Discussions](https://github.com/formbricks/formbricks/discussions). ### When to use a custom subpath Use a custom subpath (also called a Next.js base path) when your reverse proxy reserves the root domain for another service, but you still want Formbricks to live under the same hostname—for example `https://example.com/feedback`. Support for a build-time `BASE_PATH` variable is available in the Formbricks web app so that Next.js-managed routes, assets, and navigation honor the prefix. ### Requirements and limitations * `BASE_PATH` must be present during `pnpm build`; changing it afterward requires a rebuild. * Next.js inlines the base path into the client bundles, so setting `BASE_PATH` on an already-built official Formbricks image has no effect. You must build your own Formbricks web image with the required value. * Formbricks-owned public URLs need the prefix, but each variable expects a different path: `WEBAPP_URL` uses the app root, while `NEXTAUTH_URL` and optional `BETTER_AUTH_URL` use the full auth endpoint under `/custom-path/api/auth`. External webhook target URLs do not need the prefix unless they point to a Formbricks endpoint. * Known limitation: the browser auth client currently sends login and sign-up requests to `/api/auth` at the domain root instead of `/custom-path/api/auth`. Authentication therefore does not work for a true subpath-only deployment, and changing the URL environment variables does not correct the browser request path. * Your proxy must rewrite `/custom-path/*` to the Formbricks container while keeping the prefix visible to clients. ### Configure object storage separately `BASE_PATH` applies only to the Formbricks web application. It does not add the same prefix to RustFS, MinIO, or another S3-compatible storage service, and the custom image described below is a Formbricks image—not a custom storage image. Keep the S3 API at the root of a dedicated endpoint such as `https://files.example.com` and set `S3_ENDPOINT_URL` to that endpoint. Do not route it through an arbitrary prefix such as `https://example.com/custom-path/storage`: the request path is part of the AWS Signature Version 4 signature, so proxy path rewriting can invalidate signed and presigned requests. Provider settings that expose a management console under a subpath do not change the S3 API base path. For the complete storage and CORS setup, see [File Uploads Configuration](/docs/self-hosting/configuration/file-uploads). ### Configure environment variables Add the following variables to the environment you use for builds (local, CI, or Docker build args): ```bash theme={null} BASE_PATH="/custom-path" WEBAPP_URL="https://yourdomain.com/custom-path" NEXTAUTH_URL="https://yourdomain.com/custom-path/api/auth" BETTER_AUTH_URL="https://yourdomain.com/custom-path/api/auth" ``` If you use third-party OAuth providers, ensure every Formbricks callback URL you register includes the prefix. MCP clients must use `https://yourdomain.com/custom-path/api/mcp`, and OAuth discovery is served from `https://yourdomain.com/custom-path/.well-known/oauth-authorization-server/api/auth`. ### Build a Docker image with a custom subpath Make sure you have the repository checked out and create temporary files (or use --secret) for the required build-time secrets such as DATABASE\_URL, ENCRYPTION\_KEY, REDIS\_URL, and optional telemetry tokens. Use the Formbricks web Dockerfile and supply the custom subpath via --build-arg. Example: ```bash theme={null} docker build \ --progress=plain \ --no-cache \ --build-arg BASE_PATH=/custom-path \ --secret id=database_url,src=<(printf "postgresql://user:password@localhost:5432/formbricks?schema=public") \ --secret id=encryption_key,src=<(printf "your-32-character-encryption-key-here") \ --secret id=redis_url,src=<(printf "redis://localhost:6379") \ --secret id=sentry_auth_token,src=<(printf "") \ -f apps/web/Dockerfile \ -t formbricks-web \ . ``` During the build logs you should see BASE PATH /custom-path, confirming that Next.js picked up the prefix. Start the resulting image with the same runtime environment variables you normally use (database credentials, mailing provider, etc.). Point your reverse proxy so that /custom-path requests forward to [http://formbricks-web:3000/custom-path](http://formbricks-web:3000/custom-path) without stripping the prefix. ### Verify the deployment 1. Open `https://yourdomain.com/custom-path/auth/login` and confirm the page loads under the prefix. 2. In your browser's network panel, confirm Next.js assets and navigation requests include `/custom-path`. 3. Do not use login or onboarding as a successful deployment check until the browser auth-client limitation above is fixed. ### Troubleshooting checklist * Confirm your build pipeline actually passes `BASE_PATH` (and, if needed, `WEBAPP_URL`/`NEXTAUTH_URL`) into the build stage—check CI logs for the `BASE PATH /your-prefix` line and make sure custom Dockerfiles or wrappers forward `--build-arg BASE_PATH=...` correctly. * Keep `WEBAPP_URL`, `NEXTAUTH_URL`, and `BETTER_AUTH_URL` (if set) configured as shown above for server-side callbacks and discovery, but note that they do not fix the browser auth-client request path. * Re-run the Docker build when changing `BASE_PATH`; simply editing the container environment is not sufficient. * Inspect your proxy configuration to ensure it does not rewrite paths internally (e.g., `strip_prefix` needs to stay disabled). * When in doubt, rebuild locally with `--progress=plain` and verify that the `BASE PATH` line reflects your prefix. # Domain Configuration Source: https://formbricks.com/docs/self-hosting/configuration/domain-configuration Configuring your domain for Formbricks. Formbricks supports both single domain and dual domain configurations with Public/Private Domain Split feature. This guide will help you set up your domains correctly for enhanced security and organization. ## Single Domain Setup For a single domain setup, you need to configure two essential environment variables: 1. `WEBAPP_URL`: The base URL of your Formbricks instance (serves both admin and public content) 2. `NEXTAUTH_URL`: The authentication URL (should be the same as WEBAPP\_URL) ### Example Configuration ```bash theme={null} WEBAPP_URL=https://formbricks.example.com NEXTAUTH_URL=https://formbricks.example.com ``` ### Important Notes * Both URLs must be the same for authentication to work properly * The URLs should be the full URL including the protocol (http\:// or https\://) * Make sure your domain is properly configured in your DNS settings * If you're using HTTPS (recommended), ensure you have valid SSL certificates installed ## Public / Private Domain Split Formbricks supports a powerful domain separation feature that allows you to serve public content (surveys, public APIs) from a different domain than your private admin interface. This provides enhanced security and better organization. It enables you to: * Serve public content (surveys, public APIs) from a different domain than your private admin interface * Restrict admin functionality to the private domain * Ensure authentication and sensitive operations only occur on the private domain Publicly available images uploaded before the domain split (logos, images in Picture Select questions, etc.) will continue to be served from the Private Domain. **To protect your Private Domain, please reupload public images after the successful setup of the domain split.** ### Why Use Domain Split? * **Enhanced Security**: Separate public-facing surveys from your admin interface * **Better Organization**: Use dedicated domains for different purposes (e.g., `admin.company.com` for management, `surveys.company.com` for public surveys) ### Configuration To set up Public/Private Domain Split: 1. **Private Domain (WEBAPP\_URL)**: Your admin/management interface 2. **Public Domain (PUBLIC\_URL)**: For surveys and public-facing content **Important Security Note**: Formbricks handles application-level domain restrictions to ensure each domain can only access specific resources within the application. However, all domain configuration (DNS, SSL certificates, reverse proxy setup) and firewall configuration that comes with this feature to enhance security must be configured by you. Formbricks does not manage your infrastructure-level security settings. ### Example Configuration ```bash theme={null} # Private admin domain WEBAPP_URL=https://admin.formbricks.example.com NEXTAUTH_URL=https://admin.formbricks.example.com # Public domain for surveys PUBLIC_URL=https://surveys.example.com ``` ### How It Works When PUBLIC\_URL is configured: * **Admin Interface**: All admin/management functions are served from `WEBAPP_URL` * **MCP and OAuth**: MCP (`/api/mcp`) and Better Auth OAuth/OIDC discovery and token endpoints are served from `WEBAPP_URL` * **Public Surveys**: All survey links and public APIs use `PUBLIC_URL` * **SDK Integration**: The public domain is used for all client-side SDK connections * **Email Links**: Survey invitation emails use the public domain * **Sharing Features**: Survey sharing uses the public domain ### Routes Available on the Public Domain When PUBLIC\_URL is configured, the following routes are automatically served from the public domain: #### Survey Routes * `/s/{surveyId}` - Individual survey access * `/c/{jwt}` - Personalized link survey access (JWT-based access) * `/p/{survey-slug}` - Pretty URL survey access * Embedded survey endpoints #### API Routes * `/api/v1/client/{workspaceId}/*` - Client API endpoints (v1) * `/api/v2/client/{workspaceId}/*` - Client API endpoints (v2) #### Static Assets & Next.js Routes * `/favicon.ico` - Favicon * `/_next/*` - Next.js static assets and build files * `/js/*` - JavaScript files * `/css/*` - CSS stylesheets * `/images/*` - Image assets * `/fonts/*` - Font files * `/icons/*` - Icon assets * `/public/*` - Public static files * `/animated-bgs/*` - Animated Background assets #### Storage Routes * `/storage/{workspaceId}/{accessType}/{fileName}` - Public file storage access #### Health & Monitoring * `/health` - Health check endpoint #### Sharing & Embed Routes * Survey sharing endpoints * Embed widget routes * Social sharing endpoints All other routes are restricted to the private domain (`WEBAPP_URL`) for security purposes. The admin domain can access the health endpoint (/health) and public file storage (`/storage/{workspaceId}/{accessType}/{fileName}`), while all other public routes are exclusively accessible on the public domain (`PUBLIC_URL`) and will return a 404 error if accessed from the admin domain. ### App URL SDK Configuration #### Multi-Domain Setup SDK Configuration When using PUBLIC\_URL (multi-domain setup), all SDK configurations must use the **public domain**: ```js theme={null} Formbricks.setup({ workspaceId: "your-workspace-id", appUrl: "https://surveys.example.com", // Use PUBLIC_URL domain }); ``` Please refer to the [Framework Guides](/docs/surveys/website-app-surveys/framework-guides) for step-by-step instructions specific to your platform. ### DNS and SSL Configuration 1. **Set up DNS records** for both domains 2. **Configure SSL certificates** for both domains 3. **Update your reverse proxy** (nginx, Apache, Cloudflare, etc.) to route both domains to your Formbricks instance 4. **Firewall rules**: Ensure both domains can reach your Formbricks instance ### Fallback Behavior If `PUBLIC_URL` is not set: * All content (admin and public) is served from `WEBAPP_URL` * The system behaves as a single domain setup * No domain separation occurs ### Reverting to a Single Domain While it's possible to revert to a Single Domain setup, please take into account the following: 1. All image links and uploaded files are stored in the database, so they will continue to link to the Public Domain **hence the links will break.** You'll need to reupload the images, like you did when you set up the domain split. 2. Any survey link shared to with the Public Domain **will break.** 3. API calls to the Public Domain **will break**, unless updated. As of now, Formbricks does not provide an automated migration between domain setups. If you have any questions or require help, feel free to reach out to us on [GitHub Discussions](https://github.com/formbricks/formbricks/discussions). # Environment Variables Source: https://formbricks.com/docs/self-hosting/configuration/environment-variables Environment variables for Formbricks #### Custom Configurations These variables are present inside your machine's docker-compose file. Restart the docker containers if you change any variables for them to take effect. Upgrading from Formbricks 4.x to 5.0? Read the [migration guide](/docs/self-hosting/advanced/migration#v5) first. Formbricks v5 makes Hub part of the standard self-hosted runtime and changes how rate limiting is enforced. For `AI_PROVIDER=google`, use a Gemini model ID such as `gemini-3.5-flash` together with Google Cloud credentials. `gemini-3.5-flash` must use `AI_GOOGLE_CLOUD_LOCATION=global`, `us`, or `eu`; keep regional locations such as `europe-west3` or `me-central2` only for models Google lists as supported there, such as `gemini-2.5-flash`. Formbricks uses Google Cloud naming here, even though the underlying SDK still talks to Vertex AI endpoints for Gemini model access. For `AI_PROVIDER=openai-compatible`, the LLM GA v1 self-hosted path is Qwen served by vLLM through an OpenAI-compatible `/v1` endpoint. Docker Compose users can enable the bundled Qwen/vLLM service with `COMPOSE_PROFILES=qwen`, or point `AI_OPENAI_COMPATIBLE_BASE_URL` at their own endpoint. Set only the variables for the provider you use; unused provider variables can be omitted. | Variable | Description | Required | Default | | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | WEBAPP\_URL | Base URL of the private/admin Formbricks app. OAuth and MCP protected-resource URLs are derived from this value. | required | [http://localhost:3000](http://localhost:3000) | | PUBLIC\_URL | Base URL for the public domain where surveys and public-facing content are served. If not set, uses WEBAPP\_URL. OAuth and MCP do not use PUBLIC\_URL. | optional | WEBAPP\_URL | | NEXTAUTH\_URL | Legacy-compatible auth base URL. This should normally be the same as WEBAPP\_URL; Formbricks derives the Better Auth issuer under `/api/auth`. | required | [http://localhost:3000](http://localhost:3000) | | BETTER\_AUTH\_URL | Optional Better Auth base URL override. Set this only when the auth server base differs from NEXTAUTH\_URL; preserve any custom subpath. | optional | NEXTAUTH\_URL | | DATABASE\_URL | Database URL with credentials. | required | | | NEXTAUTH\_SECRET | Secret for NextAuth, used for session signing and encryption. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) | | BETTER\_AUTH\_SECRET | Optional Better Auth secret override. If unset, Formbricks uses NEXTAUTH\_SECRET for Better Auth session signing. | optional | NEXTAUTH\_SECRET | | ENCRYPTION\_KEY | Secret used by Formbricks for data encryption and audit log hashing. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) | | CRON\_SECRET | API Secret for running cron jobs. | required | (Generated by the user, must not exceed 32 bytes, `openssl rand -hex 32`) | | LOG\_LEVEL | Minimum log level (debug, info, warn, error, fatal) | optional | info | | S3\_ACCESS\_KEY | Access key for S3. | optional | (resolved by the AWS SDK) | | S3\_SECRET\_KEY | Secret key for S3. | optional | (resolved by the AWS SDK) | | S3\_REGION | Region for S3. | optional | (resolved by the AWS SDK) | | S3\_BUCKET\_NAME | S3 bucket name for data storage. Formbricks enables S3 storage when this is set. | optional (required if S3 is enabled) | | | S3\_ENDPOINT\_URL | Endpoint for S3. | optional | (resolved by the AWS SDK) | | S3\_FORCE\_PATH\_STYLE | Set to `1` to force path-style S3 URLs. Required for S3-compatible storage (MinIO, RustFS, LocalStack). Leave unset or `0` for standard AWS S3. | optional | 0 | | SAML\_DATABASE\_URL | Database URL for SAML. | optional | postgres\://postgres:@localhost:5432/formbricks-saml | | PRIVACY\_URL | URL for privacy policy. | optional | | | TERMS\_URL | URL for terms of service. | optional | | | IMPRINT\_URL | URL for imprint. | optional | | | IMPRINT\_ADDRESS | Address for imprint. | optional | | | EMAIL\_AUTH\_DISABLED | Disables the ability for users to signup or login via email and password if set to 1. | optional | | | PASSWORD\_RESET\_DISABLED | Disables password reset functionality if set to 1. | optional | | | PASSWORD\_HIBP\_CHECK\_DISABLED | Disables the Have-I-Been-Pwned breach check on password set (signup / reset) if set to 1. The check calls api.pwnedpasswords.com and fails open on network errors; set this on air-gapped / closed-network deployments to skip the outbound call. | optional | | | PASSWORD\_RESET\_TOKEN\_LIFETIME\_MINUTES | Configures how long password reset links remain valid in minutes. Accepted values are integers from 5 to 120. | optional | 30 | | EMAIL\_VERIFICATION\_DISABLED | Disables email verification if set to 1. | optional | | | RATE\_LIMITING\_DISABLED | Disables only the application-level rate limiter if set to 1. It does not disable Envoy or an equivalent edge rate limiter. | optional | | | TELEMETRY\_DISABLED | Disables telemetry reporting if set to 1. Ignored when an Enterprise License is active. | optional | | | DANGEROUSLY\_ALLOW\_WEBHOOK\_INTERNAL\_URLS | Allows webhook URLs to point to internal/private network addresses (e.g. localhost, 192.168.x.x) if set to 1. Useful for self-hosted instances that need to send webhooks to internal services. | optional | | | INVITE\_DISABLED | Disables the ability for invited users to create an account if set to 1. | optional | | | MAIL\_FROM | Email address to send emails from. | optional (required if email services are to be enabled) | | | MAIL\_FROM\_NAME | Email name/title to send emails from. | optional (required if email services are to be enabled) | | | SMTP\_HOST | Host URL of your SMTP server. | optional (required if email services are to be enabled) | | | SMTP\_PORT | Host Port of your SMTP server. | optional (required if email services are to be enabled) | | | SMTP\_USER | Username for your SMTP Server. | optional (required if email services are to be enabled) | | | SMTP\_PASSWORD | Password for your SMTP Server. | optional (required if email services are to be enabled) | | | SMTP\_AUTHENTICATED | If set to 0, the server will not require SMTP\_USER and SMTP\_PASSWORD(default is 1) | optional | | | SMTP\_SECURE\_ENABLED | SMTP secure connection. For using TLS, set to 1 else to 0. | optional (required if email services are to be enabled) | | | SMTP\_REJECT\_UNAUTHORIZED\_TLS | If set to 0, the server will accept connections without requiring authorization from the list of supplied CAs. | optional | 1 | | TURNSTILE\_SITE\_KEY | Site key for Turnstile. | optional | | | TURNSTILE\_SECRET\_KEY | Secret key for Turnstile. | optional | | | RECAPTCHA\_SITE\_KEY | Site key for survey responses recaptcha bot protection | optional | | | RECAPTCHA\_SECRET\_KEY | Secret key for recaptcha bot protection. | optional | | | GITHUB\_ID | Client ID for GitHub. | optional (required if GitHub auth is enabled) | | | GITHUB\_SECRET | Secret for GitHub. | optional (required if GitHub auth is enabled) | | | GOOGLE\_CLIENT\_ID | Client ID for Google. | optional (required if Google auth is enabled) | | | GOOGLE\_CLIENT\_SECRET | Secret for Google. | optional (required if Google auth is enabled) | | | AI\_PROVIDER | Instance-level AI provider used in the background. Supported values: `aws`, `google`, `azure`, `openai-compatible`. | optional (required if AI is enabled) | | | AI\_MODEL | Instance-level AI model or deployment name used by the active provider. | optional (required if `AI_PROVIDER` is set) | | | AI\_GOOGLE\_CLOUD\_PROJECT | Google Cloud project ID for the `google` AI provider. | optional (required if `AI_PROVIDER=google`) | | | AI\_GOOGLE\_CLOUD\_LOCATION | Google Cloud location for `google` AI requests. For `gemini-3.5-flash`, use `global`, `us`, or `eu`. | optional (required if `AI_PROVIDER=google`) | | | AI\_GOOGLE\_CLOUD\_CREDENTIALS\_JSON | Optional service account credentials JSON override for the `google` AI provider. Omit when Application Default Credentials are available. | optional | | | AI\_GOOGLE\_CLOUD\_APPLICATION\_CREDENTIALS | Optional path to Google Application Default Credentials used by the `google` AI provider. | optional | | | AI\_AWS\_REGION | AWS region for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | | | AI\_AWS\_ACCESS\_KEY\_ID | AWS access key ID for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | | | AI\_AWS\_SECRET\_ACCESS\_KEY | AWS secret access key for Amazon Bedrock. | optional (required if `AI_PROVIDER=aws`) | | | AI\_AWS\_SESSION\_TOKEN | AWS session token for Amazon Bedrock temporary credentials. | optional | | | AI\_AZURE\_BASE\_URL | Azure OpenAI / Foundry base URL. When set, this is preferred over `AI_AZURE_RESOURCE_NAME`. | optional (one of this or `AI_AZURE_RESOURCE_NAME` required if `AI_PROVIDER=azure`) | | | AI\_AZURE\_RESOURCE\_NAME | Azure resource name used to assemble the Azure OpenAI URL. | optional (one of this or `AI_AZURE_BASE_URL` required if `AI_PROVIDER=azure`) | | | AI\_AZURE\_API\_KEY | API key for Azure OpenAI / Foundry. | optional (required if `AI_PROVIDER=azure`) | | | AI\_AZURE\_API\_VERSION | Azure API version for OpenAI-compatible calls. | optional | v1 | | AI\_OPENAI\_COMPATIBLE\_BASE\_URL | Base URL for a Qwen/vLLM OpenAI-compatible `/v1` endpoint, e.g. `http://vllm:8000/v1`. | optional (required if `AI_PROVIDER=openai-compatible`) | | | AI\_OPENAI\_COMPATIBLE\_API\_KEY | Optional API key for the OpenAI-compatible endpoint. | optional | | | AI\_OPENAI\_COMPATIBLE\_PROVIDER\_NAME | Provider name passed to the OpenAI-compatible adapter. Use `vllm` for the supported Qwen/vLLM self-hosted path. | optional | openai-compatible | | AI\_OPENAI\_COMPATIBLE\_SUPPORTS\_STRUCTURED\_OUTPUTS | Set to `1` when the Qwen/vLLM endpoint supports structured outputs. | optional | 0 | | AI\_OPENAI\_COMPATIBLE\_HEADERS\_JSON | Optional JSON object of string-valued headers to send with OpenAI-compatible requests. | optional | | | AI\_OPENAI\_COMPATIBLE\_QUERY\_PARAMS\_JSON | Optional JSON object of string-valued query parameters to send with OpenAI-compatible requests. | optional | | | STRIPE\_SECRET\_KEY | Secret key for Stripe integration. | optional | | | STRIPE\_WEBHOOK\_SECRET | Webhook secret for Stripe integration. | optional | | | DEFAULT\_BRAND\_COLOR | Default brand color for your app (Can be overwritten from the UI as well). | optional | #64748b | | DEFAULT\_ORGANIZATION\_ID | Automatically assign new users to a specific organization when joining | optional | | | OIDC\_DISPLAY\_NAME | Display name for Custom OpenID Connect Provider | optional | | | OIDC\_CLIENT\_ID | Client ID for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | | | OIDC\_CLIENT\_SECRET | Secret for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | | | OIDC\_ISSUER | Issuer URL for Custom OpenID Connect Provider (should have .well-known configured at this) | optional (required if OIDC auth is enabled) | | | OIDC\_SIGNING\_ALGORITHM | Signing Algorithm for Custom OpenID Connect Provider | optional | RS256 | | OTEL\_EXPORTER\_OTLP\_ENDPOINT | Base OTLP HTTP endpoint for traces and metrics export (e.g. [http://collector:4318](http://collector:4318)). | optional | | | OTEL\_EXPORTER\_OTLP\_PROTOCOL | OTLP protocol to use for export. | optional | http/protobuf | | OTEL\_SERVICE\_NAME | Service name reported in OpenTelemetry resource attributes. | optional | formbricks | | OTEL\_RESOURCE\_ATTRIBUTES | Comma-separated resource attributes in OTel format (`key=value,key2=value2`). | optional | | | OTEL\_TRACES\_SAMPLER | Trace sampler strategy (`always_on`, `always_off`, `traceidratio`, `parentbased_traceidratio`). | optional | always\_on | | OTEL\_TRACES\_SAMPLER\_ARG | Sampling argument used by ratio-based samplers (`0` to `1`). | optional | | | PROMETHEUS\_ENABLED | Enables Prometheus metrics if set to 1. | optional | | | PROMETHEUS\_EXPORTER\_PORT | Port for Prometheus metrics. | optional | 9090 | | AUTH\_SSO\_DEFAULT\_TEAM\_ID | ID of the team that new SSO users are automatically added to. The owning organization is derived from this team. Must be set together with `AUTH_SKIP_INVITE_FOR_SSO=1` for auto-provisioning to work. | optional | | | AUTH\_SKIP\_INVITE\_FOR\_SSO | Set to `1` to allow SSO users to create an account without a manual invite. Keep unset (or `0`) for stricter access control where only invited users can join. | optional | 0 | | HTTP\_PROXY | HTTP proxy URL used for outbound requests (e.g., license checks). When both are set, `HTTPS_PROXY` takes precedence. | optional | | | HTTPS\_PROXY | HTTPS proxy URL used for outbound requests. Takes precedence over `HTTP_PROXY`. | optional | | | SENTRY\_DSN | Set this to track errors and monitor performance in Sentry. | optional | | | SENTRY\_ENVIRONMENT | Set this to identify the environment in Sentry | optional | | | SENTRY\_AUTH\_TOKEN | Set this if you want to make errors more readable in Sentry. | optional | | | SESSION\_MAX\_AGE | Configure the maximum age for the session in seconds. | optional | 86400 (24 hours) | | USER\_MANAGEMENT\_MINIMUM\_ROLE | Set this to control which roles can access user management features. Accepted values: "owner", "manager", "disabled" | optional | manager | | REDIS\_URL | Redis URL for caching, rate limiting, audit logging, and the shared BullMQ background-job queue. All web and worker replicas must share this Redis instance. Application will not start without this. | required | redis\://localhost:6379 | | BULLMQ\_WORKER\_ENABLED | Set to `1` to start BullMQ workers inside each Formbricks web process. Set to `0` only in a custom deployment that already runs an external consumer with the complete Formbricks application job handlers. | optional | 1 outside test environments | | BULLMQ\_EXTERNAL\_WORKER\_ENABLED | Custom integrations only: set to `1` on web/API processes that only enqueue jobs while an external consumer with the complete Formbricks application job handlers processes them. This flag only keeps queueing enabled; it does not provide or start a worker. | optional | 0 | | BULLMQ\_WORKER\_COUNT | Number of BullMQ worker instances to start in each Formbricks web process. Must be a positive integer. | optional | 1 | | BULLMQ\_WORKER\_CONCURRENCY | Number of jobs each in-process BullMQ worker can process concurrently. Must be a positive integer. | optional | 1 | | AUDIT\_LOG\_ENABLED | Set this to 1 to enable audit logging. Requires Redis to be configured with the REDIS\_URL env variable. | optional | 0 | | AUDIT\_LOG\_GET\_USER\_IP | Set to 1 to include user IP addresses in audit logs from request headers | optional | 0 | See [Job Runner](/docs/self-hosting/configuration/job-runner) for BullMQ architecture, scaling, and troubleshooting guidance. #### OAuth and MCP URLs The MCP server is available on the private/admin app at `/api/mcp`. OAuth discovery and token issuance are also private/admin app surfaces: ```text theme={null} {WEBAPP_URL}/api/mcp {WEBAPP_URL}/.well-known/oauth-protected-resource/api/mcp {WEBAPP_URL}/.well-known/oauth-authorization-server/api/auth ``` If you use a custom subpath, include that subpath in `WEBAPP_URL`, `NEXTAUTH_URL`, and `BETTER_AUTH_URL` if set. Do not point MCP or OAuth clients at `PUBLIC_URL`; that domain is reserved for public survey delivery and SDK traffic. #### Formbricks Hub Starting with Formbricks v5, Hub is part of the standard self-hosted runtime. When you run Formbricks with the bundled Docker Compose or Helm assets, the following variables apply: | Variable | Description | Required | Default | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------- | | HUB\_API\_KEY | API key used by the Formbricks Hub API. Generate a strong secret and use the same value wherever your deployment supplies Hub auth configuration. | required | (e.g. `openssl rand -hex 32`) | | HUB\_API\_URL | Base URL the Formbricks app uses to call Hub. With the bundled Docker stack, keep this at `http://hub:8080` unless Hub runs elsewhere. | required | `http://hub:8080` (bundled Docker), `http://localhost:8080` (local dev) | | HUB\_DATABASE\_URL | PostgreSQL connection URL for Hub. Omit to use the same database as Formbricks. | optional | Same as Formbricks `DATABASE_URL` (shared database) | #### Cube Analytics Cube is part of the baseline Formbricks v5 stack and is required. Formbricks generates the backend Cube JWT from `CUBEJS_API_SECRET`, so `CUBEJS_API_TOKEN` is not part of the supported setup contract. | Variable | Description | Required | Default | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------- | | CUBEJS\_API\_URL | Base URL the Formbricks app uses to call Cube. Local dev (app on host): `http://localhost:4000`. Docker/container: `http://cube:4000` (service name). | required | | | CUBEJS\_API\_SECRET | Shared secret Formbricks uses to sign Cube API JWTs. Generate with `openssl rand -hex 32`. | required | | | CUBEJS\_JWT\_ISSUER | JWT issuer expected by Cube and used by Formbricks when signing per-request Cube tokens. | optional | `formbricks-web` | | CUBEJS\_JWT\_AUDIENCE | JWT audience expected by Cube and used by Formbricks when signing per-request Cube tokens. | optional | `formbricks-cube` | | CUBEJS\_DB\_HOST | Database host for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment | | CUBEJS\_DB\_PORT | Database port for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment | | CUBEJS\_DB\_NAME | Database name for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment | | CUBEJS\_DB\_USER | Database user for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment | | CUBEJS\_DB\_PASS | Database password for the Cube service. Only needed when you run Cube yourself and override defaults. | optional | Depends on your Cube deployment | The bundled Docker Compose Cube service sets `CUBEJS_DEFAULT_API_SCOPES=meta,data` directly on the Cube container. If you run Cube outside the bundled Compose stack, configure the equivalent Cube service environment there rather than adding it to the Formbricks app environment. For Helm deployments, the chart deploys Cube by default (`cube.enabled: true`). To use an external Cube cluster instead, set `cube.enabled: false`, point `CUBEJS_API_URL` at your endpoint, and supply `CUBEJS_API_SECRET` through your existing secret management setup. Note: If you want to configure something that is not possible via above, please open an issue on our GitHub repo here or reach out to us on Github Discussions and we'll try our best to work out a solution with you. # File Uploads Configuration Source: https://formbricks.com/docs/self-hosting/configuration/file-uploads Configure file storage for survey images, file uploads, and Workspace assets in your self-hosted Formbricks instance Formbricks requires S3-compatible storage for file uploads. You can use external cloud storage services or the bundled RustFS option for a self-hosted solution. ## Why Configure File Uploads? Setting up file storage enables important features in Formbricks, including: * Adding images to surveys (questions, backgrounds, logos) * 'File Upload' and 'Picture Selection' question types * Workspace logos and branding * Custom organization logos in emails * Survey background images from uploads If file uploads are not configured, the above features will be disabled and users won't be able to upload files or images. ## Storage Options Formbricks supports S3-compatible storage with two main configurations: ### 1. External S3-Compatible Storage Use cloud storage services for production deployments: * **AWS S3** (Amazon Web Services) * **DigitalOcean Spaces** * **Wasabi** * **StorJ** * Any S3-compatible storage service ### 2. Bundled RustFS Storage (Self-Hosted) **Important**: Bundled RustFS requires a dedicated subdomain. You must configure a subdomain like `files.yourdomain.com` that points to your server so browser uploads can reach the object storage endpoint. Bundled RustFS is a convenience-oriented single-server option. It fits small-scale or lower-complexity self-hosted deployments, but it is not the ideal RustFS architecture for high-availability or larger-scale production storage. For stricter production requirements, prefer external object storage or a dedicated RustFS deployment. RustFS provides a self-hosted S3-compatible storage solution that runs alongside Formbricks. This option: * Runs in a Docker container alongside Formbricks * Provides full S3 API compatibility * Uses the same `S3_*` environment variables as any other S3-compatible provider ## Configuration Methods ### Option 1: One-Click Setup Script When using the Formbricks installation script, you'll be prompted to configure file uploads: ```bash theme={null} 📁 Do you want to configure file uploads? If you skip this, the following features will be disabled: - Adding images to surveys (e.g., in questions or as background) - 'File Upload' and 'Picture Selection' question types - Workspace logos - Custom organization logo in emails Configure file uploads now? [Y/n] y ``` #### External S3-Compatible Storage Choose this option for AWS S3, DigitalOcean Spaces, or other cloud providers: ```bash theme={null} 🗄️ Do you want to use an external S3-compatible storage (AWS S3/DO Spaces/etc.)? [y/N] y 🔧 Enter S3 configuration (leave Endpoint empty for AWS S3): S3 Access Key: your_access_key S3 Secret Key: your_secret_key S3 Region (e.g., us-east-1): us-east-1 S3 Bucket Name: your-bucket-name S3 Endpoint URL (leave empty if you are using AWS S3): https://your-endpoint.com ``` #### Bundled RustFS Storage Choose this option for a self-hosted S3-compatible storage that runs alongside Formbricks: **Critical Requirement**: Before proceeding, ensure you have configured a subdomain (e.g., `files.yourdomain.com`) that points to your server's IP address. This is required so browser-direct uploads can reach RustFS. ```bash theme={null} 🗄️ Do you want to use an external S3-compatible storage (AWS S3/DO Spaces/etc.)? [y/N] n 🔗 Enter the files subdomain for object storage (e.g., files.yourdomain.com): files.yourdomain.com ``` The script will automatically: * Generate separate RustFS admin and Formbricks service credentials * Create the storage bucket * Configure SSL certificates for the files subdomain * Configure Traefik routing for the subdomain * Store the generated RustFS credentials in `./formbricks/.env` with restricted permissions ### Option 2: Manual Environment Variables Add the following environment variables to your `docker-compose.yml` or `.env` file: #### For S3-Compatible Storage ```bash theme={null} # S3 Storage Configuration S3_ACCESS_KEY=your_access_key S3_SECRET_KEY=your_secret_key S3_REGION=us-east-1 S3_BUCKET_NAME=your-bucket-name # Optional: For third-party S3-compatible services (leave empty for AWS S3) S3_ENDPOINT_URL=https://your-endpoint.com # Enable path-style URLs for third-party services (1 for enabled, 0 for disabled) S3_FORCE_PATH_STYLE=1 ``` AWS S3 vs. third‑party S3: When using AWS S3 directly, leave `S3_ENDPOINT_URL` unset and set `S3_FORCE_PATH_STYLE=0` (or omit). For most third‑party S3‑compatible providers (e.g., RustFS, DigitalOcean Spaces, Wasabi, Storj), you typically must set `S3_ENDPOINT_URL` to the provider's endpoint and set `S3_FORCE_PATH_STYLE=1`. ## Provider-Specific Examples ### AWS S3 ```bash theme={null} S3_ACCESS_KEY=AKIA1234567890EXAMPLE S3_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY S3_REGION=us-east-1 S3_BUCKET_NAME=my-formbricks-uploads # S3_ENDPOINT_URL is not needed for AWS S3 # S3_FORCE_PATH_STYLE=0 ``` ### DigitalOcean Spaces ```bash theme={null} S3_ACCESS_KEY=your_spaces_key S3_SECRET_KEY=your_spaces_secret S3_REGION=nyc3 S3_BUCKET_NAME=my-formbricks-space S3_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com S3_FORCE_PATH_STYLE=1 ``` ### RustFS (Self-Hosted) ```bash theme={null} S3_ACCESS_KEY=rustfs_service_access_key S3_SECRET_KEY=rustfs_service_secret_key S3_REGION=us-east-1 S3_BUCKET_NAME=formbricks-uploads S3_ENDPOINT_URL=https://files.yourdomain.com S3_FORCE_PATH_STYLE=1 ``` ### Compatibility requirement: S3 POST Object support Formbricks uses the S3 [POST Object](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html) operation (presigned POST) for uploads. Your object storage provider must support this operation. Providers that do not implement POST Object are not compatible with Formbricks uploads. For example, Backblaze B2's S3‑compatible API currently does not support POST Object and therefore will not work with Formbricks file uploads. ## Bundled RustFS Setup When using the bundled RustFS option through the setup script, you get: ### Automatic Configuration * **Storage Service**: RustFS running in a Docker container * **Credentials**: Auto-generated admin and least-privilege service credentials * **Bucket**: Automatically created `formbricks-uploads` bucket * **SSL**: Automatic certificate generation for the files subdomain ### Access Information After setup, you'll see: ```bash theme={null} 🗄️ RustFS Storage Setup Complete: • Access Key: formbricks-service-a1b2c3d4 • Bucket: formbricks-uploads (✅ automatically created) ``` ### DNS Requirements **Critical for bundled RustFS**: The files subdomain is mandatory. Without proper DNS and reverse-proxy routing, browser uploads will fail. For the bundled RustFS setup, ensure: 1. **Main domain**: `yourdomain.com` points to your server IP 2. **Files subdomain**: `files.yourdomain.com` points to your server IP 3. **Firewall**: Ports 80 and 443 are open in your server's firewall 4. **DNS propagation**: Allow time for DNS changes to propagate globally ## Docker Compose Configuration For manual setup, update your `docker-compose.yml`: ```yaml theme={null} services: formbricks: image: ghcr.io/formbricks/formbricks:latest environment: # ... other environment variables ... # S3 Storage Configuration S3_ACCESS_KEY: your_access_key S3_SECRET_KEY: your_secret_key S3_REGION: us-east-1 S3_BUCKET_NAME: your-bucket-name S3_ENDPOINT_URL: https://your-endpoint.com # Optional S3_FORCE_PATH_STYLE: 1 # For third-party services volumes: - uploads:/home/nextjs/apps/web/uploads/ # Still needed for temporary files ``` ## Security Considerations ### IAM User Permissions When using AWS S3 or S3-compatible storage providers, ensure that the IAM user associated with your `S3_ACCESS_KEY` and `S3_SECRET_KEY` credentials has the necessary permissions to interact with your bucket. Without proper permissions, file uploads and retrievals will fail. The following IAM policy grants the minimum required permissions for Formbricks to function correctly. This policy is also used in the bundled RustFS integration: ```json theme={null} { "Statement": [ { "Action": ["s3:DeleteObject", "s3:GetObject", "s3:PutObject"], "Effect": "Allow", "Resource": ["arn:aws:s3:::your-bucket-name/*"] }, { "Action": ["s3:ListBucket"], "Effect": "Allow", "Resource": ["arn:aws:s3:::your-bucket-name"] } ], "Version": "2012-10-17" } ``` Replace `your-bucket-name` with your actual S3 bucket name. The first statement grants object-level operations (upload, retrieve, and delete files), while the second statement allows listing bucket contents. ### S3 Bucket Policy In addition to IAM user permissions, configure your S3 bucket with a least-privileged bucket policy to ensure security: 1. **Scoped Public Read Access**: Only allow public read access to specific prefixes where needed 2. **Restricted Write Access**: Only your Formbricks instance should be able to upload files 3. **CORS Configuration**: Allow requests from your Formbricks domain Example least-privileged S3 bucket policy: ```json theme={null} { "Statement": [ { "Action": "s3:GetObject", "Effect": "Allow", "Principal": "*", "Resource": "arn:aws:s3:::your-bucket-name/uploads/public/*", "Sid": "PublicReadForPublicUploads" }, { "Action": ["s3:PutObject", "s3:PutObjectAcl"], "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:user/formbricks-service" }, "Resource": "arn:aws:s3:::your-bucket-name/*", "Sid": "AllowFormbricksWrite" } ], "Version": "2012-10-17" } ``` Replace `your-bucket-name` with your actual bucket name and `arn:aws:iam::123456789012:user/formbricks-service` with the ARN of your IAM user. This policy allows public read access only to specific paths while restricting write access to your Formbricks service user. ### S3 CORS Configuration CORS (Cross-Origin Resource Sharing) must be configured on your S3 bucket to allow Formbricks to upload files using presigned POST URLs. Without proper CORS configuration, file uploads from the browser will fail. Configure CORS on your S3 bucket with the following settings: ```json theme={null} [ { "AllowedHeaders": ["*"], "AllowedMethods": ["POST", "GET", "HEAD", "DELETE", "PUT"], "AllowedOrigins": ["*"], "ExposeHeaders": ["ETag", "x-amz-meta-custom-header"], "MaxAgeSeconds": 3000 } ] ``` For production environments, consider restricting `AllowedOrigins` to your specific Formbricks domain(s) instead of using `"*"` for better security. For example: `["https://app.yourdomain.com", "https://yourdomain.com"]`. **How to configure CORS:** * **AWS S3**: Navigate to your bucket → Permissions → Cross-origin resource sharing (CORS) → Edit → Paste the JSON configuration * **DigitalOcean Spaces**: Navigate to your Space → Settings → CORS Configurations → Add CORS configuration → Paste the JSON * **RustFS (self-hosted)**: configured automatically by the bundled setup, or via a server-level env var for standalone deployments — see [RustFS CORS Configuration](#rustfs-cors-configuration) below. * **Other S3-compatible providers**: Refer to your provider's documentation for CORS configuration ### RustFS CORS Configuration RustFS supports CORS two ways: a **server-level** allow-list set via the `RUSTFS_CORS_ALLOWED_ORIGINS` environment variable on the RustFS server, and **per-bucket** CORS via the S3 `PutBucketCors` API (e.g. `mc cors set`). **Bundled Formbricks setup** (the [one-click script](/docs/self-hosting/setup/one-click), driven by `docker/rustfs-init.sh`): CORS is configured for you. The bootstrap applies a per-bucket policy via `mc cors set`, scoped to your Formbricks domain — nothing else to do. **Standalone RustFS** (managed separately from Formbricks): set `RUSTFS_CORS_ALLOWED_ORIGINS` on the RustFS server to the origin(s) you serve Formbricks from — your `WEBAPP_URL` — comma-separated for multiple values: ```env theme={null} # Set on the RustFS server, then restart RustFS RUSTFS_CORS_ALLOWED_ORIGINS=https://app.yourdomain.com ``` When `RUSTFS_CORS_ALLOWED_ORIGINS` is unset, the RustFS server allows **all** origins; a non-empty value is treated as an allow-list. So if browser uploads fail with a CORS error while the file still appears in RustFS, this variable is most likely already set to a value that excludes your Formbricks origin — add your origin to it (or set it to `*` to allow all). Use an explicit origin list in production. For standalone deployments, prefer the server-level `RUSTFS_CORS_ALLOWED_ORIGINS` variable over manually applying a per-bucket CORS policy: RustFS's support for the S3 `PutBucketCors` API has varied across releases and can return "not implemented" on some builds. (The bundled bootstrap pins a RustFS version where per-bucket `mc cors set` works.) ### RustFS Security When using bundled RustFS: * Credentials are auto-generated and secure * Generated RustFS credentials are written to a local `.env` file; keep it private and restrict it to `0600` * Access is restricted through Traefik proxy * CORS is automatically configured * Rate limiting is applied to prevent abuse * A bucket policy with the least privileges is applied to the bucket * Prefer local SSD or NVMe-backed storage for `rustfs-data`, use XFS on dedicated host-managed disks when possible, and avoid NFS or other network filesystems for RustFS data * Back up the `rustfs-data` volume regularly, especially for single-server deployments * Ship RustFS and Formbricks container logs into your normal logging and alerting stack; the bundled setup does not provision centralized audit-log export or alerting for you ## Troubleshooting ### Common Issues **Files not uploading:** 1. Check that S3 credentials are correct 2. Verify bucket exists and is accessible 3. Ensure bucket permissions allow uploads from your server 4. Check network connectivity to S3 endpoint 5. We use S3 presigned URLs for uploads. Make sure your CORS policy allows presigned URL uploads; otherwise, uploads will fail. Some providers (e.g., Hetzner’s object storage) [require a specific CORS configuration](https://github.com/formbricks/formbricks/discussions/6641#discussioncomment-14574048). If you’re using the bundled RustFS setup, this is already configured for you. 6. **The file appears in your storage but Formbricks still reports “upload failed.”** This is the classic CORS symptom — the browser uploaded the file successfully but could not read the response. For RustFS, set `RUSTFS_CORS_ALLOWED_ORIGINS` to include your Formbricks origin (see [RustFS CORS Configuration](#rustfs-cors-configuration)). **Images not displaying in surveys:** 1. Verify bucket has public read access 2. Check CORS configuration allows requests from your domain 3. Ensure S3\_ENDPOINT\_URL is correctly set for third-party services **RustFS not starting:** 1. **Verify subdomain DNS**: Ensure `files.yourdomain.com` points to your server IP (this is the most common issue) 2. **Check DNS propagation**: Use tools like `nslookup` or `dig` to verify DNS resolution 3. **Verify ports**: Ensure ports 80 and 443 are open in your firewall 4. **SSL certificate**: Check that SSL certificate generation completed successfully 5. **Container logs**: Check Docker container logs: `docker compose logs rustfs` ### Testing Your Configuration To test if file uploads are working: 1. **Admin Panel**: Try uploading a Workspace logo in **Settings → Workspace → Appearance** 2. **Survey Editor**: Attempt to add a background image to a survey 3. **Question Types**: Create a 'File Upload' or 'Picture Selection' question 4. **Check Logs**: Monitor container logs for any storage-related errors ```bash theme={null} # Check Formbricks logs docker compose logs formbricks # Check RustFS logs (if using bundled RustFS) docker compose logs rustfs ``` For additional help, join the conversation on [GitHub Discussions](https://github.com/formbricks/formbricks/discussions). # ActivePieces (On Premise) Source: https://formbricks.com/docs/self-hosting/configuration/integrations/activepieces Learn how to configure ActivePieces integration in your survey for self-hosting instance. ## Setting Up ActivePieces with Self-Hosted Formbricks ### Prerequisites * A self-hosted Formbricks instance * Access to an ActivePieces account ### Configuration Steps For general setup instructions, refer to our [comprehensive ActivePieces integration guide](https://formbricks.com/docs/platform/features/integrations/activepieces). #### Important Note for Self-Hosting When following the main guide, pay special attention to **Step 3** where you create a new connection: Instead of using `app.formbricks.com` as your `APP_URL`, you must substitute this with your own self-hosted Formbricks instance URL. ActivePieces Connection Setup ### Verification After configuring the connection: 1. Test the integration by submitting a sample survey 2. Verify that your ActivePieces workflow executes as expected 3. Check the logs if you encounter any issues ## Troubleshooting If you encounter issues with your ActivePieces integration: * Ensure your self-hosted Formbricks instance is accessible from the internet * Verify that you've entered the correct URL for your instance * Check that any necessary API keys are properly configured # Airtable (On Premise) Source: https://formbricks.com/docs/self-hosting/configuration/integrations/airtable Instantly populate your Airtable table with survey data The Airtable integration allows you to automatically send responses to an Airtable of your choice. If you are using Formbricks Cloud, you will need to configure this integration differently. Please follow this guide [here](/docs/platform/features/integrations/airtable) to set it up. ## Setup Enabling the Airtable Integration in a self-hosted environment requires creating an airtable account and changing the environment variables of your Formbricks instance. If your Airtable app is in development use, please ensure that the email used in the Airtable integration matches the email used in the Airtable OAuth app. To use other email addresses, you need to fill this information in your OAuth app settings again: ![airtable img](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738251900/image_eoaxg3.jpg) * Go to the [Airtable](https://airtable.com) and create a new account if you dont already have one. * Click on user icon on top left and open to **Developer hub** ![opendevhub](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252268/image_l3w3an.jpg) * Navigate to OAuth integrations and click on **Register an OAuth integrations** ![new](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252316/image_xmqyee.jpg) * Select a name for your integration and also add a redirect URL which will be `/api/v1/integrations/airtable/callback` ![air](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252498/image_ufbmot.jpg) * . Now fill some basic details about your integrations and then go to scope section. You need to **enable** 5 scopes: * data.records:read * data.records:write * schema.bases:read * schema.bases:write * user.email:read ![tables](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252641/image_veqzki.jpg) * Click on the "Save" button and you are done * Now just copy **Client ID** for your integration & add it to your **Formbricks environment variables** as in the docker compose file: * `AIRTABLE_CLIENT_ID` Voila! You have successfully enabled the Airtable integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in [Airtable Integration with Formbricks](/docs/platform/features/integrations/airtable) section to link an Airtable with Formbricks. Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Google Sheets (On Premise) Source: https://formbricks.com/docs/self-hosting/configuration/integrations/google-sheets Instantly populate your Google Sheet table with survey data The Google Sheets integration allows you to automatically send responses to an Google Sheets of your choice. If you are using Formbricks Cloud, you will need to configure this integration differently. Please follow this guide [here](/docs/platform/features/integrations/google-sheets) to set it up. ## Setup Integrating Google Sheets with a self-hosted Formbricks instance requires configuring Google Cloud and updating your environment variables. If your Google Cloud Console project has a publishing status of "Testing" your refresh token will expire after 7 days. To avoid that, you need to change the publishing status to "Production". [Google OAuth 2.0 documentation](https://developers.google.com/identity/protocols/oauth2?hl=en#expiration) * Go to the [Google Cloud Console](https://console.cloud.google.com/) and **create a new project**. * Enable necessary APIs: * Now select the project you just created and go to the **APIs & Services** section. * Click on the **Enable APIs and Services** button and search for **Google Sheets API** and enable it. * Configure OAuth Consent Screen: * Go to **OAuth Consent screen** and select the appropriate User Type (External or Internal). Select **Internal** if you want only the users of your Google Workspace to be able to use the integration. * Fill the required details: * App name: Name displayed during OAuth authentication. * User support email and Developer contact information: Your contact details for support. * Click on **Save and Continue**. * Add required Scopes: * Click on the **Add or Remove Scopes** button and add the scopes: * [`https://www.googleapis.com/auth/userinfo.email`](https://www.googleapis.com/auth/userinfo.email) * [`https://www.googleapis.com/auth/spreadsheets`](https://www.googleapis.com/auth/spreadsheets) * Click on the **Update** button. Verify the scopes and click on the **Save and Continue** button. * Skip the **Test Users** section and click on the **Save and Continue** button. * View the OAuth Consent Screen summary and click on the **Back to Dashboard** button. * Register OAuth Client: * Navigate to **Credentials** > **Create Credentials** > **OAuth Client ID**. * Select **Web Application** and set: * Name: Name of the OAuth Client ID. * Authorized JavaScript Origins: `https://` * Authorized redirect URIs: `https:///api/google-sheet/callback` * Save and note the Client ID and Client Secret. * Copy the Client ID and Client Secret and set them as environment variables in your Formbricks instance: * `GOOGLE_SHEETS_CLIENT_ID` * `GOOGLE_SHEETS_CLIENT_SECRET` * `GOOGLE_SHEETS_REDIRECT_URL` Now just copy **GOOGLE\_SHEETS\_CLIENT\_ID**, **GOOGLE\_SHEETS\_CLIENT\_SECRET** and **GOOGLE\_SHEETS\_REDIRECT\_URL** for your integration & add it to your **Formbricks environment variables** as in the docker compose file: * `GOOGLE_SHEETS_CLIENT_ID` * `GOOGLE_SHEETS_CLIENT_SECRET` * `GOOGLE_SHEETS_REDIRECT_URL` Voila! You have successfully enabled the Google Sheets integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in [Google Sheets Integration with Formbricks](/docs/platform/features/integrations/google-sheets) section to link a Google Sheet with Formbricks. Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # n8n (On Premise) Source: https://formbricks.com/docs/self-hosting/configuration/integrations/n8n Set up automations with your self-hosted n8n instance. The n8n integration allows you to create automations based on survey responses. If you are using Formbricks Cloud, you will need to configure this integration differently. Please follow this guide [here](/docs/platform/features/integrations/n8n) to set it up. ## Setup Enabling the n8n integration in a self-hosted environment requires a new workflow setup using n8n and changing the environment variables of your Formbricks instance. * Setup your n8n workflow: Go to [n8n.io](https://n8n.io) and create a new workflow. Search for “Formbricks” to get started: ![n8n](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252710/image_rp86wt.jpg) * **Connect Formbricks with n8n**: Now, you have to connect n8n with Formbricks via an API Key: ![CreateNewCredentialBtn](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252823/image_kyyvur.jpg) * Click on Create New Credentail button to add your host and API Key ![AddAPIKey](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252907/image_a1fmyc.jpg) Now you need an API key. Please refer to the [API Key Setup](/docs/api-reference/rest-api#how-to-generate-an-api-key) page to learn how to create one. Once you copied it in the API Key field, hit Save button to test the connection and save the credentials. ![connectiontest](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738252980/image_asox5o.jpg) * Select Event: Next, you can choose the event you want to trigger the node on. You can select multiple events: ![select event](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253186/image_xrjjrh.jpg) Here, we are adding `Response Finished` as an event, which will trigger when the survey has been filled out. * Select Survey: Next, you can choose from all the surveys you have created in this environment. You can select multiple surveys: ![select survey](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253219/image_hbubu7.jpg) Here, we are selecting two surveys. ![two survey](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253296/image_lbb4hw.jpg) * Test your trigger: In order to set up n8n you'll need a test response in the selected survey. This allows you to select the individual values of each response in your workflow. ![test trigger](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253356/image_ogjya8.jpg) Next, click on Listen for event button. ![listen](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253388/image_pzerrp.jpg) Then, go to the survey which you selected. Fill it out, and wait for the particular event to trigger (in this case it's `Response Finished`). Once the event is triggered you will see the response that you filled out in the survey. ![response](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253435/image_c0hyo9.jpg) Now you have all the data you need at hand. The next steps depend on what you want to do with it. In this tutorial, we will send submissions to a discord channel: 1. Add Discord to your workflow: Click on the plus and search `Discord`. ![discord](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253513/image_hwld7h.jpg) Fill in the `Webhook URL` and the `Content` that you want to receive in the respective discord channel. Next, click on `Execute Node` button to test the node. ![webhook](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253594/image_avxxkl.jpg) Once the execution is successful, you'll receive the content in the discord channel. ![success](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253753/image_qlxvpv.jpg) Voila! You have successfully enabled the n8n integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in the [Formbricks Integrations](/docs/platform/features/integrations/n8n) section to know more about the capabilities with Formbricks with n8n. Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Notion (On Premise) Source: https://formbricks.com/docs/self-hosting/configuration/integrations/notion Instantly populate a Notion database with survey data The Notion integration allows you to automatically send responses to a Notion database of your choice. If you are using Formbricks Cloud, you will need to configure this integration differently. Please follow this guide [here](/docs/platform/features/integrations/notion) to set it up. ## Setup Enabling the Notion Integration in a self-hosted environment requires a setup using Notion account and changing the environment variables of your Formbricks instance. * Sign up for a [Notion](https://www.notion.so/) account, if you don't have one already. * Go to the [my integrations](https://www.notion.so/my-integrations) page and click on **New integration**. * Fill up the basic information like **Name**, **Logo** and click on **Submit**. * Now, click on **Distribution** tab on the sidebar. A text will appear which will ask you to make the integration public. Click on that toggle button. A form will appear below the text. * Now provide it the details such as requested. Under **Redirect URIs** field: * If you are running formbricks locally, you can enter `http://localhost:3000/api/v1/integrations/notion/callback`. * Or, you can enter `https:///api/v1/integrations/notion/callback` * Once you've filled all the necessary details, click on **Submit**. * Now just copy from the screen the **Client ID** and **Client secret** for your integration & add it to your **Formbricks environment variables** as in the docker compose file: * `NOTION_OAUTH_CLIENT_ID` - OAuth Client ID * `NOTION_OAUTH_CLIENT_SECRET` - OAuth Client Secret Voila! You have successfully enabled the Notion integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in [Notion Integration with Formbricks](/docs/platform/features/integrations/notion) section to link your Notion with Formbricks. Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Slack (On Premise) Source: https://formbricks.com/docs/self-hosting/configuration/integrations/slack Send survey responses automatically to Slack workspace. Send survey responses automatically to Slack workspace. If you are using Formbricks Cloud, you will need to configure this integration differently. Please follow this guide [here](/docs/platform/features/integrations/slack) to set it up. ## Setup Enabling the Slack Integration in a self-hosted environment requires a setup using slack workspace account and changing the environment variables of your Formbricks instance. Make sure your self-hosted Formbricks instance works with SSL as Slack requires it. * Create a Slack workspace if you don't have one already. * Go to the [Your apps](https://api.slack.com/apps) page and **Create New App**. * Click on **From Scratch** and provide the **App Name** and select your workspace in **Pick a workspace to develop your app in:** dropdown. Click on **Create App**. * Go to the **OAuth & Permissions** tab on the sidebar and add the following **Bot Token Scopes**: * `channels:read` * `groups:read` * `chat:write` * `chat:write.public` * `chat:write.customize` * Add the **Redirect URLs** under **OAuth & Permissions** tab. You can add the following URLs: * If you are running formbricks locally, you can enter `https://localhost:3000/api/v1/integrations/slack/callback`. * Or, you can enter `https:///api/v1/integrations/slack/callback` * Now, click on **Install to Workspace** and **Allow** the permissions. * Go to the **Basic Information** tab on the sidebar and copy the **Client ID** and **Client Secret**. Copy them & add it to your **Formbricks environment variables** as in the docker compose file: * `SLACK_CLIENT_ID` - OAuth Client ID * `SLACK_CLIENT_SECRET` - OAuth Client Secret * Now, you need to enable the public distribution of your app. Go to the **Basic Information** tab and click on the **Manage distribution** button and click on the "Distribute App". * Scroll down to the **Share your app with other workspaces** section, complete the checklist and click on the **Activate public distribution** button. Voila! You have successfully enabled the Slack integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in the [Slack Integration](/docs/platform/features/integrations/slack) section to link a Slack workspace with Formbricks. Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Zapier (On Premise) Source: https://formbricks.com/docs/self-hosting/configuration/integrations/zapier Set up automations with your self-hosted Zapier instance. The Zapier integration allows you to create automations based on survey responses. If you are using Formbricks Cloud, you will need to configure this integration differently. Please follow this guide [here](/docs/platform/features/integrations/zapier) to set it up. ## Setup Setup your Zap: Go to [zapier.com](https://zapier.com/) and create a new Zap. Search for “Formbricks” to get started: ![setup](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253827/image_dj64ty.jpg) Then, choose the event you want to trigger the Zap on: ![trigger event](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738253929/image_p9kpxj_qxz2hw.jpg) Connect Formbricks with Zapier: You have to connect Zapier with Formbricks via an API Key: ![connect](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738254011/image_fqjrtl.jpg) ![second](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738254061/image_apgw1y.jpg) Now you need an API key. Please refer to the [API Key Setup](/docs/api-reference/rest-api#how-to-generate-an-api-key) page to learn how to create one. Once you copied it in the newly opened Zapier window, you will be connected: ![last](https://res.cloudinary.com/dwdb9tvii/image/upload/v1738254089/image_kzpadl.jpg) Voila! You have successfully configured Zapier to work with your self-hosted Formbricks instance. Now, you can follow the steps mentioned in the [Zapier Integration](https://formbricks.com/docs/developer-docs/integrations/zapier) section to connect it with your Formbricks app and see it live. Still struggling or something not working as expected? [Join our Github Discussions](https://github.com/formbricks/formbricks/discussions) and we'd be glad to assist you! # Job Runner Source: https://formbricks.com/docs/self-hosting/configuration/job-runner Configure and operate the BullMQ background-job worker in self-hosted Formbricks Formbricks uses a shared BullMQ Job Runner for asynchronous work such as response processing, survey scheduling, and [Workflows](/docs/workflows/overview). Formbricks starts the worker inside the web application by default, so a standard self-hosted deployment does not need a separate worker container. Every Formbricks web process starts one worker with a concurrency of one by default. You can tune these values with environment variables after you have measured your workload. ## Runtime architecture The web application creates jobs and consumes them from a shared Redis-backed queue: * Queue name: `background-jobs` * Redis key prefix: `{formbricks:jobs}` * Worker startup: Next.js server instrumentation * Default workers per web process: `1` * Default concurrent jobs per worker: `1` Redis is also used to coordinate recurring jobs. The web process retries worker and recurring-job registration after 30 seconds if startup fails. ## Current background jobs Current workloads on the shared queue include: | Workload | Job names | Purpose | | ----------------- | ------------------------------------------------ | ------------------------------------------------- | | Response pipeline | `response-pipeline.process` | Processes asynchronous response pipeline events. | | Survey scheduling | `survey-scheduling.reconcile` | Reconciles scheduled survey state. | | Workflows | `workflow-run.process`, `workflow-run.reconcile` | Executes workflow runs and recovers stalled runs. | These are current examples rather than a fixed list. Future Formbricks features can use the same Job Runner for additional background work. ## Configure the Job Runner Set `REDIS_URL` to a Redis instance reachable by every Formbricks web replica. ```env theme={null} REDIS_URL=redis://redis:6379 ``` All replicas must use the same Redis instance so they share the BullMQ queue and recurring schedules. `BULLMQ_WORKER_ENABLED` is enabled by default outside test environments. You can omit it or set it explicitly: ```env theme={null} BULLMQ_WORKER_ENABLED=1 BULLMQ_WORKER_COUNT=1 BULLMQ_WORKER_CONCURRENCY=1 ``` Restart every Formbricks web process after changing these values. Check the web process logs for the exact message `BullMQ runtime started`. Its structured fields include the queue name, Redis prefix, worker count, and worker concurrency. ## Scale across replicas Each web replica starts its own in-process workers and connects to the same queue. BullMQ coordinates job claims so one available worker ordinarily processes a queued job. Total processing capacity is approximately: ```text theme={null} web replicas x BULLMQ_WORKER_COUNT x BULLMQ_WORKER_CONCURRENCY ``` Increase concurrency gradually and monitor database, Redis, and downstream service capacity. Adding replicas also adds workers, so account for both scaling controls. ## Retry behavior BullMQ applies the following defaults to background jobs: | Behavior | Current default | | ----------------------- | ---------------------------------- | | Attempts | 3 | | Backoff | Exponential, starting at 5 seconds | | Completed job retention | Up to 24 hours or 1,000 jobs | | Failed job retention | Up to 7 days or 5,000 jobs | Individual workloads can add their own recovery behavior on top of these queue-level retries. ## Workflows Workflows use the shared Job Runner to execute runs and reconcile runs that lost their queue or worker handoff. No additional worker process is required. ### Configure action dependencies Configure the services required by the workflow actions you use. For example, the **Send email** action uses your deployment's SMTP transport and sender. Configure `SMTP_HOST`, `SMTP_PORT`, `MAIL_FROM`, `MAIL_FROM_NAME`, and any authentication or TLS variables required by your provider. See [SMTP Configuration](/docs/self-hosting/configuration/smtp) for the complete setup. ### Retry and recovery behavior Workflow delivery supports retries and recovery; it is not an exactly-once guarantee. Formbricks uses deterministic job IDs and guarded workflow run and step transitions to make redispatch safe, but operators should still expect jobs to be retried after transient failures. A recurring reconciliation job runs every three minutes to recover workflow runs that lost their queue or worker handoff: * Queued runs older than two minutes are eligible for redispatch. * Queued runs older than 24 hours are marked as failed instead of being redispatched indefinitely. * Running runs with no executor activity for one hour are marked as failed and unfinished steps are skipped. * Each reconciliation sweep processes at most 250 queued and 250 running candidates. Larger backlogs drain over subsequent sweeps. When reconciliation changes runs, Formbricks writes `Workflow run reconciliation acted` with structured counts for redispatched, aged-out, and recovered runs. ### Troubleshoot workflow jobs If workflow runs stay queued after the Job Runner is healthy: 1. Search for `BullMQ job failed` entries where `jobName` is `workflow-run.process` or `workflow-run.reconcile`. 2. Search for `Workflow run reconciliation acted` and inspect the queued and stuck-running counts. 3. Open the workflow's **Runs** view to inspect the run status and step-level error. ## Use a custom external worker The default in-process worker is the supported setup. Formbricks does not ship a standalone worker command or container. The external-worker flags are only for custom deployments that provide their own consumer and register the complete Formbricks application job handlers. In such a custom deployment, configure the web/API processes to keep enqueueing without starting local workers: ```env theme={null} BULLMQ_WORKER_ENABLED=0 BULLMQ_EXTERNAL_WORKER_ENABLED=1 REDIS_URL=redis://redis:6379 ``` `BULLMQ_EXTERNAL_WORKER_ENABLED=1` only keeps queueing enabled. It does not provide, start, or deploy a worker. Only use this configuration after your custom consumer registers the complete Formbricks job handlers and is already consuming the same queue. Otherwise, background jobs will remain queued. ## Troubleshoot the Job Runner Formbricks writes structured logs to stdout and stderr. Search the `msg` field for these exact messages: | Log message | What to check | | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `BullMQ runtime started` | The worker is connected. Verify its `workerCount`, `workerConcurrency`, `queueName`, and `prefix` fields. | | `BullMQ Redis connection error` | Verify `REDIS_URL`, DNS, network access, Redis credentials, and TLS settings. | | `Failed to start BullMQ runtime` | Review the attached `err` field, then confirm Redis is reachable from the web process. | | `BullMQ worker registration failed` | Worker startup failed. Formbricks schedules another attempt after 30 seconds. | | `BullMQ worker registration retry scheduled` | A startup retry is pending. Fix the underlying Redis or configuration error before it runs. | | `BullMQ recurring job registration failed` | Recurring schedules could not be registered. Check Redis connectivity. | | `BullMQ recurring job registration retry scheduled` | A recurring-schedule registration retry is pending for 30 seconds. | | `BullMQ job failed` | Inspect `jobName`, `jobId`, `attemptsMade`, and `err`. The job may retry until all three attempts are used. | If background jobs stay queued: 1. Confirm `REDIS_URL` is present and identical on all web replicas. 2. Confirm at least one in-process worker is enabled, or that a compatible external worker is running. 3. Look for `BullMQ runtime started` after the latest deployment or restart. 4. Check Redis connection and `BullMQ job failed` logs. 5. Use the `jobName` field to identify the affected workload and continue in its feature-specific logs or UI. See [Environment Variables](/docs/self-hosting/configuration/environment-variables) for every Job Runner setting and [Monitoring](/docs/self-hosting/setup/monitoring) for log collection guidance. # SMTP Configuration Source: https://formbricks.com/docs/self-hosting/configuration/smtp Set up email functionality for your self-hosted Formbricks instance By default, Formbricks doesn't include an SMTP server for sending emails. However, you can easily configure your self-hosted instance to use your own email provider through environment variables. ## Why Configure SMTP? Setting up an SMTP server enables important email functionality in Formbricks, including: * Email verification for new accounts * Password reset emails * Team member invitation emails * Survey response notifications ## Email Configuration Options Formbricks uses Nodemailer to send emails and supports various SMTP providers like: * AWS SES * SendGrid * Mailgun * Gmail (for low volume) * Custom SMTP servers * Other SMTP providers ## Required Environment Variables To enable email functionality, configure the following environment variables: ```bash theme={null} # Basic SMTP Configuration MAIL_FROM=noreply@yourdomain.com MAIL_FROM_NAME=Formbricks SMTP_HOST=smtp.yourprovider.com SMTP_PORT=587 SMTP_USER=your_username SMTP_PASSWORD=your_password ``` Additional optional settings: ```bash theme={null} # Enable SMTP_SECURE_ENABLED for TLS (port 465) SMTP_SECURE_ENABLED=0 # If set to 0, the server won't require authentication SMTP_AUTHENTICATED=1 # If set to 0, the server will accept connections without requiring # authorization from the list of supplied CAs (default is 1) SMTP_REJECT_UNAUTHORIZED_TLS=0 ``` ## Enabling Email Features By default, email verification and password reset features are **disabled** in Formbricks. To enable these features: ```bash theme={null} # Set to 0 to enable email verification (requires working SMTP) EMAIL_VERIFICATION_DISABLED=0 # Set to 0 to enable password reset functionality (requires working SMTP) PASSWORD_RESET_DISABLED=0 # Optional: configure the password reset link lifetime in minutes (5-120, default 30) PASSWORD_RESET_TOKEN_LIFETIME_MINUTES=30 ``` ## Configuration for One-Click Setup If you're using the one-click setup with Docker Compose, you can either: 1. Edit the docker-compose.yml file and add the SMTP environment variables: ```yaml theme={null} environment: # Email Configuration MAIL_FROM: noreply@yourdomain.com MAIL_FROM_NAME: Formbricks SMTP_HOST: smtp.yourprovider.com SMTP_PORT: 587 SMTP_USER: your_username SMTP_PASSWORD: your_password EMAIL_VERIFICATION_DISABLED: 0 PASSWORD_RESET_DISABLED: 0 PASSWORD_RESET_TOKEN_LIFETIME_MINUTES: 30 ``` 2. Or during the setup, answer "Yes" when prompted to set up the email service: ``` 📧 Do you want to set up the email service? You will need SMTP credentials for the same! [y/N] y ``` ## Provider-Specific Examples ### SendGrid ```bash theme={null} MAIL_FROM=noreply@yourdomain.com MAIL_FROM_NAME=Formbricks SMTP_HOST=smtp.sendgrid.net SMTP_PORT=587 SMTP_USER=apikey SMTP_PASSWORD=your_sendgrid_api_key ``` ### AWS SES ```bash theme={null} MAIL_FROM=noreply@yourdomain.com MAIL_FROM_NAME=Formbricks SMTP_HOST=email-smtp.us-east-1.amazonaws.com SMTP_PORT=587 SMTP_USER=your_ses_access_key SMTP_PASSWORD=your_ses_secret_key ``` ### Gmail ```bash theme={null} MAIL_FROM=your_email@gmail.com MAIL_FROM_NAME=Formbricks SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=your_email@gmail.com SMTP_PASSWORD=your_app_password ``` > **Note**: For Gmail, you need to use an App Password if you have 2FA enabled. ## Troubleshooting If you're experiencing issues with your email configuration: 1. Check that all required environment variables are set correctly 2. Verify your SMTP credentials are valid 3. Ensure your email provider allows sending from the specified MAIL\_FROM address 4. If using Gmail, ensure you're using an App Password 5. For secure connections, make sure you've set the correct port and SMTP\_SECURE\_ENABLED value For additional help, join the conversation on [GitHub Discussions](https://github.com/formbricks/formbricks/discussions). # Self-Hosting Source: https://formbricks.com/docs/self-hosting/overview Learn how to self-host Formbricks. ### System Requirements **Minimum Setup**: 1 vCPU, 2 GB RAM, 8 GB SSD. **HTTPS Required**: Ensure your server runs with a valid HTTPS certificate. #### **Self-Hosting Options** * [**One-Click Setup**](/docs/self-hosting/setup/one-click)**:** Set up Formbricks on Ubuntu in minutes with our shell script. * [**Docker Setup**](/docs/self-hosting/setup/docker)**:** Use our pre-built Docker image for an effortless start. * [**Migration Guide**](/docs/self-hosting/advanced/migration)**:** Upgrade your existing Formbricks instance smoothly. * [**Configuration**](/docs/self-hosting/configuration)**:** Customize your setup with advanced configurations. * [**Integrations**](/docs/self-hosting/configuration/integrations)**:** Connect Formbricks to third-party tools. * [**Licensing**](/docs/self-hosting/advanced/license)**:**  Learn more about our AGPL Formbricks Core and its open-source license & Unlock advanced features tailored for larger teams and enterprises with the EE License. ### **Hosting Options** * **Cloud Hosting:** Hassle-free managed deployment at [app.formbricks.com](https://app.formbricks.com). * **Self-Hosting Free:** Perfect for personal use or small teams. * **Self-Hosting with EE License:** Unlock advanced features for large teams or enterprises. ### **Licensing Options Comparison**: For Self Hosting, our core product is free however certain advanced features are a part of our Enterprise License offering. Please refer this table to understand if you want the features that are a part of it and then we’ll reach out to you! | **Feature** | Cloud Hosting | Self-Hosting Free | Self-Hosting EE License | | ----------------------- | -------------------------- | ----------------- | ----------------------- | | Setup & Maintenance | Managed | DIY | DIY | | Advanced Functionality | Full (Optional Paid plans) | Most of them | All Advanced Features | | Support & Updates | Included | Community | Prioritized | | Price | Subscription | Free | Fixed Per month | | Customization & Control | Limited | Moderate | Full | For licensing queries, contact us at [**hola@formbricks.com**](). Formbricks is designed to be intuitive and accessible, ensuring an easy setup no matter which option you choose. Start using Formbricks today and take your surveys to the next level! **Need Help?** Join the conversation on [**GitHub Discussions**](https://github.com/formbricks/formbricks/discussions). # Cluster Setup Source: https://formbricks.com/docs/self-hosting/setup/cluster-setup Run Formbricks in a high-availability cluster. ## Overview Running Formbricks as a cluster of multiple instances gives you: * **High Availability**: surveys remain accessible even if one app pod becomes unavailable * **Load Distribution**: traffic can be spread across multiple stateless Formbricks app instances * **Scalability**: you can scale app replicas horizontally as usage grows * **Zero-Downtime Updates**: rolling deployments are possible with the right orchestration setup ## Requirements For a Formbricks v5 cluster setup, plan for: * shared PostgreSQL for Formbricks and Hub, with `pgvector` support if Hub shares the same database * shared Redis/Valkey for caching, rate limiting, and audit-related flows * shared S3-compatible storage if you use file uploads or organization branding assets * a load balancer or ingress layer in front of the app * Formbricks Hub as part of the runtime * Envoy Gateway or an equivalent external edge rate limiter for the v5-covered public and API-key routes ## Architecture ```mermaid theme={null} graph TD subgraph Edge LB["Load Balancer / Ingress"] RL["Envoy Or Equivalent Edge Rate Limiter"] end subgraph Formbricks Cluster FB1["Formbricks App 1"] FB2["Formbricks App 2"] FB3["Formbricks App n"] HUB["Formbricks Hub"] end subgraph Data Storage PSQL["PostgreSQL"] REDIS["Redis / Valkey"] S3["S3 Compatible Storage"] end LB --> RL RL --> FB1 RL --> FB2 RL --> FB3 FB1 --> PSQL FB2 --> PSQL FB3 --> PSQL HUB --> PSQL FB1 --> REDIS FB2 --> REDIS FB3 --> REDIS FB1 --> HUB FB2 --> HUB FB3 --> HUB FB1 --> S3 FB2 --> S3 FB3 --> S3 ``` ### Component Description 1. **Formbricks App Replicas** * stateless application instances that serve the UI, APIs, and survey flows * can be scaled horizontally behind a load balancer 2. **Formbricks Hub** * required in Formbricks v5 * stores and serves Hub-backed feedback record data * can share the same PostgreSQL database as the main app when configured that way 3. **PostgreSQL** * primary persistent store for the Formbricks app and, by default, Hub * should be backed up and monitored like any other stateful production dependency 4. **Redis / Valkey** * required for caching, remaining application-enforced rate limits, and audit-related flows * should be shared across all app replicas 5. **S3-Compatible Storage** * used for file uploads and media-related features * should be shared across all replicas 6. **Edge Layer** * terminates or routes incoming traffic * enforces rate limiting for the route groups that moved out of the application server in v5 ## Redis Configuration Redis/Valkey is required for Formbricks to function. The application will not start without `REDIS_URL`. ```sh env theme={null} REDIS_URL=redis://your-redis-host:6379 ``` ## S3 Configuration ```sh env theme={null} S3_ACCESS_KEY=your-access-key S3_SECRET_KEY=your-secret-key S3_REGION=your-region S3_BUCKET_NAME=your-bucket-name # For S3-compatible storage (e.g., StorJ, RustFS) # Leave empty for Amazon S3 S3_ENDPOINT_URL=https://your-s3-compatible-endpoint # Enable for RustFS and most third-party S3-compatible storage # Set to 0 (or omit) for Amazon S3 S3_FORCE_PATH_STYLE=1 ``` When using S3 in a cluster setup, ensure: * all replicas use the same bucket * CORS allows your Formbricks origin (for RustFS, set `RUSTFS_CORS_ALLOWED_ORIGINS`; see [File Uploads](/docs/self-hosting/configuration/file-uploads#rustfs-cors-configuration)) * the credentials have read/write access for the assets you expect Formbricks to manage ## v5 Cluster Notes ### Hub Is Mandatory Formbricks v5 self-hosting requires Hub. Do not plan a cluster upgrade that keeps Hub disabled. ### Edge Rate Limiting Must Exist Somewhere You do not have to use the Formbricks Helm chart's Envoy bundle, but you do need equivalent edge protection for the v5-covered public and API-key routes. Use the [rate-limiting guide](/docs/self-hosting/advanced/rate-limiting) for the exact route coverage. ### Cube Is Optional Cube is only needed for analytics dashboards or other analysis flows that depend on Cube queries. It is not part of the baseline Formbricks v5 cluster runtime. ## Kubernetes Setup The current Kubernetes deployment path uses the OCI chart published from [`charts/formbricks`](https://github.com/formbricks/formbricks/tree/main/charts/formbricks): ```sh theme={null} helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \ -n formbricks \ --create-namespace \ -f values.yaml ``` For the Kubernetes-specific installation flow, mandatory Hub behavior, and Envoy bundle modes, use the dedicated [Kubernetes deployment guide](/docs/self-hosting/setup/kubernetes). # Docker Setup Source: https://formbricks.com/docs/self-hosting/setup/docker Set up Formbricks quickly using our ready-to-use Docker image. Use this guide for a manual Docker Compose setup. It downloads the production Compose file and starts the baseline Formbricks stack: Formbricks Web, PostgreSQL, Redis/Valkey, Formbricks Hub, and Cube. Optional services such as Qwen/vLLM, AI taxonomy, and RustFS are documented after the baseline stack is running. ### Requirements Make sure the following tools are installed: * Docker Engine with Docker Compose V2 (`docker compose`) * `curl` * `openssl` * A POSIX-compatible shell such as `sh`, `bash`, or `zsh` Docker and Docker Compose are usually included in tools like Docker Desktop and Rancher Desktop. `docker compose` without the hyphen is now the primary method of using docker-compose, according to the Docker documentation. ## Choose Your Setup Path * Use this manual Docker Compose guide for local installs, custom servers, or custom reverse-proxy setups. * Use the [one-click setup script](/docs/self-hosting/setup/one-click) for production Ubuntu servers where you want Traefik, HTTPS certificates, and optional RustFS automation. * Use the [migration guide](/docs/self-hosting/advanced/migration#v5) before updating an existing Formbricks 4.x install or an older v5 compose file. Starting with Formbricks v5, the production Docker Compose stack includes Formbricks Hub and Cube as part of the baseline. Keep `HUB_API_URL` at its internal default unless Hub runs elsewhere. ## Start 1. **Create a New Directory for Formbricks** Open a terminal and run the following commands to create and enter a new directory for Formbricks: ```bash theme={null} mkdir formbricks-quickstart && cd formbricks-quickstart ``` 2. **Download the Docker Files** Get the Docker Compose file plus the Cube configuration shipped with the baseline stack: ```bash theme={null} mkdir -p cube/schema curl -fsSL \ -o docker-compose.yml \ https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/docker-compose.yml curl -fsSL \ -o cube/cube.js \ https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/cube/cube.js curl -fsSL \ -o cube/schema/FeedbackRecords.js \ https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/cube/schema/FeedbackRecords.js ``` 3. **Create the Environment File** Store your configuration in `.env`. Docker Compose reads this file for variable interpolation, and the Formbricks container also loads it at startup. For a local install, use `http://localhost:3000`. For a server install, replace both URL values with your public HTTPS URL before starting the stack. ```bash theme={null} cat < .env WEBAPP_URL=http://localhost:3000 NEXTAUTH_URL=http://localhost:3000 NEXTAUTH_SECRET=$(openssl rand -hex 32) ENCRYPTION_KEY=$(openssl rand -hex 32) CRON_SECRET=$(openssl rand -hex 32) HUB_API_KEY=$(openssl rand -hex 32) CUBEJS_API_SECRET=$(openssl rand -hex 32) CUBEJS_JWT_ISSUER=formbricks-web CUBEJS_JWT_AUDIENCE=formbricks-cube EOF chmod 600 .env ``` 4. **Validate the Docker Compose Configuration** Validate the Compose file after `.env` contains the required values: ```bash theme={null} docker compose config >/dev/null ``` If validation fails, check that `.env` contains the required values and that `docker-compose.yml` has valid syntax. 5. **Start the Docker Setup** Now, you're ready to run Formbricks with Docker. Use the command below to start Formbricks together with PostgreSQL, Redis, Formbricks Hub, and Cube. ```bash theme={null} docker compose up -d ``` The `-d` flag runs the containers in the background, so they keep running even after you close the terminal. 6. **Verify the Stack** Confirm the baseline services started and the Formbricks health endpoint responds: ```bash theme={null} docker compose ps -a curl -fsS http://localhost:3000/health docker compose logs --tail=100 formbricks-migrate hub-migrate formbricks hub cube ``` `formbricks-migrate` and `hub-migrate` should complete successfully. `postgres`, `redis`, `cube`, `hub`, and `formbricks` should be running or healthy. 7. **Open Formbricks in Your Browser** Once the setup is running, open [**http://localhost:3000**](http://localhost:3000) in your browser to access Formbricks. The first time you visit, you'll see a setup wizard. Follow the steps to create your first user and start using Formbricks. The bundled Docker stack keeps Formbricks Hub and Cube internal to the compose network. The app reaches them through `http://hub:8080` and `http://cube:4000`. When AI taxonomy beta is enabled, Hub reaches taxonomy internally through `http://taxonomy:8000`. ## Optional Services Start and verify the baseline stack before enabling optional services. If `.env` already contains a `COMPOSE_PROFILES` line, update that line instead of adding a second one. ### Enable Bundled Qwen/vLLM For AI The Docker stack can optionally run Qwen through vLLM as an OpenAI-compatible `/v1` endpoint. Baseline installs do not start vLLM and can still use Google Vertex, Azure, AWS Bedrock, or another OpenAI-compatible endpoint. The bundled Qwen/vLLM service requires a GPU-capable Docker host with the NVIDIA Container Toolkit installed. Model files are stored in the `qwen-model-cache` Docker volume. Startup can take several minutes while vLLM downloads and loads the model. To use the bundled Qwen runtime, add these values to `.env`: ```bash theme={null} cat <> .env COMPOSE_PROFILES=qwen AI_PROVIDER=openai-compatible AI_MODEL=qwen3-14b-awq AI_OPENAI_COMPATIBLE_BASE_URL=http://vllm:8000/v1 AI_OPENAI_COMPATIBLE_PROVIDER_NAME=vllm AI_OPENAI_COMPATIBLE_SUPPORTS_STRUCTURED_OUTPUTS=1 EOF ``` Then start the stack with the profile and check vLLM: ```bash theme={null} docker compose --profile qwen up -d docker compose --profile qwen ps docker compose logs --tail=100 vllm formbricks curl -fsS http://127.0.0.1:8000/health ``` The vLLM endpoint is available inside the Compose network at `http://vllm:8000/v1` and is bound to `127.0.0.1:8000` by default for local checks. If you run your own Qwen/vLLM service, do not enable the `qwen` profile. Set `AI_PROVIDER`, `AI_MODEL`, and `AI_OPENAI_COMPATIBLE_BASE_URL` to your external endpoint instead. ### Enable AI Taxonomy Beta The standalone AI taxonomy service is included as an opt-in Docker Compose profile. Baseline installs do not start taxonomy and do not require taxonomy or LLM secrets. To enable taxonomy with the bundled Qwen runtime, add these values to `.env`: ```bash theme={null} cat <> .env COMPOSE_PROFILES=qwen,taxonomy TAXONOMY_SERVICE_URL=http://taxonomy:8000 TAXONOMY_SERVICE_TOKEN=$(openssl rand -hex 32) HUB_INTERNAL_API_TOKEN=$(openssl rand -hex 32) TAXONOMY_IMAGE_REF=:v0.1.0 TAXONOMY_LLM_PROVIDER=openai-compatible TAXONOMY_LLM_MODEL=qwen3-14b-awq TAXONOMY_LLM_BASE_URL=http://vllm:8000/v1 TAXONOMY_LLM_API_KEY=not-used EOF ``` Replace `:v0.1.0` with the current released `ghcr.io/formbricks/taxonomy` image tag for your Formbricks version. Production installs should pin a release tag instead of relying on `:latest`. If you run your own OpenAI-compatible endpoint, use `COMPOSE_PROFILES=taxonomy` instead and point `TAXONOMY_LLM_BASE_URL` at that `/v1` endpoint. The selected model must reliably return strict JSON because taxonomy generation validates an exact 5-level tree. To use Amazon Bedrock instead of an OpenAI-compatible endpoint, replace the taxonomy LLM values with: ```bash theme={null} TAXONOMY_LLM_PROVIDER=bedrock TAXONOMY_LLM_MODEL=eu.anthropic.claude-sonnet-4-5-20250929-v1:0 AWS_REGION=eu-north-1 AWS_BEARER_TOKEN_BEDROCK= ``` To use Gemini on Vertex AI instead, replace the taxonomy LLM values with: ```bash theme={null} TAXONOMY_LLM_PROVIDER=vertex-gemini TAXONOMY_LLM_MODEL=gemini-2.5-flash TAXONOMY_VERTEX_PROJECT= TAXONOMY_VERTEX_LOCATION= TAXONOMY_GOOGLE_CLOUD_CREDENTIALS_JSON= ``` Start the stack after updating `.env`. Compose reads `COMPOSE_PROFILES` from `.env`, so the same command works for `taxonomy` and `qwen,taxonomy` setups: ```bash theme={null} docker compose up -d docker compose ps docker compose logs --tail=100 taxonomy hub ``` If you enabled the bundled Qwen profile, also check vLLM: ```bash theme={null} docker compose logs --tail=100 vllm ``` Run the authenticated preflight after startup to verify Hub internal auth and LLM reachability: ```bash theme={null} docker compose --profile taxonomy exec -T taxonomy python - <<'PY' import os import urllib.request request = urllib.request.Request( "http://127.0.0.1:8000/v1/preflight", headers={"Authorization": "Bearer " + os.environ["TAXONOMY_SERVICE_TOKEN"]}, ) print(urllib.request.urlopen(request, timeout=10).read().decode()) PY ``` This command runs inside the taxonomy container, so `127.0.0.1:8000` refers to the taxonomy service itself. The preflight endpoint then checks Hub internal auth and LLM reachability from taxonomy. The taxonomy service remains internal to the compose network by default. For production workloads, `TAXONOMY_MAX_RECORDS` defaults to `50000`; override it only as an advanced safety limit after sizing CPU, memory, and LLM capacity. The taxonomy service is internal to the Docker network. Formbricks Web still calls Hub with HUB\_API\_KEY; Hub calls taxonomy with TAXONOMY\_SERVICE\_TOKEN; taxonomy calls Hub internal APIs with HUB\_INTERNAL\_API\_TOKEN. The one-click installer does not prompt for AI taxonomy settings. One-click users can enable the beta later by editing `./formbricks/.env`, adding the taxonomy variables above, ensuring `COMPOSE_PROFILES=taxonomy` is set, and restarting with `docker compose up -d`. If you use the one-click Traefik setup, FeedbackRecords are available on the Formbricks origin at `/api/v3/feedbackRecords` and `/v1/feedback-records`. Custom Docker reverse proxies need equivalent wiring: run gateway auth against the Formbricks app, rewrite `/api/v3/feedbackRecords` to Hub's `/v1/feedback-records`, and inject `Authorization: Bearer ` only on the Hub-bound hop. ## Update See our [migration guide](/docs/self-hosting/advanced/migration) for version-specific steps to update Formbricks. For a major migration such as Formbricks 4.x to 5.0, update your compose structure and configuration first. Pulling images alone is not enough if your stack does not yet include Hub (`HUB_API_KEY`), Cube (`cube/` config files plus `CUBEJS_API_SECRET`), or the new edge rate-limiting setup. 1. Pull the latest Formbricks image ```bash theme={null} docker compose pull ``` 2. Stop the Formbricks stack ```bash theme={null} docker compose down ``` 3. Re-start the Formbricks stack with the updated image ```bash theme={null} docker compose up -d ``` ## Optional: Add RustFS for File Storage RustFS provides S3-compatible object storage for file uploads in Formbricks. It is not required for the baseline Docker setup. Add it only when you want features like image uploads, survey file uploads, or custom logos. For a broader overview of file storage options and required environment variables, see our [File Uploads Configuration](/docs/self-hosting/configuration/file-uploads) guide. **For production deployments with HTTPS**, use the [one-click setup script](/docs/self-hosting/setup/one-click) which automatically configures RustFS with Traefik, SSL certificates, a dedicated `files.` subdomain, and least-privilege service credentials. The examples below are best suited for development, testing, or custom local setups. The bundled RustFS examples on this page are convenience-oriented single-server setups. They work well for development, evaluation, and smaller self-hosted deployments, but they are not the ideal RustFS architecture for high-availability or larger-scale production storage. For stricter production requirements, use external object storage or run a dedicated RustFS deployment separately. ### Quick Start: Repository Development Stack If you cloned the Formbricks repository, the fastest way to test file uploads locally is to use the included `docker-compose.dev.yml`, which already starts RustFS and auto-creates the `formbricks` bucket. This development compose file is not downloaded by the manual production quickstart above. If you only downloaded `docker-compose.yml`, use the manual RustFS setup below or the one-click production setup. 1. **Start the local stack** From the repository root: ```bash theme={null} docker compose -f docker-compose.dev.yml up -d ``` This starts PostgreSQL, Valkey (Redis), Mailhog, RustFS, a permissions helper, a one-time bucket bootstrap job, Formbricks Hub, and a local Cube instance for analytics testing. 2. **Access the RustFS console** Open [http://localhost:9001](http://localhost:9001) in your browser and sign in with: * Username: `devrustfs` * Password: `devrustfs123` 3. **Configure Formbricks** Update your `.env` file or environment variables: ```bash theme={null} S3_ACCESS_KEY="devrustfs" S3_SECRET_KEY="devrustfs123" S3_REGION="us-east-1" S3_BUCKET_NAME="formbricks" S3_ENDPOINT_URL="http://localhost:9000" S3_FORCE_PATH_STYLE="1" ``` 4. **Verify uploads** After uploading a file in Formbricks, open [http://localhost:9001](http://localhost:9001) and navigate to **Buckets → formbricks** to confirm the object was stored successfully. The development compose file also runs a `rustfs-init` job so you do not need to create the bucket manually. ### Manual RustFS Setup (Custom Configuration) Recommended: Prefer docker-compose.dev.yml for local development unless you need to fold RustFS into an existing custom Compose stack. If you want to add RustFS to your own `docker-compose.yml`, use a pinned RustFS image plus two helper services: ```yaml theme={null} services: rustfs-perms: image: busybox:1.36.1 user: "0:0" command: ["sh", "-c", "mkdir -p /data && chown -R 10001:10001 /data"] volumes: - rustfs-data:/data rustfs: image: rustfs/rustfs:1.0.0-alpha.93 restart: always depends_on: rustfs-perms: condition: service_completed_successfully command: /data environment: RUSTFS_ACCESS_KEY: "${FORMBRICKS_RUSTFS_ADMIN_USER}" RUSTFS_SECRET_KEY: "${FORMBRICKS_RUSTFS_ADMIN_PASSWORD}" RUSTFS_ADDRESS: ":9000" RUSTFS_CONSOLE_ENABLE: "true" RUSTFS_CONSOLE_ADDRESS: ":9001" # CORS for browser uploads: set to your Formbricks origin(s), comma-separated. # Unset = allow all origins; a value is treated as an allow-list. See the File Uploads guide. RUSTFS_CORS_ALLOWED_ORIGINS: "https://app.yourdomain.com" ports: - "9000:9000" - "9001:9001" volumes: - rustfs-data:/data rustfs-init: image: minio/mc@sha256:95b5f3f7969a5c5a9f3a700ba72d5c84172819e13385aaf916e237cf111ab868 depends_on: - rustfs environment: RUSTFS_ADMIN_USER: "${FORMBRICKS_RUSTFS_ADMIN_USER}" RUSTFS_ADMIN_PASSWORD: "${FORMBRICKS_RUSTFS_ADMIN_PASSWORD}" RUSTFS_SERVICE_USER: "${FORMBRICKS_RUSTFS_SERVICE_USER}" RUSTFS_SERVICE_PASSWORD: "${FORMBRICKS_RUSTFS_SERVICE_PASSWORD}" RUSTFS_BUCKET_NAME: "${FORMBRICKS_RUSTFS_BUCKET_NAME}" RUSTFS_POLICY_NAME: "${FORMBRICKS_RUSTFS_POLICY_NAME}" entrypoint: - /bin/sh - -c - | set -e until mc alias set rustfs http://rustfs:9000 "$RUSTFS_ADMIN_USER" "$RUSTFS_ADMIN_PASSWORD" >/dev/null 2>&1 \ && mc ls rustfs >/dev/null 2>&1; do sleep 2 done mc mb rustfs/"$RUSTFS_BUCKET_NAME" --ignore-existing cat > /tmp/formbricks-policy.json << EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:DeleteObject", "s3:GetObject", "s3:PutObject"], "Resource": ["arn:aws:s3:::$RUSTFS_BUCKET_NAME/*"] }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::$RUSTFS_BUCKET_NAME"] } ] } EOF if ! mc admin policy info rustfs "$RUSTFS_POLICY_NAME" >/dev/null 2>&1; then mc admin policy create rustfs "$RUSTFS_POLICY_NAME" /tmp/formbricks-policy.json || \ mc admin policy add rustfs "$RUSTFS_POLICY_NAME" /tmp/formbricks-policy.json fi if ! mc admin user info rustfs "$RUSTFS_SERVICE_USER" >/dev/null 2>&1; then mc admin user add rustfs "$RUSTFS_SERVICE_USER" "$RUSTFS_SERVICE_PASSWORD" fi mc admin policy attach rustfs "$RUSTFS_POLICY_NAME" --user "$RUSTFS_SERVICE_USER" ``` Declare the corresponding volume: ```yaml theme={null} volumes: rustfs-data: driver: local ``` Store the generated RustFS credentials in a local `.env` file next to your `docker-compose.yml` instead of hardcoding them in Compose: ```bash theme={null} FORMBRICKS_RUSTFS_ADMIN_USER=formbricks-root FORMBRICKS_RUSTFS_ADMIN_PASSWORD=change-this-secure-password FORMBRICKS_RUSTFS_SERVICE_USER=formbricks-service FORMBRICKS_RUSTFS_SERVICE_PASSWORD=change-this-service-password FORMBRICKS_RUSTFS_BUCKET_NAME=formbricks FORMBRICKS_RUSTFS_POLICY_NAME=formbricks-policy FORMBRICKS_RUSTFS_REGION=us-east-1 ``` Then configure Formbricks to use the RustFS service credentials: ```bash theme={null} S3_ACCESS_KEY="${FORMBRICKS_RUSTFS_SERVICE_USER}" S3_SECRET_KEY="${FORMBRICKS_RUSTFS_SERVICE_PASSWORD}" S3_REGION="${FORMBRICKS_RUSTFS_REGION}" S3_BUCKET_NAME="${FORMBRICKS_RUSTFS_BUCKET_NAME}" S3_ENDPOINT_URL="http://rustfs:9000" S3_FORCE_PATH_STYLE="1" ``` Start the stack: ```bash theme={null} docker compose up -d ``` The bucket and service account are created automatically by the `rustfs-init` job defined above, so no manual RustFS console step is required. Restrict the `.env` file to `0600` and do not commit it to source control. For production, prefer the [one-click setup script](/docs/self-hosting/setup/one-click), which creates a separate least-privilege service account automatically. #### Tips & Common Gotchas * **Permission denied on `/data`**: Ensure the mounted directory or volume is owned by UID `10001`. The `rustfs-perms` helper handles this for Compose-managed volumes. * **Storage medium matters**: Prefer local SSD or NVMe storage for `rustfs-data`, use XFS on dedicated host-managed disks where possible, and avoid NFS or other network filesystems for RustFS data. * **Connection refused**: Ensure the `rustfs` container is running and port `9000` is reachable from the Formbricks container. * **Bucket not found**: Confirm that `rustfs-init` completed successfully or create the bucket manually with `mc`. * **Auth failed**: Confirm that `S3_ACCESS_KEY` and `S3_SECRET_KEY` match the RustFS credentials configured on the server. * **Backups**: Back up the `rustfs-data` volume regularly, especially for single-server deployments. * **Console exposure**: Do not expose the RustFS console port publicly in production. Keep it on a private network or behind admin-only controls. * **Health check**: From the Formbricks container: ```bash theme={null} docker compose exec formbricks sh -c 'wget -O- http://rustfs:9000/health' ``` ### Production Setup with Traefik For production deployments, use the [one-click setup script](/docs/self-hosting/setup/one-click), which automatically configures: * RustFS behind Traefik on a dedicated `files.yourdomain.com` subdomain * Automatic SSL certificate generation via Let's Encrypt * CORS configuration scoped to your Formbricks domain * Rate limiting middleware * Separate RustFS admin and Formbricks service credentials * A `rustfs-init` job that creates the bucket and access policy The production setup from [formbricks.sh](https://github.com/formbricks/formbricks/blob/main/docker/formbricks.sh) adds the reverse proxy wiring and bootstrap automation needed for long-lived deployments. Even in the one-click flow, bundled RustFS remains a convenience-oriented single-server deployment. For higher availability, stricter operational requirements, or larger storage footprints, prefer external object storage or a dedicated RustFS deployment managed separately from Formbricks. ## Debug If startup fails, first check the resolved configuration and container state: ```bash theme={null} docker compose config >/dev/null docker compose ps -a ``` Then inspect the services that commonly explain startup issues: ```bash theme={null} docker compose logs --tail=200 formbricks-migrate hub-migrate formbricks hub cube docker compose logs --tail=200 postgres redis ``` Common checks: * **Missing or empty secrets**: Confirm `.env` contains `NEXTAUTH_SECRET`, `ENCRYPTION_KEY`, `CRON_SECRET`, `HUB_API_KEY`, and `CUBEJS_API_SECRET`. * **Migration failures**: Check `formbricks-migrate`, `hub-migrate`, and `postgres` logs. Do not remove Docker volumes on an existing install unless you intend to delete its data. * **Cube is unhealthy**: Confirm `cube/cube.js`, `cube/schema/FeedbackRecords.js`, and `CUBEJS_API_SECRET` exist, then inspect `docker compose logs cube`. * **Hub auth errors**: Confirm the same `HUB_API_KEY` from `.env` is used by Formbricks Web and the Hub service. * **Port conflicts**: Confirm ports `3000`, `6379`, and any optional service ports you enabled are not already in use on the host. * **Optional Qwen/vLLM issues**: Check GPU availability, NVIDIA Container Toolkit installation, and `docker compose --profile qwen logs --tail=200 vllm`. To edit any of the available environment variables, check out our [Configuration](/docs/self-hosting/configuration/environment-variables) section. If you have any questions or require help, reach out on [**GitHub Discussions**](https://github.com/formbricks/formbricks/discussions). # Kubernetes Deployment Source: https://formbricks.com/docs/self-hosting/setup/kubernetes Deploy Formbricks on Kubernetes with the current OCI Helm chart. Deploy Formbricks on Kubernetes using the current OCI Helm chart published from the `charts/formbricks` directory in the Formbricks repository. Formbricks v5 self-hosting expects Hub to be part of the runtime. The chart handles that by default. Use the [migration guide](/docs/self-hosting/advanced/migration#v5) before upgrading an existing 4.x deployment. ## Prerequisites Ensure you have the following before proceeding: * a running Kubernetes cluster * Helm 3 installed locally * a public hostname for `formbricks.webappUrl` * a plan for PostgreSQL and Redis/Valkey, either in-cluster or managed externally * an edge rate-limiting plan for the v5-covered routes: the chart's Envoy bundle or an equivalent external edge solution ## 1. Install The Chart ```yaml theme={null} formbricks: webappUrl: https://surveys.example.com ``` Add any additional overrides you need for ingress, external services, secrets, or Enterprise license features. ```sh theme={null} helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \ -n formbricks \ --create-namespace \ -f values.yaml ``` By default, the chart deploys: * the Formbricks application * Formbricks Hub * PostgreSQL * Redis * generated Kubernetes Secrets ## 2. Configure Secrets And External Services ### Using Generated Secrets The default chart path keeps `secret.enabled: true`, which lets the chart generate the required application secrets for you. ### Using Managed PostgreSQL And Redis For production workloads, many teams prefer managed services: ```yaml theme={null} postgresql: enabled: false externalDatabaseUrl: "postgresql://user:password@your-postgres-host:5432/formbricks" redis: enabled: false externalRedisUrl: "redis://your-redis-host:6379" ``` ### Using External Secrets If your cluster already uses an external secret manager, enable `externalSecret` and point it at your existing SecretStore. Ensure the resulting app secret exposes the values your deployment needs, including `DATABASE_URL`, `REDIS_URL`, and `HUB_API_KEY`. ## 3. v5-Specific Deployment Notes ### Hub Is Mandatory Formbricks v5 does not support `hub.enabled=false`. Keep the default `hub.enabled=true` behavior in place. Use `hub.image.tag`, `hub.resources`, and `hub.existingSecret` only when you need to pin or customize the Hub deployment details. ### Envoy Bundle Modes The chart supports three edge patterns for the v5-covered routes: * **Bundled Envoy controller**: set `envoy.enabled=true` and `envoy.controller.enabled=true` * **Existing cluster Envoy controller**: set `envoy.enabled=true` and `envoy.controller.enabled=false` * **Equivalent external edge protection**: keep using your platform's own ingress or gateway layer if it already provides equivalent rate-limiting coverage If you use the chart-managed Envoy rate-limiting path, enable a dedicated backend with: ```yaml theme={null} envoyRedis: enabled: true ``` This keeps Envoy rate-limiting state separate from the application's own Redis traffic. ### Cube Cube is part of the baseline Formbricks v5 stack and is bundled with the chart by default (`cube.enabled: true`). To run an external Cube cluster instead: * set `cube.enabled: false` to skip the bundled Cube deployment * point the app at your external endpoint via `deployment.env.CUBEJS_API_URL` * supply `CUBEJS_API_SECRET` via `deployment.env` or `deployment.envFrom` if you disable generated secrets ### Optional Bundled Qwen/vLLM For AI The Helm chart can optionally deploy a Formbricks-provided Qwen runtime through vLLM. This is disabled by default and requires GPU-capable Kubernetes nodes. To deploy Qwen/vLLM and automatically point the Formbricks app at the in-cluster OpenAI-compatible endpoint: ```yaml theme={null} llm: enabled: true ``` With `llm.enabled: true`, the chart renders the vLLM router and Qwen serving engine, then injects the required `AI_PROVIDER=openai-compatible` app environment variables unless you override them in `deployment.env`. If you want to deploy the bundled Qwen runtime without changing the Formbricks app AI configuration: ```yaml theme={null} llm: enabled: true autoConfigureApp: false ``` Keep `llm.enabled: false` when you use Google Vertex, Azure, AWS Bedrock, or an externally managed OpenAI-compatible endpoint. Configure those providers through `deployment.env`. ### Optional AI Taxonomy Beta The Helm chart can optionally deploy the standalone taxonomy service. This is disabled by default and does not force the bundled Qwen/vLLM runtime. To deploy taxonomy and reuse the bundled Qwen/vLLM runtime: ```yaml theme={null} llm: enabled: true taxonomy: enabled: true ``` With this setup, taxonomy uses the in-cluster vLLM router for cluster labeling and 5-level taxonomy generation. The chart also injects `TAXONOMY_SERVICE_URL`, `TAXONOMY_SERVICE_TOKEN`, and `HUB_INTERNAL_API_TOKEN` into Hub API. To use your own OpenAI-compatible LLM endpoint instead, keep `llm.enabled: false` and set the taxonomy LLM values: ```yaml theme={null} taxonomy: enabled: true llm: model: qwen3-14b-awq baseUrl: http://my-llm-gateway:8000/v1 existingSecret: taxonomy-llm-secret ``` The `taxonomy-llm-secret` secret must contain `TAXONOMY_LLM_API_KEY`. If your endpoint does not enforce authentication, store a non-empty dummy value. To use Gemini on Vertex AI instead, keep `llm.enabled: false`, create a secret containing `TAXONOMY_GOOGLE_CLOUD_CREDENTIALS_JSON`, and configure the Vertex provider: ```yaml theme={null} taxonomy: enabled: true llm: provider: vertex-gemini model: gemini-2.5-flash vertex: project: formbricks-cloud location: europe-west3 existingSecret: taxonomy-vertex-secret ``` The service account in `TAXONOMY_GOOGLE_CLOUD_CREDENTIALS_JSON` must be allowed to call Vertex AI for the selected project and location. After startup, run the authenticated preflight check from inside the taxonomy pod: ```sh theme={null} kubectl exec -n formbricks deploy/formbricks-taxonomy -- \ python -c 'import os, urllib.request; req = urllib.request.Request("http://127.0.0.1:8000/v1/preflight", headers={"Authorization": "Bearer " + os.environ["TAXONOMY_SERVICE_TOKEN"]}); print(urllib.request.urlopen(req, timeout=10).read().decode())' ``` The taxonomy service is internal-only by default. Kubernetes probes use public `/health`; `/v1/preflight` is for operator validation and requires the internal taxonomy bearer token. ## 4. Upgrade The Deployment For normal chart upgrades: ```sh theme={null} helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks \ -n formbricks \ -f values.yaml ``` For a Formbricks 4.x to 5.0 migration, confirm the following before running the upgrade: * Hub remains enabled * `HUB_API_KEY` is present * your edge rate-limiting plan is in place * any required `AI_*` variables are added * `CUBEJS_API_SECRET` is configured (the generated app secret supplies it by default; provide an external endpoint if you set `cube.enabled: false`) ## 5. Key Values | Field | Description | | -------------------------------- | -------------------------------------------------------------- | | `formbricks.webappUrl` | Public base URL for the Formbricks app | | `deployment.image.tag` | Formbricks image tag override | | `hub.enabled` | Must stay `true` in Formbricks v5 | | `hub.image.tag` | Hub image tag override | | `envoy.enabled` | Enables chart-managed Envoy Gateway resources | | `envoy.controller.enabled` | Installs the bundled Envoy controller when `true` | | `envoyRedis.enabled` | Deploys a dedicated Redis backend for Envoy rate limiting | | `llm.enabled` | Deploys the optional bundled Qwen/vLLM runtime | | `llm.autoConfigureApp` | Injects Formbricks OpenAI-compatible env vars for bundled Qwen | | `taxonomy.enabled` | Deploys the optional standalone AI taxonomy service | | `postgresql.externalDatabaseUrl` | Uses an external PostgreSQL service instead of in-cluster | | `redis.externalRedisUrl` | Uses an external Redis/Valkey service instead of in-cluster | For the complete values surface, refer to the chart README in the repository: [charts/formbricks/README.md](https://github.com/formbricks/formbricks/tree/main/charts/formbricks). ## 6. Uninstalling The Deployment To remove the deployment: ```sh theme={null} helm uninstall formbricks -n formbricks ``` If you also want to remove in-cluster persistent volumes: ```sh theme={null} kubectl delete pvc --all -n formbricks ``` # Monitoring Source: https://formbricks.com/docs/self-hosting/setup/monitoring Monitoring your Formbricks installation for optimal performance. ## Logging Formbricks follows Next.js best practices with all logs being written to stdout/stderr, making it easy to collect and forward logs to your preferred logging solution. ### Log Levels * `debug`: Detailed information for debugging purposes. * `info`: General information about the system's operation. * `warn`: Potential issues that may require attention. * `error`: Errors that occur during the operation of the system. * `fatal`: Critical errors that cause the system to crash. ### Log Format Formbricks uses JSON format for logs, which is structured and easy to parse. Each log entry includes: * `level`: The log level (e.g., info, error). * `time`: Timestamp of the log entry in milliseconds since epoch. * `pid`: Process ID of the application. * `hostname`: Hostname of the server where the log was generated. * `requestId`: Unique identifier for the request (if applicable). * `userId`: Unique identifier for the user (if applicable). * `msg`: The log message. * `stack`: Stack trace (if applicable). * `data`: Additional data related to the log entry. ### Example Log Entry ```json theme={null} { "hostname": "server-1", "level": 30, "msg": "User logged in successfully", "pid": 12345, "requestId": "abc-123", "time": 1710000000000, "userId": "user-789" } ``` ### Configuring Log Levels You can configure the minimum log level using the `LOG_LEVEL` environment variable. Valid values: `debug`, `info`, `warn`, `error`, `fatal`. The default log level in production environments is `warn`, while in development environments it is `debug`. ```env theme={null} LOG_LEVEL=debug ``` ### AccessDocker Container Logs ```bash theme={null} # One-Click setup cd formbricks docker compose logs # Standard Docker commands docker logs docker logs -f # Follow logs ``` ### Access Kubernetes Pod Logs ```bash theme={null} kubectl logs -n kubectl logs -f -n # Follow logs ``` ### Log Forwarding Since all logs are written to stdout/stderr, you can integrate with various logging solutions: * ELK Stack (Elasticsearch, Logstash, Kibana) * Fluentd/Fluent Bit * Datadog * Splunk * CloudWatch Logs (AWS) ## OpenTelemetry Integration Formbricks offers two complementary observability approaches: 1. **Next.js OpenTelemetry** - For tracing and APM integration 2. **Prometheus Integration** - For metrics collection and monitoring ### Next.js OpenTelemetry for Tracing Formbricks leverages Next.js's built-in OpenTelemetry instrumentation for tracing and APM integration. When enabled, it automatically instruments various aspects of your application, providing detailed insights into request flows and performance. To enable Next.js OpenTelemetry, set the following environment variables: ```env theme={null} OTEL_ENABLED=true OTEL_ENDPOINT= # e.g., http://localhost:4318/v1/traces for OTLP HTTP OTEL_SERVICE_NAME=formbricks NEXT_OTEL_VERBOSE=1 # Optional: enables detailed tracing ``` #### Default Next.js Instrumentation The Next.js OpenTelemetry integration automatically tracks: * HTTP requests and responses * Route rendering * API route execution * Server-side operations * Database queries * External API calls #### Supported Backends for Tracing OpenTelemetry trace data can be exported to various observability platforms: ##### Tracing Backends * Jaeger * Zipkin * Tempo ##### APM & Full-Stack Observability Platforms * New Relic * Datadog * Dynatrace * Azure Monitor * AWS X-Ray * Google Cloud Trace ## Prometheus Integration Formbricks implements a dedicated Prometheus metrics exporter using OpenTelemetry. This integration runs a metrics server on a separate port and exposes metrics in Prometheus format for scraping. It focuses specifically on host and runtime metrics rather than application-specific traces. ### Configuration To enable and configure the Prometheus exporter, set the following environment variables: ```env theme={null} PROMETHEUS_ENABLED=1 PROMETHEUS_EXPORTER_PORT=9464 # Optional, defaults to 9464 ``` The `PROMETHEUS_ENABLED` environment variable must be set to `1` to enable metrics collection. The `PROMETHEUS_EXPORTER_PORT` variable is optional and defaults to 9464 if not specified. The exporter listens on all network interfaces (0.0.0.0) and exposes metrics at the `/metrics` endpoint. ### Available Metrics The metrics exported by the Prometheus integration include: * **Host Metrics**: * CPU usage (user, system, idle) * Memory usage (used, free, cached) * Disk I/O (reads, writes) * Network I/O (bytes in/out, packets in/out) * **HTTP Metrics**: * Request counts * Request durations * Error rates * **Runtime Metrics**: * Garbage collection frequency and duration * Event loop lag * Heap statistics (size, used, available) ### Collecting Metrics You can scrape metrics from your Prometheus server by adding the following to your Prometheus configuration: ```yaml theme={null} scrape_configs: - job_name: "formbricks" static_configs: - targets: ["your-formbricks-host:9464"] ``` ### Resource Attributes The metrics include resource attributes automatically detected from: * Environment variables * Process information * Host information These attributes can help you filter and group metrics in your dashboards. ## Health Checks Available endpoints: ``` GET /health ``` Use these endpoints for monitoring system health in container orchestration and monitoring tools. # One-Click Setup Source: https://formbricks.com/docs/self-hosting/setup/one-click How to set up Formbricks instance with a one-click script This only works with an Ubuntu machine, so ensure the underlying OS is verified beforehand! If you’re looking to quickly set up a production instance of Formbricks on an Ubuntu server, this guide is for you. Using a convenient shell script, you can install everything—including Docker, Postgres DB, and an SSL certificate—in just a few steps. The script takes care of all the dependencies and configuration for your server, making the process smooth and simple. This setup uses **Traefik** as a **reverse proxy**, essential for directing incoming traffic to the correct container and enabling secure internet access to Formbricks. Traefik is chosen for its simplicity and automatic SSL management via Let’s Encrypt. The bundled RustFS option in this one-click setup is designed for convenience on a single server. It is a good fit for small-scale or low-complexity self-hosted deployments, but it is not the ideal RustFS architecture for high-availability or larger production environments. For stricter production requirements, use external object storage or run a dedicated RustFS deployment separately from the Formbricks one-click stack. When bundled RustFS is enabled, the installer stores the generated RustFS credentials in `./formbricks/.env` and restricts the file to `0600`. Keep that file private, include it in your server backup plan, and avoid checking it into source control or copying it to shared locations. For better RustFS performance and stability, prefer local SSD or NVMe-backed storage for the host volume behind `rustfs-data`. Avoid NFS and other network filesystems for bundled RustFS data, and use XFS on dedicated storage disks when you manage the host layout yourself. For other operating systems or a more customized installation, please refer to the advanced installation guide with [Docker](/docs/self-hosting/setup/docker). ### Requirements * An Ubuntu Virtual Machine with SSH access. * A custom domain with an **A record** pointing to your server. * Ports **80** and **443** are open in your VM's Security Group, allowing Traefik to create an SSL certificate. ### Deployment Run this command in your terminal: ```bash theme={null} curl -fsSL https://raw.githubusercontent.com/formbricks/formbricks/stable/docker/formbricks.sh -o formbricks.sh && chmod +x formbricks.sh && ./formbricks.sh install ``` The current v5 one-click stack is based on the production Docker Compose file and includes Formbricks Hub and Cube as part of the baseline (Cube configuration lives under `formbricks/cube/`). Ensure your generated `formbricks/docker-compose.yml` contains a non-empty `HUB_API_KEY` and that `formbricks/.env` contains `CUBEJS_API_SECRET` before treating the v5 stack as ready. If either value is missing after the script finishes, add it manually. `HUB_API_URL` should normally stay at `http://hub:8080`. The v5 one-click Traefik setup also exposes Hub-backed FeedbackRecords through Formbricks at `/api/v3/feedbackRecords` and `/v1/feedback-records`. Traefik calls the internal Formbricks gateway auth endpoint first, then forwards allowed requests to Hub with the generated `HUB_API_KEY`. Browser callers should request short-lived gateway tokens from `/api/v3/gateway/token` with `{ "service": "feedbackRecords" }`. ### Script Prompts During installation, the script will prompt you to provide some details: * **Overwriting Docker GPG Keys**: If Docker GPG keys already exist, the script will ask whether you want to overwrite them. ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) ``` * **Domain Name**: Enter the domain name where you’ll host Formbricks. The domain will be used to generate an SSL certificate. Do not include the protocol (http/https). ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) y 🔄 Updating your package list again. 🐳 Installing Docker. 🚀 Testing your Docker installation. 🎉 Docker is installed! 🐳 Adding your user to the Docker group to avoid using sudo with docker commands. 🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance! 📁 Created Formbricks Quickstart directory at ./formbricks. 🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)): ``` * **HTTPS Certificate Setup**: The script will ask if you’d like to create an HTTPS certificate for your domain. Enter `Y` to proceed (highly recommended for secure access). ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) y 🔄 Updating your package list again. 🐳 Installing Docker. 🚀 Testing your Docker installation. 🎉 Docker is installed! 🐳 Adding your user to the Docker group to avoid using sudo with docker commands. 🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance! 📁 Created Formbricks Quickstart directory at ./formbricks. 🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)): my.hosted.url.com 🔗 Do you want us to set up an HTTPS certificate for you? [Y/n] ``` * **DNS Setup Prompt**: Ensure that your domain's DNS is correctly configured and ports 80 and 443 are open. Confirm this by entering `Y`. This step is crucial for proper SSL certificate issuance and secure server access. ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) y 🔄 Updating your package list again. 🐳 Installing Docker. 🚀 Testing your Docker installation. 🎉 Docker is installed! 🐳 Adding your user to the Docker group to avoid using sudo with docker commands. 🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance! 📁 Created Formbricks Quickstart directory at ./formbricks. 🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)): my.hosted.url.com 🔗 Do you want us to set up an HTTPS certificate for you? [Y/n] Y 🔗 Please make sure that the domain points to the server's IP address and that ports 80 & 443 are open in your server's firewall. Is everything set up? [Y/n] ``` * **Email Address for SSL Certificate**: Provide an email address to register the SSL certificate. Notifications regarding the certificate will be sent to this address. ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) y 🔄 Updating your package list again. 🐳 Installing Docker. 🚀 Testing your Docker installation. 🎉 Docker is installed! 🐳 Adding your user to the Docker group to avoid using sudo with docker commands. 🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance! 📁 Created Formbricks Quickstart directory at ./formbricks. 🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)): my.hosted.url.com 🔗 Do you want us to set up an HTTPS certificate for you? [Y/n] Y 🔗 Please make sure that the domain points to the server's IP address and that ports 80 & 443 are open in your server's firewall. Is everything set up? [Y/n] Y 💡 Please enter your email address for the SSL certificate: ``` * **Enforce HTTPS with HSTS**: Enabling HTTP Strict Transport Security (HSTS) ensures all communication with your server is encrypted. It’s a recommended best practice. Enter `Y` to enforce HTTPS. ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) y 🔄 Updating your package list again. 🐳 Installing Docker. 🚀 Testing your Docker installation. 🎉 Docker is installed! 🐳 Adding your user to the Docker group to avoid using sudo with docker commands. 🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance! 📁 Created Formbricks Quickstart directory at ./formbricks. 🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)): my.hosted.url.com 🔗 Do you want us to set up an HTTPS certificate for you? [Y/n] Y 🔗 Please make sure that the domain points to the server's IP address and that ports 80 & 443 are open in your server's firewall. Is everything set up? [Y/n] Y 💡 Please enter your email address for the SSL certificate: docs@formbricks.com 🔗 Do you want to enforce HTTPS (HSTS)? [Y/n] ``` * **Email Service Setup Prompt**: The script will ask if you want to set up the email service. Enter `Y` to proceed.(default is `N`). You can skip this step if you don't want to set up the email service. You will still be able to use Formbricks without setting up the email service. ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) y 🔄 Updating your package list again. 🐳 Installing Docker. 🚀 Testing your Docker installation. 🎉 Docker is installed! 🐳 Adding your user to the Docker group to avoid using sudo with docker commands. 🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance! 📁 Created Formbricks Quickstart directory at ./formbricks. 🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)): my.hosted.url.com 🔗 Do you want us to set up an HTTPS certificate for you? [Y/n] Y 🔗 Please make sure that the domain points to the server's IP address and that ports 80 & 443 are open in your server's firewall. Is everything set up? [Y/n] Y 💡 Please enter your email address for the SSL certificate: docs@formbricks.com 🔗 Do you want to enforce HTTPS (HSTS)? [Y/n] Y 🚗 Configuring Traefik... 💡 Created traefik.yaml and traefik-dynamic.yaml file. 💡 Created acme.json file with correct permissions. 📧 Do you want to set up the email service? You will need SMTP credentials for the same! [y/N] ``` **That's it**! After running the command and providing the required information, visit the domain name you entered, and you should see the Formbricks home wizard! ``` 🚀 Executing default step of installing Formbricks 🧱 Welcome to the Formbricks Setup Script 🛸 Fasten your seatbelts! We're setting up your Formbricks environment on your Ubuntu 24.04 LTS server. 🧹 Time to sweep away any old Docker installations. 🔄 Updating your package list. 📦 Installing the necessary dependencies. 🔑 Adding Docker's official GPG key and setting up the stable repository. File '/etc/apt/keyrings/docker.gpg' exists. Overwrite? (y/N) y 🔄 Updating your package list again. 🐳 Installing Docker. 🚀 Testing your Docker installation. 🎉 Docker is installed! 🐳 Adding your user to the Docker group to avoid using sudo with docker commands. 🎉 Hooray! Docker is all set and ready to go. You're now ready to run your Formbricks instance! 📁 Created Formbricks Quickstart directory at ./formbricks. 🔗 Please enter your domain name for the SSL certificate (🚨 do NOT enter the protocol (http/https/etc)): my.hosted.url.com 🔗 Do you want us to set up an HTTPS certificate for you? [Y/n] Y 🔗 Please make sure that the domain points to the server's IP address and that ports 80 & 443 are open in your server's firewall. Is everything set up? [Y/n] Y 💡 Please enter your email address for the SSL certificate: docs@formbricks.com 🔗 Do you want to enforce HTTPS (HSTS)? [Y/n] Y 🚗 Configuring Traefik... 💡 Created traefik.yaml and traefik-dynamic.yaml file. 💡 Created acme.json file with correct permissions. 📧 Do you want to set up the email service? You will need SMTP credentials for the same! [y/N] N 📥 Downloading docker-compose.yml from Formbricks GitHub repository... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 6632 100 6632 0 0 24280 0 --:--:-- --:--:-- --:--:-- 24382 🚙 Updating docker-compose.yml with your custom inputs... 🚗 NEXTAUTH_SECRET updated successfully! 🚗 ENCRYPTION_KEY updated successfully! 🚗 CRON_SECRET updated successfully! [+] Running 4/4 ✔ Network formbricks_default Created 0.2s ✔ Container formbricks-postgres-1 Started 1.0s ✔ Container formbricks-formbricks-1 Started 1.6s ✔ Container traefik Started 2.8s 🔗 To edit more variables and deeper config, go to the formbricks/docker-compose.yml, edit the file, and restart the container! 🚨 Make sure you have set up the DNS records as well as inbound rules for the domain name and IP address of this instance. 🎉 All done! Please setup your Formbricks instance by visiting your domain at https://my.hosted.url.com. You can check the status of Formbricks & Traefik with 'cd formbricks && sudo docker compose ps.' ``` ## Update To update Formbricks for a minor or patch release, run: ``` ./formbricks.sh update ``` The script pulls the latest images, stops the running containers, and starts the stack again. `./formbricks.sh update` does **not** rewrite your existing `formbricks/docker-compose.yml`. For a major migration such as Formbricks 4.x to 5.0, follow the [migration guide](/docs/self-hosting/advanced/migration#v5) first, merge the current v5 Compose changes into your existing deployment, confirm `HUB_API_KEY` is set, and only then run the update command. If your older one-click install also uses bundled MinIO for file uploads, review that storage path separately before the first v5 restart; newer self-hosting updates move the bundled object-storage path to RustFS, while external S3-compatible storage keeps the same `S3_*` app contract. ## Stop To stop Formbricks, simply run the following command: ``` ./formbricks.sh stop ``` The script will automatically stop all the Formbricks related containers and brings the entire stack down. ## Restart To restart Formbricks, simply run the following command: ``` ./formbricks.sh restart ``` The script will automatically restart all the Formbricks related containers and brings the entire stack up with the previous configuration. ## Cleanup RustFS init (optional) During the one-click setup, a temporary `rustfs-init` service configures RustFS (bucket, policy, service user). It is idempotent and safe to leave in place; it will do nothing on subsequent starts once the configuration exists. If you prefer to remove the `rustfs-init` service and its references after a successful setup, run: ``` ./formbricks.sh cleanup-rustfs-init ``` `./formbricks.sh cleanup-minio-init` is still available as a backward-compatible alias. This only removes the init job and its Compose references; it does not delete any data or affect your RustFS configuration. ## Uninstall To uninstall Formbricks, simply run the following command, but keep in mind that this will delete all your data! ``` ./formbricks.sh uninstall ``` The script will automatically stop all the Formbricks related containers, remove the Formbricks directory, and delete the Docker network. ## Debug If you encounter any issues, you can check the logs of the containers with: ``` ./formbricks.sh logs ``` ## Troubleshooting If you encounter any issues, consider the following steps: * **Inbound Rules**: Make sure you have added inbound rules for Port 80 and 443 in your VM's Security Group. * **A Record**: Verify that you have set up an A record for your domain, pointing to your VM's IP address. * **Check Docker Instances**: Run `docker ps` to check the status of the Docker instances. * **Check Formbricks Logs**: Run `cd formbricks && docker compose logs` to check the logs of the Formbricks stack. If you have any questions or require help, feel free to reach out to us on [**GitHub Discussions**](https://github.com/formbricks/formbricks/discussions). 😃[ ](https://formbricks.com/docs/developer-docs/rest-api) # Learn from Churn Source: https://formbricks.com/docs/surveys/best-practices/cancel-subscription Mastering Churn Surveys with Formbricks | Essential Tips & Steps Churn is hard, but can teach you a lot. Whenever a user decides that your product isn’t worth it anymore, you have a unique opportunity to get deep insights. These insights are pure gold to reduce churn. ## Purpose The Churn Survey is among the most effective ways to identify weaknesses in your offering. People were willing to pay but now are not anymore: What changed? Let’s find out! ## Formbricks Approach * Ask at exactly the right point in time * Follow-up to prevent bad reviews ## Overview To run the Churn Survey in your app you want to proceed as follows: 1. Create new Churn Survey at [app.formbricks.com](https://app.formbricks.com/) 2. Set up the user action to display survey at right point in time 3. Choose correct recontact options to never miss a feedback 4. Prevent that churn! ### Formbricks Widget running? We assume that you have already installed the Formbricks Widget in your web app. It’s required to display messages and surveys in your app. If not, please follow the [Quick Start Guide (takes 15mins max.)](/docs/surveys/website-app-surveys/quickstart) ### 1. Create new Churn Survey If you don't have an account yet, create one at [app.formbricks.com](https://app.formbricks.com/auth/signup) Click on "Create Survey" and choose the template [Churn Survey](https://formbricks.com/survey-templates/churn-survey): Create churn survey by template ### 2. Update questions (if you like) You’re free to update the question and answer options. However, based on our experience, we suggest giving the provided template a go 😊 Change text content *Want to change the button color? You can do so in **Settings → Workspace → Appearance**.* Save, and move over to the “Audience” tab. ### 3. Pre-segment your audience In this case, you don’t really need to pre-segment your audience. You likely want to ask everyone who hits the “Cancel subscription” button. ### 4. Set up a trigger To create the trigger for your Churn Survey, you have three options to choose from: * **Trigger by Inner Text:** You likely have a “Cancel Subscription” button in your app. You can setup a user Action with the according `Inner Text` to trigger the survey, like so: Set the trigger by inner Text * **Trigger by CSS Selector:** In case you have more than one button saying “Cancel Subscription” in your app and only want to display the survey when one of them is clicked, you want to be more specific. The best way to do that is to give this button the HTML `id=“cancel-subscription”` and set your user action up like so: Set the trigger by CSS Selector * **Trigger by page view filters:** Lastly, you could also display your survey on a subpage “/subscription-cancelled” where you forward users once they cancelled the trial subscription. You can then create a user Action with the type `Page View` and add select `Limit to specific pages` to add url filters, with the following settings: Set the trigger by page URL Whenever a user visits this page, matches the filter conditions above and the recontact options (below) the survey will be displayed ✅ Here is our complete [Actions manual](/docs/surveys/website-app-surveys/actions/) covering [No-Code](/docs/surveys/website-app-surveys/actions#setting-up-no-code-actions) and [Code](/docs/surveys/website-app-surveys/actions#setting-up-code-actions) Actions. ### 5. Select Action in the “When to ask” card Select feedback button action ### 6. Last step: Set Recontact Options correctly Lastly, scroll down to “Recontact Options”. Here you have to choose the correct settings to make sure you milk these super valuable insights. You want to make sure that this survey is always displayed, no matter if the user has already seen a survey in the past days: Set recontact options These settings make sure the survey is always displayed, when a user wants to Cancel their subscription. ### 7. Congrats! You’re ready to publish your survey 💃 Publish survey Formbricks Widget running? You need to have the Formbricks Widget installed to display the Churn Survey in your app. Please follow [this tutorial (Step 4 onwards)](/docs/surveys/website-app-surveys/quickstart) to install the widget. ### **Get those insights! 🎉** # Contact form Source: https://formbricks.com/docs/surveys/best-practices/contact-form A step-by-step guide to creating a contact form using Formbricks. Welcome to this comprehensive guide on creating a contact form using Formbricks. Whether you're just starting out or you're a seasoned developer, this tutorial will walk you through every step of building an engaging and effective contact form. ## What We’ll Build By the end of this tutorial, you'll have created a simple contact form featuring: * A welcoming introduction. * Fields for collecting the user's name and email. * A question to find out why they’re contacting you. * A message field for users to share their queries. ### Setting Up Your Form First, let's lay the groundwork for your form: * Head to the Surveys page and click on **New Survey**. * Select **Start from Scratch** to create a new form. * In the form editor, click the three dots next to a question, then select **Change Question Type** and choose **Statement (Call to Action)**. Toggle button for Statement (Call to Action) * Add a welcoming statement to greet your users and explain the form's purpose. * Personalize the greeting to make it inviting and encourage engagement. A warm welcome sets the tone for your form. Make it friendly to encourage users to participate. ### Adding the Name Field Next, let's capture the user's name: * Click **Add Question**. Adding a question in Formbricks * Enter the prompts for the name field. * Turn off the **Long Answer** option at the bottom right. * Adjust any **settings**, such as making the field required. Name field configuration ### Adding the Email Field Now, let’s add a field to collect the user's email address: * Click **Add Question** again. * Select Email as the input type. * Enter a prompt for the email field. Email field configuration ### Adding a Reason for Contact Let’s now understand why the user is contacting you: * Click **Add Question** once again. * Select **Change Question** Type and choose **Single Select**. * Add the question "Why are you contacting us today?" Single Select question configuration Predefined options help categorize inquiries, making it easier for you to respond appropriately. * Add options like "General Inquiry," "Support," and "Feedback." Single Select question configuration ### Adding a Message Field Finally, let’s provide a space for the user’s message: * Click **Add Question** for the last time. * Add the question: "Your Message." * Set the placeholder text to something like "Please write your message here." Message field configuration * Consider setting a minimum character count to ensure detailed messages. ### Finalizing Your Form Once your form is complete, follow these final steps: * Review and rearrange the questions if necessary. * Test the form by filling it out as a user. * Customize the **Thank You** message for submissions. * Publish the form to get a shareable link. * Enable submission notifications: * Go to **Settings → Account → Notifications**. * Verify your email address. * Ensure that **Survey** notifications are enabled. ### Integrating the Contact Form into Your Website After publishing the form, follow these steps to integrate it into your site: * **Copy the Shareable Link** Embed Image configuration * Find your form in the Formbricks dashboard, and click Share. * Select Embed in a Web Page. - **Embed the Code** * Copy the provided code and paste it into your website where you want the form to appear. Note: There is an options toggle button called "Embed Mode." When enabled, it updates the `src` to `"?embed=true"` and displays your survey in a minimalist design, removing padding and background for a cleaner look. * **Test the Integration** * Check if the form displays correctly on your site. * Submit a test entry to ensure everything works and notifications are received. ## Conclusion Congratulations! You’ve successfully created and integrated a professional contact form using Formbricks. This form will help you collect valuable information from your visitors in an efficient, user-friendly way. A great contact form strikes the balance between collecting necessary details and being simple enough to encourage submissions. **You’ve achieved just that!** # Docs Feedback Source: https://formbricks.com/docs/surveys/best-practices/docs-feedback A step-by-step guide to getting feedback on your Documentation with Formbricks Docs Feedback allows you to measure how clear your documentation is. ## Purpose Your users don’t spend as much time thinking about your product as you do. To fight the "Curse of Knowledge" you have to measure how clear your docs are. ## Installation To get this running, you'll need a bit of time. Here are the steps we're going through: * Set up Formbricks Cloud * Build the frontend * Connect to API * Test ## 1. Setting up Formbricks Cloud * Create a [Formbricks Cloud](https://app.formbricks.com/auth/signup) account. * Then, create a survey using the template [Docs Feedback](https://formbricks.com/survey-templates/docs-feedback): select docs template * Change the Internal Question ID of the first question to **“isHelpful”** to make your life easier 😉 change id * Similarly, you can change the Internal Question ID of the *Please elaborate* question to **“additionalFeedback”** and the one of the *Page URL* question to **“pageUrl”**. The answers must be identical. If you want different options than "Yes 👍" and "No 👎", you need to update the choices accordingly. They must match the frontend we’re building in the next step. * Click on “Continue to Settings or select the audience tab manually. Scroll down to “Survey Trigger” and create a new Action: set up when to ask card * Our goal is to create an event that never triggers. This might seem odd, but it's a necessary workaround. Fill out the action as shown in the screenshot: add action * Select the Non-Event in the dropdown. Now you see that the “Publish survey” button is active. Publish your survey 🤝 select nonevent **You’re all setup in Formbricks Cloud for now 👍** ## 2. Build the frontend Your frontend might work differently Your frontend likely looks and works differently. This is an example specific to our tech stack. We want to illustrate what you should consider building yours. Before we start, lets talk about the widget. It works like this: * Once the user selects yes/no, a partial response is sent to the Formbricks API. It includes the feedback and the current page url. * Then the user is presented with an additional open text field to further explain their choice. Once it's submitted, the previous response is updated with the additional feedback. This allows us to capture and analyze partial feedback where the user is not willing to provide additional information. **Let's do this 👇** * Open the code editor where you handle your docs page. * Likely, you have a template file or similar which renders the navigation at the bottom of the page: doc navigation Locate that file. We are using the [Tailwind Template “Syntax”](https://tailwindui.com/templates/syntax) in this case. * Write the frontend code for the widget. Here is the full component (we break it down right below): ```tsx Entire Widget theme={null} import { Button } from "@/modules/ui/components/Button"; import { Popover, PopoverContent, PopoverTrigger } from "@/modules/ui/popover"; import { useRouter } from "next/router"; import { useState } from "react"; import { handleFeedbackSubmit, updateFeedback, } from "../../lib/handleFeedbackSubmit"; export const DocsFeedback = () => { const router = useRouter(); const [isOpen, setIsOpen] = useState(false); const [sharedFeedback, setSharedFeedback] = useState(false); const [responseId, setResponseId] = useState(null); const [freeText, setFreeText] = useState(""); if ( !process.env.NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID || !process.env.NEXT_PUBLIC_FORMBRICKS_COM_API_HOST || !process.env.NEXT_PUBLIC_FORMBRICKS_COM_WORKSPACE_ID ) { return null; } return (
{!sharedFeedback ? (
Was this page helpful?
{["Yes 👍", " No 👎"].map((option) => ( { const id = await handleFeedbackSubmit( option, router.asPath ); setResponseId(id); }} > {option} ))}