Creative MCP
Linear issue visuals
An agent generates on-brand stills with Brandiseer for Linear issues and projects (issue covers, project banners, cycle or roadmap stills, changelog or launch stills); a person then attaches those files in the Linear UI, or an agent with a Linear API key places the public imageUrl into issue or project markdown (or uploads bytes via Linear fileUpload). Brandiseer does not create Linear issues or projects and does not sync.
- BrandiseerBrand memory and on-brand stills via MCP or REST
- LinearIssues, projects, and file or markdown image placement
- CursorAgent host for Brandiseer MCP or REST (optional Linear GraphQL write)
- Claude CoworkAgent host for Brandiseer MCP or REST (optional Linear GraphQL write)
Brandiseer + Linear (+ Cursor or Claude Cowork)

By the end of this guide you will have a repeatable way to make on-brand stills for Linear issues and projects. An agent in Cursor or Claude Cowork calls Brandiseer, gets an imageUrl for an issue cover, a project banner, a cycle or roadmap still, or a changelog or launch still, and that file is placed on the issue or project.
Brandiseer owns the brand memory and the still. Linear owns the issues, the projects, and where the file sits. Brandiseer returns imageUrl only. It does not create Linear issues or projects, and it does not sync. There is no Brandiseer Linear app in this guide, and there is no Linear MCP in this guide. The default handoff is a person: download the file (or open imageUrl) and attach it in the Linear UI. Optionally, an agent that already holds a Linear API key can place that public URL in issue or project markdown, or upload the file bytes with Linear fileUpload and a server-side PUT. A person still decides which issue or project is final.
People who keep campaign records in Airtable should use Airtable record visuals. People who brief stills inside Slack should use Slack creative desk. Recurring captions and Buffer scheduling should use Social media automation. 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
- Product, marketing, and ops teams who already track work in Linear and need an on-brand still on an issue or project without opening a separate design tool for every ticket
- Agencies that keep one Brandiseer Brand ID per client and one Linear workspace (or a named set of teams) per client
- Operators who can download an
imageUrland attach it in the Linear UI, or who already give an agent a Linear API key and want that agent to place the public URL in markdown
Ideal customer profile: a team whose issues and projects already live in Linear, whose brand already lives in Brandiseer, and whose missing piece is a brand-safe still plus a clear handoff into the Linear UI or into markdown on an issue or project that already exists.
Why Brandiseer in this stack
Linear is where the issue and the project live. Cursor or Cowork is the agent that turns a brief into a still. Brandiseer stores the logo and visual identity once, then applies that memory on every still. The prompt carries subject, layout, composition, and on-image text. It does not restate colours, fonts, mood, or visual style.
A launch thread drifts when every cover is a one-off export from a general image tool. Brandiseer keeps the still on the same brand as the rest of the work. Linear still decides which issue, which project, and which markdown field hold the file.
This is a different job from the sibling guides:
- 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 posts with 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 issue and project stills and hands
imageUrlto Linear. Brandiseer does not push into Linear by itself.

Stack
| Layer | Tool | Role |
|---|---|---|
| Brand visuals | Brandiseer MCP and/or REST v2 | Generate on-brand stills. 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. Optional Linear GraphQL write |
| Agent host (pick one) | Claude Cowork | Same contract as Cursor for interactive issue and project stills |
| Work tracker | Linear | Issues, projects, and file or markdown image placement. A person attaches by default |

