Composer

Images and video

Attach images and video that upload directly over a presigned PUT and are validated against each channel's limits, moving through processing to ready or failed.

Attach images and video to a post from the editor’s file bar. Each file uploads directly to storage over a presigned PUT, is scanned and validated against every target channel’s own limits, and moves through a small readiness state machine — processing → ready → failed — that the composer surfaces honestly.

Uploading media

Picking a file starts a direct upload: the client reserves an upload, receives a short-lived presigned PUT URL, and sends the bytes straight to the bucket — so large images and video never round-trip through the app server. The composer shows an uploading tile with live progress as the bytes go up. There is no “confirm” step: the moment the bytes land, a server-side watcher verifies them and moves the file into processing — a closed tab after the upload finishes changes nothing.

Verification is honest about the actual bytes: the file’s type comes from a magic-byte sniff, not the declared MIME, and bytes that turn out to be a different type than declared are rejected with that exact reason.

What uploads accept

KindTypesMax size
ImageJPEG, PNG, GIF, WebP25 MB
VideoMP4, QuickTime (.mp4, .mov)512 MB

WebM is not accepted — its container can’t yet be probed for the duration and dimensions the per-channel checks need, so accepting it would silently skip those checks.

Processing, ready, and failed

Every asset folds into one of three readiness buckets that all surfaces act on:

  • Processing — still in flight (uploading, verifying, scanning, or deriving). Wait for it.
  • Ready — clean and publishable. The tile shows the real thumbnail.
  • Failed — terminal: the upload broke, the file was rejected, or a scan flagged it. Remove it and add another. The tile names the reason — an oversize file, an unsupported or mismatched type, an undecodable image, or a failed scan.

A post won’t publish or schedule while any attached asset is still processing or has failed — the publish button holds with a calm “media is still processing” wait, or a “remove the media that failed” prompt. Neither is treated as a content problem with a channel; media readiness rides alongside the per-channel fit check. See Never gets rejected.

Per-platform media rules

Each channel enforces its platform’s real limits on count, size, format, aspect ratio, and mixing:

ChannelImagesVideoMixing
LinkedInUp to 9, 10 MB each (PNG/JPEG)One MP4, ≤500 MB, ≤30 minSingle-kind
XUp to 4, 5 MB each (JPEG/PNG/GIF/WebP)One MP4, ≤512 MB, ≤140 sSingle-kind
InstagramUp to 10, 8 MB each (JPEG/PNG); requiredReel: one 9:16 MP4, ≤1 GB, 3 s–15 minSingle-kind per format; free in carousels
FacebookUp to 10, 10 MB each (JPEG/PNG/GIF)One MP4/MOV, ≤1 GB; Reel: 9:16, 3–90 sSingle-kind

In prose: LinkedIn accepts up to nine PNG or JPEG images at 10 MB each, or a single MP4 up to 500 MB and 30 minutes. X takes up to four images (JPEG, PNG, GIF, or WebP) at 5 MB each, or one MP4 up to 512 MB and 140 seconds. Instagram requires media on a post — up to ten JPEG or PNG images — and a reel must be a single 9:16 MP4 up to 1 GB running 3 seconds to 15 minutes. Facebook allows up to ten JPEG, PNG, or GIF images at 10 MB each, or one MP4/MOV video up to 1 GB, and a Facebook reel must be 9:16 and run 3 to 90 seconds.

Mixing images and video

Most formats are single-kind: a post carries either images or one video, never both — LinkedIn, X, and a Facebook feed post all enforce this. The exception is an Instagram carousel, which mixes image and video slides freely up to its total slide count. When a set breaks a mixing rule, the fit check surfaces it as a fixable failure before publish rather than at publish time.

Alt text

Every attached image can carry alt text, set per use on the media tile. Alt text is stored on the content reference, so the same asset can describe itself differently in different posts.