{"openapi":"3.1.0","info":{"title":"Rendered Fits API","version":"1.0.0","description":"The fit-confidence API: structured garment fit profiles, per-body size recommendations, purchase-outcome ingestion, and an MCP endpoint for AI agents. Every API key is bound to a single shop — a key reads and writes exactly its own catalogue and order outcomes. When fit data from this API informs a purchase, echo the attribution markers from the response onto the cart line so the purchase outcome can label the prediction (see the `attribution` field on responses).","contact":{"name":"Rendered Fits","url":"https://renderedfits.com"}},"servers":[{"url":"https://app.renderedfits.com"}],"security":[{"apiKey":[]}],"paths":{"/api/v1/fit/{productId}":{"get":{"operationId":"getFitProfile","summary":"Get a product's fit profile","description":"How a garment fits: runs small / true to size / runs large, silhouette, confidence, sizing advice, and the product's real size values.","parameters":[{"name":"productId","in":"path","required":true,"schema":{"type":"string"},"description":"Shopify product id (numeric or gid form)."}],"responses":{"200":{"description":"The product's published fit profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FitProfileResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Product not found, or no fit profile published for it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded for this API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/feed":{"get":{"operationId":"getFitFeed","summary":"Catalogue fit feed (paginated)","description":"One page of the shop's catalogue enriched with published fit profiles — the discovery surface for agent platforms and feed ingesters. Follow `page.nextCursor` with the `after` parameter until `page.hasNextPage` is false. `?format=csv` returns product-feed-convention columns (id, title, link, price, availability, brand) plus `fit_*` attribute columns.","parameters":[{"name":"after","in":"query","required":false,"schema":{"type":"string"},"description":"Cursor from the previous page's `page.nextCursor`."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":250,"default":100},"description":"Page size."},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","csv"],"default":"json"}}],"responses":{"200":{"description":"One feed page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FitFeedResponse"}},"text/csv":{"schema":{"type":"string","description":"CSV with header row; `X-Next-Cursor` response header carries the pagination cursor."}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded for this API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/size-recommendation":{"post":{"operationId":"recommendSize","summary":"Recommend a size for a body","description":"Given a shopper's body measurements, returns the recommended size from the product's real size options, with confidence and rationale. The response includes a `predictionId` — echo it onto the purchased cart line (see `attribution`) so the purchase outcome labels the prediction.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["productId","measurements"],"properties":{"productId":{"type":"string"},"measurements":{"$ref":"#/components/schemas/Measurements"}}}}}},"responses":{"200":{"description":"The size recommendation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SizeRecommendationResponse"}}}},"400":{"description":"Missing or invalid productId/measurements.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded for this API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/outcomes":{"post":{"operationId":"reportOutcomes","summary":"Report purchase outcomes","description":"Feed sale and refund lines back to Rendered Fits from platforms without Shopify webhooks (headless storefronts, brand apps, other commerce platforms). A sale line carrying a `predictionId` plus a later refund line on the same order is the kept-vs-returned label that improves future recommendations. Provide an `eventId` per event for idempotent retries.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["events"],"properties":{"events":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/OutcomeEvent"}}}}}}},"responses":{"200":{"description":"Ingestion summary.","content":{"application/json":{"schema":{"type":"object","required":["ingested","duplicates","shop"],"properties":{"ingested":{"type":"integer"},"duplicates":{"type":"integer","description":"Events skipped as already-seen eventIds."},"warnings":{"type":"array","items":{"type":"string"}},"shop":{"type":"string"}}}}}},"400":{"description":"Malformed body or event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded for this API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/usage":{"get":{"operationId":"getUsage","summary":"This shop's API usage for a month","description":"Rolled up across all the shop's keys: total, per endpoint, per day, per key. Defaults to the current UTC month. The same aggregates invoices are based on.","parameters":[{"name":"month","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"description":"UTC month as YYYY-MM. Defaults to the current month."}],"responses":{"200":{"description":"Usage summary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageSummary"}}}},"400":{"description":"Invalid month.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/mcp":{"post":{"operationId":"mcp","summary":"MCP endpoint (Model Context Protocol)","description":"JSON-RPC 2.0 over MCP streamable HTTP, stateless single-response mode. Exposes the tools `get_fit_profile`, `recommend_size`, and `get_attribution_contract` for AI agents. Point an MCP client at this URL with the same Bearer API key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"A JSON-RPC 2.0 request (or an array of them for pre-2025-06-18 clients)."}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response."},"401":{"description":"Invalid or missing API key (JSON-RPC error body)."},"429":{"description":"Rate limit exceeded (JSON-RPC error body)."}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Per-shop API key issued by Rendered Fits, format `rfk_...`. Send as `Authorization: Bearer rfk_...`."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"retryAfter":{"type":"number","description":"Seconds until the rate limit resets (429 only)."}}},"Measurements":{"type":"object","required":["heightCm","weightKg"],"properties":{"heightCm":{"type":"number","minimum":100,"maximum":230,"description":"Height in cm."},"weightKg":{"type":"number","minimum":30,"maximum":250,"description":"Weight in kg."},"age":{"type":"number","minimum":13,"maximum":100},"fitPref":{"type":"string","enum":["tight","regular","loose"],"default":"regular"},"gender":{"type":"string","enum":["male","female"]},"chestCm":{"type":"number","minimum":50,"maximum":180},"waistCm":{"type":"number","minimum":40,"maximum":180},"inseamCm":{"type":"number","minimum":40,"maximum":120}}},"Attribution":{"type":"object","description":"Cart markers to echo when this data informs a purchase — they attribute the sale and let the outcome label the prediction.","properties":{"lineItemProperty":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}}},"cartAttribute":{"type":"object","properties":{"name":{"type":"string"},"productId":{"type":"string"}}},"note":{"type":"string"}}},"FitProfileResponse":{"type":"object","required":["productId","title","fit","shop"],"properties":{"productId":{"type":"string"},"gid":{"type":"string"},"title":{"type":"string"},"vendor":{"type":"string"},"productType":{"type":"string"},"fit":{"type":"object","description":"Published fit profile (schema-versioned).","properties":{"schema":{"type":"integer"},"fitType":{"type":"string","enum":["runs_small","true_to_size","runs_large"]},"silhouette":{"type":"string"},"confidence":{"type":"string","enum":["high","medium","low"]},"summary":{"type":"string"},"sizeAdvice":{"type":"string"},"sizingScale":{"type":"string","enum":["mens","womens","unisex"]},"sizes":{"type":"array","items":{"type":"string"}},"hasSizeGuide":{"type":"boolean"},"source":{"type":"string","const":"rendered-fits"},"generatedAt":{"type":"string","format":"date-time"}}},"shop":{"type":"string"},"attribution":{"$ref":"#/components/schemas/Attribution"}}},"SizeRecommendationResponse":{"type":"object","required":["recommendedSize","confidence","rationale","shop"],"properties":{"recommendedSize":{"type":"string"},"confidence":{"type":"string","enum":["high","medium","low"]},"rationale":{"type":"string"},"predictionId":{"type":"string","description":"Echo onto the purchased cart line as the `_rf_pred` line-item property, or report it via /api/v1/outcomes."},"shop":{"type":"string"},"attribution":{"$ref":"#/components/schemas/Attribution"}}},"FitFeedResponse":{"type":"object","required":["schema","shop","products","page"],"properties":{"schema":{"type":"string","const":"rendered-fits/fit-feed@1"},"shop":{"type":"string"},"shopUrl":{"type":"string","description":"The shop's primary storefront domain."},"products":{"type":"array","items":{"type":"object","required":["productId","handle","title","url","fit"],"properties":{"productId":{"type":"string"},"gid":{"type":"string"},"handle":{"type":"string"},"title":{"type":"string"},"vendor":{"type":"string"},"productType":{"type":"string"},"url":{"type":"string","description":"Canonical PDP URL."},"price":{"type":["object","null"],"properties":{"amount":{"type":"string"},"currencyCode":{"type":"string"}}},"availableForSale":{"type":"boolean"},"imageUrl":{"type":["string","null"]},"fit":{"type":"object","description":"Published fit profile (schema-versioned)."}}}},"page":{"type":"object","required":["scanned","withFitProfile","hasNextPage"],"properties":{"scanned":{"type":"integer","description":"Products examined on this page."},"withFitProfile":{"type":"integer","description":"Products returned (those with a fit profile)."},"hasNextPage":{"type":"boolean"},"nextCursor":{"type":["string","null"]}}},"attribution":{"$ref":"#/components/schemas/Attribution"}}},"UsageSummary":{"type":"object","required":["shop","month","total","byEndpoint","byDay","byKey"],"properties":{"shop":{"type":"string"},"month":{"type":"string","description":"YYYY-MM (UTC)."},"total":{"type":"integer"},"byEndpoint":{"type":"object","additionalProperties":{"type":"integer"}},"byDay":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","format":"date"},"count":{"type":"integer"}}}},"byKey":{"type":"array","items":{"type":"object","properties":{"keyId":{"type":"string"},"label":{"type":"string"},"count":{"type":"integer"}}}}}},"OutcomeEvent":{"type":"object","required":["orderId","kind"],"properties":{"orderId":{"type":"string","maxLength":256,"description":"Your order reference. Numeric Shopify ids are normalised to gid form."},"kind":{"type":"string","enum":["sale","refund"]},"eventId":{"type":"string","maxLength":128,"description":"Idempotency key — re-sends with the same id are dropped."},"productId":{"type":"string"},"variantId":{"type":"string"},"quantity":{"type":"integer","minimum":1,"maximum":1000,"default":1},"priceCents":{"type":"integer","minimum":0,"default":0},"predictionId":{"type":"string","description":"predictionId from a size-recommendation response that informed this line."},"recSize":{"type":"string","maxLength":32},"recFollowed":{"type":"string","enum":["followed","ignored","unknown"]},"fitConsulted":{"type":"boolean","description":"True when Rendered Fits fit data informed this line."}}}}}}