You only need one agent host. The Brandiseer contract is the same on both. There is no Brandiseer Linear app that drops a still onto an issue. The handoff is an image URL, a download, or a Linear GraphQL call that you perform with Linear's own API. This guide does not use a Linear MCP. Do not invent one.
Brandiseer docs: Developers · guide/docs · guide/image · Settings → API
Linear docs: GraphQL · How to upload a file · Attachments · SDK · API and webhooks
Endpoint tables and HTTP contracts are in API reference at the end of this guide.
Linear (placement, not generation)
Three practical paths. Use the first one until a GraphQL write is actually needed. A fourth Linear feature, attachmentCreate, is a different thing. It is listed here so an agent does not use it as a file upload.
- Linear UI (default). Download the file or open
imageUrl. On the issue or project the human named, attach that file in the Linear UI (drag it into the description, or upload it from the editor). Brandiseer stops at the URL. Linear stores its own file. - Markdown public URL (easiest API path). Include
in an issuedescription, a comment body, or projectcontent. Send that markdown withissueUpdate,commentCreate, orprojectUpdatetoPOST https://api.linear.app/graphql. Linear fetches the public image and stores it in Linear's private storage.imageUrlmust be publicly fetchable. See How to upload a file. This is a Linear call with the human's key. It is not a Brandiseer push. fileUpload, then PUT (server-side). Call thefileUpload(contentType, filename, size)mutation. PUT the raw bytes touploadFile.uploadUrlwithContent-Typeand every header inuploadFile.headers. Then useuploadFile.assetUrlin markdown on the issue, comment, or project. The PUT must run server-side. Linear's content security policy blocks a browser PUT. See the same upload guide.attachmentCreateis a link card, and it is out of scope for this still. It links an external resource (title,subtitle,url,iconUrl) the way a GitHub pull request card does.iconUrlis a small png or jpg icon. It is not the way to attach a Brandiseer PNG. Do not call it for these stills. Docs: Attachments.
issueUpdate replaces description. projectUpdate replaces content. Read the current markdown first when the human needs the existing text kept, then send the full markdown plus the new image. commentCreate adds a comment and leaves the description in place. That is the safer write when the issue body must stay as it is.
Project icon on projectUpdate is an icon name. It is not a custom cover upload, and there is no documented field in this guide that sets a project cover image URL for a Brandiseer still. For a project banner or thumbnail, put the markdown image in project content, or have a person upload the file in the Linear UI.
Cycle and roadmap stills, and changelog or launch stills, use the same handoff. Generate the still, then attach it in the UI or place the markdown image on the issue or project the human named. This guide does not document a cycle cover field or a changelog image field.
Files Linear has fetched or accepted live in Linear's private storage. Viewing those Linear-hosted URLs outside Linear requires authentication. Keep the Brandiseer imageUrl as the public URL until Linear has stored the file. See GraphQL (Accessing Images) and the upload guide.
How it works
Architecture, from brief to a still sitting on a Linear issue or project:
- You store the brand once in Brandiseer (logo and reference visuals teach the look).
- The agent host (Cursor or Cowork) has Brandiseer MCP or REST, the Brand ID (
bds_c_...), and the standing instructions in this guide. - You name the slot: issue cover, project banner, cycle or roadmap still, or changelog or launch still. The agent rewrites that brief into subject, layout, and on-image text, then sets
aspectRatio,resolution,mode, anduseLogoas fields. - Brandiseer returns
imageUrl. The agent reports the URL, the crop, the mode, and a one-line placement note (which issue or project, and whether the file is for the UI, a comment, a description, or project content). It does not claim Linear changed unless a Linear call you actually performed returned success. - A person attaches the file in the Linear UI, or an agent with a Linear API key writes the public
imageUrlinto markdown (or uploads the bytes). A person reviews the issue or project when the creative is ready.

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 Linear workspace you can edit, and the issue or project the still belongs on
- The issue id (UUID or identifier such as
BLA-123) or the project UUID for any GraphQL write. Copy a model UUID from Linear with the command menu: Cmd/Ctrl+K, then "Copy model UUID". See GraphQL. Do not guess ids - Optional, only for GraphQL writes: a Linear personal API key from Settings → Account → Security & access, or an OAuth access token. Personal keys can be full access or limited to Read, Write, Admin, Create issues, or Create comments, and they can be limited to specific teams. See API and webhooks. A description or project content write needs write access on that team. A key limited to Create comments can add a comment image and cannot rewrite an issue description
- Store the Linear key in the agent secret store. Never in an issue, 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 Linear still 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 workspaces 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 a Linear issue, a page, 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 Linear stills.
- Fill the defaults the prompt asks for: Brand ID, default crops per slot, resolution, mode, and whether
useLogois on when the brief is silent. - If GraphQL writes are in scope, also note that the Linear key lives in the secret store. Leave issue and project ids 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 an issue cover in the Linear UI
- Ask for one issue cover with a layout-only brief (the Spring cover in Example flows).
- Confirm the agent checked the brand, avoided colour and mood words, and returned
imageUrlplus crop and mode. - Open the Linear issue, attach that file in the UI, and confirm the image shows.
- Leave the issue unmarked as approved if you are only proving the handoff.
- Send a second brief that only says "make the cover premium and moody". The agent should ask what is physically in the frame instead of generating.
Stage 6: Optional Linear GraphQL write
Skip this stage if people will attach stills by hand. Use it only when a Linear API key is already in the secret store and the human named the issue or project.
- Create a personal API key in Linear at Settings → Account → Security & access. Give it access only to the teams the human named. For a description or project content write, the key needs write access. For a comment-only image, Create comments is enough. See API and webhooks. An OAuth access token is the other supported credential. Brandiseer is not the OAuth app.
- Send every Linear call as
POST https://api.linear.app/graphqlwithContent-Type: application/json. A personal API key uses the headerAuthorization: <API_KEY>with noBearerprefix. An OAuth access token usesAuthorization: Bearer <token>. See GraphQL. - Markdown public URL. Prefer
commentCreatewhen the issue description must stay untouched. Setbodyto markdown that includes. To put the image in the issue body, readdescriptionfirst, thenissueUpdatewith the full markdown. To put a project banner in the project document, readcontentfirst, thenprojectUpdatewith the full markdown. Do not sendiconunless the human asked to change the icon name. Never seticontoimageUrl. Linear fetches the public URL into private storage. If the URL is not publicly fetchable, this path fails. - Bytes upload. Only from a server. Call
fileUploadwithcontentType,filename, andsize(bytes). PUT the file touploadFile.uploadUrl. SetContent-Type, then copy every{ key, value }inuploadFile.headersonto that PUT. The official SDK example also setsCache-Control: public, max-age=31536000before applying the returned headers, so a returned header of the same name wins. A browser PUT is blocked by Linear's content security policy. A 403 on the PUT usually means a required header was left off. After the PUT succeeds, placeuploadFile.assetUrlin markdown withcommentCreate,issueUpdate, orprojectUpdate. See How to upload a file. - Confirm the mutation
successflag is true and the responseerrorsarray is absent. Then stop. A person reviews the issue or project. Do not callattachmentCreateto attach the PNG. Do not treat a Linear-hosted asset URL as a public CDN link.
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. - Map the Linear slot to a documented ratio. Issue cover or attachment still:
16:9or1:1, matching the frame the human named. Project banner or thumbnail (via project content markdown or the UI):16:9. Cycle or roadmap still:16:9. Changelog or launch still:16:9. Portrait, only when the brief truly needs it:9:16. Brandiseer also offers4:3. None of the slots above require it. Use4:3only when the human asks for that frame. - Prefer
designfor finished Linear stills.fastis the API default if you omitmode, and it is the right fallback if a design job fails once. - Prefer
1Kor2Kfor these stills unless the brief asks for4K. - Set
useLogotrue only when the brief asked for a logo (or your default says so). Titles that the Linear issue title will hold should stay off the image. Say "empty region" for that area instead of describing a font. - Full rules: Prompt compatibility.
System prompt
Paste this into Cursor or Cowork as the standing instructions for Linear stills. Replace the placeholders before the first generate.
You generate on-brand stills for Linear issues and projects. Brandiseer
returns an image URL. You do not create Linear issues or projects, you do
not sync Linear, and you do not claim an issue or project changed unless a
Linear 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
- Brandiseer does not host a Linear integration. There is no native
Brandiseer to Linear app in this guide, and there is no Linear MCP.
Defaults for this workspace (fill in once):
- Brand ID: bds_c_...
- Issue cover aspectRatio: ask if the frame is not known (16:9 or 1:1)
- Project banner aspectRatio: 16:9
- Cycle or roadmap still aspectRatio: 16:9
- Changelog or launch still aspectRatio: 16:9
- Portrait aspectRatio, only if the brief truly needs it: 9:16
- mode: design
- resolution: 2K (use 1K if file size or speed matters)
- useLogo when the brief is silent: false
- 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. 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.
3. 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.
4. Choose aspectRatio from the slot. Put resolution, mode, and useLogo in
API fields, not in the prompt.
5. Generate one still. Return imageUrl, aspectRatio, mode, resolution,
useLogo, and a placement note: which issue or project the human named,
and which path (UI attach, comment, issue description, or project
content).
6. Do not tell the user Linear changed unless a Linear GraphQL call you
actually performed returned success true and no errors array.
Linear placement, only when the human already asked for it:
- Default path: the human downloads imageUrl (or opens it) and attaches it
in the Linear UI. You do not claim that happened.
- Endpoint: POST https://api.linear.app/graphql
Personal API key header: Authorization: <API_KEY> (no Bearer).
OAuth access token header: Authorization: Bearer <token>.
Keys: Linear Settings, Account, Security & access.
Docs: https://linear.app/developers/graphql
https://linear.app/docs/api-and-webhooks
- Markdown public URL (easiest API path): put
 in issue description (issueUpdate),
