Skip to main content

Files

A file belongs to a version. Files are either uploaded blobs (stored under the add-on's Flysystem mount) or external links (probed via the DirectUrlSource adapter). Routes mount at /api/mc-dm-files/.

List files

GET /api/mc-dm-files/

Scope: mc_dm_download:read.

Parent filter: version_id is required for non-super-user keys.

ParamTypeNotes
version_iduintRequired for user keys. Optional for super-user keys.

Soft-deleted files (is_deleted=1) are always excluded.

Order whitelist: display_order, file_size, download_count. Default display_order desc. There is no since support. Files do not carry a primary date column.

Example

curl -H "XF-Api-Key: $KEY" \
"https://example.com/api/mc-dm-files/?version_id=88&order=display_order&direction=asc"

Response

{
"files": [
{
"file_id": 200,
"version_id": 88,
"filename": "acme-importer-1.4.0.zip",
"display_label": "Windows build",
"file_size": 4194304,
"file_hash": "sha256:c0ffee...",
"mime_type": "application/zip",
"is_primary": true,
"is_externally_linked": false,
"external_url": null,
"validation_status": "passed",
"display_order": 1,
"download_count": 31,
"display_name": "acme-importer-1.4.0.zip",
"is_external": false,
"is_link_only": false
}
],
"pagination": { "page": 1, "per_page": 20, "total": 2, "last_page": 1 }
}

Get a single file

GET /api/mc-dm-files/<file_id>/

Scope: mc_dm_download:read.

Permission: the visitor must pass canView on the parent download.

Response: { "file": { ... } }.

Upload or attach a file

POST /api/mc-dm-files/

Scope: mc_dm_download:write.

Permission: canManageFiles on the parent download.

This endpoint accepts either a multipart blob upload or an external_url, never both. Sending both fails HTTP 422 with code mc_dm_api_error.upload_exclusive. Sending neither fails with mc_dm_api_error.upload_required.

FieldTypeNotes
version_iduintRequired.
filemultipart fileEither this or external_url.
external_urlstringEither this or file. The URL is probed via DirectUrlSource.
link_onlyboolOnly meaningful with external_url. 1 = store as link only (no remote fetch). 0 (default) = probe and ingest.
display_labelstringOptional human label (e.g. Windows build).
is_primaryboolIf 1, demotes existing primary siblings on save.
display_orderuintOptional sort key.

Blob upload example

curl -X POST -H "XF-Api-Key: $KEY" \
-F "version_id=88" \
-F "file=@dist/acme-importer-1.5.0.zip" \
-F "display_label=Windows build" \
-F "is_primary=1" \
https://example.com/api/mc-dm-files/

Files are streamed to the add-on's Flysystem mount and processed by the registered Processor adapters: hash, size, mime, magic-byte, decompression-bomb. If a processor rejects the file, the upload returns HTTP 422 with code mc_dm_api_error.processor_rejected. The file row is left at validation_status='failed' for moderator review.

PHP-level upload errors (e.g. upload_max_filesize exceeded) return HTTP 422 with mc_dm_api_error.upload_exceeds_php_limit.

External URL example

curl -X POST -H "XF-Api-Key: $KEY" \
-d "version_id=88" \
-d "external_url=https://github.com/acme/acme/releases/download/v1.5.0/dist.zip" \
-d "display_label=GitHub release" \
https://example.com/api/mc-dm-files/

The response shape for both modes is { "file": { ... } }.

Update a file

PUT /api/mc-dm-files/<file_id>/

Scope: mc_dm_download:write.

Permission: canManageFiles on the parent download.

Only metadata is editable. To replace the blob or change external_url, soft-delete the file and POST a new one.

FieldType
display_labelstring
is_primarybool
display_orderuint

Setting is_primary=true automatically demotes the previous primary on the same version.

curl -X PUT -H "XF-Api-Key: $KEY" \
-d "display_label=Windows installer" \
-d "is_primary=1" \
https://example.com/api/mc-dm-files/200/

Response: { "file": { ... } }.

Delete a file

DELETE /api/mc-dm-files/<file_id>/

Scope: mc_dm_download:write always. mc_dm_download:delete_hard additionally when hard=1.

Permission: canManageFiles. Hard delete additionally requires canHardDelete on the parent download.

ParamEffect
hard1 = hard delete the row and remove the blob from storage (skipped for externally-linked files). Default 0 = soft delete (sets is_deleted=1, blob retained).

When the deleted file was the primary, the next file by display_order is auto-promoted on hard delete only.

curl -X DELETE -H "XF-Api-Key: $KEY" \
https://example.com/api/mc-dm-files/200/

Response: { "success": true }.

Verbose mode (?with=full)

Adds:

FieldTypeNotes
external_source_typestringAdapter id that handled the file (e.g. direct-url).
is_availableboolStorage exists or external URL still reachable.
validation_attemptsint
validation_errorstring | null
last_attempt_dateintUnix timestamp.