Widget Installation Guide

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

    ⚑ Quick Start

    Get up and running in under 5 minutes with our simple script tag. No complex setup required.

    πŸš€ Framework Support

    Detailed guides for React, Vue, Next.js, Laravel, Django, WordPress, and more.

    General Installation

    1Get Your Widget ID

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

    • Navigate to the Widgets page in your dashboard.
    • Select the widget you wish to install.
    • On the widget details page, find the "Installation" section.
    • Your unique Widget ID (also called Client Key) will be displayed there. Copy it.
    2Copy the Embed Code
    This code snippet will load the widget on your site.
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    id="feedbask-widget-script"
    ></script>

    Remember to replace YOUR_WIDGET_ID with your actual Widget ID from Step 1.

    Language Configuration

    Available Languages
    The widget supports multiple languages and will automatically adapt its interface text.

    To configure the widget language, add the data-language parameter to your script tag:

    English (Default)

    data-language="en"

    French

    data-language="fr"

    German

    data-language="de"

    Spanish

    data-language="es"

    Chinese

    data-language="zh"

    Khmer

    data-language="km"

    Vietnamese

    data-language="vi"

    Example with French language:

    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="fr"
    id="feedbask-widget-script"
    ></script>

    Note: If no language is specified, the widget will default to English. The language setting affects all text displayed in the widget interface, including buttons, labels, and validation messages.

    Programmatic Widget Triggers

    Manual Widget Control
    Take complete control over when your feedback widget appears by using programmatic triggers.

    By default, the widget shows a floating feedback button that users can click. However, you can configure the widget to be hidden by default and only show it when you call a JavaScript function. This is perfect for integrating feedback collection into your existing UI workflow and creating custom user experiences.

    πŸ“‹ Common Use Cases

    • β€’ Post-purchase feedback: Show feedback form after successful checkout
    • β€’ Navigation integration: Add "Give Feedback" button to your menu
    • β€’ Custom workflows: Trigger feedback collection on specific user actions
    • β€’ Thank you pages: Display feedback form on completion pages
    • β€’ Help desk integration: Embed in support workflows
    • β€’ Contextual feedback: Show feedback for specific features or sections
    • β€’ A/B testing: Control when feedback is requested for experiments
    1Configure Manual Trigger

    First, set up your widget to use the "Manual (On Click)" trigger type:

    1. Go to your widget settings in the Feedbask dashboard
    2. Navigate to the "Behavior & Targeting" section
    3. Under "Widget Trigger", select "Manual (On Click)"
    4. Save your widget configuration

    Note: When manual trigger is selected, the floating feedback button will be hidden by default.

    2Install Widget Script

    Install the widget script normally on your website:

    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    3Trigger the Widget Programmatically

    Use the global JavaScript function to open the widget when needed:

    // Open the feedback widget
    window.feedbask.open()

    Common Implementation Examples:

    Example 1: Button Click
    <button onclick="window.feedbask.open()">
    Give Feedback
    </button>
    Example 2: After Successful Action
    // After a successful purchase
    function handlePurchaseSuccess() {
    showSuccessMessage()
    // Show feedback widget after 2 seconds
    setTimeout(() => {
    window.feedbask.open()
    }, 2000)
    }
    Example 3: Navigation Menu Integration
    <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
    <a href="#" onclick="window.feedbask.open(); return false;">
    Feedback
    </a>
    </nav>
    Best Practices & Guidelines
    Important considerations for production deployments.
    βœ… Best Practices
    • β€’ Always check widget availability before calling functions
    • β€’ Use proper error handling to prevent JavaScript errors
    • β€’ Implement loading states to provide user feedback
    • β€’ Test on multiple devices and browsers
    • β€’ Use meaningful timing - trigger feedback at relevant moments
    • β€’ Provide visual cues when feedback is being requested
    ❌ Common Mistakes
    • β€’ Calling functions immediately on page load without checking readiness
    • β€’ No error handling - causing JavaScript errors when widget fails to load
    • β€’ Showing feedback too frequently to the same user
    • β€’ Interrupting user workflows with poorly timed feedback requests
    • β€’ Not testing on mobile devices or different screen sizes
    • β€’ Not providing fallbacks when widget fails to load

    User Identification

    Copy this prompt into Cursor AI to implement this feature
    Click to expand and see the full prompt
    Identify Logged-in Users
    Link feedback to users in your system and pre-fill user information for a better experience.

    When users are logged into your application, you can identify them to the widget. This allows you to:

    βœ… Benefits

    • β€’ Pre-fill user information: Name and email are automatically filled
    • β€’ Link feedback to users: Track feedback from specific users in your dashboard

    πŸ“‹ Use Cases

    • β€’ SaaS applications: Track feedback from paid users
    • β€’ E-commerce: Link reviews to customer accounts
    • β€’ Support systems: Connect feedback to user profiles
    • β€’ Internal tools: Track employee feedback
    1Identify a User

    Call the identify method after a user logs into your site:

    // Call this after a user logs into your site
    window.feedbask.identify({
    userId: 'user_12345', // Required: A stable, unique ID from your system
    name: 'Ada Lovelace', // Optional: Pre-fills name field
    email: 'ada@example.com', // Optional: Pre-fills email field
    // You can add any other custom properties
    plan: 'premium',
    createdAt: '2023-10-27T10:00:00Z',
    department: 'Engineering'
    });

    Widget Control API

    Widget Control Methods
    Programmatically control and interact with the Feedbask widget using JavaScript.

    The widget exposes all its control methods through the global window.feedbask object after initialization. All methods are queued if called before the widget is fully loaded, so you can call them immediately without waiting.

    Basic Control

    • β€’ open(): Opens the widget menu
    • β€’ close(): Closes the widget
    • β€’ isOpen(): Check if widget is open
    • β€’ getCurrentSection(): Get active section

    Section Navigation

    • β€’ openBug(): Open bug report section
    • β€’ openFeature(): Open feature request
    • β€’ openReview(): Open review section
    • β€’ openFeedback(): Open general feedback
    • β€’ openRoadmap(): Open roadmap section

    Next.js Installation

    App Router (Recommended)
    For Next.js 13+ with the App Router

    Add the script to your root layout file ( app/layout.tsx):

    import Script from 'next/script'
    export default function RootLayout({
    children,
    }: {
    children: React.ReactNode
    }) {
    return (
    <html lang="en">
    <body>
    {children}
    <Script
    id="feedbask-widget-script"
    strategy="lazyOnload"
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    />
    </body>
    </html>
    )
    }
    Pages Router
    For Next.js with the traditional Pages Router

    Add the script to your pages/_document.tsx file:

    import { Html, Head, Main, NextScript } from 'next/document'
    import Script from 'next/script'
    export default function Document() {
    return (
    <Html lang="en">
    <Head />
    <body>
    <Main />
    <NextScript />
    <Script
    id="feedbask-widget-script"
    strategy="lazyOnload"
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    />
    </body>
    </Html>
    )
    }

    React Installation

    Add the script to your index.html file:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My React App</title>
    </head>
    <body>
    <div id="root"></div>
    <!-- Feedbask Widget -->
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    <script type="module" src="/src/main.tsx"></script>
    </body>
    </html>

    Vue.js Installation

    Vue 3 with Vite

    Add the script to your index.html file:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Vue App</title>
    </head>
    <body>
    <div id="app"></div>
    <!-- Feedbask Widget -->
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    <script type="module" src="/src/main.ts"></script>
    </body>
    </html>
    Nuxt.js

    Add the script to your nuxt.config.ts:

    export default defineNuxtConfig({
    app: {
    head: {
    script: [
    {
    defer: true,
    src: 'https://cdn.feedbask.com/widget.js',
    'data-client-key': 'YOUR_WIDGET_ID',
    'data-language': 'en',
    id: 'feedbask-widget-script'
    }
    ]
    }
    }
    })

    WordPress Installation

    Method 1: Using a Plugin (Recommended)
    1. Install a code injection plugin like WPCode (Insert Headers and Footers) or Code Snippets
    2. Go to Code Snippets β†’ Add Snippet (or similar in your chosen plugin)
    3. Choose "Add Your Custom Code (New Snippet)"
    4. Select "HTML Snippet" as the code type
    5. Give your snippet a descriptive name like " Feedbask Feedback Widget"
    6. Paste the Feedbask script in the code editor:
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    1. Set location to "Footer" and activate the snippet
    2. Test the widget by visiting your website (may need to clear cache)

    Pro Tip: This method is theme-independent, so your widget will persist even if you change themes.

    Method 2: Theme Functions

    Add this code to your theme's functions.php file (usually found in Appearance β†’ Theme Editor):

    function add_feedbask_widget() {
    ?>
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    <?php
    }
    add_action('wp_footer', 'add_feedbask_widget');

    Laravel Installation

    Add the script to your main layout file (typically resources/views/layouts/app.blade.php):

    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ config('app.name', 'Laravel') }}</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
    </head>
    <body>
    @yield('content')
    <!-- Feedbask Widget -->
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="{{ env('FEEDBASK_WIDGET_ID') }}"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    </body>
    </html>

    Environment Configuration:

    Add the following line to your .env file:

    FEEDBASK_WIDGET_ID=your_actual_widget_id

    Django Installation

    Add the script to your base template (typically templates/base.html):

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Django App{% endblock %}</title>
    </head>
    <body>
    {% block content %}
    {% endblock %}
    <!-- Feedbask Widget -->
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="{{ FEEDBASK_WIDGET_ID }}"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    </body>
    </html>

    Settings Configuration:

    Add this to your Django settings file (usually settings.py):

    FEEDBASK_WIDGET_ID = 'your_actual_widget_id'

    Don't forget to add 'FEEDBASK_WIDGET_ID': FEEDBASK_WIDGET_ID to your template context processors if needed.

    Webflow Installation

    1. Open your Webflow project in the Designer
    2. Go to Project Settings (gear icon in the top left)
    3. Navigate to the Custom Code tab
    4. Scroll down to the Footer Code section
    5. Paste the Feedbask script code
    6. Click Save Changes
    7. Publish your site for the changes to take effect

    Note: The widget will only appear on your published site, not in the Webflow Designer preview.

    Framer Installation

    1. Open your Framer project
    2. Click on the Settings icon in the toolbar
    3. Go to the General tab
    4. Scroll down to find the Custom Code section
    5. Click End of </body> tag
    6. Paste the Feedbask script code
    7. Click Save
    8. Publish your site

    Shopify Installation

    1. Go to your Shopify admin panel
    2. Navigate to Online Store β†’ Themes
    3. Click Actions β†’ Edit code for your active theme
    4. Open the theme.liquid file
    5. Scroll to the bottom and find the </body> tag
    6. Paste the Feedbask script just before the closing body tag
    7. Click Save

    Warning: Always backup your theme before making changes. Consider creating a duplicate theme for testing.

    Static HTML Installation

    For static HTML websites, add the script directly to your HTML files before the closing </body> tag:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Website</title>
    </head>
    <body>
    <h1>Welcome to my website!</h1>
    <p>Your content here...</p>
    <!-- Feedbask Widget -->
    <script
    defer
    src="https://cdn.feedbask.com/widget.js"
    data-client-key="YOUR_WIDGET_ID"
    data-language="en"
    id="feedbask-widget-script"
    ></script>
    </body>
    </html>

    Simple Setup: Just replace YOUR_WIDGET_ID with your actual widget ID and you're ready to go!

    Webhooks

    Webhook Overview
    Receive real-time notifications when new feedback is submitted

    Webhooks allow you to receive HTTP notifications whenever specific events occur in your Feedbask program. This enables you to:

    • Integrate feedback into your existing workflow tools (Slack, Discord, etc.)
    • Automatically create tickets in your issue tracking system
    • Build custom analytics and reporting dashboards
    • Trigger automated responses or workflows

    Supported Events

    • β€’ new_bug_report - When a user reports a bug
    • β€’ new_feature_request - When a user requests a feature
    Setting Up Webhooks
    Step 1: Navigate to Settings

    Go to your program dashboard and click on Settings β†’ Integrations β†’ Webhooks

    Step 2: Create a New Webhook

    Click "Add Webhook" and provide:

    • Name: A descriptive name for your webhook
    • URL: Your endpoint URL (must be HTTPS for production)
    • Events: Select which events should trigger this webhook
    Step 3: Test Your Webhook

    Use the "Test Webhook" button to send a test payload to your endpoint

    Security Note: Webhook URLs are encrypted before storage and all webhook payloads are sent over HTTPS.

    Webhook Payload Format
    JSON payload structure sent to your endpoint
    Bug Report Payload
    {
    "event_type": "new_bug_report",
    "timestamp": "2024-01-15T10:30:00.000Z",
    "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
    "webhook_name": "Slack Integration",
    "program": {
    "name": "My App",
    "website_url": "https://myapp.com"
    },
    "data": {
    "id": "bug_12345",
    "title": "Button not working",
    "description": "The submit button doesn't respond when clicked",
    "severity": "medium",
    "status": "new",
    "reporter_email": "user@example.com",
    "reporter_name": "John Doe",
    "screenshot_url": "https://storage.feedbask.com/screenshots/...",
    "external_user_id": "user_789",
    "user_properties": {
    "plan": "premium",
    "company": "Acme Corp"
    },
    "user_context": {
    "page_url": "https://myapp.com/dashboard",
    "page_title": "Dashboard",
    "browser": "Chrome 120.0",
    "os": "Windows 10",
    "screen_size": "1920x1080"
    }
    }
    }
    Feature Request Payload
    {
    "event_type": "new_feature_request",
    "timestamp": "2024-01-15T10:35:00.000Z",
    "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
    "webhook_name": "Slack Integration",
    "program": {
    "name": "My App",
    "website_url": "https://myapp.com"
    },
    "data": {
    "id": "feature_67890",
    "title": "Dark mode support",
    "description": "Would love to have a dark mode option for nighttime use",
    "status": "new",
    "priority": "high",
    "upvotes": 0,
    "requester_email": "user@example.com",
    "requester_name": "Jane Smith",
    "external_user_id": "user_456",
    "user_properties": {
    "plan": "basic",
    "signup_date": "2024-01-01"
    }
    }
    }
    Test Webhook Payload
    {
    "event_type": "test",
    "timestamp": "2024-01-15T10:40:00.000Z",
    "program_id": "prog_12345",
    "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
    "webhook_name": "My Webhook",
    "data": {
    "message": "This is a test webhook from Feedbask",
    "test": true
    }
    }
    Webhook Headers
    HTTP headers included with webhook requests

    Each webhook request includes the following headers:

    Content-Type: application/json
    User-Agent: Feedbask-Webhook/1.0
    X-Feedbask-Event: new_bug_report
    X-Feedbask-Delivery: 550e8400-e29b-41d4-a716-446655440000-1705316400000-1
    • X-Feedbask-Event: The event type that triggered the webhook
    • X-Feedbask-Delivery: Unique delivery ID (webhook_id-timestamp-attempt)
    Security & Best Practices

    Security

    • β€’ URLs encrypted at rest
    • β€’ HTTPS required
    • β€’ 10-second timeout

    Best Practices

    • β€’ Respond with 200 OK quickly
    • β€’ Process asynchronously
    • β€’ Use delivery ID for idempotency

    Retry Policy: Failed webhooks retry 3 times (1s, 2s, 4s delays). 4xx errors don't retry.

    Webhook Limits

    Webhook availability varies by plan:

    Free Plan

    Not available

    Upgrade to Growth to enable webhooks

    Growth Plan

    5 webhooks per program

    Enterprise Plan

    Unlimited webhooks

    Common Use Cases
    Slack

    Slack Integration

    Send bug reports and feature requests directly to Slack channels for team visibility

    Jira

    Jira Integration

    Automatically create Jira tickets from bug reports with all context included

    Gmail

    Email Notifications

    Trigger custom email workflows for high-priority feedback

    Google Analytics

    Analytics Pipelines

    Stream feedback data to your analytics tools for deeper insights

    Slack Integration Example
    Complete implementation guide for sending Feedbask notifications to Slack

    Step 1: Set Up Slack Incoming Webhook

    1. 1. Go to api.slack.com/apps and click "Create New App"
    2. 2. Choose "From scratch" and name your app (e.g., "Feedbask Notifications")
    3. 3. Select your Slack workspace
    4. 4. Navigate to "Features β†’ Incoming Webhooks" and toggle it ON
    5. 5. Click "Add New Webhook to Workspace"
    6. 6. Choose a channel (e.g., #feedback or #bug-reports)
    7. 7. Copy the Webhook URL (keep it secure!)

    Security Note: Your Webhook URL contains a secret token. Store it in environment variables, never commit it to your repository.

    Step 2: Create Your Webhook Handler (Node.js/Express)

    Create an endpoint to receive Feedbask webhooks and forward them to Slack with rich formatting:

    // webhook-handler.js
    const express = require('express');
    const axios = require('axios');
    const app = express();
    // Your Slack Webhook URL (store in environment variable)
    const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL;
    app.use(express.json());
    // Webhook endpoint that Feedbask will call
    app.post('/webhooks/feedbask', async (req, res) => {
    try {
    const { event_type, data, program } = req.body;
    // Build Slack message based on event type
    let slackMessage;
    if (event_type === 'new_bug_report') {
    slackMessage = formatBugReportForSlack(data, program);
    } else if (event_type === 'new_feature_request') {
    slackMessage = formatFeatureRequestForSlack(data, program);
    } else if (event_type === 'test') {
    slackMessage = formatTestMessageForSlack(data);
    }
    // Send to Slack
    if (slackMessage) {
    await axios.post(SLACK_WEBHOOK_URL, slackMessage);
    }
    // Always respond quickly to Feedbask
    res.status(200).json({ received: true });
    } catch (error) {
    console.error('Webhook processing error:', error);
    // Still return 200 to prevent retries if it's our error
    res.status(200).json({ received: true, error: true });
    }
    });
    // Format bug report using Slack Block Kit
    function formatBugReportForSlack(data, program) {
    const severityEmoji = {
    critical: 'πŸ”΄',
    high: '🟠',
    medium: '🟑',
    low: '🟒'
    };
    return {
    blocks: [
    {
    type: "header",
    text: {
    type: "plain_text",
    text: "πŸ› New Bug Report",
    emoji: true
    }
    },
    {
    type: "section",
    fields: [
    {
    type: "mrkdwn",
    text: `*Title:*\n${data.title}`
    },
    {
    type: "mrkdwn",
    text: `*Severity:*\n${severityEmoji[data.severity] || 'βšͺ'} ${data.severity}`
    },
    {
    type: "mrkdwn",
    text: `*Reporter:*\n${data.reporter_name} (${data.reporter_email})`
    },
    {
    type: "mrkdwn",
    text: `*Status:*\n${data.status}`
    }
    ]
    },
    {
    type: "section",
    text: {
    type: "mrkdwn",
    text: `*Description:*\n${data.description}`
    }
    },
    // Add context information if available
    ...(data.user_context ? [{
    type: "context",
    elements: [
    {
    type: "mrkdwn",
    text: `πŸ“± ${data.user_context.browser} | πŸ’» ${data.user_context.os} | πŸ“ ${data.user_context.screen_size}`
    }
    ]
    }] : []),
    // Add user properties if available
    ...(data.user_properties && Object.keys(data.user_properties).length > 0 ? [{
    type: "section",
    text: {
    type: "mrkdwn",
    text: `*User Properties:*\n${Object.entries(data.user_properties)
    .map(([key, value]) => `β€’ ${key}: ${value}`)
    .join('\n')}`
    }
    }] : []),
    // Add action buttons
    {
    type: "actions",
    elements: [
    ...(data.screenshot_url ? [{
    type: "button",
    text: {
    type: "plain_text",
    text: "View Screenshot",
    emoji: true
    },
    url: data.screenshot_url,
    style: "primary"
    }] : []),
    {
    type: "button",
    text: {
    type: "plain_text",
    text: "View in Feedbask",
    emoji: true
    },
    url: `${program.website_url}/dashboard/bugs/${data.id}`
    }
    ]
    },
    {
    type: "divider"
    },
    {
    type: "context",
    elements: [
    {
    type: "mrkdwn",
    text: `From *${program.name}* | Bug ID: ${data.id}`
    }
    ]
    }
    ]
    };
    }
    // Format feature request using Slack Block Kit
    function formatFeatureRequestForSlack(data, program) {
    const priorityEmoji = {
    critical: '🚨',
    high: '⚑',
    medium: 'πŸ“Œ',
    low: 'πŸ’­'
    };
    return {
    blocks: [
    {
    type: "header",
    text: {
    type: "plain_text",
    text: "✨ New Feature Request",
    emoji: true
    }
    },
    {
    type: "section",
    fields: [
    {
    type: "mrkdwn",
    text: `*Title:*\n${data.title}`
    },
    {
    type: "mrkdwn",
    text: `*Priority:*\n${priorityEmoji[data.priority] || 'πŸ“‹'} ${data.priority}`
    },
    {
    type: "mrkdwn",
    text: `*Requester:*\n${data.requester_name} (${data.requester_email})`
    },
    {
    type: "mrkdwn",
    text: `*Upvotes:*\nπŸ‘ ${data.upvotes || 0}`
    }
    ]
    },
    {
    type: "section",
    text: {
    type: "mrkdwn",
    text: `*Description:*\n${data.description}`
    }
    },
    // Add user properties if available
    ...(data.user_properties && Object.keys(data.user_properties).length > 0 ? [{
    type: "section",
    text: {
    type: "mrkdwn",
    text: `*User Properties:*\n${Object.entries(data.user_properties)
    .map(([key, value]) => `β€’ ${key}: ${value}`)
    .join('\n')}`
    }
    }] : []),
    {
    type: "actions",
    elements: [
    {
    type: "button",
    text: {
    type: "plain_text",
    text: "View in Feedbask",
    emoji: true
    },
    url: `${program.website_url}/dashboard/features/${data.id}`,
    style: "primary"
    }
    ]
    },
    {
    type: "divider"
    },
    {
    type: "context",
    elements: [
    {
    type: "mrkdwn",
    text: `From *${program.name}* | Feature ID: ${data.id}`
    }
    ]
    }
    ]
    };
    }
    // Format test webhook message
    function formatTestMessageForSlack(data) {
    return {
    blocks: [
    {
    type: "section",
    text: {
    type: "mrkdwn",
    text: "βœ… *Feedbask Webhook Test*\n\nYour webhook integration is working correctly!"
    }
    },
    {
    type: "context",
    elements: [
    {
    type: "mrkdwn",
    text: `Test message received at ${new Date().toISOString()}`
    }
    ]
    }
    ]
    };
    }
    const PORT = process.env.PORT || 3000;
    app.listen(PORT, () => {
    console.log(`Webhook handler running on port ${PORT}`);
    });

    Step 3: Configure Environment Variables

    # .env file
    SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXX
    PORT=3000

    Step 4: Deploy and Configure in Feedbask

    1. 1. Deploy your webhook handler to a service like Vercel, Railway, or Heroku
    2. 2. Get your public endpoint URL (e.g., https://your-app.herokuapp.com/webhooks/feedbask)
    3. 3. In Feedbask, go to Settings β†’ Integrations β†’ Webhooks
    4. 4. Click "Add Webhook" and configure:
      • β€’ Name: Slack Integration
      • β€’ URL: Your deployed endpoint URL
      • β€’ Events: Select "Bug Reports" and "Feature Requests"
    5. 5. Click "Test Webhook" to verify the integration

    Advanced Features

    Threading Support

    Group related feedback in Slack threads:

    // Store message timestamps to create threads
    const messageThreads = new Map();
    async function sendToSlackWithThreading(webhook, message, threadKey) {
    // Check if we have an existing thread
    const threadTs = messageThreads.get(threadKey);
    if (threadTs) {
    // Post as a reply in the thread
    message.thread_ts = threadTs;
    }
    const response = await axios.post(webhook, message);
    // Store the timestamp for future threading
    if (!threadTs && response.data.ts) {
    messageThreads.set(threadKey, response.data.ts);
    }
    return response;
    }
    Custom Filtering

    Route different types of feedback to different Slack channels:

    // Route to different channels based on severity/priority
    const SLACK_CHANNELS = {
    critical: process.env.SLACK_CRITICAL_WEBHOOK,
    high: process.env.SLACK_HIGH_PRIORITY_WEBHOOK,
    normal: process.env.SLACK_GENERAL_WEBHOOK
    };
    function getWebhookBySeverity(severity) {
    if (severity === 'critical') return SLACK_CHANNELS.critical;
    if (severity === 'high') return SLACK_CHANNELS.high;
    return SLACK_CHANNELS.normal;
    }
    Interactive Responses

    Add interactive buttons for quick actions:

    // Add interactive buttons with callback IDs
    {
    type: "actions",
    elements: [
    {
    type: "button",
    text: { type: "plain_text", text: "Assign to Me" },
    action_id: "assign_bug",
    value: data.id
    },
    {
    type: "button",
    text: { type: "plain_text", text: "Mark as Resolved" },
    action_id: "resolve_bug",
    value: data.id,
    style: "primary"
    },
    {
    type: "button",
    text: { type: "plain_text", text: "Create Jira Ticket" },
    action_id: "create_jira",
    value: data.id
    }
    ]
    }

    Troubleshooting

    Widget Not Appearing
    • Verify you've replaced YOUR_WIDGET_ID with your actual Widget ID
    • Check that the script is placed before the closing </body> tag
    • Clear your browser cache and try in an incognito window
    • Check the browser console for any JavaScript errors
    Widget Not Responding
    • Ensure JavaScript is enabled in your browser
    • Check for conflicting scripts that might prevent the widget from loading
    • Verify your widget is active in the dashboard
    • Test on a different browser or device
    Console Errors
    • Open your browser's developer console (F12)
    • Look for any red error messages
    • Common errors include incorrect widget ID or network issues
    • Contact support with any error messages for help