curl --request POST \
--url https://fit.kleep.ai/v1/recommendations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Kleep-Retailer: <kleep-retailer>' \
--data '
{
"product_id": "JEAN-0142",
"shopper": {
"measurements": {
"gender": "female",
"age": 34,
"height_cm": 168,
"weight_kg": 61,
"body_shape": {
"stomach": "medium",
"hips": "wide"
}
}
},
"locale": "en-GB",
"market": "GB",
"conversation_id": "conv_5521"
}
'import requests
url = "https://fit.kleep.ai/v1/recommendations"
payload = {
"product_id": "JEAN-0142",
"shopper": { "measurements": {
"gender": "female",
"age": 34,
"height_cm": 168,
"weight_kg": 61,
"body_shape": {
"stomach": "medium",
"hips": "wide"
}
} },
"locale": "en-GB",
"market": "GB",
"conversation_id": "conv_5521"
}
headers = {
"Kleep-Retailer": "<kleep-retailer>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Kleep-Retailer': '<kleep-retailer>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
product_id: 'JEAN-0142',
shopper: {
measurements: {
gender: 'female',
age: 34,
height_cm: 168,
weight_kg: 61,
body_shape: {stomach: 'medium', hips: 'wide'}
}
},
locale: 'en-GB',
market: 'GB',
conversation_id: 'conv_5521'
})
};
fetch('https://fit.kleep.ai/v1/recommendations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://fit.kleep.ai/v1/recommendations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'product_id' => 'JEAN-0142',
'shopper' => [
'measurements' => [
'gender' => 'female',
'age' => 34,
'height_cm' => 168,
'weight_kg' => 61,
'body_shape' => [
'stomach' => 'medium',
'hips' => 'wide'
]
]
],
'locale' => 'en-GB',
'market' => 'GB',
'conversation_id' => 'conv_5521'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Kleep-Retailer: <kleep-retailer>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://fit.kleep.ai/v1/recommendations"
payload := strings.NewReader("{\n \"product_id\": \"JEAN-0142\",\n \"shopper\": {\n \"measurements\": {\n \"gender\": \"female\",\n \"age\": 34,\n \"height_cm\": 168,\n \"weight_kg\": 61,\n \"body_shape\": {\n \"stomach\": \"medium\",\n \"hips\": \"wide\"\n }\n }\n },\n \"locale\": \"en-GB\",\n \"market\": \"GB\",\n \"conversation_id\": \"conv_5521\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Kleep-Retailer", "<kleep-retailer>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://fit.kleep.ai/v1/recommendations")
.header("Kleep-Retailer", "<kleep-retailer>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"product_id\": \"JEAN-0142\",\n \"shopper\": {\n \"measurements\": {\n \"gender\": \"female\",\n \"age\": 34,\n \"height_cm\": 168,\n \"weight_kg\": 61,\n \"body_shape\": {\n \"stomach\": \"medium\",\n \"hips\": \"wide\"\n }\n }\n },\n \"locale\": \"en-GB\",\n \"market\": \"GB\",\n \"conversation_id\": \"conv_5521\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://fit.kleep.ai/v1/recommendations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Kleep-Retailer"] = '<kleep-retailer>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"product_id\": \"JEAN-0142\",\n \"shopper\": {\n \"measurements\": {\n \"gender\": \"female\",\n \"age\": 34,\n \"height_cm\": 168,\n \"weight_kg\": 61,\n \"body_shape\": {\n \"stomach\": \"medium\",\n \"hips\": \"wide\"\n }\n }\n },\n \"locale\": \"en-GB\",\n \"market\": \"GB\",\n \"conversation_id\": \"conv_5521\"\n}"
response = http.request(request)
puts response.read_body{
"recommendation_id": "rec_7Hq2mV9xKp",
"profile_id": "prf_3Nf8sLw1Qz",
"product_id": "JEAN-0142",
"size": {
"label": "M",
"variant_id": "JEAN-0142-M",
"available": true
},
"alternatives": [
{
"label": "L",
"variant_id": "JEAN-0142-L",
"direction": "larger",
"available": true,
"reason": "For a looser fit"
}
],
"fit": [
{
"zone": "waist",
"fit": "just_right"
},
{
"zone": "hips",
"fit": "slightly_tight"
}
],
"product_fit": {
"tendency": "runs_small",
"score": -0.4
},
"summary": "I'd recommend size M. It should feel slightly snug at the hips; take an L if you prefer a looser fit."
}{
"error": {
"code": "invalid_input",
"message": "height_cm must be at least 120",
"details": {
"field": "shopper.measurements.height_cm"
}
}
}{
"error": {
"code": "unauthorized",
"message": "Invalid API key"
}
}{
"error": {
"code": "retailer_not_enabled",
"message": "Retailer 9b1f… has not enabled this partner"
}
}{
"error": {
"code": "product_not_found",
"message": "No product JEAN-9999 for this retailer"
}
}{
"error": {
"code": "missing_inputs",
"message": "Height and weight are needed",
"missing": [
"height_cm",
"weight_kg"
],
"questions": [
{
"id": "height_cm",
"required": true,
"type": "number",
"unit": "cm",
"min": 120,
"max": 230,
"prompt": "How tall are you?"
},
{
"id": "weight_kg",
"required": true,
"type": "number",
"unit": "kg",
"min": 20,
"max": 250,
"prompt": "How much do you weigh?"
}
]
}
}{
"error": {
"code": "rate_limited",
"message": "Too many requests"
}
}Create recommendation
The size for one shopper and one product, in one call. Send the measurements collected in the conversation or a stored profile_id. When answers are missing, the call returns 422 missing_inputs with the questions to ask: ask them, then call again.
curl --request POST \
--url https://fit.kleep.ai/v1/recommendations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Kleep-Retailer: <kleep-retailer>' \
--data '
{
"product_id": "JEAN-0142",
"shopper": {
"measurements": {
"gender": "female",
"age": 34,
"height_cm": 168,
"weight_kg": 61,
"body_shape": {
"stomach": "medium",
"hips": "wide"
}
}
},
"locale": "en-GB",
"market": "GB",
"conversation_id": "conv_5521"
}
'import requests
url = "https://fit.kleep.ai/v1/recommendations"
payload = {
"product_id": "JEAN-0142",
"shopper": { "measurements": {
"gender": "female",
"age": 34,
"height_cm": 168,
"weight_kg": 61,
"body_shape": {
"stomach": "medium",
"hips": "wide"
}
} },
"locale": "en-GB",
"market": "GB",
"conversation_id": "conv_5521"
}
headers = {
"Kleep-Retailer": "<kleep-retailer>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Kleep-Retailer': '<kleep-retailer>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
product_id: 'JEAN-0142',
shopper: {
measurements: {
gender: 'female',
age: 34,
height_cm: 168,
weight_kg: 61,
body_shape: {stomach: 'medium', hips: 'wide'}
}
},
locale: 'en-GB',
market: 'GB',
conversation_id: 'conv_5521'
})
};
fetch('https://fit.kleep.ai/v1/recommendations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://fit.kleep.ai/v1/recommendations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'product_id' => 'JEAN-0142',
'shopper' => [
'measurements' => [
'gender' => 'female',
'age' => 34,
'height_cm' => 168,
'weight_kg' => 61,
'body_shape' => [
'stomach' => 'medium',
'hips' => 'wide'
]
]
],
'locale' => 'en-GB',
'market' => 'GB',
'conversation_id' => 'conv_5521'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Kleep-Retailer: <kleep-retailer>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://fit.kleep.ai/v1/recommendations"
payload := strings.NewReader("{\n \"product_id\": \"JEAN-0142\",\n \"shopper\": {\n \"measurements\": {\n \"gender\": \"female\",\n \"age\": 34,\n \"height_cm\": 168,\n \"weight_kg\": 61,\n \"body_shape\": {\n \"stomach\": \"medium\",\n \"hips\": \"wide\"\n }\n }\n },\n \"locale\": \"en-GB\",\n \"market\": \"GB\",\n \"conversation_id\": \"conv_5521\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Kleep-Retailer", "<kleep-retailer>")
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://fit.kleep.ai/v1/recommendations")
.header("Kleep-Retailer", "<kleep-retailer>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"product_id\": \"JEAN-0142\",\n \"shopper\": {\n \"measurements\": {\n \"gender\": \"female\",\n \"age\": 34,\n \"height_cm\": 168,\n \"weight_kg\": 61,\n \"body_shape\": {\n \"stomach\": \"medium\",\n \"hips\": \"wide\"\n }\n }\n },\n \"locale\": \"en-GB\",\n \"market\": \"GB\",\n \"conversation_id\": \"conv_5521\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://fit.kleep.ai/v1/recommendations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Kleep-Retailer"] = '<kleep-retailer>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"product_id\": \"JEAN-0142\",\n \"shopper\": {\n \"measurements\": {\n \"gender\": \"female\",\n \"age\": 34,\n \"height_cm\": 168,\n \"weight_kg\": 61,\n \"body_shape\": {\n \"stomach\": \"medium\",\n \"hips\": \"wide\"\n }\n }\n },\n \"locale\": \"en-GB\",\n \"market\": \"GB\",\n \"conversation_id\": \"conv_5521\"\n}"
response = http.request(request)
puts response.read_body{
"recommendation_id": "rec_7Hq2mV9xKp",
"profile_id": "prf_3Nf8sLw1Qz",
"product_id": "JEAN-0142",
"size": {
"label": "M",
"variant_id": "JEAN-0142-M",
"available": true
},
"alternatives": [
{
"label": "L",
"variant_id": "JEAN-0142-L",
"direction": "larger",
"available": true,
"reason": "For a looser fit"
}
],
"fit": [
{
"zone": "waist",
"fit": "just_right"
},
{
"zone": "hips",
"fit": "slightly_tight"
}
],
"product_fit": {
"tendency": "runs_small",
"score": -0.4
},
"summary": "I'd recommend size M. It should feel slightly snug at the hips; take an L if you prefer a looser fit."
}{
"error": {
"code": "invalid_input",
"message": "height_cm must be at least 120",
"details": {
"field": "shopper.measurements.height_cm"
}
}
}{
"error": {
"code": "unauthorized",
"message": "Invalid API key"
}
}{
"error": {
"code": "retailer_not_enabled",
"message": "Retailer 9b1f… has not enabled this partner"
}
}{
"error": {
"code": "product_not_found",
"message": "No product JEAN-9999 for this retailer"
}
}{
"error": {
"code": "missing_inputs",
"message": "Height and weight are needed",
"missing": [
"height_cm",
"weight_kg"
],
"questions": [
{
"id": "height_cm",
"required": true,
"type": "number",
"unit": "cm",
"min": 120,
"max": 230,
"prompt": "How tall are you?"
},
{
"id": "weight_kg",
"required": true,
"type": "number",
"unit": "kg",
"min": 20,
"max": 250,
"prompt": "How much do you weigh?"
}
]
}
}{
"error": {
"code": "rate_limited",
"message": "Too many requests"
}
}Authorizations
Your partner API key: kp_live_… in production, kp_test_… on the sandbox retailer.
Headers
The retailer's Kleep public_id. Every call is scoped to one retailer.
Body
The retailer's product ID, as in its catalog.
"JEAN-0142"
Who the recommendation is for. Send a profile_id you stored, or the measurements collected in the conversation, or both (the measurements then update the profile).
Show child attributes
Show child attributes
Only when the shopper says how they like to wear it.
fitted, regular, loose BCP 47 language tag for summary, prompts and size labels.
"fr-FR"
ISO 3166-1 alpha-2 country of the shopper. Selects the market's size labels.
"FR"
Your conversation or ticket ID. Lets Kleep attribute later orders to the chat.
"conv_5521"
Response
The recommendation.
Identifies this recommendation, for feedback and attribution.
The shopper's Kleep profile. Store it with your conversation or customer record and send it next time instead of asking again.
Show child attributes
Show child attributes
The recommendation as one or two ready-to-say sentences, in the requested locale.
Neighbouring sizes that can also fit, at most one smaller and one larger.
Show child attributes
Show child attributes
How the recommended size fits, per body zone of the product's size chart.
Show child attributes
Show child attributes
How the product fits compared to its size chart, from Kleep's purchase and return data. null when there isn't enough data.
Show child attributes
Show child attributes
