Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with SurveyMonkey

Integrate SurveyMonkey with a Lovable app using SurveyMonkey's API v3 and OAuth2. Create a Supabase Edge Function that proxies API calls using a SurveyMonkey access token stored in Cloud → Secrets. Embed surveys via iframe, process response webhooks with a second Edge Function, and build survey analytics dashboards by querying the responses endpoint.

What you'll learn

  • How to authenticate with SurveyMonkey's API v3 using OAuth2 and store the access token in Lovable Cloud Secrets
  • How to create a Supabase Edge Function that lists surveys, fetches responses, and manages collectors via the SurveyMonkey API
  • How to embed SurveyMonkey surveys in a Lovable app via iframe with collector URL customization
  • How to create a webhook receiver Edge Function that processes incoming survey response events
  • How to build a real-time survey analytics dashboard from response data stored in Supabase
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate18 min read40 minutesAnalyticsMarch 2026RapidDev Engineering Team
TL;DR

Integrate SurveyMonkey with a Lovable app using SurveyMonkey's API v3 and OAuth2. Create a Supabase Edge Function that proxies API calls using a SurveyMonkey access token stored in Cloud → Secrets. Embed surveys via iframe, process response webhooks with a second Edge Function, and build survey analytics dashboards by querying the responses endpoint.

Integrating SurveyMonkey with Your Lovable App

SurveyMonkey is the most widely deployed survey platform in the enterprise world, trusted for customer satisfaction surveys, NPS tracking, employee feedback programs, and market research. For Lovable app builders, integrating SurveyMonkey serves two distinct use cases: embedding surveys directly inside the app to capture feedback at the right moment in the user journey, and pulling response data from SurveyMonkey into a dashboard to analyze aggregate trends and individual respondent details without logging into SurveyMonkey itself.

SurveyMonkey's API v3 is a RESTful API that provides full programmatic access to surveys, responses, collectors, and webhooks. All API calls require OAuth2 authentication — you register a developer app in SurveyMonkey's developer portal, authorize it with your SurveyMonkey account, and receive a long-lived access token. This token must be stored in Lovable's Cloud → Secrets panel and accessed only from Edge Functions — never from client-side React code. SurveyMonkey's API v3 requires the Authorization header with a Bearer token, and exposing this token in frontend code would allow anyone to read your survey data and manipulate your surveys.

The integration architecture uses two Edge Functions: one that acts as a proxy for SurveyMonkey API calls (listing surveys, fetching responses, creating collectors) and one that receives incoming webhook events when new responses arrive. The webhook approach is critical for real-time response dashboards — polling the API every few minutes is both inefficient and rate-limited, while webhooks deliver response data within seconds of completion. Lovable's security infrastructure blocks approximately 1,200 hardcoded API keys daily and holds SOC 2 Type II certification, ensuring the SurveyMonkey access token stays encrypted in Cloud Secrets.

Integration method

Edge Function Integration

SurveyMonkey integrates with Lovable through Supabase Edge Functions that proxy all API calls using an OAuth2 access token stored in Cloud → Secrets. One Edge Function handles survey listing, response fetching, and collector management via SurveyMonkey's REST API v3. A second Edge Function receives and processes incoming response webhooks, storing response data in your Supabase database for real-time analytics dashboards.

Prerequisites

  • A Lovable account with an existing project that has Supabase configured
  • A SurveyMonkey account — Team Advantage plan or higher is required for API access and webhook support
  • A SurveyMonkey developer app registered at developer.surveymonkey.com with an OAuth2 access token generated
  • At least one published survey in your SurveyMonkey account with a web link collector created
  • Basic familiarity with how webhooks work — the SurveyMonkey webhook will POST to your Edge Function URL when responses arrive

Step-by-step guide

1

Register a SurveyMonkey developer app and get your OAuth2 access token

