# How to Integrate Bolt.new with Qualtrics

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 25 minutes
- Last updated: April 2026

## TL;DR

Integrate Qualtrics with Bolt.new by calling the Qualtrics REST API through a Next.js API route — the API token is kept server-side and all requests are proxied to prevent CORS errors. Fetch survey responses, build an analytics dashboard, or distribute survey links by embedding the Qualtrics survey JavaScript snippet. Qualtrics requires a paid subscription; for accessible survey APIs without enterprise pricing, consider Typeform or SurveyMonkey.

## Build a Qualtrics Survey Analytics Dashboard in Bolt.new

Qualtrics is the leading enterprise survey and experience management platform, widely used by Fortune 500 companies, universities, and research institutions. Its REST API v3 provides programmatic access to surveys, response data, contacts, distributions, and reports — enabling you to build custom analytics dashboards, automate survey distribution, and integrate research data into other business applications. The API uses a straightforward API token authentication model, making integration technically accessible once you have the credentials.

The primary integration challenge for Bolt.new is CORS: Qualtrics's API does not allow direct browser requests from arbitrary origins. All API calls must be proxied through a server-side API route. In Bolt.new with a Next.js project, this means creating API routes that accept requests from your React frontend, add the Qualtrics API token from server-side environment variables, and forward the request to Qualtrics's servers. The Qualtrics API responds to your server, which then returns the data to your frontend — Qualtrics never sees the browser request directly.

Qualtrics requires a paid subscription — there is no free tier. The platform is enterprise-priced, starting around $1,500/year for basic plans and scaling significantly for enterprise features. If you are evaluating Qualtrics for a new project and cost is a concern, Typeform ($25/month) and SurveyMonkey ($25/month) both offer developer-friendly REST APIs with free or low-cost tiers. This guide assumes you have an existing Qualtrics account through your organization, which is the most common scenario for developers building integrations.

## Before you start

- A Qualtrics account with API access — check Account Settings → Qualtrics IDs for your API token
- Your Qualtrics datacenter ID (shown in Account Settings → Qualtrics IDs, looks like 'iad1' or 'eu')
- At least one survey created in your Qualtrics account with some responses for testing
- A Bolt.new account with a Next.js project open
- Basic familiarity with Next.js API routes and environment variables

## Step-by-step guide

### 1. Get Your Qualtrics API Credentials

Qualtrics uses a simple API token for authentication — there is no OAuth flow or developer application to register. The API token is tied to your individual Qualtrics user account and has the same permissions as your account in the Qualtrics platform.

