# Media

Posts reference media by id. There are three ways to add a file.

## Import from a URL

Best for files already online (your CDN, a shop, a shared Drive link that downloads directly):

```sh
curl https://postflow.biz.id/api/v1/media \
  -H "Authorization: Bearer $POSTFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://cdn.example.com/reel.mp4", "width": 1080, "height": 1920, "durationMs": 21000 }'
```

- The URL must be public `https://`; private and local addresses are refused.
- The server must answer within 60 seconds, and send `Content-Length` for videos.
- The type comes from the response's `Content-Type`.

## Upload a file

Send the bytes as the request body, up to 30 MB for images and 1 GB for videos:

```sh
curl "https://postflow.biz.id/api/v1/media/upload?name=menu.jpg" \
  -H "Authorization: Bearer $POSTFLOW_API_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @menu.jpg
```

For videos add `width`, `height` and `durationMs` to the query, as for imports.

## Multipart upload (large or resumable)

1. [`POST /media/uploads`](/api/operations/startmediaupload/) with `{ name, mime, size, width?, height?, durationMs? }`. You get
   `mediaId`, `uploadId`, `partSize` (10 MB) and `partCount`.
2. For each part `n` from 1: [`PUT /media/uploads/{mediaId}/parts/{n}?uploadId=…`](/api/operations/uploadmediapart/) with the
   part's bytes. Keep each `etag`.
3. [`POST /media/uploads/{mediaId}/complete`](/api/operations/completemediaupload/) with `{ uploadId, parts: [{ partNumber, etag }] }`.

Cancel with [`/abort`](/api/operations/abortmediaupload/). Unfinished uploads don't show in the library.

## What PostFlow measures

Images are measured automatically (width and height). Videos can't be measured on the server, so send their size and length.
Without them, pre-flight can't check aspect ratio or duration and only warns (`media_dimensions_unknown`).

| | |
|---|---|
| Types | JPEG, PNG, WebP, GIF, MP4, MOV, WebM |
| Size | Images 30 MB, videos 1 GB (lower per platform, see [Platforms](/platforms/)) |
| Storage | Counted against the plan; `402 PLAN_LIMIT` when full |

## Library

- [`GET /media`](/api/operations/listmedia/): newest first, with `usedIn` (posts using the file); filter by `kind` or name (`q`).
- [`GET /media/{id}/file`](/api/operations/downloadmedia/): the file itself (`?thumb=1` for a video's thumbnail). The `url` field
  in responses is the app's path; with the API, use this endpoint.
- [`DELETE /media/{id}`](/api/operations/deletemedia/): only files no post uses. [`POST /media/delete`](/api/operations/deletemediabulk/) deletes up to 100 and skips used ones.

To crop or convert a photo for a platform, open the post in the app: the composer offers a one-click fix.