SurveyMonkey's API requires OAuth2 authentication through a developer app registration. Go to developer.surveymonkey.com and log in with your SurveyMonkey account. Click 'My Apps' in the top navigation, then click 'Add a New App'. Give your app a name (like 'Lovable Integration'), set the Application Type to 'Private' (since this is for your own account), and set the OAuth Redirect URI to https://oauth.lovable.app/callback or any valid HTTPS URL — for a private app you will use the 'authorize' flow to get a token manually rather than implementing a full OAuth flow. After creating the app, you will see your app's Client ID and Client Secret on the app detail page. For the simplest integration approach, use SurveyMonkey's 'Get a Token' feature: on the app detail page, click the 'Get Token' button. This opens SurveyMonkey's authorization page — click 'Authorize' to grant your app access to your SurveyMonkey account. You will be redirected to your redirect URI with an authorization code, or for private apps, SurveyMonkey may show the access token directly. For a more automated approach, exchange the authorization code for a long-lived access token by making a POST request to https://api.surveymonkey.com/oauth/token with your client_id, client_secret, code, and grant_type=authorization_code. SurveyMonkey access tokens do not expire unless revoked, making them suitable for long-term storage in Cloud Secrets. Once you have your access token, go to your Lovable project, click the + icon next to Preview to open the Cloud tab, click Secrets, and add SURVEYMONKEY_ACCESS_TOKEN with your token value. Also add SURVEYMONKEY_CLIENT_ID and SURVEYMONKEY_CLIENT_SECRET for any future token operations. For survey embedding, add VITE_SURVEYMONKEY_COLLECTOR_URL with the web link URL of your survey collector — this is safe to use as a VITE_ variable since it is a public collector URL.

Pro tip: SurveyMonkey access tokens are long-lived but can be revoked if the associated developer app is deleted or unauthorized. Store the token creation date in your documentation so you can check when to refresh it. If your app shows 'Unauthorized' errors, the token may have been revoked and needs to be regenerated through the developer portal.

Expected result: SURVEYMONKEY_ACCESS_TOKEN, SURVEYMONKEY_CLIENT_ID, and SURVEYMONKEY_CLIENT_SECRET are stored in Lovable Cloud Secrets. VITE_SURVEYMONKEY_COLLECTOR_URL is configured for the frontend. The SurveyMonkey developer app shows 'Authorized' status in the developer portal.

2

Create the SurveyMonkey API proxy Edge Function

Create a Supabase Edge Function that proxies requests to SurveyMonkey's API v3, handling authentication and abstracting the API details from the frontend. The function accepts requests from your Lovable React components and forwards them to SurveyMonkey's REST API with the Bearer token from Deno.env.get('SURVEYMONKEY_ACCESS_TOKEN'). SurveyMonkey's API v3 base URL is https://api.surveymonkey.com/v3. The most useful endpoints for a Lovable integration are: GET /surveys (list all surveys in the account), GET /surveys/{survey_id}/responses (list responses for a survey, paginated), GET /surveys/{survey_id}/responses/bulk (fetch multiple responses with their full answer details in a single call), and GET /surveys/{survey_id}/rollups (get aggregated summary statistics for all questions). The proxy Edge Function should accept a resource parameter identifying which API endpoint to call, and an optional query parameter for endpoint-specific options (survey ID, page number, per_page limit). It reads the access token from the secret, constructs the SurveyMonkey API URL, makes the authenticated request, and returns the response to the frontend. The function should also handle SurveyMonkey's pagination model — the API returns 50 results per page by default with a next link in the response links object. For response data, the bulk endpoint is most efficient: it returns complete response details including all answers in a single API call. The default responses endpoint returns metadata only, requiring additional calls for each response's answer data. Design your proxy to prefer the bulk endpoint for dashboard use cases where you need answer data. Include error handling for SurveyMonkey's common error responses: 401 Unauthorized (invalid or expired token), 429 Too Many Requests (API rate limit exceeded — SurveyMonkey limits to 120 requests per minute on most plans), and 403 Forbidden (feature not available on the current SurveyMonkey plan).

Lovable Prompt

