Widget and catalogue
The client of a brand (for createWidget with brand_id only)
Public, cacheable 1 hour. An unknown brand, or one of a paused or churned client, answers 404 brand_not_found.
Parameters| Name | In | Type | Description |
|---|
brand_id required | path | string | The brand's numeric id. |
Answer fields (200)| Name | Type | Description |
|---|
client_id required | integer | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Widget customization (theme and texts) of a client
Browsers may cache it 5 minutes; the CDN never does (D40). From a domain the client does not allow, the answer is {eligible:false, reason:"not_whitelisted"} (D39).
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id. |
Answer fields (200)| Name | Type | Description |
|---|
theme required | object | |
i18n required | object | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Is the page that framed the widget iframe an allowed domain of the client?
Called once per page load by the iframe (T13.03.01) before it talks to the parent page. Exact host or wildcard subdomain, as CORS; www. and no www. differ unless both are listed, and http: pages count only outside production. Never cached. From a domain the client does not allow, the answer is {allowed:false, reason:"not_whitelisted"} (D39).
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id. |
origin required | query | string | The parent page origin, e.g. https://www.trimtexstore.com. |
Answer fields (200)| Name | Type | Description |
|---|
allowed required | boolean | |
reason | "not_whitelisted" | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Which pages may frame the widget iframe of a client (frame-ancestors)
Asked by the plugin host for every iframe page request (T13.03.02), which copies the value into the page's Content-Security-Policy: frame-ancestors. The same origins the origin check allows: https://<host> for each allowed domain, https://*.<host> for a wildcard, plus https://admin.shopify.com for a Shopify client (theme editor), plus the environment's back-office origin for every serving client (its live and reference previews frame the same page). An unknown, malformed or not-serving client gets 'none'. Also sent in the X-Fmt-Frame-Ancestors header. Never cached; follows a domain change within 60 s.
Parameters| Name | In | Type | Description |
|---|
cid | query | string | The client id from the iframe URL (?cid=). |
Answer fields (200)| Name | Type | Description |
|---|
frame_ancestors required | string | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
One size chart, published version, in the shape the widget and the engine read
By numeric id or unique name. Cached 5 minutes by the CDN and browsers, purged when the chart is published or deleted. An unknown chart, a chart of another client, a chart never published, or a paused or churned client answers 404 (never cached). From a domain the client does not allow, the answer is {eligible:false, reason:"not_whitelisted"} (D39).
Parameters| Name | In | Type | Description |
|---|
id_or_slug required | path | string | The chart's numeric id, or its unique name (slug) such as 1_Bottom. |
client_id required | query | string | The client's numeric id. |
system | query | string | Size system to present as primary (e.g. FR); default the chart's own. Unknown → 400 naming the chart's systems. |
Answer fields (200)| Name | Type | Description |
|---|
id required | integer | Added: the chart id; a recommendation records it with version. |
version required | integer | Added: the published version served (0 = stored before versions). |
revision required | string | Added: sha256 of the served version's content. |
unique_name required | string | |
name required | string | |
gender required | "female" | "male" | "unisex" | "babies" | "boys" | "girls" | null | |
body_part required | "top" | "bottom" | "full" | "bosom" | "feet" | "head" | |
fit required | "tight" | "regular" | "loose" | "extreme" | "custom" | null | |
basis required | "body" | "garment" | Added: garment charts carry garment values and ease (D11). |
primary_system required | object | |
systems required | array of string | Added: every size system the chart has labels in, primary first. |
primary_measurements required | array of "Chest" | "Underchest" | "Waist" | "Hips" | "Height" | "Weight" | "ShoulderSpan" | "ArmLength" | "LegContour" | "Inseam" | "InseamFloor" | "FootLength" | "HeadCircumference" | |
length_unit required | "cm" | "in" | |
comment required | string | |
sizes required | array of object | |
ease_cm required | object | null | |
measurements required | array of object | One row per size and measurement: = for an exact value, a > min and a < max for a range. |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Reference brands for the shopper reference step, most used first
The D19-compatible wire format (D91): type_of_references is cloth, shoe or head; the answer maps brand name → category slug → reference products, with most_used_brands (default 5, at most 20) and client_brand. With the client's plugin_use_client_references flag on and a non-empty list, only those brands in the merchant's order. Readable from any origin (*) and cached 10 minutes, keyed by the full URL. An unknown or paused client answers 404, a bad gender or type 422 naming the field, the database down 503 (errors never cached). From a domain the client does not allow: {eligible:false, reason:"not_whitelisted", references:{}, most_used_brands:[], client_brand:null} (D39).
Parameters| Name | In | Type | Description |
|---|
gender required | query | "female" | "male" | "unisex" | "boys" | "girls" | "babies" | D19 compatible: the shopper's gender. |
type_of_references required | query | "cloth" | "shoe" | "head" | D19 compatible: cloth, shoe or head. |
client_id required | query | string | The client's numeric id. |
number_of_most_used | query | string | D19 compatible: how many most-used brand names (default 5, at most 20, 0 for none). |
language | query | string | Added: the language of the category labels (default en; a missing label falls back to EN). |
Answer fields (200)| Name | Type | Description |
|---|
references required | object | D19 compatible: brand name → category slug → reference products. |
most_used_brands required | array of string | D19 compatible: brand names, most used first. |
client_brand required | string | null | D19 compatible: the client's own brand when it is in the catalogue. |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | | A query field is missing or invalid (error.fields names it). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
A reference brand's published size charts for a category and gender
Only the reference catalogue's published, current charts of the brand (a merged brand answers for the kept one), for the shopper's gender and unisex (children: their own gender only), each with its size systems and sizes. An unknown brand or category answers []; no brand_id 422. Readable from any origin (*), cached 10 minutes. From a domain the client does not allow: {eligible:false, reason:"not_whitelisted"} (D39).
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id. |
gender required | query | "female" | "male" | "unisex" | "boys" | "girls" | "babies" | D19 compatible: the shopper's gender. |
status | query | "active" | D19 compatible: always active. |
brand_id required | query | string | D19 compatible: the reference brand. |
category | query | string | D19 compatible: a category slug; without it every category of the brand. |
language | query | string | Added: the language of the category labels (default en; a missing label falls back to EN). |
Answer fields (200)| Name | Type | Description |
|---|
eligible required | false | |
reason required | "not_whitelisted" | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | | A query field is missing or invalid (error.fields names it). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
The size system catalogue (EU, FR, UK, US, … and shoe scales)
The same for every shop: no client_id, readable from any origin (Access-Control-Allow-Origin: *), cached 5 minutes by the CDN and browsers. Ids never change (EU is 2). There is no write route (405). The catalogue unreadable answers 503 (never cached).
Parameters| Name | In | Type | Description |
|---|
language | query | string | Language of the names (fr, fr-FR). A language with no translation gets the English names. |
Answer fields (200)| Name | Type | Description |
|---|
items required | array of object | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Is this product eligible, and the widget's set-up for it (compatible keys, then ours)
The compatible answer (D19, CONTRACTS §1.1) with our keys added after it; each field's description starts with D19 compatible: or Added:. Not eligible: only {eligible:false, reason} (D50 reasons). From a domain the client does not allow, from an unknown client, or without Origin (curl, a server), the answer is {eligible:false, reason:"not_whitelisted"} (D39). An eligible answer may be kept 60 s by the browser; the CDN never keeps it (D52). The database down answers 503 with Retry-After.
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id. |
client_product_id required | query | string | The merchant's product id, exactly as in the feed (no trimming, no case change). |
available_sizes | query | string | The page's size names, comma-separated (S,M,L). Only part of the cache key; never makes a product not eligible. |
Answer fields (200)| Name | Type | Description |
|---|
eligible required | true | D19 compatible: the widget may show. |
social_proof required | boolean | D19 compatible: social-proof data drives the reco. |
reco_mode required | "classic" | "social_proof" | "usual_size" | null | D19 compatible: explicit mode, then social proof, then chart, then usual size. |
shoe_shape_slug required | string | null | D19 compatible: shoe last (E07), null for clothes. |
widget_customization required | object | D19 compatible: widget flags; both true when the client saved none. |
gender required | "male" | "female" | "unisex" | "boys" | "girls" | "babies" | null | D19 compatible: product gender. |
measurement_names required | array of string | D19 compatible: the chart's measurement names only (no ranges). |
body_part required | "clothes" | "feet" | "head" | null | D19 compatible: routing body part (E07). |
sizes required | array of object | D19 compatible: the chart's sizes in chart order, order from 1. |
sizing_chart_slug required | string | null | D19 compatible: the chart's slug. |
comfort_renderer_theme required | object | D19 compatible: fit-scale colours (ours by default, D8). |
has_references_step required | boolean | D19 compatible: the reference-brands step is shown. |
has_usual_size_step required | boolean | D19 compatible: the usual-size step is shown. |
preferred_size_system_id required | integer | null | D19 compatible: the id of usual_size_system_name in GET /v1/sizes (EU = 2); null when usual_size_list is empty. |
usual_size_list required | array of string | D19 compatible: the sizes a usual-size shopper picks from, in that system (the merchant's list or the chart sizes): in usual-size mode, and for every product with a chart when the merchant shows the usual-size path (D105); [] otherwise. |
usual_size_system_name required | string | null | D19 compatible: the size system code of usual_size_list (EU); null when the list is empty. |
sizing_adjustment required | number | D19 compatible: the product's merchant size offset (−1, 0 or +1; T04.07.03, D104), 0 by default. Fit, whitelist and note stay on the server. |
reason required | null | Added: null when eligible. |
product required | object | Added: the product's public fields (no database ids or validation data). |
product_body_part required | "bosom" | "top" | "bottom" | "full" | "feet" | "head" | null | Added: the garment region. |
steps required | object | Added: which optional steps the flow shows. |
fit_preferences required | array of "slim" | "regular" | "relaxed" | Added: the fit preferences (slim, regular, relaxed) the chart has ease for; [] for body charts (D105). |
garment_type required | "top" | "jacket" | "tights" | "shorts" | "one_piece" | "shoe" | "helmet" | null | Added: the 3D garment template the clothed body wears for the product (top, jacket, tights, shorts, one_piece, shoe, helmet): the merchant's pick, else the linking rule's, else derived from the category and body part; null = no garment, the widget shows the skin fit view. The widget also shows none when the merchant turned clothes off (plugin_show_garments in the customization theme) or the template is not published yet (T14.14.09). |
garment_look required | "base" | "tee" | null | Added: the look of garment_type (D131 3): tee = the CC0 T-shirt (a top product whose category says t-shirt / tee / singlet, or the merchant's "T-shirt" pick), else base (the generated garment); null when garment_type is null. The widget asks the garment route for this look. |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Report that a product page's sizes do not match its size chart
Unchanged legacy path. The body is optional (the legacy SDK sends none): {page_sizes, status}, at most 200 sizes. The server computes the status itself with the shared size match. One report per product per day is kept (later ones update it). An unknown client or product, a paused client, or a domain the client does not allow is dropped with a 200 (D39).
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id. |
client_product_id required | query | string | The merchant's product id. |
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
page_sizes | array of string | |
status | "SOMEHOW_VALID" | "INVALID" | |
Answer fields (200)| Name | Type | Description |
|---|
received required | integer | 1 when the report was stored. |
dropped | integer | |
reason | "not_whitelisted" | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Suggested products for one cross-sell tab, with the shopper's size (compatible query)
One tab of suggested products for the product page (S07.12, D125), at most 12, each with the size the reco engine gives this profile on that product (null when it has no chart or the engine cannot decide). Latest releases: the same gender (and unisex) and body part, newest first. You might like: the same feed category, the same size chart first. Bought by people like you: kept by at least 5 shoppers close in gender, age, height and weight (training rows of S05.05), most kept first. Bought together: kept in at least 2 of the same orders, most shared orders first; full returns do not count. Only published products still in the feed, with a size in stock and an https page on an allowed domain of the client; never the current product. The cross-sell flag off or no match answers an empty list. An unknown product or chart answers {eligible:false, reason:"unknown_product"} like plugin_info; from a domain the client does not allow, or without Origin, {eligible:false, reason:"not_whitelisted", products:[]} (D39). A bad tab or value answers 422 validation_failed with error.fields. Answers are no-store; body values are never logged. The GET form is kept for compatibility; the widget sends the POST form, so body values never travel in a URL.
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id. |
client_product_id required | query | string | The product page's product id (merchant id). |
cross_sell_products_type required | query | "recentProducts" | "similarProducts" | "boughtBySimilarPeopleProducts" | "boughtTogetherProducts" | The tab: recentProducts (latest releases), similarProducts (you might like), boughtBySimilarPeopleProducts (bought by people like you), boughtTogetherProducts (bought together). |
sizing_chart_slug | query | string | The product's chart; an unknown one answers not found. |
gender | query | "male" | "female" | "unisex" | "boys" | "girls" | "babies" | |
age | query | string | Years, a whole number 0–120. |
height | query | string | cm, 40–250. |
weight | query | string | kg, 2–300. |
foot_length | query | string | cm, 10–34 (shoe sizes). |
client_host_name | query | string | Accepted and ignored. |
client_base_uri | query | string | Accepted and ignored. |
Answer fields (200)| Name | Type | Description |
|---|
eligible required | true | |
type required | "recentProducts" | "similarProducts" | "boughtBySimilarPeopleProducts" | "boughtTogetherProducts" | |
products required | array of object | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | | A field is missing or invalid (error.fields names it). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Suggested products for one cross-sell tab, with the shopper's size (JSON body)
One tab of suggested products for the product page (S07.12, D125), at most 12, each with the size the reco engine gives this profile on that product (null when it has no chart or the engine cannot decide). Latest releases: the same gender (and unisex) and body part, newest first. You might like: the same feed category, the same size chart first. Bought by people like you: kept by at least 5 shoppers close in gender, age, height and weight (training rows of S05.05), most kept first. Bought together: kept in at least 2 of the same orders, most shared orders first; full returns do not count. Only published products still in the feed, with a size in stock and an https page on an allowed domain of the client; never the current product. The cross-sell flag off or no match answers an empty list. An unknown product or chart answers {eligible:false, reason:"unknown_product"} like plugin_info; from a domain the client does not allow, or without Origin, {eligible:false, reason:"not_whitelisted", products:[]} (D39). A bad tab or value answers 422 validation_failed with error.fields. Answers are no-store; body values are never logged. The same names as the GET query, as a JSON body; numbers as numbers.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | string | |
client_product_id required | string | The product page's product id (merchant id). |
cross_sell_products_type required | "recentProducts" | "similarProducts" | "boughtBySimilarPeopleProducts" | "boughtTogetherProducts" | The tab: recentProducts (latest releases), similarProducts (you might like), boughtBySimilarPeopleProducts (bought by people like you), boughtTogetherProducts (bought together). |
sizing_chart_slug | string | The product's chart; an unknown one answers not found. |
gender | "male" | "female" | "unisex" | "boys" | "girls" | "babies" | |
age | integer | null | |
height | number | null | |
weight | number | null | |
foot_length | number | null | |
client_host_name | string | Accepted and ignored. |
client_base_uri | string | Accepted and ignored. |
Answer fields (200)| Name | Type | Description |
|---|
eligible required | true | |
type required | "recentProducts" | "similarProducts" | "boughtBySimilarPeopleProducts" | "boughtTogetherProducts" | |
products required | array of object | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | | A field is missing or invalid (error.fields names it). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Counts of the integration events seen in the last 15 minutes
The docs snippet generator's Verify button (T12.08.04). Counts of product, cart and transaction_end events this client sent in the last 15 minutes, with test and invalid events counted apart; never any event data. Browser calls only from the docs origin. Answers are no-store: poll every 5 s. Limited to 30 calls a minute per IP (429 with Retry-After). A missing or non-numeric client_id answers 422 validation_failed with error.fields; an unknown client answers 404 not_found.
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id. |
Answer fields (200)| Name | Type | Description |
|---|
client_id required | integer | |
window_minutes required | 15 | The events counted are from this many minutes back. |
poll_seconds required | 5 | Poll at this interval; the answer is never cached. |
checked_at required | string | |
events required | object | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found | No client has this id (not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | client_id is missing or not a whole positive number (validation_failed, with error.fields). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Subscribe an email address to sub-processor changes
The sub-processor page's form (T13.04.02, D130 2). Sends a confirm email; the address gets change emails only after its link is clicked (valid 7 days). The answer is the same for a new, pending or already subscribed address and never names the address. Browser calls only from the docs origin. Limited to 5 requests per hour per IP, 3 per day per address and 500 per day in total (429 with Retry-After). A bad address answers 422 validation_failed with error.fields and field_errors.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
email required | string | The address to notify. |
Answer fields (202)| Name | Type | Description |
|---|
status required | "pending" | |
Answers| Status | Error codes | Meaning |
|---|
202 | | Accepted |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | Not a valid email address (validation_failed, with field_errors). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Confirm a sub-processor change subscription
The confirm link of the email (T13.04.02-TC11). Docs origin only for browsers.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
token required | string | The token from the email link. |
Answer fields (200)| Name | Type | Description |
|---|
status required | "confirmed" | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found | The link is unknown, already used or older than 7 days (not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Stop sub-processor change emails and delete the address
The one-click unsubscribe link of every change email (T13.04.02-TC12). The address is deleted at once. Docs origin only for browsers.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
token required | string | The token from the email link. |
Answer fields (200)| Name | Type | Description |
|---|
status required | "unsubscribed" | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found | The link is unknown or the address is already removed (not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Size recommendations
The size widget calls these endpoints itself: you do not need to call them to show sizes. This is a read-only overview of their request and answer shapes, so you can read what the widget sends and gets.
Size-chart (classic) size recommendation for clothes: the D19-compatible answer keys, then ours (CONTRACTS §8)
From a shop page on an allowed domain of client_id (or a server with a recommend API key); anyone else gets {eligible:false, reason:"not_whitelisted", comfort:[]} (D39). Values outside the widget limits, unknown genders or shapes, unknown tape keys and a ± over 5 cm answer 422 validation_failed with error.fields (never the values). An unknown or not-eligible product, or a paused client, answers 404 product_not_eligible with its D50 reason. 60 calls a minute per tracking_id (429), on top of the client + IP limit. Answers are no-store; nothing the shopper sent is logged or stored. The database down answers 503. Quick answers go to the body-service predictor (sd per dimension); tape values in measurements_cm are used as entered (uncertainty_cm, default 1). comfort[0] is recommended_size ?? closest_size; status is recommended, between_sizes, needs_measurements (also when the predictor is down or untrained) or no_match. available never changes the pick. size_conversions (contract v2) holds every chart size in up to 3 other systems, the shop_country's system first. auto: true marks the silent page-load reco: the same answer, counted apart, and an incomplete saved profile answers needs_measurements instead of 422.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | string | The client's numeric id (a number, as the D19 widget sends it, or digits). |
product_id required | string | The merchant's product id, exactly as in the feed. |
tracking_id | string | The device's tracking id; absent before analytics consent (D7). Never changes the size. |
available_sizes | array of string | The page's sizes (live stock). Only sets available; never changes the pick. |
shop_country | string | |
channel | string | |
language | string | |
model | string | number | null | The A/B "Model Variation" value. |
references | array of object | Reference brands (used from S05.04). |
auto | boolean | Auto-reco (T05.08.01): the widget asked silently at page load from the saved profile. Same answer; an incomplete profile answers needs_measurements instead of 422. Counted and logged apart. |
gender required | "male" | "female" | "unisex" | "boys" | "girls" | "babies" | |
age | number | |
height | number | cm |
weight | number | kg |
body_shape | string | null | Female A|XA|H|X, male slug such as regular-athletic-narrow. |
bra | "N/A" | object | null | {region, band, cup} (band as a number or text); {region:"N/A", band:"N/A", cup:"N/A"} = no bra. |
sizing_chart_slug | string | null | Ignored when the product has a chart (D76). |
with_comfort | boolean | |
usualSize | object | null | |
measurements_cm | object | Tape measurements (measured path, D13), keyed by vocabulary id (chest, waist, hips…). |
uncertainty_cm | number | The shopper's own ± for tape values, 0–5 cm (default 1). |
fit_preference | "slim" | "regular" | "relaxed" | |
Answer fields (200)| Name | Type | Description |
|---|
comfort required | array of object | D19: best first; comfort[0] is the shown size. Empty when none. |
sizing_chart_info required | object | null | D19: the full chart used. |
user_measurements required | object | D19: the estimated body under the chart measurement names; Height and Weight as given. |
size_conversions required | object | D19, D89: every chart size (primary label) → its label in up to 3 other systems, the shop country's system first: {M: {FR: {size_name: "38"}, UK: {size_name: "10"}}}. Never the primary system; an empty entry for a size with no other label; present for every status. |
contract_version required | 2 | |
id required | string | Reco id, sent as reco_id in the reco event. |
engine_version required | string | |
body_model_version | string | |
predictor_version | string | |
chart_id required | string | null | The chart used; null for a social-proof answer on a product with no chart. |
chart_revision required | string | null | The chart's content hash; null when chart_id is null. |
product_id required | string | |
mode required | "classic" | "social_proof" | "usual_size" | "shoes" | "head" | |
input_source required | "questions" | "measurements" | "mixed" | "usual_size" | "profile" | |
status required | "recommended" | "between_sizes" | "needs_measurements" | "no_match" | |
recommended_size required | string | null | |
closest_size required | string | null | |
alternatives required | array of string | |
direction_hint required | "size_up_for_relaxed" | "size_down_for_slim" | null | |
deciding_measurement | string | null | D98, T07.09.09 (additive, contract version unchanged): the predicted primary measurement (vocabulary id, e.g. waist) whose tape value would most raise the chance of the right size; null when the gain is under the engine threshold, when every primary value is entered, for needs_measurements and single-size answers. Absent on usual-size, social-proof, shoe and head answers. |
available required | boolean | null | |
message_key required | "RESULT_RECOMMENDED" | "RESULT_BETWEEN_SIZES" | "RESULT_NEEDS_MEASUREMENTS" | "RESULT_NO_MATCH" | "RESULT_NOT_AVAILABLE" | "PRODUCT_UNIQUE_SIZE" | "EDGE_OF_CHART" | "SORRY_SOCIAL_PROOF" | |
missing_measurements required | array of string | |
fit_preference required | "slim" | "regular" | "relaxed" | "brand" | |
measurements_used required | object | |
candidates required | array of object | |
comforts required | object | Vocabulary id → level of the shown size. |
primary_size_system required | string | |
incoherent_references | array of object | S05.04: references that contradict the body data or each other (D91), by their place in the request. On every classic answer (empty when none is flagged); a flag never blocks the size. |
social_proof_sizes | array of object | Social proof (S05.05): each size's share among similar shoppers who kept the product, best first, ratios rounded to 2 decimals summing to 1; empty for no_match. |
first_size | object | Social proof (D19 key): the top size and its ratio; {name: null, ratio: null} for no_match. |
second_size | object | Social proof (D19 key): the second size, or nulls. |
primary_system | object | null | Social proof (D19 key): the size system of the names, {name: "EU"}; null without a chart. |
sizing_chart_id | integer | null | Social proof (D19 key): the product's linked chart, or null when it has none. |
ratio_high | boolean | Social proof: the top ratio is above 0.6. |
social_proof_level | "product" | "category_gender_body_part" | "body_part_gender" | null | Social proof: which rows gave the shares (the product, then fallbacks); null for no_match. |
social_proof_samples | integer | Social proof: how many similar shoppers were used (or found, for no_match). |
morph_params | array of number | |
found | boolean | Usual size (D19 key): a size was found. |
name | string | null | Usual size (D19 key): the size, as recommended_size. |
size_order | integer | null | Usual size (D19 key): 0-based place in the chart. |
is_usual_size | true | Usual size: lets the widget send usual_size_add_to_cart. |
offset_applied | integer | Usual size: sizes actually moved from the usual size. Classic with a product offset (T04.07.03): sizes the offset moved (0 at the chart's edge). |
sizing_adjustment | number | Usual size, or classic with a product offset: the product's merchant offset that was used. |
precision required | null | Stays null until calibrated (S14.09). |
accuracy_validated required | false | |
confidence_probability required | null | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | | Unknown resource, or a product with no reco (product_not_eligible with reason). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | Values fail validation (validation_failed); error.fields names them. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Foot-length size recommendation: shoes (CONTRACTS §8, S05.07)
From a shop page on an allowed domain of client_id (or a server with a recommend API key); anyone else gets {eligible:false, reason:"not_whitelisted", comfort:[]} (D39). Values outside the widget limits, unknown genders or shapes, unknown tape keys and a ± over 5 cm answer 422 validation_failed with error.fields (never the values). An unknown or not-eligible product, or a paused client, answers 404 product_not_eligible with its D50 reason. 60 calls a minute per tracking_id (429), on top of the client + IP limit. Answers are no-store; nothing the shopper sent is logged or stored. The database down answers 503. foot_length (cm, 10–34) is matched with the product's feet chart, ±0.25 cm (D111); a chart that gives the shoe's inner length adds the toe allowance (0.5–1.0 cm). Without it, adult male / female get an estimate from height (source: predicted, a larger ±), moved by shoe references (type_of_references=shoe); with no height, shoe references alone give it; children and unisex without a foot length answer needs_measurements. With none of foot length, height or a reference: 422 naming foot_length. A product that is not feet is a 422 naming product_id. One zone, FootLength (TOO_TIGHT = too short … TOO_LOOSE = too long); between two sizes the size where the foot is too short is never first. The §8 shape with mode: shoes, no deciding_measurement, no morph_params.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | string | The client's numeric id (a number, as the D19 widget sends it, or digits). |
product_id required | string | The merchant's product id, exactly as in the feed. |
tracking_id | string | The device's tracking id; absent before analytics consent (D7). Never changes the size. |
available_sizes | array of string | The page's sizes (live stock). Only sets available; never changes the pick. |
shop_country | string | |
channel | string | |
language | string | |
model | string | number | null | The A/B "Model Variation" value. |
references | array of object | Reference brands (used from S05.04). |
auto | boolean | Auto-reco (T05.08.01): the widget asked silently at page load from the saved profile. Same answer; an incomplete profile answers needs_measurements instead of 422. Counted and logged apart. |
gender required | "male" | "female" | "unisex" | "boys" | "girls" | "babies" | |
age | number | |
height | number | cm |
weight | number | kg |
foot_length | number | cm, 10–34. Optional with height or a shoe reference. |
Answer fields (200)| Name | Type | Description |
|---|
comfort required | array of object | D19: best first; comfort[0] is the shown size. Empty when none. |
sizing_chart_info required | object | null | D19: the full chart used. |
user_measurements required | object | D19: the estimated body under the chart measurement names; Height and Weight as given. |
size_conversions required | object | D19, D89: every chart size (primary label) → its label in up to 3 other systems, the shop country's system first: {M: {FR: {size_name: "38"}, UK: {size_name: "10"}}}. Never the primary system; an empty entry for a size with no other label; present for every status. |
contract_version required | 2 | |
id required | string | Reco id, sent as reco_id in the reco event. |
engine_version required | string | |
body_model_version | string | |
predictor_version | string | |
chart_id required | string | null | The chart used; null for a social-proof answer on a product with no chart. |
chart_revision required | string | null | The chart's content hash; null when chart_id is null. |
product_id required | string | |
mode required | "classic" | "social_proof" | "usual_size" | "shoes" | "head" | |
input_source required | "questions" | "measurements" | "mixed" | "usual_size" | "profile" | |
status required | "recommended" | "between_sizes" | "needs_measurements" | "no_match" | |
recommended_size required | string | null | |
closest_size required | string | null | |
alternatives required | array of string | |
direction_hint required | "size_up_for_relaxed" | "size_down_for_slim" | null | |
deciding_measurement | string | null | D98, T07.09.09 (additive, contract version unchanged): the predicted primary measurement (vocabulary id, e.g. waist) whose tape value would most raise the chance of the right size; null when the gain is under the engine threshold, when every primary value is entered, for needs_measurements and single-size answers. Absent on usual-size, social-proof, shoe and head answers. |
available required | boolean | null | |
message_key required | "RESULT_RECOMMENDED" | "RESULT_BETWEEN_SIZES" | "RESULT_NEEDS_MEASUREMENTS" | "RESULT_NO_MATCH" | "RESULT_NOT_AVAILABLE" | "PRODUCT_UNIQUE_SIZE" | "EDGE_OF_CHART" | "SORRY_SOCIAL_PROOF" | |
missing_measurements required | array of string | |
fit_preference required | "slim" | "regular" | "relaxed" | "brand" | |
measurements_used required | object | |
candidates required | array of object | |
comforts required | object | Vocabulary id → level of the shown size. |
primary_size_system required | string | |
incoherent_references | array of object | S05.04: references that contradict the body data or each other (D91), by their place in the request. On every classic answer (empty when none is flagged); a flag never blocks the size. |
social_proof_sizes | array of object | Social proof (S05.05): each size's share among similar shoppers who kept the product, best first, ratios rounded to 2 decimals summing to 1; empty for no_match. |
first_size | object | Social proof (D19 key): the top size and its ratio; {name: null, ratio: null} for no_match. |
second_size | object | Social proof (D19 key): the second size, or nulls. |
primary_system | object | null | Social proof (D19 key): the size system of the names, {name: "EU"}; null without a chart. |
sizing_chart_id | integer | null | Social proof (D19 key): the product's linked chart, or null when it has none. |
ratio_high | boolean | Social proof: the top ratio is above 0.6. |
social_proof_level | "product" | "category_gender_body_part" | "body_part_gender" | null | Social proof: which rows gave the shares (the product, then fallbacks); null for no_match. |
social_proof_samples | integer | Social proof: how many similar shoppers were used (or found, for no_match). |
morph_params | array of number | |
found | boolean | Usual size (D19 key): a size was found. |
name | string | null | Usual size (D19 key): the size, as recommended_size. |
size_order | integer | null | Usual size (D19 key): 0-based place in the chart. |
is_usual_size | true | Usual size: lets the widget send usual_size_add_to_cart. |
offset_applied | integer | Usual size: sizes actually moved from the usual size. Classic with a product offset (T04.07.03): sizes the offset moved (0 at the chart's edge). |
sizing_adjustment | number | Usual size, or classic with a product offset: the product's merchant offset that was used. |
precision required | null | Stays null until calibrated (S14.09). |
accuracy_validated required | false | |
confidence_probability required | null | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | | Unknown resource, or a product with no reco (product_not_eligible with reason). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | Values fail validation (validation_failed); error.fields names them. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Head-circumference size recommendation: helmets and hats (CONTRACTS §8, S05.07)
From a shop page on an allowed domain of client_id (or a server with a recommend API key); anyone else gets {eligible:false, reason:"not_whitelisted", comfort:[]} (D39). Values outside the widget limits, unknown genders or shapes, unknown tape keys and a ± over 5 cm answer 422 validation_failed with error.fields (never the values). An unknown or not-eligible product, or a paused client, answers 404 product_not_eligible with its D50 reason. 60 calls a minute per tracking_id (429), on top of the client + IP limit. Answers are no-store; nothing the shopper sent is logged or stored. The database down answers 503. head_circumference (cm; adults 50–65, boys / girls 48–58, babies 40–58) is matched with the product's head chart, ±0.5 cm (D109). It is never estimated from height or weight: without it a helmet reference (type_of_references=head) gives it, and with neither the answer is 422 naming head_circumference. A product that is not head is a 422 naming product_id. One girth zone, HeadCircumference. The §8 shape with mode: head, no deciding_measurement, no morph_params.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | string | The client's numeric id (a number, as the D19 widget sends it, or digits). |
product_id required | string | The merchant's product id, exactly as in the feed. |
tracking_id | string | The device's tracking id; absent before analytics consent (D7). Never changes the size. |
available_sizes | array of string | The page's sizes (live stock). Only sets available; never changes the pick. |
shop_country | string | |
channel | string | |
language | string | |
model | string | number | null | The A/B "Model Variation" value. |
references | array of object | Reference brands (used from S05.04). |
auto | boolean | Auto-reco (T05.08.01): the widget asked silently at page load from the saved profile. Same answer; an incomplete profile answers needs_measurements instead of 422. Counted and logged apart. |
gender required | "male" | "female" | "unisex" | "boys" | "girls" | "babies" | |
age | number | |
height | number | cm |
weight | number | kg |
head_circumference | number | cm. Optional with a helmet reference. |
Answer fields (200)| Name | Type | Description |
|---|
comfort required | array of object | D19: best first; comfort[0] is the shown size. Empty when none. |
sizing_chart_info required | object | null | D19: the full chart used. |
user_measurements required | object | D19: the estimated body under the chart measurement names; Height and Weight as given. |
size_conversions required | object | D19, D89: every chart size (primary label) → its label in up to 3 other systems, the shop country's system first: {M: {FR: {size_name: "38"}, UK: {size_name: "10"}}}. Never the primary system; an empty entry for a size with no other label; present for every status. |
contract_version required | 2 | |
id required | string | Reco id, sent as reco_id in the reco event. |
engine_version required | string | |
body_model_version | string | |
predictor_version | string | |
chart_id required | string | null | The chart used; null for a social-proof answer on a product with no chart. |
chart_revision required | string | null | The chart's content hash; null when chart_id is null. |
product_id required | string | |
mode required | "classic" | "social_proof" | "usual_size" | "shoes" | "head" | |
input_source required | "questions" | "measurements" | "mixed" | "usual_size" | "profile" | |
status required | "recommended" | "between_sizes" | "needs_measurements" | "no_match" | |
recommended_size required | string | null | |
closest_size required | string | null | |
alternatives required | array of string | |
direction_hint required | "size_up_for_relaxed" | "size_down_for_slim" | null | |
deciding_measurement | string | null | D98, T07.09.09 (additive, contract version unchanged): the predicted primary measurement (vocabulary id, e.g. waist) whose tape value would most raise the chance of the right size; null when the gain is under the engine threshold, when every primary value is entered, for needs_measurements and single-size answers. Absent on usual-size, social-proof, shoe and head answers. |
available required | boolean | null | |
message_key required | "RESULT_RECOMMENDED" | "RESULT_BETWEEN_SIZES" | "RESULT_NEEDS_MEASUREMENTS" | "RESULT_NO_MATCH" | "RESULT_NOT_AVAILABLE" | "PRODUCT_UNIQUE_SIZE" | "EDGE_OF_CHART" | "SORRY_SOCIAL_PROOF" | |
missing_measurements required | array of string | |
fit_preference required | "slim" | "regular" | "relaxed" | "brand" | |
measurements_used required | object | |
candidates required | array of object | |
comforts required | object | Vocabulary id → level of the shown size. |
primary_size_system required | string | |
incoherent_references | array of object | S05.04: references that contradict the body data or each other (D91), by their place in the request. On every classic answer (empty when none is flagged); a flag never blocks the size. |
social_proof_sizes | array of object | Social proof (S05.05): each size's share among similar shoppers who kept the product, best first, ratios rounded to 2 decimals summing to 1; empty for no_match. |
first_size | object | Social proof (D19 key): the top size and its ratio; {name: null, ratio: null} for no_match. |
second_size | object | Social proof (D19 key): the second size, or nulls. |
primary_system | object | null | Social proof (D19 key): the size system of the names, {name: "EU"}; null without a chart. |
sizing_chart_id | integer | null | Social proof (D19 key): the product's linked chart, or null when it has none. |
ratio_high | boolean | Social proof: the top ratio is above 0.6. |
social_proof_level | "product" | "category_gender_body_part" | "body_part_gender" | null | Social proof: which rows gave the shares (the product, then fallbacks); null for no_match. |
social_proof_samples | integer | Social proof: how many similar shoppers were used (or found, for no_match). |
morph_params | array of number | |
found | boolean | Usual size (D19 key): a size was found. |
name | string | null | Usual size (D19 key): the size, as recommended_size. |
size_order | integer | null | Usual size (D19 key): 0-based place in the chart. |
is_usual_size | true | Usual size: lets the widget send usual_size_add_to_cart. |
offset_applied | integer | Usual size: sizes actually moved from the usual size. Classic with a product offset (T04.07.03): sizes the offset moved (0 at the chart's edge). |
sizing_adjustment | number | Usual size, or classic with a product offset: the product's merchant offset that was used. |
precision required | null | Stays null until calibrated (S14.09). |
accuracy_validated required | false | |
confidence_probability required | null | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | | Unknown resource, or a product with no reco (product_not_eligible with reason). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | Values fail validation (validation_failed); error.fields names them. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Social-proof size recommendation: sizes kept by similar shoppers (CONTRACTS §8, S05.05)
From a shop page on an allowed domain of client_id (or a server with a recommend API key); anyone else gets {eligible:false, reason:"not_whitelisted", comfort:[]} (D39). Values outside the widget limits, unknown genders or shapes, unknown tape keys and a ± over 5 cm answer 422 validation_failed with error.fields (never the values). An unknown or not-eligible product, or a paused client, answers 404 product_not_eligible with its D50 reason. 60 calls a minute per tracking_id (429), on top of the client + IP limit. Answers are no-store; nothing the shopper sent is logged or stored. The database down answers 503. For clothes, shoes and headwear in social-proof mode (the product's reco_mode override, or the client's default mode): gender, age, height and weight are required, foot_length / head_circumference sharpen shoes and headwear, sizing_chart_slug is null. The shares come from the shopper's nearest neighbours (same gender, about 50) among the shoppers who kept the product, falling back to its category + body part, then its body part; social_proof_sizes lists them best first, rounded to 2 decimals and summing to 1, and the top one is recommended_size (available: false when out of stock; no other size is swapped in). The older widget keys first_size, second_size, size_conversions, primary_system and sizing_chart_id (null without a chart) are kept. Fewer than the configured minimum (default 30) similar shoppers at every level, or no training data yet, answer status: no_match with message_key: SORRY_SOCIAL_PROOF, empty social_proof_sizes and null first_size / second_size. A product that is not in social-proof mode answers 404 product_not_eligible with reason: not_social_proof. Shares change only after the next daily training run; training data that cannot be read answers 503.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | string | The client's numeric id (a number, as the D19 widget sends it, or digits). |
product_id required | string | The merchant's product id, exactly as in the feed. |
tracking_id | string | The device's tracking id; absent before analytics consent (D7). Never changes the size. |
available_sizes | array of string | The page's sizes (live stock). Only sets available; never changes the pick. |
shop_country | string | |
channel | string | |
language | string | |
model | string | number | null | The A/B "Model Variation" value. |
references | array of object | Reference brands (used from S05.04). |
auto | boolean | Auto-reco (T05.08.01): the widget asked silently at page load from the saved profile. Same answer; an incomplete profile answers needs_measurements instead of 422. Counted and logged apart. |
gender required | "male" | "female" | "unisex" | "boys" | "girls" | "babies" | |
age | number | |
height | number | cm |
weight | number | kg |
foot_length | number | |
head_circumference | number | |
sizing_chart_slug | string | null | |
Answer fields (200)| Name | Type | Description |
|---|
comfort required | array of object | D19: best first; comfort[0] is the shown size. Empty when none. |
sizing_chart_info required | object | null | D19: the full chart used. |
user_measurements required | object | D19: the estimated body under the chart measurement names; Height and Weight as given. |
size_conversions required | object | D19, D89: every chart size (primary label) → its label in up to 3 other systems, the shop country's system first: {M: {FR: {size_name: "38"}, UK: {size_name: "10"}}}. Never the primary system; an empty entry for a size with no other label; present for every status. |
contract_version required | 2 | |
id required | string | Reco id, sent as reco_id in the reco event. |
engine_version required | string | |
body_model_version | string | |
predictor_version | string | |
chart_id required | string | null | The chart used; null for a social-proof answer on a product with no chart. |
chart_revision required | string | null | The chart's content hash; null when chart_id is null. |
product_id required | string | |
mode required | "classic" | "social_proof" | "usual_size" | "shoes" | "head" | |
input_source required | "questions" | "measurements" | "mixed" | "usual_size" | "profile" | |
status required | "recommended" | "between_sizes" | "needs_measurements" | "no_match" | |
recommended_size required | string | null | |
closest_size required | string | null | |
alternatives required | array of string | |
direction_hint required | "size_up_for_relaxed" | "size_down_for_slim" | null | |
deciding_measurement | string | null | D98, T07.09.09 (additive, contract version unchanged): the predicted primary measurement (vocabulary id, e.g. waist) whose tape value would most raise the chance of the right size; null when the gain is under the engine threshold, when every primary value is entered, for needs_measurements and single-size answers. Absent on usual-size, social-proof, shoe and head answers. |
available required | boolean | null | |
message_key required | "RESULT_RECOMMENDED" | "RESULT_BETWEEN_SIZES" | "RESULT_NEEDS_MEASUREMENTS" | "RESULT_NO_MATCH" | "RESULT_NOT_AVAILABLE" | "PRODUCT_UNIQUE_SIZE" | "EDGE_OF_CHART" | "SORRY_SOCIAL_PROOF" | |
missing_measurements required | array of string | |
fit_preference required | "slim" | "regular" | "relaxed" | "brand" | |
measurements_used required | object | |
candidates required | array of object | |
comforts required | object | Vocabulary id → level of the shown size. |
primary_size_system required | string | |
incoherent_references | array of object | S05.04: references that contradict the body data or each other (D91), by their place in the request. On every classic answer (empty when none is flagged); a flag never blocks the size. |
social_proof_sizes | array of object | Social proof (S05.05): each size's share among similar shoppers who kept the product, best first, ratios rounded to 2 decimals summing to 1; empty for no_match. |
first_size | object | Social proof (D19 key): the top size and its ratio; {name: null, ratio: null} for no_match. |
second_size | object | Social proof (D19 key): the second size, or nulls. |
primary_system | object | null | Social proof (D19 key): the size system of the names, {name: "EU"}; null without a chart. |
sizing_chart_id | integer | null | Social proof (D19 key): the product's linked chart, or null when it has none. |
ratio_high | boolean | Social proof: the top ratio is above 0.6. |
social_proof_level | "product" | "category_gender_body_part" | "body_part_gender" | null | Social proof: which rows gave the shares (the product, then fallbacks); null for no_match. |
social_proof_samples | integer | Social proof: how many similar shoppers were used (or found, for no_match). |
morph_params | array of number | |
found | boolean | Usual size (D19 key): a size was found. |
name | string | null | Usual size (D19 key): the size, as recommended_size. |
size_order | integer | null | Usual size (D19 key): 0-based place in the chart. |
is_usual_size | true | Usual size: lets the widget send usual_size_add_to_cart. |
offset_applied | integer | Usual size: sizes actually moved from the usual size. Classic with a product offset (T04.07.03): sizes the offset moved (0 at the chart's edge). |
sizing_adjustment | number | Usual size, or classic with a product offset: the product's merchant offset that was used. |
precision required | null | Stays null until calibrated (S14.09). |
accuracy_validated required | false | |
confidence_probability required | null | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | | Unknown resource, or a product with no reco (product_not_eligible with reason). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | Values fail validation (validation_failed); error.fields names them. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Usual-size recommendation: the shopper's usual size moved by fit feeling and desired fit (CONTRACTS §8)
From a shop page on an allowed domain of client_id (or a server with a recommend API key); anyone else gets {eligible:false, reason:"not_whitelisted", comfort:[]} (D39). Values outside the widget limits, unknown genders or shapes, unknown tape keys and a ± over 5 cm answer 422 validation_failed with error.fields (never the values). An unknown or not-eligible product, or a paused client, answers 404 product_not_eligible with its D50 reason. 60 calls a minute per tracking_id (429), on top of the client + IP limit. Answers are no-store; nothing the shopper sent is logged or stored. The database down answers 503. The usual size (one of plugin_info.usual_size_list, read in size_system_id or else the product's preferred system) moves by the fit answers: narrow and oversize +½ size, comfy and slim −½, plus the product's own offset, rounded away from zero, at most ±2 (D89). A sizing_adjustment sent here is ignored. Answers the §8 shape with mode: usual_size and the D19 keys found, name, size_order, precision, plus is_usual_size and offset_applied. A name outside the list, or an unknown size_system_id, is a 422 naming the field. Needs no predictor.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | string | The client's numeric id (a number, as the D19 widget sends it, or digits). |
product_id required | string | The merchant's product id, exactly as in the feed. |
tracking_id | string | The device's tracking id; absent before analytics consent (D7). Never changes the size. |
available_sizes | array of string | The page's sizes (live stock). Only sets available; never changes the pick. |
shop_country | string | |
channel | string | |
language | string | |
model | string | number | null | The A/B "Model Variation" value. |
references | array of object | Reference brands (used from S05.04). |
auto | boolean | Auto-reco (T05.08.01): the widget asked silently at page load from the saved profile. Same answer; an incomplete profile answers needs_measurements instead of 422. Counted and logged apart. |
size_system_id | integer | string | A size system id of GET /v1/sizes (EU is 2). Default: the product's preferred system. |
usual_size_name required | string | One of plugin_info.usual_size_list. |
fit_feeling required | "narrow" | "perfect" | "comfy" | How the usual size fits: narrow, perfect or comfy. |
desired_fit required | "slim" | "regular" | "oversize" | How this product should fit: slim, regular or oversize. |
sizing_adjustment | number | Ignored: the product's own offset is used (D89). |
Answer fields (200)| Name | Type | Description |
|---|
comfort required | array of object | D19: best first; comfort[0] is the shown size. Empty when none. |
sizing_chart_info required | object | null | D19: the full chart used. |
user_measurements required | object | D19: the estimated body under the chart measurement names; Height and Weight as given. |
size_conversions required | object | D19, D89: every chart size (primary label) → its label in up to 3 other systems, the shop country's system first: {M: {FR: {size_name: "38"}, UK: {size_name: "10"}}}. Never the primary system; an empty entry for a size with no other label; present for every status. |
contract_version required | 2 | |
id required | string | Reco id, sent as reco_id in the reco event. |
engine_version required | string | |
body_model_version | string | |
predictor_version | string | |
chart_id required | string | null | The chart used; null for a social-proof answer on a product with no chart. |
chart_revision required | string | null | The chart's content hash; null when chart_id is null. |
product_id required | string | |
mode required | "classic" | "social_proof" | "usual_size" | "shoes" | "head" | |
input_source required | "questions" | "measurements" | "mixed" | "usual_size" | "profile" | |
status required | "recommended" | "between_sizes" | "needs_measurements" | "no_match" | |
recommended_size required | string | null | |
closest_size required | string | null | |
alternatives required | array of string | |
direction_hint required | "size_up_for_relaxed" | "size_down_for_slim" | null | |
deciding_measurement | string | null | D98, T07.09.09 (additive, contract version unchanged): the predicted primary measurement (vocabulary id, e.g. waist) whose tape value would most raise the chance of the right size; null when the gain is under the engine threshold, when every primary value is entered, for needs_measurements and single-size answers. Absent on usual-size, social-proof, shoe and head answers. |
available required | boolean | null | |
message_key required | "RESULT_RECOMMENDED" | "RESULT_BETWEEN_SIZES" | "RESULT_NEEDS_MEASUREMENTS" | "RESULT_NO_MATCH" | "RESULT_NOT_AVAILABLE" | "PRODUCT_UNIQUE_SIZE" | "EDGE_OF_CHART" | "SORRY_SOCIAL_PROOF" | |
missing_measurements required | array of string | |
fit_preference required | "slim" | "regular" | "relaxed" | "brand" | |
measurements_used required | object | |
candidates required | array of object | |
comforts required | object | Vocabulary id → level of the shown size. |
primary_size_system required | string | |
incoherent_references | array of object | S05.04: references that contradict the body data or each other (D91), by their place in the request. On every classic answer (empty when none is flagged); a flag never blocks the size. |
social_proof_sizes | array of object | Social proof (S05.05): each size's share among similar shoppers who kept the product, best first, ratios rounded to 2 decimals summing to 1; empty for no_match. |
first_size | object | Social proof (D19 key): the top size and its ratio; {name: null, ratio: null} for no_match. |
second_size | object | Social proof (D19 key): the second size, or nulls. |
primary_system | object | null | Social proof (D19 key): the size system of the names, {name: "EU"}; null without a chart. |
sizing_chart_id | integer | null | Social proof (D19 key): the product's linked chart, or null when it has none. |
ratio_high | boolean | Social proof: the top ratio is above 0.6. |
social_proof_level | "product" | "category_gender_body_part" | "body_part_gender" | null | Social proof: which rows gave the shares (the product, then fallbacks); null for no_match. |
social_proof_samples | integer | Social proof: how many similar shoppers were used (or found, for no_match). |
morph_params | array of number | |
found | boolean | Usual size (D19 key): a size was found. |
name | string | null | Usual size (D19 key): the size, as recommended_size. |
size_order | integer | null | Usual size (D19 key): 0-based place in the chart. |
is_usual_size | true | Usual size: lets the widget send usual_size_add_to_cart. |
offset_applied | integer | Usual size: sizes actually moved from the usual size. Classic with a product offset (T04.07.03): sizes the offset moved (0 at the chart's edge). |
sizing_adjustment | number | Usual size, or classic with a product offset: the product's merchant offset that was used. |
precision required | null | Stays null until calibrated (S14.09). |
accuracy_validated required | false | |
confidence_probability required | null | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | | Unknown resource, or a product with no reco (product_not_eligible with reason). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | Values fail validation (validation_failed); error.fields names them. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Events and tracking
Where the tag, the widget and your server send events. Every event needs a non-null tracking_id; see Event schemas for each event's fields.
Send one tracking event, an array or {events:[…]} of up to 50 (64 KiB)
One event {client_id, event, tracking_id, …} (the compatible form, D19), a JSON array or {events:[…]}. text/plain (sendBeacon) is read as JSON. The call is queued and answered 202 {received, accepted, rejected} (D72); events are validated, enriched and stored by the workers. An unknown event is listed in rejected (a single event: 400 unknown_event); an event_id already sent with other data is event_id_conflict (a single event: 409). An unknown client answers 400 unknown_client; a bot or a paused client is dropped with a 2xx. Events from a domain the client does not allow are dropped with a 200 (D39). Limits: the per-call limit plus 200 events/s per tracking_id and 5,000 events/s per client (429). The queue down answers 503 queue_unavailable; another content type 415.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | |
event required | string | |
tracking_id | string | |
timestamp_ms | integer | |
event_id | string | |
schema_version | 1 | |
user_agent | string | |
shop_country | string | null | |
channel | string | null | |
instance_id | string | |
product_id | string | |
brand_id | integer | null | |
product_associated | boolean | |
ab_data | object | |
testEvent | true | |
extra | object | |
Answer fields (202)| Name | Type | Description |
|---|
received required | integer | Events in the call (the compatible key, D19). |
accepted | integer | Events taken into the queue (or already stored). |
rejected | array of object | Events refused at the door, by their index in the call. |
dropped | integer | Events dropped without an error to fix: origin not allowed (D39), a bot, a paused client. |
reason | "not_whitelisted" | "bot" | "client_not_serving" | |
Answers| Status | Error codes | Meaning |
|---|
202 | | Accepted |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Integration-script heartbeat and debug events (__debug__)
Same body forms, limits, origin rules and answer as POST /events; every item is stored as a __debug__ event (kept 7 days).
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | |
event required | string | |
tracking_id | string | |
timestamp_ms | integer | |
event_id | string | |
schema_version | 1 | |
user_agent | string | |
shop_country | string | null | |
channel | string | null | |
instance_id | string | |
product_id | string | |
brand_id | integer | null | |
product_associated | boolean | |
ab_data | object | |
testEvent | true | |
extra | object | |
Answer fields (202)| Name | Type | Description |
|---|
received required | integer | Events in the call (the compatible key, D19). |
accepted | integer | Events taken into the queue (or already stored). |
rejected | array of object | Events refused at the door, by their index in the call. |
dropped | integer | Events dropped without an error to fix: origin not allowed (D39), a bot, a paused client. |
reason | "not_whitelisted" | "bot" | "client_not_serving" | |
Answers| Status | Error codes | Meaning |
|---|
202 | | Accepted |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Product-analytics proxy: one UX event under its name (the compatible sendMixpanelEvent)
The compatible body {payload: {client_id, tracking_id, …event fields}, mixpanel_properties: {$referrer, $screen_width, $screen_height, utm_source}}; only those four page properties are kept. The name (URL-encoded) is a catalogue event name or a mapped older product-analytics name (Plugin Opened → plugin_open, Size Validated → size_validated, …); any other name is listed in rejected as unknown_event (202). The event then goes through the same intake as POST /events (origin rules and the D39 soft refusal, bots, rate limits, 64 KiB, the queue) and is stored like it (ux events are kept 90 days). No vendor is called in the request: with the client's analytics forwarding on, the worker forwards stored UX events later (T08.08.02).
Parameters| Name | In | Type | Description |
|---|
name required | path | string | The event name, URL-encoded, such as size_validated or Size%20Validated. |
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
payload required | object | The event without its name: client_id, tracking_id and the event's fields. |
mixpanel_properties | object | |
Answer fields (202)| Name | Type | Description |
|---|
received required | integer | Events in the call (the compatible key, D19). |
accepted | integer | Events taken into the queue (or already stored). |
rejected | array of object | Events refused at the door, by their index in the call. |
dropped | integer | Events dropped without an error to fix: origin not allowed (D39), a bot, a paused client. |
reason | "not_whitelisted" | "bot" | "client_not_serving" | |
Answers| Status | Error codes | Meaning |
|---|
202 | | Accepted |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Shopify Web Pixel event (cart, checkout)
Sent from Shopify's pixel sandbox, so the body's origin (the shop page) must be an allowed domain of client_id; otherwise the event is dropped with a 200. An optional ingest_key is verified when sent: a wrong one answers 401 invalid_ingest_key. Accepted events are queued (202) like POST /events; checkout becomes transaction_end.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
client_id required | integer | string | |
origin | string | |
event required | string | |
ingest_key | object | Optional fmt_ik_… key (a string); verified when sent. |
tracking_id | string | null | |
shop_country | string | null | |
product_id | string | number | null | |
variant_id | string | number | null | |
timestamp | string | |
Answer fields (202)| Name | Type | Description |
|---|
received required | integer | Events in the call (the compatible key, D19). |
accepted | integer | Events taken into the queue (or already stored). |
rejected | array of object | Events refused at the door, by their index in the call. |
dropped | integer | Events dropped without an error to fix: origin not allowed (D39), a bot, a paused client. |
reason | "not_whitelisted" | "bot" | "client_not_serving" | |
Answers| Status | Error codes | Meaning |
|---|
202 | | Accepted |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
3D body
The measurement vocabulary (ids, label keys, groups, ranges, definitions) and the body model version
Served from memory, built once per model version from the shared vocabulary; fitter-only dimensions (pelvisGirth) are left out. ETag names the model version and a digest of the vocabulary; cached 5 min. Model data with no shopper data: any origin may read it (D82). The body-service down or loading answers 503.
Answer fields (200)| Name | Type | Description |
|---|
modelVersion required | string | |
units required | "cm" | |
dimensions required | array of object | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
The default female or male body of the current body model (a public, immutable CDN artifact)
Without modelVersion: a 302 (cached 60 s) to ?bodyType=…&modelVersion=<current>. With it: the body-service's default-body report (params, measurements, rest joints, tape paths), Cache-Control: public, max-age=31536000, immutable, built once per body type and model version (no job per shopper). Another model version answers 404. Model data with no shopper data: any origin may read it (D82).
Parameters| Name | In | Type | Description |
|---|
bodyType required | query | "female" | "male" | female or male. |
modelVersion | query | string | Body model version (anny-0.6.1-fmt-1). Without it the answer is a 302 to the current one. |
Answer fields (200)| Name | Type | Description |
|---|
modelVersion required | string | Body model version; part of the URL and of every cache key. |
bodyType required | "female" | "male" | |
params required | array of number | [logScale, ...morphs] of the default body. |
measuredCm required | object | Every mesh dimension of the vocabulary, in cm. |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
302 | | To the current model version's immutable URL. |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Simple-mode answers → every body measurement with its own sd, and the live body's morph params
Anonymous shopper call from an allowed domain of client_id (D14); another origin, no Origin or an unknown or paused client is refused (403). Units are cm and kg. Synchronous, answered by the body-service predictor within 1 s (no job). Boys and girls get {status:"needs_measurements"} while the predictor has no child data. Values outside the widget's limits, unknown fields, a shape of the other gender or a bra on a male body answer 422 validation_failed with error.fields (never the values). No answer and no measurement is logged or stored. Rate limited per device (tracking_id, else client and IP): 429. The body-service down or slow answers 503; no trained predictor answers 501 predictor_not_available with accuracyValidated:false. accuracyValidated stays false until the accuracy validation (S14.09) signs off.
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id (or the X-Fmt-Client header). |
tracking_id | query | string | The widget's tracking id: the device the rate limit counts (else client and IP). |
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
gender required | "female" | "male" | "boys" | "girls" | |
age required | integer | |
heightCm required | number | |
weightKg required | number | |
bodyShape | string | null | |
bra | "N/A" | object | null | |
entered | object | |
Answer fields (200)| Name | Type | Description |
|---|
status required | "predicted" | |
bodyType required | "female" | "male" | |
measurements required | object | |
morphParams required | array of number | |
morphParamLabels required | array of string | |
bodyGapCm required | object | |
outsideTrainingData required | boolean | |
modelVersion required | string | |
predictorVersion required | string | |
accuracyValidated required | boolean | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
501 | predictor_not_available | No trained predictor yet (predictor_not_available, accuracyValidated:false). |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Fit the body to tape values in a background job (202 + the job)
Anonymous shopper call from an allowed domain of client_id (D14); another origin, no Origin or an unknown or paused client is refused (403). Needs Idempotency-Key. Answers 202 with the job and Location; poll GET /v1/body/jobs/{id} every retryAfterMs. The request and the result are stored encrypted and kept 24 h. Limits: per device (tracking_id, else client and IP; 15 a minute) and per client (429), at most 100 queued fits per client (429). The body-service down or loading answers 503.
Parameters| Name | In | Type | Description |
|---|
client_id required | query | string | The client's numeric id (or the X-Fmt-Client header). |
tracking_id | query | string | The widget's tracking id: the device the rate limit counts (else client and IP). |
Idempotency-Key required | header | string | |
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
bodyType required | "female" | "male" | |
measurementsCm required | object | |
locked | array of string | |
parameters | array of number | |
shape | object | |
population | object | |
Answer fields (202)| Name | Type | Description |
|---|
id required | string | |
kind required | "fit" | |
state required | "queued" | "running" | "done" | "failed" | "cancelled" | |
createdAt required | string | |
finishedAt required | string | null | |
expiresAt required | string | |
attempts required | integer | |
retryAfterMs required | integer | null | |
modelVersion required | string | null | |
error required | object | null | |
results required | object | null | |
Answers| Status | Error codes | Meaning |
|---|
202 | | Accepted |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
409 | conflict | The Idempotency-Key was used with another body (conflict). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
A body job's state (queued, running, done, failed, cancelled) and, when done, its result links
Anonymous shopper call from an allowed domain of client_id (D14); another origin, no Origin or an unknown or paused client is refused (403). Another client's job, or an unknown id, is 404; after 24 h 410. retryAfterMs (also Retry-After) is 300 ms during the first 3 s, then longer; null once final. A failed job carries an error code (invalid_request, body_service_unavailable, deadline_exceeded, fit_failed).
Parameters| Name | In | Type | Description |
|---|
id required | path | string | The job id from POST /v1/body/fit. |
client_id required | query | string | The client's numeric id (or the X-Fmt-Client header). |
tracking_id | query | string | The widget's tracking id: the device the rate limit counts (else client and IP). |
Answer fields (200)| Name | Type | Description |
|---|
id required | string | |
kind required | "fit" | |
state required | "queued" | "running" | "done" | "failed" | "cancelled" | |
createdAt required | string | |
finishedAt required | string | null | |
expiresAt required | string | |
attempts required | integer | |
retryAfterMs required | integer | null | |
modelVersion required | string | null | |
error required | object | null | |
results required | object | null | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Cancel a queued or running body job
Anonymous shopper call from an allowed domain of client_id (D14); another origin, no Origin or an unknown or paused client is refused (403). A queued or running job becomes cancelled and never gets a result, even when its fit ends later. A job already done, failed or cancelled stays as it is, with cancelled: false.
Parameters| Name | In | Type | Description |
|---|
id required | path | string | The job id from POST /v1/body/fit. |
client_id required | query | string | The client's numeric id (or the X-Fmt-Client header). |
tracking_id | query | string | The widget's tracking id: the device the rate limit counts (else client and IP). |
Answer fields (200)| Name | Type | Description |
|---|
id required | string | |
kind required | "fit" | |
state required | "queued" | "running" | "done" | "failed" | "cancelled" | |
createdAt required | string | |
finishedAt required | string | null | |
expiresAt required | string | |
attempts required | integer | |
retryAfterMs required | integer | null | |
modelVersion required | string | null | |
error required | object | null | |
results required | object | null | |
cancelled required | boolean | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
A fitted body: <id>.glb (Y-up metres, joints as nodes), <id>.json (the fit report) or <id>.obj
Anonymous shopper call from an allowed domain of client_id (D14); another origin, no Origin or an unknown or paused client is refused (403). Only for a done job of this client (else 404); 410 after 24 h. The GLB has the feet at y=0 and, with morphs=1, one morph target per morph parameter. The JSON report holds body numbers only. The old .mtl routes are gone (404).
Parameters| Name | In | Type | Description |
|---|
file required | path | string | <job id>.glb, <job id>.json or <job id>.obj. |
client_id required | query | string | The client's numeric id (or the X-Fmt-Client header). |
tracking_id | query | string | The widget's tracking id: the device the rate limit counts (else client and IP). |
morphs | query | "0" | "1" | 1: the GLB carries one morph target per morph parameter (GLB only). |
Answer fields (200)| Name | Type | Description |
|---|
modelVersion required | string | |
bodyType required | "female" | "male" | |
units required | "cm" | |
coordinateSystem required | string | |
requestedCm required | object | |
constrained required | array of string | |
notConstrained required | array of string | |
params required | array of number | |
paramLabels required | array of string | |
measuredCm required | object | |
errorsCm required | object | |
gapsCm | object | |
toleranceCm required | number | |
matched required | boolean | |
message required | string | |
shape required | object | |
cupSize required | string | null | |
cupConvention required | string | null | |
solver required | object | |
vertexCount required | integer | |
triangleCount required | integer | |
restJoints required | array of object | |
paths required | object | |
pathNotes required | object | |
convention required | string | |
limitations required | string | |
population | object | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
The client morph basis GLB: base mesh, 16 positive and 16 negative morphs, bust grid, joints, paths
Without modelVersion: a 302 (cached 60 s) to the current version. With it: the GLB (model/gltf-binary, gzip when accepted), Cache-Control: public, max-age=31536000, immutable; another model version is 404, a bodyType other than female/male or a lod other than 1/2 is 400. The manifest (bounds, target scales, joint deltas, measurement indices) is at asset.extras.fmtMorphBasis (contract: morphBasisManifestSchema). Model data with no shopper data: any origin may read it (D82).
Parameters| Name | In | Type | Description |
|---|
bodyType required | query | "female" | "male" | female or male. |
lod required | query | "1" | "2" | 1 full resolution (Body Studio), 2 decimated (widget). |
modelVersion | query | string | Body model version (anny-0.6.1-fmt-1). Without it the answer is a 302 to the current one. |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
302 | | To the current model version's immutable URL. |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
A garment GLB bound to the body: rest mesh, 41 morph targets in the basis order, binding, fit zones
T14.14.02 (D127–D129). Without modelVersion: a 302 (cached 60 s) to the current version. With it: the GLB (model/gltf-binary, brotli when accepted, else gzip), Cache-Control: public, max-age=31536000, immutable. An unknown template, bodyType, lod (only 1) or look (tee is a top look) is 400 naming the field; another model version is 404 not_found; a template not built yet (shoe, helmet) is 404 garment_not_built; the body-service down is 503, never cached. The manifest is at asset.extras.fmtGarment (contract: garmentManifestSchema). Model data with no shopper data: any origin may read it (D82).
Parameters| Name | In | Type | Description |
|---|
template required | path | "top" | "jacket" | "tights" | "shorts" | "one_piece" | "shoe" | "helmet" | Garment template (top, jacket, …). |
bodyType required | query | "female" | "male" | female or male. |
lod required | query | "1" | Level of detail: only 1 (the full Anny mesh, D92). |
look | query | "base" | "tee" | base (generated, default) or tee (the CC0 T-shirt, top only). |
modelVersion | query | string | Body model version (anny-0.6.1-fmt-1). Without it the answer is a 302 to the current one. |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
302 | | To the current model version's immutable URL. |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
410 | gone | Expired: jobs and results are kept 24 h (gone). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | The body fails validation (validation_failed); error.fields names the fields, never the values. |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Shopify app
Ask FitMyType to set up the Shopify app through collaborator access
The docs site's white-glove form (T12.01.06). Browser calls only from the docs origin. One open ops ticket per shop: a second request adds to it. Limited to 5 requests per hour per IP, 5 per day per shop and 200 per day in total (429 with Retry-After). A bad address or code answers 422 validation_failed with fields and a top-level field_errors map of plain messages. The code is never sent back.
Request body: application/json, text/plain
Request body fields| Name | Type | Description |
|---|
shop_domain required | string | The shop's xxx.myshopify.com address; a pasted admin URL is accepted. |
request_code required | string | Shopify's collaborator request code (Settings → Users → Security). |
Answer fields (201)| Name | Type | Description |
|---|
id required | integer | |
shop_domain required | string | |
status required | "open" | "closed" | |
added_to_existing required | boolean | True when the shop already had an open request: this one was added to it. |
created_at required | string | |
Answers| Status | Error codes | Meaning |
|---|
201 | | Created |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
422 | validation_failed | Invalid shop address or request code (validation_failed, with field_errors). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
The Shopify app's embedded home page (the app's application_url)
HTML page Shopify frames in the admin (T12.01.05). Only https://<shop> and https://admin.shopify.com may frame it (Content-Security-Policy: frame-ancestors); without a valid shop nobody may. It holds no shop data: it loads App Bridge, then the checklist from GET /v1/shopify/home/status.
Parameters| Name | In | Type | Description |
|---|
shop | query | string | |
host | query | string | |
embedded | query | string | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | invalid_api_key invalid_ingest_key | A sent API or ingest key is not valid (invalid_api_key, invalid_ingest_key). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
The app home's five setup checks for the shop of the session token
Store connected, products imported (count, pending validation, order history backfill), size button added (published theme's product template), tracking connected (web pixel), first events received (product, cart, transaction_end in the last 24 h). The shop comes only from the session token: a shop naming another shop answers 404. Shopify's theme and pixel checks run at most once a minute per shop. Accept: text/html answers the checklist as an HTML fragment. A missing or invalid token answers 401 with X-Shopify-Retry-Invalid-Session-Request: 1.
Parameters| Name | In | Type | Description |
|---|
shop | query | string | |
Authorization required | header | string | |
Answer fields (200)| Name | Type | Description |
|---|
shop required | string | |
state required | "installed" | "waiting_for_ops" | "not_connected" | |
steps required | array of object | |
done required | integer | |
total required | 5 | |
missing_scopes required | array of string | |
approve_url required | string | |
dashboard_url required | string | |
support_email required | string | |
checked_at required | string | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | unauthorized | Missing or invalid Shopify session token (unauthorized). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
The app home's "Open dashboard": a one-time back-office sign-in link
With the Shopify session token, Shopify is asked who the admin user is (online token exchange; the token is not kept). When a back-office member of the shop's client has that verified email (not staff, no two-factor sign-in), the answer is {kind: sso, url} with a code in the URL fragment, valid 60 s and usable once (POST /bo/v1/auth/shopify-sso); otherwise {kind: login, url} (the sign-in page).
Parameters| Name | In | Type | Description |
|---|
Authorization required | header | string | |
Answer fields (200)| Name | Type | Description |
|---|
kind required | "sso" | "login" | |
url required | string | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | unauthorized | Missing or invalid Shopify session token (unauthorized). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
The Shopify app opens: install on the first open, then the shop's state
Called by the embedded app home with Authorization: Bearer <Shopify session token> (T12.01.01). The first open exchanges the session token for an expiring offline token (sealed, never returned), links the shop to the one client that has its domain, writes the fitmytype_app.client_id metafield, connects the web pixel and starts the first import. A shop no client has, or two clients have, waits for ops (waiting_for_ops). A missing or invalid token answers 401 with X-Shopify-Retry-Invalid-Session-Request: 1; 503 when the app is not set up on this server.
Parameters| Name | In | Type | Description |
|---|
Authorization required | header | string | |
Answer fields (200)| Name | Type | Description |
|---|
shop required | string | |
state required | "installed" | "waiting_for_ops" | "uninstalled" | "redacted" | |
client_id required | integer | null | |
source_id required | integer | null | |
message required | string | null | What the merchant reads while ops links the shop; null otherwise. |
missing_scopes required | array of string | |
metafield required | object | null | |
pixel required | object | null | |
import required | object | null | |
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | unauthorized | Missing or invalid Shopify session token (unauthorized). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |
Shopify's webhooks to the app (uninstall, scope changes, GDPR)
Only Shopify calls this. X-Shopify-Hmac-SHA256 (HMAC-SHA256 of the raw body, keyed by the app's credentials) is checked before the body is read: wrong or missing answers 401 with an empty body; a valid one answers 200 with an empty body (never the D38 error format). Topics: app/uninstalled, app/scopes_update, customers/data_request, customers/redact, shop/redact; repeats are dropped by X-Shopify-Webhook-Id.
Request body: application/json, text/plain
Answers| Status | Error codes | Meaning |
|---|
200 | | OK |
400 | bad_request invalid_json invalid_client_id | Malformed request or client_id (bad_request, invalid_json, invalid_client_id). |
401 | | Wrong or missing HMAC signature (empty body). |
403 | not_whitelisted | Preflight from an origin the client does not allow (not_whitelisted), or a key without the scope or of another client. |
404 | not_found brand_not_found | Unknown resource (not_found, brand_not_found). |
413 | payload_too_large | Body over the route's limit (payload_too_large). |
429 | rate_limited | Rate limited (rate_limited); wait Retry-After seconds. (header Retry-After) |
503 | capacity_busy rate_limit_unavailable | Busy or a dependency is down (capacity_busy, rate_limit_unavailable, …); retry after Retry-After. (header Retry-After) |