---
title: Media
description: Attach images, video, and documents through URLs, blobs, streams, and uploaded references with bounded memory and explicit processing states.
---

Media attachments describe real bytes and where they come from. The SDK bounds what it buffers, validates what each platform accepts, and keeps processing states explicit.

## Four media sources

Every attachment names a `kind` (image, video, or document) and a `source`. Document attachments cover paged files such as PDF, PPTX, or DOCX, and only adapters that declare the `document` format accept them.

```ts
// A public HTTPS URL the platform fetches itself
{ kind: "image", source: { kind: "https-url", url: "https://cdn.example.com/a.jpg" } }

// Bytes you hold in memory
{ kind: "video", source: { kind: "blob", blob, fingerprint: contentHash }, mimeType: "video/mp4", byteSize: blob.size }

// A replayable stream factory for large files
{ kind: "video", source: { kind: "stream", open: () => file.stream(), fingerprint: contentHash } }

// A previously uploaded, account-bound media reference
{ kind: "image", source: { kind: "media-ref", ref: mediaRef } }
```

Blob and stream sources require a content fingerprint, which feeds idempotency so a retried request reuses the same upload identity. Optional fields like `mimeType`, `byteSize`, dimensions, `durationSeconds`, `altText`, and captions travel with the attachment where platforms accept them.

## Streams are bounded and replayable

Uploads read streams with bounded buffering, so a large video does not sit fully in memory. The source must be replayable: the adapter may need a fresh stream for a retry, which is why the stream source is a factory function rather than a consumed stream.

## Platforms differ, loudly

Each adapter validates media against its platform's real rules during local preparation: which sources it accepts, size ceilings, item counts, required fields. TikTok accepts verified HTTPS origins only. The X adapter uploads images up to 5 MiB each; video and GIF uploads are not supported yet. LinkedIn accepts one MP4 video per post, uploaded from a Blob of 75 KB to 500 MB, and publishes it only after LinkedIn reports it `AVAILABLE`. LinkedIn also accepts one uploaded PDF, PPT, PPTX, DOC, or DOCX document per post, up to 100 MB, with a required title. Bluesky posts take up to four images or one MP4 video that was uploaded first with `media.upload`. YouTube requires exactly one video with explicit visibility. Nothing is silently cropped, converted, or dropped; unsupported input fails preparation with a structured issue. The platform pages document each rule set.

## Upload once, reference later

Where a platform separates upload from publishing, `social.media.upload` returns an account-bound media reference you can persist and use in later publish requests through the media-ref source. Adapters check upstream readiness before publishing; a still-processing upload needs an explicit later status check, never background polling.

## Processing is a state, not a promise

Video pipelines return processing outcomes: the upload completed but the platform is still working. Keep the delivery reference and reconcile explicitly. A confirmed platform identity produces published; a confirmed rejection produces failed; a lost response stays unknown until reconciled. See [references and outcomes](/concepts/references-and-outcomes).
