{"schemaVersion":"1.0.0","kind":"brandiseer.use-case","id":"linear-issue-visuals","canonicalUrl":"https://blog.brandiseer.com/use-cases/linear-issue-visuals","contentHash":"8c64a1aa4383cce025d1f0db557c1551fbca2d0990372e56a0b6d48ea0f88277","metadata":{"title":"Linear issue visuals","outcome":"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.","category":"creative-mcp","featured":false,"timeEstimate":"~25 to 40 min setup","methods":["MCP","REST"],"hosts":["Cursor","Cowork"],"image":"https://blog.brandiseer.com/use-case-assets/linear-issue-visuals/cover-linear-issue-visuals.png","updated":"2026-10-01"},"stack":[{"name":"Brandiseer","role":"Brand memory and on-brand stills via MCP or REST","logo":"/logo-b.png","href":"https://brandiseer.com"},{"name":"Linear","role":"Issues, projects, and file or markdown image placement","href":"https://linear.app"},{"name":"Cursor","role":"Agent host for Brandiseer MCP or REST (optional Linear GraphQL write)"},{"name":"Claude Cowork","role":"Agent host for Brandiseer MCP or REST (optional Linear GraphQL write)"}],"workflow":{"audience":"- 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\n- Agencies that keep one Brandiseer Brand ID per client and one Linear workspace (or a named set of teams) per client\n- Operators who can download an `imageUrl` and 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\n\n**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.","rationale":"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.\n\nA 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.\n\nThis is a different job from the sibling guides:\n\n- [Airtable record visuals](/use-cases/airtable-record-visuals) hands `imageUrl` into an Airtable Attachment field.\n- [Slack creative desk](/use-cases/slack-creative-desk) is a request-and-return desk. A person writes a brief in Slack and an agent posts a still back in the thread.\n- [Social media automation](/use-cases/social-media-automation) writes captions and schedules posts with Buffer.\n- [Always-on creative automation](/use-cases/always-on-creative-automation) is the HTTP step in Zapier, Make, or n8n.\n- [Webflow site visuals](/use-cases/webflow-site-visuals) hands `imageUrl` into Webflow Assets or CMS image fields.\n- [WordPress site visuals](/use-cases/wordpress-site-visuals) hands `imageUrl` into the WordPress Media Library.\n- [Framer site visuals](/use-cases/framer-site-visuals) hands `imageUrl` into Framer Assets, the canvas, or a CMS image field.\n- This guide generates issue and project stills and hands `imageUrl` to Linear. Brandiseer does not push into Linear by itself.\n\n![Example strip: issue cover, project banner, cycle or roadmap still, and changelog or launch still](https://blog.brandiseer.com/use-case-assets/linear-issue-visuals/example-strip.png)","architecture":"Architecture, from brief to a still sitting on a Linear issue or project:\n\n1. You store the brand once in Brandiseer (logo and reference visuals teach the look).\n2. The agent host (Cursor or Cowork) has Brandiseer MCP or REST, the Brand ID (`bds_c_...`), and the standing instructions in this guide.\n3. 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`, and `useLogo` as fields.\n4. 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.\n5. A person attaches the file in the Linear UI, or an agent with a Linear API key writes the public `imageUrl` into markdown (or uploads the bytes). A person reviews the issue or project when the creative is ready.\n\n![Workflow infographic: brief, Brandiseer still, imageUrl, attach in Linear, human reviews](https://blog.brandiseer.com/use-case-assets/linear-issue-visuals/workflow-infographic.png)","prerequisites":"- Company website URL, if the brand is not in Brandiseer yet\n- Brandiseer account with at least one finished brand profile\n- API key (`bds_...`) from [Settings → API](https://brandiseer.com/settings/api) or [Developers](https://brandiseer.com/developers)\n- Brand ID (`bds_c_...`) for every brand the agent may call\n- Credits for the stills you actually generate (see the table in Limits & FAQ)\n- Cursor or Claude Cowork, with Brandiseer MCP or a way to call REST\n- A Linear workspace you can edit, and the issue or project the still belongs on\n- 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](https://linear.app/developers/graphql). Do not guess ids\n- 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](https://linear.app/docs/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\n- 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\n\n1. Go to [brandiseer.com](https://brandiseer.com). In the homepage input, enter your company website URL and click **Try My Brand**.\n2. Wait about one minute while Brandiseer analyses the site and picks a logo and visuals.\n3. Review those visuals carefully. They teach Brandiseer your look. Every later Linear still is influenced by what you leave here.\n4. Remove anything off-brand (partner logos, stock, irrelevant screenshots). Add campaign creatives or product photography that match the look you want.\n5. Click **Add Brand**, sign up if prompted, then wait about 30 seconds for the brand profile to finish.\n\nAgencies repeat this once per client. Do not point two client workspaces at one Brand ID.\n\n### Stage 2: API key and Brand ID\n\n1. Open [brandiseer.com/developers](https://brandiseer.com/developers) or [Settings → API](https://brandiseer.com/settings/api).\n2. Create an API key (`bds_...`). Put it in the agent host's secret store. Do not paste it into a Linear issue, a page, Slack, or a git repo.\n3. Copy the Brand ID (`bds_c_...`).\n4. Confirm the key with `GET https://brandiseer.com/api/v1/credits` and `GET https://brandiseer.com/api/v1/brands`, header `X-API-Key`. The credits JSON includes `credits`. The brands list includes each brand `id`.\n5. Optional reachability check, no key required: `GET https://brandiseer.com/api/v1/health` returns `{ \"status\": \"ok\" }`.\n\nRead [guide/docs](https://brandiseer.com/guide/docs) and [guide/image](https://brandiseer.com/guide/image) once while you write the standing instructions. Those pages are the live contract if a field name ever moves.\n\n### Stage 3: Connect Brandiseer to the agent host\n\nUse Cursor or Cowork. One host is enough.\n\n**A. MCP**\n\n1. Add the Brandiseer MCP server at `https://brandiseer.com/api/mcp`.\n2. Authenticate with `Authorization: Bearer` and the API key, in the secret field your host provides.\n3. Confirm the tool list shows `brandiseer_list_brands`, `brandiseer_check_credits`, and `brandiseer_generate_image`. If a fourth Brandiseer tool appears, ignore it until [guide/docs](https://brandiseer.com/guide/docs) documents it.\n\n**B. REST**\n\n1. On [Developers](https://brandiseer.com/developers), open **View Docs** and use **Copy for LLM** if you want the reference inside the session.\n2. Tell the agent to prefer `POST https://brandiseer.com/api/v2/generate` or `POST https://brandiseer.com/api/v2/generate/async` with `X-API-Key`.\n3. If Design mode times out, switch that call to async and poll `GET https://brandiseer.com/api/v2/jobs/:jobId` every 3 to 5 seconds.\n\n### Stage 4: Save the standing instructions\n\n1. Paste the **System prompt** from this guide into the Cursor rule or Cowork instructions that will run Linear stills.\n2. Fill the defaults the prompt asks for: Brand ID, default crops per slot, resolution, mode, and whether `useLogo` is on when the brief is silent.\n3. 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.\n4. Ask the agent to call `brandiseer_list_brands` (or `GET /api/v1/brands`) and confirm the Brand ID exists before the first paid still.\n\n### Stage 5: Smoke test an issue cover in the Linear UI\n\n1. Ask for one issue cover with a layout-only brief (the Spring cover in Example flows).\n2. Confirm the agent checked the brand, avoided colour and mood words, and returned `imageUrl` plus crop and mode.\n3. Open the Linear issue, attach that file in the UI, and confirm the image shows.\n4. Leave the issue unmarked as approved if you are only proving the handoff.\n5. 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.\n\n### Stage 6: Optional Linear GraphQL write\n\nSkip 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.\n\n1. 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](https://linear.app/docs/api-and-webhooks). An OAuth access token is the other supported credential. Brandiseer is not the OAuth app.\n2. Send every Linear call as `POST https://api.linear.app/graphql` with `Content-Type: application/json`. A personal API key uses the header `Authorization: <API_KEY>` with no `Bearer` prefix. An OAuth access token uses `Authorization: Bearer <token>`. See [GraphQL](https://linear.app/developers/graphql).\n3. **Markdown public URL.** Prefer `commentCreate` when the issue description must stay untouched. Set `body` to markdown that includes `![alt](https://public-imageUrl)`. To put the image in the issue body, read `description` first, then `issueUpdate` with the full markdown. To put a project banner in the project document, read `content` first, then `projectUpdate` with the full markdown. Do not send `icon` unless the human asked to change the icon name. Never set `icon` to `imageUrl`. Linear fetches the public URL into private storage. If the URL is not publicly fetchable, this path fails.\n4. **Bytes upload.** Only from a server. Call `fileUpload` with `contentType`, `filename`, and `size` (bytes). PUT the file to `uploadFile.uploadUrl`. Set `Content-Type`, then copy every `{ key, value }` in `uploadFile.headers` onto that PUT. The official SDK example also sets `Cache-Control: public, max-age=31536000` before 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, place `uploadFile.assetUrl` in markdown with `commentCreate`, `issueUpdate`, or `projectUpdate`. See [How to upload a file](https://linear.app/developers/how-to-upload-a-file-to-linear).\n5. Confirm the mutation `success` flag is true and the response `errors` array is absent. Then stop. A person reviews the issue or project. Do not call `attachmentCreate` to attach the PNG. Do not treat a Linear-hosted asset URL as a public CDN link.","promptingRules":"When the agent builds `prompt`:\n\n- Keep subject, layout, composition, and on-image text (what the words say and where they sit).\n- Never send brand colours, fonts, mood, or visual style. Brand memory already applies those. Style words fight the brand and produce off-brand stills.\n- Put crop in `aspectRatio`, quality in `mode`, pixel size in `resolution`, and logo on/off in `useLogo`. Those are body fields, not prompt prose.\n- Map the Linear slot to a documented ratio. Issue cover or attachment still: `16:9` or `1: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 offers `4:3`. None of the slots above require it. Use `4:3` only when the human asks for that frame.\n- Prefer `design` for finished Linear stills. `fast` is the API default if you omit `mode`, and it is the right fallback if a design job fails once.\n- Prefer `1K` or `2K` for these stills unless the brief asks for `4K`.\n- Set `useLogo` true 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.\n- Full rules: [Prompt compatibility](https://brandiseer.com/guide/image).","systemPrompts":"Paste this into Cursor or Cowork as the standing instructions for Linear stills. Replace the placeholders before the first generate.\n\n```text\nYou generate on-brand stills for Linear issues and projects. Brandiseer\nreturns an image URL. You do not create Linear issues or projects, you do\nnot sync Linear, and you do not claim an issue or project changed unless a\nLinear call you actually performed returned success.\n\nBrandiseer:\n- MCP: https://brandiseer.com/api/mcp with Authorization: Bearer.\n  Tools you may call: brandiseer_list_brands, brandiseer_check_credits,\n  brandiseer_generate_image. Do not invent tool names.\n- REST: X-API-Key: bds_... (secret store only).\n  Prefer POST https://brandiseer.com/api/v2/generate\n  or POST https://brandiseer.com/api/v2/generate/async\n  then poll GET https://brandiseer.com/api/v2/jobs/:jobId every 3 to 5 seconds\n  until data.status is done or failed, or about 10 minutes have passed.\n- Live contract: https://brandiseer.com/guide/docs and\n  https://brandiseer.com/guide/image\n- Brandiseer does not host a Linear integration. There is no native\n  Brandiseer to Linear app in this guide, and there is no Linear MCP.\n\nDefaults for this workspace (fill in once):\n- Brand ID: bds_c_...\n- Issue cover aspectRatio: ask if the frame is not known (16:9 or 1:1)\n- Project banner aspectRatio: 16:9\n- Cycle or roadmap still aspectRatio: 16:9\n- Changelog or launch still aspectRatio: 16:9\n- Portrait aspectRatio, only if the brief truly needs it: 9:16\n- mode: design\n- resolution: 2K (use 1K if file size or speed matters)\n- useLogo when the brief is silent: false\n- Multi-brand map, if any: brand name to bds_c_...\n\nOn every request:\n1. Resolve brandId from the brief or the default. If more than one brand\n   exists and the brief does not name one, ask. Never fall through to\n   another client's Brand ID.\n2. Check credits before a batch (brandiseer_check_credits or\n   GET https://brandiseer.com/api/v1/credits). A single still can proceed\n   if the last known balance covers the mode and resolution in the table\n   you were given. On 402, stop.\n3. Rewrite the brief into subject, layout, composition, and on-image text.\n   Strip colours, fonts, mood, and visual style. If stripping leaves no\n   physical subject, ask what is in the frame. Do not generate.\n4. Choose aspectRatio from the slot. Put resolution, mode, and useLogo in\n   API fields, not in the prompt.\n5. Generate one still. Return imageUrl, aspectRatio, mode, resolution,\n   useLogo, and a placement note: which issue or project the human named,\n   and which path (UI attach, comment, issue description, or project\n   content).\n6. Do not tell the user Linear changed unless a Linear GraphQL call you\n   actually performed returned success true and no errors array.\n\nLinear placement, only when the human already asked for it:\n- Default path: the human downloads imageUrl (or opens it) and attaches it\n  in the Linear UI. You do not claim that happened.\n- Endpoint: POST https://api.linear.app/graphql\n  Personal API key header: Authorization: <API_KEY> (no Bearer).\n  OAuth access token header: Authorization: Bearer <token>.\n  Keys: Linear Settings, Account, Security & access.\n  Docs: https://linear.app/developers/graphql\n  https://linear.app/docs/api-and-webhooks\n- Markdown public URL (easiest API path): put\n  ![alt](https://public-imageUrl) in issue description (issueUpdate),\n  a comment (commentCreate), or project content (projectUpdate).\n  Linear fetches that public image into private storage. imageUrl must be\n  publicly fetchable.\n  Docs: https://linear.app/developers/how-to-upload-a-file-to-linear\n- issueUpdate replaces description. projectUpdate replaces content. Read\n  the current markdown first when existing text must stay, then send the\n  full markdown plus the new image. commentCreate adds a comment and leaves\n  the description in place.\n- fileUpload then PUT (server-side only): fileUpload(contentType, filename,\n  size) then PUT bytes to uploadFile.uploadUrl with Content-Type and every\n  returned header. Use uploadFile.assetUrl in markdown afterwards. Linear\n  CSP blocks a client-side PUT.\n- Do not use attachmentCreate to attach the PNG. attachmentCreate links an\n  external resource (title, subtitle, url, iconUrl), like a GitHub PR card.\n  Docs: https://linear.app/developers/attachments\n- Project icon on projectUpdate is an icon name. Do not write imageUrl into\n  icon. There is no documented \"set project cover image URL\" field. Put\n  banners in project content markdown, or leave the upload to the human.\n- Do not create issues or projects. Do not invent a Linear MCP.\n- Linear-hosted file URLs are private. Keep imageUrl as the public URL\n  until Linear has stored the file.\n\nOut of scope:\n- Slack replies, Buffer scheduling, and Zapier, Make, or n8n scenarios.\n- Airtable, Webflow, WordPress, and Framer handoffs (use those guides).\n- Creating Linear issues, projects, cycles, or changelog entries.\n- Legal approval of claims, prices, or offers printed on the image.\n- GET https://brandiseer.com/api/v1/usage (browser session only).\n```\n\n### Prompt rewriter\n\nUse this when a brief arrives as messy notes. It does not call Brandiseer. It only cleans text before you put it in `prompt`. The word list is a starting filter, not an official dictionary. If a phrase still describes colour, type, mood, or look, delete it by hand.\n\n```javascript\n// Local rewriter for a Cursor or Cowork helper script.\n// Keeps subject, layout, composition, and on-image text.\n// Strips colour, type, mood, and visual-style language.\n\nconst STYLE_WORDS =\n  /\\b(colou?rs?|palette|hex|fonts?|typefaces?|typography|serif|sans-serif|moods?|vibes?|aesthetics?|minimalist|luxury|playful|cinematic|pastel|neon|gradients?|moody|elegant|premium|on-brand)\\b/gi;\n\nconst SLOT_TO_RATIO = {\n  issue: \"16:9\",\n  \"issue cover\": \"16:9\",\n  cover: \"16:9\",\n  attachment: \"16:9\",\n  \"attachment still\": \"16:9\",\n  \"16:9\": \"16:9\",\n  project: \"16:9\",\n  banner: \"16:9\",\n  \"project banner\": \"16:9\",\n  thumbnail: \"16:9\",\n  \"project thumbnail\": \"16:9\",\n  cycle: \"16:9\",\n  roadmap: \"16:9\",\n  \"cycle still\": \"16:9\",\n  \"roadmap still\": \"16:9\",\n  changelog: \"16:9\",\n  launch: \"16:9\",\n  \"changelog still\": \"16:9\",\n  \"launch still\": \"16:9\",\n  square: \"1:1\",\n  \"1:1\": \"1:1\",\n  portrait: \"9:16\",\n  \"9:16\": \"9:16\",\n  \"4:3\": \"4:3\",\n};\n\nfunction rewriteBrandiseerPrompt(input) {\n  const headline = String(input.headline || \"\").trim();\n  const subject = String(input.subject || input.brief || \"\").trim();\n  const slotKey = String(input.slot || input.crop || \"\").trim().toLowerCase();\n  const aspectRatio = SLOT_TO_RATIO[slotKey] || \"\";\n\n  let prompt = [subject, headline ? `On-image text: \"${headline}\".` : \"\"]\n    .filter(Boolean)\n    .join(\" \");\n\n  prompt = prompt\n    .replace(/#[0-9a-f]{3,8}\\b/gi, \" \")\n    .replace(STYLE_WORDS, \" \")\n    .replace(/\\s+/g, \" \")\n    .trim();\n\n  return {\n    prompt,\n    aspectRatio,\n    needsSubject: prompt.length < 12 || !aspectRatio,\n  };\n}\n```\n\nIf `needsSubject` is true, ask the human for a subject and a 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.","addOns":"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.\n\n### Approval before the issue is treated as final\n\nAfter `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.\n\n### One brand per client workspace\n\nKeep 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.\n\n### Credit soft-limit\n\nWhen `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.\n\n### Placement log\n\nKeep 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.\n\n### Handoff to the sibling guides\n\n- Record stills on Airtable Attachment fields: [Airtable record visuals](/use-cases/airtable-record-visuals).\n- Stills requested in Slack, returned in a thread: [Slack creative desk](/use-cases/slack-creative-desk).\n- Captions and a Buffer schedule: [Social media automation](/use-cases/social-media-automation).\n- A Zapier, Make, or n8n trigger that should deliver `imageUrl` on a schedule: [Always-on creative automation](/use-cases/always-on-creative-automation).\n- Webflow Assets or CMS: [Webflow site visuals](/use-cases/webflow-site-visuals).\n- WordPress Media Library: [WordPress site visuals](/use-cases/wordpress-site-visuals).\n- Framer Assets, canvas, or CMS: [Framer site visuals](/use-cases/framer-site-visuals).\n\nDo not embed an Airtable base, a Buffer schedule, a Slack desk, an always-on HTTP scenario, Webflow, WordPress, or Framer inside this Linear guide.\n\n### Optional motion (not the issue MVP)\n\nVideo is image-to-video only. Generate the still first, then send its public `imageUrl` to `POST https://brandiseer.com/api/v2.1/video/generate`. Poll `GET https://brandiseer.com/api/v2.1/video/jobs/:jobId`. Video jobs use `status` `completed` and a `videoUrl`, which is a different contract from image jobs (`done` and `imageUrl`). Read [guide/video](https://brandiseer.com/guide/video) for tier, duration, and resolution before you enable this. Video credits are per second and are not the still table below. A video failure does not undo the still. 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.","limitsAndFaq":"### Does Brandiseer update the Linear issue?\n\nNo. 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.\n\n### Is there a Brandiseer app inside Linear?\n\nNo. There is no Brandiseer Linear integration in this guide. Do not look for a native \"send to Linear\" button on the Brandiseer side.\n\n### Do I need a Linear MCP?\n\nNo. 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.\n\n### Which Linear calls are in this guide?\n\nOnly calls confirmed on Linear's developer docs:\n\n- Endpoint: `POST https://api.linear.app/graphql` on [GraphQL](https://linear.app/developers/graphql)\n- Personal API key header: `Authorization: <API_KEY>` (no `Bearer`). OAuth access token header: `Authorization: Bearer <token>`\n- Markdown image on an existing issue description: `issueUpdate` with `description`. This replaces the description. Read it first if the current text must stay\n- Markdown image on a new comment: `commentCreate` with `issueId` and `body`. This leaves the description in place\n- Markdown image on existing project content: `projectUpdate` with `content`. This replaces the content. Read it first if the current text must stay. Do not set `icon` to an image URL\n- Direct byte upload: `fileUpload(contentType, filename, size)`, then a server-side PUT to `uploadFile.uploadUrl`, then `uploadFile.assetUrl` in markdown. Docs: [How to upload a file](https://linear.app/developers/how-to-upload-a-file-to-linear)\n\n`attachmentCreate` is documented on [Attachments](https://linear.app/developers/attachments) and is out of scope here. It creates an external link card. It does not upload the Brandiseer PNG.\n\nIssue 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.\n\nLinear's upload guide also shows a base64 data URL inside markdown. This guide does not use that path. Use a public `imageUrl` or `fileUpload`.\n\n### Where do the keys live?\n\nThe 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>`.\n\n### What do stills cost?\n\nCredits per still. Failures refund. Async submits deduct immediately and refund if the job fails.\n\n| Mode | 1K | 2K | 4K |\n| --- | --- | --- | --- |\n| Fast | 4 | 6 | 8 |\n| Design | 5 | 6 | 8 |\n| Pro | 6 | 8 | 10 |\n\n`402` means not enough credits. Pricing context: [brandiseer.com/pricing](https://brandiseer.com/pricing).\n\n### Why is the still off-brand?\n\nAlmost always because the prompt contained colours, fonts, mood, or visual style. Strip them and generate again. See [guide/image](https://brandiseer.com/guide/image).\n\n### Why does the crop look wrong on the issue?\n\nBrandiseer 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.\n\n### How long can I poll?\n\nAbout 10 minutes, then the Brandiseer job expires. Poll every 3 to 5 seconds. Stop on `done` or `failed` as well. Do not start a second job for the same slot while the first is still open, or you will pay twice.\n\n### Which generate URL should new Linear work use?\n\n`POST /api/v2/generate` when the agent host waits long enough. `POST /api/v2/generate/async` when it does not. Legacy `POST /api/v1/generate` remains for older callers and only supports Fast and Pro via `proMode` boolean.\n\n### Do Linear-hosted image URLs stay public?\n\nNo. 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](https://linear.app/developers/how-to-upload-a-file-to-linear) and Accessing Images on [GraphQL](https://linear.app/developers/graphql).\n\n### What if the markdown image does not appear?\n\nCheck 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.\n\n### Can I set a project cover image URL?\n\nThis 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.\n\n### Does the agent approve claims on the image?\n\nNo. If the still has a price, a discount, or a legal line, a person reads it before the issue or project is treated as final. Brandiseer does not review the claim.","demo":"**Motion brief only. This is not an embed, and there is no walkthrough URL yet.**\n\n**Working title:** Linear issue visuals\n**Length:** 45 to 75 seconds\n\n**Story beats:**\n\n1. A person asks Cursor or Cowork for an issue cover with a layout-only brief\n2. The agent calls Brandiseer (MCP or REST v2) and shows an on-brand still\n3. `imageUrl` is attached on a Linear issue in the UI\n4. Optional cut: a comment is created with the public URL through Linear GraphQL, still unapproved\n5. A human reviews the issue only after the still looks right next to the title\n\n**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."},"sections":[{"id":"overview","heading":"Overview","markdown":"**Brandiseer + Linear (+ Cursor or Claude Cowork)**\n\n![Cover: an on-brand still landing on a Linear issue](https://blog.brandiseer.com/use-case-assets/linear-issue-visuals/cover-linear-issue-visuals.png)\n\nBy 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.\n\nBrandiseer 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.\n\n> People who keep campaign records in Airtable should use [Airtable record visuals](/use-cases/airtable-record-visuals). People who brief stills inside Slack should use [Slack creative desk](/use-cases/slack-creative-desk). Recurring captions and Buffer scheduling should use [Social media automation](/use-cases/social-media-automation). Schedules and webhooks in Zapier, Make, or n8n should use [Always-on creative automation](/use-cases/always-on-creative-automation). Webflow site stills should use [Webflow site visuals](/use-cases/webflow-site-visuals). WordPress Media Library stills should use [WordPress site visuals](/use-cases/wordpress-site-visuals). Framer site stills should use [Framer site visuals](/use-cases/framer-site-visuals). This guide does not recreate any of those."},{"id":"audience","heading":"Who it's for","markdown":"- 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\n- Agencies that keep one Brandiseer Brand ID per client and one Linear workspace (or a named set of teams) per client\n- Operators who can download an `imageUrl` and 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\n\n**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."},{"id":"why-brandiseer","heading":"Why Brandiseer in this stack","markdown":"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.\n\nA 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.\n\nThis is a different job from the sibling guides:\n\n- [Airtable record visuals](/use-cases/airtable-record-visuals) hands `imageUrl` into an Airtable Attachment field.\n- [Slack creative desk](/use-cases/slack-creative-desk) is a request-and-return desk. A person writes a brief in Slack and an agent posts a still back in the thread.\n- [Social media automation](/use-cases/social-media-automation) writes captions and schedules posts with Buffer.\n- [Always-on creative automation](/use-cases/always-on-creative-automation) is the HTTP step in Zapier, Make, or n8n.\n- [Webflow site visuals](/use-cases/webflow-site-visuals) hands `imageUrl` into Webflow Assets or CMS image fields.\n- [WordPress site visuals](/use-cases/wordpress-site-visuals) hands `imageUrl` into the WordPress Media Library.\n- [Framer site visuals](/use-cases/framer-site-visuals) hands `imageUrl` into Framer Assets, the canvas, or a CMS image field.\n- This guide generates issue and project stills and hands `imageUrl` to Linear. Brandiseer does not push into Linear by itself.\n\n![Example strip: issue cover, project banner, cycle or roadmap still, and changelog or launch still](https://blog.brandiseer.com/use-case-assets/linear-issue-visuals/example-strip.png)"},{"id":"stack","heading":"Stack","markdown":"| Layer | Tool | Role |\n| --- | --- | --- |\n| Brand visuals | Brandiseer MCP and/or REST v2 | Generate on-brand stills. MCP uses `Authorization: Bearer`. REST uses `X-API-Key: bds_...` |\n| Agent host (pick one) | Cursor | Read the brief, call Brandiseer, return `imageUrl` and a placement note. Optional Linear GraphQL write |\n| Agent host (pick one) | Claude Cowork | Same contract as Cursor for interactive issue and project stills |\n| Work tracker | Linear | Issues, projects, and file or markdown image placement. A person attaches by default |\n\n![Stack diagram: Cursor or Cowork, Brandiseer, and Linear issue attachments](https://blog.brandiseer.com/use-case-assets/linear-issue-visuals/stack-diagram.png)\n\nYou 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.\n\n**Brandiseer docs:** [Developers](https://brandiseer.com/developers) · [guide/docs](https://brandiseer.com/guide/docs) · [guide/image](https://brandiseer.com/guide/image) · [Settings → API](https://brandiseer.com/settings/api)\n\n**Linear docs:** [GraphQL](https://linear.app/developers/graphql) · [How to upload a file](https://linear.app/developers/how-to-upload-a-file-to-linear) · [Attachments](https://linear.app/developers/attachments) · [SDK](https://linear.app/developers/sdk) · [API and webhooks](https://linear.app/docs/api-and-webhooks)\n\nEndpoint tables and HTTP contracts are in API reference at the end of this guide.\n\n### Linear (placement, not generation)\n\nThree 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.\n\n1. **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.\n2. **Markdown public URL (easiest API path).** Include `![alt](https://public-imageUrl)` in an issue `description`, a comment body, or project `content`. Send that markdown with `issueUpdate`, `commentCreate`, or `projectUpdate` to `POST https://api.linear.app/graphql`. Linear fetches the public image and stores it in Linear's private storage. `imageUrl` must be publicly fetchable. See [How to upload a file](https://linear.app/developers/how-to-upload-a-file-to-linear). This is a Linear call with the human's key. It is not a Brandiseer push.\n3. **`fileUpload`, then PUT (server-side).** Call the `fileUpload(contentType, filename, size)` mutation. PUT the raw bytes to `uploadFile.uploadUrl` with `Content-Type` and every header in `uploadFile.headers`. Then use `uploadFile.assetUrl` in 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.\n4. **`attachmentCreate` is 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. `iconUrl` is 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](https://linear.app/developers/attachments).\n\n`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.\n\nProject `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.\n\nCycle 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.\n\nFiles 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](https://linear.app/developers/graphql) (Accessing Images) and the [upload guide](https://linear.app/developers/how-to-upload-a-file-to-linear)."},{"id":"architecture","heading":"How it works","markdown":"Architecture, from brief to a still sitting on a Linear issue or project:\n\n1. You store the brand once in Brandiseer (logo and reference visuals teach the look).\n2. The agent host (Cursor or Cowork) has Brandiseer MCP or REST, the Brand ID (`bds_c_...`), and the standing instructions in this guide.\n3. 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`, and `useLogo` as fields.\n4. 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.\n5. A person attaches the file in the Linear UI, or an agent with a Linear API key writes the public `imageUrl` into markdown (or uploads the bytes). A person reviews the issue or project when the creative is ready.\n\n![Workflow infographic: brief, Brandiseer still, imageUrl, attach in Linear, human reviews](https://blog.brandiseer.com/use-case-assets/linear-issue-visuals/workflow-infographic.png)"},{"id":"prerequisites","heading":"What you need","markdown":"- Company website URL, if the brand is not in Brandiseer yet\n- Brandiseer account with at least one finished brand profile\n- API key (`bds_...`) from [Settings → API](https://brandiseer.com/settings/api) or [Developers](https://brandiseer.com/developers)\n- Brand ID (`bds_c_...`) for every brand the agent may call\n- Credits for the stills you actually generate (see the table in Limits & FAQ)\n- Cursor or Claude Cowork, with Brandiseer MCP or a way to call REST\n- A Linear workspace you can edit, and the issue or project the still belongs on\n- 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](https://linear.app/developers/graphql). Do not guess ids\n- 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](https://linear.app/docs/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\n- Store the Linear key in the agent secret store. Never in an issue, a prompt you commit, Slack, or git"},{"id":"setup","heading":"Setup","markdown":"### Stage 1: Add your brand on Brandiseer\n\n1. Go to [brandiseer.com](https://brandiseer.com). In the homepage input, enter your company website URL and click **Try My Brand**.\n2. Wait about one minute while Brandiseer analyses the site and picks a logo and visuals.\n3. Review those visuals carefully. They teach Brandiseer your look. Every later Linear still is influenced by what you leave here.\n4. Remove anything off-brand (partner logos, stock, irrelevant screenshots). Add campaign creatives or product photography that match the look you want.\n5. Click **Add Brand**, sign up if prompted, then wait about 30 seconds for the brand profile to finish.\n\nAgencies repeat this once per client. Do not point two client workspaces at one Brand ID.\n\n### Stage 2: API key and Brand ID\n\n1. Open [brandiseer.com/developers](https://brandiseer.com/developers) or [Settings → API](https://brandiseer.com/settings/api).\n2. Create an API key (`bds_...`). Put it in the agent host's secret store. Do not paste it into a Linear issue, a page, Slack, or a git repo.\n3. Copy the Brand ID (`bds_c_...`).\n4. Confirm the key with `GET https://brandiseer.com/api/v1/credits` and `GET https://brandiseer.com/api/v1/brands`, header `X-API-Key`. The credits JSON includes `credits`. The brands list includes each brand `id`.\n5. Optional reachability check, no key required: `GET https://brandiseer.com/api/v1/health` returns `{ \"status\": \"ok\" }`.\n\nRead [guide/docs](https://brandiseer.com/guide/docs) and [guide/image](https://brandiseer.com/guide/image) once while you write the standing instructions. Those pages are the live contract if a field name ever moves.\n\n### Stage 3: Connect Brandiseer to the agent host\n\nUse Cursor or Cowork. One host is enough.\n\n**A. MCP**\n\n1. Add the Brandiseer MCP server at `https://brandiseer.com/api/mcp`.\n2. Authenticate with `Authorization: Bearer` and the API key, in the secret field your host provides.\n3. Confirm the tool list shows `brandiseer_list_brands`, `brandiseer_check_credits`, and `brandiseer_generate_image`. If a fourth Brandiseer tool appears, ignore it until [guide/docs](https://brandiseer.com/guide/docs) documents it.\n\n**B. REST**\n\n1. On [Developers](https://brandiseer.com/developers), open **View Docs** and use **Copy for LLM** if you want the reference inside the session.\n2. Tell the agent to prefer `POST https://brandiseer.com/api/v2/generate` or `POST https://brandiseer.com/api/v2/generate/async` with `X-API-Key`.\n3. If Design mode times out, switch that call to async and poll `GET https://brandiseer.com/api/v2/jobs/:jobId` every 3 to 5 seconds.\n\n### Stage 4: Save the standing instructions\n\n1. Paste the **System prompt** from this guide into the Cursor rule or Cowork instructions that will run Linear stills.\n2. Fill the defaults the prompt asks for: Brand ID, default crops per slot, resolution, mode, and whether `useLogo` is on when the brief is silent.\n3. 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.\n4. Ask the agent to call `brandiseer_list_brands` (or `GET /api/v1/brands`) and confirm the Brand ID exists before the first paid still.\n\n### Stage 5: Smoke test an issue cover in the Linear UI\n\n1. Ask for one issue cover with a layout-only brief (the Spring cover in Example flows).\n2. Confirm the agent checked the brand, avoided colour and mood words, and returned `imageUrl` plus crop and mode.\n3. Open the Linear issue, attach that file in the UI, and confirm the image shows.\n4. Leave the issue unmarked as approved if you are only proving the handoff.\n5. 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.\n\n### Stage 6: Optional Linear GraphQL write\n\nSkip 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.\n\n1. 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](https://linear.app/docs/api-and-webhooks). An OAuth access token is the other supported credential. Brandiseer is not the OAuth app.\n2. Send every Linear call as `POST https://api.linear.app/graphql` with `Content-Type: application/json`. A personal API key uses the header `Authorization: <API_KEY>` with no `Bearer` prefix. An OAuth access token uses `Authorization: Bearer <token>`. See [GraphQL](https://linear.app/developers/graphql).\n3. **Markdown public URL.** Prefer `commentCreate` when the issue description must stay untouched. Set `body` to markdown that includes `![alt](https://public-imageUrl)`. To put the image in the issue body, read `description` first, then `issueUpdate` with the full markdown. To put a project banner in the project document, read `content` first, then `projectUpdate` with the full markdown. Do not send `icon` unless the human asked to change the icon name. Never set `icon` to `imageUrl`. Linear fetches the public URL into private storage. If the URL is not publicly fetchable, this path fails.\n4. **Bytes upload.** Only from a server. Call `fileUpload` with `contentType`, `filename`, and `size` (bytes). PUT the file to `uploadFile.uploadUrl`. Set `Content-Type`, then copy every `{ key, value }` in `uploadFile.headers` onto that PUT. The official SDK example also sets `Cache-Control: public, max-age=31536000` before 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, place `uploadFile.assetUrl` in markdown with `commentCreate`, `issueUpdate`, or `projectUpdate`. See [How to upload a file](https://linear.app/developers/how-to-upload-a-file-to-linear).\n5. Confirm the mutation `success` flag is true and the response `errors` array is absent. Then stop. A person reviews the issue or project. Do not call `attachmentCreate` to attach the PNG. Do not treat a Linear-hosted asset URL as a public CDN link."},{"id":"prompting-rules","heading":"Prompting rules for Brandiseer images","markdown":"When the agent builds `prompt`:\n\n- Keep subject, layout, composition, and on-image text (what the words say and where they sit).\n- Never send brand colours, fonts, mood, or visual style. Brand memory already applies those. Style words fight the brand and produce off-brand stills.\n- Put crop in `aspectRatio`, quality in `mode`, pixel size in `resolution`, and logo on/off in `useLogo`. Those are body fields, not prompt prose.\n- Map the Linear slot to a documented ratio. Issue cover or attachment still: `16:9` or `1: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 offers `4:3`. None of the slots above require it. Use `4:3` only when the human asks for that frame.\n- Prefer `design` for finished Linear stills. `fast` is the API default if you omit `mode`, and it is the right fallback if a design job fails once.\n- Prefer `1K` or `2K` for these stills unless the brief asks for `4K`.\n- Set `useLogo` true 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.\n- Full rules: [Prompt compatibility](https://brandiseer.com/guide/image)."},{"id":"system-prompts","heading":"System prompt","markdown":"Paste this into Cursor or Cowork as the standing instructions for Linear stills. Replace the placeholders before the first generate.\n\n```text\nYou generate on-brand stills for Linear issues and projects. Brandiseer\nreturns an image URL. You do not create Linear issues or projects, you do\nnot sync Linear, and you do not claim an issue or project changed unless a\nLinear call you actually performed returned success.\n\nBrandiseer:\n- MCP: https://brandiseer.com/api/mcp with Authorization: Bearer.\n  Tools you may call: brandiseer_list_brands, brandiseer_check_credits,\n  brandiseer_generate_image. Do not invent tool names.\n- REST: X-API-Key: bds_... (secret store only).\n  Prefer POST https://brandiseer.com/api/v2/generate\n  or POST https://brandiseer.com/api/v2/generate/async\n  then poll GET https://brandiseer.com/api/v2/jobs/:jobId every 3 to 5 seconds\n  until data.status is done or failed, or about 10 minutes have passed.\n- Live contract: https://brandiseer.com/guide/docs and\n  https://brandiseer.com/guide/image\n- Brandiseer does not host a Linear integration. There is no native\n  Brandiseer to Linear app in this guide, and there is no Linear MCP.\n\nDefaults for this workspace (fill in once):\n- Brand ID: bds_c_...\n- Issue cover aspectRatio: ask if the frame is not known (16:9 or 1:1)\n- Project banner aspectRatio: 16:9\n- Cycle or roadmap still aspectRatio: 16:9\n- Changelog or launch still aspectRatio: 16:9\n- Portrait aspectRatio, only if the brief truly needs it: 9:16\n- mode: design\n- resolution: 2K (use 1K if file size or speed matters)\n- useLogo when the brief is silent: false\n- Multi-brand map, if any: brand name to bds_c_...\n\nOn every request:\n1. Resolve brandId from the brief or the default. If more than one brand\n   exists and the brief does not name one, ask. Never fall through to\n   another client's Brand ID.\n2. Check credits before a batch (brandiseer_check_credits or\n   GET https://brandiseer.com/api/v1/credits). A single still can proceed\n   if the last known balance covers the mode and resolution in the table\n   you were given. On 402, stop.\n3. Rewrite the brief into subject, layout, composition, and on-image text.\n   Strip colours, fonts, mood, and visual style. If stripping leaves no\n   physical subject, ask what is in the frame. Do not generate.\n4. Choose aspectRatio from the slot. Put resolution, mode, and useLogo in\n   API fields, not in the prompt.\n5. Generate one still. Return imageUrl, aspectRatio, mode, resolution,\n   useLogo, and a placement note: which issue or project the human named,\n   and which path (UI attach, comment, issue description, or project\n   content).\n6. Do not tell the user Linear changed unless a Linear GraphQL call you\n   actually performed returned success true and no errors array.\n\nLinear placement, only when the human already asked for it:\n- Default path: the human downloads imageUrl (or opens it) and attaches it\n  in the Linear UI. You do not claim that happened.\n- Endpoint: POST https://api.linear.app/graphql\n  Personal API key header: Authorization: <API_KEY> (no Bearer).\n  OAuth access token header: Authorization: Bearer <token>.\n  Keys: Linear Settings, Account, Security & access.\n  Docs: https://linear.app/developers/graphql\n  https://linear.app/docs/api-and-webhooks\n- Markdown public URL (easiest API path): put\n  ![alt](https://public-imageUrl) in issue description (issueUpdate),\n  a comment (commentCreate), or project content (projectUpdate).\n  Linear fetches that public image into private storage. imageUrl must be\n  publicly fetchable.\n  Docs: https://linear.app/developers/how-to-upload-a-file-to-linear\n- issueUpdate replaces description. projectUpdate replaces content. Read\n  the current markdown first when existing text must stay, then send the\n  full markdown plus the new image. commentCreate adds a comment and leaves\n  the description in place.\n- fileUpload then PUT (server-side only): fileUpload(contentType, filename,\n  size) then PUT bytes to uploadFile.uploadUrl with Content-Type and every\n  returned header. Use uploadFile.assetUrl in markdown afterwards. Linear\n  CSP blocks a client-side PUT.\n- Do not use attachmentCreate to attach the PNG. attachmentCreate links an\n  external resource (title, subtitle, url, iconUrl), like a GitHub PR card.\n  Docs: https://linear.app/developers/attachments\n- Project icon on projectUpdate is an icon name. Do not write imageUrl into\n  icon. There is no documented \"set project cover image URL\" field. Put\n  banners in project content markdown, or leave the upload to the human.\n- Do not create issues or projects. Do not invent a Linear MCP.\n- Linear-hosted file URLs are private. Keep imageUrl as the public URL\n  until Linear has stored the file.\n\nOut of scope:\n- Slack replies, Buffer scheduling, and Zapier, Make, or n8n scenarios.\n- Airtable, Webflow, WordPress, and Framer handoffs (use those guides).\n- Creating Linear issues, projects, cycles, or changelog entries.\n- Legal approval of claims, prices, or offers printed on the image.\n- GET https://brandiseer.com/api/v1/usage (browser session only).\n```\n\n### Prompt rewriter\n\nUse this when a brief arrives as messy notes. It does not call Brandiseer. It only cleans text before you put it in `prompt`. The word list is a starting filter, not an official dictionary. If a phrase still describes colour, type, mood, or look, delete it by hand.\n\n```javascript\n// Local rewriter for a Cursor or Cowork helper script.\n// Keeps subject, layout, composition, and on-image text.\n// Strips colour, type, mood, and visual-style language.\n\nconst STYLE_WORDS =\n  /\\b(colou?rs?|palette|hex|fonts?|typefaces?|typography|serif|sans-serif|moods?|vibes?|aesthetics?|minimalist|luxury|playful|cinematic|pastel|neon|gradients?|moody|elegant|premium|on-brand)\\b/gi;\n\nconst SLOT_TO_RATIO = {\n  issue: \"16:9\",\n  \"issue cover\": \"16:9\",\n  cover: \"16:9\",\n  attachment: \"16:9\",\n  \"attachment still\": \"16:9\",\n  \"16:9\": \"16:9\",\n  project: \"16:9\",\n  banner: \"16:9\",\n  \"project banner\": \"16:9\",\n  thumbnail: \"16:9\",\n  \"project thumbnail\": \"16:9\",\n  cycle: \"16:9\",\n  roadmap: \"16:9\",\n  \"cycle still\": \"16:9\",\n  \"roadmap still\": \"16:9\",\n  changelog: \"16:9\",\n  launch: \"16:9\",\n  \"changelog still\": \"16:9\",\n  \"launch still\": \"16:9\",\n  square: \"1:1\",\n  \"1:1\": \"1:1\",\n  portrait: \"9:16\",\n  \"9:16\": \"9:16\",\n  \"4:3\": \"4:3\",\n};\n\nfunction rewriteBrandiseerPrompt(input) {\n  const headline = String(input.headline || \"\").trim();\n  const subject = String(input.subject || input.brief || \"\").trim();\n  const slotKey = String(input.slot || input.crop || \"\").trim().toLowerCase();\n  const aspectRatio = SLOT_TO_RATIO[slotKey] || \"\";\n\n  let prompt = [subject, headline ? `On-image text: \"${headline}\".` : \"\"]\n    .filter(Boolean)\n    .join(\" \");\n\n  prompt = prompt\n    .replace(/#[0-9a-f]{3,8}\\b/gi, \" \")\n    .replace(STYLE_WORDS, \" \")\n    .replace(/\\s+/g, \" \")\n    .trim();\n\n  return {\n    prompt,\n    aspectRatio,\n    needsSubject: prompt.length < 12 || !aspectRatio,\n  };\n}\n```\n\nIf `needsSubject` is true, ask the human for a subject and a 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."},{"id":"add-ons","heading":"Add-ons","markdown":"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.\n\n### Approval before the issue is treated as final\n\nAfter `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.\n\n### One brand per client workspace\n\nKeep 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.\n\n### Credit soft-limit\n\nWhen `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.\n\n### Placement log\n\nKeep 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.\n\n### Handoff to the sibling guides\n\n- Record stills on Airtable Attachment fields: [Airtable record visuals](/use-cases/airtable-record-visuals).\n- Stills requested in Slack, returned in a thread: [Slack creative desk](/use-cases/slack-creative-desk).\n- Captions and a Buffer schedule: [Social media automation](/use-cases/social-media-automation).\n- A Zapier, Make, or n8n trigger that should deliver `imageUrl` on a schedule: [Always-on creative automation](/use-cases/always-on-creative-automation).\n- Webflow Assets or CMS: [Webflow site visuals](/use-cases/webflow-site-visuals).\n- WordPress Media Library: [WordPress site visuals](/use-cases/wordpress-site-visuals).\n- Framer Assets, canvas, or CMS: [Framer site visuals](/use-cases/framer-site-visuals).\n\nDo not embed an Airtable base, a Buffer schedule, a Slack desk, an always-on HTTP scenario, Webflow, WordPress, or Framer inside this Linear guide.\n\n### Optional motion (not the issue MVP)\n\nVideo is image-to-video only. Generate the still first, then send its public `imageUrl` to `POST https://brandiseer.com/api/v2.1/video/generate`. Poll `GET https://brandiseer.com/api/v2.1/video/jobs/:jobId`. Video jobs use `status` `completed` and a `videoUrl`, which is a different contract from image jobs (`done` and `imageUrl`). Read [guide/video](https://brandiseer.com/guide/video) for tier, duration, and resolution before you enable this. Video credits are per second and are not the still table below. A video failure does not undo the still. 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."},{"id":"limits-and-faq","heading":"Limits & FAQ","markdown":"### Does Brandiseer update the Linear issue?\n\nNo. 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.\n\n### Is there a Brandiseer app inside Linear?\n\nNo. There is no Brandiseer Linear integration in this guide. Do not look for a native \"send to Linear\" button on the Brandiseer side.\n\n### Do I need a Linear MCP?\n\nNo. 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.\n\n### Which Linear calls are in this guide?\n\nOnly calls confirmed on Linear's developer docs:\n\n- Endpoint: `POST https://api.linear.app/graphql` on [GraphQL](https://linear.app/developers/graphql)\n- Personal API key header: `Authorization: <API_KEY>` (no `Bearer`). OAuth access token header: `Authorization: Bearer <token>`\n- Markdown image on an existing issue description: `issueUpdate` with `description`. This replaces the description. Read it first if the current text must stay\n- Markdown image on a new comment: `commentCreate` with `issueId` and `body`. This leaves the description in place\n- Markdown image on existing project content: `projectUpdate` with `content`. This replaces the content. Read it first if the current text must stay. Do not set `icon` to an image URL\n- Direct byte upload: `fileUpload(contentType, filename, size)`, then a server-side PUT to `uploadFile.uploadUrl`, then `uploadFile.assetUrl` in markdown. Docs: [How to upload a file](https://linear.app/developers/how-to-upload-a-file-to-linear)\n\n`attachmentCreate` is documented on [Attachments](https://linear.app/developers/attachments) and is out of scope here. It creates an external link card. It does not upload the Brandiseer PNG.\n\nIssue 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.\n\nLinear's upload guide also shows a base64 data URL inside markdown. This guide does not use that path. Use a public `imageUrl` or `fileUpload`.\n\n### Where do the keys live?\n\nThe 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>`.\n\n### What do stills cost?\n\nCredits per still. Failures refund. Async submits deduct immediately and refund if the job fails.\n\n| Mode | 1K | 2K | 4K |\n| --- | --- | --- | --- |\n| Fast | 4 | 6 | 8 |\n| Design | 5 | 6 | 8 |\n| Pro | 6 | 8 | 10 |\n\n`402` means not enough credits. Pricing context: [brandiseer.com/pricing](https://brandiseer.com/pricing).\n\n### Why is the still off-brand?\n\nAlmost always because the prompt contained colours, fonts, mood, or visual style. Strip them and generate again. See [guide/image](https://brandiseer.com/guide/image).\n\n### Why does the crop look wrong on the issue?\n\nBrandiseer 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.\n\n### How long can I poll?\n\nAbout 10 minutes, then the Brandiseer job expires. Poll every 3 to 5 seconds. Stop on `done` or `failed` as well. Do not start a second job for the same slot while the first is still open, or you will pay twice.\n\n### Which generate URL should new Linear work use?\n\n`POST /api/v2/generate` when the agent host waits long enough. `POST /api/v2/generate/async` when it does not. Legacy `POST /api/v1/generate` remains for older callers and only supports Fast and Pro via `proMode` boolean.\n\n### Do Linear-hosted image URLs stay public?\n\nNo. 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](https://linear.app/developers/how-to-upload-a-file-to-linear) and Accessing Images on [GraphQL](https://linear.app/developers/graphql).\n\n### What if the markdown image does not appear?\n\nCheck 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.\n\n### Can I set a project cover image URL?\n\nThis 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.\n\n### Does the agent approve claims on the image?\n\nNo. If the still has a price, a discount, or a legal line, a person reads it before the issue or project is treated as final. Brandiseer does not review the claim."},{"id":"demo","heading":"Demo / motion","markdown":"**Motion brief only. This is not an embed, and there is no walkthrough URL yet.**\n\n**Working title:** Linear issue visuals\n**Length:** 45 to 75 seconds\n\n**Story beats:**\n\n1. A person asks Cursor or Cowork for an issue cover with a layout-only brief\n2. The agent calls Brandiseer (MCP or REST v2) and shows an on-brand still\n3. `imageUrl` is attached on a Linear issue in the UI\n4. Optional cut: a comment is created with the public URL through Linear GraphQL, still unapproved\n5. A human reviews the issue only after the still looks right next to the title\n\n**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."},{"id":"additional-1-example-flows","heading":"Example flows","markdown":"### Flow A: Issue cover, attached by a person\n\nThe 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.\"\n\nThe 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.\n\n### Flow B: Issue covers for a set of tickets\n\nThe 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.\n\n### Flow C: Comment image via public URL, not yet approved\n\nThe 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 `![Spring cover](https://public-imageUrl)`. 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.\n\n### Flow D: Project banner, plus a cycle still and a launch still\n\nThe brief: \"Project banner for the launch project. Product left of centre. On-image text Spring launch on the right. 16:9.\"\n\nThe 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 `projectUpdate`s the full markdown with `![Project banner](https://public-imageUrl)`. It does not write `imageUrl` into `icon`. `icon` stays whatever icon name the project already has, unless you asked to change that name.\n\nA 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.\n\n### Flow E: Credit check before an issue set\n\nBefore 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.\n\n### Flow F: Style-only brief\n\nThe brief: \"Make the issue cover feel premium and moody.\"\n\nThe 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."},{"id":"additional-2-related-use-cases","heading":"Related use cases","markdown":"- [Airtable record visuals](/use-cases/airtable-record-visuals): Brandiseer stills placed on Airtable Attachment fields\n- [Always-on creative automation](/use-cases/always-on-creative-automation): Zapier, Make, or n8n calls Brandiseer REST and delivers `imageUrl` downstream\n- [Slack creative desk](/use-cases/slack-creative-desk): people request stills in Slack and get them back in-thread\n- [Social media automation](/use-cases/social-media-automation): on-brand images, captions, and Buffer scheduling\n- [Webflow site visuals](/use-cases/webflow-site-visuals): Brandiseer stills placed in Webflow Assets or CMS\n- [WordPress site visuals](/use-cases/wordpress-site-visuals): Brandiseer stills placed in the WordPress Media Library\n- [Framer site visuals](/use-cases/framer-site-visuals): Brandiseer stills placed in Framer Assets, on the canvas, or on a CMS image field"},{"id":"additional-3-links","heading":"Links","markdown":"| Resource | URL |\n| --- | --- |\n| Brandiseer | https://brandiseer.com |\n| Developers | https://brandiseer.com/developers |\n| API reference (guide/docs) | https://brandiseer.com/guide/docs |\n| Image prompt compatibility | https://brandiseer.com/guide/image |\n| Video prompting (optional motion) | https://brandiseer.com/guide/video |\n| MCP endpoint (agent hosts) | https://brandiseer.com/api/mcp |\n| API keys | https://brandiseer.com/settings/api |\n| Pricing / credits | https://brandiseer.com/pricing |\n| Linear | https://linear.app |\n| Linear GraphQL | https://linear.app/developers/graphql |\n| Linear file upload | https://linear.app/developers/how-to-upload-a-file-to-linear |\n| Linear attachments (external link cards) | https://linear.app/developers/attachments |\n| Linear SDK | https://linear.app/developers/sdk |\n| Linear API keys and webhooks | https://linear.app/docs/api-and-webhooks |\n| Visual pack manifest | `assets/MANIFEST.md` |\n| Sibling: Airtable record visuals | https://blog.brandiseer.com/use-cases/airtable-record-visuals |\n| Sibling: Always-on creative automation | https://blog.brandiseer.com/use-cases/always-on-creative-automation |\n| Sibling: Slack creative desk | https://blog.brandiseer.com/use-cases/slack-creative-desk |\n| Sibling: Social media automation | https://blog.brandiseer.com/use-cases/social-media-automation |\n| Sibling: Webflow site visuals | https://blog.brandiseer.com/use-cases/webflow-site-visuals |\n| Sibling: WordPress site visuals | https://blog.brandiseer.com/use-cases/wordpress-site-visuals |\n| Sibling: Framer site visuals | https://blog.brandiseer.com/use-cases/framer-site-visuals |"},{"id":"additional-4-source","heading":"Source","markdown":"Public Use case README for 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](https://brandiseer.com/guide/docs) and [guide/image](https://brandiseer.com/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."},{"id":"api-reference","heading":"API reference","markdown":"Wiring reference for agents and HTTP modules. The sections above are the human path.\n\n### MCP (interactive stills in Cursor or Cowork)\n\nConnect `https://brandiseer.com/api/mcp` with `Authorization: Bearer` and the API key. The only tools this guide uses are:\n\n- `brandiseer_list_brands`\n- `brandiseer_check_credits`\n- `brandiseer_generate_image`\n\nDo not invent extra Brandiseer tool names. If the host needs explicit `mode`, `aspectRatio`, or async polling, use REST v2 below. MCP does not edit Linear. Do not invent a Linear MCP.\n\n### REST (prefer v2 images)\n\nSend `X-API-Key: bds_...` on every authenticated Brandiseer call. There is no official SDK. Any HTTP client the agent host can run is enough.\n\n| Call | What you get |\n| --- | --- |\n| `POST https://brandiseer.com/api/v2/generate` | Sync still. `200` body `{ \"imageUrl\", \"creditsUsed\" }`. Design mode often takes 40 to 70 seconds. |\n| `POST https://brandiseer.com/api/v2/generate/async` | `202` body `{ \"data\": { \"jobId\" } }` (ids look like `bds_job_...`). Credits are deducted when you submit. |\n| `GET https://brandiseer.com/api/v2/jobs/:jobId` | Poll every 3 to 5 seconds with the same API key. `data.status` becomes `done` or `failed`. Jobs expire after about 10 minutes. Failures refund credits. |\n| `POST https://brandiseer.com/api/v1/generate` | Legacy only. `proMode` boolean. Fast and Pro only. Prefer v2. |\n| `GET https://brandiseer.com/api/v1/credits` | JSON includes `credits`, the current balance for that key. |\n| `GET https://brandiseer.com/api/v1/brands` | Brands for the key. Each brand `id` looks like `bds_c_...`. |\n| `GET https://brandiseer.com/api/v1/health` | `{ \"status\": \"ok\" }`. No API key. |\n\nImage body fields: `brandId`, `prompt`, optional `aspectRatio` (`1:1`, `16:9`, `9:16`, `4:3`), `resolution` (`1K`, `2K`, `4K`), `mode` (`fast`, `design`, or `pro`; default `fast` if you omit it), `useLogo`.\n\nSync status codes you should branch on: `200` success, `400` bad body, `401` bad key, `402` not enough credits, `404` brand not found, `500` server error.\n\nDone job body: `{ \"data\": { \"status\": \"done\", \"imageUrl\", \"creditsUsed\", \"referenceImage\" } }`. Hand `data.imageUrl` to Linear. Failed job body: `{ \"data\": { \"status\": \"failed\", \"error\": { \"code\", \"message\" } } }`.\n\nCursor and Cowork can wait on a sync Design call when the session allows it. If the HTTP client times out under about 60 seconds, use async and poll. Do not point Linear at the Brandiseer MCP URL.\n\n### Linear GraphQL (placement only)\n\nSend every call to `POST https://api.linear.app/graphql` with `Content-Type: application/json`.\n\n| Credential | Header |\n| --- | --- |\n| Personal API key | `Authorization: <API_KEY>` (no `Bearer`) |\n| OAuth access token | `Authorization: Bearer <token>` |\n\nCreate 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](https://linear.app/docs/api-and-webhooks) and [GraphQL](https://linear.app/developers/graphql).\n\nCheck 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.\n\n| Call | What you get |\n| --- | --- |\n| `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. |\n| `issueUpdate(id, input: { description })` | Replaces the issue description with the markdown you send. Include `![alt](public imageUrl)` in that markdown. |\n| `commentCreate(input: { issueId, body })` | Adds a comment. `body` is markdown. This leaves `description` in place. |\n| `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. |\n| `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`. |\n| `fileUpload(contentType, filename, size)` | Returns `uploadFile.uploadUrl`, `uploadFile.assetUrl`, and `uploadFile.headers` (`key`, `value`). `size` is the file size in bytes. |\n\nMarkdown public URL is the easiest API path. Linear fetches the image at that public URL into private storage. Docs: [How to upload a file](https://linear.app/developers/how-to-upload-a-file-to-linear).\n\nByte upload, server-side only:\n\n1. Call `fileUpload`. If `success` is false or `uploadFile` is missing, stop.\n2. PUT the raw bytes to `uploadFile.uploadUrl`. Set `Content-Type` to the same type you sent as `contentType`. The [SDK example](https://linear.app/developers/how-to-upload-a-file-to-linear) also sets `Cache-Control: public, max-age=31536000`. Then set every header from `uploadFile.headers` so those values are on the request.\n3. A browser PUT fails Linear's content security policy. Run the PUT on a server. A 403 usually means a returned header was missing.\n4. Place `uploadFile.assetUrl` in markdown with `commentCreate`, `issueUpdate`, or `projectUpdate`. That URL points at Linear's private storage.\n\n`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](https://linear.app/developers/attachments).\n\nThe [TypeScript SDK](https://linear.app/developers/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.\n\nThis 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.\n\n### Field mapping\n\nBrandiseer generate body:\n\n| Incoming field | API | Rule |\n| --- | --- | --- |\n| 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. |\n| 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. |\n| brandId | `brandId` | `bds_c_...`. Required. |\n| mode | `mode` | `fast`, `design`, or `pro`. Omit and the API uses `fast`. Prefer `design` for these stills. |\n| useLogo | `useLogo` | `true` or `false`. |\n| subject or brief | `prompt` | After the rewriter. If the cleaned text has no subject, do not call Brandiseer. |\n| resolution | `resolution` | `1K`, `2K`, or `4K`. Prefer `2K` or `1K` for these stills. |\n\nLinear placement. The issue and project below are whatever the human named. Do not invent them.\n\n| Incoming value | Linear | Rule |\n| --- | --- | --- |\n| Public Brandiseer `imageUrl` | `![alt](url)` inside markdown | Required for the markdown path. Linear fetches this URL. It must be public. |\n| Issue description | `issueUpdate` input `description` | Replaces the description. Read the current value first when text must stay. |\n| Comment | `commentCreate` input `body`, plus `issueId` | Adds a comment. Does not replace the description. |\n| Project document | `projectUpdate` input `content` | Replaces project content. Read the current value first when text must stay. |\n| Project icon | `projectUpdate` input `icon` | Icon name only. Do not write `imageUrl` here. There is no project cover image URL field in this guide. |\n| 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. |\n| External link card | `attachmentCreate` | Out of scope for this PNG. Title, subtitle, url, and iconUrl. Not a file upload. |\n\n### Example JSON\n\nUse this body for both Brandiseer sync and async. Only the URL and the response change.\n\n```json\n{\n  \"brandId\": \"bds_c_...\",\n  \"prompt\": \"Wide issue cover. Product centred on the left third. Empty right half for a title the Linear issue will hold. No on-image text.\",\n  \"aspectRatio\": \"16:9\",\n  \"resolution\": \"2K\",\n  \"mode\": \"design\",\n  \"useLogo\": false\n}\n```\n\nSync: `POST https://brandiseer.com/api/v2/generate`. A Design 2K still costs 6 credits, so a success body looks like:\n\n```json\n{\n  \"imageUrl\": \"https://...\",\n  \"creditsUsed\": 6\n}\n```\n\nAsync: `POST https://brandiseer.com/api/v2/generate/async`. HTTP `202`:\n\n```json\n{\n  \"data\": {\n    \"jobId\": \"bds_job_...\"\n  }\n}\n```\n\nPoll `GET https://brandiseer.com/api/v2/jobs/bds_job_...` with the same `X-API-Key`. When it finishes:\n\n```json\n{\n  \"data\": {\n    \"status\": \"done\",\n    \"imageUrl\": \"https://...\",\n    \"creditsUsed\": 6,\n    \"referenceImage\": null\n  }\n}\n```\n\nWhen it fails (credits are refunded):\n\n```json\n{\n  \"data\": {\n    \"status\": \"failed\",\n    \"error\": {\n      \"code\": \"...\",\n      \"message\": \"...\"\n    }\n  }\n}\n```\n\nIgnore `referenceImage` for delivery. Linear gets `imageUrl` (or a file you downloaded from it).\n\nComment 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.\n\n```json\n{\n  \"query\": \"mutation CommentCreate($issueId: String!, $body: String!) { commentCreate(input: { issueId: $issueId, body: $body }) { success comment { id } } }\",\n  \"variables\": {\n    \"issueId\": \"BLA-123\",\n    \"body\": \"![Spring cover](https://...)\"\n  }\n}\n```\n\nIssue description. Read `description` first. This example sends existing text plus the new image, which replaces the description with that full string.\n\n```json\n{\n  \"query\": \"mutation IssueUpdate($id: String!, $description: String!) { issueUpdate(id: $id, input: { description: $description }) { success issue { id } } }\",\n  \"variables\": {\n    \"id\": \"BLA-123\",\n    \"description\": \"Existing issue text.\\n\\n![Spring cover](https://...)\"\n  }\n}\n```\n\nProject content. Read `content` first. Do not include `icon` in this input.\n\n```json\n{\n  \"query\": \"mutation ProjectUpdate($id: String!, $content: String!) { projectUpdate(id: $id, input: { content: $content }) { success project { id } } }\",\n  \"variables\": {\n    \"id\": \"PROJECT_UUID\",\n    \"content\": \"Existing project text.\\n\\n![Project banner](https://...)\"\n  }\n}\n```\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`.\n\n```json\n{\n  \"query\": \"mutation FileUpload($contentType: String!, $filename: String!, $size: Int!) { fileUpload(contentType: $contentType, filename: $filename, size: $size) { success uploadFile { uploadUrl assetUrl headers { key value } } } }\",\n  \"variables\": {\n    \"contentType\": \"image/png\",\n    \"filename\": \"spring-issue-cover.png\",\n    \"size\": 1828983\n  }\n}\n```\n\nThen PUT the bytes to `uploadUrl` from a server, with `Content-Type: image/png` and every returned header. Use `assetUrl` in the markdown examples above.\n\nSlot prompts that stay layout-only:\n\n| Slot | aspectRatio | Prompt |\n| --- | --- | --- |\n| 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. |\n| Issue cover | `1:1` | Square issue cover. Single subject centred, full item in frame. Plain surrounding space. No on-image text. |\n| Project banner | `16:9` | Landscape project banner. Subject left of centre. On-image text: \"Spring launch\" in the right third. |\n| 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. |\n| Changelog or launch still | `16:9` | Landscape launch still. Product left of centre. On-image text: \"Spring launch\" in the right third. |\n| Portrait | `9:16` | Portrait still. Single subject in the upper two thirds. Empty band along the bottom. No on-image text. |"}],"actions":{"tryBrandiseer":"https://brandiseer.com","createApiKey":"https://brandiseer.com/settings/api","developerDocs":"https://brandiseer.com/developers","mcpEndpoint":"https://brandiseer.com/api/mcp"},"representations":{"html":"https://blog.brandiseer.com/use-cases/linear-issue-visuals","markdown":"https://blog.brandiseer.com/use-cases/linear-issue-visuals/agent.md","json":"https://blog.brandiseer.com/use-cases/linear-issue-visuals/guide.json"}}