To find your credentials, log into your Qualtrics account and click on your account name in the top-right corner, then select Account Settings. In the Qualtrics IDs section (sometimes labelled as User Settings or Account IDs depending on your Qualtrics version), you will find three pieces of information you need: the API Token (a long alphanumeric string), the Datacenter ID (a short string like 'iad1', 'fra1', 'syd1', etc.), and your User ID (starts with UR_). Note the datacenter carefully — all API requests are made to a subdomain of qualtrics.com based on your datacenter (e.g., https://iad1.qualtrics.com/API/v3/ for the iad1 datacenter).

If you do not see an API Token, click the Generate Token button. If the API section is not visible at all, your Qualtrics account may not have API access enabled. Contact your organization's Qualtrics administrator — API access is typically a feature that needs to be enabled at the organization level for security reasons. Once you have your token, keep it secret. Unlike the Supabase anon key, the Qualtrics API token grants full access to your Qualtrics account including creating, modifying, and deleting surveys and response data. It must never be exposed in client-side code.

Add your credentials to your Bolt.new project's .env file. Use the QUALTRICS_ prefix for clarity. The datacenter ID is technically not sensitive (it is visible in your Qualtrics URL), but keeping it in .env alongside the token is good practice for consistency across environments.

```
# .env — add to project root in Bolt
# Found at: Qualtrics Account → Account Settings → Qualtrics IDs
QUALTRICS_API_TOKEN=your_api_token_here
QUALTRICS_DATACENTER=iad1
# Your survey's ID (found in the survey URL: /survey-builder/SV_xxxxxxxx/...)
QUALTRICS_SURVEY_ID=SV_xxxxxxxxxxxxxxx
# For distribution features (found in Contacts → Mailing Lists)
QUALTRICS_MAILING_LIST_ID=ML_xxxxxxxxxxxxxxx
```

**Expected result:** Your Qualtrics API token and datacenter ID are saved in the .env file. You can verify the token by making a test request to list your surveys in the next step.

### 2. Create a Qualtrics API Proxy Route

The Qualtrics REST API requires the API token to be sent in the X-API-TOKEN header. Because browser-based fetch requests cannot send custom headers to cross-origin servers without CORS approval, and Qualtrics's API servers do not whitelist arbitrary frontend origins, all Qualtrics API calls must go through a server-side API route in your Next.js app.

Create a generic proxy utility and specific routes for the operations you need. The proxy pattern is consistent across all Qualtrics endpoints: your React frontend calls your Next.js API route (same-origin, no CORS issues), the API route adds the Qualtrics API token from environment variables, forwards the request to Qualtrics's servers, and returns the response. This is straightforward in Bolt's WebContainer during development — outbound HTTP calls to Qualtrics's servers work fine.

For the dashboard use case, you need two endpoints: GET /api/qualtrics/surveys to list all surveys in the account, and GET /api/qualtrics/surveys/[surveyId]/summary to get response metadata for a specific survey. The Qualtrics API base URL is constructed from your datacenter: https://{datacenter}.qualtrics.com/API/v3/. The surveys endpoint returns an array of survey objects each containing the id, name, ownerId, lastModified, and isActive fields.

A critical thing to understand about fetching responses: Qualtrics uses an asynchronous export pattern for large datasets. You cannot simply GET all responses — you must POST to start an export, poll until it completes, then download the exported file. For dashboard purposes where you only need aggregate counts (not individual responses), the survey summary endpoint provides response counts directly without the async export process. Use the export API only when you need to analyze or display individual responses.

```
// lib/qualtrics-client.ts
export class QualtricsClient {
  private baseUrl: string;
  private apiToken: string;

  constructor() {
    const datacenter = process.env.QUALTRICS_DATACENTER;
    const token = process.env.QUALTRICS_API_TOKEN;

    if (!datacenter || !token) {
      throw new Error('QUALTRICS_DATACENTER and QUALTRICS_API_TOKEN must be set');
    }

    this.baseUrl = `https://${datacenter}.qualtrics.com/API/v3`;
    this.apiToken = token;
  }

  async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
    const url = `${this.baseUrl}${endpoint}`;
    const res = await fetch(url, {
      ...options,
      headers: {
        'X-API-TOKEN': this.apiToken,
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });

    if (!res.ok) {
      const error = await res.json().catch(() => ({ message: res.statusText }));
      throw new Error(`Qualtrics API error ${res.status}: ${JSON.stringify(error)}`);
    }

    return res.json();
  }

  async getSurveys() {
    const data = await this.request<{ result: { elements: QualtricssurveyItem[] } }>('/surveys');
    return data.result.elements;
  }

  async getSurvey(surveyId: string) {
    const data = await this.request<{ result: QualtricssurveyDetail }>(`/surveys/${surveyId}`);
    return data.result;
  }

  async getResponseCounts(surveyId: string) {
    const data = await this.request<{ result: { auditable: number; generated: number; deleted: number } }>(
      `/responseexports/${surveyId}/responsecounts`
    );
    return data.result;
  }
}

interface QualtricssurveyItem {
  id: string;
  name: string;
  ownerId: string;
  lastModified: string;
  creationDate: string;
  isActive: boolean;
}

interface QualtricssurveyDetail extends QualtricssurveyItem {
  questions: Record<string, unknown>;
}

// app/api/qualtrics/surveys/route.ts
import { NextResponse } from 'next/server';
import { QualtricsClient } from '@/lib/qualtrics-client';

