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).
Service reference and endpoints
Section titled “Service reference and endpoints”Public API (unauthenticated)
Section titled “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/v2 | Prefix-based suggestions. |
| Recommender | POST | live.luigisbox.com | /v1/recommend | Product recommendations. |
| Shopping Assistant | POST | live.luigisbox.com | /v1/assistant | AI-powered structured dialogue that helps customers discover products. |
| Top items | GET | live.luigisbox.com | /v1/top_items | Most popular items. |
| Trending queries | GET | live.luigisbox.com | /v2/trending_queries | Trending search terms. |
| Analytics | POST | api.luigisbox.com | /v1 | Events API. Ingest user interactions. |
Private API (authenticated)
Section titled “Private API (authenticated)”Auth: Requires HMAC Signature using your Private Key. Usage: Backend Server ONLY.
Indexing hub (content management)
Section titled “Indexing hub (content management)”Manage your product catalog.
| Method | Base URL | Endpoint | Description |
|---|---|---|---|
POST | live.luigisbox.com | /v1/content | Create/Replace. Add new products. |
PATCH | live.luigisbox.com | /v1/content | Partial Update. Update specific fields. |
DELETE | live.luigisbox.com | /v1/content | Delete. Remove products. |
PATCH | live.luigisbox.com | /v1/update_by_query | Batch Update. Update items matching a query. |
GET | live.luigisbox.com | /v1/content_export | Export. Download your current index data. |
Recommender customization
Section titled “Recommender customization”Manage pinning rules and batch recommendations.
| Method | Base URL | Endpoint | Description |
|---|---|---|---|
POST | live.luigisbox.com | /v1/recommender/pin/{ID}/scopes | Define pinning scopes (rules). |
GET | live.luigisbox.com | /v1/recommender/pin/{ID}/summary | Get pinning configuration summary. |
POST | live.luigisbox.com | /v1/recommender/batching/{ID}/users | Trigger batch recommendation generation for users. |
* | live.luigisbox.com | /v1/recommender/pin/* | Various CRUD endpoints for managing pins. |
Authentication (HMAC)
Section titled “Authentication (HMAC)”Only the Private API endpoints require a custom HMAC-SHA256 signature.
Credentials
Section titled “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
Section titled “The signing flow”To authenticate, you must construct a specific “String to Sign,” hash it, and attach it as a header.
flowchart TD
A["HTTP method"]
B["Content-Type"]
C["Date header"]
D["Path"]
E["Canonical string"]
F{"Hash function"}
G["Raw bytes"]
H["Final signature"]
I["Authorization header"]
J["API server"]
Key["Private secret"]
A --> E
B --> E
C --> E
D --> E
E -->|HMAC SHA-256| F
Key -.-> F
F -->|Binary output| G
G -->|Base64 encode| H
H -->|Format string| I
I -->|Send| JDetailed specification
Section titled “Detailed specification”Step 1: Required headers
Section titled “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
Section titled “Step 2: Canonical string construction”Concatenate the following four strings, separated by a newline character (\n).
- HTTP Verb: Uppercase (e.g.,
POST,PUT,DELETE). - Content-Type: Exactly as sent in the header (e.g.,
application/json; charset=utf-8). - Date: Exactly as sent in the header.
- Resource Path: The absolute path excluding the query string (e.g.,
/v1/content, not/v1/content?foo=bar).
Step 3: Signature calculation
Section titled “Step 3: Signature calculation”- Compute the HMAC-SHA256 digest of the Canonical String using your Secret Key.
- Encode the binary digest into a Base64 string.
Example implementations
Section titled “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)).stripend
date = Time.now.httpdatedigest("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")User identification
Section titled “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 | client_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 | customer_id | Optional. The database ID of the logged-in user. Send this only when the user is logged in. |
The session continuity rule
Section titled “The session continuity rule”The client_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 LuigiBox as LBX Note over User, App: Anonymous browsing User->>App: Views Homepage App->>LuigiBox: Event with device_user_id COOKIE_123 Note over User, App: User logs in with auth_user_id USER_99 User->>App: Enters Credentials App->>LuigiBox: Event with device_user_id COOKIE_123 and auth_user_id USER_99 Note over LuigiBox: History merges because device_user_id stays the same Note over User, App: User logs out User->>App: Clicks Logout App->>LuigiBox: Event with device_user_id COOKIE_123 Note over LuigiBox: Session continues anonymously
Rate limiting (throttling)
Section titled “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 |
Handle 429 errors
Section titled “Handle 429 errors”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 content request API-->>Client: 429 Retry-After 5 seconds Note over Client: Wait 5 seconds Client->>API: Retry content request 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).
Error handling
Section titled “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
Section titled “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.
Was this page helpful?
Thanks.