Navigation

API Principles

Luigi's Box provides HTTP-based APIs for two main purposes: serving search results to users (Public) and managing your data/configuration (Private).

1. Service Reference & Endpoints

Note Base URL Note: Most services (Search, Content, Recommender) live on live.luigisbox.com. The Analytics Event API lives on api.luigisbox.com.

Versioning Strategy

Our APIs are versioned (e.g., /v1/, /v2/). While the principles below apply to all versions, always check the specific integration guide for the latest stable version number.

Public API (Unauthenticated)

Auth: Requires only your public tracker_id.

Usage: Safe to call from Frontend (Browsers, Mobile Apps).

Category Method Base URL Endpoint Description
Search GET/POST live.luigisbox.com /search Full-text search & facets.
Autocomplete GET live.luigisbox.com /autocomplete/v* Prefix-based suggestions.
Recommender POST live.luigisbox.com /v*/recommend Product recommendations.
Shopping Assistant POST live.luigisbox.com /v*/assistant AI-powered structured dialogue that helps customers discover products.
Top items GET live.luigisbox.com /v*/top_items Most popular items.
Trending queries GET live.luigisbox.com /v*/trending_queries Trending search terms.
Analytics POST api.luigisbox.com /v* Events API. Ingest user interactions.

Private API (Authenticated)

Auth: Requires HMAC Signature using your Private Key. Usage: Backend Server ONLY.

Indexing Hub (Content Management)

Manage your product catalog.

Method Base URL Endpoint Description
POST live.luigisbox.com /v*/content Create/Replace. Add new products.
PATCH live.luigisbox.com /v*/content Partial Update. Update specific fields.
DELETE live.luigisbox.com /v*/content Delete. Remove products.
PATCH live.luigisbox.com /v*/update_by_query Batch Update. Update items matching a query.
GET live.luigisbox.com /v*/content_export Export. Download your current index data.

Recommender Customization

Manage pinning rules and batch recommendations.