Create a Supabase Edge Function called 'surveymonkey-proxy' that accepts POST requests with a resource field (like 'surveys', 'responses', or 'rollups') and optional params (survey_id, page). Make authenticated GET requests to the SurveyMonkey API v3 using SURVEYMONKEY_ACCESS_TOKEN from Deno secrets. For 'responses', use the /bulk endpoint to get full answer data. Return the API response as JSON. Handle 429 rate limit errors by returning a 429 response with a retry-after hint.

Paste this in Lovable chat

supabase/functions/surveymonkey-proxy/index.ts
1// supabase/functions/surveymonkey-proxy/index.ts
2import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
3
4const corsHeaders = {
5 'Access-Control-Allow-Origin': '*',
6 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
7};
8
9const SM_BASE = 'https://api.surveymonkey.com/v3';
10
11serve(async (req) => {
12 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders });
13
14 try {
15 const token = Deno.env.get('SURVEYMONKEY_ACCESS_TOKEN');
16 if (!token) {
17 return new Response(JSON.stringify({ error: 'SurveyMonkey token not configured' }), {
18 status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
19 });
20 }
21
22 const { resource, survey_id, page = 1, per_page = 50 } = await req.json();
23 let url = '';
24
25 switch (resource) {
26 case 'surveys':
27 url = `${SM_BASE}/surveys?per_page=${per_page}&page=${page}`;
28 break;
29 case 'responses':
30 if (!survey_id) throw new Error('survey_id required for responses');
31 url = `${SM_BASE}/surveys/${survey_id}/responses/bulk?per_page=${per_page}&page=${page}`;
32 break;
33 case 'rollups':
34 if (!survey_id) throw new Error('survey_id required for rollups');
35 url = `${SM_BASE}/surveys/${survey_id}/rollups`;
36 break;
37 default:
38 return new Response(JSON.stringify({ error: 'Unknown resource' }), {
39 status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
40 });
41 }
42
43 const smResponse = await fetch(url, {
44 headers: {
45 'Authorization': `Bearer ${token}`,
46 'Content-Type': 'application/json',
47 },
48 });
49
50 if (smResponse.status === 429) {
51 return new Response(JSON.stringify({ error: 'Rate limit exceeded', retry_after: 60 }), {
52 status: 429, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
53 });
54 }
55
56 const data = await smResponse.json();
57 return new Response(JSON.stringify(data), {
58 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
59 });
60 } catch (error) {
61 return new Response(JSON.stringify({ error: String(error) }), {
62 status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
63 });
64 }
65});

Pro tip: SurveyMonkey's API rate limits vary by plan. The Basic paid tier allows 120 requests per minute. If your dashboard makes multiple API calls (surveys list, then responses for each survey), batch them efficiently to avoid hitting the limit. Cache responses in your Supabase database and only refresh when the data is stale.

Expected result: The surveymonkey-proxy Edge Function is deployed. Calling it with { resource: 'surveys' } returns a list of surveys from the SurveyMonkey account. Calling it with { resource: 'responses', survey_id: 'xxx' } returns response data with full answer details.

3

Embed a SurveyMonkey survey in your Lovable app

SurveyMonkey surveys are embedded using iframe elements pointing to a collector URL. A collector in SurveyMonkey is a distribution channel — a web link collector generates a URL that renders the survey in the browser. Each survey can have multiple collectors, allowing you to track where responses come from (in-app versus email versus link shared on social). To get your collector URL, go to your SurveyMonkey account, open your survey, click 'Collect Responses', and find the 'Web Link' collector. Copy the URL — it will look like https://www.surveymonkey.com/r/XXXXXXX. This is the URL you embed in the iframe. SurveyMonkey supports additional URL parameters for customizing the collector behavior. The most useful are: custom_variables (pass user-specific data that appears in the response), as_url=1 (embed in iframe mode with no navigation chrome), and ?nobar=1 (hide the SurveyMonkey progress bar). Combine these with your user's ID as a custom variable so you can match responses back to Supabase users: append ?custom_variable=userId to the collector URL. For the embedding pattern in Lovable, create a modal or dedicated page component that shows the survey iframe. Detect when the survey is completed by listening for the window message event — SurveyMonkey posts a message to the parent window when the survey is submitted. This allows your Lovable app to close the survey modal and trigger post-survey actions (like showing a thank you message or updating the user's profile to mark the survey as completed) automatically without polling the API. Store the collector URL as a VITE_SURVEYMONKEY_COLLECTOR_URL environment variable so it can be changed without redeploying. For apps with multiple surveys (an onboarding survey, an NPS survey, a feature feedback survey), store each URL as a separate VITE_ variable.

