Downloads
A download is the top-level releaseable unit. It owns versions, files, comments, and per-download team membership. Routes use the internal prefix mc-dm-downloads. The public mount is /api/mc-dm-downloads/.
List downloads
GET /api/mc-dm-downloads/
Scope: mc_dm_download:read.
Filters:
| Param | Type | Notes |
|---|---|---|
category_id | uint | Restrict to a single category. |
prefix_id | uint | Restrict to a single prefix. |
download_state | enum | visible, moderated, deleted, or draft. Non-moderator keys are forced to visible. |
since | ISO 8601 | Filters by last_update. |
Order whitelist: created_date, last_update, download_count, view_count, title. Default last_update desc.
Permission filter: rows the visitor cannot view are dropped from the page. The pagination.total reflects the pre-filter total, so a visible page may have fewer than per_page rows.
Example
curl -H "XF-Api-Key: $KEY" \
"https://example.com/api/mc-dm-downloads/?category_id=4&order=download_count&direction=desc&per_page=10"
Response
{
"downloads": [
{
"download_id": 42,
"category_id": 4,
"user_id": 17,
"title": "Acme Importer",
"tag_line": "Bulk import via CSV",
"description": "...",
"view_url": "https://example.com/downloads/acme-importer.42/",
"icon_url": "https://example.com/data/mc-downloads/icons/...",
"cover_url": null,
"rating_avg": 4.6,
"rating_count": 12,
"review_count": 9,
"download_count": 318,
"view_count": 2104,
"last_update": 1746200000,
"created_date": 1745000000,
"download_state": "visible",
"prefix_id": 0,
"current_version_id": 88
}
],
"pagination": { "page": 1, "per_page": 10, "total": 87, "last_page": 9 }
}
Get a single download
GET /api/mc-dm-downloads/<download_id>/
Scope: mc_dm_download:read.
Permission: the visitor must pass canView on the download.
curl -H "XF-Api-Key: $KEY" https://example.com/api/mc-dm-downloads/42/
The response is { "download": { ... } } with the same shape as a list element.
Create a download
POST /api/mc-dm-downloads/
Scope: mc_dm_download:write.
Permission: canAddDownload on the target category, or a super-user key.
| Field | Type | Notes |
|---|---|---|
category_id | uint | Required. |
title | string | Required. |
tag_line | string | Optional short tagline. |
description | string | Optional long description. BB code permitted. |
prefix_id | uint | Optional. Must be a prefix assigned to the category. |
tags | string[] | Optional. Per-row tag strings. |
version_string | string | Required when the category supports versioning. Defaults to today (Y-m-d) if omitted on a versioning category. |
Example
curl -X POST -H "XF-Api-Key: $KEY" \
-d "category_id=4" \
-d "title=Acme Importer" \
-d "tag_line=Bulk import via CSV" \
-d "description=Imports CSV files at scale." \
-d "version_string=1.0.0" \
https://example.com/api/mc-dm-downloads/
Response
{
"download": {
"download_id": 142,
"category_id": 4,
"title": "Acme Importer",
"download_state": "visible",
"current_version_id": 188,
"...": "..."
}
}
If the category requires approval and the visitor does not bypass it, download_state is moderated.
Update a download
PUT /api/mc-dm-downloads/<download_id>/
Scope: mc_dm_download:write.
Permission: canEdit. Changing category_id additionally requires canMove.
| Field | Type |
|---|---|
title | string |
tag_line | string |
description | string |
prefix_id | uint (0 to clear) |
category_id | uint (move) |
tags | string[] |
Fields omitted from the request body are left untouched. An empty string is a deliberate clear. Sending no key at all is a no-op.
curl -X PUT -H "XF-Api-Key: $KEY" \
-d "tag_line=Now with magic" \
https://example.com/api/mc-dm-downloads/142/
Response: { "download": { ... } }.
Delete a download
DELETE /api/mc-dm-downloads/<download_id>/
Scope: mc_dm_download:write always. mc_dm_download:delete_hard is required additionally when hard=1.
Permission: canDelete. Hard delete additionally requires canHardDelete.
| Param | Effect |
|---|---|
hard | 1 = hard delete (irreversible). Default 0 = soft delete (recoverable for the retention window). |
reason | Optional string written to the audit log. |
Soft delete
curl -X DELETE -H "XF-Api-Key: $KEY" \
-d "reason=Spam" \
https://example.com/api/mc-dm-downloads/142/
Hard delete
curl -X DELETE -H "XF-Api-Key: $KEY" \
-d "hard=1" \
-d "reason=DMCA" \
https://example.com/api/mc-dm-downloads/142/
Both return { "success": true }. The audit log records the action type as download.soft_deleted or download.hard_deleted.
Verbose mode (?with=full)
Adds the following keys:
| Field | Type | Notes |
|---|---|---|
can_view | bool | Visitor permission. |
can_edit | bool | Visitor permission. |
can_delete | bool | Visitor permission. |
can_download | bool | Visitor permission. |
is_visible | bool | |
is_password_protected | bool | |
Category | object | Embedded category result at normal verbosity. |
CurrentVersion | object | Embedded version result at normal verbosity. Present when set. |