Edge Recommendation API Reference

Real-time product-recommendation endpoints served at the CDN edge. Most are CloudFront Functions that synthesize a JSON response from a KeyValueStore on every viewer request (no origin fetch); two are DMP-backed Lambdas fronted by a caching distribution. All are GET, anonymous, and CORS-open (Access-Control-Allow-Origin: *).

Every endpoint also answers OPTIONS with 204 for CORS preflight. Endpoints marked ?render=html can return a human-readable HTML card view instead of JSON.

CloudFront Functionshttps://addon-reco.cscbandit.com
DMP Lambdas (cdn stack)https://reco-cdn.cscbandit.com
GET /{brand}/v1/

Walk-mode add-on pick

Single best add-on for a cart, found by walking a slugified category hierarchy leaf→root until a co-purchase list matches. `{brand}` is a registered brand prefix (e.g. `bc` = Backcountry).

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
sku repeatable required string Cart item styleids. URL-decoded (`+` = space). First value is the anchor.
category repeatable required string Category hierarchy, root→leaf, one repeated param per level. Walked leaf→root for the first match.

Example request

GET https://addon-reco.cscbandit.com/bc/v1/?sku=XYZ789&category=Bikes&category=Road%20Bikes

Response (application/json; charset=utf-8)

{
  "recommendation": {
    "addon": {
      "styleid": "ABC123",
      "stylename": "Trail Jacket",
      "brandname": "Acme",
      "style_color": "ABC123-RED",
      "picked_sku": "ABC123-RED-M",
      "current_price": 49.99,
      "on_hand": 5,
      "styleid_on_hand_total": 12,
      "lift": 2.5,
      "n_cooccur": 100,
      "n_addon_orders": 45
    },
    "anchorStyleid": "XYZ789",
    "anchorCategoryName": "Road Bikes",
    "anchorSlug": "road-bikes",
    "matchLevel": "leaf"
  },
  "brand": "bc"
}

Errors

StatusWhen
400Missing `sku` or `category`.
404Unknown brand prefix (not in brands.ts).
405Method other than GET/OPTIONS.
500No data source configured for the brand.
GET /{brand}/v1/cat/{slug}

Path-mode add-on pick

Same pick as walk-mode but the anchor category is taken from the URL path (`{slug}`) instead of repeated `category=` params. Used by CC (`/cc/v1/cat/`).

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
sku repeatable required string Cart item styleids. At least one required.

Example request

GET https://addon-reco.cscbandit.com/cc/v1/cat/road-shoes?sku=ABC123

Response (application/json; charset=utf-8)

{
  "recommendation": { "addon": { "styleid": "ABC123", "picked_sku": "ABC123-RED-M", "current_price": 49.99, "...": "see walk-mode" }, "matchLevel": "leaf" },
  "brand": "cc"
}

Errors

StatusWhen
400Missing `sku`.
404Unknown brand prefix.
405Method other than GET/OPTIONS.
GET /cc/v2/ ?render=html

Persona fast-path add-on

Single add-on chosen by a persona model (cart slugs/brands/discipline → cluster → predicate pick), falling back to the v1 co-purchase pick on a miss.

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
sku repeatable required string Cart item styleids.
slug repeatable required string Union of cart item slug ancestors.
brand repeatable optional string Distinct brands on cart items.
discipline repeatable optional string Top-level discipline tokens (e.g. 'road', 'mtb', 'gravel').
priceTier optional 'low'|'mid'|'premium'|'flagship' default: low Price tier of the cart.
cartSize optional integer default: sku count Distinct styleids in cart.
render optional 'html' Return a self-contained HTML card view instead of JSON.

Example request

GET https://addon-reco.cscbandit.com/cc/v2/?sku=ABC123&slug=road-shoes&priceTier=premium

Response (application/json; charset=utf-8)

{
  "recommendation": {
    "addon": {
      "rank": 1,
      "styleid": "ABC123",
      "picked_sku": "ABC123-RED-M",
      "style_color": "ABC123-RED",
      "current_price": 79.99,
      "on_hand": 10,
      "source": "persona"
    },
    "anchorStyleid": "XYZ789",
    "pick_via": "predicate",
    "cluster_id": 42,
    "persona_label": "Weekend Roadie"
  },
  "brand": "cc"
}

Errors

StatusWhen
400Missing `sku` or `slug`.
404Unknown path.
405Method other than GET/OPTIONS.
GET /cc/v2/cart-filler/ ?render=html

Free-shipping cart filler