Lovable Prompt

Create a SurveyModal component that embeds a SurveyMonkey survey in a shadcn Dialog. The iframe src should be VITE_SURVEYMONKEY_COLLECTOR_URL with the current user's Supabase ID appended as a custom_variables query parameter. Listen for window message events to detect survey completion and close the modal automatically. After completion, update a 'survey_completed' field in the user's Supabase profile so the survey is only shown once.

Paste this in Lovable chat

src/components/SurveyModal.tsx
1// src/components/SurveyModal.tsx
2import { useEffect, useRef } from 'react';
3import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
4import { supabase } from '@/integrations/supabase/client';
5
6interface SurveyModalProps {
7 open: boolean;
8 onClose: () => void;
9 userId: string;
10}
11
12const COLLECTOR_URL = import.meta.env.VITE_SURVEYMONKEY_COLLECTOR_URL;
13
14export function SurveyModal({ open, onClose, userId }: SurveyModalProps) {
15 const completedRef = useRef(false);
16
17 const surveyUrl = COLLECTOR_URL
18 ? `${COLLECTOR_URL}?custom_variables=${encodeURIComponent(userId)}`
19 : '';
20
21 useEffect(() => {
22 async function handleMessage(event: MessageEvent) {
23 // SurveyMonkey posts 'survey_finished' on completion
24 if (event.data === 'survey_finished' && !completedRef.current) {
25 completedRef.current = true;
26 await supabase
27 .from('profiles')
28 .update({ survey_completed: true })
29 .eq('id', userId);
30 onClose();
31 }
32 }
33 window.addEventListener('message', handleMessage);
34 return () => window.removeEventListener('message', handleMessage);
35 }, [userId, onClose]);
36
37 if (!COLLECTOR_URL) return null;
38
39 return (
40 <Dialog open={open} onOpenChange={onClose}>
41 <DialogContent className="max-w-2xl h-[80vh] p-0 overflow-hidden">
42 <DialogHeader className="px-6 pt-4">
43 <DialogTitle>Quick Feedback Survey</DialogTitle>
44 </DialogHeader>
45 <iframe
46 src={surveyUrl}
47 className="w-full h-full border-none"
48 title="Feedback Survey"
49 allow="camera; microphone"
50 />
51 </DialogContent>
52 </Dialog>
53 );
54}

Pro tip: SurveyMonkey's custom_variables feature stores values alongside each response, visible in the response data and exports. Use the Supabase user ID as a custom variable so you can join survey responses back to your user records — this enables personalizing follow-up actions based on who gave which response.

Expected result: The SurveyModal component opens with the SurveyMonkey survey embedded in an iframe. The survey renders correctly and navigates through questions. On survey completion, the modal closes automatically and the user's profile is updated with survey_completed: true.

4

Create a webhook receiver Edge Function for real-time responses

SurveyMonkey webhooks deliver response data to your Edge Function URL within seconds of a respondent submitting the survey, enabling real-time analytics and automated follow-up workflows. Setting up the webhook requires two steps: creating the webhook in SurveyMonkey's developer portal pointing to your Edge Function URL, and implementing the Edge Function that processes the incoming events. In your SurveyMonkey developer app (at developer.surveymonkey.com), navigate to your app, click 'Webhooks', and click 'Add Webhook'. Set the subscription URL to your Edge Function's URL — once deployed, this will be https://[your-project-ref].supabase.co/functions/v1/surveymonkey-webhook. Set the event type to 'response_completed' to receive an event when each survey response is fully submitted. Set the object type to 'survey' and specify your survey's ID. Click Save. The webhook payload SurveyMonkey sends is minimal — it contains the event type, the survey ID, and the response ID, but not the full response data. Your Edge Function must make a separate API call to retrieve the full response details using the response ID. This two-step pattern (receive notification → fetch details) is standard in SurveyMonkey's webhook architecture. In the Edge Function, after receiving the webhook POST, extract the response_id from the payload, then call the SurveyMonkey API to GET /surveys/{survey_id}/responses/{response_id} with the Bearer token to fetch the complete response including all answer values. Parse the answer data, extract the fields you care about (NPS score, open-text answers, custom variables), and store the structured data in your Supabase database. Run any follow-up logic — sending emails to detractors, updating user records, triggering notifications — from within the Edge Function after storing the response. SurveyMonkey does not include a webhook signature for verification (unlike Stripe's HMAC signing). To secure your webhook endpoint, verify that the incoming request includes a valid API token in a custom header, or implement IP allowlisting based on SurveyMonkey's documented webhook source IPs.

