Creative MCP
Ad creative refresh
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.
- BrandiseerBrand memory and on-brand ad images via MCP or REST
- Meta Ads ManagerFeed, Stories and Reels placements; optional image library upload via Marketing API
- Google AdsDisplay and Performance Max image assets
- CursorAgent host for Brandiseer MCP or REST
- Claude CoworkAgent host for Brandiseer MCP or REST
Brandiseer + Meta Ads Manager + Google Ads (+ Cursor or Claude Cowork)

By 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.
Brandiseer 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.
The 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.
People who need on-brand stills for Google Slides or Drive should use Drive & Slides deck visuals. People who keep issue and project stills in Linear should use Linear issue visuals. People who keep campaign records in Airtable should use Airtable record visuals. People who brief stills inside Slack should use Slack creative desk. Organic captions and Buffer scheduling should use 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. Webflow site stills should use Webflow site visuals. WordPress Media Library stills should use WordPress site visuals. Framer site stills should use Framer site visuals. This guide does not recreate any of those.
Who it's for
- 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
- Agencies that keep one Brandiseer Brand ID per client and one Meta ad account or Google Ads account per client
- Operators who can download an
imageUrland 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
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.
Why Brandiseer in this stack
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.
A 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.
This is a different job from the sibling guides:
- Drive & Slides deck visuals hands
imageUrlto a Google Slides slide or a Drive file. - Linear issue visuals hands
imageUrlto a Linear issue or project. - Airtable record visuals hands
imageUrlinto an Airtable Attachment field. - 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.
- 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.
- Always-on creative automation is the HTTP step in Zapier, Make, or n8n.
- Webflow site visuals hands
imageUrlinto Webflow Assets or CMS image fields. - WordPress site visuals hands
imageUrlinto the WordPress Media Library. - Framer site visuals hands
imageUrlinto Framer Assets, the canvas, or a CMS image field. - This guide generates paid-ad image variants and hands
imageUrlto a person, or to that person's own image-library upload. Brandiseer does not push into Meta or Google Ads by itself.

Stack
| Layer | Tool | Role |
|---|---|---|
| Brand visuals | Brandiseer MCP and/or REST v2 | Generate on-brand ad images. MCP uses Authorization: Bearer. REST uses X-API-Key: bds_... |
| Agent host (pick one) | Cursor | Read the brief, call Brandiseer, return imageUrl and a placement note |
| Agent host (pick one) | Claude Cowork | Same contract as Cursor for interactive ad images |
| 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 |
| 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 |

You 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.
Brandiseer docs: Developers · guide/docs · guide/image · Settings → API
Meta docs: Ad account ad images · Instagram ads media requirements · Marketing API authorization
Google Ads docs: REST auth · REST examples, upload binary assets · Working with assets · Performance Max asset requirements · ImageAsset
Endpoint tables and HTTP contracts are in API reference at the end of this guide.
Meta Ads Manager and Google Ads (placement, not generation)
Three practical paths. Use the first one until an image-library write is actually needed. Brandiseer is finished when it returns imageUrl.
- 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. - 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}/adimageswith the image as base64bytes. The response struct includeshash. 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. - 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:mutatewithtypeIMAGEandimageAsset.dataset 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.
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.
A person still reviews the image and launches the test.
How it works
Architecture, from a stale ad to a fresh image the person can test:
- 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.
- 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.
- Generate. The agent calls Brandiseer with
aspectRatio,resolution,mode, anduseLogoas fields. Square feed uses1:1. Stories and Reels use9:16. Landscape display uses16:9. Preferdesignand2K. imageUrlto ad library. Brandiseer returnsimageUrlonly. 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 Metahashor the Google Ads resource name. It does not claim an ad changed.- 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.

