Quick Start
Copy-Paste Embed (easiest way) Drop the snippet below anywhere you want the survey to appear. Replace YOUR_SURVEY_ID with the ID from the UserLoop dashboard and you're done—no extra setup required.
(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); })();
When you need more control If you prefer to manage script loading yourself, or you need to toggle advanced options such as email_collection or branding, initialise the SDK manually once the DOM is ready:
window.addEventListener('DOMContentLoaded', function () { const SURVEY_ID = 'YOUR_SURVEY_ID'; const TARGET_ELEMENT = document.getElementById('userloop_survey'); const CONFIG = { email_collection: true, expanded_mode: false, userloop_branding: false, }; const userLoopInstance = UserLoop(SURVEY_ID, TARGET_ELEMENT, CONFIG); userLoopInstance.init(); });
Add optional customer and transaction context by passing a fourth argument when available (see Passing Customer and Transaction Data).
Initializer Anatomy UserLoop(surveyId, targetElement, config, context) returns an instance with init, refresh, setSurveyId, and mount methods. Below is a summary of the most common configuration options for production use:
| Option | Type | Description |
|---|---|---|
email_collection |
boolean |
Displays an email capture step when the survey requires it and the customer record lacks an email address. |
expanded_mode |
boolean |
Forces the survey into an expanded layout that stretches to the width of its container. Useful for wider marketing pages. |
userloop_branding |
boolean |
When true, shows the "Powered by UserLoop" footer if your survey has branding enabled. |
surface |
string |
Identifies the integration surface for analytics (defaults to sdk). Update if you manage multiple surfaces. |
quiz_mode |
boolean |
Enables quiz mode. After the respondent answers all questions, the SDK calls UserLoop's AI recommendation engine and displays personalized product suggestions. |
quiz_api_key |
string |
API key for the quiz recommendation service. Required when quiz_mode is true. |
allow_restart |
boolean |
Shows a "Restart survey" button on the thank-you page, letting respondents retake the survey. |
auto_answer_first_question |
string |
Pass an answer_id to automatically select and submit the first question. Useful for post-purchase flows where the trigger determines the first answer. |
The fourth context argument is optional. Pass customer and transaction objects when you have them; the SDK defaults to empty objects when they are omitted.
Passing Customer and Transaction Data Enriching responses with customer and order context makes it easier to segment and act on feedback. When you're ready, populate any of the fields below before calling init() or refresh(). Each field is optional—provide only what you have.
Customer Fields
email: Customer email address *id: Internal customer identifier (e.g., CRM or platform ID)firstName,lastName: Optional name fields *phone: Optional phone number
Transaction Fields
transaction_id: Order or transaction identifier *order_creation_date: ISO-8601 timestamp (2024-03-01T10:05:00.000Z)total: Numeric order total *currency: ISO currency code (e.g.,USD)coupon_code: Applied discount code *utm_source,utm_medium,utm_campaign,utm_content,utm_term: Attribution parameters *platform: Platform name (e.g.,Shopify,WooCommerce,Custom Storefront)source: Traffic source descriptor (website,email, etc.)products: Array of{ id, name, product_url }entries describing purchased items When additional fields become available (for example, after checkout completes), update the objects and calluserLoopInstance.refresh()to pull the latest survey configuration while preserving current state when possible.
const customer = { email: order.email, id: order.customerId, }; const transaction = { transaction_id: order.id, total: order.total, currency: order.currency, order_creation_date: order.createdAt, products: order.items.map((item) => ({ id: item.productId, name: item.title, product_url: item.url, })), }; // Later, pass the context when initializing or refreshing const userLoopInstance = UserLoop(SURVEY_ID, TARGET_ELEMENT, CONFIG, { customer, transaction, }); userLoopInstance.init();
Platform Examples
Shopify Liquid
const SURVEY_ID = 'YOUR_SURVEY_ID'; const TARGET_ELEMENT = document.getElementById('userloop_survey'); const CONFIG = { email_collection: true }; const customer = { email: '{{ customer.email | escape }}' || '', id: '{{ customer.id }}' || '', }; const transaction = {% if order %}{ transaction_id: '{{ order.order_number }}', total: {{ order.total_price | default: 0 }}, currency: '{{ order.currency | escape }}', order_creation_date: '{{ order.created_at | date: "%Y-%m-%dT%H:%M:%S.%LZ" }}', coupon_code: '{{ order.discount_code | escape }}', products: [ {% for line_item in order.line_items %} { id: '{{ line_item.product_id }}', name: '{{ line_item.title | escape }}', }{% unless forloop.last %},{% endunless %} {% endfor %} ], platform: 'Shopify', source: 'website', }{% else %}{}{% endif %}; document.addEventListener('DOMContentLoaded', function () { const userLoopInstance = UserLoop(SURVEY_ID, TARGET_ELEMENT, CONFIG, { customer, transaction, }); userLoopInstance.init(); });
WooCommerce (PHP Template)
const SURVEY_ID = 'YOUR_SURVEY_ID'; const TARGET_ELEMENT = document.getElementById('userloop_survey'); const CONFIG = { email_collection: true }; const customer = { email: '<?php echo esc_js( wp_get_current_user()->user_email ?? '' ); ?>', id: '<?php echo esc_js( wp_get_current_user()->ID ?? '' ); ?>', }; <?php $transaction_payload = []; if ( function_exists( 'wc_get_order' ) && isset( $_GET['order-received'] ) ) { $order = wc_get_order( intval( $_GET['order-received'] ) ); if ( $order ) { $transaction_payload = [ 'transaction_id' => $order->get_id(), 'currency' => $order->get_currency(), 'total' => $order->get_total(), 'order_creation_date' => $order->get_date_created()->date( 'c' ), 'products' => array_map( fn ( $item ) => [ 'id' => $item->get_product_id(), 'name' => $item->get_name(), ], $order->get_items() ), 'platform' => 'WooCommerce', 'source' => 'website', ]; } } ?> const transaction = <?php echo wp_json_encode( $transaction_payload ); ?>; document.addEventListener('DOMContentLoaded', function () { const userLoopInstance = UserLoop(SURVEY_ID, TARGET_ELEMENT, CONFIG, { customer, transaction, }); userLoopInstance.init(); });
Advanced Capabilities
- Refresh with new data: If customer or transaction details change after initialization (for example, when a user completes checkout), update your local objects and call
userLoopInstance.refresh()to rebuild the survey with the latest context. - Switch surveys dynamically: Use
userLoopInstance.setSurveyId('NEW_SURVEY_ID')to load a different survey into the same container without reloading the page. - Remount after DOM changes: On single-page applications, call
userLoopInstance.mount()when route transitions replace the container element so the SDK can reattach itself. - Collect emails conditionally: Combine
email_collection: truewith a populatedcustomer.emailto skip the email step for known customers while still collecting contact info for anonymous visitors. - Quiz mode with product recommendations: Set
quiz_mode: trueand provide aquiz_api_keyto enable AI-powered product recommendations. After all questions are answered, the SDK displays personalized product cards with images, prices, AI-generated reasoning, and "View Product" links with UTM tracking. - Auto-answer the first question: Pass
auto_answer_first_question: 'ANSWER_ID'to automatically select and submit the first answer. This is useful for email campaign integrations where the link click itself determines the first response. - Allow survey restarts: Set
allow_restart: trueto add a "Restart survey" button on the thank-you page, letting respondents retake the survey without reloading the page.
Best Practices
- Load the SDK once per page and reuse the returned instance rather than creating duplicates.
- Keep your container element stable; if your framework re-renders nodes, call
mount()after the new node is in place. - Validate that the
SURVEY_IDconfigured in production points to the correct environment-specific survey. - Test the full survey and quiz flow (including any recommendation steps configured in the dashboard) to ensure responses and follow-up logic behave correctly.
- Monitor network requests in your browser developer tools to confirm the SDK can reach
https://userloop.ioendpoints from your environment.