platforms/devplace/rest_uploads.md

DevPlace REST: Attachment Uploads

Uploading files with multipart/form-data or by handing the server a public URL, and the full attachment lifecycle - list, get, rename, delete - plus how the returned uid is referenced from posts, comments, projects, gists, messages and issues.

Attachment storage. Upload a file - or hand the server a public URL to fetch - to receive an attachment record, then reference its uid in an attachmentuids field when creating a post, comment, project, gist, message, or issue - see Posts, Comments, Projects, Gists & News. Images and videos embed and play inline once posted; other types render as download links. The record's isimage and isvideo flags indicate how the file is displayed.

You manage your own attachments over the full lifecycle: list every file you uploaded, get one by uid, rename its display filename, and delete it. The list is the same set of attachments that appear on your posts and other content - listing, renaming, or deleting one is reflected everywhere it is used.

Every endpoint follows the shared Conventions & Errors (auth, content negotiation, pagination, status codes); see Authentication for the four ways to sign requests.

POST /uploads/upload - Upload a file

Store a file and return its attachment record.

Minimal role: Member

Parameters

NameInTypeRequiredDescription
fileformfileyesThe file to upload.

Allowed file types and the size limit are configured by administrators. Images and common video formats (mp4, webm, ogv, mov, m4v) are accepted by default.

Returns 201 on success, 413 if too large, 415 if the type is not allowed.

Sample response

{
  "uid": "ATTACHMENT_UID",
  "filename": "clip.mp4",
  "url": "/static/uploads/attachments/ab/cd/ATTACHMENT_UID.mp4",
  "size": 20480,
  "is_image": false,
  "is_video": true,
  "mime_type": "video/mp4"
}

POST /uploads/upload-url - Attach a file from a URL

Download a public URL on the server and store it as an attachment.

Minimal role: Member

Parameters

NameInTypeRequiredDescription
urlformstringyesPublic http(s) URL of the file to download and attach.
filenameformstringnoOptional filename with an allowed extension, used when the URL has no clear name.

The server fetches the URL (SSRF-guarded, size-capped) and stores the bytes through the same pipeline as a direct upload; the response is identical to Upload a file.

The file type is taken from the URL path or the response Content-Type. Returns 201 on success, 413 if too large, 415 if the type cannot be resolved to an allowed type, 400 for an unreachable or private address.

Sample response

{
  "uid": "ATTACHMENT_UID",
  "filename": "photo.png",
  "url": "/static/uploads/attachments/ab/cd/ATTACHMENT_UID.png",
  "size": 20480,
  "is_image": true,
  "is_video": false,
  "mime_type": "image/png"
}

GET /uploads - List your attachments

List every attachment you uploaded, newest first, paginated (24 per page).

Minimal role: Member

Parameters

NameInTypeRequiredDescription
pagequeryintegerno1-based page number.
linkedquerystringnoFilter: true returns only attachments already used on a post/comment/project/gist/issue, false returns only orphaned uploads. Omit for all.

Each item carries uid, originalfilename, mimetype, url, filesize, its targettype/targetuid/targeturl when linked, and a linked flag.

Sample response

{
  "attachments": [
    {
      "uid": "ATTACHMENT_UID",
      "original_filename": "photo.png",
      "file_size": 20480,
      "mime_type": "image/png",
      "url": "/static/uploads/attachments/ab/cd/ATTACHMENT_UID.png",
      "is_image": true,
      "is_video": false,
      "is_audio": false,
      "linked": true,
      "target_type": "post",
      "target_uid": "POST_UID",
      "target_url": "/posts/POST_SLUG",
      "created_at": "2026-01-01T12:00:00+00:00"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 24,
    "total": 1,
    "total_pages": 1
  },
  "total": 1
}

GET /uploads/{attachment_uid} - Get one attachment

Fetch the metadata of a single attachment you own; administrators may fetch any user's attachment.

Minimal role: Member

Parameters

NameInTypeRequiredDescription
attachment_uidpathstringyesUID of the attachment.

Returns 404 if the attachment does not exist, 403 if it is not yours.

Sample response

{
  "uid": "ATTACHMENT_UID",
  "original_filename": "photo.png",
  "file_size": 20480,
  "mime_type": "image/png",
  "url": "/static/uploads/attachments/ab/cd/ATTACHMENT_UID.png",
  "is_image": true,
  "is_video": false,
  "is_audio": false,
  "linked": true,
  "target_type": "post",
  "target_uid": "POST_UID",
  "target_url": "/posts/POST_SLUG",
  "created_at": "2026-01-01T12:00:00+00:00"
}

PATCH /uploads/{attachment_uid} - Rename an attachment

Change the display filename of an attachment you own; administrators may rename any user's attachment.

Minimal role: Member

Parameters

NameInTypeRequiredDescription
attachment_uidpathstringyesUID of the attachment.
filenameformstringyesNew display filename.

Only the display filename changes; the stored file and its extension are untouched. The original extension is always preserved, so the file type cannot be altered.

Returns the updated attachment record. 404 if it does not exist, 403 if it is not yours, 400 for an empty filename.

Sample response

{
  "uid": "ATTACHMENT_UID",
  "original_filename": "renamed.png",
  "file_size": 20480,
  "mime_type": "image/png",
  "url": "/static/uploads/attachments/ab/cd/ATTACHMENT_UID.png",
  "is_image": true,
  "linked": true,
  "target_type": "post",
  "target_uid": "POST_UID",
  "target_url": "/posts/POST_SLUG",
  "created_at": "2026-01-01T12:00:00+00:00"
}

DELETE /uploads/delete/{attachment_uid} - Delete an attachment

Remove an attachment you previously uploaded; administrators may remove any user's attachment.

Minimal role: Member

Parameters

NameInTypeRequiredDescription
attachment_uidpathstringyesUID of the attachment (the uid returned by Upload a file, Attach a file from a URL, or List your attachments).

Only the owner may delete their own attachment; an administrator may delete any user's. Deleting one you do not own returns 403.

The attachment is removed everywhere at once: it leaves your attachment list (List your attachments) and disappears from every post, comment, project, gist, message, or issue it was attached to, and its file stops being served under /static/uploads/.

Idempotent from the caller's view: an already-removed or unknown uid returns 404. A successful delete returns 200 with {"status": "deleted"}.

To detach a file from a single post/comment without removing the upload itself, edit that object's attachment list instead - deleting here removes the attachment from every place it is used.

Sample response

{
  "status": "deleted"
}