Lovable Prompt

Create a Supabase Edge Function called 'surveymonkey-webhook' that receives POST requests from SurveyMonkey. Parse the event_type, object_id (survey_id), and resources[0].id (response_id) from the body. Fetch the full response from the SurveyMonkey API using SURVEYMONKEY_ACCESS_TOKEN. Extract the first question's value (NPS score) and any custom_variables. Store the result in a Supabase 'survey_responses' table with columns: response_id, survey_id, nps_score, user_id (from custom_variables), answers (JSONB), submitted_at.

Paste this in Lovable chat

supabase/functions/surveymonkey-webhook/index.ts
1// supabase/functions/surveymonkey-webhook/index.ts
2import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
3import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
4
5const corsHeaders = { 'Access-Control-Allow-Origin': '*',
6 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type' };
7
8serve(async (req) => {
9 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders });
10 if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 });
11
12 try {
13 const token = Deno.env.get('SURVEYMONKEY_ACCESS_TOKEN');
14 const supabase = createClient(
15 Deno.env.get('SUPABASE_URL') ?? '',
16 Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
17 );
18
19 const payload = await req.json();
20 const surveyId = payload.object_id;
21 const responseId = payload.resources?.[0]?.id;
22
23 if (!surveyId || !responseId) {
24 return new Response(JSON.stringify({ error: 'Missing survey_id or response_id' }), {
25 status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
26 });
27 }
28
29 // Fetch full response details from SurveyMonkey API
30 const smResp = await fetch(
31 `https://api.surveymonkey.com/v3/surveys/${surveyId}/responses/${responseId}/details`,
32 { headers: { 'Authorization': `Bearer ${token}` } }
33 );
34 const responseData = await smResp.json();
35
36 const customVars = responseData.custom_variables ?? {};
37 const pages = responseData.pages ?? [];
38 const firstAnswer = pages[0]?.questions?.[0]?.answers?.[0]?.choice_id
39 ?? pages[0]?.questions?.[0]?.answers?.[0]?.text ?? null;
40
41 await supabase.from('survey_responses').upsert({
42 response_id: responseId,
43 survey_id: surveyId,
44 user_id: customVars.user_id ?? null,
45 nps_score: firstAnswer !== null ? Number(firstAnswer) : null,
46 answers: responseData,
47 submitted_at: responseData.date_modified,
48 }, { onConflict: 'response_id' });
49
50 return new Response(JSON.stringify({ success: true }), {
51 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
52 });
53 } catch (error) {
54 console.error('Webhook error:', error);
55 return new Response(JSON.stringify({ error: String(error) }), {
56 status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
57 });
58 }
59});

Pro tip: Register the webhook in SurveyMonkey after deploying the Edge Function, not before — you need the deployed function URL. After registering, use SurveyMonkey's 'Test Webhook' button in the developer portal to send a test event and verify your function processes it correctly.

Expected result: The surveymonkey-webhook Edge Function is deployed and its URL is registered as a webhook in the SurveyMonkey developer portal. Submitting a test response causes a new row to appear in the Supabase survey_responses table within seconds. Cloud Logs show the successful API fetch and database insert.

Common use cases

Embed a product feedback survey after users complete a key action

