{"schemaVersion":"1.0.0","kind":"brandiseer.use-case","id":"ad-creative-refresh","canonicalUrl":"https://blog.brandiseer.com/use-cases/ad-creative-refresh","contentHash":"8b21f22c23846f9b1a262c9c01b56cd5600f5a567079712ad4bc00b40a0feea3","metadata":{"title":"Ad creative refresh","outcome":"When paid social or display ads go stale (ad fatigue: frequency up, CTR down), an agent briefs and generates fresh on-brand ad image variants with Brandiseer for each placement (square feed, vertical story/reel, landscape display, seasonal or offer variant); a person then downloads imageUrl and uploads it in Meta Ads Manager or Google Ads, or an agent with the advertiser's own API credentials uploads the image into the ad account's image library. Brandiseer does not create, edit, launch, or pause ads or campaigns, does not read ad performance, and does not sync.","category":"creative-mcp","featured":false,"timeEstimate":"~25 to 40 min setup","methods":["MCP","REST"],"hosts":["Cursor","Cowork"],"image":"https://blog.brandiseer.com/use-case-assets/ad-creative-refresh/cover-ad-creative-refresh.png","updated":"2026-10-05"},"stack":[{"name":"Brandiseer","role":"Brand memory and on-brand ad images via MCP or REST","logo":"/logo-b.png","href":"https://brandiseer.com"},{"name":"Meta Ads Manager","role":"Feed, Stories and Reels placements; optional image library upload via Marketing API","href":"https://adsmanager.facebook.com"},{"name":"Google Ads","role":"Display and Performance Max image assets","href":"https://ads.google.com"},{"name":"Cursor","role":"Agent host for Brandiseer MCP or REST"},{"name":"Claude Cowork","role":"Agent host for Brandiseer MCP or REST"}],"workflow":{"audience":"- Performance marketers, founders, and in-house media teams whose paid social or display ads have gone stale and who need fresh on-brand images without opening a separate design tool for every placement\n- Agencies that keep one Brandiseer Brand ID per client and one Meta ad account or Google Ads account per client\n- Operators who can download an `imageUrl` and upload it in Ads Manager or Google Ads, or who already give an agent the advertiser's own API credentials and want that agent to stop at the image library\n\n**Ideal customer profile:** a team whose paid ads already live in Meta Ads Manager or Google Ads, whose brand already lives in Brandiseer, and whose missing piece is a brand-safe image variant plus a clear handoff into the ad account. The person still decides what launches.","rationale":"Meta Ads Manager and Google Ads are where the ad account, the placements, and the launch live. Cursor or Cowork is the agent that turns a brief into stills. Brandiseer stores the logo and visual identity once, then applies that memory on every image. The prompt carries subject, layout, composition, and on-image text. It does not restate colours, fonts, mood, or visual style.\n\nA refresh drifts when every new ad image is a one-off export from a general image tool. Brandiseer keeps the still on the same brand as the rest of the work. The ad platform still decides which ad, which placement, and which test go live. Brandiseer does not see frequency, CTR, or spend, and it does not turn an image into a live ad.\n\nThis is a different job from the sibling guides:\n\n- [Drive & Slides deck visuals](/use-cases/drive-slides-deck-visuals) hands `imageUrl` to a Google Slides slide or a Drive file.\n- [Linear issue visuals](/use-cases/linear-issue-visuals) hands `imageUrl` to a Linear issue or project.\n- [Airtable record visuals](/use-cases/airtable-record-visuals) hands `imageUrl` into an Airtable Attachment field.\n- [Slack creative desk](/use-cases/slack-creative-desk) is a request-and-return desk. A person writes a brief in Slack and an agent posts a still back in the thread.\n- [Social media automation](/use-cases/social-media-automation) writes captions and schedules organic posts with Buffer. It does not refresh paid ads, and this guide does not post through Buffer.\n- [Always-on creative automation](/use-cases/always-on-creative-automation) is the HTTP step in Zapier, Make, or n8n.\n- [Webflow site visuals](/use-cases/webflow-site-visuals) hands `imageUrl` into Webflow Assets or CMS image fields.\n- [WordPress site visuals](/use-cases/wordpress-site-visuals) hands `imageUrl` into the WordPress Media Library.\n- [Framer site visuals](/use-cases/framer-site-visuals) hands `imageUrl` into Framer Assets, the canvas, or a CMS image field.\n- This guide generates paid-ad image variants and hands `imageUrl` to a person, or to that person's own image-library upload. Brandiseer does not push into Meta or Google Ads by itself.\n\n![Example strip: square feed, vertical story, landscape banner, and a seasonal variant](https://blog.brandiseer.com/use-case-assets/ad-creative-refresh/example-strip.png)","architecture":"Architecture, from a stale ad to a fresh image the person can test:\n\n1. **Spot fatigue.** A person reads frequency and CTR in their own Meta Ads Manager or Google Ads reporting, or pastes those numbers into the brief. The agent does not pull ad performance through Brandiseer. There is no ads connection that reads metrics.\n2. **Brief variants.** You name the placement: square feed, Stories or Reels, landscape display, or a seasonal or offer variant of one of those. Ask for 3 to 4 variants per placement. The agent rewrites each brief into subject, layout, and on-image text. It does not invent a price or an offer.\n3. **Generate.** The agent calls Brandiseer with `aspectRatio`, `resolution`, `mode`, and `useLogo` as fields. Square feed uses `1:1`. Stories and Reels use `9:16`. Landscape display uses `16:9`. Prefer `design` and `2K`.\n4. **`imageUrl` to ad library.** Brandiseer returns `imageUrl` only. A person downloads it and uploads it in Ads Manager or Google Ads. Optionally, an agent with the advertiser's own credentials uploads the bytes into the image library and reports the Meta `hash` or the Google Ads resource name. It does not claim an ad changed.\n5. **Human launches and tests.** A person builds or swaps the ad, launches the test, and reads the result in the ad platform. Brandiseer does not launch, pause, or judge the test.\n\n![Workflow infographic: spot fatigue, brief variants, generate, imageUrl to ad library, human launches and tests](https://blog.brandiseer.com/use-case-assets/ad-creative-refresh/workflow-infographic.png)","prerequisites":"- Company website URL, if the brand is not in Brandiseer yet\n- Brandiseer account with at least one finished brand profile\n- API key (`bds_...`) from [Settings → API](https://brandiseer.com/settings/api) or [Developers](https://brandiseer.com/developers)\n- Brand ID (`bds_c_...`) for every brand the agent may call\n- Credits for the stills you actually generate (see the table in Limits & FAQ)\n- Cursor or Claude Cowork, with Brandiseer MCP or a way to call REST\n- A Meta ad account, a Google Ads account, or both, that you can upload images into\n- The placement you are refreshing, and any on-image line you actually want printed (price, offer, or seasonal line). If you do not supply that line, the image stays free of one\n- For any Meta library upload: the numeric ad account id and the advertiser's own Marketing API OAuth access token, in the secret store. Brandiseer is not the Meta app\n- For any Google Ads library upload: the customer id (digits only, no hyphens), a Google Ads developer token from a manager account, and an OAuth access token with scope `https://www.googleapis.com/auth/adwords`. Brandiseer does not issue the developer token\n- Store the Brandiseer key and any ad-platform credential in the agent secret store. Never in an ad, a prompt you commit, Slack, or git","setup":"### Stage 1: Add your brand on Brandiseer\n\n1. Go to [brandiseer.com](https://brandiseer.com). In the homepage input, enter your company website URL and click **Try My Brand**.\n2. Wait about one minute while Brandiseer analyses the site and picks a logo and visuals.\n3. Review those visuals carefully. They teach Brandiseer your look. Every later ad image is influenced by what you leave here.\n4. Remove anything off-brand (partner logos, stock, irrelevant screenshots). Add campaign creatives or product photography that match the look you want.\n5. Click **Add Brand**, sign up if prompted, then wait about 30 seconds for the brand profile to finish.\n\nAgencies repeat this once per client. Do not point two client ad accounts at one Brand ID.\n\n### Stage 2: API key and Brand ID\n\n1. Open [brandiseer.com/developers](https://brandiseer.com/developers) or [Settings → API](https://brandiseer.com/settings/api).\n2. Create an API key (`bds_...`). Put it in the agent host's secret store. Do not paste it into an ad, a doc, Slack, or a git repo.\n3. Copy the Brand ID (`bds_c_...`).\n4. Confirm the key with `GET https://brandiseer.com/api/v1/credits` and `GET https://brandiseer.com/api/v1/brands`, header `X-API-Key`. The credits JSON includes `credits`. The brands list includes each brand `id`.\n5. Optional reachability check, no key required: `GET https://brandiseer.com/api/v1/health` returns `{ \"status\": \"ok\" }`.\n\nRead [guide/docs](https://brandiseer.com/guide/docs) and [guide/image](https://brandiseer.com/guide/image) once while you write the standing instructions. Those pages are the live contract if a field name ever moves.\n\n### Stage 3: Connect Brandiseer to the agent host\n\nUse Cursor or Cowork. One host is enough.\n\n**A. MCP**\n\n1. Add the Brandiseer MCP server at `https://brandiseer.com/api/mcp`.\n2. Authenticate with `Authorization: Bearer` and the API key, in the secret field your host provides.\n3. Confirm the tool list shows `brandiseer_list_brands`, `brandiseer_check_credits`, and `brandiseer_generate_image`. If a fourth Brandiseer tool appears, ignore it until [guide/docs](https://brandiseer.com/guide/docs) documents it.\n\n**B. REST**\n\n1. On [Developers](https://brandiseer.com/developers), open **View Docs** and use **Copy for LLM** if you want the reference inside the session.\n2. Tell the agent to prefer `POST https://brandiseer.com/api/v2/generate` or `POST https://brandiseer.com/api/v2/generate/async` with `X-API-Key`.\n3. If Design mode times out, switch that call to async and poll `GET https://brandiseer.com/api/v2/jobs/:jobId` every 3 to 5 seconds.\n\n### Stage 4: Save the standing instructions\n\n1. Paste the **System prompt** from this guide into the Cursor rule or Cowork instructions that will run ad image refreshes.\n2. Fill the defaults the prompt asks for: Brand ID, default crops per placement, resolution, mode, and whether `useLogo` is on when the brief is silent.\n3. If image-library writes are in scope, also note that the Meta token or the Google Ads developer token and OAuth token live in the secret store. Leave the ad account id and the customer id blank until the human names them.\n4. Ask the agent to call `brandiseer_list_brands` (or `GET /api/v1/brands`) and confirm the Brand ID exists before the first paid still.\n\n### Stage 5: Smoke test one feed image in the Ads Manager UI\n\n1. Ask for one square feed image with a layout-only brief (the spring feed image in Example flows). Do not ask the agent to read your ad account.\n2. Confirm the agent checked the brand, avoided colour and mood words, did not invent a price or offer, and returned `imageUrl` plus crop and mode.\n3. Download that file. In Meta Ads Manager, upload it as a new ad image, or in Google Ads upload it as an image asset. Confirm the image shows in the library.\n4. Do not launch an ad if you are only proving the handoff. Leave the test for a person after the image looks right.\n5. Send a second brief that only says \"make the ad premium and moody\". The agent should ask what is physically in the frame instead of generating.\n\n### Stage 6: Optional image-library upload\n\nSkip this stage if people will upload stills by hand. Use it only when the advertiser's own credential is already in the secret store and the human named the account. Stop when the library accepts the file. Do not create, edit, launch, or pause an ad.\n\n**Meta Marketing API**\n\n1. Use an app and an OAuth access token the advertiser already owns. Brandiseer is not that app and does not mint the token. See [Marketing API authorization](https://developers.facebook.com/docs/marketing-api/overview/authorization/).\n2. Download the PNG bytes from `imageUrl`. Base64-encode those bytes.\n3. `POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages`. The adimages reference checked for this guide lists v25.0 as the current version on that page. `ad_account_id` is the numeric id, so the path contains the `act_` prefix. The documented body parameter for the file is `bytes`, a base64 string. `copy_from` copies an image between ad accounts. Do not use it to upload a new Brandiseer file. See [Ad account ad images](https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/).\n4. Marketing API reference examples pass the OAuth token as the form field `access_token`. A `190` on this edge means invalid OAuth 2.0 access token. A `200` means permissions error.\n5. Read `hash` from the image struct under `images`. Keep that hash for the person. Do not call an ad creative, ad, ad set, or campaign endpoint.\n\n**Google Ads API**\n\n1. You need both a developer token and an OAuth access token. The developer token is sent on every call in the `developer-token` header. A manager account finds it at [ads.google.com/aw/apicenter](https://ads.google.com/aw/apicenter). The OAuth scope is `https://www.googleapis.com/auth/adwords`. Brandiseer does not create the Google Cloud client and does not hold the developer token. See [REST auth](https://developers.google.com/google-ads/api/rest/auth).\n2. Download the PNG bytes from `imageUrl`. Base64-encode those bytes. `imageAsset.data` is the raw bytes, not the Brandiseer URL.\n3. `POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate` with `Content-Type: application/json`, `Authorization: Bearer ACCESS_TOKEN`, and `developer-token`. Customer ids have no hyphens (`1234567890`, not `123-456-7890`). If a manager account is calling a client account, also send `login-customer-id` with the manager's customer id. If the OAuth user is a direct user of the target client, omit `login-customer-id`. The REST examples page checked for this guide uses version `25`. See [Upload binary assets](https://developers.google.com/google-ads/api/rest/examples).\n4. Body: one `operations` item whose `create` sets `name`, `type` to `IMAGE`, and `imageAsset.data` to the base64 bytes. Use a unique descriptive name. The mutate result includes the new asset's resource name. Image assets cannot be changed or removed programmatically after upload. See [Working with assets](https://developers.google.com/google-ads/api/docs/assets/working-with-assets).\n5. Stop. Do not link the asset to a campaign, an ad group, or a Performance Max asset group. A 16:9 Brandiseer file is not a 1.91:1 landscape marketing image. The person crops before they use it that way, then launches themselves.","promptingRules":"When the agent builds `prompt`:\n\n- Keep subject, layout, composition, and on-image text (what the words say and where they sit).\n- Never send brand colours, fonts, mood, or visual style. Brand memory already applies those. Style words fight the brand and produce off-brand stills.\n- Put crop in `aspectRatio`, quality in `mode`, pixel size in `resolution`, and logo on/off in `useLogo`. Those are body fields, not prompt prose.\n- Keep on-image text short. One short line is enough. Leave it out when the ad headline will live in the ad platform, not on the image.\n- Do not invent a price, a discount, a date, or an offer. If the human did not supply the exact line, the image has no offer text.\n- On `9:16`, leave an empty band at the top and an empty band at the bottom so Stories and Reels UI (profile, buttons, captions) does not cover the subject. Stories and Reels safe zones differ. This guide does not invent a pixel inset. Keep the subject in the middle band.\n- Map the placement to a documented Brandiseer ratio. Supported values are `1:1`, `16:9`, `9:16`, and `4:3`. Square feed: `1:1`. Stories or Reels: `9:16`. Landscape display: `16:9`. Seasonal or offer variant: the same ratio as the placement it refreshes.\n- Feed portrait `4:5` is not a Brandiseer `aspectRatio`. Do not send `4:5`. If the human wants a 4:5 feed crop, generate `9:16` and let them crop, or stay on `1:1` for the square feed. Say which one you used.\n- Google Display and Performance Max landscape marketing images are 1.91:1 (recommended 1200 by 628, minimum 600 by 314). Brandiseer has no 1.91:1 ratio. Generate `16:9` and let the person crop. Square marketing images are 1:1, which Brandiseer can generate directly. The ratio is checked when someone later links the asset, not during the library upload. See [Performance Max asset requirements](https://developers.google.com/google-ads/api/performance-max/asset-requirements).\n- `4:3` is a real Brandiseer ratio. None of the placements above require it. Use `4:3` only when the human asks for that frame.\n- For a test set, generate 3 to 4 variants per placement. Change what is in the frame or how it is arranged. Do not differentiate variants with colour, mood, or an invented offer.\n- Prefer `design` for finished ad images. `fast` is the API default if you omit `mode`, and it is the right fallback if a design job fails once.\n- Prefer `2K` for these stills. Use `1K` when file size or speed matters. Use `4K` only when the brief asks for it.\n- Set `useLogo` true only when the brief asked for a logo (or your default says so).\n- Full rules: [Prompt compatibility](https://brandiseer.com/guide/image).","systemPrompts":"Paste this into Cursor or Cowork as the standing instructions for ad image refreshes. Replace the placeholders before the first generate.\n\n```text\nYou generate on-brand ad images for paid social and display placements.\nBrandiseer returns an image URL. You do not create, edit, launch, or pause\nads, campaigns, ad sets, or creatives. You do not read ad metrics. You do\nnot sync Meta or Google Ads. You do not claim an ad account changed unless\nan image-library call you actually performed returned success.\n\nBrandiseer:\n- MCP: https://brandiseer.com/api/mcp with Authorization: Bearer.\n  Tools you may call: brandiseer_list_brands, brandiseer_check_credits,\n  brandiseer_generate_image. Do not invent tool names.\n- REST: X-API-Key: bds_... (secret store only).\n  Prefer POST https://brandiseer.com/api/v2/generate\n  or POST https://brandiseer.com/api/v2/generate/async\n  then poll GET https://brandiseer.com/api/v2/jobs/:jobId every 3 to 5 seconds\n  until data.status is done or failed, or about 10 minutes have passed.\n- Live contract: https://brandiseer.com/guide/docs and\n  https://brandiseer.com/guide/image\n- Supported aspectRatio values: 1:1, 16:9, 9:16, 4:3. Do not send 4:5\n  or 1.91:1. Brandiseer does not host a Meta app or a Google Ads app.\n  There is no ads MCP.\n\nDefaults for this workspace (fill in once):\n- Brand ID: bds_c_...\n- Square feed aspectRatio: 1:1\n- Stories or Reels aspectRatio: 9:16\n- Landscape display aspectRatio: 16:9\n- Seasonal or offer variant: same ratio as the placement it refreshes\n- Feed portrait 4:5: not supported. Ask, then use 9:16 for a crop the\n  human will make, or stay on 1:1. Never send 4:5.\n- mode: design\n- resolution: 2K (use 1K if file size or speed matters)\n- useLogo when the brief is silent: false\n- Variants per placement when the human asks for a test set: 3 to 4\n- Multi-brand map, if any: brand name to bds_c_...\n\nOn every request:\n1. Resolve brandId from the brief or the default. If more than one brand\n   exists and the brief does not name one, ask. Never fall through to\n   another client's Brand ID.\n2. Fatigue numbers (frequency, CTR, spend) come from the human. Do not\n   call an ads API to read performance. If the brief has no placement,\n   ask which one. Do not invent the account.\n3. Check credits before a batch (brandiseer_check_credits or\n   GET https://brandiseer.com/api/v1/credits). A single still can proceed\n   if the last known balance covers the mode and resolution in the table\n   you were given. On 402, stop.\n4. Rewrite the brief into subject, layout, composition, and on-image text.\n   Strip colours, fonts, mood, and visual style. If stripping leaves no\n   physical subject, ask what is in the frame. Do not generate.\n5. On-image text stays short. Use only a line the human supplied. If they\n   did not give a price, discount, date, or offer, do not invent one.\n   On 9:16, keep the subject in the middle band and leave empty bands at\n   the top and bottom.\n6. Choose aspectRatio from the placement. Put resolution, mode, and\n   useLogo in API fields, not in the prompt. Square feed is 1:1. Stories\n   or Reels are 9:16. Landscape display is 16:9. Use 4:3 only when the\n   human names that frame.\n7. Generate one still per variant. Return imageUrl, aspectRatio, mode,\n   resolution, useLogo, and a placement note: which account and placement\n   the human named, and which path (Ads Manager or Google Ads UI upload,\n   Meta adimages hash, or Google Ads asset resource name).\n8. Do not tell the user an ad, campaign, or library changed unless the\n   library call you actually performed returned success.\n\nImage library, only when the human already asked for it:\n- Default path: the human downloads imageUrl and uploads it in Meta Ads\n  Manager or Google Ads, then builds or swaps the ad and launches the\n  test. You do not claim that happened. You do not launch.\n- Meta, library upload only:\n  POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages\n  Parameter bytes: base64 of the PNG downloaded from imageUrl.\n  Do not use copy_from for a new file.\n  Pass the advertiser's own OAuth access token. Marketing API reference\n  examples send it as the form field access_token.\n  Read hash from the struct under images. Stop. Do not create a creative,\n  ad, ad set, or campaign.\n  Docs: https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/\n- Google Ads, library upload only:\n  Headers: Authorization: Bearer ACCESS_TOKEN, developer-token, and\n  login-customer-id only when a manager is calling a client account.\n  Scope: https://www.googleapis.com/auth/adwords\n  Customer ids have no hyphens.\n  POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate\n  create.type = IMAGE\n  create.imageAsset.data = base64 raw bytes (not the imageUrl string)\n  create.name = a unique descriptive name\n  Keep the resource name from the mutate result. Stop. Do not link the\n  asset and do not create an ad. A 16:9 file still needs a human crop\n  before it is used as a 1.91:1 landscape marketing image.\n  Docs: https://developers.google.com/google-ads/api/rest/auth\n  https://developers.google.com/google-ads/api/rest/examples\n  https://developers.google.com/google-ads/api/docs/assets/working-with-assets\n\nOut of scope:\n- Reading frequency, CTR, spend, or any other ad metric.\n- Creating, editing, launching, or pausing ads, campaigns, ad sets, or creatives.\n- Slack replies, Buffer scheduling, and Zapier, Make, or n8n scenarios.\n- Drive, Slides, Linear, Airtable, Webflow, WordPress, and Framer handoffs\n  (use those guides).\n- Legal approval of claims, prices, or offers printed on the image.\n- GET https://brandiseer.com/api/v1/usage (browser session only).\n```\n\n### Prompt rewriter\n\nUse this when a brief arrives as messy notes. It does not call Brandiseer. It only cleans text before you put it in `prompt`. The word list is a starting filter, not an official dictionary. If a phrase still describes colour, type, mood, or look, delete it by hand. `4:5` and `1.91:1` are absent on purpose. They are not Brandiseer ratios.\n\n```javascript\n// Local rewriter for a Cursor or Cowork helper script.\n// Keeps subject, layout, composition, and on-image text.\n// Strips colour, type, mood, and visual-style language.\n\nconst STYLE_WORDS =\n  /\\b(colou?rs?|palette|hex|fonts?|typefaces?|typography|serif|sans-serif|moods?|vibes?|aesthetics?|minimalist|luxury|playful|cinematic|pastel|neon|gradients?|moody|elegant|premium|on-brand)\\b/gi;\n\nconst SLOT_TO_RATIO = {\n  feed: \"1:1\",\n  \"square feed\": \"1:1\",\n  square: \"1:1\",\n  \"1:1\": \"1:1\",\n  story: \"9:16\",\n  stories: \"9:16\",\n  reel: \"9:16\",\n  reels: \"9:16\",\n  \"story reel\": \"9:16\",\n  \"stories reels\": \"9:16\",\n  vertical: \"9:16\",\n  portrait: \"9:16\",\n  \"9:16\": \"9:16\",\n  landscape: \"16:9\",\n  display: \"16:9\",\n  banner: \"16:9\",\n  \"landscape display\": \"16:9\",\n  \"16:9\": \"16:9\",\n  \"4:3\": \"4:3\",\n};\n\nfunction rewriteBrandiseerPrompt(input) {\n  const headline = String(input.headline || \"\").trim();\n  const subject = String(input.subject || input.brief || \"\").trim();\n  const slotKey = String(input.slot || input.crop || \"\").trim().toLowerCase();\n  const aspectRatio = SLOT_TO_RATIO[slotKey] || \"\";\n\n  let prompt = [subject, headline ? `On-image text: \"${headline}\".` : \"\"]\n    .filter(Boolean)\n    .join(\" \");\n\n  prompt = prompt\n    .replace(/#[0-9a-f]{3,8}\\b/gi, \" \")\n    .replace(STYLE_WORDS, \" \")\n    .replace(/\\s+/g, \" \")\n    .trim();\n\n  return {\n    prompt,\n    aspectRatio,\n    needsSubject: prompt.length < 12 || !aspectRatio,\n  };\n}\n```\n\nIf `needsSubject` is true, ask the human for a subject and a placement. Do not POST. A seasonal or offer variant is not its own key. Pass the placement it refreshes (`square feed`, `story`, or `landscape display`) and put only a human-supplied line in `headline`. If the human asked for 4:5 or 1.91:1, do not invent a ratio. Ask, then use `9:16` or `16:9` and say the person will crop. Use `4:3` only when the human names that frame.","addOns":"Add these after the feed-image smoke test passes. They do not change Brandiseer's image contract, and they do not give Brandiseer an ads sync.\n\n### Approval before anything launches\n\nAfter `imageUrl` is in the image library, a named reviewer checks the still. Until that happens, do not launch. Brandiseer does not approve legal claims, prices, or offers. Ad review policies stay with Meta or Google. Brandiseer does not know those policies and does not submit an ad for review.\n\n### One brand per client account\n\nKeep a table: Meta ad account id or Google Ads customer id, secret name, and `bds_c_...`. Require a named account on every library write when more than one client exists. If the name misses, stop and list the clients you configured. Confirm Brand IDs with `GET /api/v1/brands`. Never fall through to another client's Brand ID or another account's token.\n\n### Credit soft-limit\n\nWhen `credits` is under a threshold you choose (for example 40), switch new ad images to `mode: fast` and `resolution: 1K`, or skip them. Fast at 1K costs 4 credits. An explicit \"run anyway\" from the human can override the skip. The threshold is yours, not a Brandiseer default.\n\n### Placement log\n\nKeep a short note next to the work: account id, placement, variant, `brandId`, `mode`, `aspectRatio`, `resolution`, `imageUrl`, and whether a person uploaded it, a Meta `hash` came back, or a Google Ads resource name came back. Brandiseer does not write this log. Meta and Google Ads do not send it back to Brandiseer.\n\n### Handoff to the sibling guides\n\n- Deck stills in Google Slides or Drive: [Drive & Slides deck visuals](/use-cases/drive-slides-deck-visuals).\n- Issue and project stills in Linear: [Linear issue visuals](/use-cases/linear-issue-visuals).\n- Record stills on Airtable Attachment fields: [Airtable record visuals](/use-cases/airtable-record-visuals).\n- Stills requested in Slack, returned in a thread: [Slack creative desk](/use-cases/slack-creative-desk).\n- Organic captions and a Buffer schedule: [Social media automation](/use-cases/social-media-automation). That path is not a paid ad refresh.\n- A Zapier, Make, or n8n trigger that should deliver `imageUrl` on a schedule: [Always-on creative automation](/use-cases/always-on-creative-automation).\n- Webflow Assets or CMS: [Webflow site visuals](/use-cases/webflow-site-visuals).\n- WordPress Media Library: [WordPress site visuals](/use-cases/wordpress-site-visuals).\n- Framer Assets, canvas, or CMS: [Framer site visuals](/use-cases/framer-site-visuals).\n\nDo not embed a Slides deck, a Linear issue, an Airtable base, a Buffer schedule, a Slack desk, an always-on HTTP scenario, Webflow, WordPress, or Framer inside this ad creative guide.\n\n### Optional motion (not the refresh MVP)\n\nVideo is image-to-video only. Generate the still first, then send its public `imageUrl` to `POST https://brandiseer.com/api/v2.1/video/generate`. Poll `GET https://brandiseer.com/api/v2.1/video/jobs/:jobId`. Video jobs use `status` `completed` and a `videoUrl`, which is a different contract from image jobs (`done` and `imageUrl`). Read [guide/video](https://brandiseer.com/guide/video) for tier, duration, and resolution before you enable this. Video credits are per second and are not the still table below. A video failure does not undo the still. An ad can hold that video only after a person uploads it in the ad platform. This guide does not document a video-ad create call. Leave motion off unless the brief explicitly asks to animate.","limitsAndFaq":"### Does Brandiseer create or launch the ad?\n\nNo. Brandiseer returns `imageUrl`. A person downloads that file and uploads it in Meta Ads Manager or Google Ads, then builds or swaps the ad and launches the test. An agent with the advertiser's own credentials can upload the bytes into the image library only. None of those paths create, edit, launch, or pause an ad, and none of them sync the ad account back to Brandiseer.\n\n### Is there a Brandiseer app for Meta or Google Ads?\n\nNo. There is no Brandiseer Meta app and no Brandiseer Google Ads app in this guide. Do not look for a native \"send to Ads Manager\" button on the Brandiseer side.\n\n### Do I need an ads MCP?\n\nNo. This guide uses the Ads Manager or Google Ads UI, or the documented image-library calls with the advertiser's own credentials. Do not invent an ads MCP. Brandiseer still only returns the still.\n\n### Can the agent read frequency or CTR?\n\nNo. Spotting fatigue is the person, in their own Ads Manager or Google Ads reporting, or by pasting numbers into the brief. Brandiseer does not read ad performance. This guide does not document an insights or reporting call.\n\n### Which Meta call is in this guide?\n\nOnly the image-library upload confirmed on Meta's ad account ad images reference:\n\n- `POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages`\n- Parameter `bytes`: the image file as a base64 string\n- The return struct includes `hash`, `url`, `url_128`, `url_256`, `url_256_height`, `url_256_width`, `height`, `width`, and `name`, under `images`\n- Error `100` invalid parameter, `200` permissions error, `190` invalid OAuth 2.0 access token, `80004` too many calls to this ad account, `368` the action was deemed abusive or is otherwise disallowed, `613` rate limit\n\n`copy_from` is a different operation (copy an existing hash between ad accounts). This guide does not use it for a new file. This guide does not call ad creative, ad, ad set, or campaign endpoints. Docs: [Ad account ad images](https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/).\n\n### Which Google Ads call is in this guide?\n\nOnly the image asset upload confirmed on Google's REST examples and asset docs:\n\n- `POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate`\n- Headers: `Authorization: Bearer ACCESS_TOKEN`, `developer-token`, and `login-customer-id` when a manager calls a client account\n- Body: `operations[].create` with `type` `IMAGE`, `name`, and `imageAsset.data` (base64 raw bytes)\n- The mutate result includes the new asset's resource name\n- OAuth scope: `https://www.googleapis.com/auth/adwords`\n\nThis guide does not link the asset and does not create or pause an ad. An uploaded image asset cannot be changed or removed programmatically. Docs: [REST auth](https://developers.google.com/google-ads/api/rest/auth) · [REST examples](https://developers.google.com/google-ads/api/rest/examples) · [Working with assets](https://developers.google.com/google-ads/api/docs/assets/working-with-assets).\n\n### Where do the keys live?\n\nThe Brandiseer key (`bds_...`), the Meta OAuth token, and the Google Ads developer token plus OAuth token live in the agent host's secret store. Never in an ad, a doc, a Slack message, a prompt you commit, or a workflow export in git. Those ad-platform credentials belong to the advertiser. Brandiseer does not store them. Rotate any secret if it leaks. Poll a Brandiseer job with the same Brandiseer key that submitted it.\n\n### What do stills cost?\n\nCredits per still. Failures refund. Async submits deduct immediately and refund if the job fails.\n\n| Mode | 1K | 2K | 4K |\n| --- | --- | --- | --- |\n| Fast | 4 | 6 | 8 |\n| Design | 5 | 6 | 8 |\n| Pro | 6 | 8 | 10 |\n\n`402` means not enough credits. Pricing context: [brandiseer.com/pricing](https://brandiseer.com/pricing).\n\n### Why is the still off-brand?\n\nAlmost always because the prompt contained colours, fonts, mood, or visual style. Strip them and generate again. See [guide/image](https://brandiseer.com/guide/image).\n\n### Why does the crop look wrong in the placement?\n\nBrandiseer aspect ratios are `1:1`, `16:9`, `9:16`, and `4:3`. Generate the square feed at `1:1`, Stories and Reels at `9:16`, and landscape display at `16:9`. Brandiseer cannot emit `4:5` or `1.91:1`. Crop those in the ad platform.\n\nInstagram stream ads recommend `1:1` and also allow `4:5` and `1.91:1`. Instagram Stories recommend `9:16`. The minimum image width called out on the Instagram ads media requirements page is 600 px for Stories. If a Story image sits between `4:5` and `9:16` and you provide no crop spec, Instagram crops it to `4:5`. See [Media requirements](https://developers.facebook.com/docs/instagram/ads-api/reference/media-requirements/).\n\nGoogle landscape marketing images must be 1.91:1 (recommended 1200 by 628, minimum 600 by 314, maximum file size 5120 KB). Square marketing images must be 1:1 (recommended 1200 by 1200, minimum 300 by 300, same file cap). Linking a 16:9 file as a landscape marketing image returns `ASPECT_RATIO_NOT_ALLOWED`. That check happens when the asset is linked, not when it is uploaded. See [Performance Max asset requirements](https://developers.google.com/google-ads/api/performance-max/asset-requirements).\n\n### How long can I poll?\n\nAbout 10 minutes, then the Brandiseer job expires. Poll every 3 to 5 seconds. Stop on `done` or `failed` as well. Do not start a second job for the same variant while the first is still open, or you will pay twice.\n\n### Which generate URL should new ad images use?\n\n`POST /api/v2/generate` when the agent host waits long enough. `POST /api/v2/generate/async` when it does not. Legacy `POST /api/v1/generate` remains for older callers and only supports Fast and Pro via `proMode` boolean.\n\n### Why did the Meta upload fail?\n\nCheck the error code on the adimages edge. `100` is an invalid parameter (the `bytes` value is the usual suspect). `190` is an invalid OAuth 2.0 access token. `200` is a permissions error, which means this token cannot upload to that ad account. `80004` means too many calls to this ad account. `613` is a rate limit. `368` means the action was deemed abusive or is otherwise disallowed. The path must be `/act_{ad_account_id}/adimages` with the numeric id. Do not tell the human the library updated when `hash` is missing.\n\n### Why did the Google Ads upload fail?\n\nThe call needs `developer-token` and `Authorization: Bearer` with scope `https://www.googleapis.com/auth/adwords`. Customer ids omit hyphens. A manager calling a client account also sends `login-customer-id`. `imageAsset.data` must be the base64 raw bytes, not `imageUrl`. Image asset names should be unique. An image asset cannot be edited in place after it exists. This guide does not treat a failed mutate as a stored asset.\n\n### Does a library upload launch a test?\n\nNo. A Meta `hash` and a Google Ads resource name sit in the image library. A person still builds or swaps the ad, submits it to the platform's ad review, and launches. Ad review policies stay with Meta or Google. Brandiseer does not submit the ad and does not know the review result.\n\n### Does the agent approve claims on the image?\n\nNo. If the still has a price, a discount, or a legal line, a person reads it before anything launches. The words must be a line the human supplied. Brandiseer does not review the claim.","demo":"**Motion brief only. This is not an embed, and there is no walkthrough URL yet.**\n\n**Working title:** Ad creative refresh\n**Length:** 45 to 75 seconds\n\n**Story beats:**\n\n1. A person pastes a fatigue note and asks Cursor or Cowork for a square feed refresh with a layout-only brief\n2. The agent calls Brandiseer (MCP or REST v2) and shows an on-brand still\n3. `imageUrl` is downloaded and uploaded in Meta Ads Manager or Google Ads by a person\n4. Optional cut: a Meta `adimages` call returns a hash, and no ad is created\n5. A human launches a test only after the still looks right\n\n**On screen:** the agent transcript with the prompt (no colour or mood words, no invented offer), one finished still, and the image sitting in the ad account library. Blur the Brandiseer API key and any Meta or Google credential. Do not show a fake video player in the guide."},"sections":[{"id":"overview","heading":"Overview","markdown":"**Brandiseer + Meta Ads Manager + Google Ads (+ Cursor or Claude Cowork)**\n\n![Cover: a fatigued ad replaced by a fresh on-brand set](https://blog.brandiseer.com/use-case-assets/ad-creative-refresh/cover-ad-creative-refresh.png)\n\nBy the end of this guide you will have a repeatable way to refresh stale paid social and display images. An agent in Cursor or Claude Cowork calls Brandiseer and gets an `imageUrl` for a square feed, a vertical Story or Reel, a landscape display frame, or a seasonal or offer variant of one of those placements. A person then downloads that file and uploads it in Meta Ads Manager or Google Ads.\n\nBrandiseer owns the brand memory and the still. Meta Ads Manager owns Feed, Stories, and Reels, plus the ad, the ad set, and the campaign. Google Ads owns Display and Performance Max, plus the ad and the campaign. Brandiseer returns `imageUrl` only. It does not create, edit, launch, or pause ads, campaigns, ad sets, or creatives. It does not read ad metrics. It does not sync. There is no Brandiseer Meta app in this guide, no Brandiseer Google Ads app, and no ads MCP. Do not invent any of them.\n\nThe default handoff is a person. They download `imageUrl` and upload it as a new ad image or image asset, then build or swap the ad and launch the test themselves. Optionally, an agent that already holds the advertiser's own credentials can upload the image bytes into that ad account's image library and stop there. A Meta upload returns an image hash. A Google Ads upload returns an asset resource name. A person still reviews the image and launches. Spotting fatigue is also a person: they read their own Ads Manager or Google Ads reporting, or they paste the numbers into the brief. The agent does not read frequency or CTR through Brandiseer.\n\n> People who need on-brand stills for Google Slides or Drive should use [Drive & Slides deck visuals](/use-cases/drive-slides-deck-visuals). People who keep issue and project stills in Linear should use [Linear issue visuals](/use-cases/linear-issue-visuals). People who keep campaign records in Airtable should use [Airtable record visuals](/use-cases/airtable-record-visuals). People who brief stills inside Slack should use [Slack creative desk](/use-cases/slack-creative-desk). Organic captions and Buffer scheduling should use [Social media automation](/use-cases/social-media-automation). That guide posts organically through Buffer. It is not paid ads, and this guide does not schedule or publish posts. Schedules and webhooks in Zapier, Make, or n8n should use [Always-on creative automation](/use-cases/always-on-creative-automation). Webflow site stills should use [Webflow site visuals](/use-cases/webflow-site-visuals). WordPress Media Library stills should use [WordPress site visuals](/use-cases/wordpress-site-visuals). Framer site stills should use [Framer site visuals](/use-cases/framer-site-visuals). This guide does not recreate any of those."},{"id":"audience","heading":"Who it's for","markdown":"- Performance marketers, founders, and in-house media teams whose paid social or display ads have gone stale and who need fresh on-brand images without opening a separate design tool for every placement\n- Agencies that keep one Brandiseer Brand ID per client and one Meta ad account or Google Ads account per client\n- Operators who can download an `imageUrl` and upload it in Ads Manager or Google Ads, or who already give an agent the advertiser's own API credentials and want that agent to stop at the image library\n\n**Ideal customer profile:** a team whose paid ads already live in Meta Ads Manager or Google Ads, whose brand already lives in Brandiseer, and whose missing piece is a brand-safe image variant plus a clear handoff into the ad account. The person still decides what launches."},{"id":"why-brandiseer","heading":"Why Brandiseer in this stack","markdown":"Meta Ads Manager and Google Ads are where the ad account, the placements, and the launch live. Cursor or Cowork is the agent that turns a brief into stills. Brandiseer stores the logo and visual identity once, then applies that memory on every image. The prompt carries subject, layout, composition, and on-image text. It does not restate colours, fonts, mood, or visual style.\n\nA refresh drifts when every new ad image is a one-off export from a general image tool. Brandiseer keeps the still on the same brand as the rest of the work. The ad platform still decides which ad, which placement, and which test go live. Brandiseer does not see frequency, CTR, or spend, and it does not turn an image into a live ad.\n\nThis is a different job from the sibling guides:\n\n- [Drive & Slides deck visuals](/use-cases/drive-slides-deck-visuals) hands `imageUrl` to a Google Slides slide or a Drive file.\n- [Linear issue visuals](/use-cases/linear-issue-visuals) hands `imageUrl` to a Linear issue or project.\n- [Airtable record visuals](/use-cases/airtable-record-visuals) hands `imageUrl` into an Airtable Attachment field.\n- [Slack creative desk](/use-cases/slack-creative-desk) is a request-and-return desk. A person writes a brief in Slack and an agent posts a still back in the thread.\n- [Social media automation](/use-cases/social-media-automation) writes captions and schedules organic posts with Buffer. It does not refresh paid ads, and this guide does not post through Buffer.\n- [Always-on creative automation](/use-cases/always-on-creative-automation) is the HTTP step in Zapier, Make, or n8n.\n- [Webflow site visuals](/use-cases/webflow-site-visuals) hands `imageUrl` into Webflow Assets or CMS image fields.\n- [WordPress site visuals](/use-cases/wordpress-site-visuals) hands `imageUrl` into the WordPress Media Library.\n- [Framer site visuals](/use-cases/framer-site-visuals) hands `imageUrl` into Framer Assets, the canvas, or a CMS image field.\n- This guide generates paid-ad image variants and hands `imageUrl` to a person, or to that person's own image-library upload. Brandiseer does not push into Meta or Google Ads by itself.\n\n![Example strip: square feed, vertical story, landscape banner, and a seasonal variant](https://blog.brandiseer.com/use-case-assets/ad-creative-refresh/example-strip.png)"},{"id":"stack","heading":"Stack","markdown":"| Layer | Tool | Role |\n| --- | --- | --- |\n| Brand visuals | Brandiseer MCP and/or REST v2 | Generate on-brand ad images. MCP uses `Authorization: Bearer`. REST uses `X-API-Key: bds_...` |\n| Agent host (pick one) | Cursor | Read the brief, call Brandiseer, return `imageUrl` and a placement note |\n| Agent host (pick one) | Claude Cowork | Same contract as Cursor for interactive ad images |\n| Paid social | Meta Ads Manager | Feed, Stories, and Reels. A person uploads and launches by default. Optional image library upload with the advertiser's own Marketing API token |\n| Paid display | Google Ads | Display and Performance Max image assets. A person uploads and launches by default. Optional image asset upload with the advertiser's own developer token and OAuth |\n\n![Stack diagram: Cursor or Cowork, Brandiseer, and Meta Ads plus Google Ads](https://blog.brandiseer.com/use-case-assets/ad-creative-refresh/stack-diagram.png)\n\nYou only need one agent host. The Brandiseer contract is the same on both. There is no Brandiseer app that creates an ad in Meta or Google. The handoff is an image URL, a download, or an image-library call that you perform with the advertiser's own API. This guide does not use an ads MCP. Do not invent one.\n\n**Brandiseer docs:** [Developers](https://brandiseer.com/developers) · [guide/docs](https://brandiseer.com/guide/docs) · [guide/image](https://brandiseer.com/guide/image) · [Settings → API](https://brandiseer.com/settings/api)\n\n**Meta docs:** [Ad account ad images](https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/) · [Instagram ads media requirements](https://developers.facebook.com/docs/instagram/ads-api/reference/media-requirements/) · [Marketing API authorization](https://developers.facebook.com/docs/marketing-api/overview/authorization/)\n\n**Google Ads docs:** [REST auth](https://developers.google.com/google-ads/api/rest/auth) · [REST examples, upload binary assets](https://developers.google.com/google-ads/api/rest/examples) · [Working with assets](https://developers.google.com/google-ads/api/docs/assets/working-with-assets) · [Performance Max asset requirements](https://developers.google.com/google-ads/api/performance-max/asset-requirements) · [ImageAsset](https://developers.google.com/google-ads/api/reference/rpc/v25/ImageAsset)\n\nEndpoint tables and HTTP contracts are in API reference at the end of this guide.\n\n### Meta Ads Manager and Google Ads (placement, not generation)\n\nThree practical paths. Use the first one until an image-library write is actually needed. Brandiseer is finished when it returns `imageUrl`.\n\n1. **Ads Manager or Google Ads UI (default).** Download the file or open `imageUrl`. Upload it as a new ad image in Meta Ads Manager, or as an image asset in Google Ads. Then build or swap the ad and launch the test yourself. Brandiseer stops at the URL. The ad platform stores its own copy. A person always reviews and launches.\n2. **Meta image library (optional agent path).** Only with the advertiser's own Marketing API access token, and only to upload bytes. The call is `POST /act_{ad_account_id}/adimages` with the image as base64 `bytes`. The response struct includes `hash`. That hash is an image-library id a person, or their own tooling, can use later. This guide does not create a creative, an ad, an ad set, or a campaign.\n3. **Google Ads image asset (optional agent path).** Only with the advertiser's own Google Ads developer token plus OAuth. The call is `customers/{customerId}/assets:mutate` with `type` `IMAGE` and `imageAsset.data` set to the raw image bytes (base64 in JSON). The mutate result includes the new asset's resource name. This guide does not link that asset to a campaign, an ad group, or an asset group, and it does not create or pause an ad.\n\n`ad_account_id`, the Google Ads customer id, and which placement to refresh come from the human. Do not guess them. Do not read them from Brandiseer. Brandiseer does not hold Meta or Google credentials.\n\nA person still reviews the image and launches the test."},{"id":"architecture","heading":"How it works","markdown":"Architecture, from a stale ad to a fresh image the person can test:\n\n1. **Spot fatigue.** A person reads frequency and CTR in their own Meta Ads Manager or Google Ads reporting, or pastes those numbers into the brief. The agent does not pull ad performance through Brandiseer. There is no ads connection that reads metrics.\n2. **Brief variants.** You name the placement: square feed, Stories or Reels, landscape display, or a seasonal or offer variant of one of those. Ask for 3 to 4 variants per placement. The agent rewrites each brief into subject, layout, and on-image text. It does not invent a price or an offer.\n3. **Generate.** The agent calls Brandiseer with `aspectRatio`, `resolution`, `mode`, and `useLogo` as fields. Square feed uses `1:1`. Stories and Reels use `9:16`. Landscape display uses `16:9`. Prefer `design` and `2K`.\n4. **`imageUrl` to ad library.** Brandiseer returns `imageUrl` only. A person downloads it and uploads it in Ads Manager or Google Ads. Optionally, an agent with the advertiser's own credentials uploads the bytes into the image library and reports the Meta `hash` or the Google Ads resource name. It does not claim an ad changed.\n5. **Human launches and tests.** A person builds or swaps the ad, launches the test, and reads the result in the ad platform. Brandiseer does not launch, pause, or judge the test.\n\n![Workflow infographic: spot fatigue, brief variants, generate, imageUrl to ad library, human launches and tests](https://blog.brandiseer.com/use-case-assets/ad-creative-refresh/workflow-infographic.png)"},{"id":"prerequisites","heading":"What you need","markdown":"- Company website URL, if the brand is not in Brandiseer yet\n- Brandiseer account with at least one finished brand profile\n- API key (`bds_...`) from [Settings → API](https://brandiseer.com/settings/api) or [Developers](https://brandiseer.com/developers)\n- Brand ID (`bds_c_...`) for every brand the agent may call\n- Credits for the stills you actually generate (see the table in Limits & FAQ)\n- Cursor or Claude Cowork, with Brandiseer MCP or a way to call REST\n- A Meta ad account, a Google Ads account, or both, that you can upload images into\n- The placement you are refreshing, and any on-image line you actually want printed (price, offer, or seasonal line). If you do not supply that line, the image stays free of one\n- For any Meta library upload: the numeric ad account id and the advertiser's own Marketing API OAuth access token, in the secret store. Brandiseer is not the Meta app\n- For any Google Ads library upload: the customer id (digits only, no hyphens), a Google Ads developer token from a manager account, and an OAuth access token with scope `https://www.googleapis.com/auth/adwords`. Brandiseer does not issue the developer token\n- Store the Brandiseer key and any ad-platform credential in the agent secret store. Never in an ad, a prompt you commit, Slack, or git"},{"id":"setup","heading":"Setup","markdown":"### Stage 1: Add your brand on Brandiseer\n\n1. Go to [brandiseer.com](https://brandiseer.com). In the homepage input, enter your company website URL and click **Try My Brand**.\n2. Wait about one minute while Brandiseer analyses the site and picks a logo and visuals.\n3. Review those visuals carefully. They teach Brandiseer your look. Every later ad image is influenced by what you leave here.\n4. Remove anything off-brand (partner logos, stock, irrelevant screenshots). Add campaign creatives or product photography that match the look you want.\n5. Click **Add Brand**, sign up if prompted, then wait about 30 seconds for the brand profile to finish.\n\nAgencies repeat this once per client. Do not point two client ad accounts at one Brand ID.\n\n### Stage 2: API key and Brand ID\n\n1. Open [brandiseer.com/developers](https://brandiseer.com/developers) or [Settings → API](https://brandiseer.com/settings/api).\n2. Create an API key (`bds_...`). Put it in the agent host's secret store. Do not paste it into an ad, a doc, Slack, or a git repo.\n3. Copy the Brand ID (`bds_c_...`).\n4. Confirm the key with `GET https://brandiseer.com/api/v1/credits` and `GET https://brandiseer.com/api/v1/brands`, header `X-API-Key`. The credits JSON includes `credits`. The brands list includes each brand `id`.\n5. Optional reachability check, no key required: `GET https://brandiseer.com/api/v1/health` returns `{ \"status\": \"ok\" }`.\n\nRead [guide/docs](https://brandiseer.com/guide/docs) and [guide/image](https://brandiseer.com/guide/image) once while you write the standing instructions. Those pages are the live contract if a field name ever moves.\n\n### Stage 3: Connect Brandiseer to the agent host\n\nUse Cursor or Cowork. One host is enough.\n\n**A. MCP**\n\n1. Add the Brandiseer MCP server at `https://brandiseer.com/api/mcp`.\n2. Authenticate with `Authorization: Bearer` and the API key, in the secret field your host provides.\n3. Confirm the tool list shows `brandiseer_list_brands`, `brandiseer_check_credits`, and `brandiseer_generate_image`. If a fourth Brandiseer tool appears, ignore it until [guide/docs](https://brandiseer.com/guide/docs) documents it.\n\n**B. REST**\n\n1. On [Developers](https://brandiseer.com/developers), open **View Docs** and use **Copy for LLM** if you want the reference inside the session.\n2. Tell the agent to prefer `POST https://brandiseer.com/api/v2/generate` or `POST https://brandiseer.com/api/v2/generate/async` with `X-API-Key`.\n3. If Design mode times out, switch that call to async and poll `GET https://brandiseer.com/api/v2/jobs/:jobId` every 3 to 5 seconds.\n\n### Stage 4: Save the standing instructions\n\n1. Paste the **System prompt** from this guide into the Cursor rule or Cowork instructions that will run ad image refreshes.\n2. Fill the defaults the prompt asks for: Brand ID, default crops per placement, resolution, mode, and whether `useLogo` is on when the brief is silent.\n3. If image-library writes are in scope, also note that the Meta token or the Google Ads developer token and OAuth token live in the secret store. Leave the ad account id and the customer id blank until the human names them.\n4. Ask the agent to call `brandiseer_list_brands` (or `GET /api/v1/brands`) and confirm the Brand ID exists before the first paid still.\n\n### Stage 5: Smoke test one feed image in the Ads Manager UI\n\n1. Ask for one square feed image with a layout-only brief (the spring feed image in Example flows). Do not ask the agent to read your ad account.\n2. Confirm the agent checked the brand, avoided colour and mood words, did not invent a price or offer, and returned `imageUrl` plus crop and mode.\n3. Download that file. In Meta Ads Manager, upload it as a new ad image, or in Google Ads upload it as an image asset. Confirm the image shows in the library.\n4. Do not launch an ad if you are only proving the handoff. Leave the test for a person after the image looks right.\n5. Send a second brief that only says \"make the ad premium and moody\". The agent should ask what is physically in the frame instead of generating.\n\n### Stage 6: Optional image-library upload\n\nSkip this stage if people will upload stills by hand. Use it only when the advertiser's own credential is already in the secret store and the human named the account. Stop when the library accepts the file. Do not create, edit, launch, or pause an ad.\n\n**Meta Marketing API**\n\n1. Use an app and an OAuth access token the advertiser already owns. Brandiseer is not that app and does not mint the token. See [Marketing API authorization](https://developers.facebook.com/docs/marketing-api/overview/authorization/).\n2. Download the PNG bytes from `imageUrl`. Base64-encode those bytes.\n3. `POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages`. The adimages reference checked for this guide lists v25.0 as the current version on that page. `ad_account_id` is the numeric id, so the path contains the `act_` prefix. The documented body parameter for the file is `bytes`, a base64 string. `copy_from` copies an image between ad accounts. Do not use it to upload a new Brandiseer file. See [Ad account ad images](https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/).\n4. Marketing API reference examples pass the OAuth token as the form field `access_token`. A `190` on this edge means invalid OAuth 2.0 access token. A `200` means permissions error.\n5. Read `hash` from the image struct under `images`. Keep that hash for the person. Do not call an ad creative, ad, ad set, or campaign endpoint.\n\n**Google Ads API**\n\n1. You need both a developer token and an OAuth access token. The developer token is sent on every call in the `developer-token` header. A manager account finds it at [ads.google.com/aw/apicenter](https://ads.google.com/aw/apicenter). The OAuth scope is `https://www.googleapis.com/auth/adwords`. Brandiseer does not create the Google Cloud client and does not hold the developer token. See [REST auth](https://developers.google.com/google-ads/api/rest/auth).\n2. Download the PNG bytes from `imageUrl`. Base64-encode those bytes. `imageAsset.data` is the raw bytes, not the Brandiseer URL.\n3. `POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate` with `Content-Type: application/json`, `Authorization: Bearer ACCESS_TOKEN`, and `developer-token`. Customer ids have no hyphens (`1234567890`, not `123-456-7890`). If a manager account is calling a client account, also send `login-customer-id` with the manager's customer id. If the OAuth user is a direct user of the target client, omit `login-customer-id`. The REST examples page checked for this guide uses version `25`. See [Upload binary assets](https://developers.google.com/google-ads/api/rest/examples).\n4. Body: one `operations` item whose `create` sets `name`, `type` to `IMAGE`, and `imageAsset.data` to the base64 bytes. Use a unique descriptive name. The mutate result includes the new asset's resource name. Image assets cannot be changed or removed programmatically after upload. See [Working with assets](https://developers.google.com/google-ads/api/docs/assets/working-with-assets).\n5. Stop. Do not link the asset to a campaign, an ad group, or a Performance Max asset group. A 16:9 Brandiseer file is not a 1.91:1 landscape marketing image. The person crops before they use it that way, then launches themselves."},{"id":"prompting-rules","heading":"Prompting rules for Brandiseer images","markdown":"When the agent builds `prompt`:\n\n- Keep subject, layout, composition, and on-image text (what the words say and where they sit).\n- Never send brand colours, fonts, mood, or visual style. Brand memory already applies those. Style words fight the brand and produce off-brand stills.\n- Put crop in `aspectRatio`, quality in `mode`, pixel size in `resolution`, and logo on/off in `useLogo`. Those are body fields, not prompt prose.\n- Keep on-image text short. One short line is enough. Leave it out when the ad headline will live in the ad platform, not on the image.\n- Do not invent a price, a discount, a date, or an offer. If the human did not supply the exact line, the image has no offer text.\n- On `9:16`, leave an empty band at the top and an empty band at the bottom so Stories and Reels UI (profile, buttons, captions) does not cover the subject. Stories and Reels safe zones differ. This guide does not invent a pixel inset. Keep the subject in the middle band.\n- Map the placement to a documented Brandiseer ratio. Supported values are `1:1`, `16:9`, `9:16`, and `4:3`. Square feed: `1:1`. Stories or Reels: `9:16`. Landscape display: `16:9`. Seasonal or offer variant: the same ratio as the placement it refreshes.\n- Feed portrait `4:5` is not a Brandiseer `aspectRatio`. Do not send `4:5`. If the human wants a 4:5 feed crop, generate `9:16` and let them crop, or stay on `1:1` for the square feed. Say which one you used.\n- Google Display and Performance Max landscape marketing images are 1.91:1 (recommended 1200 by 628, minimum 600 by 314). Brandiseer has no 1.91:1 ratio. Generate `16:9` and let the person crop. Square marketing images are 1:1, which Brandiseer can generate directly. The ratio is checked when someone later links the asset, not during the library upload. See [Performance Max asset requirements](https://developers.google.com/google-ads/api/performance-max/asset-requirements).\n- `4:3` is a real Brandiseer ratio. None of the placements above require it. Use `4:3` only when the human asks for that frame.\n- For a test set, generate 3 to 4 variants per placement. Change what is in the frame or how it is arranged. Do not differentiate variants with colour, mood, or an invented offer.\n- Prefer `design` for finished ad images. `fast` is the API default if you omit `mode`, and it is the right fallback if a design job fails once.\n- Prefer `2K` for these stills. Use `1K` when file size or speed matters. Use `4K` only when the brief asks for it.\n- Set `useLogo` true only when the brief asked for a logo (or your default says so).\n- Full rules: [Prompt compatibility](https://brandiseer.com/guide/image)."},{"id":"system-prompts","heading":"System prompt","markdown":"Paste this into Cursor or Cowork as the standing instructions for ad image refreshes. Replace the placeholders before the first generate.\n\n```text\nYou generate on-brand ad images for paid social and display placements.\nBrandiseer returns an image URL. You do not create, edit, launch, or pause\nads, campaigns, ad sets, or creatives. You do not read ad metrics. You do\nnot sync Meta or Google Ads. You do not claim an ad account changed unless\nan image-library call you actually performed returned success.\n\nBrandiseer:\n- MCP: https://brandiseer.com/api/mcp with Authorization: Bearer.\n  Tools you may call: brandiseer_list_brands, brandiseer_check_credits,\n  brandiseer_generate_image. Do not invent tool names.\n- REST: X-API-Key: bds_... (secret store only).\n  Prefer POST https://brandiseer.com/api/v2/generate\n  or POST https://brandiseer.com/api/v2/generate/async\n  then poll GET https://brandiseer.com/api/v2/jobs/:jobId every 3 to 5 seconds\n  until data.status is done or failed, or about 10 minutes have passed.\n- Live contract: https://brandiseer.com/guide/docs and\n  https://brandiseer.com/guide/image\n- Supported aspectRatio values: 1:1, 16:9, 9:16, 4:3. Do not send 4:5\n  or 1.91:1. Brandiseer does not host a Meta app or a Google Ads app.\n  There is no ads MCP.\n\nDefaults for this workspace (fill in once):\n- Brand ID: bds_c_...\n- Square feed aspectRatio: 1:1\n- Stories or Reels aspectRatio: 9:16\n- Landscape display aspectRatio: 16:9\n- Seasonal or offer variant: same ratio as the placement it refreshes\n- Feed portrait 4:5: not supported. Ask, then use 9:16 for a crop the\n  human will make, or stay on 1:1. Never send 4:5.\n- mode: design\n- resolution: 2K (use 1K if file size or speed matters)\n- useLogo when the brief is silent: false\n- Variants per placement when the human asks for a test set: 3 to 4\n- Multi-brand map, if any: brand name to bds_c_...\n\nOn every request:\n1. Resolve brandId from the brief or the default. If more than one brand\n   exists and the brief does not name one, ask. Never fall through to\n   another client's Brand ID.\n2. Fatigue numbers (frequency, CTR, spend) come from the human. Do not\n   call an ads API to read performance. If the brief has no placement,\n   ask which one. Do not invent the account.\n3. Check credits before a batch (brandiseer_check_credits or\n   GET https://brandiseer.com/api/v1/credits). A single still can proceed\n   if the last known balance covers the mode and resolution in the table\n   you were given. On 402, stop.\n4. Rewrite the brief into subject, layout, composition, and on-image text.\n   Strip colours, fonts, mood, and visual style. If stripping leaves no\n   physical subject, ask what is in the frame. Do not generate.\n5. On-image text stays short. Use only a line the human supplied. If they\n   did not give a price, discount, date, or offer, do not invent one.\n   On 9:16, keep the subject in the middle band and leave empty bands at\n   the top and bottom.\n6. Choose aspectRatio from the placement. Put resolution, mode, and\n   useLogo in API fields, not in the prompt. Square feed is 1:1. Stories\n   or Reels are 9:16. Landscape display is 16:9. Use 4:3 only when the\n   human names that frame.\n7. Generate one still per variant. Return imageUrl, aspectRatio, mode,\n   resolution, useLogo, and a placement note: which account and placement\n   the human named, and which path (Ads Manager or Google Ads UI upload,\n   Meta adimages hash, or Google Ads asset resource name).\n8. Do not tell the user an ad, campaign, or library changed unless the\n   library call you actually performed returned success.\n\nImage library, only when the human already asked for it:\n- Default path: the human downloads imageUrl and uploads it in Meta Ads\n  Manager or Google Ads, then builds or swaps the ad and launches the\n  test. You do not claim that happened. You do not launch.\n- Meta, library upload only:\n  POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages\n  Parameter bytes: base64 of the PNG downloaded from imageUrl.\n  Do not use copy_from for a new file.\n  Pass the advertiser's own OAuth access token. Marketing API reference\n  examples send it as the form field access_token.\n  Read hash from the struct under images. Stop. Do not create a creative,\n  ad, ad set, or campaign.\n  Docs: https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/\n- Google Ads, library upload only:\n  Headers: Authorization: Bearer ACCESS_TOKEN, developer-token, and\n  login-customer-id only when a manager is calling a client account.\n  Scope: https://www.googleapis.com/auth/adwords\n  Customer ids have no hyphens.\n  POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate\n  create.type = IMAGE\n  create.imageAsset.data = base64 raw bytes (not the imageUrl string)\n  create.name = a unique descriptive name\n  Keep the resource name from the mutate result. Stop. Do not link the\n  asset and do not create an ad. A 16:9 file still needs a human crop\n  before it is used as a 1.91:1 landscape marketing image.\n  Docs: https://developers.google.com/google-ads/api/rest/auth\n  https://developers.google.com/google-ads/api/rest/examples\n  https://developers.google.com/google-ads/api/docs/assets/working-with-assets\n\nOut of scope:\n- Reading frequency, CTR, spend, or any other ad metric.\n- Creating, editing, launching, or pausing ads, campaigns, ad sets, or creatives.\n- Slack replies, Buffer scheduling, and Zapier, Make, or n8n scenarios.\n- Drive, Slides, Linear, Airtable, Webflow, WordPress, and Framer handoffs\n  (use those guides).\n- Legal approval of claims, prices, or offers printed on the image.\n- GET https://brandiseer.com/api/v1/usage (browser session only).\n```\n\n### Prompt rewriter\n\nUse this when a brief arrives as messy notes. It does not call Brandiseer. It only cleans text before you put it in `prompt`. The word list is a starting filter, not an official dictionary. If a phrase still describes colour, type, mood, or look, delete it by hand. `4:5` and `1.91:1` are absent on purpose. They are not Brandiseer ratios.\n\n```javascript\n// Local rewriter for a Cursor or Cowork helper script.\n// Keeps subject, layout, composition, and on-image text.\n// Strips colour, type, mood, and visual-style language.\n\nconst STYLE_WORDS =\n  /\\b(colou?rs?|palette|hex|fonts?|typefaces?|typography|serif|sans-serif|moods?|vibes?|aesthetics?|minimalist|luxury|playful|cinematic|pastel|neon|gradients?|moody|elegant|premium|on-brand)\\b/gi;\n\nconst SLOT_TO_RATIO = {\n  feed: \"1:1\",\n  \"square feed\": \"1:1\",\n  square: \"1:1\",\n  \"1:1\": \"1:1\",\n  story: \"9:16\",\n  stories: \"9:16\",\n  reel: \"9:16\",\n  reels: \"9:16\",\n  \"story reel\": \"9:16\",\n  \"stories reels\": \"9:16\",\n  vertical: \"9:16\",\n  portrait: \"9:16\",\n  \"9:16\": \"9:16\",\n  landscape: \"16:9\",\n  display: \"16:9\",\n  banner: \"16:9\",\n  \"landscape display\": \"16:9\",\n  \"16:9\": \"16:9\",\n  \"4:3\": \"4:3\",\n};\n\nfunction rewriteBrandiseerPrompt(input) {\n  const headline = String(input.headline || \"\").trim();\n  const subject = String(input.subject || input.brief || \"\").trim();\n  const slotKey = String(input.slot || input.crop || \"\").trim().toLowerCase();\n  const aspectRatio = SLOT_TO_RATIO[slotKey] || \"\";\n\n  let prompt = [subject, headline ? `On-image text: \"${headline}\".` : \"\"]\n    .filter(Boolean)\n    .join(\" \");\n\n  prompt = prompt\n    .replace(/#[0-9a-f]{3,8}\\b/gi, \" \")\n    .replace(STYLE_WORDS, \" \")\n    .replace(/\\s+/g, \" \")\n    .trim();\n\n  return {\n    prompt,\n    aspectRatio,\n    needsSubject: prompt.length < 12 || !aspectRatio,\n  };\n}\n```\n\nIf `needsSubject` is true, ask the human for a subject and a placement. Do not POST. A seasonal or offer variant is not its own key. Pass the placement it refreshes (`square feed`, `story`, or `landscape display`) and put only a human-supplied line in `headline`. If the human asked for 4:5 or 1.91:1, do not invent a ratio. Ask, then use `9:16` or `16:9` and say the person will crop. Use `4:3` only when the human names that frame."},{"id":"add-ons","heading":"Add-ons","markdown":"Add these after the feed-image smoke test passes. They do not change Brandiseer's image contract, and they do not give Brandiseer an ads sync.\n\n### Approval before anything launches\n\nAfter `imageUrl` is in the image library, a named reviewer checks the still. Until that happens, do not launch. Brandiseer does not approve legal claims, prices, or offers. Ad review policies stay with Meta or Google. Brandiseer does not know those policies and does not submit an ad for review.\n\n### One brand per client account\n\nKeep a table: Meta ad account id or Google Ads customer id, secret name, and `bds_c_...`. Require a named account on every library write when more than one client exists. If the name misses, stop and list the clients you configured. Confirm Brand IDs with `GET /api/v1/brands`. Never fall through to another client's Brand ID or another account's token.\n\n### Credit soft-limit\n\nWhen `credits` is under a threshold you choose (for example 40), switch new ad images to `mode: fast` and `resolution: 1K`, or skip them. Fast at 1K costs 4 credits. An explicit \"run anyway\" from the human can override the skip. The threshold is yours, not a Brandiseer default.\n\n### Placement log\n\nKeep a short note next to the work: account id, placement, variant, `brandId`, `mode`, `aspectRatio`, `resolution`, `imageUrl`, and whether a person uploaded it, a Meta `hash` came back, or a Google Ads resource name came back. Brandiseer does not write this log. Meta and Google Ads do not send it back to Brandiseer.\n\n### Handoff to the sibling guides\n\n- Deck stills in Google Slides or Drive: [Drive & Slides deck visuals](/use-cases/drive-slides-deck-visuals).\n- Issue and project stills in Linear: [Linear issue visuals](/use-cases/linear-issue-visuals).\n- Record stills on Airtable Attachment fields: [Airtable record visuals](/use-cases/airtable-record-visuals).\n- Stills requested in Slack, returned in a thread: [Slack creative desk](/use-cases/slack-creative-desk).\n- Organic captions and a Buffer schedule: [Social media automation](/use-cases/social-media-automation). That path is not a paid ad refresh.\n- A Zapier, Make, or n8n trigger that should deliver `imageUrl` on a schedule: [Always-on creative automation](/use-cases/always-on-creative-automation).\n- Webflow Assets or CMS: [Webflow site visuals](/use-cases/webflow-site-visuals).\n- WordPress Media Library: [WordPress site visuals](/use-cases/wordpress-site-visuals).\n- Framer Assets, canvas, or CMS: [Framer site visuals](/use-cases/framer-site-visuals).\n\nDo not embed a Slides deck, a Linear issue, an Airtable base, a Buffer schedule, a Slack desk, an always-on HTTP scenario, Webflow, WordPress, or Framer inside this ad creative guide.\n\n### Optional motion (not the refresh MVP)\n\nVideo is image-to-video only. Generate the still first, then send its public `imageUrl` to `POST https://brandiseer.com/api/v2.1/video/generate`. Poll `GET https://brandiseer.com/api/v2.1/video/jobs/:jobId`. Video jobs use `status` `completed` and a `videoUrl`, which is a different contract from image jobs (`done` and `imageUrl`). Read [guide/video](https://brandiseer.com/guide/video) for tier, duration, and resolution before you enable this. Video credits are per second and are not the still table below. A video failure does not undo the still. An ad can hold that video only after a person uploads it in the ad platform. This guide does not document a video-ad create call. Leave motion off unless the brief explicitly asks to animate."},{"id":"limits-and-faq","heading":"Limits & FAQ","markdown":"### Does Brandiseer create or launch the ad?\n\nNo. Brandiseer returns `imageUrl`. A person downloads that file and uploads it in Meta Ads Manager or Google Ads, then builds or swaps the ad and launches the test. An agent with the advertiser's own credentials can upload the bytes into the image library only. None of those paths create, edit, launch, or pause an ad, and none of them sync the ad account back to Brandiseer.\n\n### Is there a Brandiseer app for Meta or Google Ads?\n\nNo. There is no Brandiseer Meta app and no Brandiseer Google Ads app in this guide. Do not look for a native \"send to Ads Manager\" button on the Brandiseer side.\n\n### Do I need an ads MCP?\n\nNo. This guide uses the Ads Manager or Google Ads UI, or the documented image-library calls with the advertiser's own credentials. Do not invent an ads MCP. Brandiseer still only returns the still.\n\n### Can the agent read frequency or CTR?\n\nNo. Spotting fatigue is the person, in their own Ads Manager or Google Ads reporting, or by pasting numbers into the brief. Brandiseer does not read ad performance. This guide does not document an insights or reporting call.\n\n### Which Meta call is in this guide?\n\nOnly the image-library upload confirmed on Meta's ad account ad images reference:\n\n- `POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages`\n- Parameter `bytes`: the image file as a base64 string\n- The return struct includes `hash`, `url`, `url_128`, `url_256`, `url_256_height`, `url_256_width`, `height`, `width`, and `name`, under `images`\n- Error `100` invalid parameter, `200` permissions error, `190` invalid OAuth 2.0 access token, `80004` too many calls to this ad account, `368` the action was deemed abusive or is otherwise disallowed, `613` rate limit\n\n`copy_from` is a different operation (copy an existing hash between ad accounts). This guide does not use it for a new file. This guide does not call ad creative, ad, ad set, or campaign endpoints. Docs: [Ad account ad images](https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/).\n\n### Which Google Ads call is in this guide?\n\nOnly the image asset upload confirmed on Google's REST examples and asset docs:\n\n- `POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate`\n- Headers: `Authorization: Bearer ACCESS_TOKEN`, `developer-token`, and `login-customer-id` when a manager calls a client account\n- Body: `operations[].create` with `type` `IMAGE`, `name`, and `imageAsset.data` (base64 raw bytes)\n- The mutate result includes the new asset's resource name\n- OAuth scope: `https://www.googleapis.com/auth/adwords`\n\nThis guide does not link the asset and does not create or pause an ad. An uploaded image asset cannot be changed or removed programmatically. Docs: [REST auth](https://developers.google.com/google-ads/api/rest/auth) · [REST examples](https://developers.google.com/google-ads/api/rest/examples) · [Working with assets](https://developers.google.com/google-ads/api/docs/assets/working-with-assets).\n\n### Where do the keys live?\n\nThe Brandiseer key (`bds_...`), the Meta OAuth token, and the Google Ads developer token plus OAuth token live in the agent host's secret store. Never in an ad, a doc, a Slack message, a prompt you commit, or a workflow export in git. Those ad-platform credentials belong to the advertiser. Brandiseer does not store them. Rotate any secret if it leaks. Poll a Brandiseer job with the same Brandiseer key that submitted it.\n\n### What do stills cost?\n\nCredits per still. Failures refund. Async submits deduct immediately and refund if the job fails.\n\n| Mode | 1K | 2K | 4K |\n| --- | --- | --- | --- |\n| Fast | 4 | 6 | 8 |\n| Design | 5 | 6 | 8 |\n| Pro | 6 | 8 | 10 |\n\n`402` means not enough credits. Pricing context: [brandiseer.com/pricing](https://brandiseer.com/pricing).\n\n### Why is the still off-brand?\n\nAlmost always because the prompt contained colours, fonts, mood, or visual style. Strip them and generate again. See [guide/image](https://brandiseer.com/guide/image).\n\n### Why does the crop look wrong in the placement?\n\nBrandiseer aspect ratios are `1:1`, `16:9`, `9:16`, and `4:3`. Generate the square feed at `1:1`, Stories and Reels at `9:16`, and landscape display at `16:9`. Brandiseer cannot emit `4:5` or `1.91:1`. Crop those in the ad platform.\n\nInstagram stream ads recommend `1:1` and also allow `4:5` and `1.91:1`. Instagram Stories recommend `9:16`. The minimum image width called out on the Instagram ads media requirements page is 600 px for Stories. If a Story image sits between `4:5` and `9:16` and you provide no crop spec, Instagram crops it to `4:5`. See [Media requirements](https://developers.facebook.com/docs/instagram/ads-api/reference/media-requirements/).\n\nGoogle landscape marketing images must be 1.91:1 (recommended 1200 by 628, minimum 600 by 314, maximum file size 5120 KB). Square marketing images must be 1:1 (recommended 1200 by 1200, minimum 300 by 300, same file cap). Linking a 16:9 file as a landscape marketing image returns `ASPECT_RATIO_NOT_ALLOWED`. That check happens when the asset is linked, not when it is uploaded. See [Performance Max asset requirements](https://developers.google.com/google-ads/api/performance-max/asset-requirements).\n\n### How long can I poll?\n\nAbout 10 minutes, then the Brandiseer job expires. Poll every 3 to 5 seconds. Stop on `done` or `failed` as well. Do not start a second job for the same variant while the first is still open, or you will pay twice.\n\n### Which generate URL should new ad images use?\n\n`POST /api/v2/generate` when the agent host waits long enough. `POST /api/v2/generate/async` when it does not. Legacy `POST /api/v1/generate` remains for older callers and only supports Fast and Pro via `proMode` boolean.\n\n### Why did the Meta upload fail?\n\nCheck the error code on the adimages edge. `100` is an invalid parameter (the `bytes` value is the usual suspect). `190` is an invalid OAuth 2.0 access token. `200` is a permissions error, which means this token cannot upload to that ad account. `80004` means too many calls to this ad account. `613` is a rate limit. `368` means the action was deemed abusive or is otherwise disallowed. The path must be `/act_{ad_account_id}/adimages` with the numeric id. Do not tell the human the library updated when `hash` is missing.\n\n### Why did the Google Ads upload fail?\n\nThe call needs `developer-token` and `Authorization: Bearer` with scope `https://www.googleapis.com/auth/adwords`. Customer ids omit hyphens. A manager calling a client account also sends `login-customer-id`. `imageAsset.data` must be the base64 raw bytes, not `imageUrl`. Image asset names should be unique. An image asset cannot be edited in place after it exists. This guide does not treat a failed mutate as a stored asset.\n\n### Does a library upload launch a test?\n\nNo. A Meta `hash` and a Google Ads resource name sit in the image library. A person still builds or swaps the ad, submits it to the platform's ad review, and launches. Ad review policies stay with Meta or Google. Brandiseer does not submit the ad and does not know the review result.\n\n### Does the agent approve claims on the image?\n\nNo. If the still has a price, a discount, or a legal line, a person reads it before anything launches. The words must be a line the human supplied. Brandiseer does not review the claim."},{"id":"demo","heading":"Demo / motion","markdown":"**Motion brief only. This is not an embed, and there is no walkthrough URL yet.**\n\n**Working title:** Ad creative refresh\n**Length:** 45 to 75 seconds\n\n**Story beats:**\n\n1. A person pastes a fatigue note and asks Cursor or Cowork for a square feed refresh with a layout-only brief\n2. The agent calls Brandiseer (MCP or REST v2) and shows an on-brand still\n3. `imageUrl` is downloaded and uploaded in Meta Ads Manager or Google Ads by a person\n4. Optional cut: a Meta `adimages` call returns a hash, and no ad is created\n5. A human launches a test only after the still looks right\n\n**On screen:** the agent transcript with the prompt (no colour or mood words, no invented offer), one finished still, and the image sitting in the ad account library. Blur the Brandiseer API key and any Meta or Google credential. Do not show a fake video player in the guide."},{"id":"additional-1-example-flows","heading":"Example flows","markdown":"### Flow A: One fatigued feed ad, uploaded by a person\n\nThe person has already seen frequency up and CTR down on one square feed ad, and they paste that note into the brief. The brief: \"Square feed refresh. Product centred, full item in frame. Empty margin around it. No on-image text. No logo. 1:1. I will upload it myself.\"\n\nThe agent selects the default `brandId`, sets `aspectRatio` to `1:1`, `mode` to `design`, `resolution` to `2K`, and `useLogo` to false, and returns `imageUrl`. It does not open Ads Manager to confirm the fatigue. You download the file, upload it as a new ad image, then build or swap the ad and launch the test yourself. Brandiseer did not create the ad.\n\n### Flow B: Variants across four placements\n\nThe brief lists four placements: square feed (`1:1`), Stories or Reels (`9:16`), landscape display (`16:9`), and a seasonal variant that reuses the square feed ratio. Each placement asks for 3 variants. Each variant says what is physically in the frame. No variant adds a price. The agent generates 12 stills and labels them with placement and variant. You upload each file in the ad platform UI. Twelve Design 2K stills cost 72 credits if all succeed. Failures refund. Four variants on each of the four placements would be 16 stills and 96 credits. Do not start that larger set unless the human asked for 4.\n\n### Flow C: Seasonal refresh with a line the human supplied\n\nThe brief: \"Seasonal refresh of the square feed. Same product on a table, one autumn leaf beside it. On-image text: Autumn hours. 1:1. No other words.\"\n\nThe agent keeps the line \"Autumn hours\" because the human wrote it, uses `1:1`, and returns `imageUrl`. It does not add a year, a discount, or a second line. You upload it and decide whether that seasonal ad replaces the fatigued one. If the brief had said \"seasonal\" and had not supplied the words, the agent would generate with no on-image offer and would ask before printing one.\n\n### Flow D: Optional Meta image library upload\n\nThe ad account already exists. You have asked for a library upload, and the advertiser's Marketing API token is in the secret store. The brief names a `1:1` feed image and gives `ad_account_id`. The agent generates the still, downloads the bytes, and `POST`s them as `bytes` to `/act_{ad_account_id}/adimages`. It returns the `hash` and stops. It does not create a creative or an ad. You use that hash later, or you ignore it and upload another way. If the call returns error `190` or `200`, the agent says so and does not claim the library changed.\n\n### Flow E: Credit check before a placement set\n\nBefore Flow B, the agent calls `brandiseer_check_credits` or `GET https://brandiseer.com/api/v1/credits` and reads `credits`. Twelve Design 2K stills need 72 credits. If the balance is lower, it stops or switches the batch to `fast` at `1K` (4 credits each) only if you asked for that fallback. `GET /api/v1/usage` is a browser session endpoint. Do not call it from the agent.\n\n### Flow F: Style-only brief rejected\n\nThe brief: \"Make the ad feel premium and moody, and pause the old one.\"\n\nThe agent does not generate. It asks which placement, what is physically in the frame, whether any words must appear on the image, and whether the logo is on. It does not pause an ad. Colour, font, and mood stay out of the prompt. It also refuses a brief whose only subject is a style word, and it refuses to invent a sale price so the variants look different."},{"id":"additional-2-related-use-cases","heading":"Related use cases","markdown":"- [Drive & Slides deck visuals](/use-cases/drive-slides-deck-visuals): Brandiseer stills placed on Google Slides slides or in Google Drive\n- [Linear issue visuals](/use-cases/linear-issue-visuals): Brandiseer stills placed on Linear issues and projects\n- [Airtable record visuals](/use-cases/airtable-record-visuals): Brandiseer stills placed on Airtable Attachment fields\n- [Always-on creative automation](/use-cases/always-on-creative-automation): Zapier, Make, or n8n calls Brandiseer REST and delivers `imageUrl` downstream\n- [Slack creative desk](/use-cases/slack-creative-desk): people request stills in Slack and get them back in-thread\n- [Social media automation](/use-cases/social-media-automation): on-brand images, captions, and organic Buffer scheduling (not paid ads)\n- [Webflow site visuals](/use-cases/webflow-site-visuals): Brandiseer stills placed in Webflow Assets or CMS\n- [WordPress site visuals](/use-cases/wordpress-site-visuals): Brandiseer stills placed in the WordPress Media Library\n- [Framer site visuals](/use-cases/framer-site-visuals): Brandiseer stills placed in Framer Assets, on the canvas, or on a CMS image field"},{"id":"additional-3-links","heading":"Links","markdown":"| Resource | URL |\n| --- | --- |\n| Brandiseer | https://brandiseer.com |\n| Developers | https://brandiseer.com/developers |\n| API reference (guide/docs) | https://brandiseer.com/guide/docs |\n| Image prompt compatibility | https://brandiseer.com/guide/image |\n| Video prompting (optional motion) | https://brandiseer.com/guide/video |\n| MCP endpoint (agent hosts) | https://brandiseer.com/api/mcp |\n| API keys | https://brandiseer.com/settings/api |\n| Pricing / credits | https://brandiseer.com/pricing |\n| Meta Ads Manager | https://adsmanager.facebook.com |\n| Meta: ad account ad images | https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/ |\n| Meta: Instagram ads media requirements | https://developers.facebook.com/docs/instagram/ads-api/reference/media-requirements/ |\n| Meta: Marketing API authorization | https://developers.facebook.com/docs/marketing-api/overview/authorization/ |\n| Google Ads | https://ads.google.com |\n| Google Ads: REST auth | https://developers.google.com/google-ads/api/rest/auth |\n| Google Ads: REST examples | https://developers.google.com/google-ads/api/rest/examples |\n| Google Ads: working with assets | https://developers.google.com/google-ads/api/docs/assets/working-with-assets |\n| Google Ads: Performance Max asset requirements | https://developers.google.com/google-ads/api/performance-max/asset-requirements |\n| Google Ads: ImageAsset | https://developers.google.com/google-ads/api/reference/rpc/v25/ImageAsset |\n| Google Ads API center (developer token) | https://ads.google.com/aw/apicenter |\n| Visual pack manifest | `assets/MANIFEST.md` |\n| Live: Drive & Slides deck visuals | https://blog.brandiseer.com/use-cases/drive-slides-deck-visuals |\n| Live: Linear issue visuals | https://blog.brandiseer.com/use-cases/linear-issue-visuals |\n| Live: Airtable record visuals | https://blog.brandiseer.com/use-cases/airtable-record-visuals |\n| Live: Slack creative desk | https://blog.brandiseer.com/use-cases/slack-creative-desk |\n| Live: Social media automation | https://blog.brandiseer.com/use-cases/social-media-automation |\n| Live: Always-on creative automation | https://blog.brandiseer.com/use-cases/always-on-creative-automation |\n| Live: Webflow site visuals | https://blog.brandiseer.com/use-cases/webflow-site-visuals |\n| Live: WordPress site visuals | https://blog.brandiseer.com/use-cases/wordpress-site-visuals |\n| Live: Framer site visuals | https://blog.brandiseer.com/use-cases/framer-site-visuals |\n| Sibling: Drive & Slides deck visuals | https://blog.brandiseer.com/use-cases/drive-slides-deck-visuals |\n| Sibling: Linear issue visuals | https://blog.brandiseer.com/use-cases/linear-issue-visuals |\n| Sibling: Airtable record visuals | https://blog.brandiseer.com/use-cases/airtable-record-visuals |\n| Sibling: Always-on creative automation | https://blog.brandiseer.com/use-cases/always-on-creative-automation |\n| Sibling: Slack creative desk | https://blog.brandiseer.com/use-cases/slack-creative-desk |\n| Sibling: Social media automation | https://blog.brandiseer.com/use-cases/social-media-automation |\n| Sibling: Webflow site visuals | https://blog.brandiseer.com/use-cases/webflow-site-visuals |\n| Sibling: WordPress site visuals | https://blog.brandiseer.com/use-cases/wordpress-site-visuals |\n| Sibling: Framer site visuals | https://blog.brandiseer.com/use-cases/framer-site-visuals |"},{"id":"additional-4-source","heading":"Source","markdown":"Public Use case README for an ad creative refresh. Brandiseer MCP and REST v2 image generation from Cursor or Claude Cowork, then a human downloads `imageUrl` and uploads it in Meta Ads Manager or Google Ads, or an agent with the advertiser's own credentials uploads the bytes into the ad account image library (Meta `POST /act_{ad_account_id}/adimages`, or Google Ads `assets:mutate` with an image asset). Aligned with [guide/docs](https://brandiseer.com/guide/docs) and [guide/image](https://brandiseer.com/guide/image) (5 October 2026), with Meta's ad account ad images reference, and with Google Ads REST auth, the REST binary-asset example, and the asset upload docs. Brandiseer returns `imageUrl` only. It does not create, edit, launch, or pause ads or campaigns, does not read ad performance, and does not sync. There is no Brandiseer Meta app, no Brandiseer Google Ads app, and no ads MCP. Distinct from Drive and Slides deck visuals, Linear issue visuals, Airtable record visuals, Slack creative desk, Buffer organic scheduling, Zapier/Make/n8n always-on HTTP, Webflow site visuals, WordPress site visuals, and Framer site visuals."},{"id":"api-reference","heading":"API reference","markdown":"Wiring reference for agents and HTTP modules. The sections above are the human path.\n\n### MCP (interactive stills in Cursor or Cowork)\n\nConnect `https://brandiseer.com/api/mcp` with `Authorization: Bearer` and the API key. The only tools this guide uses are:\n\n- `brandiseer_list_brands`\n- `brandiseer_check_credits`\n- `brandiseer_generate_image`\n\nDo not invent extra Brandiseer tool names. If the host needs explicit `mode`, `aspectRatio`, or async polling, use REST v2 below. MCP does not edit Meta or Google Ads. Do not invent an ads MCP.\n\n### REST (prefer v2 images)\n\nSend `X-API-Key: bds_...` on every authenticated Brandiseer call. There is no official SDK. Any HTTP client the agent host can run is enough.\n\n| Call | What you get |\n| --- | --- |\n| `POST https://brandiseer.com/api/v2/generate` | Sync still. `200` body `{ \"imageUrl\", \"creditsUsed\" }`. Design mode often takes 40 to 70 seconds. |\n| `POST https://brandiseer.com/api/v2/generate/async` | `202` body `{ \"data\": { \"jobId\" } }` (ids look like `bds_job_...`). Credits are deducted when you submit. |\n| `GET https://brandiseer.com/api/v2/jobs/:jobId` | Poll every 3 to 5 seconds with the same API key. `data.status` becomes `done` or `failed`. Jobs expire after about 10 minutes. Failures refund credits. |\n| `POST https://brandiseer.com/api/v1/generate` | Legacy only. `proMode` boolean. Fast and Pro only. Prefer v2. |\n| `GET https://brandiseer.com/api/v1/credits` | JSON includes `credits`, the current balance for that key. |\n| `GET https://brandiseer.com/api/v1/brands` | Brands for the key. Each brand `id` looks like `bds_c_...`. |\n| `GET https://brandiseer.com/api/v1/health` | `{ \"status\": \"ok\" }`. No API key. |\n\nImage body fields: `brandId`, `prompt`, optional `aspectRatio` (`1:1`, `16:9`, `9:16`, `4:3`), `resolution` (`1K`, `2K`, `4K`), `mode` (`fast`, `design`, or `pro`; default `fast` if you omit it), `useLogo`.\n\nSync status codes you should branch on: `200` success, `400` bad body, `401` bad key, `402` not enough credits, `404` brand not found, `500` server error.\n\nDone job body: `{ \"data\": { \"status\": \"done\", \"imageUrl\", \"creditsUsed\", \"referenceImage\" } }`. Hand `data.imageUrl` to the person, or download those bytes for a library upload. Failed job body: `{ \"data\": { \"status\": \"failed\", \"error\": { \"code\", \"message\" } } }`.\n\nCursor and Cowork can wait on a sync Design call when the session allows it. If the HTTP client times out under about 60 seconds, use async and poll. Do not point Meta or Google Ads at the Brandiseer MCP URL.\n\n### Meta ad image upload (library only)\n\nSend this only when the human asked for a library upload and the advertiser's own token is in the secret store. Brandiseer does not mint that token.\n\n| Call | What you get |\n| --- | --- |\n| `POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages` | Creates an ad image in that ad account's library. The documented file parameter is `bytes` (base64). The `images` node contains a struct with `hash`. |\n\n`ad_account_id` is the numeric id. The path includes the `act_` prefix. The version segment `v25.0` is the current version listed on the ad account ad images reference checked for this guide. If the advertiser's app is pinned to another version that same reference still documents, change only the version segment.\n\n| Parameter | Rule |\n| --- | --- |\n| `bytes` | Required for a new file. Base64 of the image bytes you downloaded from `imageUrl`. Not a remote URL. |\n| `copy_from` | Copies an existing image from another ad account (`source_account_id` and `hash`). Out of scope for a new Brandiseer file. |\n| `access_token` | The advertiser's own OAuth access token. Marketing API reference examples send it as a form field. Do not send the Brandiseer key here. |\n\nReturn struct fields, from the adimages reference: `hash`, `url`, `url_128`, `url_256`, `url_256_height`, `url_256_width`, `height`, `width`, `name`. Read `hash`. The map key inside `images` is chosen by Meta. Do not assume it is your filename.\n\nError codes on this edge: `100` invalid parameter, `200` permissions error, `190` invalid OAuth 2.0 access token, `368` abusive or disallowed, `613` rate limit, `80004` too many calls to this ad account.\n\nThis guide does not document creating an ad creative from `hash`. The hash is the library id a person, or their own tooling, uses later.\n\n### Google Ads image asset upload (library only)\n\nSend this only when the human asked for a library upload. You need the advertiser's developer token and OAuth access token. Brandiseer does not issue either.\n\n| Header | Rule |\n| --- | --- |\n| `Authorization` | `Bearer ACCESS_TOKEN`. Scope `https://www.googleapis.com/auth/adwords`. |\n| `developer-token` | Required on every Google Ads API call. From a manager account at [API center](https://ads.google.com/aw/apicenter). |\n| `login-customer-id` | The manager customer id, digits only, when a manager calls a client account. Omit it when the OAuth user is a direct user of the target client. |\n| `Content-Type` | `application/json` |\n\n| Call | What you get |\n| --- | --- |\n| `POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate` | Creates an image asset. `{customerId}` has no hyphens. The mutate result includes the new asset's resource name. |\n\n`imageAsset.data` is mutate-only raw bytes. In JSON, send standard base64 (padding is accepted; URL-safe base64 is also accepted on the REST examples page). Do not put `imageUrl` in `data`.\n\nThe REST examples page body sets `name`, `type` `IMAGE`, and `imageAsset.data`. The upload-image sample also sets `fileSize`, `mimeType`, and `fullSize` on the image asset. This guide does not treat those three as required, because the shorter REST example omits them.\n\nUnique names are required for image assets. If you supply an existing name, Google creates a new name by appending a unique string. If an existing image asset has the same content and a different name, the new name is dropped silently. After upload, an image asset cannot be changed or removed programmatically.\n\nThis guide does not call a campaign, ad group, asset group, or ad mutate. The resource name is what a person, or their own tooling, uses later. A 16:9 upload can sit in the library and still fail a later link as a landscape marketing image, because that link expects 1.91:1.\n\n### Field mapping\n\nBrandiseer generate body:\n\n| Incoming field | API | Rule |\n| --- | --- | --- |\n| Placement (square feed, Stories or Reels, landscape display, seasonal or offer) | `aspectRatio` | Square feed: `1:1`. Stories or Reels: `9:16`. Landscape display: `16:9`. Seasonal or offer: the placement's ratio. `4:5` and `1.91:1` are not sent. `4:3` only when the human names it. |\n| headline | inside `prompt` only | On-image text and placement. Not a separate body field. Only a line the human supplied. Leave it out when the ad platform will hold the headline. |\n| brandId | `brandId` | `bds_c_...`. Required. |\n| mode | `mode` | `fast`, `design`, or `pro`. Omit and the API uses `fast`. Prefer `design` for these stills. |\n| useLogo | `useLogo` | `true` or `false`. |\n| subject or brief | `prompt` | After the rewriter. If the cleaned text has no subject, do not call Brandiseer. |\n| resolution | `resolution` | `1K`, `2K`, or `4K`. Prefer `2K` for these stills. |\n\nLibrary placement. The account below is whatever the human named. Do not invent it.\n\n| Incoming value | Library | Rule |\n| --- | --- | --- |\n| PNG bytes downloaded from `imageUrl` | Meta `bytes`, or Google `imageAsset.data` | Base64. Not the URL string. |\n| Meta ad account id | path `act_{ad_account_id}` | Numeric id. Required for a Meta upload. |\n| Meta response `hash` | image library id | Stop here. Do not create a creative in this guide. |\n| Google customer id | path `customers/{customerId}` | Digits only. Required for a Google upload. |\n| Google asset name | `create.name` | Unique and descriptive. |\n| Google mutate resource name | image library id | Stop here. Do not link the asset in this guide. |\n\n### Example JSON\n\nUse this body for both Brandiseer sync and async. Only the URL and the response change.\n\n```json\n{\n  \"brandId\": \"bds_c_...\",\n  \"prompt\": \"Square feed ad. Product centred, full item in frame. Empty margin around the subject. No on-image text.\",\n  \"aspectRatio\": \"1:1\",\n  \"resolution\": \"2K\",\n  \"mode\": \"design\",\n  \"useLogo\": false\n}\n```\n\nSync: `POST https://brandiseer.com/api/v2/generate`. A Design 2K still costs 6 credits, so a success body looks like:\n\n```json\n{\n  \"imageUrl\": \"https://...\",\n  \"creditsUsed\": 6\n}\n```\n\nAsync: `POST https://brandiseer.com/api/v2/generate/async`. HTTP `202`:\n\n```json\n{\n  \"data\": {\n    \"jobId\": \"bds_job_...\"\n  }\n}\n```\n\nPoll `GET https://brandiseer.com/api/v2/jobs/bds_job_...` with the same `X-API-Key`. When it finishes:\n\n```json\n{\n  \"data\": {\n    \"status\": \"done\",\n    \"imageUrl\": \"https://...\",\n    \"creditsUsed\": 6,\n    \"referenceImage\": null\n  }\n}\n```\n\nWhen it fails (credits are refunded):\n\n```json\n{\n  \"data\": {\n    \"status\": \"failed\",\n    \"error\": {\n      \"code\": \"...\",\n      \"message\": \"...\"\n    }\n  }\n}\n```\n\nIgnore `referenceImage` for delivery. The ad account gets a file you downloaded from `imageUrl`.\n\nMeta library upload. `POST` form fields to `https://graph.facebook.com/v25.0/act_AD_ACCOUNT_ID/adimages`. `BYTES_BASE64` is the PNG you downloaded, not `imageUrl`.\n\n```text\nbytes=BYTES_BASE64\naccess_token=ADVERTISER_ACCESS_TOKEN\n```\n\nRead `hash` from the struct under `images`. The official return type is a map whose image struct includes `hash`, `url`, `height`, `width`, and `name`. Do not continue into an ad creative call.\n\nGoogle Ads library upload. `POST` this JSON to `https://googleads.googleapis.com/v25/customers/CUSTOMER_ID/assets:mutate` with `Authorization: Bearer ACCESS_TOKEN`, `developer-token`, and `Content-Type: application/json`. Add `login-customer-id` when a manager is calling a client. `DATA_BASE64` is the raw image, matching the REST examples page.\n\n```json\n{\n  \"operations\": [\n    {\n      \"create\": {\n        \"name\": \"Square feed refresh 1\",\n        \"type\": \"IMAGE\",\n        \"imageAsset\": {\n          \"data\": \"DATA_BASE64\"\n        }\n      }\n    }\n  ]\n}\n```\n\nA successful mutate includes the new asset's resource name. Do not chain a campaign or ad mutate onto this request.\n\nSlot prompts that stay layout-only:\n\n| Slot | aspectRatio | Prompt |\n| --- | --- | --- |\n| Square feed | `1:1` | Square feed ad. Product centred, full item in frame. Empty margin around the subject. No on-image text. |\n| Stories or Reels | `9:16` | Vertical story frame. Subject in the middle band. Empty band along the top. Empty band along the bottom. No on-image text. |\n| Landscape display | `16:9` | Landscape display frame. Subject left of centre. Empty right third. No on-image text. |\n| Seasonal or offer variant | same as the placement | Same frame as that placement. On-image text only when the human supplied the exact line. Otherwise no on-image text. |\n| Named 4:3 frame | `4:3` | 4:3 frame. Single subject centred, full item in frame. Empty margin around the subject. No on-image text. |"}],"actions":{"tryBrandiseer":"https://brandiseer.com","createApiKey":"https://brandiseer.com/settings/api","developerDocs":"https://brandiseer.com/developers","mcpEndpoint":"https://brandiseer.com/api/mcp"},"representations":{"html":"https://blog.brandiseer.com/use-cases/ad-creative-refresh","markdown":"https://blog.brandiseer.com/use-cases/ad-creative-refresh/agent.md","json":"https://blog.brandiseer.com/use-cases/ad-creative-refresh/guide.json"}}