Media and image uploads
Uploads do not pass through the API. You ask for a presigned URL, you PUT the bytes at object storage, and you tell us it finished. Three requests, and the file never touches our bandwidth or our memory.
Last updated
Endpoints
| Method | Path | What it does | Credential |
|---|---|---|---|
| POST | /v1/blog/media | Record a file and get an upload URL | Session or agent |
| POST | /v1/blog/media/{id}/complete | Confirm the upload finished | Session or agent |
| GET | /v1/blog/media | List media | Key or session |
| DELETE | /v1/blog/media/{id} | Delete media | Session or agent |
The three requests
Ask for a URL
Send the filename, the media type, the size and a SHA-256 of the bytes. You get back a media row with
uploadStatus: "pending"and a presigned URL that expires shortly.Examplebash curl -X POST https://api.cmskite.com/v1/blog/media \ -H "authorization: Bearer $ACCESS_TOKEN" \ -H "x-tenant-id: $WORKSPACE_ID" \ -H "x-project-id: $PROJECT_ID" \ -H 'content-type: application/json' \ -d '{ "filename": "rotterdam.jpg", "mimeType": "image/jpeg", "sizeBytes": 481223, "checksumSha256": "9f86d0818…" }'PUT the bytes
Straight at object storage, from wherever the file is — including the visitor’s browser. No credential of yours is involved; the presigned URL is the authorisation, and it is narrow and short-lived.
Examplebash curl -X PUT "$UPLOAD_URL" \ -H 'content-type: image/jpeg' \ --data-binary @rotterdam.jpgConfirm it
The media row flips to
readyand becomes visible to a project key. Until then it is not served: a half-finished upload is not content.Examplebash curl -X POST https://api.cmskite.com/v1/blog/media/$MEDIA_ID/complete \ -H "authorization: Bearer $ACCESS_TOKEN" \ -H "x-tenant-id: $WORKSPACE_ID" \ -H "x-project-id: $PROJECT_ID"
The same file twice is the same file
Media is content-addressed by its checksum within a project. Re-uploading the same logo for the fortieth time returns the row you already have instead of making a fortieth copy — which is the most common thing a media library is asked to do, and makes uploading idempotent for free.
What is accepted
- Images and documents. A
text/htmlupload is refused: a file served from your domain that can run script is not a media library, it is an XSS vector. - A malformed checksum or a negative size is a 400 before any URL is issued.
- Storage is metered per plan.
blog.media.storage_bytesis the capability; your dashboard shows what you have used. - An upload that is never confirmed is reaped. A presigned URL that nobody used costs you nothing.