Endpoints
Generate ads
The main generation endpoint. Submit a creative brief and Dessert returns a
delivery_id within milliseconds. The actual ads — copy,
rendered statics, animated MP4s — come back through the status endpoint or
your webhook a few minutes later.
Every request creates one delivery containing
count ad concepts. Each concept is rendered in two aspect ratios
(vertical 1080×1920 and square 1080×1350), so a request with
count: 3 produces six rendered files.
Headers
| Header | Description |
|---|---|
| Authorizationrequired | Bearer dsrt_live_… |
| Content-Typerequired | application/json |
| Idempotency-Keyoptional | A unique string (UUID recommended) that lets you retry safely. We cache the response for 24h and replay it on duplicate keys instead of generating new ads. |
Request body
| Field | Description |
|---|---|
| countintegeroptional | Number of ad concepts to generate. Defaults to 1. Each concept produces one vertical + one square render. |
| formatstringoptional |
"full" (default) — static + animated video, 15 credits per ad."static" — static image only, 5 credits per ad.
|
| brand_idstringoptional | The brand to generate against. Omit and we use your account's default brand. Get IDs from GET /v1/brands. |
| product_idsstring[]optional | Restrict generation to specific products. If omitted, Dessert rotates across all active products on the brand. |
| creativeobjectoptional | Creative direction overrides. See creative object below. |
| in_situbooleanoptional | If true, the product is composited into a lifestyle scene via Gemini before the ad is rendered. Defaults to false. |
| webhook_urlstringoptional | HTTPS URL that we POST to when the delivery completes. See Webhooks. |
The creative object
| Field | Description |
|---|---|
| copy_anglestringoptional |
Forces the headline + body angle. One of:
"pain + solution", "benefit outcome", "sensory",
"transformation", "ritual", "social proof".
Omit to let Dessert rotate.
|
| brand_tonestringoptional | Free-form tone override for this request (e.g. "clinical authority", "playful modern"). Falls back to the brand default. |
| animation_promptsstring[]optional | Pin the animation styles to a specific subset. Pass one or more of the 36 production Animation Prompt categories — see the styles reference for the full list. |
| style_searchstringoptional |
Natural-language description of the look you want
(e.g. "warm honey drip on amber, minimal, slow"). Used only
when animation_prompts is empty. Behind the scenes we run an
embedding search and pin the top matches as featured styles.
|
Two ways to constrain style
animation_prompts is categorical and exact — use it when you know
the named prompt (e.g. "Serum Droplets").
style_search is semantic — use it when you want to describe a vibe.
If you set both, animation_prompts wins.
Request examples
A minimal call using only your defaults:
curl
Python
TypeScript
MCP
curl https://api.dessert.dev/v1/ads/generate \
-H "Authorization: Bearer $DESSERT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"count": 3,
"format": "full",
"creative": {
"copy_angle": "sensory",
"brand_tone": "clinical authority",
"animation_prompts": ["Serum Droplets", "Lotion Texture"]
},
"webhook_url": "https://yourapp.com/dessert-callback"
}'
import os, requests
r = requests.post(
"https://api.dessert.dev/v1/ads/generate",
headers={
"Authorization": f"Bearer {os.environ['DESSERT_API_KEY']}",
"Idempotency-Key": "campaign-q2-2026-batch-01",
},
json={
"count": 3,
"format": "full",
"creative": {
"copy_angle": "sensory",
"brand_tone": "clinical authority",
"animation_prompts": ["Serum Droplets", "Lotion Texture"],
},
"webhook_url": "https://yourapp.com/dessert-callback",
},
timeout=30,
)
r.raise_for_status()
delivery = r.json()
const res = await fetch("https://api.dessert.dev/v1/ads/generate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.DESSERT_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "campaign-q2-2026-batch-01",
},
body: JSON.stringify({
count: 3,
format: "full",
creative: {
copy_angle: "sensory",
brand_tone: "clinical authority",
animation_prompts: ["Serum Droplets", "Lotion Texture"],
},
webhook_url: "https://yourapp.com/dessert-callback",
}),
});
const delivery = await res.json();
{
"tool": "dessert.generate_ads",
"arguments": {
"count": 3,
"format": "full",
"creative": {
"copy_angle": "sensory",
"brand_tone": "clinical authority",
"animation_prompts": ["Serum Droplets", "Lotion Texture"]
},
"webhook_url": "https://yourapp.com/dessert-callback"
}
}
Response — 202 Accepted
| Field | Description |
|---|---|
| delivery_idstring | Opaque identifier for this delivery. Pass to GET /v1/deliveries/:id. |
| statusstring | Always "queued" on first response. |
| request_idstring | Tracing ID for support — include in any bug reports. |
| ad_count_expectedinteger | How many concepts will be generated (= your count). |
| formatstring | Echo of the format you requested. |
| credits_chargedinteger | Credits deducted from your balance for this delivery. |
| credits_balance_afterinteger | Remaining credit balance after deduction. |
| status_urlstring | Full URL to poll for delivery progress. |
| eta_secondsinteger | Rough completion estimate (180s for static, 480s for full). |
| replayed_from_idempotency_keyboolean | Present and true only when an Idempotency-Key matched a prior request. |
{
"delivery_id": "recXXXXXXXXXXXXXX",
"status": "queued",
"request_id": "req_a1b2c3d4e5f6789012345abc",
"ad_count_expected": 3,
"format": "full",
"credits_charged": 45,
"credits_balance_after": 205,
"status_url": "https://api.dessert.dev/v1/deliveries/recXXXXXXXXXXXXXX",
"eta_seconds": 480
}
Errors
| Status | Code | Cause |
|---|---|---|
| 400 | invalid_count | count was not a positive integer. |
| 400 | invalid_format | format was not "full" or "static". |
| 400 | no_brand_selected | Account has no default brand and no brand_id was provided. Create one with POST /v1/brands. |
| 402 | insufficient_credits | Not enough credits and auto-recharge is off or failed. Top up. |
| 404 | brand_not_found | The brand_id does not belong to this account. |
| 425 | brand_onboarding_in_progress | Brand scrape is still running. Try again in 15–60 minutes. |
| 502 | orchestration_failed | Upstream orchestrator was unreachable. Safe to retry (with an Idempotency-Key). |
See the Errors reference for the full envelope.