Connect the workflow
Give the agent access to Brandiseer
Create an API key for the runnable setup, or connect the hosted MCP endpoint when your agent supports MCP.
What you need
- Company website URL, if the brand is not in Brandiseer yet
- Brandiseer account with at least one finished brand profile
- API key (
bds_...) from Settings → API or Developers - Brand ID (
bds_c_...) for every brand the agent may call - Credits for the stills you actually generate (see the table in Limits & FAQ)
- Cursor or Claude Cowork, with Brandiseer MCP or a way to call REST
- A Meta ad account, a Google Ads account, or both, that you can upload images into
- 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
- 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
- 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 - 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
- Go to brandiseer.com. In the homepage input, enter your company website URL and click Try My Brand.
- Wait about one minute while Brandiseer analyses the site and picks a logo and visuals.
- Review those visuals carefully. They teach Brandiseer your look. Every later ad image is influenced by what you leave here.
- Remove anything off-brand (partner logos, stock, irrelevant screenshots). Add campaign creatives or product photography that match the look you want.
- Click Add Brand, sign up if prompted, then wait about 30 seconds for the brand profile to finish.
Agencies repeat this once per client. Do not point two client ad accounts at one Brand ID.
Stage 2: API key and Brand ID
- Open brandiseer.com/developers or Settings → API.
- 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. - Copy the Brand ID (
bds_c_...). - Confirm the key with
GET https://brandiseer.com/api/v1/creditsandGET https://brandiseer.com/api/v1/brands, headerX-API-Key. The credits JSON includescredits. The brands list includes each brandid. - Optional reachability check, no key required:
GET https://brandiseer.com/api/v1/healthreturns{ "status": "ok" }.
Read guide/docs and guide/image once while you write the standing instructions. Those pages are the live contract if a field name ever moves.
Stage 3: Connect Brandiseer to the agent host
Use Cursor or Cowork. One host is enough.
A. MCP
- Add the Brandiseer MCP server at
https://brandiseer.com/api/mcp. - Authenticate with
Authorization: Bearerand the API key, in the secret field your host provides. - Confirm the tool list shows
brandiseer_list_brands,brandiseer_check_credits, andbrandiseer_generate_image. If a fourth Brandiseer tool appears, ignore it until guide/docs documents it.
B. REST
- On Developers, open View Docs and use Copy for LLM if you want the reference inside the session.
- Tell the agent to prefer
POST https://brandiseer.com/api/v2/generateorPOST https://brandiseer.com/api/v2/generate/asyncwithX-API-Key. - If Design mode times out, switch that call to async and poll
GET https://brandiseer.com/api/v2/jobs/:jobIdevery 3 to 5 seconds.
Stage 4: Save the standing instructions
- Paste the System prompt from this guide into the Cursor rule or Cowork instructions that will run ad image refreshes.
- Fill the defaults the prompt asks for: Brand ID, default crops per placement, resolution, mode, and whether
useLogois on when the brief is silent. - 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.
- Ask the agent to call
brandiseer_list_brands(orGET /api/v1/brands) and confirm the Brand ID exists before the first paid still.
Stage 5: Smoke test one feed image in the Ads Manager UI
- 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.
- Confirm the agent checked the brand, avoided colour and mood words, did not invent a price or offer, and returned
imageUrlplus crop and mode. - 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.
- Do not launch an ad if you are only proving the handoff. Leave the test for a person after the image looks right.
- 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.
Stage 6: Optional image-library upload
Skip 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.
Meta Marketing API
- 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.
- Download the PNG bytes from
imageUrl. Base64-encode those bytes. 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_idis the numeric id, so the path contains theact_prefix. The documented body parameter for the file isbytes, a base64 string.copy_fromcopies an image between ad accounts. Do not use it to upload a new Brandiseer file. See Ad account ad images.- Marketing API reference examples pass the OAuth token as the form field
access_token. A190on this edge means invalid OAuth 2.0 access token. A200means permissions error. - Read
hashfrom the image struct underimages. Keep that hash for the person. Do not call an ad creative, ad, ad set, or campaign endpoint.
Google Ads API
- You need both a developer token and an OAuth access token. The developer token is sent on every call in the
developer-tokenheader. A manager account finds it at ads.google.com/aw/apicenter. The OAuth scope ishttps://www.googleapis.com/auth/adwords. Brandiseer does not create the Google Cloud client and does not hold the developer token. See REST auth. - Download the PNG bytes from
imageUrl. Base64-encode those bytes.imageAsset.datais the raw bytes, not the Brandiseer URL. POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutatewithContent-Type: application/json,Authorization: Bearer ACCESS_TOKEN, anddeveloper-token. Customer ids have no hyphens (1234567890, not123-456-7890). If a manager account is calling a client account, also sendlogin-customer-idwith the manager's customer id. If the OAuth user is a direct user of the target client, omitlogin-customer-id. The REST examples page checked for this guide uses version25. See Upload binary assets.- Body: one
operationsitem whosecreatesetsname,typetoIMAGE, andimageAsset.datato 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. - 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.
Prompting rules for Brandiseer images
When the agent builds prompt:
- Keep subject, layout, composition, and on-image text (what the words say and where they sit).
- Never send brand colours, fonts, mood, or visual style. Brand memory already applies those. Style words fight the brand and produce off-brand stills.
- Put crop in
aspectRatio, quality inmode, pixel size inresolution, and logo on/off inuseLogo. Those are body fields, not prompt prose. - 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.
- 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.
- 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. - Map the placement to a documented Brandiseer ratio. Supported values are
1:1,16:9,9:16, and4: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. - Feed portrait
4:5is not a BrandiseeraspectRatio. Do not send4:5. If the human wants a 4:5 feed crop, generate9:16and let them crop, or stay on1:1for the square feed. Say which one you used. - 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:9and 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. 4:3is a real Brandiseer ratio. None of the placements above require it. Use4:3only when the human asks for that frame.- 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.
- Prefer
designfor finished ad images.fastis the API default if you omitmode, and it is the right fallback if a design job fails once. - Prefer
2Kfor these stills. Use1Kwhen file size or speed matters. Use4Konly when the brief asks for it. - Set
useLogotrue only when the brief asked for a logo (or your default says so). - Full rules: Prompt compatibility.
System prompt
Paste this into Cursor or Cowork as the standing instructions for ad image refreshes. Replace the placeholders before the first generate.
You generate on-brand ad images for paid social and display placements.
Brandiseer returns an image URL. You do not create, edit, launch, or pause
ads, campaigns, ad sets, or creatives. You do not read ad metrics. You do
not sync Meta or Google Ads. You do not claim an ad account changed unless
an image-library call you actually performed returned success.
Brandiseer:
- MCP: https://brandiseer.com/api/mcp with Authorization: Bearer.
Tools you may call: brandiseer_list_brands, brandiseer_check_credits,
brandiseer_generate_image. Do not invent tool names.
- REST: X-API-Key: bds_... (secret store only).
Prefer POST https://brandiseer.com/api/v2/generate
or POST https://brandiseer.com/api/v2/generate/async
then poll GET https://brandiseer.com/api/v2/jobs/:jobId every 3 to 5 seconds
until data.status is done or failed, or about 10 minutes have passed.
- Live contract: https://brandiseer.com/guide/docs and
https://brandiseer.com/guide/image
- Supported aspectRatio values: 1:1, 16:9, 9:16, 4:3. Do not send 4:5
or 1.91:1. Brandiseer does not host a Meta app or a Google Ads app.
There is no ads MCP.
Defaults for this workspace (fill in once):
- Brand ID: bds_c_...
- Square feed aspectRatio: 1:1
- Stories or Reels aspectRatio: 9:16
- Landscape display aspectRatio: 16:9
- Seasonal or offer variant: same ratio as the placement it refreshes
- Feed portrait 4:5: not supported. Ask, then use 9:16 for a crop the
human will make, or stay on 1:1. Never send 4:5.
- mode: design
- resolution: 2K (use 1K if file size or speed matters)
- useLogo when the brief is silent: false
- Variants per placement when the human asks for a test set: 3 to 4
- Multi-brand map, if any: brand name to bds_c_...
On every request:
1. Resolve brandId from the brief or the default. If more than one brand
exists and the brief does not name one, ask. Never fall through to
another client's Brand ID.
2. Fatigue numbers (frequency, CTR, spend) come from the human. Do not
call an ads API to read performance. If the brief has no placement,
ask which one. Do not invent the account.
3. Check credits before a batch (brandiseer_check_credits or
GET https://brandiseer.com/api/v1/credits). A single still can proceed
if the last known balance covers the mode and resolution in the table
you were given. On 402, stop.
4. Rewrite the brief into subject, layout, composition, and on-image text.
Strip colours, fonts, mood, and visual style. If stripping leaves no
physical subject, ask what is in the frame. Do not generate.
5. On-image text stays short. Use only a line the human supplied. If they
did not give a price, discount, date, or offer, do not invent one.
On 9:16, keep the subject in the middle band and leave empty bands at
the top and bottom.
6. Choose aspectRatio from the placement. Put resolution, mode, and
useLogo in API fields, not in the prompt. Square feed is 1:1. Stories
or Reels are 9:16. Landscape display is 16:9. Use 4:3 only when the
human names that frame.
7. Generate one still per variant. Return imageUrl, aspectRatio, mode,
resolution, useLogo, and a placement note: which account and placement
the human named, and which path (Ads Manager or Google Ads UI upload,
Meta adimages hash, or Google Ads asset resource name).
8. Do not tell the user an ad, campaign, or library changed unless the
library call you actually performed returned success.
Image library, only when the human already asked for it:
- Default path: the human downloads imageUrl and uploads it in Meta Ads
Manager or Google Ads, then builds or swaps the ad and launches the
test. You do not claim that happened. You do not launch.
- Meta, library upload only:
POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages
Parameter bytes: base64 of the PNG downloaded from imageUrl.
Do not use copy_from for a new file.
Pass the advertiser's own OAuth access token. Marketing API reference
examples send it as the form field access_token.
Read hash from the struct under images. Stop. Do not create a creative,
ad, ad set, or campaign.
Docs: https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/
- Google Ads, library upload only:
Headers: Authorization: Bearer ACCESS_TOKEN, developer-token, and
login-customer-id only when a manager is calling a client account.
Scope: https://www.googleapis.com/auth/adwords
Customer ids have no hyphens.
POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate
create.type = IMAGE
create.imageAsset.data = base64 raw bytes (not the imageUrl string)
create.name = a unique descriptive name
Keep the resource name from the mutate result. Stop. Do not link the
asset and do not create an ad. A 16:9 file still needs a human crop
before it is used as a 1.91:1 landscape marketing image.
Docs: https://developers.google.com/google-ads/api/rest/auth
https://developers.google.com/google-ads/api/rest/examples
https://developers.google.com/google-ads/api/docs/assets/working-with-assets
Out of scope:
- Reading frequency, CTR, spend, or any other ad metric.
- Creating, editing, launching, or pausing ads, campaigns, ad sets, or creatives.
- Slack replies, Buffer scheduling, and Zapier, Make, or n8n scenarios.
- Drive, Slides, Linear, Airtable, Webflow, WordPress, and Framer handoffs
(use those guides).
- Legal approval of claims, prices, or offers printed on the image.
- GET https://brandiseer.com/api/v1/usage (browser session only).
Prompt rewriter
Use 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.
// Local rewriter for a Cursor or Cowork helper script.
// Keeps subject, layout, composition, and on-image text.
// Strips colour, type, mood, and visual-style language.
const STYLE_WORDS =
/\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;
const SLOT_TO_RATIO = {
feed: "1:1",
"square feed": "1:1",
square: "1:1",
"1:1": "1:1",
story: "9:16",
stories: "9:16",
reel: "9:16",
reels: "9:16",
"story reel": "9:16",
"stories reels": "9:16",
vertical: "9:16",
portrait: "9:16",
"9:16": "9:16",
landscape: "16:9",
display: "16:9",
banner: "16:9",
"landscape display": "16:9",
"16:9": "16:9",
"4:3": "4:3",
};
function rewriteBrandiseerPrompt(input) {
const headline = String(input.headline || "").trim();
const subject = String(input.subject || input.brief || "").trim();
const slotKey = String(input.slot || input.crop || "").trim().toLowerCase();
const aspectRatio = SLOT_TO_RATIO[slotKey] || "";
let prompt = [subject, headline ? `On-image text: "${headline}".` : ""]
.filter(Boolean)
.join(" ");
prompt = prompt
.replace(/#[0-9a-f]{3,8}\b/gi, " ")
.replace(STYLE_WORDS, " ")
.replace(/\s+/g, " ")
.trim();
return {
prompt,
aspectRatio,
needsSubject: prompt.length < 12 || !aspectRatio,
};
}
If 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.
Add-ons
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.
Approval before anything launches
After 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.
One brand per client account
Keep 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.
Credit soft-limit
When 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.
Placement log
Keep 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.
Handoff to the sibling guides
- Deck stills in Google Slides or Drive: Drive & Slides deck visuals.
- Issue and project stills in Linear: Linear issue visuals.
- Record stills on Airtable Attachment fields: Airtable record visuals.
- Stills requested in Slack, returned in a thread: Slack creative desk.
- Organic captions and a Buffer schedule: Social media automation. That path is not a paid ad refresh.
- A Zapier, Make, or n8n trigger that should deliver
imageUrlon a schedule: Always-on creative automation. - Webflow Assets or CMS: Webflow site visuals.
- WordPress Media Library: WordPress site visuals.
- Framer Assets, canvas, or CMS: Framer site visuals.
Do 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.
Optional motion (not the refresh MVP)
Video 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 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.
Limits & FAQ
Does Brandiseer create or launch the ad?
No. 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.
Is there a Brandiseer app for Meta or Google Ads?
No. 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.
Do I need an ads MCP?
No. 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.
Can the agent read frequency or CTR?
No. 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.
Which Meta call is in this guide?
Only the image-library upload confirmed on Meta's ad account ad images reference:
POST https://graph.facebook.com/v25.0/act_{ad_account_id}/adimages- Parameter
bytes: the image file as a base64 string - The return struct includes
hash,url,url_128,url_256,url_256_height,url_256_width,height,width, andname, underimages - Error
100invalid parameter,200permissions error,190invalid OAuth 2.0 access token,80004too many calls to this ad account,368the action was deemed abusive or is otherwise disallowed,613rate limit
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.
Which Google Ads call is in this guide?
Only the image asset upload confirmed on Google's REST examples and asset docs:
POST https://googleads.googleapis.com/v25/customers/{customerId}/assets:mutate- Headers:
Authorization: Bearer ACCESS_TOKEN,developer-token, andlogin-customer-idwhen a manager calls a client account - Body:
operations[].createwithtypeIMAGE,name, andimageAsset.data(base64 raw bytes) - The mutate result includes the new asset's resource name
- OAuth scope:
https://www.googleapis.com/auth/adwords
This 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 · REST examples · Working with assets.
Where do the keys live?
The 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.
What do stills cost?
Credits per still. Failures refund. Async submits deduct immediately and refund if the job fails.
| Mode | 1K | 2K | 4K |
|---|---|---|---|
| Fast | 4 | 6 | 8 |
| Design | 5 | 6 | 8 |
| Pro | 6 | 8 | 10 |
402 means not enough credits. Pricing context: brandiseer.com/pricing.
Why is the still off-brand?
Almost always because the prompt contained colours, fonts, mood, or visual style. Strip them and generate again. See guide/image.
Why does the crop look wrong in the placement?
Brandiseer 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.
Instagram 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.
Google 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.
How long can I poll?
About 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.
Which generate URL should new ad images use?
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.
Why did the Meta upload fail?
Check 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.
Why did the Google Ads upload fail?
The 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.
Does a library upload launch a test?
No. 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.
Does the agent approve claims on the image?
No. 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
Motion brief only. This is not an embed, and there is no walkthrough URL yet.
Working title: Ad creative refresh Length: 45 to 75 seconds
Story beats:
- A person pastes a fatigue note and asks Cursor or Cowork for a square feed refresh with a layout-only brief
- The agent calls Brandiseer (MCP or REST v2) and shows an on-brand still
imageUrlis downloaded and uploaded in Meta Ads Manager or Google Ads by a person- Optional cut: a Meta
adimagescall returns a hash, and no ad is created - A human launches a test only after the still looks right
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.
Example flows
Flow A: One fatigued feed ad, uploaded by a person
The 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."
The 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.
Flow B: Variants across four placements
The 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.
Flow C: Seasonal refresh with a line the human supplied
The 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."
The 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.
Flow D: Optional Meta image library upload
The 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 POSTs 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.
Flow E: Credit check before a placement set
Before 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.
Flow F: Style-only brief rejected
The brief: "Make the ad feel premium and moody, and pause the old one."
The 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.
Related use cases
- Drive & Slides deck visuals: Brandiseer stills placed on Google Slides slides or in Google Drive
- Linear issue visuals: Brandiseer stills placed on Linear issues and projects
- Airtable record visuals: Brandiseer stills placed on Airtable Attachment fields
- Always-on creative automation: Zapier, Make, or n8n calls Brandiseer REST and delivers
imageUrldownstream - Slack creative desk: people request stills in Slack and get them back in-thread
- Social media automation: on-brand images, captions, and organic Buffer scheduling (not paid ads)
- Webflow site visuals: Brandiseer stills placed in Webflow Assets or CMS
- WordPress site visuals: Brandiseer stills placed in the WordPress Media Library
- Framer site visuals: Brandiseer stills placed in Framer Assets, on the canvas, or on a CMS image field
Links
Source
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 and 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.
API reference
Wiring reference for agents and HTTP modules. The sections above are the human path.
MCP (interactive stills in Cursor or Cowork)
Connect https://brandiseer.com/api/mcp with Authorization: Bearer and the API key. The only tools this guide uses are:
brandiseer_list_brandsbrandiseer_check_creditsbrandiseer_generate_image
Do 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.
REST (prefer v2 images)
Send X-API-Key: bds_... on every authenticated Brandiseer call. There is no official SDK. Any HTTP client the agent host can run is enough.
| Call | What you get |
|---|---|
POST https://brandiseer.com/api/v2/generate | Sync still. 200 body { "imageUrl", "creditsUsed" }. Design mode often takes 40 to 70 seconds. |
POST https://brandiseer.com/api/v2/generate/async | 202 body { "data": { "jobId" } } (ids look like bds_job_...). Credits are deducted when you submit. |
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. |
POST https://brandiseer.com/api/v1/generate | Legacy only. proMode boolean. Fast and Pro only. Prefer v2. |
GET https://brandiseer.com/api/v1/credits | JSON includes credits, the current balance for that key. |
GET https://brandiseer.com/api/v1/brands | Brands for the key. Each brand id looks like bds_c_.... |
GET https://brandiseer.com/api/v1/health | { "status": "ok" }. No API key. |
Image 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.
Sync 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.
Done 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" } } }.
Cursor 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.
Meta ad image upload (library only)
Send 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.
| Call | What you get |
|---|---|
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. |
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.
| Parameter | Rule |
|---|---|
bytes | Required for a new file. Base64 of the image bytes you downloaded from imageUrl. Not a remote URL. |
copy_from | Copies an existing image from another ad account (source_account_id and hash). Out of scope for a new Brandiseer file. |
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. |
Return 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.
Error 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.
This guide does not document creating an ad creative from hash. The hash is the library id a person, or their own tooling, uses later.
Google Ads image asset upload (library only)
Send 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.
| Header | Rule |
|---|---|
Authorization | Bearer ACCESS_TOKEN. Scope https://www.googleapis.com/auth/adwords. |
developer-token | Required on every Google Ads API call. From a manager account at API center. |
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. |
Content-Type | application/json |
| Call | What you get |
|---|---|
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. |
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.
The 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.
Unique 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.
This 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.
Field mapping
Brandiseer generate body:
| Incoming field | API | Rule |
|---|---|---|
| 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. |
| 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. |
| brandId | brandId | bds_c_.... Required. |
| mode | mode | fast, design, or pro. Omit and the API uses fast. Prefer design for these stills. |
| useLogo | useLogo | true or false. |
| subject or brief | prompt | After the rewriter. If the cleaned text has no subject, do not call Brandiseer. |
| resolution | resolution | 1K, 2K, or 4K. Prefer 2K for these stills. |
Library placement. The account below is whatever the human named. Do not invent it.
| Incoming value | Library | Rule |
|---|---|---|
PNG bytes downloaded from imageUrl | Meta bytes, or Google imageAsset.data | Base64. Not the URL string. |
| Meta ad account id | path act_{ad_account_id} | Numeric id. Required for a Meta upload. |
Meta response hash | image library id | Stop here. Do not create a creative in this guide. |
| Google customer id | path customers/{customerId} | Digits only. Required for a Google upload. |
| Google asset name | create.name | Unique and descriptive. |
| Google mutate resource name | image library id | Stop here. Do not link the asset in this guide. |
Example JSON
Use this body for both Brandiseer sync and async. Only the URL and the response change.
{
"brandId": "bds_c_...",
"prompt": "Square feed ad. Product centred, full item in frame. Empty margin around the subject. No on-image text.",
"aspectRatio": "1:1",
"resolution": "2K",
"mode": "design",
"useLogo": false
}
Sync: POST https://brandiseer.com/api/v2/generate. A Design 2K still costs 6 credits, so a success body looks like:
{
"imageUrl": "https://...",
"creditsUsed": 6
}
Async: POST https://brandiseer.com/api/v2/generate/async. HTTP 202:
{
"data": {
"jobId": "bds_job_..."
}
}
Poll GET https://brandiseer.com/api/v2/jobs/bds_job_... with the same X-API-Key. When it finishes:
{
"data": {
"status": "done",
"imageUrl": "https://...",
"creditsUsed": 6,
"referenceImage": null
}
}
When it fails (credits are refunded):
{
"data": {
"status": "failed",
"error": {
"code": "...",
"message": "..."
}
}
}
Ignore referenceImage for delivery. The ad account gets a file you downloaded from imageUrl.
Meta 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.
bytes=BYTES_BASE64
access_token=ADVERTISER_ACCESS_TOKEN
Read 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.
Google 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.
{
"operations": [
{
"create": {
"name": "Square feed refresh 1",
"type": "IMAGE",
"imageAsset": {
"data": "DATA_BASE64"
}
}
}
]
}
A successful mutate includes the new asset's resource name. Do not chain a campaign or ad mutate onto this request.
Slot prompts that stay layout-only:
| Slot | aspectRatio | Prompt |
|---|---|---|
| Square feed | 1:1 | Square feed ad. Product centred, full item in frame. Empty margin around the subject. No on-image text. |
| 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. |
| Landscape display | 16:9 | Landscape display frame. Subject left of centre. Empty right third. No on-image text. |
| 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. |
| 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. |
Run it with your brand
Connect the workflow to brand memory.
Save your visual identity in Brandiseer, then use MCP or the API to generate from the same brand across every supported workflow.
