Skip to main content

Media

The media library manages uploaded files (images, videos, audio, documents) with automatic variant generation for images, MIME type detection, deduplication, and per-locale metadata.

Endpoints

Media Files

MethodPathPermissionDescription
GET/sites/{site_id}/media?page&per_page&search&mime_category&folder_idReadList media files (paginated, searchable)
GET/media/{id}ReadGet media file with variants
GET/media/{id}/usageReadGet all references to a media file
POST/mediaAuthorCreate a media record (JSON metadata)
POST/media/uploadAuthorUpload a file (multipart/form-data)
PUT/media/{id}AuthorUpdate media metadata
DELETE/media/{id}?forceAuthorSoft delete (409 if in use, ?force=true to override)

Metadata

MethodPathPermissionDescription
GET/media/{id}/metadataReadList metadata for a media file
POST/media/{id}/metadataReadCreate metadata
PUT/media/metadata/{metadata_id}ReadUpdate metadata
DELETE/media/metadata/{metadata_id}ReadDelete metadata

List Media

Supports full-text search across filename, alt text, caption, and title. Filter by MIME category (image, video, audio, document) or folder.

curl -H "X-API-Key: oy_live_abc123..." \
"https://your-domain.com/api/v1/sites/{site_id}/media?search=hero&mime_category=image&page=1"

Get Media Usage

Returns all references to a media file across blogs, pages, and sites. Use this before deleting to understand the impact.

curl -H "X-API-Key: oy_live_abc123..." \
https://your-domain.com/api/v1/media/{id}/usage

Response 200 OK

{
"usage_count": 3,
"references": [
{ "content_type": "blog", "content_id": "uuid", "title": "my-post", "usage": "cover_image" },
{ "content_type": "blog", "content_id": "uuid", "title": "my-post", "usage": "attachment" },
{ "content_type": "page", "content_id": "uuid", "title": "/about", "usage": "section_cover" }
]
}

Scanned references:

  • Blog cover and header images (blogs.cover_image_id, blogs.header_image_id)
  • Blog attachments and photos (blog_attachments, blog_photos)
  • Page section cover images (page_sections.cover_image_id)
  • Site logo and favicon (sites.logo_url, sites.favicon_url — URL match)

Safe Deletion

Deleting a media file now checks for active references:

  • DELETE /media/{id} — returns 409 Conflict with usage details if the media is referenced
  • DELETE /media/{id}?force=true — overrides the check and deletes anyway

When deletion is blocked, the response includes the usage count and a descriptive message. The ?force=true parameter is for intentional cleanup when you know the references will be updated.

Even with ?force=true, the media record is only soft-deleted (is_deleted = TRUE). The database record remains for audit purposes.

Upload a File

Use multipart/form-data with the following fields:

  • file -- The file to upload
  • site_ids -- JSON array of site UUIDs, e.g., ["uuid1"]
  • folder_id -- Optional folder UUID
  • is_global -- Optional boolean (default: false)

The API automatically detects the MIME type via magic bytes, computes a SHA-256 checksum for deduplication, and generates image variants (thumbnail, small, medium, large) for image files.

curl -X POST \
-H "X-API-Key: oy_live_abc123..." \
-F "file=@photo.jpg" \
-F 'site_ids=["550e8400-..."]' \
https://your-domain.com/api/v1/media/upload

Response 201 Created -- Returns the media record with generated variants.

If the same file (by checksum) has been uploaded before, the existing record is returned with 200 OK instead.

Media Folders

MethodPathPermissionDescription
GET/sites/{site_id}/media-foldersReadList media folders for a site
POST/sites/{site_id}/media-foldersAuthorCreate a media folder
PUT/media-folders/{id}AuthorUpdate a media folder
DELETE/media-folders/{id}AuthorDelete a media folder

List Media Folders

Returns all media folders for a site, ordered by display order.

curl -H "X-API-Key: oy_live_abc123..." \
https://your-domain.com/api/v1/sites/{site_id}/media-folders

Response 200 OK

[
{
"id": "770e8400-e29b-41d4-a716-446655440000",
"site_id": "550e8400-e29b-41d4-a716-446655440000",
"parent_id": null,
"name": "Photos",
"display_order": 0,
"created_at": "2025-01-15T12:00:00Z",
"updated_at": "2025-01-15T12:00:00Z"
}
]

Create a Media Folder

Creates a new media folder. Supports nesting via optional parent_id.

curl -X POST \
-H "X-API-Key: oy_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"name": "Photos",
"parent_id": null,
"display_order": 0
}' \
https://your-domain.com/api/v1/sites/{site_id}/media-folders

Response 201 Created -- Returns the created MediaFolderResponse.

Update a Media Folder

Updates a media folder's name, parent, or display order. All fields are optional.

curl -X PUT \
-H "X-API-Key: oy_live_abc123..." \
-H "Content-Type: application/json" \
-d '{"name": "Updated Photos"}' \
https://your-domain.com/api/v1/media-folders/{id}

Response 200 OK -- Returns the updated MediaFolderResponse.

Delete a Media Folder

Deletes a media folder. Media files in the folder are not deleted -- they become unassigned.

curl -X DELETE \
-H "X-API-Key: oy_live_abc123..." \
https://your-domain.com/api/v1/media-folders/{id}

Response 204 No Content

File Size Limits

File size limits are configurable per site via site settings. The default maximum is 50 MB.

Allowed File Types

Images (JPEG, PNG, WebP, GIF, SVG, TIFF, BMP, ICO), Video (MP4, WebM, OGG, AVI, MOV), Audio (MP3, WAV, OGG, AAC, FLAC), Documents (PDF, Markdown, Plain text), and common archive formats.