Trigger a SurveyMonkey survey embed after users complete a purchase, finish onboarding, or reach a milestone in your app. Using a SurveyMonkey collector URL embedded in an iframe or modal, you can capture feedback at the exact moment when the user's experience is most salient. Response data flows back via webhook to your Supabase database for analysis.

Lovable Prompt

Add a feedback survey modal that appears after a user completes their first project. Embed the SurveyMonkey survey collector URL in an iframe inside a shadcn Dialog component. Show the modal 10 seconds after the project completion event, only once per user (track whether they have seen it in the user's Supabase profile). Use the VITE_SURVEYMONKEY_COLLECTOR_URL environment variable for the survey URL.

Copy this prompt to try it in Lovable

Build a survey response analytics dashboard

Create a dashboard inside your Lovable app that shows aggregate survey results — average NPS scores, response rate trends over time, most common open-text themes — without requiring team members to log into SurveyMonkey. A Supabase Edge Function fetches summary response data from the SurveyMonkey API and stores it in your database for fast dashboard queries.

Lovable Prompt

Create a SurveyAnalytics page that fetches response summaries from the SurveyMonkey API via a Supabase Edge Function called 'surveymonkey-proxy'. Display: total responses this month, average rating for question 1 (NPS score), a bar chart showing response distribution by day, and a list of the 5 most recent responses with their completion time. Store fetched responses in a Supabase 'survey_responses' table and refresh every 30 minutes.

Copy this prompt to try it in Lovable

Automatically process NPS responses and trigger follow-up workflows

Set up a SurveyMonkey webhook that fires when each response is submitted. The webhook Edge Function parses the response, extracts the NPS score, and triggers different workflows based on the score: store detractors (0-6) in a follow-up queue for customer success, send promoters (9-10) a referral program invitation, and log all responses to a Supabase analytics table for trend reporting.

Lovable Prompt

Create a Supabase Edge Function called 'surveymonkey-webhook' that receives POST requests from SurveyMonkey when new responses arrive. Parse the response_id from the webhook body, fetch the full response details from the SurveyMonkey API, extract the NPS question answer, and store the result in a Supabase 'nps_responses' table with columns: response_id, user_email, nps_score, submitted_at, and follow_up_status. For scores below 7, set follow_up_status to 'pending'.

Copy this prompt to try it in Lovable

Troubleshooting

Edge Function returns '401 Unauthorized' when calling the SurveyMonkey API

Cause: The SURVEYMONKEY_ACCESS_TOKEN secret is missing from Cloud Secrets, the token has been revoked (through the developer portal or account password change), or the token was created for a different SurveyMonkey account than the one containing the surveys.

Solution: Go to Cloud → Secrets and verify SURVEYMONKEY_ACCESS_TOKEN is present with no extra spaces or characters. In SurveyMonkey's developer portal, check that your app shows 'Authorized' status — if it shows 'Unauthorized', re-authorize the app by clicking 'Get Token' again. After updating the secret, redeploy the Edge Function by making a minor change in Lovable to force a re-deploy, since Edge Functions cache their environment at deployment time.

SurveyMonkey webhook events arrive but the full response fetch returns 404

Cause: SurveyMonkey webhooks fire as soon as the response is submitted, but the response may not be fully processed and available via the API for a few seconds. The webhook event arrives before the response details endpoint is ready.

Solution: Add a short delay before fetching the response details in the webhook Edge Function, or implement a retry mechanism with exponential backoff. A 2-second delay using await new Promise(resolve => setTimeout(resolve, 2000)) before the API call is usually sufficient for SurveyMonkey's processing lag.

typescript
1// Add before the SurveyMonkey API fetch in the webhook handler:
2await new Promise(resolve => setTimeout(resolve, 2000)); // Wait for SM processing
3const smResp = await fetch(/* ... */);

The survey iframe shows 'This survey is not available' inside the Lovable app

Cause: The SurveyMonkey collector is closed or has reached its response limit, the collector URL is incorrect, or the survey has been moved to a different account.

Solution: Log in to SurveyMonkey, open the survey, click Collect Responses, and verify the web link collector status is 'Open' and has not reached its response limit. Copy the collector URL fresh from the SurveyMonkey interface and update VITE_SURVEYMONKEY_COLLECTOR_URL. Also check that the URL includes the correct /r/ path format.

API returns 403 Forbidden when accessing certain endpoints like /responses or /webhooks

Cause: The SurveyMonkey plan associated with the account does not include API access for that feature. Webhooks and response access via API require at least the Team Advantage plan — the Basic paid plan has limited API access.

Solution: Check the SurveyMonkey plan in account settings. Basic plans restrict certain API endpoints. The Team Advantage plan or higher is required for full API v3 access including webhooks, bulk response export, and rollup statistics. If upgrading the plan is not possible, the embed-only approach (iframe embedding without webhook processing) works on all plans.

Best practices

  • Always store the SurveyMonkey access token in Cloud → Secrets and access it only from Edge Functions via Deno.env.get() — exposing the token in frontend code allows anyone to read your surveys and responses.
  • Use SurveyMonkey's custom_variables feature to pass the Supabase user ID with each survey response — this enables joining survey data back to your user records for personalized follow-up and analysis.
  • Cache survey response summaries in your Supabase database rather than making live API calls for every dashboard view — SurveyMonkey's API rate limit is 120 requests per minute, and a dashboard showing multiple survey summaries can easily hit this limit.
  • Create separate collectors for different distribution channels (in-app, email, social) even for the same survey — this lets you track response rates by channel and analyze whether in-app respondents differ from email respondents.
  • Implement webhook idempotency by using the response_id as a unique key in your Supabase survey_responses table — SurveyMonkey may send duplicate webhook events, and upsert prevents duplicate rows from being created.
  • Test your webhook integration thoroughly before production: use SurveyMonkey's 'Test Webhook' feature to send sample events, and verify your Edge Function handles the payload format correctly before real responses arrive.
  • Store raw response JSONB alongside extracted structured fields in your Supabase table — the full response data gives you flexibility to extract additional fields later without re-fetching from SurveyMonkey.

Alternatives

Frequently asked questions

Does SurveyMonkey API access require a paid plan?

Yes, SurveyMonkey's API v3 access requires at least the Team Advantage plan. The free and basic paid plans do not include API access. Webhooks and bulk response export also require Team Advantage or higher. Check surveymonkey.com/pricing for current plan details, as SurveyMonkey frequently changes their plan structure.

Can I show different surveys to different users in my Lovable app?

Yes. Each SurveyMonkey survey can have multiple collectors, each with its own URL. Store multiple collector URLs as separate VITE_ environment variables and use conditional logic in your React component to show the appropriate survey based on user properties — their plan, onboarding stage, or segment. You can also create programmatic surveys and collectors via the API, allowing fully dynamic survey generation.

How do I match survey responses back to specific users in my Supabase database?

Use SurveyMonkey's custom_variables feature by appending ?custom_variables=USERID to your collector URL, where USERID is the Supabase user's ID. When the response arrives via webhook, the custom_variables field in the response payload contains this value. Store it as the user_id in your survey_responses table so you can join survey data with your users table for personalized analysis and follow-up.

What happens if a user starts but does not finish the embedded survey?

SurveyMonkey webhooks for 'response_completed' only fire when a survey is fully submitted — partial responses are not delivered via webhook. SurveyMonkey does save partial responses automatically (accessible in the response list with a 'Partial' status), but accessing them requires polling the API. For tracking abandonment rates, use SurveyMonkey's built-in completion rate analytics rather than custom webhook-based tracking.

Can I build custom survey forms in Lovable instead of embedding SurveyMonkey?

Yes — for simple surveys, building native React forms in Lovable and storing responses directly in Supabase is simpler than the SurveyMonkey integration. Use the SurveyMonkey integration when you need SurveyMonkey's survey analytics, panel management, benchmark data, pre-built templates, or when SurveyMonkey is a corporate standard in your organization. For basic NPS or CSAT forms, Lovable's native form capabilities with Supabase storage are often sufficient.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.