Method Base URL Endpoint Description
POST live.luigisbox.com /v*/recommender/pin/{ID}/scopes Define pinning scopes (rules).
GET live.luigisbox.com /v*/recommender/pin/{ID}/summary Get pinning configuration summary.
POST live.luigisbox.com /v*/recommender/batching/{ID}/users Trigger batch recommendation generation for users.
* live.luigisbox.com /v*/recommender/pin/* Various CRUD endpoints for managing pins.

Danger Security Warning: Never call the Private API (Content/Pinning) from a frontend application. Doing so exposes your Private Key and allows attackers to corrupt your data.


2. Authentication (HMAC)

Only the Private API endpoints require a custom HMAC-SHA256 signature.

Credentials

You will need your API credentials found in Luigi's Box App > Settings > Integration Settings:

  • Tracker_ID (Public Key): Identifies your account.
  • Secret Key (Private Key): Used to sign requests.

The Signing Flow

To authenticate, you must construct a specific "String to Sign," hash it, and attach it as a header.

flowchart TD subgraph Construction [1. Construct String to Sign] direction TB A[HTTP Method] B[Content-Type] C[Date Header] D[Path] A & B & C & D -->|Join with \n| E(Canonical String) end subgraph Signing [2. Crypto Operations] E -->|HMAC SHA-256| F{Hash Function} Key[Private Secret] -.-> F F -->|Binary Output| G(Raw Bytes) G -->|Base64 Encode| H(Final Signature) end subgraph Header [3. Attach Header] H -->|Format String| I["Authorization: ApiAuth tracker_id:signature"] I -->|Send| J((API Server)) end style F fill:#f96,stroke:#333,stroke-width:2px style I fill:#bbf,stroke:#333,stroke-width:2px

Detailed Specification

Step 1: Required Headers

Every authenticated request must include these headers. The values used here must match the values used in the signature generation byte-for-byte.

Header Description
Date The current timestamp in HTTP Date format (e.g., Thu, 29 Jun 2017 12:11:16 GMT).
Note: Our server clock tolerance is ±5 seconds. You must regenerate the date and signature for every request.
Content-Type Describes your payload (e.g., application/json; charset=utf-8).
Authorization The authentication string. Format: AppName [tracker_id]:[signature]

Step 2: Canonical String Construction

Concatenate the following four strings, separated by a newline character (\n).

  1. HTTP Verb: Uppercase (e.g., POST, PUT, DELETE).
  2. Content-Type: Exactly as sent in the header (e.g., application/json; charset=utf-8).
  3. Date: Exactly as sent in the header.
  4. Resource Path: The absolute path excluding the query string (e.g., /v1/content, not /v1/content?foo=bar).

Step 3: Signature Calculation

  1. Compute the HMAC-SHA256 digest of the Canonical String using your Secret Key.
  2. Encode the binary digest into a Base64 string.

Example Implementations

require 'time'
require 'openssl'
require 'base64'

def digest(key, method, endpoint, date)
  content_type = 'application/json; charset=utf-8'

  data = "#{method}\n#{content_type}\n#{date}\n#{endpoint}"

  dg = OpenSSL::Digest.new('sha256')
  Base64.strict_encode64(OpenSSL::HMAC.digest(dg, key, data)).strip
end


date = Time.now.httpdate
digest("secret", "POST", "/v1/content", date)
<?php

function digest($key, $method, $endpoint, $date) {
  $content_type = 'application/json; charset=utf-8';

  $data = "{$method}\n{$content_type}\n{$date}\n{$endpoint}";

  $signature = trim(base64_encode(hash_hmac('sha256', $data, $key, true)));

  return $signature;
}


$date = gmdate('D, d M Y H:i:s T');
digest("secret", "POST", "/v1/content", $date);
// This configuration and code work with the Postman tool
// https://www.getpostman.com/
//
// Start by creating the required HTTP headers in the "Headers" tab
//  - Content-Type: application/json; charset=utf-8
//  - Authorization: {{authorization}}
//  - date: {{date}}
//
// The {{variable}} is a postman variable syntax. It will be replaced
// by values precomputed by the following pre-request script.

var privateKey = "your-secret";
var publicKey = "your-tracker-id";
var contentType = "application/json; charset=utf-8";

var requestUri = request.url.replace(/^.*\/\/[^\/]+/, '').replace(/\?.*$/, '');
var timestamp = new Date().toUTCString();
var signature = [request.method, contentType, timestamp, requestUri].join("\n");

var encryptedSignature = CryptoJS.HmacSHA256(signature, privateKey).toString(CryptoJS.enc.Base64);

pm.request.headers.add({
    key: "Content-Type",
    value: contentType
});
pm.request.headers.add({
    key: "Authorization",
    value: "ApiAuth " + publicKey + ":" + encryptedSignature
});
pm.request.headers.add({
    key: "Date",
    value: timestamp
});

#!/bin/bash

digest() {
  KEY=$1
  METHOD=$2
  CONTENT_TYPE="application/json; charset=utf-8"
  ENDPOINT=$3
  DATE=$4

  DATA="$METHOD\n$CONTENT_TYPE\n$DATE\n$ENDPOINT"

  printf "$DATA" | openssl dgst -sha256 -hmac "$KEY" -binary | base64
}

date=$(env LC_ALL=en_US date -u '+%a, %d %b %Y %H:%M:%S GMT')
echo $(digest "secret", "GET", "/v1/content", $date)

3. User Identification

While "Authentication" identifies your server to us, "User Identification" identifies your customer to the AI.

To maintain session continuity and personalization, you must handle two specific IDs correctly in every frontend request (Search, Autocomplete, Analytics).

ID Type Parameter Description
Device ID device_user_id Required. A persistent identifier for the browser/device (e.g., a cookie ID). Must remain constant for the entire session, regardless of login state.
Auth ID auth_user_id Optional. The database ID of the logged-in user. Send this only when the user is logged in.

The "Session Continuity" Rule

The device_user_id is the anchor. You must never change it mid-session, even if the user logs in or out.

sequenceDiagram participant User participant App participant LB as Luigi's Box Note over User, App: 1. Anonymous Browsing User->>App: Views Homepage App->>LB: Event (device_id: "COOKIE_123") Note over User, App: 2. User Logs In (ID: USER_99) User->>App: Enters Credentials App->>LB: Event (device_id: "COOKIE_123", auth_id: "USER_99") Note over LB: CORRECT: History merged because device_id matches. Note over User, App: 3. User Logs Out User->>App: Clicks Logout App->>LB: Event (device_id: "COOKIE_123") Note over LB: CORRECT: Session continues anonymously.

Danger Common Mistake: Do not replace the device_user_id with the auth_user_id upon login.

  • Wrong: Anonymous (dev_1) -> Login -> Logged In (user_99)

  • Result: The system thinks a new user has arrived. The session breaks, and personalization history is lost.

4. Rate Limiting (Throttling)

We enforce rate limits to ensure stability. Limits are applied per Tracker ID (Account level) and per IP Address.

Endpoint Account Limit IP Limit
Content Updates 500 req / min
5 concurrent reqs
-
Autocomplete 800 req / min 30 req / 5s
Search 350 req / min 30 req / 5s
Recommender 60 req / 5s 30 req / 5s

Handling 429 Errors (Smart Retry)

If you exceed a limit, you will receive a 429 Too Many Requests status. Your application must handle this gracefully.

We provide a Retry-After header indicating how many seconds you must wait.

sequenceDiagram participant Client participant API Client->>API: POST /v1/content API-->>Client: 429 Too Many Requests
Retry-After: 5 Note over Client: Wait 5 seconds Client->>API: POST /v1/content API-->>Client: 200 OK

Recommended Strategy:

  • Realtime APIs (Search, Autocomplete): Do not retry immediately. Discard the request.
  • Batch APIs (Content Updates): Use an exponential backoff strategy (wait Retry-After, then retry).

5. Error Handling

You should design your integration to be resilient to network failures.

Code Reason Type Action
401 Auth Failed Fatal Check your Signature generation. The response body contains the string we expected vs. what you sent.
408 Timeout Transient Safe to retry once.
429 Throttling Transient Wait and retry (see above).
500 Server Error Fatal Log the error and contact support.
502 Bad Gateway Transient Usually a deploy or network blip. Retry after 1s.
503 Service Unavailable Transient Check luigisboxstatus.com.

Development Mode

To ensure your integration is robust, we may enable Development Mode on your account during the integration phase.

What it does:

  • A small percentage of your requests will deliberately fail with various error codes (408, 429, 500).
  • These errors are marked in the JSON body as development_mode: true.

Why: This forces your developers to write proper error handling and retry logic before you go to production.

Note Note: Development mode is never enabled on Production accounts without explicit consent.