a comment (commentCreate), or project content (projectUpdate).
Linear fetches that public image into private storage. imageUrl must be
publicly fetchable.
Docs: https://linear.app/developers/how-to-upload-a-file-to-linear
- issueUpdate replaces description. projectUpdate replaces content. Read
the current markdown first when existing text must stay, then send the
full markdown plus the new image. commentCreate adds a comment and leaves
the description in place.
- fileUpload then PUT (server-side only): fileUpload(contentType, filename,
size) then PUT bytes to uploadFile.uploadUrl with Content-Type and every
returned header. Use uploadFile.assetUrl in markdown afterwards. Linear
CSP blocks a client-side PUT.
- Do not use attachmentCreate to attach the PNG. attachmentCreate links an
external resource (title, subtitle, url, iconUrl), like a GitHub PR card.
Docs: https://linear.app/developers/attachments
- Project icon on projectUpdate is an icon name. Do not write imageUrl into
icon. There is no documented "set project cover image URL" field. Put
banners in project content markdown, or leave the upload to the human.
- Do not create issues or projects. Do not invent a Linear MCP.
- Linear-hosted file URLs are private. Keep imageUrl as the public URL
until Linear has stored the file.
Out of scope:
- Slack replies, Buffer scheduling, and Zapier, Make, or n8n scenarios.
- Airtable, Webflow, WordPress, and Framer handoffs (use those guides).
- Creating Linear issues, projects, cycles, or changelog entries.
- 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.
// 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 = {
issue: "16:9",
"issue cover": "16:9",
cover: "16:9",
attachment: "16:9",
"attachment still": "16:9",
"16:9": "16:9",
project: "16:9",
banner: "16:9",
"project banner": "16:9",
thumbnail: "16:9",
"project thumbnail": "16:9",
cycle: "16:9",
roadmap: "16:9",
"cycle still": "16:9",
"roadmap still": "16:9",
changelog: "16:9",
launch: "16:9",
"changelog still": "16:9",
"launch still": "16:9",
square: "1:1",
"1:1": "1:1",
portrait: "9:16",
"9:16": "9:16",
"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 slot. Do not POST. The cover and attachment keys above default to 16:9 only so a named cover can proceed. If the human has not confirmed 16:9 or 1:1 for an issue cover, ask before you generate. Use 9:16 only when the brief truly needs a portrait. Use 4:3 only when the human names that frame.
Add-ons
Add these after the cover smoke test passes. They do not change Brandiseer's image contract, and they do not give Brandiseer a Linear sync.
Approval before the issue is treated as final
After imageUrl is on the issue or project, a named reviewer checks the still in Linear. Until that happens, leave the issue status on its current workflow state. Brandiseer does not approve legal claims, prices, or offers. Brandiseer also does not know your workflow state names. Do not invent one, and do not move the issue to Done as part of generating a still.
One brand per client workspace
Keep a table: Linear workspace name, team ids the key may touch, secret name, and bds_c_.... Require a named issue or project on every GraphQL write when more than one workspace exists. If the name misses, stop and list the workspaces you configured. Confirm Brand IDs with GET /api/v1/brands. Never fall through to another client's Brand ID or another workspace's key.
Credit soft-limit
When credits is under a threshold you choose (for example 40), switch new Linear stills 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: issue or project id, slot, brandId, mode, aspectRatio, resolution, imageUrl, and whether a person attached it, a markdown URL write placed it, or a fileUpload PUT placed it. Brandiseer does not write this log. Linear does not send it back to Brandiseer.
Handoff to the sibling guides
- Record stills on Airtable Attachment fields: Airtable record visuals.
- Stills requested in Slack, returned in a thread: Slack creative desk.
- Captions and a Buffer schedule: Social media automation.
- 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 an Airtable base, a Buffer schedule, a Slack desk, an always-on HTTP scenario, Webflow, WordPress, or Framer inside this Linear guide.
Optional motion (not the issue 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. A Linear issue can hold the video file only after a person, or a Linear markdown or fileUpload write, puts that file on the issue or project. Leave this off unless the brief explicitly asks to animate.
Limits & FAQ
Does Brandiseer update the Linear issue?
No. Brandiseer returns imageUrl. A person attaches that file in the Linear UI, or an agent with a Linear API key places the public URL in markdown (or uploads the bytes through fileUpload). None of those paths create an issue or a project, and none of them sync Linear back to Brandiseer.
Is there a Brandiseer app inside Linear?
No. There is no Brandiseer Linear integration in this guide. Do not look for a native "send to Linear" button on the Brandiseer side.
Do I need a Linear MCP?
No. This guide uses the Linear UI, or the documented GraphQL API with a personal API key or an OAuth access token. Do not invent a Linear MCP tool. Brandiseer still only returns the still.
Which Linear calls are in this guide?
Only calls confirmed on Linear's developer docs:
- Endpoint:
POST https://api.linear.app/graphqlon GraphQL - Personal API key header:
Authorization: <API_KEY>(noBearer). OAuth access token header:Authorization: Bearer <token> - Markdown image on an existing issue description:
issueUpdatewithdescription. This replaces the description. Read it first if the current text must stay - Markdown image on a new comment:
commentCreatewithissueIdandbody. This leaves the description in place - Markdown image on existing project content:
projectUpdatewithcontent. This replaces the content. Read it first if the current text must stay. Do not seticonto an image URL - Direct byte upload:
fileUpload(contentType, filename, size), then a server-side PUT touploadFile.uploadUrl, thenuploadFile.assetUrlin markdown. Docs: How to upload a file
attachmentCreate is documented on Attachments and is out of scope here. It creates an external link card. It does not upload the Brandiseer PNG.
Issue ids, project ids, and team access come from your workspace. This guide does not invent a "set issue cover" Brandiseer endpoint, and it does not create issues or projects.
Linear's upload guide also shows a base64 data URL inside markdown. This guide does not use that path. Use a public imageUrl or fileUpload.
Where do the keys live?
The Brandiseer key (bds_...) and the Linear personal API key or OAuth token live in the agent host's secret store. Never in a Linear issue, a Slack message, a prompt you commit, or a workflow export in git. Create the Linear personal key at Settings → Account → Security & access. Limit it to the teams the human named. A description or project write needs write access. A comment-only image can use a key limited to Create comments. Rotate either secret if it leaks. Poll a Brandiseer job with the same Brandiseer key that submitted it. Send the Linear personal key as Authorization: <API_KEY> with no Bearer word. Send an OAuth token as Authorization: Bearer <token>.
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 on the issue?
Brandiseer aspect ratios are 1:1, 16:9, 9:16, and 4:3. Generate the issue cover at 16:9 or 1:1, and generate the project banner, the cycle or roadmap still, and the changelog or launch still at 16:9. Use 9:16 only for a real portrait slot. Crop in Linear if that view uses a tighter frame.
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 slot while the first is still open, or you will pay twice.
Which generate URL should new Linear work 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.
Do Linear-hosted image URLs stay public?
No. After Linear fetches a public markdown image, or after a fileUpload PUT, the file lives in Linear's private storage. Those Linear URLs need authentication outside the Linear app. The Brandiseer imageUrl is the public URL you hand to Linear. It must be publicly fetchable for the markdown path. See How to upload a file and Accessing Images on GraphQL.
What if the markdown image does not appear?
Check four things. The imageUrl must be public, because Linear fetches it. The personal key must be Authorization: <API_KEY> with no Bearer prefix, or the OAuth token must be Authorization: Bearer <token>. The key must be allowed to write that team (or to create comments, if you only added a comment). The mutation response must have success: true and no errors array. GraphQL can return HTTP 200 with errors while part of the payload is empty. If you replaced description or content without reading it first, restore the previous markdown and send the full text plus the image. A 403 on the byte PUT means the request missed headers from uploadFile.headers or ran in the browser. Do not tell the human the issue updated when success is false or errors is present.
Can I set a project cover image URL?
This guide has no documented Linear field that sets a custom project cover from a Brandiseer URL. projectUpdate.icon is an icon name. Put the banner in project content as a markdown image, or upload the file in the Linear UI.
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 the issue or project is treated as final. 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: Linear issue visuals Length: 45 to 75 seconds
Story beats:
- A person asks Cursor or Cowork for an issue cover with a layout-only brief
- The agent calls Brandiseer (MCP or REST v2) and shows an on-brand still
imageUrlis attached on a Linear issue in the UI- Optional cut: a comment is created with the public URL through Linear GraphQL, still unapproved
- A human reviews the issue only after the still looks right next to the title
On screen: the agent transcript with the prompt (no colour or mood words), one finished still, and the Linear issue with the image visible. Blur the Brandiseer API key and the Linear API key. Do not show a fake video player in the guide.
Example flows
Flow A: Issue cover, attached by a person
The brief: "Issue cover for the spring launch ticket. Product on the left. Leave the right half empty. The issue title will hold the headline. No logo on the image. 16:9."
The agent selects the default brandId, sets aspectRatio to 16:9, mode to design, resolution to 2K, and useLogo to false, and returns imageUrl. You download the file and attach it on that issue in the Linear UI. You mark the issue reviewed later, after the still looks right next to the title.
Flow B: Issue covers for a set of tickets
The brief lists four issues. Each one says what is in the frame and that the cover is square, with the issue title kept in Linear rather than on the image. The agent generates four stills, all 1:1, and labels them with the issue identifier. You attach each file on its issue. Four Design 2K stills cost 24 credits if all succeed. Failures refund.
Flow C: Comment image via public URL, not yet approved
The issue already exists. You have asked for an API write, and the Linear key is in the secret store with access to that team. The brief names a 16:9 cover. The agent generates the still, then commentCreate with a body of . Linear fetches the public file. You open the issue, confirm the image shows on the comment, and leave approval for a separate step. The issue description is unchanged because this path adds a comment. If you had used issueUpdate instead, the agent would have read description first and sent the existing markdown plus the image.
Flow D: Project banner, plus a cycle still and a launch still
The brief: "Project banner for the launch project. Product left of centre. On-image text Spring launch on the right. 16:9."
The agent uses 16:9 and says so in the placement note. You attach the file in the project UI, or the agent reads project content and projectUpdates the full markdown with . It does not write imageUrl into icon. icon stays whatever icon name the project already has, unless you asked to change that name.
A cycle or roadmap still, and a changelog or launch still, use the same 16:9 generation and the same handoff onto the issue or project you named. There is no cycle cover URL field and no changelog image field in this guide.
Flow E: Credit check before an issue set
Before Flow B, the agent calls brandiseer_check_credits or GET https://brandiseer.com/api/v1/credits and reads credits. Four Design 2K stills need 24 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
The brief: "Make the issue cover feel premium and moody."
The agent does not generate. It asks which issue, what is physically in the frame, whether any words must appear on the image, whether the frame is 16:9 or 1:1, and whether the logo is on. Colour, font, and mood stay out of the prompt.
Related use cases
- 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 Buffer scheduling
- 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 Linear issue visuals. Brandiseer MCP and REST v2 image generation from Cursor or Claude Cowork, then a human attaches imageUrl in the Linear UI, or an agent with a Linear API key places that public URL in issue or project markdown (or uploads bytes with fileUpload and a server-side PUT). Aligned with guide/docs and guide/image (1 October 2026), and with Linear's GraphQL, file upload, and attachments docs. Brandiseer returns imageUrl only. It does not create Linear issues or projects and does not sync. Distinct from Airtable record visuals, Slack creative desk, Buffer social 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 Linear. Do not invent a Linear 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 Linear. 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 Linear at the Brandiseer MCP URL.
Linear GraphQL (placement only)
Send every call to POST https://api.linear.app/graphql with Content-Type: application/json.
| Credential | Header |
|---|---|
| Personal API key | Authorization: <API_KEY> (no Bearer) |
| OAuth access token | Authorization: Bearer <token> |
Create personal keys at Settings → Account → Security & access. A key can be full access or restricted to Read, Write, Admin, Create issues, or Create comments, and it can be limited to specific teams. See API and webhooks and GraphQL.
Check the response errors array before you treat a call as successful. GraphQL can return HTTP 200 with errors. Also require success: true on these mutations.
| Call | What you get |
|---|---|
query issue(id:) | Current issue. id may be a UUID or an identifier such as BLA-123. Read description before an issueUpdate that must keep existing text. |
issueUpdate(id, input: { description }) | Replaces the issue description with the markdown you send. Include  in that markdown. |
commentCreate(input: { issueId, body }) | Adds a comment. body is markdown. This leaves description in place. |
query project(id:) | Current project. Use the project UUID from Copy model UUID. Read content before a projectUpdate that must keep existing text. icon is an icon name. |
projectUpdate(id, input: { content }) | Replaces project content with the markdown you send. Do not pass icon unless the human asked to change the icon name. Never set icon to imageUrl. |
fileUpload(contentType, filename, size) | Returns uploadFile.uploadUrl, uploadFile.assetUrl, and uploadFile.headers (key, value). size is the file size in bytes. |
Markdown public URL is the easiest API path. Linear fetches the image at that public URL into private storage. Docs: How to upload a file.
Byte upload, server-side only:
- Call
fileUpload. Ifsuccessis false oruploadFileis missing, stop. - PUT the raw bytes to
uploadFile.uploadUrl. SetContent-Typeto the same type you sent ascontentType. The SDK example also setsCache-Control: public, max-age=31536000. Then set every header fromuploadFile.headersso those values are on the request. - A browser PUT fails Linear's content security policy. Run the PUT on a server. A 403 usually means a returned header was missing.
- Place
uploadFile.assetUrlin markdown withcommentCreate,issueUpdate, orprojectUpdate. That URL points at Linear's private storage.
attachmentCreate is not in the table above on purpose. It links an external resource with title, subtitle, url, and iconUrl. Use it for a GitHub-style link card if you have that separate job. Do not use it to attach a Brandiseer PNG. Docs: Attachments.
The TypeScript SDK can call the same mutations, including fileUpload(contentType, filename, size). This guide does not require the SDK. A GraphQL HTTP client is enough. The SDK does not remove the server-side PUT rule.
This guide does not call issueCreate or projectCreate. Linear's upload doc shows issueCreate as an example of a markdown image. Placement here targets issues and projects the human already named.
Field mapping
Brandiseer generate body:
| Incoming field | API | Rule |
|---|---|---|
| Slot (issue cover, project banner, cycle or roadmap, changelog or launch) | aspectRatio | Issue cover: 16:9 or 1:1. Project banner: 16:9. Cycle or roadmap: 16:9. Changelog or launch: 16:9. Portrait: 9:16 only when needed. 4:3 only when the human names it. |
| headline | inside prompt only | On-image text and placement. Not a separate body field. Leave it out when the Linear issue title 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 or 1K for these stills. |
Linear placement. The issue and project below are whatever the human named. Do not invent them.
| Incoming value | Linear | Rule |
|---|---|---|
Public Brandiseer imageUrl |  inside markdown | Required for the markdown path. Linear fetches this URL. It must be public. |
| Issue description | issueUpdate input description | Replaces the description. Read the current value first when text must stay. |
| Comment | commentCreate input body, plus issueId | Adds a comment. Does not replace the description. |
| Project document | projectUpdate input content | Replaces project content. Read the current value first when text must stay. |
| Project icon | projectUpdate input icon | Icon name only. Do not write imageUrl here. There is no project cover image URL field in this guide. |
| File bytes | fileUpload contentType, filename, size, then PUT | Server-side PUT to uploadFile.uploadUrl with every returned header plus Content-Type. Then use uploadFile.assetUrl in markdown. |
| External link card | attachmentCreate | Out of scope for this PNG. Title, subtitle, url, and iconUrl. Not a file upload. |
Example JSON
Use this body for both Brandiseer sync and async. Only the URL and the response change.
{
"brandId": "bds_c_...",
"prompt": "Wide issue cover. Product centred on the left third. Empty right half for a title the Linear issue will hold. No on-image text.",
"aspectRatio": "16:9",
"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. Linear gets imageUrl (or a file you downloaded from it).
Comment image. POST this JSON to https://api.linear.app/graphql. Use Authorization: <API_KEY> for a personal key. This adds a comment and leaves the issue description in place.
{
"query": "mutation CommentCreate($issueId: String!, $body: String!) { commentCreate(input: { issueId: $issueId, body: $body }) { success comment { id } } }",
"variables": {
"issueId": "BLA-123",
"body": ""
}
}
Issue description. Read description first. This example sends existing text plus the new image, which replaces the description with that full string.
{
"query": "mutation IssueUpdate($id: String!, $description: String!) { issueUpdate(id: $id, input: { description: $description }) { success issue { id } } }",
"variables": {
"id": "BLA-123",
"description": "Existing issue text.\n\n"
}
}
Project content. Read content first. Do not include icon in this input.
{
"query": "mutation ProjectUpdate($id: String!, $content: String!) { projectUpdate(id: $id, input: { content: $content }) { success project { id } } }",
"variables": {
"id": "PROJECT_UUID",
"content": "Existing project text.\n\n"
}
}
fileUpload. size is the byte length. If introspection shows a different scalar for size, use that scalar. The arguments stay contentType, filename, and size.
{
"query": "mutation FileUpload($contentType: String!, $filename: String!, $size: Int!) { fileUpload(contentType: $contentType, filename: $filename, size: $size) { success uploadFile { uploadUrl assetUrl headers { key value } } } }",
"variables": {
"contentType": "image/png",
"filename": "spring-issue-cover.png",
"size": 1828983
}
}
Then PUT the bytes to uploadUrl from a server, with Content-Type: image/png and every returned header. Use assetUrl in the markdown examples above.
Slot prompts that stay layout-only:
| Slot | aspectRatio | Prompt |
|---|---|---|
| Issue cover | 16:9 | Wide issue cover. Product centred on the left third. Empty right half for a title the Linear issue will hold. No on-image text. |
| Issue cover | 1:1 | Square issue cover. Single subject centred, full item in frame. Plain surrounding space. No on-image text. |
| Project banner | 16:9 | Landscape project banner. Subject left of centre. On-image text: "Spring launch" in the right third. |
| Cycle or roadmap still | 16:9 | Landscape planning still. Three work areas left to right. Empty caption band along the bottom. No on-image text. |
| Changelog or launch still | 16:9 | Landscape launch still. Product left of centre. On-image text: "Spring launch" in the right third. |
| Portrait | 9:16 | Portrait still. Single subject in the upper two thirds. Empty band along the bottom. 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.