export async function GET() {
  try {
    const client = new QualtricsClient();
    const surveys = await client.getSurveys();

    return NextResponse.json({
      surveys: surveys.map(s => ({
        id: s.id,
        name: s.name,
        lastModified: s.lastModified,
        isActive: s.isActive,
      })),
    });
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Failed to fetch surveys';
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
```

**Expected result:** The /api/qualtrics/surveys endpoint returns your Qualtrics survey list in the Bolt preview. The survey names and modification dates are visible in the response, confirming the API token and datacenter are correctly configured.

### 3. Build the Survey Analytics Dashboard

With the API proxy routes working, build the analytics dashboard that displays survey data and response metrics. The dashboard provides research teams with a custom view of their Qualtrics data beyond the built-in Qualtrics reporting interface — useful when you need to combine Qualtrics data with other business metrics, apply custom filtering, or embed the data in an internal tool.

The core dashboard features are: a survey list table showing all surveys with their creation date and response count, a survey detail view showing question breakdown, and a response trend chart showing how many responses were collected per day or week. For the response trend chart, you need to use Qualtrics's Response Export API which is asynchronous: POST to /responseexports to start the export, poll GET /responseexports/{progressId} until status is 'complete', then download the file URL in the response.

For charts, Recharts integrates easily with Next.js and works in Bolt's WebContainer preview. Use a BarChart for per-survey response counts and a LineChart for response trends over time. shadcn/ui's Card, Table, and Badge components provide clean layout containers for the survey data.

For a quicker alternative to the full async export, the Qualtrics API provides a response counts summary at /surveys/{surveyId}/questions that includes summary statistics for each question. For NPS surveys, this returns distribution data (promoters, passives, detractors) directly without needing the full export. Use the summary endpoint first to build the dashboard quickly, then add the full export for drill-down views when needed. The API rate limit is 3,000 requests per day for standard accounts — cache your survey list and response counts on the server side and refresh them on a schedule rather than on every page visit.

```
// app/dashboard/surveys/page.tsx
'use client';

import { useEffect, useState } from 'react';

interface Survey {
  id: string;
  name: string;
  lastModified: string;
  isActive: boolean;
}

export default function SurveysDashboard() {
  const [surveys, setSurveys] = useState<Survey[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  const fetchSurveys = async () => {
    setLoading(true);
    try {
      const res = await fetch('/api/qualtrics/surveys');
      if (!res.ok) throw new Error('Failed to fetch surveys');
      const data = await res.json();
      setSurveys(data.surveys);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'An error occurred');
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { fetchSurveys(); }, []);

  if (loading) {
    return (
      <div className="p-8 max-w-5xl mx-auto">
        <div className="h-8 bg-gray-200 rounded w-48 mb-6 animate-pulse" />
        {[...Array(5)].map((_, i) => (
          <div key={i} className="h-12 bg-gray-100 rounded mb-2 animate-pulse" />
        ))}
      </div>
    );
  }

  if (error) {
    return (
      <div className="p-8 max-w-5xl mx-auto">
        <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
          Error: {error}
        </div>
      </div>
    );
  }

  const activeSurveys = surveys.filter(s => s.isActive).length;

  return (
    <div className="p-8 max-w-5xl mx-auto">
      <div className="flex items-center justify-between mb-6">
        <h1 className="text-2xl font-bold">Qualtrics Surveys</h1>
        <button
          onClick={fetchSurveys}
          className="text-sm bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg font-medium"
        >
          Refresh
        </button>
      </div>

      <div className="grid grid-cols-3 gap-4 mb-8">
        <div className="bg-white border rounded-xl p-4">
          <div className="text-2xl font-bold">{surveys.length}</div>
          <div className="text-sm text-gray-500">Total Surveys</div>
        </div>
        <div className="bg-white border rounded-xl p-4">
          <div className="text-2xl font-bold text-green-600">{activeSurveys}</div>
          <div className="text-sm text-gray-500">Active</div>
        </div>
        <div className="bg-white border rounded-xl p-4">
          <div className="text-2xl font-bold text-gray-400">{surveys.length - activeSurveys}</div>
          <div className="text-sm text-gray-500">Inactive</div>
        </div>
      </div>

      <div className="bg-white border rounded-xl overflow-hidden">
        <table className="w-full">
          <thead className="bg-gray-50 border-b">
            <tr>
              <th className="text-left px-4 py-3 text-sm font-medium text-gray-600">Survey Name</th>
              <th className="text-left px-4 py-3 text-sm font-medium text-gray-600">Status</th>
              <th className="text-left px-4 py-3 text-sm font-medium text-gray-600">Last Modified</th>
            </tr>
          </thead>
          <tbody>
            {surveys.map(survey => (
              <tr key={survey.id} className="border-b last:border-0 hover:bg-gray-50">
                <td className="px-4 py-3 text-sm font-medium">{survey.name}</td>
                <td className="px-4 py-3">
                  <span className={`text-xs px-2 py-1 rounded-full font-medium ${
                    survey.isActive
                      ? 'bg-green-100 text-green-700'
                      : 'bg-gray-100 text-gray-500'
                  }`}>
                    {survey.isActive ? 'Active' : 'Inactive'}
                  </span>
                </td>
                <td className="px-4 py-3 text-sm text-gray-500">
                  {new Date(survey.lastModified).toLocaleDateString()}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}
```

**Expected result:** The survey analytics dashboard loads and displays your Qualtrics surveys with their active status and modification dates. The summary tiles show total, active, and inactive survey counts.

### 4. Embed a Qualtrics Survey and Handle Webhooks After Deployment

For collecting survey responses inside your Bolt.new app, embed the Qualtrics survey directly in a page using Qualtrics's JavaScript embed snippet. This approach keeps users within your application rather than redirecting them to a standalone Qualtrics survey URL. Qualtrics provides an embed snippet from the survey distribution settings: go to Distributions → Anonymous Link → Get Embed Code, and copy the JavaScript snippet.

In a React component, load this script using useEffect with a dynamically created script element. The Qualtrics embed script adds a survey widget or redirects the current page to the survey depending on your embed configuration. For a full-page survey experience, use an anonymous survey link in an iframe instead — this is often cleaner for React applications since it avoids the Qualtrics script interfering with React's DOM management.

For real-time response notifications, Qualtrics supports webhooks (called 'Event Subscriptions' in the API) that POST to your endpoint when a survey is submitted. During development in Bolt's WebContainer, your preview URL is not publicly accessible, so Qualtrics's servers cannot reach it. You must deploy to Netlify or Bolt Cloud first to test webhook functionality. After deploying, register your webhook URL using the Qualtrics Event Subscriptions API: POST to /eventsubscriptions with your survey ID, the event you want to subscribe to (e.g., surveyengine.completeSurvey), and your webhook URL. Store the incoming response data in a Supabase table for later analysis.

Alternatively, for a simpler polling approach without webhooks, create a scheduled Supabase Edge Function (or a cron-triggered Netlify Function) that calls your Qualtrics API route at regular intervals to fetch new responses since the last check and sync them to your database. This avoids the webhook infrastructure complexity and works well for non-real-time dashboards.

```
// app/api/webhooks/qualtrics/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@supabase/supabase-js';

// Qualtrics sends webhooks as application/x-www-form-urlencoded by default
// Configure your event subscription to use JSON for cleaner handling
export async function POST(request: Request) {
  try {
    const contentType = request.headers.get('content-type') || '';

    let payload: Record<string, unknown>;
    if (contentType.includes('application/json')) {
      payload = await request.json();
    } else {
      // Handle form-encoded Qualtrics payloads
      const formData = await request.formData();
      payload = Object.fromEntries(formData.entries());
    }

    console.log('Qualtrics webhook received:', JSON.stringify(payload, null, 2));

    // Save to Supabase for analysis
    const supabase = createClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.SUPABASE_SERVICE_ROLE_KEY! // Use service role to bypass RLS
    );

    const { error } = await supabase.from('survey_responses').insert({
      survey_id: payload.SurveyID || payload.surveyId,
      response_id: payload.ResponseID || payload.responseId,
      submitted_at: new Date().toISOString(),
      raw_payload: payload,
    });

    if (error) console.error('Supabase insert error:', error);

    // Always return 200 quickly — Qualtrics will retry on non-200 responses
    return NextResponse.json({ received: true });
  } catch (error) {
    console.error('Webhook processing error:', error);
    // Still return 200 to prevent Qualtrics from retrying
    return NextResponse.json({ received: true });
  }
}
```

**Expected result:** The survey embed page shows the Qualtrics survey in an iframe. After deploying to Netlify or Bolt Cloud and registering the webhook URL in Qualtrics, survey completions are received by the webhook handler and saved to Supabase.

## Best practices

- Cache Qualtrics survey lists and response counts in memory or Supabase for 5-10 minutes — the Qualtrics API has a 3,000 requests/day limit that analytics dashboards can exhaust quickly
- Use the response counts summary endpoint instead of the full export API for dashboard metrics — it returns aggregate data instantly without the async export process
- Store the QUALTRICS_API_TOKEN as a server-side-only environment variable (no NEXT_PUBLIC_ prefix) since it grants full account access and must never appear in client-side code
- Deploy to Netlify or Bolt Cloud before testing webhook integrations — Qualtrics webhooks cannot reach the Bolt WebContainer development environment
- For production survey embeds, use anonymous survey links in iframes rather than the JavaScript embed snippet — the iframe approach avoids potential conflicts between Qualtrics's script and React's virtual DOM
- Add request logging to your Qualtrics proxy routes during development to debug API responses — Qualtrics error messages are informative but only visible server-side
- When building for multiple client organizations using Qualtrics, store API tokens in a database table with organization IDs rather than as static environment variables

## Use cases

### Survey Response Analytics Dashboard

Build a real-time analytics dashboard in Bolt.new that pulls response data from Qualtrics surveys, calculates completion rates and average scores, and visualizes results with charts. Perfect for research teams that need custom reporting beyond Qualtrics's built-in reports.

Prompt example:

```
Build a Qualtrics survey analytics dashboard in Next.js. Create an API route at app/api/qualtrics/surveys/route.ts that fetches all surveys from the Qualtrics API (GET https://{datacenter}.qualtrics.com/API/v3/surveys) using an Authorization header with the API token from process.env.QUALTRICS_API_TOKEN and datacenter from process.env.QUALTRICS_DATACENTER. Create another route app/api/qualtrics/surveys/[surveyId]/responses/route.ts that fetches response counts. Build a dashboard page at /surveys showing a table of surveys with name, creation date, question count, and response count. Add a bar chart using recharts showing responses per survey over the last 30 days.
```

### Embedded Survey with Response Notifications

Embed a Qualtrics survey directly in a Bolt.new page so users can complete it without leaving the app, then trigger a notification or update a Supabase record when the survey is completed. Uses Qualtrics's JavaScript embed snippet for the survey display and a webhook for completion events.

Prompt example:

```
Add a Qualtrics survey embed to this Next.js app. Create a client component at components/QualtricsSurvey.tsx that uses useEffect to dynamically load the Qualtrics survey embed JavaScript snippet. The component should accept a surveyId prop and embed the survey using the Qualtrics Web Intercept or anonymous survey link approach. Show a loading state while the script loads and a thank-you message after completion (detected via the QSI.API.unload event). Style the container with a border and card layout using Tailwind.
```

### Automated Survey Distribution for New Users

Automatically send a Qualtrics survey to new users when they complete onboarding in a Bolt.new app. Uses the Qualtrics Distributions API to create personalized survey links for each user and the Qualtrics Mailer to send them, triggered from a Supabase database webhook or an API route called during onboarding completion.

Prompt example:

```
Create an API route at app/api/qualtrics/distribute/route.ts that accepts POST requests with a user's email, first name, and last name. The route should call the Qualtrics Create Distribution endpoint (POST https://{datacenter}.qualtrics.com/API/v3/distributions) to generate a personalized survey invitation for the survey ID in process.env.QUALTRICS_SURVEY_ID using the mailing list ID from process.env.QUALTRICS_MAILING_LIST_ID. Include error handling for Qualtrics API rate limits. Return the generated survey link in the response so the frontend can display it immediately.
```

## Troubleshooting

### Qualtrics API returns 401 Unauthorized with the error 'API token not found'

Cause: The API token is missing or incorrectly set, the X-API-TOKEN header is misspelled, or the token was generated for a different Qualtrics account than the datacenter being used.

Solution: Verify the QUALTRICS_API_TOKEN and QUALTRICS_DATACENTER in your .env file match what is shown in your Qualtrics Account Settings → Qualtrics IDs. Tokens are account-specific — a token from account A will not work against account B's datacenter. Regenerate the token in Qualtrics if you are uncertain whether the current one is valid.

```
// Verify the header name is exactly X-API-TOKEN (not Authorization or X-Qualtrics-Token)
const res = await fetch(`https://${datacenter}.qualtrics.com/API/v3/surveys`, {
  headers: {
    'X-API-TOKEN': process.env.QUALTRICS_API_TOKEN!, // Must be server-side only
    'Content-Type': 'application/json',
  },
});
```

### Direct fetch to Qualtrics API from a React component returns a CORS error

Cause: Qualtrics's API does not allow cross-origin requests from browser clients. Direct calls from React components (client-side) are blocked by the browser's CORS policy.

Solution: All Qualtrics API calls must go through your Next.js API routes (server-side). Move the fetch call from your React component to an API route file in app/api/, and call your own API route from the React component instead.

```
// WRONG: calling Qualtrics directly from React component
const res = await fetch('https://iad1.qualtrics.com/API/v3/surveys');

// CORRECT: call your own API route which proxies to Qualtrics
const res = await fetch('/api/qualtrics/surveys');
```

### Qualtrics webhooks are not received even after registering the webhook URL

Cause: The webhook URL was registered pointing to the Bolt WebContainer preview URL, which is not publicly accessible. Qualtrics cannot send HTTP POST requests to the browser-based development environment.

Solution: Deploy to Netlify or Bolt Cloud to get a public URL, then re-register the webhook with the deployed URL (e.g., https://your-app.netlify.app/api/webhooks/qualtrics). Use the Qualtrics Event Subscriptions API or the Qualtrics dashboard to update the endpoint URL.

### Response export API returns progress as 'inProgress' indefinitely and never completes

Cause: The Qualtrics response export is asynchronous — you must poll the progress endpoint until the status is 'complete', then download the file from the provided URL. If polling stops early, the export appears stuck.

Solution: Implement proper polling with a maximum retry count. Poll every 2-3 seconds up to 30 times (60-90 seconds total). If still not complete, return an error and retry on the next request.

```
async function exportResponses(surveyId: string, client: QualtricsClient) {
  // Start export
  const { result } = await client.request<{ result: { progressId: string } }>(
    `/surveys/${surveyId}/export-responses`,
    { method: 'POST', body: JSON.stringify({ format: 'json' }) }
  );

  // Poll for completion
  let attempts = 0;
  while (attempts < 30) {
    await new Promise(r => setTimeout(r, 2000)); // wait 2 seconds
    const { result: progress } = await client.request<{ result: { status: string; fileId?: string } }>(
      `/surveys/${surveyId}/export-responses/${result.progressId}`
    );
    if (progress.status === 'complete' && progress.fileId) {
      return client.request(`/surveys/${surveyId}/export-responses/${result.progressId}/file`);
    }
    attempts++;
  }
  throw new Error('Export timed out');
}
```

## Frequently asked questions

### Does Qualtrics have a free tier for API access?

No. Qualtrics does not offer a free tier — all plans are enterprise-priced, typically starting around $1,500/year for basic accounts. If you need a free or low-cost survey API for development, Typeform offers a free plan with 10 responses/month and a $25/month paid plan with API access and webhooks. SurveyMonkey also has a $25/month plan with API access.

### How do I find my Qualtrics datacenter ID?

Log into Qualtrics, click your account name in the top right, select Account Settings, and look for the Qualtrics IDs section. Your datacenter ID is shown there (e.g., 'iad1', 'fra1', 'syd1'). It is also visible in your Qualtrics URL — the subdomain of your Qualtrics platform URL (https://yourorg.qualtrics.com shows 'yourorg' but the API datacenter may differ). Check Account Settings → Qualtrics IDs for the exact value.

### Can I embed a Qualtrics survey in a Bolt.new app without the JavaScript snippet?

Yes. The simplest approach is using an anonymous survey link in an iframe. Go to Distributions → Anonymous Link in your Qualtrics survey, copy the survey URL, and add it as the iframe src in your React component. This approach is clean, avoids JavaScript snippet conflicts with React, and works in both the Bolt preview and after deployment. The main limitation is that the iframe cannot communicate with your React app when the survey completes.

### Why does the Qualtrics response export take so long?

Qualtrics processes response exports asynchronously on their servers. For small surveys with a few hundred responses, the export typically completes in 5-15 seconds. For large surveys with thousands of responses or complex data, it can take 1-2 minutes. This is why the export API uses a polling pattern: start the export, poll the progress endpoint every 2-3 seconds, and download the file URL once it is complete. For dashboard aggregate metrics, use the response counts summary endpoint instead — it returns instantly without export processing.

### Can Qualtrics webhooks be received during development in Bolt.new?

No. Qualtrics sends webhook events via HTTP POST requests to your registered endpoint. Bolt's WebContainer development environment runs inside a browser tab with no publicly accessible URL. Qualtrics's servers cannot reach it. Deploy to Netlify or Bolt Cloud to get a public URL, then register that URL as your webhook endpoint in Qualtrics's Event Subscriptions settings. Development typically requires testing webhook behavior against the deployed staging environment.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/qualtrics
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/qualtrics
