UserLoop SDK
UserLoop SDK Integration Guide
The UserLoop SDK lets you embed survey and quiz experiences anywhere on your website so you can capture feedback and transactional context right alongside customer journeys. This guide walks through the core integration steps, explains how to pass customer and order data, and highlights advanced capabilities for production deployments.
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.
<!-- UserLoop Survey Embed -->
<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>
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:
<script async src="https://cdn.userloop.io/sdk-2/userloop.js"></script>
<div id="userloop_survey"></div>
<script>
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();
});
</script>
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 |
---|---|---|
| | Displays an email capture step when the survey requires it and the customer record lacks an email address. |
| | Forces the survey into an expanded layout that stretches to the width of its container. Useful for wider marketing pages. |
| | When |
| | Identifies the integration surface for analytics (defaults to |
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 addressid
: Internal customer identifier (e.g., CRM or platform ID)firstName
,lastName
: Optional name fieldsphone
: Optional phone number
Transaction Fields
transaction_id
: Order or transaction identifierorder_creation_date
: ISO-8601 timestamp (2024-03-01T10:05:00.000Z
)total
: Numeric order totalcurrency
: ISO currency code (e.g.,USD
)coupon_code
: Applied discount codeutm_source
,utm_medium
,utm_campaign
,utm_content
,utm_term
: Attribution parametersplatform
: 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 call userLoopInstance.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
<div id="userloop_survey"></div>
<script>
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();
});
</script>
WooCommerce (PHP Template)
<div id="userloop_survey"></div>
<script>
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();
});
</script>
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: true
with a populatedcustomer.email
to skip the email step for known customers while still collecting contact info for anonymous visitors.
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_ID
configured 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.io
endpoints from your environment.
Updated on: 19/09/2025
Thank you!