💻

Developer Documentation

3 articles James By James

Build with the UserLoop API, SDK, and Model Context Protocol server.

UserLoop API

Getting Started - Base URL: https://api.userloop.io - Generate API keys inside the UserLoop dashboard. Keys are tied to a company, scoped by feature, and shown only once at creation time. - Keep keys private. Rotate immediately if you suspect compromise. Authenticate Every Request Send the full key string exactly as issued using the X-API-Key header: curl https://api.userloop.io/health \ -H "X-API-Key: " The API validates scopes, survey allowlists, and optional origin restrictions before serving any protected data. Date & Pagination Helpers - start_date and end_date must use YYYY-MM-DD format. When omitted, endpoints default to the broadest safe range (from 1970-01-01 through the current day) so analytics always receive explicit boundaries. - limit and offset control pagination. limit defaults to 50 (maximum 200). offset defaults to 0. Endpoint Reference 1. Health Check — GET /health Verifies the service is reachable. No authentication required. { "status": "ok" } 2. Survey Catalog — GET /surveys Lists surveys accessible to the calling API key. Query parameters: Name Type Description company_id string (optional) Validate the company owning the key. If provided it must match the key’s company; otherwise it defaults automatically. Example request: curl "https://api.userloop.io/surveys" \ -H "X-API-Key: " Example response (trimmed for brevity): { "company_id": "1621...", "count": 2, "surveys": [ { "id": "1624...", "title": "Post Purchase Email Survey", "format": "Email", "status": "active", "question_count": 6, "created_at": "2021-05-19T10:52:50.657Z", "updated_at": "2025-10-10T12:10:34.630Z", "toggles": { "progress_bar": true, "discount_enabled": true }, "schedule": { "post_purchase": "in 2 days" }, "discount": { "header": "Your 10% Coupon Awaits", "shopify_price_rules": [ { "api_c2_id": "1032299675825" } ] } } ] } Response fields Field Type Description company_id string Company ID count integer Number of surveys returned. surveys array Collection of survey summaries ordered by last update. surveys[].id string Unique survey identifier. surveys[].title string Human readable survey name. surveys[].format string Channel (Email, Checkout, Link, etc.). surveys[].status string active or archived . surveys[].question_count integer Number of questions associated with the survey. surveys[].question_ids array List of question IDs. surveys[].created_at / updated_at ISO 8601 string Creation and last modification timestamps. surveys[].toggles , schedule , triggers , discount , colors , recipients , flags , incentives , sharing , integrations object Grouped metadata copied from the survey configuration. Keys are normalized to snake_case . 3. Survey Metadata — GET /surveys/{survey_id} Retrieves the full configuration (questions, answer choices, metadata) for a single survey. curl "https://api.userloop.io/surveys/1710884429805x361876466030084100" \ -H "X-API-Key: " { "survey_id": "1710884429805x361876466030084100", "survey": "Post Purchase Email Survey", "company_id": "1621...", "questions": [ { "question": "How satisfied were you with your recent order?", "question_id": "1710884436289x589566297607766000", "type": "CSAT", "answers": [ { "answer": "1", "answer_id": "1658..." }, { "answer": "2", "answer_id": "1659..." } ] } ] } Response fields Field Type Description survey_id string Survey identifier. survey string Survey title. company_id string Owning company. questions array Ordered list of questions in the survey. questions[].question_id string Question identifier used in analytics queries. questions[].question string Question text. questions[].type string Question type (CSAT, NPS, etc.). questions[].answers array Answer options (when applicable). answers[].answer_id string Answer identifier used in analytics filters. answers[].answer string Display text for the answer choice. 4. Aggregated Analytics — GET /responses?view=counts Calculates response counts, percentages, and revenue metrics per answer option. Required query parameters: Name Type Description survey_id string Survey to analyze. Must be enabled for the calling key. question_id string Question to aggregate. ---- ---- ---- answer_ids comma-separated strings Restrict analytics to specific answer IDs. Defaults to all answers in the question. start_date , end_date YYYY-MM-DD Restrict analytics to a date window. Defaults to full history. Example request: curl "https://api.userloop.io/responses?view=counts&survey_id=1710884429805x361876466030084100&question_id=1710884436289x589566297607766000&start_date=2025-09-01&end_date=2025-09-30" \ -H "X-API-Key: " Example response (abridged): { "data": [ { "answer_id": "1658...", "answer_text": "1", "count": 75, "percentage": 50, "revenue": 15000, "aov": 200, "currency": "USD" } ], "meta": { "survey_id": "1710884429805x361876466030084100", "survey": "Post Purchase Email Survey", "question": { "id": "1710884436289x589566297607766000", "text": "How satisfied were you with your recent order?", "type": "CSAT" }, "totals": { "responses": 150, "unique_customers": 120, "sum_responses": 150 }, "filters": { "answer_ids": ["1658...", "1659..."], "start_date": "2025-09-01", "end_date": "2025-09-30" } } } Response fields Field Type Description data array Aggregated metrics per answer option (sorted by count desc). data[].answer_id string Answer identifier. data[].answer_text string Answer label (resolved from survey config when available). data[].count integer Number of responses recorded for the answer. data[].percentage number Share of total responses for the answer (0–100). data[].revenue integer Sum of order_total values associated with the answer (rounded). data[].aov integer Average order value for the answer (rounded). data[].currency string Currency code used for revenue metrics. meta object Contextual metadata for the aggregation. meta.survey_id string Survey identifier. meta.survey string Survey title (if available). meta.question object Question metadata ( id , text , type ). meta.totals.responses integer Count of responses returned by analytics ( count_survey_responses ). meta.totals.unique_customers integer Unique respondent count. meta.totals.sum_responses integer Sum of data[].count ; falls back to total responses when rows are empty. meta.filters object Effective filters applied to the analytics call. meta.filters.answer_ids array Answer IDs used for aggregation. meta.filters.start_date / end_date string ISO dates bounding the analytics query. 5. Open-Ended Feedback — GET /responses?view=open Fetches paginated free-text responses with associated metadata. Required query parameters: survey_id, question_id Optional query parameters: start_date, end_date, limit, offset Example request: curl "https://api.userloop.io/responses?view=open&survey_id=1710884429805x361876466030084100&question_id=1658178244899x246576140312903680&limit=50" \ -H "X-API-Key: " Example response (first record shown): { "data": [ { "id": "abc123", "unique_id": "abc123", "recipient": "[email protected]", "survey": "1710884429805x361876466030084100", "creation_date": "2025-09-10T12:00:00Z", "open_ended_response": "Great product!", "line_items": ["Product A"], "line_items_count": 1, "order_total": 199.99, "currency": "USD" } ], "pagination": { "total_count": 150, "page_size": 50, "current_page": 1, "total_pages": 3, "has_next_page": true, "has_previous_page": false }, "filters": { "survey_id": "1710884429805x361876466030084100", "question_id": "1658178244899x246576140312903680", "start_date": "1970-01-01", "end_date": "2025-09-30" } } Response fields Field Type Description data array Open-text responses ordered by creation_date desc. data[].id / unique_id string Stable response identifier. data[].recipient string Email (when captured). data[].survey string Survey identifier. data[].creation_date string ISO timestamp of the response. data[].open_ended_response string Free-text answer content. data[].line_items array Associated products/items (if present). data[].line_items_count integer Number of items in line_items . data[].order_total number Monetary value associated with the response. data[].currency string Currency code. pagination object Pagination metadata supplied by the endpoint. pagination.total_count integer Total open-text responses matching the filters. pagination.page_size integer Page size applied to the request. pagination.current_page integer 1-indexed page number based on offset . pagination.total_pages integer Total calculated pages. pagination.has_next_page / has_previous_page boolean Convenience flags for pagination UI. filters object Effective filters included in the request. filters.survey_id string Survey identifier. filters.question_id string Question identifier. filters.start_date / end_date string Date range applied to the query. 6. Raw Responses — GET /responses Provides tabular response data similar to the CSV export. Supports standard pagination and filtering. Sample request: curl "https://api.userloop.io/responses?survey_id=1710884429805x361876466030084100&start_date=2025-09-01&limit=25" \ -H "X-API-Key: " { "responses": [ { "id": "abc123", "unique_id": "abc123", "survey": "1710884429805x361876466030084100", "question_id": "1658...", "question_text": "How satisfied were you with your recent order?", "creation_date": "2025-09-10T12:00:00Z", "answer_id": "1658...", "answer_text": "5", "order_total": 99.99, "currency": "USD" } ], "pagination": { "total_count": 1500, "limit": 25, "offset": 0, "has_next_page": true } } Response fields Field Type Description responses array Tabular response data matching the export schema. responses[].id / unique_id string Response identifier. responses[].survey string Survey identifier. responses[].question_id string Question identifier. responses[].question_text string Question text captured at response time. responses[].creation_date string ISO timestamp of the response. responses[].answer_id string Answer identifier (if structured). responses[].answer_text string Selected answer text (or numeric/NPS value). responses[].open_ended_response string Free-text answer (when relevant). responses[].order_total number Order total associated with the response. responses[].currency string Currency code. responses[].utm_* , environment , surface , landing_site , etc. string Additional marketing and contextual metadata captured by UserLoop. pagination object Pagination metadata mirroring the request. pagination.total_count integer Total number of responses matching filters (may be null when exact count unavailable). pagination.limit integer Page size used for the query. pagination.offset integer Offset applied to the query. pagination.has_next_page boolean Indicates whether more pages are available. 7. Single Response — GET /responses/{response_id} Retrieves one record by its unique ID. Useful when cross-referencing from webhooks or CRM. curl "https://api.userloop.io/responses/abc123" \ -H "X-API-Key: " { "response": { "id": "abc123", "unique_id": "abc123", "survey": "1710884429805x361876466030084100", "question_id": "1658...", "answer_text": "5" } } Response fields Field Type Description response object Response record matching the structure in the raw responses endpoint. response.id / unique_id string Response identifier. response.survey string Survey identifier. response.question_id string Question identifier. response.answer_text string Selected answer. Additional fields (e.g., order_total , utm_* ) may be present depending on the record. Error Handling Errors are returned with a consistent envelope: { "error": "Forbidden", "code": "FORBIDDEN", "detail": "Survey not allowed for this key" } Common error codes: - UNAUTHORIZED – Invalid, revoked, or expired key. - FORBIDDEN – Missing scope, survey not in allowlist, or origin not permitted. - BAD_REQUEST – Invalid parameters (missing IDs, malformed dates, etc.). - NOT_FOUND – Record does not exist or is not accessible to the caller. - INTERNAL – Upstream failure (e.g., Supabase error). Retry or contact support. All errors are safe to expose to clients; sensitive details (such as decrypted tokens) never appear in responses. Best Practices 1. Cache survey metadata when possible; the schema only changes when you update surveys in UserLoop. 2. Respect pagination limits. Use limit/offset for large exports. 3. Filter by date to speed up analytics calls, especially when embedding reporting dashboards. 4. Secure your keys. Store them in encrypted configuration stores and rotate periodically. 5. Monitor rate limits. Contact UserLoop if you expect sustained high throughput so we can tune allocations. Support If you encounter issues: 1. Confirm your key has the correct scopes and survey access inside the dashboard. 2. Double-check parameter spelling and formats (particularly survey_id, question_id, and date strings). 3. Review HTTP status codes and error payloads for hints. 4. Reach out to your UserLoop contact or support team with the request timestamp, key_id, and the full response body for faster troubleshooting. Happy building!