Picks one add-on priced inside a band above the current cart total — to nudge the shopper over a free-shipping / promo threshold. Band = [gap, gap × ceiling).

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
sku repeatable required string Cart item styleids.
slug repeatable required string Cart item slug ancestors.
gap required number Dollars remaining to the threshold. Must be finite and > 0, else no pick.
ceiling optional number default: 2 Upper band multiplier. Clamped to (1, 10].
brand repeatable optional string Distinct brands on cart items.
discipline repeatable optional string Top-level discipline tokens.
priceTier optional 'low'|'mid'|'premium'|'flagship' default: low Price tier of the cart.
cartSize optional integer default: sku count Distinct styleids in cart.
render optional 'html' HTML card view instead of JSON.

Example request

GET https://addon-reco.cscbandit.com/cc/v2/cart-filler/?sku=ABC123&slug=road-shoes&gap=25&ceiling=2

Response (application/json; charset=utf-8)

{
  "pick": {
    "styleid": "ABC123",
    "picked_sku": "ABC123-RED-M",
    "stylename": "Ride Socks",
    "brandname": "Acme",
    "current_price": 32.00,
    "on_hand": 3,
    "lift": 1.8,
    "style_color": "ABC123-RED"
  },
  "pick_via": "persona",
  "cluster_id": 42,
  "persona_label": "Weekend Roadie",
  "gap": 25.00,
  "band": { "min": 25.00, "max": 50.00 },
  "brand": "cc"
}

Errors

StatusWhen
400Missing required params or invalid `gap`.
404Unknown path.
405Method other than GET/OPTIONS.
GET /cc/v3/ ?render=html

Ranked add-on list

Returns an ordered array of add-ons (persona predicate picks first, then slug lookups, then v1 fallback), deduped against the cart.

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
sku repeatable required string Cart item styleids.
slug repeatable required string Cart item slug ancestors.
max optional integer default: 5 Max recommendations. Hard cap 10.
brand repeatable optional string Distinct brands.
discipline repeatable optional string Top-level discipline tokens.
priceTier optional 'low'|'mid'|'premium'|'flagship' default: low Price tier of the cart.
cartSize optional integer default: sku count Distinct styleids in cart.
render optional 'html' HTML card view instead of JSON.

Example request

GET https://addon-reco.cscbandit.com/cc/v3/?sku=ABC123&slug=road-shoes&max=10

Response (application/json; charset=utf-8)

{
  "recommendations": [
    {
      "addon": { "rank": 1, "styleid": "ABC123", "picked_sku": "ABC123-RED-M", "current_price": 89.99, "source": "persona" },
      "pick_via": "predicate",
      "cluster_id": 42,
      "persona_label": "Weekend Roadie"
    }
  ],
  "anchorStyleid": "XYZ789",
  "max": 5,
  "brand": "cc"
}

Errors

StatusWhen
400Missing `sku` or `slug`.
404Unknown path.
405Method other than GET/OPTIONS.
GET /cc/ctl/v1/{styleid} ?render=html

Complete-the-look outfit

Precomputed "complete the look" outfit for a PDP styleid: the main item plus matching items (by type/category) with candidate products each.

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
render optional 'html' HTML view instead of JSON.

Example request

GET https://addon-reco.cscbandit.com/cc/ctl/v1/ABC123?render=html

Response (application/json; charset=utf-8)

{
  "styleid": "ABC123",
  "status": "ok",
  "basis": "on-model",
  "main": { "styleid": "ABC123", "name": "Race Jersey", "brand": "Acme", "color": "Red", "category": "Jerseys", "gender": "Men's" },
  "detection_images": ["https://content.competitivecyclist.com/..."],
  "items": [
    {
      "type": "Shorts", "category": "Bottoms", "color": "Black", "gender": "Men's", "match_count": 3,
      "products": [
        { "uniqueId": "prod123", "brand": "Acme", "title": "Bib Shorts", "price": 159.99, "productUrl": "https://...", "imageUrl": "https://..." }
      ]
    }
  ]
}

Errors

StatusWhen
404No look computed for the styleid.
405Method other than GET/OPTIONS.
GET /cc/oh/v1/ ?render=html

Cross-order recommendations

Blends two cross-order signals: replenishment picks for owned consumables that are "due", then next-purchase styles bought after owning the cart's categories. Replenish picks lead.

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
sku repeatable required string Owned styleids from recent orders.
slug repeatable required string Owned category slugs (seo_url_token).
daysSince repeatable optional integer Days since purchase, aligned positionally with `sku`. Without it an item is never "due".
max optional integer default: 5 Total recommendation cap. Hard cap 10.
maxReplenish optional integer default: 2 Cap on replenish picks placed first.
render optional 'html' HTML view instead of JSON.

Example request

