Docs

REST API

Media

Media

Media

Upload images and video once, then attach them to any number of posts via media_ids.

The media object

Field Type Description
id integer Unique id. Pass to a post's media_ids.
original_name string The uploaded filename.
mime_type string e.g. image/jpeg, video/mp4.
size_bytes integer File size.
cdn_url string Public URL of the file.
width / height integer|null Pixel dimensions for images.
duration number|null Seconds, for video.
folder string|null Optional organizational folder.
tags string[] Optional tags.
alt_text string|null Accessibility text.

Upload media

POST /api/media

Send as multipart/form-data.

Field Type Required Notes
file file yes jpg, jpeg, png, gif, mp4, mov, webm. Max 100 MB.
folder string no Organizational folder.
tags string[] no Each tag ≤ 50 characters.
alt_text string no ≤ 500 characters.
curl -X POST https://schedulenchill.com/api/media \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -F "file=@./launch.png" \
  -F "folder=product-launch" \
  -F "tags[]=launch" \
  -F "alt_text=Product launch screenshot"

Returns 201 Created with the media object. Use its id in a post's media_ids.

Building over MCP or from a server with a public file URL? Use the upload_media tool, which ingests media by URL instead of multipart upload.


Upload a video from your own computer

The single-request upload above is capped, and a large video in one request is fragile — one dropped connection and you start over. For anything big, open a resumable session and stream the file in chunks. Nothing is capped by request size, and an interrupted upload picks up where it stopped.

This is what your AI tool does when you say "upload ~/Videos/demo.mp4 and schedule it" — the MCP upload_media tool with source="local" returns exactly the envelope below.

1. Open a session

POST /api/media/uploads
Field Type Required Notes
filename string yes Original name, with extension.
mime_type string yes e.g. video/mp4.
size_bytes integer yes Exact size. Finalize rejects a mismatch.
sha256 string no Hex digest. When given, finalize verifies against it.
curl -X POST https://schedulenchill.com/api/media/uploads \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"filename":"demo.mp4","mime_type":"video/mp4","size_bytes":11402,
       "sha256":"86e39c5a..."}'
{
  "upload_id": "779b36fd-6d13-4c42-b3e5-c731956b093c",
  "chunk_size": 8388608,
  "received_bytes": 0,
  "size_bytes": 11402,
  "expires_at": "2026-08-04T07:35:44+00:00"
}

2. Stream the chunks

PATCH each chunk_size-byte slice as a raw body — not multipart — with offset set to the running byte count (0, chunk_size, 2 × chunk_size, …).

curl -X PATCH "https://schedulenchill.com/api/media/uploads/UPLOAD_ID?offset=0" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @./chunk-0
{ "received_bytes": 11402, "size_bytes": 11402, "completed": true }

Send chunks one at a time, in order. Parallel PATCHes corrupt the assembled file. If a chunk fails, re-GET the session to read received_bytes and resume from there — that number is the source of truth, not your own bookkeeping.

3. Finalize

curl -X POST https://schedulenchill.com/api/media/uploads/UPLOAD_ID/complete \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"

Returns the media object. Its id goes in a post's media_ids.

{ "id": 12, "original_name": "demo.mp4", "mime_type": "video/mp4", "size_bytes": 11402 }

Notes

  • Use a token with * abilities (the default). A restricted token 401s on the chunk PATCHes.
  • /mcp and /api/* share one auth guard, so the same bearer works for both.
  • Sessions expire — expires_at is in the envelope. Abandoned ones are pruned.
  • DELETE /api/media/uploads/{id} cancels a session you no longer want.

List media

GET /api/media

Paginated, 15 per page. Optional filters:

Query param Description
search Match filename.
type image or video.
folder Filter by folder.
tags Filter by tag.
page Page number.

Retrieve media

GET /api/media/{id}

Update media

PUT /api/media/{id}

Update metadata only — folder, tags, alt_text. The file itself is immutable; upload a new file to replace it.


Delete media

DELETE /api/media/{id}

Returns 204 No Content.