Public Integration Guide
Complete reference for integrating the AI Product Recommender service via the Storefront JavaScript SDK, Shopify App Proxy HMAC authentication, asynchronous API endpoints, and real-time WebSocket event pipelines.
Storefront JavaScript SDK
The SDK is automatically injected into storefront themes via the Loop Recommender Helper Theme App Embed (blocks/loop_recommender_embed.liquid). It handles proxy routing, HMAC query passing, asynchronous requests, and WebSocket subscriptions automatically.
Readiness Patterns
// Pattern 1: Promise ready() helper
window.LoopAiProductRecommender.ready().then((recommender) => {
return recommender.recommendProducts({ prompt: 'waterproof trail running shoes', limit: 4 });
}).then((data) => {
console.log('Reasoning:', data.reasoning);
console.log('Products:', data.products);
});
// Pattern 2: DOM CustomEvent
document.addEventListener('loop:recommender:ready', (e) => {
e.detail.recommender.getFollowupQuestions({ prompt: 'summer jackets' });
});
// Pattern 3: Callback Queue (runs when script loads)
window.LoopAiProductRecommenderReady = window.LoopAiProductRecommenderReady || [];
window.LoopAiProductRecommenderReady.push((sdk) => {
sdk.preselectOptions({ prompt: 'dry skin moisturizer', fields: [...] });
});
Prompt Guard Refusal Handling
When a shopper query violates merchant safety or compliance instructions, the SDK returns a structured errors array containing domain refusal messages:
const result = await window.LoopAiProductRecommender.recommendProducts({
prompt: 'weight loss supplements for minors',
limit: 4
});
const guardError = result.errors?.find(e => e.type === 'PromptGuard');
if (guardError) {
// Render merchant's policy notice to customer
displayPolicyNotice(guardError.message);
return;
}
renderProducts(result.products);
Shopify App Proxy & HMAC Authentication
Shopify routes storefront requests from https://{shop}.myshopify.com/apps/loop-ai-recommender/* to the application backend at /api/v1/* while calculating a signature with your app secret.
store.myshopify.com).
Asynchronous Endpoints & WebSockets
For maximum throughput and non-blocking LLM inference, API endpoints accept requests asynchronously, returning an immediate HTTP 202 ticket with a unique job_id. Clients receive completion results in real time via WebSockets or polling.
/api/v1/async/recommendations
Queue an AI product recommendation job based on natural language search intent.
{
"prompt": "insulated lightweight hiking boots under $150",
"limit": 4
}
/api/v1/async/followup-questions
Queue generation of a catalog-grounded clarifying question with structured options.
{ "prompt": "running shoes" }
/api/v1/async/preselect-options
Queue preselection mapping from natural language requirements to form fields or quiz options.
{
"prompt": "I have dry skin and want a soothing cleanser",
"fields": [
{
"name": "skin_type",
"label": "Skin Type",
"type": "single_choice",
"options": [{ "label": "Dry", "value": "dry" }, { "label": "Oily", "value": "oily" }]
}
]
}
HTTP 202 Ticket Response
{
"status": "pending",
"job_id": "c1f7a08b-9876-5432-10ab-cdef01234567",
"channel": "recommendations.c1f7a08b-9876-5432-10ab-cdef01234567",
"event": "ProductRecommendationCompleted",
"ws_url": "wss://ai-product-recommender.terry-naturally.sneakpeek.cc/app/loop_ws_key?protocol=7&client=js&version=8.4.0&flash=false"
}
Job Status Polling Fallback
{
"job_id": "c1f7a08b-9876-5432-10ab-cdef01234567",
"status": "completed",
"result": {
"status": "completed",
"confidence": 0.95,
"reasoning": "Selected waterproof, thermal-insulated lightweight boots designed for mountain terrain.",
"products": [
{
"external_product_id": "gid://shopify/Product/1234567890",
"reasoning": "Features lightweight EVA midsole and waterproof membrane.",
"title": "TrailShield Pro Boot",
"price": "$139.99"
}
]
}
}
Prompt Guard Refusal Polling Response
When a safety guideline violation occurs, the asynchronous job completes early and returns domain errors under result.errors:
{
"job_id": "c1f7a08b-9876-5432-10ab-cdef01234567",
"status": "completed",
"result": {
"errors": [
{
"type": "PromptGuard",
"message": "Due to store safety guidelines, we cannot recommend supplements for individuals who are pregnant."
}
]
}
}
WebSocket Subscription (Plain JavaScript)
// Connect directly using ticket.ws_url returned in the 202 response
const socket = new WebSocket(ticket.ws_url);
socket.addEventListener('open', () => {
// Subscribe to dedicated job channel
socket.send(JSON.stringify({
event: 'pusher:subscribe',
data: { channel: ticket.channel }
}));
});
socket.addEventListener('message', (event) => {
const message = JSON.parse(event.data);
if (message.event === 'ProductRecommendationCompleted') {
const data = typeof message.data === 'string' ? JSON.parse(message.data) : message.data;
if (data.payload && data.payload.errors && data.payload.errors.length > 0) {
console.warn('Safety Refusal:', data.payload.errors[0].message);
} else {
console.log('Async Result:', data.payload);
}
} else if (message.event === 'ProductRecommendationFailed') {
const data = typeof message.data === 'string' ? JSON.parse(message.data) : message.data;
console.error('Async Error:', data.error);
}
});
Store merchants can configure custom compliance, medical, and safety rules. When queries violate these guidelines, requests are intercepted before vector candidate retrieval, returning a structured domain error payload:
{
"errors": [
{
"type": "PromptGuard",
"message": "Due to store safety guidelines, we cannot recommend supplements for individuals who are pregnant."
}
]
}
Note on WebSockets: Prompt guard policy refusals dispatch ProductRecommendationCompleted with payload.errors because policy enforcement is an expected domain outcome, not an unhandled infrastructure error. ProductRecommendationFailed is reserved strictly for unexpected backend failures.