GET https://addon-reco.cscbandit.com/cc/oh/v1/?sku=ABC123&sku=DEF456&slug=road-shoes&daysSince=45&daysSince=30&max=5

Response (application/json; charset=utf-8)

{
  "recommendations": [
    { "styleid": "ABC123", "rep_sku": "ABC123-RED-M", "current_price": 19.99, "tier": "replenish", "due_ratio": 1.5, "group": "chamois-cream" },
    { "styleid": "DEF456", "rep_sku": "DEF456-BLK-L", "current_price": 129.99, "tier": "next-purchase", "lift": 2.3 }
  ],
  "anchorStyleid": "ABC123",
  "max": 5,
  "brand": "cc"
}

Errors

StatusWhen
400Missing `sku` or `slug`.
404Unknown path.
405Method other than GET/OPTIONS.
GET /cc/{buyer-selected|alan-selected}/v1/ ?render=html

Static curated pick map

Two hand-curated category→sku maps baked into the bundle (no KVS, no I/O), backing the CC mini-cart "People Also Bought" A/B test: `buyer-selected` (merch picks, Variant 1) and `alan-selected` (Variant 0). With `slug`, returns one pick; without, dumps the whole map.

Base https://addon-reco.cscbandit.com   Cache-Control no-store

Query parameters

ParamTypeNotes
slug optional string Category slug to look up. Omit to dump the full map.
render optional 'html' Debug table view instead of JSON.

Example request

GET https://addon-reco.cscbandit.com/cc/buyer-selected/v1/?slug=road-shoes

Response (application/json; charset=utf-8)

// with ?slug=road-shoes
{ "variant": "buyer-selected", "slug": "road-shoes", "picked_sku": "ABC123" }

// without slug
{ "variant": "buyer-selected", "count": 124, "map": { "road-shoes": "ABC123", "mtb-pedals": "XYZ789" } }

Errors

StatusWhen
404Unknown path.
405Method other than GET/OPTIONS.
GET /bc/v1/addons ?render=html

Add-on assignments

Live add-on assignments from the DMP (`sku_bc_addon_assignments`) for one or more parent SKUs. Edge-cached on the `cdn/` distribution; each cache miss is a signed SQL round-trip to DMP.

Base https://reco-cdn.cscbandit.com   Cache-Control public, max-age=300 (success); no-store (errors)

Query parameters

ParamTypeNotes
sku repeatable required string Parent SKUs. De-duped, order preserved. Max 50; blanks ignored.
account optional string default: bc DMP account id.
render optional 'html' HTML view instead of JSON.

Example request

GET https://reco-cdn.cscbandit.com/bc/v1/addons?sku=SKU1&sku=SKU2&account=bc

Response (application/json; charset=utf-8)

{
  "account": "bc",
  "requested": ["SKU1", "SKU2"],
  "results": [
    { "parentSku": "SKU1", "addons": [ { "addOnSku": "ADDON-SKU", "slot": 1, "source": "affinity-model", "createdAt": "2025-01-15T10:00:00Z" } ] }
  ],
  "count": 3,
  "timing_ms": { "total": 145.5 }
}

Errors

StatusWhen
400No `sku`, or more than 50 SKUs.
405Method other than GET/OPTIONS.
502DMP query failed.
GET /bc/v1/complements ?render=html

Knowledge-graph complements

Complementary products from the DMP product knowledge graph (edge or projection source) for one or more source styleids. Edge-cached on the `cdn/` distribution.

Base https://reco-cdn.cscbandit.com   Cache-Control public, max-age=300 (success); no-store (errors)

Query parameters

ParamTypeNotes
sku repeatable required string Source styleids. De-duped, order preserved. Max 50; blanks ignored.
brand optional string default: bc Brand code for node-id hashing.
limit optional integer default: 10 Complements per SKU. Clamped to [1, 50].
render optional 'html' HTML view instead of JSON.

Example request

GET https://reco-cdn.cscbandit.com/bc/v1/complements?sku=SKU1&sku=SKU2&brand=bc&limit=10

Response (application/json; charset=utf-8)

{
  "brand": "bc",
  "requested": ["SKU1"],
  "limit": 10,
  "results": [
    {
      "sku": "SKU1",
      "source": "edges",
      "complements": [
        { "weight": 0.85, "confidence": 0.92, "sharedActivities": ["road-cycling"], "source": "edges", "detail": { "styleid": "COMP-SKU", "name": "..." } }
      ]
    }
  ],
  "count": 15,
  "timing_ms": { "total": 234.2 }
}

Errors

StatusWhen
400No `sku`, or more than 50 SKUs.
405Method other than GET/OPTIONS.
502DMP query failed.