DevPlace REST: Posts and Comments
Endpoints for the home page and feed plus the full create, read, edit and delete cycle for posts and comments, with their form fields, topic enum, attachment_uids handling, pagination cursors and JSON payload shapes.
The core content types. Read endpoints render HTML pages; write endpoints accept form fields and redirect to the new or updated resource. List fields such as attachmentuids are repeated form keys - upload files first via Uploads and pass the returned uids here. Engage with this content through Votes, Reactions, Bookmarks & Polls.
Every endpoint follows the shared Conventions & Errors (auth, content negotiation, pagination, status codes); see Authentication for the four ways to sign requests.
GET / - Home
The home page. Guests see the marketing splash; authenticated users see a personalized home (welcome, feed shortcut, latest posts, news). It no longer redirects to /feed. The latest-posts section interleaves authors so no two consecutive posts share an author.
Minimal role: Public
Sample response
{
"is_authenticated": false,
"user_post_count": 0,
"user_stars": 0,
"user_xp": 0,
"user_level": 0,
"xp_progress_pct": 0,
"unread_count": 0,
"landing_articles": [
{
"uid": "UID",
"slug": "slug",
"title": "Title",
"description": "text",
"url": "/path",
"source_name": "string",
"grade": 0,
"featured": 0,
"synced_at": "2026-01-01T00:00:00+00:00",
"time_ago": "2 hours ago",
"image_url": "/path"
}
],
"landing_posts": [
{
"post": null,
"author": {
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
},
"time_ago": "2 hours ago",
"comment_count": 0,
"stars": 0,
"slug": "slug"
}
],
"top_contributors": [],
"trending_topics": [
{
"topic": "random",
"count": 0
}
]
}
GET /feed - Browse the feed
The main post feed. Returns an HTML page. Each page interleaves authors so no two consecutive posts share an author.
Minimal role: Public
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
tab | query | enum | no | Feed selector. Allowed: all, trending, following. |
topic | query | enum | no | Filter by topic. Allowed: devlog, showcase, question, rant, fun, random, politics. |
search | query | string | no | Search post title, content, and author username. |
before | query | string | no | Pagination cursor. |
Sample response
{
"posts": [
{
"post": {
"uid": "UID",
"slug": "slug",
"user_uid": "UID",
"title": "Title",
"content": "text",
"topic": "random",
"stars": 0,
"image": "string",
"project_uid": "UID",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
},
"maturity": "string",
"author": {
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
},
"time_ago": "2 hours ago",
"my_vote": 0,
"comment_count": 0,
"attachments": [
{
"uid": "UID",
"filename": "string",
"url": "/path",
"size": 0,
"is_image": false,
"is_video": false,
"mime_type": "string",
"created_at": "2026-01-01T00:00:00+00:00",
"can_modify": false
}
],
"recent_comments": [
{
"comment": {
"uid": "UID",
"user_uid": "UID",
"content": "text",
"parent_uid": "UID",
"target_type": "string",
"target_uid": "UID",
"created_at": "2026-01-01T00:00:00+00:00"
},
"author": {
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
},
"time_ago": "2 hours ago",
"votes": {
"up": 0,
"down": 0
},
"my_vote": 0,
"children": [],
"attachments": [
{
"uid": "UID",
"filename": "string",
"url": "/path",
"size": 0,
"is_image": false,
"is_video": false,
"mime_type": "string",
"created_at": "2026-01-01T00:00:00+00:00",
"can_modify": false
}
],
"reactions": {
"counts": {},
"mine": []
}
}
],
"reactions": {
"counts": {},
"mine": []
},
"bookmarked": false,
"poll": {
"uid": "UID",
"question": "string",
"options": [
{
"uid": "UID",
"label": "string",
"count": 0,
"votes": 0,
"pct": 0
}
],
"total": 0,
"my_choice": "string",
"voted": "string"
},
"project_link": {
"uid": "UID",
"name": "string",
"slug": "slug",
"url": "/path"
}
}
],
"current_tab": "string",
"current_topic": "random",
"search": "string",
"next_cursor": "2026-01-01T00:00:00+00:00",
"total_members": 0,
"posts_today": 0,
"total_projects": 0,
"total_gists": 0,
"top_authors": [
{
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
}
],
"daily_topic": null,
"online_users": [
{
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
}
]
}
POST /posts/create - Create a post
Publish a post, optionally with a poll. Redirects to the new post.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
content | form | textarea | yes | Body, 10-125000 characters. |
title | form | string | no | Optional title, up to 500 characters. |
topic | form | enum | no | Post topic. Allowed: devlog, showcase, question, rant, fun, random, politics. |
project_uid | form | string | no | Attach to a project. |
poll_question | form | string | no | Optional poll question. |
poll_options | form | string | no | Repeat the field for each poll option, or send a single newline- or comma-separated string (2-6 options). |
Returns a
302redirect to/posts/{slug}on success.
Sample response
{
"ok": true,
"redirect": "/posts/POST_SLUG",
"data": {
"uid": "POST_UID",
"slug": "POST_SLUG",
"url": "/posts/POST_SLUG"
}
}
GET /posts/{post_slug} - View a post
Render a post with comments. Returns an HTML page.
Minimal role: Public
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
post_slug | path | string | yes | Slug or UID of the post. |
Sample response
{
"maturity": "string",
"post": {
"uid": "UID",
"slug": "slug",
"user_uid": "UID",
"title": "Title",
"content": "text",
"topic": "random",
"stars": 0,
"image": "string",
"project_uid": "UID",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
},
"author": {
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
},
"is_owner": false,
"star_count": 0,
"my_vote": 0,
"time_ago": "2 hours ago",
"comments": [
{
"comment": {
"uid": "UID",
"user_uid": "UID",
"content": "text",
"parent_uid": "UID",
"target_type": "string",
"target_uid": "UID",
"created_at": "2026-01-01T00:00:00+00:00"
},
"author": {
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
},
"time_ago": "2 hours ago",
"votes": {
"up": 0,
"down": 0
},
"my_vote": 0,
"children": [],
"attachments": [
{
"uid": "UID",
"filename": "string",
"url": "/path",
"size": 0,
"is_image": false,
"is_video": false,
"mime_type": "string",
"created_at": "2026-01-01T00:00:00+00:00",
"can_modify": false
}
],
"reactions": {
"counts": {},
"mine": []
}
}
],
"attachments": [
{
"uid": "UID",
"filename": "string",
"url": "/path",
"size": 0,
"is_image": false,
"is_video": false,
"mime_type": "string",
"created_at": "2026-01-01T00:00:00+00:00",
"can_modify": false
}
],
"reactions": {
"counts": {},
"mine": []
},
"bookmarked": false,
"poll": {
"uid": "UID",
"question": "string",
"options": [
{
"uid": "UID",
"label": "string",
"count": 0,
"votes": 0,
"pct": 0
}
],
"total": 0,
"my_choice": "string",
"voted": "string"
},
"comment_count": 0,
"related_posts": [
{
"post": {
"uid": "UID",
"slug": "slug",
"user_uid": "UID",
"title": "Title",
"content": "text",
"topic": "random",
"stars": 0,
"image": "string",
"project_uid": "UID",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
},
"maturity": "string",
"author": {
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
},
"time_ago": "2 hours ago",
"my_vote": 0,
"comment_count": 0,
"attachments": [
{
"uid": "UID",
"filename": "string",
"url": "/path",
"size": 0,
"is_image": false,
"is_video": false,
"mime_type": "string",
"created_at": "2026-01-01T00:00:00+00:00",
"can_modify": false
}
],
"recent_comments": [
{
"comment": {
"uid": "UID",
"user_uid": "UID",
"content": "text",
"parent_uid": "UID",
"target_type": "string",
"target_uid": "UID",
"created_at": "2026-01-01T00:00:00+00:00"
},
"author": {
"uid": "UID",
"username": "username",
"avatar_seed": "string",
"bio": "text",
"location": "string",
"git_link": "string",
"website": "string",
"level": 0,
"xp": 0,
"xp_progress_pct": 0,
"xp_next_level": 0,
"stars": 0,
"created_at": "2026-01-01T00:00:00+00:00",
"last_seen": "string"
},
"time_ago": "2 hours ago",
"votes": {
"up": 0,
"down": 0
},
"my_vote": 0,
"children": [],
"attachments": [
{
"uid": "UID",
"filename": "string",
"url": "/path",
"size": 0,
"is_image": false,
"is_video": false,
"mime_type": "string",
"created_at": "2026-01-01T00:00:00+00:00",
"can_modify": false
}
],
"reactions": {
"counts": {},
"mine": []
}
}
],
"reactions": {
"counts": {},
"mine": []
},
"bookmarked": false,
"poll": {
"uid": "UID",
"question": "string",
"options": [
{
"uid": "UID",
"label": "string",
"count": 0,
"votes": 0,
"pct": 0
}
],
"total": 0,
"my_choice": "string",
"voted": "string"
},
"project_link": {
"uid": "UID",
"name": "string",
"slug": "slug",
"url": "/path"
}
}
],
"topics": [
"random"
],
"project_link": {
"uid": "UID",
"name": "string",
"slug": "slug",
"url": "/path"
}
}
POST /posts/edit/{post_slug} - Edit a post
Update a post you own, optionally adding a poll if it has none.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
post_slug | path | string | yes | Slug or UID of the post. |
content | form | textarea | yes | Body, 10-125000 characters. |
title | form | string | no | Optional title. |
topic | form | enum | no | Post topic. Allowed: devlog, showcase, question, rant, fun, random, politics. |
poll_question | form | string | no | Optional poll question. Adds a poll only when the post has none. |
poll_options | form | string | no | Repeat the field for each poll option, or send a single newline- or comma-separated string (2-6 options). |
Sample response
{
"ok": true,
"redirect": "/posts/POST_SLUG",
"data": {
"uid": "POST_UID",
"slug": "POST_SLUG",
"url": "/posts/POST_SLUG"
}
}
POST /posts/delete/{post_slug} - Delete a post
Delete a post you own; administrators may delete any user's post. Soft-deleted (hidden everywhere but restorable from admin trash) and cascades its comments and votes.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
post_slug | path | string | yes | Slug or UID of the post. |
Sample response
{
"ok": true,
"redirect": "/feed",
"data": null
}
POST /comments/create - Create a comment
Comment on any commentable target. Supports nested replies.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
content | form | textarea | yes | Body, 3-1000 characters. |
target_uid | form | string | no | UID of the target (or use post_uid). |
post_uid | form | string | no | Convenience alias for a post target. |
target_type | form | enum | no | Type of the target. Allowed: post, project, news, issue, gist, quiz. |
parent_uid | form | string | no | Parent comment UID for a reply. |
Either
targetuidorpostuidis required.
Sample response
{
"ok": true,
"redirect": "/posts/POST_SLUG#comment-COMMENT_UID",
"data": {
"uid": "COMMENT_UID",
"url": "/posts/POST_SLUG#comment-COMMENT_UID"
}
}
POST /comments/edit/{comment_uid} - Edit a comment
Edit the body of a comment you own. Returns the updated comment.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
comment_uid | path | string | yes | UID of the comment. |
content | form | textarea | yes | New body, 3-1000 characters. |
Sample response
{
"uid": "COMMENT_UID",
"content": "Edited body.",
"url": "/posts/POST_SLUG#comment-COMMENT_UID",
"updated_at": "2026-06-15T12:00:00+00:00"
}
POST /comments/delete/{comment_uid} - Delete a comment
Delete a comment you own; administrators may delete any user's comment. Soft-deleted (hidden everywhere but restorable from admin trash).
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
comment_uid | path | string | yes | UID of the comment. |
Sample response
{
"ok": true,
"redirect": "/posts/POST_SLUG",
"data": {
"deleted_uid": "COMMENT_UID",
"target_type": "post",
"target_uid": "TARGET_UID"
}
}