Skip to main content
There are two ways to attach media to a post. Pick by size.
Default to the streaming upload. Base64 inflates a payload by about a third and the whole thing has to be held in memory at once, so it stops working well before the documented size limits.

Streaming upload

Send the raw bytes as the request body with the file’s own Content-Type. Not multipart, not base64 — the bytes themselves.
Response
Pass the whole handle object — both fields — in uploadedHandles when you create the post:
preview.r2Url is a real URL you can show in a composer before the post exists. It is a staging location, not the published permalink.

Inline base64

For small images, skip the upload round trip:
Send the base64 payload only — no data:image/jpeg;base64, prefix.

Carousels

Order is the order you supply. Sources are concatenated as uploadedHandles then mediaItems, so don’t split one carousel across both unless you want that ordering.

Video covers

Pass coverImage to choose a video’s thumbnail. Same base64 shape as mediaItems:

Validation

Media is checked against the target platform and mediaType before the post is accepted — dimensions, duration, file size, and count. A rejection comes back as 400 naming the offending item:

Removing media from a post

While a post is still editable, drop one item without touching the rest:
mediaId comes from the media[] array on the post. This deletes the stored blob as well as the row.