--- title: "DevPlace REST: Attachment Uploads" description: "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." language: null framework: null category: api_design tags: - devplace - api - http-api - uploads - attachments - multipart - file-storage - rest - media keywords: - devplace POST /uploads/upload multipart - devplace upload from url endpoint - devplace attachment uid attachment_uids - devplace rename delete attachment api - devplace is_image is_video attachment flags last_updated: 2026-08-12 difficulty: beginner version: "DevPlace (devplace.net), documented 2026-08" related: - rest_posts_and_comments.md - conventions_and_errors.md - authentication.md - README.md search_priority: normal status: published --- # DevPlace REST: Attachment Uploads 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 `attachment_uids` field when creating a post, comment, project, gist, message, or issue - see [Posts, Comments, Projects, Gists & News](rest_posts_and_comments.md). Images and videos embed and play inline once posted; other types render as download links. The record's `is_image` and `is_video` 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](conventions_and_errors.md) (auth, content negotiation, pagination, status codes); see [Authentication](authentication.md) 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** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `file` | form | file | yes | The 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** ```json { "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** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `url` | form | string | yes | Public http(s) URL of the file to download and attach. | | `filename` | form | string | no | Optional 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** ```json { "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** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `page` | query | integer | no | 1-based page number. | | `linked` | query | string | no | Filter: `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`, `original_filename`, `mime_type`, `url`, `file_size`, its `target_type`/`target_uid`/`target_url` when linked, and a `linked` flag. **Sample response** ```json { "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** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `attachment_uid` | path | string | yes | UID of the attachment. | > Returns `404` if the attachment does not exist, `403` if it is not yours. **Sample response** ```json { "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** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `attachment_uid` | path | string | yes | UID of the attachment. | | `filename` | form | string | yes | New 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** ```json { "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** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `attachment_uid` | path | string | yes | UID 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** ```json { "status": "deleted" } ```