UserLoop SDK

The UserLoop JavaScript SDK lets you embed a survey or quiz in any website, including custom storefronts, landing pages, account areas, and post-purchase experiences. It has no framework dependency and renders a responsive survey inside an element you choose. Quick Start Add this snippet where you want the survey to appear. Replace YOUR_SURVEY_ID with the survey ID from your UserLoop dashboard. <div id="userloop_survey"></div> <script> (function () { var SURVEY_ID = 'YOUR_SURVEY_ID'; var TARGET_ID = 'userloop_survey'; function start() { var target = document.getElementById(TARGET_ID); if (!target) return; UserLoop(SURVEY_ID, target).init(); } if (window.UserLoop) { start(); return; } var script = document.createElement('script'); script.src = 'https://cdn.userloop.io/sdk-2/userloop.js'; script.async = true; script.onload = start; document.head.appendChild(script); })(); </script> The SDK loads the published survey, applies its colours and settings, and handles navigation and response submission. A single-select survey rendered by the UserLoop JavaScript SDK Initialising the SDK If your site manages script loading itself, include the SDK and create an instance directly: <script src="https://cdn.userloop.io/sdk-2/userloop.js"></script> <div id="userloop_survey"></div> <script> const survey = UserLoop( 'YOUR_SURVEY_ID', document.getElementById('userloop_survey'), { email_collection: true, expanded_mode: false, }, { customer: {}, transaction: {}, } ); survey.init(); </script> The initializer is: UserLoop(surveyId, targetElement, config, context) Argument Description surveyId The survey ID shown in UserLoop. targetElement The DOM element in which the survey should render. config Optional SDK behaviour and preview settings. context Optional customer and transaction data to attach to responses and use for targeting. Call init() after the target element exists in the page. Common Configuration Options Option Type Description email_collection boolean Shows an email field when the survey requires one and the supplied customer has no email address. expanded_mode boolean Expands the survey to the width of its container. preview_mode boolean Renders the survey for design and testing without applying live question targeting. preview_start_question_id string Opens a preview on a particular question. preview_thankyou_page boolean Opens the thank-you screen directly in preview mode. auto_answer_first_question string Selects and submits a particular answer ID on the first question. Useful for links from email campaigns. allow_restart boolean Adds an option to restart the survey after completion. surface string Identifies a custom integration surface in response metadata. The default is sdk . quiz_mode boolean Enables quiz behaviour for a survey configured as a quiz. config object Uses a preloaded survey document and skips the configuration request. It must be a plain object in the same shape as UserLoop's published survey document. config_url string Fetches the survey document from your own HTTP or HTTPS URL instead of UserLoop's default survey endpoint. Ignored when config is set. language string Adds or replaces the language query parameter when the SDK fetches from config_url . Branding, colours, progress bars, button text, thank-you content, rewards, and most presentation settings come from the published survey configuration rather than the embed code. Loading a Preloaded or Custom Survey Document Use config when your page already has the published survey document. The SDK renders that object directly and does not make a configuration request: const survey = UserLoop( 'YOUR_SURVEY_ID', document.getElementById('userloop_survey'), { config: surveyDocument, surface: 'landingpage', } ); await survey.init(); Use config_url when your application serves the survey document from its own endpoint. Add language when that endpoint supports a localized document: const survey = UserLoop( 'YOUR_SURVEY_ID', document.getElementById('userloop_survey'), { config_url: 'https://example.com/survey?survey_id=YOUR_SURVEY_ID', language: 'fr', } ); await survey.init(); The SDK preserves existing query parameters in config_url and adds or replaces only language. Your endpoint must return the same survey-document shape as UserLoop's standard survey endpoint and must allow the respondent's browser to fetch it. These options apply only to the survey ID passed to UserLoop(...): - init() and refresh() continue to use the preloaded document or custom URL for that survey. - refresh(newSurveyId) and setSurveyId(newSurveyId) use UserLoop's standard survey endpoint when switching to a different survey ID. - config takes priority when both config and config_url are provided. - An invalid config value or a config_url that is not an HTTP or HTTPS URL is ignored. The SDK writes a warning to the browser console and falls back to the standard survey endpoint. Do not put API keys or other secrets in config_url. The SDK does not log the full custom URL, but the URL is still used by the respondent's browser. Instance Methods UserLoop(...) returns an instance with these methods: Method Description init() Loads and renders the survey. Returns a promise. refresh() Reloads the current survey configuration. refresh(newSurveyId) Switches the instance to another survey and loads it. setSurveyId(newSurveyId) Alias for switching surveys with refresh(newSurveyId) . mount() Mounts the survey into its configured target element. Supported Question Types The SDK renders the question types configured in UserLoop, including Single Select, Multi Select, Open Ended, Email, Date, Phone, Number Score and NPS, CSAT, Video, and Info Screen. Follow-up questions, optional questions, custom button text, auto-proceed, redirect links, progress indicators, and thank-you rewards are driven by the survey configuration. See Question Types for guidance on choosing and configuring questions. Info Screens An Info Screen is a content-only step. Use one to introduce a survey, explain what will happen next, divide a longer survey into sections, or describe a reward before asking a question. An Info Screen with a heading, image, explanatory copy, and Continue button in the UserLoop SDK An Info Screen can contain: - A heading - Multi-line supporting text - An optional image - The survey's normal Continue button It is placed in the survey like any other question, so you can position it at the beginning, end, or between questions. The same product and customer targeting rules also apply. For API-driven survey configurations, an Info Screen has type: "Info Screen": { "question_id": "intro-screen", "type": "Info Screen", "question": "Before you start", "question_subheading": "This takes about two minutes.\n\nYou'll receive 10% off at the end.", "info_image": "https://example.com/survey-intro.jpg", "required": false, "answers": [] } Info Screen behaviour: - It collects and submits no answer, so it creates no response row and does not appear in response exports. - It advances the progress bar as a survey step, but is not numbered as a question. - Supporting text keeps its line breaks. - A broken optional image is removed without preventing the rest of the screen from loading. - The Continue button is always available. Required and skip settings do not apply. - An Info Screen cannot be used as a conditional follow-up question. - If email collection is enabled, the email field appears on the first answerable question rather than on the Info Screen. - A live survey containing only Info Screens is not shown because there is no response to collect. Preview mode can still render it for design review. Info Screens are supported by the JavaScript SDK and by UserLoop's Shopify Checkout, Popup Survey, and App Block survey experiences. Passing Customer and Transaction Data Pass customer and order context as the fourth argument. All fields are optional; send only the data your site has. const customer = { id: 'customer_123', email: '[email protected]', firstName: 'Alex', lastName: 'Morgan', order_count: 3, }; const transaction = { transaction_id: 'order_456', order_creation_date: '2026-08-07T09:30:00.000Z', total: 89.95, currency: 'GBP', coupon_code: 'WELCOME10', platform: 'Custom Storefront', source: 'website', utm_source: 'newsletter', products: [ { id: 'shopify-product-id', name: 'Example product', product_url: 'https://example.com/products/example-product', }, ], }; const survey = UserLoop( 'YOUR_SURVEY_ID', document.getElementById('userloop_survey'), { email_collection: true }, { customer, transaction } ); await survey.init(); This context enriches the response data and lets UserLoop decide whether targeted questions apply. Question Targeting Questions, including Info Screens, can be limited by product or customer status in the survey configuration: - Product targeting matches against product IDs in the transaction. For Shopify, pass product IDs rather than variant IDs. - New-customer and returning-customer targeting uses customer.order_count or transaction.order_count. - Live embeds hide questions whose targeting rules do not match. - Preview mode intentionally ignores targeting and renders every configured question so the complete design can be reviewed. If targeting removes every answerable question, the live survey container is hidden instead of displaying an empty survey. Refreshing or Switching Surveys Use refresh() when your page gains updated order data or when you want to reload the published survey configuration: await survey.refresh(); To switch the same embed to another survey: await survey.refresh('ANOTHER_SURVEY_ID'); // Equivalent: await survey.setSurveyId('ANOTHER_SURVEY_ID'); Video Responses Video questions let respondents record with their camera or choose an existing file. The SDK handles permission prompts, upload progress, retry messaging, and the normal response submission flow. Whether Video is available and its maximum duration are controlled by the survey configuration. Because camera and microphone access require a secure browser context, serve pages containing Video questions over HTTPS. If a respondent declines camera access, the file-upload option can still be used where supported. Integrations When the survey is connected to integrations such as Shopify, Klaviyo, Slack, gift cards, webhooks, or response notifications, the published survey includes the necessary integration flags. The SDK copies those flags into response payloads automatically; you do not need to duplicate them in your embed configuration. Testing and Troubleshooting Before publishing your integration: 1. Confirm the survey is published and the survey ID is correct. 2. Confirm https://cdn.userloop.io/sdk-2/userloop.js is allowed by your site's Content Security Policy. 3. Make sure the target element exists before calling UserLoop(...). 4. Test at mobile and desktop widths. 5. Pass representative product IDs and order counts when testing targeted questions. 6. Use preview_mode: true to review all questions, including those whose live targeting would not match your test customer. 7. Check the browser console and Network panel if the survey does not load. Do not place private credentials or API keys in browser-side SDK configuration. The survey ID is intended to be used by the browser; secret server-side keys are not. Need Help? If you have questions about an SDK integration, contact us through live chat. Include the page URL, survey ID, browser-console error, and a description of the behaviour you expected so we can investigate quickly.

