Images in pages
Bundle images with a page from the browser, or host them over MCP with add_page_image, and reference them by URL.
A page keeps its images as hosted assets and references them by URL, instead of as giant inline base64 blobs that bloat the HTML and can hit your plan's page-size limit. There are two ways to get images into a page: dropping them in the browser, or hosting them over MCP.
From the browser
When you create a page from an HTML file (on the dashboard) or replace one (from its settings), drop the HTML file together with its images. You can:
- select the HTML file and its image files together, or
- drag the HTML file and its images onto the upload box at once, or
- drag the whole folder, which keeps subfolder paths like
images/hero.png.
Any relative image reference in the HTML is then rewritten to its hosted asset URL automatically, so the page renders exactly as it did on your machine. For example, a file that contains:
<img src="hero.png">
<img src="images/cat.jpg">publishes with each src pointing at a hosted /api/page-assets/... URL.
If an image the HTML references was not included in the upload, the page still publishes, and you get a notice with a count of the images that could not be matched, so you can re-upload with them included.
From an AI client (MCP)
Call add_page_image with the page's id and either a public
image url (which is fetched server-side) or the raw dataBase64 bytes plus a
contentType. It stores the image and returns a hosted URL. Drop that URL into
an <img src="..."> in the page with create_page, update_page, or
patch_page.
This is the natural flow for an agent, which writes its own HTML and just needs somewhere to host each image. There is no relative-path bundling over MCP; use the URL the tool returns.
Cleaning up an unused upload
An image uploaded with add_page_image or request_image_upload_url that never
ends up in a page, for example because the page creation failed or you decided
against it, still counts against your plan's image storage limit. If you have
MCP access, call delete_page_image with the asset's id or
url (whichever you have) to remove it and free that storage. This is
permanent, there is no undo, and if some other page happens to reference the
same URL, that image will break there too, so only delete an upload you are
sure is unused.
What gets rewritten
Rewriting on browser upload only touches relative references, and only in the attributes that load an image:
<img src>and<img srcset><source src>and<source srcset>inside a<picture>
References that already point somewhere are left untouched: a full http(s)://
URL, a protocol-relative //host/... URL, a data: URI, and an in-page #
anchor. Escaped example text (such as <img src="hero.png"> shown inside a
code block) is left alone too, since only real elements are rewritten.
Supported formats and limits
Page images are raster only (PNG, JPEG, GIF, WebP) and up to 4 MB each. SVG is not accepted, because an SVG served from the app origin could run script.
The image size limit is per image; your plan's page-size limit still applies to the HTML document itself.
Markdown pages
Image bundling applies to HTML uploads only. A Markdown page has no bundled asset
folder to resolve a relative path against, so its images must already be an
absolute http(s) URL or a data: URI. To host a Markdown page's images, upload
them with add_page_image first and reference the URL it returns.
Where images are served
Each asset is served from /api/page-assets/<id>/<name>. The <id> is an
unguessable token that grants access; the <name> at the end is decorative, to
keep the URL readable. Assets are stored privately and streamed through this
gated route, and they belong to the page rather than to a single version, so an
image keeps working even if you roll the page back to an earlier version that
still references it.