POST /v1/content/add-image takes a public image URL. A file on your machine
takes three free calls, because a JSON request body can’t carry a file that
size. Open an upload, POST the raw bytes to the URL it returns, then add the
image with its uploadId.
What You Can Upload
Open an Upload
Name the file, its type, and its size in bytes (wc -c < logo.png prints it).
201:
contentType or a size over the cap is 400 INVALID_REQUEST.
With 20 uploads already open, the brand gets 429 RATE_LIMITED, and
Retry-After is when the oldest one expires. An organization key names the
brand with X-Brand-Id here and on the last call, as on every brand-scoped
route.
Send the File
POST the raw bytes touploadUrl before expiresAt. Send no Authorization
header. The URL carries its own credential, so keep it as private as an API
key.
200 with { uploadId, status: "uploaded", size, expiresAt }. The
new expiresAt is 15 minutes after the bytes landed, and it is your deadline
for the next call. Sending again never replaces the first file.
Add It to the Library
200 with the image, now in the brand library and on
Assets:
contentType that differs is
ignored. fileName names the converted file, and the image’s description in
the library comes from its caption. Use assetId to find the image in
GET /v1/brand/images or to remove it with DELETE /v1/brand/images/{assetId}.
A repeat with the same uploadId returns the same answer for 24 hours, even
if the image was deleted since. A retry after a timeout is safe.
All Three Steps in One Call
The TypeScript SDK’sbrew.content.uploadImage opens the upload, sends the
bytes, and adds the image:
contentType is optional here; the file name’s extension gives it. Before
sending anything it refuses an empty file, a file over 20 MB, or an SVG over
2 MB with a TypeError. It does not retry opening the upload (a retry after a
lost answer would hold a second of the 20 slots), so if that step fails, call
uploadImage again. The other answers and errors are the ones above.
From the CLI
--json for the raw answer). The two-step form is
brew-cli content create-image-upload --file-name logo.png --size 48213, then
the curl it prints, then brew-cli content add-image --upload-id <uploadId>.
brew-cli brand delete-image <assetId> removes an image (it asks first; pass
--yes in scripts).
From MCP
Over MCP the flow iscreate_image_upload, the bytes to uploadUrl, then
add_image with the uploadId. Sending the bytes takes a client with a shell
or an HTTP tool on the file’s machine, such as Claude Code or Cursor. A
chat-only client should ask for a public link and call add_image with
imageUrl (see Assets).
See Also
- Assets: find, add, and delete images in the app and from an agent.
POST /v1/content/image-uploadsandPOST /v1/content/add-imagein the Public API v1 reference (sidebar): full schemas.- Rate limits: the 20 open uploads.
- Errors: every code above.
Need Help?
Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:- Self-Service Tools
- Talk to Our Team
Search Documentation
Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.
ChatGPT/Claude Integration
Click “Open in ChatGPT” at the top right of any page to explore it further with ChatGPT or Claude.