MCP Server

The UserLoop MCP server connects Claude, ChatGPT, and other compatible AI clients to your UserLoop survey data through the Model Context Protocol. You can explore feedback, compare answer counts, review NPS, find individual responses, and chart response volume by asking questions in natural language. All UserLoop MCP tools are read-only. They can retrieve and analyse survey data, but they cannot create, edit, or delete anything in your UserLoop account. What You Can Ask Try prompts such as: - “Chart response volume across all surveys for the last 90 days and break it down by survey.” - “How did customers answer ‘How did you hear about us?’ this month?” - “Show the NPS score and promoter, passive, and detractor breakdown for our quarterly survey.” - “Summarise the main themes in open-text responses from the post-purchase survey.” - “Compare answer counts, revenue, and average order value for this question.” Your AI client chooses the appropriate UserLoop tools automatically. Interactive Charts Clients that support MCP Apps can display UserLoop results as interactive charts directly in the conversation. Clients without MCP Apps support still receive the same data as compact JSON. Response Volume Over Time View response volume for one survey, one question, or every survey combined. Switch between 7 days, 30 days, 90 days, 12 months, or all time, and use automatic, daily, weekly, or monthly grouping. Company-wide results also include a per-survey breakdown and a data-table view. UserLoop MCP response-volume chart showing weekly responses across all surveys Answer Counts Multiple-choice results are shown as a horizontal bar chart with response counts and share. Hover over a result to see more detail, including revenue and average order value when available. UserLoop MCP answer-count chart for a multiple-choice survey question NPS Number Score and NPS questions include the headline NPS score, average score, promoter/passive/detractor split, 0–10 score distribution, and revenue metrics when available. UserLoop MCP NPS chart showing score and promoter, passive, and detractor segments The screenshots above use example data. When connected, the charts use your UserLoop survey data. Available Tools The server currently provides eight tools: Tool What it does userloop_health Checks the connection and confirms your API key is valid userloop_list_surveys Lists all surveys, including their questions and question types userloop_get_survey Retrieves one survey and its questions userloop_analytics_counts Returns answer counts for a question, including NPS and revenue metrics when available userloop_responses_timeseries Charts response volume by day, week, or month for one survey, one question, or all surveys userloop_responses_open Retrieves paginated open-text responses for a question userloop_responses_raw Retrieves paginated raw responses for a survey userloop_get_response Retrieves one response by its ID Before You Connect Create a UserLoop API key in your UserLoop account. API keys start with ul_live and are shown only once, so create a new one if you no longer have the full key. See UserLoop API for more information. Your MCP server URL is: https://mcp.userloop.io/mcp?api_key=ul_live_... Replace ul_live_... with your complete API key. Keep this URL private. It contains your UserLoop API key. Do not share it, publish it, or include it in screenshots. Rotate the key immediately if it is exposed. Connect to Claude or Claude Desktop Remote MCP connectors are available on supported Claude plans. 1. Open Settings > Connectors in Claude or Claude Desktop. 2. Select Add custom connector. 3. Name the connector UserLoop. 4. Paste the complete MCP server URL, including ?api_key=.... 5. Select Add. 6. In a conversation, open Search and tools and enable the UserLoop connector. Remote servers in Claude Desktop are configured through Settings > Connectors, not claude_desktop_config.json. See Anthropic’s custom connector guide for current plan and workspace requirements. Connect to ChatGPT Custom MCP apps are available to supported ChatGPT workspace plans and may need to be enabled by a workspace administrator. 1. Enable developer mode if your workspace requires it. 2. Open Settings > Apps > Create, or ask a workspace administrator to open Workspace settings > Apps > Create. 3. Enter the complete MCP server URL, including ?api_key=.... 4. Choose No authentication if prompted. The UserLoop API key is already included in the URL you entered. 5. Scan the available tools and create the app. 6. Enable UserLoop from the app menu in a conversation. See OpenAI’s developer mode and MCP apps guide for current availability and administrator controls. Connect from Another MCP Client Use the complete URL above with a client that supports remote MCP servers over Streamable HTTP. A typical configuration looks like this: { "mcpServers": { "userloop": { "type": "http", "url": "https://mcp.userloop.io/mcp?api_key=ul_live_..." } } } The exact configuration format depends on your MCP client. Privacy and Email Redaction Email redaction is enabled by default for open-text, raw-response, and individual-response tools. For example, [email protected] is returned in a masked form. This helps protect customer privacy when data is sent to a third-party AI service. The response tools support redact_emails: false, but only disable redaction when you have a clear reason and are comfortable sharing those addresses with your AI provider. Response-Volume Notes - Response-volume charts can combine all surveys in a single request and include totals for each survey. - A response-volume row represents an answer given to a question, rather than a completed survey submission. Use a question filter when you want to count answers to one specific question. - The server reads up to 2,000 response rows for a time-series request. If a result is marked as truncated, narrow the date range or select a specific survey. Troubleshooting The connection or health check fails - Confirm that the URL includes the complete ?api_key=ul_live_... query parameter. - The MCP server no longer accepts the old X-UserLoop-Key header method. - Create a new UserLoop API key if the original key has been revoked, lost, or exposed. The new time-series tool is missing Refresh or rescan the connector’s tools. In ChatGPT, newly added tools may need to be enabled by a workspace administrator. If refreshing does not work, remove and re-add the connector using the current URL. Charts are not displayed Your client may not support MCP Apps, or it may need to refresh the UserLoop connector. The tool still returns the underlying data, which the AI assistant can analyse and present as text or a table. Need Help? If you have questions about the UserLoop MCP server, contact us through live chat and we’ll be happy to help.