curl --request POST \
--url https://salespilot.crossup.ai/v1/recommendations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"connection_id": "<string>",
"placement_key": "<string>",
"context_item_id": "<string>",
"cart_items": [
{
"item_id": "<string>",
"variant_id": "<string>",
"quantity": 2
}
],
"cart_id": "<string>",
"client": {
"page_url": "<string>",
"referrer": "<string>",
"utm": {
"source": "<string>",
"medium": "<string>",
"campaign": "<string>",
"term": "<string>",
"content": "<string>"
},
"languages": [
"<string>"
],
"timezone": "<string>",
"screen": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"viewport": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"connection": {
"effective_type": "<string>",
"downlink_mbps": 1,
"rtt_ms": 1,
"save_data": true
},
"device": {
"mobile": true,
"platform": "<string>",
"touch_points": 1,
"memory_gb": 1,
"cpu_cores": 1
}
}
}
'import requests
url = "https://salespilot.crossup.ai/v1/recommendations"
payload = {
"connection_id": "<string>",
"placement_key": "<string>",
"context_item_id": "<string>",
"cart_items": [
{
"item_id": "<string>",
"variant_id": "<string>",
"quantity": 2
}
],
"cart_id": "<string>",
"client": {
"page_url": "<string>",
"referrer": "<string>",
"utm": {
"source": "<string>",
"medium": "<string>",
"campaign": "<string>",
"term": "<string>",
"content": "<string>"
},
"languages": ["<string>"],
"timezone": "<string>",
"screen": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"viewport": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"connection": {
"effective_type": "<string>",
"downlink_mbps": 1,
"rtt_ms": 1,
"save_data": True
},
"device": {
"mobile": True,
"platform": "<string>",
"touch_points": 1,
"memory_gb": 1,
"cpu_cores": 1
}
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
connection_id: '<string>',
placement_key: '<string>',
context_item_id: '<string>',
cart_items: [{item_id: '<string>', variant_id: '<string>', quantity: 2}],
cart_id: '<string>',
client: {
page_url: '<string>',
referrer: '<string>',
utm: {
source: '<string>',
medium: '<string>',
campaign: '<string>',
term: '<string>',
content: '<string>'
},
languages: ['<string>'],
timezone: '<string>',
screen: {width: 1, height: 1, pixel_ratio: 1},
viewport: {width: 1, height: 1, pixel_ratio: 1},
connection: {effective_type: '<string>', downlink_mbps: 1, rtt_ms: 1, save_data: true},
device: {
mobile: true,
platform: '<string>',
touch_points: 1,
memory_gb: 1,
cpu_cores: 1
}
}
})
};
fetch('https://salespilot.crossup.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://salespilot.crossup.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([
'connection_id' => '<string>',
'placement_key' => '<string>',
'context_item_id' => '<string>',
'cart_items' => [
[
'item_id' => '<string>',
'variant_id' => '<string>',
'quantity' => 2
]
],
'cart_id' => '<string>',
'client' => [
'page_url' => '<string>',
'referrer' => '<string>',
'utm' => [
'source' => '<string>',
'medium' => '<string>',
'campaign' => '<string>',
'term' => '<string>',
'content' => '<string>'
],
'languages' => [
'<string>'
],
'timezone' => '<string>',
'screen' => [
'width' => 1,
'height' => 1,
'pixel_ratio' => 1
],
'viewport' => [
'width' => 1,
'height' => 1,
'pixel_ratio' => 1
],
'connection' => [
'effective_type' => '<string>',
'downlink_mbps' => 1,
'rtt_ms' => 1,
'save_data' => true
],
'device' => [
'mobile' => true,
'platform' => '<string>',
'touch_points' => 1,
'memory_gb' => 1,
'cpu_cores' => 1
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://salespilot.crossup.ai/v1/recommendations"
payload := strings.NewReader("{\n \"connection_id\": \"<string>\",\n \"placement_key\": \"<string>\",\n \"context_item_id\": \"<string>\",\n \"cart_items\": [\n {\n \"item_id\": \"<string>\",\n \"variant_id\": \"<string>\",\n \"quantity\": 2\n }\n ],\n \"cart_id\": \"<string>\",\n \"client\": {\n \"page_url\": \"<string>\",\n \"referrer\": \"<string>\",\n \"utm\": {\n \"source\": \"<string>\",\n \"medium\": \"<string>\",\n \"campaign\": \"<string>\",\n \"term\": \"<string>\",\n \"content\": \"<string>\"\n },\n \"languages\": [\n \"<string>\"\n ],\n \"timezone\": \"<string>\",\n \"screen\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"viewport\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"connection\": {\n \"effective_type\": \"<string>\",\n \"downlink_mbps\": 1,\n \"rtt_ms\": 1,\n \"save_data\": true\n },\n \"device\": {\n \"mobile\": true,\n \"platform\": \"<string>\",\n \"touch_points\": 1,\n \"memory_gb\": 1,\n \"cpu_cores\": 1\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
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://salespilot.crossup.ai/v1/recommendations")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"connection_id\": \"<string>\",\n \"placement_key\": \"<string>\",\n \"context_item_id\": \"<string>\",\n \"cart_items\": [\n {\n \"item_id\": \"<string>\",\n \"variant_id\": \"<string>\",\n \"quantity\": 2\n }\n ],\n \"cart_id\": \"<string>\",\n \"client\": {\n \"page_url\": \"<string>\",\n \"referrer\": \"<string>\",\n \"utm\": {\n \"source\": \"<string>\",\n \"medium\": \"<string>\",\n \"campaign\": \"<string>\",\n \"term\": \"<string>\",\n \"content\": \"<string>\"\n },\n \"languages\": [\n \"<string>\"\n ],\n \"timezone\": \"<string>\",\n \"screen\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"viewport\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"connection\": {\n \"effective_type\": \"<string>\",\n \"downlink_mbps\": 1,\n \"rtt_ms\": 1,\n \"save_data\": true\n },\n \"device\": {\n \"mobile\": true,\n \"platform\": \"<string>\",\n \"touch_points\": 1,\n \"memory_gb\": 1,\n \"cpu_cores\": 1\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://salespilot.crossup.ai/v1/recommendations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"connection_id\": \"<string>\",\n \"placement_key\": \"<string>\",\n \"context_item_id\": \"<string>\",\n \"cart_items\": [\n {\n \"item_id\": \"<string>\",\n \"variant_id\": \"<string>\",\n \"quantity\": 2\n }\n ],\n \"cart_id\": \"<string>\",\n \"client\": {\n \"page_url\": \"<string>\",\n \"referrer\": \"<string>\",\n \"utm\": {\n \"source\": \"<string>\",\n \"medium\": \"<string>\",\n \"campaign\": \"<string>\",\n \"term\": \"<string>\",\n \"content\": \"<string>\"\n },\n \"languages\": [\n \"<string>\"\n ],\n \"timezone\": \"<string>\",\n \"screen\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"viewport\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"connection\": {\n \"effective_type\": \"<string>\",\n \"downlink_mbps\": 1,\n \"rtt_ms\": 1,\n \"save_data\": true\n },\n \"device\": {\n \"mobile\": true,\n \"platform\": \"<string>\",\n \"touch_points\": 1,\n \"memory_gb\": 1,\n \"cpu_cores\": 1\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"decision_id": "<string>",
"request_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"placement_key": "<string>",
"surface": "pdp",
"context": {
"cart_items": [
{
"item_id": "<string>",
"variant_id": "<string>",
"quantity": 2
}
],
"shooter": {
"item_id": "<string>",
"product": {
"item": {
"item_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"external_id": "<string>",
"title": {},
"status": "<string>",
"source_version": "<string>",
"primary_media": {
"url": "<string>",
"alt": {}
},
"description": {},
"handles": {},
"urls": {},
"media": [
{
"url": "<string>",
"alt": {}
}
],
"brand": "<string>",
"tags": [
"<string>"
],
"free_shipping": true,
"taxonomy": [
{
"taxonomy_id": "<string>",
"external_id": "<string>",
"name": {},
"handles": {},
"parent_id": "<string>"
}
]
},
"variants": [
{
"variant_id": "<string>",
"item_id": "<string>",
"external_id": "<string>",
"status": "<string>",
"sku": "<string>",
"title": {},
"price": {
"amount": "<string>",
"currency": "<string>"
},
"promotional_price": {
"amount": "<string>",
"currency": "<string>"
},
"attributes": [
{
"name": {},
"value": {}
}
]
}
],
"availability": [
{
"variant_id": "<string>",
"sellable": "<string>",
"as_of": "2023-11-07T05:31:56Z",
"quantity": 123
}
],
"refresh_revision": 2
}
}
},
"results": [
{
"item_id": "<string>",
"position": 2,
"reason_codes": [
"<string>"
],
"variant_id": "<string>",
"product": {
"item": {
"item_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"external_id": "<string>",
"title": {},
"status": "<string>",
"source_version": "<string>",
"primary_media": {
"url": "<string>",
"alt": {}
},
"description": {},
"handles": {},
"urls": {},
"media": [
{
"url": "<string>",
"alt": {}
}
],
"brand": "<string>",
"tags": [
"<string>"
],
"free_shipping": true,
"taxonomy": [
{
"taxonomy_id": "<string>",
"external_id": "<string>",
"name": {},
"handles": {},
"parent_id": "<string>"
}
]
},
"variants": [
{
"variant_id": "<string>",
"item_id": "<string>",
"external_id": "<string>",
"status": "<string>",
"sku": "<string>",
"title": {},
"price": {
"amount": "<string>",
"currency": "<string>"
},
"promotional_price": {
"amount": "<string>",
"currency": "<string>"
},
"attributes": [
{
"name": {},
"value": {}
}
]
}
],
"availability": [
{
"variant_id": "<string>",
"sellable": "<string>",
"as_of": "2023-11-07T05:31:56Z",
"quantity": 123
}
],
"refresh_revision": 2
}
}
],
"reason_codes": [
"<string>"
],
"generated_at": "2023-11-07T05:31:56Z",
"visitor": {
"visitor_id": "<string>",
"session_id": "<string>"
}
}{
"type": "https://api.crossup.ai/salespilot/errors/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "<string>",
"code": "invalid_request",
"request_id": "<string>",
"errors": [
{
"code": "<string>",
"detail": "<string>",
"pointer": "<string>"
}
]
}{
"type": "https://api.crossup.ai/salespilot/errors/unauthorized",
"title": "Authentication required",
"status": 401,
"detail": "<string>",
"code": "unauthorized",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/forbidden-connection",
"title": "Connection is not accessible",
"status": 403,
"detail": "<string>",
"code": "forbidden_connection",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/not-found",
"title": "Resource not found",
"status": 404,
"detail": "<string>",
"code": "not_found",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/cart-items-limit-exceeded",
"title": "Cart item limit exceeded",
"status": 422,
"detail": "<string>",
"code": "cart_items_limit_exceeded",
"request_id": "<string>",
"max_items_per_cart": 200,
"actual_items": 2
}{
"type": "https://api.crossup.ai/salespilot/errors/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "<string>",
"code": "rate_limited",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/internal-error",
"title": "Internal server error",
"status": 500,
"detail": "<string>",
"code": "internal_error",
"request_id": "<string>"
}Request a recommendation
Returns the recommendation for a storefront moment: the products in order, each one already projected onto the canonical catalog.
curl --request POST \
--url https://salespilot.crossup.ai/v1/recommendations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"connection_id": "<string>",
"placement_key": "<string>",
"context_item_id": "<string>",
"cart_items": [
{
"item_id": "<string>",
"variant_id": "<string>",
"quantity": 2
}
],
"cart_id": "<string>",
"client": {
"page_url": "<string>",
"referrer": "<string>",
"utm": {
"source": "<string>",
"medium": "<string>",
"campaign": "<string>",
"term": "<string>",
"content": "<string>"
},
"languages": [
"<string>"
],
"timezone": "<string>",
"screen": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"viewport": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"connection": {
"effective_type": "<string>",
"downlink_mbps": 1,
"rtt_ms": 1,
"save_data": true
},
"device": {
"mobile": true,
"platform": "<string>",
"touch_points": 1,
"memory_gb": 1,
"cpu_cores": 1
}
}
}
'import requests
url = "https://salespilot.crossup.ai/v1/recommendations"
payload = {
"connection_id": "<string>",
"placement_key": "<string>",
"context_item_id": "<string>",
"cart_items": [
{
"item_id": "<string>",
"variant_id": "<string>",
"quantity": 2
}
],
"cart_id": "<string>",
"client": {
"page_url": "<string>",
"referrer": "<string>",
"utm": {
"source": "<string>",
"medium": "<string>",
"campaign": "<string>",
"term": "<string>",
"content": "<string>"
},
"languages": ["<string>"],
"timezone": "<string>",
"screen": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"viewport": {
"width": 1,
"height": 1,
"pixel_ratio": 1
},
"connection": {
"effective_type": "<string>",
"downlink_mbps": 1,
"rtt_ms": 1,
"save_data": True
},
"device": {
"mobile": True,
"platform": "<string>",
"touch_points": 1,
"memory_gb": 1,
"cpu_cores": 1
}
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
connection_id: '<string>',
placement_key: '<string>',
context_item_id: '<string>',
cart_items: [{item_id: '<string>', variant_id: '<string>', quantity: 2}],
cart_id: '<string>',
client: {
page_url: '<string>',
referrer: '<string>',
utm: {
source: '<string>',
medium: '<string>',
campaign: '<string>',
term: '<string>',
content: '<string>'
},
languages: ['<string>'],
timezone: '<string>',
screen: {width: 1, height: 1, pixel_ratio: 1},
viewport: {width: 1, height: 1, pixel_ratio: 1},
connection: {effective_type: '<string>', downlink_mbps: 1, rtt_ms: 1, save_data: true},
device: {
mobile: true,
platform: '<string>',
touch_points: 1,
memory_gb: 1,
cpu_cores: 1
}
}
})
};
fetch('https://salespilot.crossup.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://salespilot.crossup.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([
'connection_id' => '<string>',
'placement_key' => '<string>',
'context_item_id' => '<string>',
'cart_items' => [
[
'item_id' => '<string>',
'variant_id' => '<string>',
'quantity' => 2
]
],
'cart_id' => '<string>',
'client' => [
'page_url' => '<string>',
'referrer' => '<string>',
'utm' => [
'source' => '<string>',
'medium' => '<string>',
'campaign' => '<string>',
'term' => '<string>',
'content' => '<string>'
],
'languages' => [
'<string>'
],
'timezone' => '<string>',
'screen' => [
'width' => 1,
'height' => 1,
'pixel_ratio' => 1
],
'viewport' => [
'width' => 1,
'height' => 1,
'pixel_ratio' => 1
],
'connection' => [
'effective_type' => '<string>',
'downlink_mbps' => 1,
'rtt_ms' => 1,
'save_data' => true
],
'device' => [
'mobile' => true,
'platform' => '<string>',
'touch_points' => 1,
'memory_gb' => 1,
'cpu_cores' => 1
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://salespilot.crossup.ai/v1/recommendations"
payload := strings.NewReader("{\n \"connection_id\": \"<string>\",\n \"placement_key\": \"<string>\",\n \"context_item_id\": \"<string>\",\n \"cart_items\": [\n {\n \"item_id\": \"<string>\",\n \"variant_id\": \"<string>\",\n \"quantity\": 2\n }\n ],\n \"cart_id\": \"<string>\",\n \"client\": {\n \"page_url\": \"<string>\",\n \"referrer\": \"<string>\",\n \"utm\": {\n \"source\": \"<string>\",\n \"medium\": \"<string>\",\n \"campaign\": \"<string>\",\n \"term\": \"<string>\",\n \"content\": \"<string>\"\n },\n \"languages\": [\n \"<string>\"\n ],\n \"timezone\": \"<string>\",\n \"screen\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"viewport\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"connection\": {\n \"effective_type\": \"<string>\",\n \"downlink_mbps\": 1,\n \"rtt_ms\": 1,\n \"save_data\": true\n },\n \"device\": {\n \"mobile\": true,\n \"platform\": \"<string>\",\n \"touch_points\": 1,\n \"memory_gb\": 1,\n \"cpu_cores\": 1\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
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://salespilot.crossup.ai/v1/recommendations")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"connection_id\": \"<string>\",\n \"placement_key\": \"<string>\",\n \"context_item_id\": \"<string>\",\n \"cart_items\": [\n {\n \"item_id\": \"<string>\",\n \"variant_id\": \"<string>\",\n \"quantity\": 2\n }\n ],\n \"cart_id\": \"<string>\",\n \"client\": {\n \"page_url\": \"<string>\",\n \"referrer\": \"<string>\",\n \"utm\": {\n \"source\": \"<string>\",\n \"medium\": \"<string>\",\n \"campaign\": \"<string>\",\n \"term\": \"<string>\",\n \"content\": \"<string>\"\n },\n \"languages\": [\n \"<string>\"\n ],\n \"timezone\": \"<string>\",\n \"screen\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"viewport\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"connection\": {\n \"effective_type\": \"<string>\",\n \"downlink_mbps\": 1,\n \"rtt_ms\": 1,\n \"save_data\": true\n },\n \"device\": {\n \"mobile\": true,\n \"platform\": \"<string>\",\n \"touch_points\": 1,\n \"memory_gb\": 1,\n \"cpu_cores\": 1\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://salespilot.crossup.ai/v1/recommendations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"connection_id\": \"<string>\",\n \"placement_key\": \"<string>\",\n \"context_item_id\": \"<string>\",\n \"cart_items\": [\n {\n \"item_id\": \"<string>\",\n \"variant_id\": \"<string>\",\n \"quantity\": 2\n }\n ],\n \"cart_id\": \"<string>\",\n \"client\": {\n \"page_url\": \"<string>\",\n \"referrer\": \"<string>\",\n \"utm\": {\n \"source\": \"<string>\",\n \"medium\": \"<string>\",\n \"campaign\": \"<string>\",\n \"term\": \"<string>\",\n \"content\": \"<string>\"\n },\n \"languages\": [\n \"<string>\"\n ],\n \"timezone\": \"<string>\",\n \"screen\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"viewport\": {\n \"width\": 1,\n \"height\": 1,\n \"pixel_ratio\": 1\n },\n \"connection\": {\n \"effective_type\": \"<string>\",\n \"downlink_mbps\": 1,\n \"rtt_ms\": 1,\n \"save_data\": true\n },\n \"device\": {\n \"mobile\": true,\n \"platform\": \"<string>\",\n \"touch_points\": 1,\n \"memory_gb\": 1,\n \"cpu_cores\": 1\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"decision_id": "<string>",
"request_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"placement_key": "<string>",
"surface": "pdp",
"context": {
"cart_items": [
{
"item_id": "<string>",
"variant_id": "<string>",
"quantity": 2
}
],
"shooter": {
"item_id": "<string>",
"product": {
"item": {
"item_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"external_id": "<string>",
"title": {},
"status": "<string>",
"source_version": "<string>",
"primary_media": {
"url": "<string>",
"alt": {}
},
"description": {},
"handles": {},
"urls": {},
"media": [
{
"url": "<string>",
"alt": {}
}
],
"brand": "<string>",
"tags": [
"<string>"
],
"free_shipping": true,
"taxonomy": [
{
"taxonomy_id": "<string>",
"external_id": "<string>",
"name": {},
"handles": {},
"parent_id": "<string>"
}
]
},
"variants": [
{
"variant_id": "<string>",
"item_id": "<string>",
"external_id": "<string>",
"status": "<string>",
"sku": "<string>",
"title": {},
"price": {
"amount": "<string>",
"currency": "<string>"
},
"promotional_price": {
"amount": "<string>",
"currency": "<string>"
},
"attributes": [
{
"name": {},
"value": {}
}
]
}
],
"availability": [
{
"variant_id": "<string>",
"sellable": "<string>",
"as_of": "2023-11-07T05:31:56Z",
"quantity": 123
}
],
"refresh_revision": 2
}
}
},
"results": [
{
"item_id": "<string>",
"position": 2,
"reason_codes": [
"<string>"
],
"variant_id": "<string>",
"product": {
"item": {
"item_id": "<string>",
"store_id": "<string>",
"connection_id": "<string>",
"external_id": "<string>",
"title": {},
"status": "<string>",
"source_version": "<string>",
"primary_media": {
"url": "<string>",
"alt": {}
},
"description": {},
"handles": {},
"urls": {},
"media": [
{
"url": "<string>",
"alt": {}
}
],
"brand": "<string>",
"tags": [
"<string>"
],
"free_shipping": true,
"taxonomy": [
{
"taxonomy_id": "<string>",
"external_id": "<string>",
"name": {},
"handles": {},
"parent_id": "<string>"
}
]
},
"variants": [
{
"variant_id": "<string>",
"item_id": "<string>",
"external_id": "<string>",
"status": "<string>",
"sku": "<string>",
"title": {},
"price": {
"amount": "<string>",
"currency": "<string>"
},
"promotional_price": {
"amount": "<string>",
"currency": "<string>"
},
"attributes": [
{
"name": {},
"value": {}
}
]
}
],
"availability": [
{
"variant_id": "<string>",
"sellable": "<string>",
"as_of": "2023-11-07T05:31:56Z",
"quantity": 123
}
],
"refresh_revision": 2
}
}
],
"reason_codes": [
"<string>"
],
"generated_at": "2023-11-07T05:31:56Z",
"visitor": {
"visitor_id": "<string>",
"session_id": "<string>"
}
}{
"type": "https://api.crossup.ai/salespilot/errors/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "<string>",
"code": "invalid_request",
"request_id": "<string>",
"errors": [
{
"code": "<string>",
"detail": "<string>",
"pointer": "<string>"
}
]
}{
"type": "https://api.crossup.ai/salespilot/errors/unauthorized",
"title": "Authentication required",
"status": 401,
"detail": "<string>",
"code": "unauthorized",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/forbidden-connection",
"title": "Connection is not accessible",
"status": 403,
"detail": "<string>",
"code": "forbidden_connection",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/not-found",
"title": "Resource not found",
"status": 404,
"detail": "<string>",
"code": "not_found",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/cart-items-limit-exceeded",
"title": "Cart item limit exceeded",
"status": 422,
"detail": "<string>",
"code": "cart_items_limit_exceeded",
"request_id": "<string>",
"max_items_per_cart": 200,
"actual_items": 2
}{
"type": "https://api.crossup.ai/salespilot/errors/rate-limited",
"title": "Too many requests",
"status": 429,
"detail": "<string>",
"code": "rate_limited",
"request_id": "<string>"
}{
"type": "https://api.crossup.ai/salespilot/errors/internal-error",
"title": "Internal server error",
"status": 500,
"detail": "<string>",
"code": "internal_error",
"request_id": "<string>"
}The six moments
surface | When | What it needs |
|---|---|---|
pdp | Product page | context_item_id (the starting product) or at least one item in cart_items |
cart | Right after adding to the cart (the popup, not the cart page) | same |
checkout | When the purchase starts | same |
success-page | Purchase finished | at least one item in cart_items |
in-checkout | Inside the checkout | at least one item in cart_items |
carousel | The cart page | at least one item in cart_items |
400 invalid_request
and pointer names the field. A context_item_id in a cart moment is echoed
in context, but it doesn’t change the recommendation.
The request doesn’t pick the store
There is nostore_id: the server takes the store from the key and checks
that connection_id belongs to it. No body field reads someone else’s data.
The visitor
visitor is optional: { visitor_id, session_id? }, shaped vis_… and
ses_…. It never makes the request fail: with another shape, it’s dropped and
not stored. It is never shopper data:
Visitor and context.
Every request is recorded with its visitor and the recommendation it returned:
that’s how a click is later tied to it.
What it returns
The response is the recommendation (in the API,Decision: the list of products recommended for that moment).
decision_id: the recommendation’s id, unique per response. Two identical requests are two recommendations.request_id: the same value as theX-Request-IDheader.context: what the recommendation started from.shootercarries the starting product, andcart_items, the cart as it arrived.results[]:item_id,variant_id?,position,reason_codes[]andproduct, complete: item, taxonomy, variants with prices and availability.
score and no engine suggestion id. The ranking is the
position, and a contract test fails if a score ever shows up.product is optional: if it couldn’t be read, the result goes out without it,
and you can resolve it with read the catalog.
item.description travels as the platform sends it; Tiendanube sends HTML, so
sanitize it.
reason_codes shows up on the recommendation and on each result: one explains
the whole response, the other that position. On an empty list, it says what happened:
| Code | What happened |
|---|---|
no_offer | The engine has nothing for that moment. |
module_not_configured | The store doesn’t have that moment enabled. |
missing_price | An item arrived without a price, and the engine needs it. |
unknown_external_id | The engine recommended something the catalog doesn’t have yet; it’s skipped. |
brain_unavailable | The engine didn’t answer in time. The store keeps selling. |
brain_error:<status> | The engine rejected the query with that status. |
cart_items is an array of objects
Optional (omitted means []), but never null. Each element is
{ item_id, variant_id, quantity }, with quantity ≥ 1. Past the limit, the
response is cart_items_limit_exceeded,
with max_items_per_cart and actual_items.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
Every field but the connection, the placement and the surface is optional in the schema, and the surface decides what is needed. pdp, cart and checkout need a shooter (context_item_id) or at least one cart item. success-page, in-checkout and carousel need at least one cart item; a shooter sent with them is recorded and echoed but does not drive the engine. A request with neither is a 400.
cart_id is the store's own id for the cart and belongs with cart_items: send it whenever the cart is not empty. It is not required by the schema — a request without it is served normally — but the decision it produces cannot be tied back to a sale.
11The storefront moment that asks for recommendations. Three moments start from a product the shopper is looking at or just added (the shooter, context_item_id): pdp (product page), cart (right after adding to the cart) and checkout (when the purchase starts). The other three read only the cart: success-page (purchase finished), in-checkout (inside the checkout) and carousel (the cart page).
pdp, cart, checkout, success-page, in-checkout, carousel 1The shooter item (the product the moment starts from), when the moment has one.
1The cart as the storefront sees it. Omitted means empty.
Show child attributes
Show child attributes
The store's own id for this cart. Send it whenever cart_items is not empty: it is what ties the sale back to the offer once the order comes in through the webhook. A request that carries a cart without it still answers 200 and still serves a recommendation, but that decision cannot be attributed to a sale later.
1 - 256Pseudonymous identity of the browser that asks. The storefront SDK creates and keeps it; it carries no personal data and must never be an email, a phone number or a platform customer id. Sending it is optional, and a visitor that does not match this shape never fails the request: the server drops it, answers without it and does not store it.
Show child attributes
Show child attributes
What the storefront SDK can read in the browser about the request. Every field is optional and sending it never fails the request: a context the server cannot read is dropped and the recommendation is served anyway. The server also records the client IP, User-Agent, Accept-Language, Origin and Referer from the HTTP request; none of this is returned.
Show child attributes
Show child attributes
Response
Recommendation decision
Unique per response. Signals about what was shown reference it.
1The same value as the X-Request-ID response header.
1111The storefront moment that asks for recommendations. Three moments start from a product the shopper is looking at or just added (the shooter, context_item_id): pdp (product page), cart (right after adding to the cart) and checkout (when the purchase starts). The other three read only the cart: success-page (purchase finished), in-checkout (inside the checkout) and carousel (the cart page).
pdp, cart, checkout, success-page, in-checkout, carousel 1What the decision was taken from, echoed so the storefront does not have to remember it.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
1Pseudonymous identity of the browser that asks. The storefront SDK creates and keeps it; it carries no personal data and must never be an email, a phone number or a platform customer id. Sending it is optional, and a visitor that does not match this shape never fails the request: the server drops it, answers without it and does not store it.
Show child attributes
Show child attributes