> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trypost.it/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload media file

> Upload an image, video, or PDF from your machine and attach it to a post.

For attaching media from a public URL instead of uploading bytes, use [`POST /posts/{post}/media/from-url`](/api-reference/endpoint/attach-media-from-url). To reuse a file already in the Asset Library, use [`POST /posts/{post}/media/from-asset`](/api-reference/endpoint/attach-existing-asset).

## Path parameters

<ParamField path="post" type="string" required>
  The UUID of the post to attach the file to.
</ParamField>

## Request

Send as `multipart/form-data` with a single `media` field.

<ParamField body="media" type="file" required>
  The file to upload. Must be one of the allowed types:

  * **Images**: `image/jpeg`, `image/png`, `image/gif`, `image/webp` — up to **10 MB** (`MEDIA_IMAGE_MAX_SIZE_MB`)
  * **Videos**: `video/mp4`, `video/quicktime` (MOV) — up to **1 GB** (`MEDIA_VIDEO_MAX_SIZE_MB`)
  * **Documents**: `application/pdf` — up to **100 MB** (`MEDIA_DOCUMENT_MAX_SIZE_MB`)
</ParamField>

## Behaviour

* The post must belong to the caller's current workspace; otherwise the response is `404`.
* The file's MIME type is **intersected with the platforms enabled on the post**. For example, uploading an image to a TikTok-only post returns `422` unless the post targets `tiktok_photo`. Uploading a PDF only succeeds on platforms that accept documents (e.g. LinkedIn `linkedin_post`).
* Per-type size caps are enforced after MIME detection. The FormRequest uses the video ceiling as an overall bound; per-type limits still apply.
* PNG and WebP still images are normalized to **JPEG (q100)** at storage time for universal platform compatibility. GIF is preserved (animation kept for X / Bluesky / Mastodon / Telegram / Discord). MP4, MOV, and PDF are stored as-is.
* The new media is stored under `medias/{uuid}.{ext}` on the configured filesystem disk (`local`, `s3`, or `r2`) and appended to the post's `media[]` array with its `size` and `meta` (image `width`/`height`, video `duration` in seconds read from the file). Per-network size, duration, GIF and MOV caps are enforced when the post is scheduled or published — see [Media → Enforced on the server](/knowledge-base/media#enforced-on-the-server-not-just-in-the-editor).

## Response

Returns the updated post — same shape as [`GET /posts/{post}`](/api-reference/endpoint/get-post), with the newly-uploaded item in `media[]`.

On validation failure (unsupported type, file too large, no enabled platform accepts the type) the response is `422` with `errors.media[]`.

<RequestExample>
  ```bash theme={null}
  curl -X POST https://app.trypost.it/api/posts/9f1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c/media \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Accept: application/json" \
    -F "media=@./photo.jpg"
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "id": "9f1a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
    "content": "Launch day!",
    "media": [
      {
        "id": "01HX1A2B3C4D5E6F7G8H9I0J1K",
        "path": "medias/2681a1bf-131f-41b1-9866-755c1cb51f97.jpg",
        "url": "https://media.trypost.it/medias/2681a1bf-131f-41b1-9866-755c1cb51f97.jpg",
        "type": "image",
        "mime_type": "image/jpeg",
        "original_filename": "photo.jpg"
      }
    ],
    "status": "draft",
    "scheduled_at": null,
    "published_at": null,
    "platforms": [],
    "labels": [],
    "created_at": "2025-01-14 09:00:00",
    "updated_at": "2025-01-14 09:30:00"
  }
  ```
</ResponseExample>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.