Endpoints

Products

List the products on your brand's catalog. Returns each product's id, handle, name, image, and aspect ratio — everything your code (or your LLM) needs to decide which products to feature in a generate call.

Products are scraped from your website during brand onboarding. If a product doesn't appear here, either onboarding hasn't completed yet (poll GET /v1/brands) or the product was marked inactive during human review.


List products

GET https://api.dessert.dev/v1/products Try it →

Request

curl
Python
TypeScript
MCP
curl https://api.dessert.dev/v1/products \
  -H "Authorization: Bearer $DESSERT_API_KEY"
requests.get(
    "https://api.dessert.dev/v1/products",
    headers={"Authorization": f"Bearer {os.environ['DESSERT_API_KEY']}"},
).json()
await fetch("https://api.dessert.dev/v1/products", {
  headers: { Authorization: `Bearer ${process.env.DESSERT_API_KEY}` },
}).then(r => r.json());
{ "tool": "dessert.list_products", "arguments": {} }

Response

FieldDescription
brand_idstringYour brand's record id (same as brand_id from /v1/brands).
brand_namestringYour brand's display name.
product_countintegerTotal products on file (active + inactive).
usable_countintegerNumber of products that can currently be used in ad generation. usable_for_ads=true requires both Active status and a hero image.
products[].idstringStable record id, e.g. "rec0aBcD1234". Use this OR handle OR name in product_refs.
products[].handlestringURL slug from your site, e.g. "jet-lag-mask". Most readable form to use in code.
products[].namestringProduct display name, e.g. "Jet Lag Mask".
products[].statusstringActive, Inactive, Draft, etc. Only Active products can be used in generation.
products[].usable_for_adsbooleanConvenience flag — true if status is Active, a hero image exists, AND the product type is supported by our renderer.
products[].is_compatiblebooleanWhether this product type is supported. See Supported products. Always true for single creams, serums, balms, oils, gels, lip products, mists, and sunscreens; false for multi-packs, wipes, accessories, bar soap, hair, fragrance.
products[].incompatibility_reasonstringHuman-readable reason when is_compatible is false. Null otherwise.
products[].categorystringProduct category (Serum, Mask, Cleanser, etc.).
products[].aspect_ratiostring"vertical" or "square" — measured from the actual product bbox inside the hero image, not the canvas dims.
products[].image_urlstringPublic URL to the hero image used in ads (transparent background, ready to composite).
{
  "brand_id": "rec0FM6NeZwryyUb5",
  "brand_name": "Summer Fridays",
  "product_count": 50,
  "usable_count": 47,
  "products": [
    {
      "id": "recf8DevtHcUhFuWW",
      "handle": "jet-lag-mask",
      "name": "Jet Lag Mask",
      "status": "Active",
      "usable_for_ads": true,
      "category": "Mask",
      "aspect_ratio": "vertical",
      "image_url": "https://storage.googleapis.com/molly-468114-assets/.../jet-lag-mask.png"
    },
    {
      "id": "receBqZAyGzy2tmvd",
      "handle": "cc-me-serum",
      "name": "CC Me Serum with Vitamin C + Niacinamide",
      "status": "Active",
      "usable_for_ads": true,
      "category": "Serum",
      "aspect_ratio": "vertical",
      "image_url": "https://storage.googleapis.com/molly-468114-assets/.../cc-me-serum.png"
    }
  ]
}

Referencing products in generate

Once you have the product list, pass any mix of these to product_refs on POST /v1/ads/generate:

Ref shapeExampleNotes
Record idstring"rec0aBcD1234"Most precise. Stable across product renames.
Handlestring"jet-lag-mask"Most readable. Stable as long as the product URL doesn't change.
Name substringstring"jet lag"Case-insensitive substring match. Matches first hit if multiple products share the substring.

Refs that don't match any product on your brand are silently dropped (the rest of the request still runs). Pass an empty product_refs (or omit it) to let Dessert auto-select products by lowest usage.

curl -X POST https://api.dessert.dev/v1/ads/generate \
  -H "Authorization: Bearer $DESSERT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Prefer: wait" \
  -d '{
    "count": 2,
    "format": "full",
    "product_refs": ["jet-lag-mask", "cc-me-serum"],
    "creative": { "copy_angle": "sensory", "brand_tone": "playful modern" }
  }'

What's NOT exposed

The products endpoint intentionally omits a few fields:

  • The full Product Page Content we scraped — verbose; you already own this on your own site.
  • Raw reviews — high-volume, also your own data.
  • Internal scoring (usage history, match tags) — these inform our selectors and aren't customer-facing.

If you have a use case for any of these (e.g., your LLM wants ingredient context to write more grounded copy), reach out to support@dessert.dev — we can ship a scoped enrichment flag.