API Reference
Base URL
Every endpoint shares one base URL; all paths are versioned under /v1.
API Keys
Keys are generated from the Developer Access tab in your dashboard. Every request requires an X-API-KEY header with no Bearer prefix.
| Type | Prefix | Where to use |
|---|---|---|
| Publishable | lehnz_pk_... | Browser and client-side code |
| Secret | lehnz_sk_... | Server-side only; never expose in the browser |
403 Forbidden.Headers
| Header | Required | Value |
|---|---|---|
| X-API-KEY | ✅ Yes | Your publishable or secret key |
| Content-Type | ✅ Yes | application/json |
Ingestion Endpoints
All ingestion endpoints return 202 Accepted and use the same response envelope: { success, message, data: { accepted }, error }.
POST /v1/items/upsert
Load your catalog before anything else; Lehnz can only recommend items it knows about. Re-call whenever items change. To hide or remove an item, upsert it with status: inactive or deleted rather than omitting it.
Auth: Secret key (server-side only).
| Field | Required | Description |
|---|---|---|
| item_id | ✅ | Stable item identifier. |
| item_type | ✅ | Domain-specific category, e.g. product or article. |
| status | ✅ | active = recommendable; inactive = hidden; deleted = soft-deleted. |
| attributes | Free-form metadata (title, price, brand, category) used for filtering and ranking. | |
| created_at | Item creation timestamp (ISO 8601). |
{"success": true,"message": "items accepted","data": { "accepted": 1 },"error": null}
POST /v1/users/upsert
Load your users so recommendations can be personalised to them. Re-call when a profile changes or a user deactivates. Set status: inactive or deleted to preserve event history for analytics.
Auth: Secret key (server-side only).
| Field | Required | Description |
|---|---|---|
| user_id | ✅ | Stable user identifier (your application's user ID). |
| status | ✅ | active = visible to recommendations; inactive = hidden, history kept; deleted = soft-deleted. |
| created_at | ✅ | User signup timestamp (ISO 8601). |
| attributes | Free-form metadata (segment, plan, locale). |
{"success": true,"message": "users accepted","data": { "accepted": 1 },"error": null}
POST /v1/events/ingest
Stream every meaningful interaction so Lehnz learns in real time. Accepts a single event object or an array. When an interaction came from a recommendation, include recommendation_id to close the attribution loop.
Auth: Publishable key (browser-safe).
| Field | Required | Description |
|---|---|---|
| user_id | ✅ | Stable identifier. Anonymous shoppers use a client-generated UUID; signed-in shoppers use the application's real user ID. |
| event_family | ✅ | exposure | engagement | conversion | system |
| event_name | ✅ | Lowercase event name (e.g. view, purchase). |
| previous_user_id | Required only for identify events: the prior anonymous ID being aliased. | |
| item_id | Null when the event is not tied to a specific item. | |
| recommendation_id | Include when the event was triggered by a served recommendation. | |
| event_id | Client-supplied UUID; server assigns one if omitted. | |
| context | Free-form context object (device, locale, session). |
{"success": true,"message": "events accepted","data": { "accepted": 1 },"error": null}
Recommendations
One endpoint, many strategies. POST /v1/recommend serves every use case; the strategy field picks the algorithm.
POST /v1/recommend
Auth: Publishable key (browser-safe). See the Strategies Guide for detailed schemas, modifiers, and request/response structures.
| Field | Description |
|---|---|
| strategy | One of 10 base strategies (default personalized). |
| user_id | Required for user-based strategies. |
| context_item_id | Required for item-to-item strategies like similar or frequently-bought-together. |
| filters | Attribute filters, e.g. { "category": "shoes" }. |
| limit | Items to return (default 20, max 100). |
| page | 1-indexed page number for pagination (default 1). |
{"success": true,"message": "Success","data": {"recommendation_id": "550e8400-e29b-41d4-a716-446655440000","tenant_id": "glow-beauty-ng","domain": "commerce","strategy": "personalized","mode": "behavioral","results": [{ "item_id": "prod_7", "score": 0.985, "reason": "behavioral_match", "metadata": { "brand": "Sony" } }],"recommended_items": ["prod_7", "prod_3", "prod_22"],"pagination": { "page": 1, "limit": 20, "has_next": true },"latency_ms": 42.5},"error": null}
Response Envelope
All responses share the same structure:
Success
{"success": true,"message": "...","data": { }}
Error
{"success": false,"message": "Human-readable error","error": "Detail"}
HTTP Status Codes
| Status | Meaning |
|---|---|
| 200 OK | Synchronous request succeeded. |
| 202 Accepted | Data queued for async processing (Ingestion endpoints). Events are typically available within seconds. |
| 400 Bad Request | Validation error; check the message field. |
| 401 Unauthorized | Missing or invalid X-API-KEY. |
| 403 Forbidden | Wrong key type for this endpoint (e.g. using a publishable key on a server-only route) or tier restriction. |
| 429 Rate Limited | Back off and retry (see RateLimit-Reset header). |
| 500 Server Error | Internal Server Error. |
Rate Limits
- 1,000 requests per 15 minutes per organisation, shared across all API keys.
- Auth endpoints (
/auth/register,/auth/login) are capped at 10 requests per 15 minutes per IP. - To stay efficient during ingestion, batch multiple events into a single array.
On 429 Rate Limited, retry with exponential backoff; the RateLimit-Reset header tells you when to try again.