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
Request
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
| Field | Description |
|---|---|
| brand_idstring | Your brand's record id (same as brand_id from /v1/brands). |
| brand_namestring | Your brand's display name. |
| product_countinteger | Total products on file (active + inactive). |
| usable_countinteger | Number of products that can currently be used in ad generation. usable_for_ads=true requires both Active status and a hero image. |
| products[].idstring | Stable record id, e.g. "rec0aBcD1234". Use this OR handle OR name in product_refs. |
| products[].handlestring | URL slug from your site, e.g. "jet-lag-mask". Most readable form to use in code. |
| products[].namestring | Product display name, e.g. "Jet Lag Mask". |
| products[].statusstring | Active, Inactive, Draft, etc. Only Active products can be used in generation. |
| products[].usable_for_adsboolean | Convenience flag — true if status is Active, a hero image exists, AND the product type is supported by our renderer. |
| products[].is_compatibleboolean | Whether 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_reasonstring | Human-readable reason when is_compatible is false. Null otherwise. |
| products[].categorystring | Product 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_urlstring | Public 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 shape | Example | Notes |
|---|---|---|
| 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.