Service Integration Reference

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.

1. Client Integration

Storefront JavaScript SDK

window.LoopAiProductRecommender

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);
2. Security & Forwarding

Shopify App Proxy & HMAC Authentication

HMAC-SHA256

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.

shop Shopify myshopify domain parameter (e.g. store.myshopify.com).
timestamp Unix epoch timestamp when the request was initiated by Shopify.
signature HMAC-SHA256 hex digest of sorted query parameters.
3. Asynchronous Pipeline

Asynchronous Endpoints & WebSockets

HTTP 202 + 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.

POST /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
}
POST /api/v1/async/followup-questions

Queue generation of a catalog-grounded clarifying question with structured options.

{ "prompt": "running shoes" }
POST /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);
  }
});
Safety Policies & Error Handling Contract

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.