# retoor <retoor@molodetz.nl>
from .._shared import COMMENT_TARGETS, GIST_LANGUAGES, PROJECT_TYPES, endpoint, field
from devplacepy.constants import TOPICS
GROUP = {
"slug": "content",
"title": "Posts, Comments, Projects, Gists & News",
"intro": """
# Posts, Comments, Projects, Gists & News
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 `attachment_uids` are
repeated form keys - upload files first via [Uploads](/docs/uploads.html) and pass the returned
uids here. Engage with this content through [Votes, Reactions, Bookmarks & Polls](/docs/social-actions.html).
Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html) (auth, content
negotiation, pagination, status codes); see [Authentication](/docs/authentication.html) for the
four ways to sign requests.
""",
"endpoints": [
endpoint(
id="home",
method="GET",
path="/",
title="Home",
summary=(
"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."
),
auth="public",
interactive=True,
),
endpoint(
id="feed-list",
method="GET",
path="/feed",
title="Browse the feed",
summary="The main post feed. Returns an HTML page. Each page interleaves authors so no two consecutive posts share an author.",
auth="public",
interactive=True,
params=[
field(
"tab",
"query",
"enum",
False,
"all",
"Feed selector.",
["all", "trending", "following"],
),
field(
"topic", "query", "enum", False, "", "Filter by topic.", TOPICS
),
field(
"search",
"query",
"string",
False,
"",
"Search post title, content, and author username.",
),
field("before", "query", "string", False, "", "Pagination cursor."),
],
),
endpoint(
id="posts-create",
method="POST",
path="/posts/create",
title="Create a post",
summary="Publish a post, optionally with a poll. Redirects to the new post.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"content",
"form",
"textarea",
True,
"Posted from a script.",
"Body, 10-125000 characters.",
),
field(
"title",
"form",
"string",
False,
"Hello",
"Optional title, up to 500 characters.",
),
field(
"topic", "form", "enum", False, "random", "Post topic.", TOPICS
),
field(
"project_uid",
"form",
"string",
False,
"",
"Attach to a project.",
),
field(
"poll_question",
"form",
"string",
False,
"",
"Optional poll question.",
),
field(
"poll_options",
"form",
"string",
False,
"",
"Repeat the field for each poll option, or send a single newline- or comma-separated string (2-6 options).",
),
],
notes=["Returns a `302` redirect to `/posts/{slug}` on success."],
),
endpoint(
id="posts-detail",
method="GET",
path="/posts/{post_slug}",
title="View a post",
summary="Render a post with comments. Returns an HTML page.",
auth="public",
interactive=True,
params=[
field(
"post_slug",
"path",
"string",
True,
"POST_SLUG",
"Slug or UID of the post.",
)
],
),
endpoint(
id="posts-edit",
method="POST",
path="/posts/edit/{post_slug}",
title="Edit a post",
summary="Update a post you own, optionally adding a poll if it has none.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"post_slug",
"path",
"string",
True,
"POST_SLUG",
"Slug or UID of the post.",
),
field(
"content",
"form",
"textarea",
True,
"Updated body.",
"Body, 10-125000 characters.",
),
field(
"title",
"form",
"string",
False,
"Updated title",
"Optional title.",
),
field(
"topic", "form", "enum", False, "random", "Post topic.", TOPICS
),
field(
"poll_question",
"form",
"string",
False,
"",
"Optional poll question. Adds a poll only when the post has none.",
),
field(
"poll_options",
"form",
"string",
False,
"",
"Repeat the field for each poll option, or send a single newline- or comma-separated string (2-6 options).",
),
],
),
endpoint(
id="posts-delete",
method="POST",
path="/posts/delete/{post_slug}",
title="Delete a post",
summary="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.",
auth="user",
destructive=True,
params=[
field(
"post_slug",
"path",
"string",
True,
"POST_SLUG",
"Slug or UID of the post.",
)
],
),
endpoint(
id="comments-create",
method="POST",
path="/comments/create",
title="Create a comment",
summary="Comment on any commentable target. Supports nested replies.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"content",
"form",
"textarea",
True,
"Nice work.",
"Body, 3-1000 characters.",
),
field(
"target_uid",
"form",
"string",
False,
"POST_UID",
"UID of the target (or use post_uid).",
),
field(
"post_uid",
"form",
"string",
False,
"",
"Convenience alias for a post target.",
),
field(
"target_type",
"form",
"enum",
False,
"post",
"Type of the target.",
COMMENT_TARGETS,
),
field(
"parent_uid",
"form",
"string",
False,
"",
"Parent comment UID for a reply.",
),
],
notes=["Either `target_uid` or `post_uid` is required."],
),
endpoint(
id="comments-edit",
method="POST",
path="/comments/edit/{comment_uid}",
title="Edit a comment",
summary="Edit the body of a comment you own. Returns the updated comment.",
auth="user",
encoding="form",
params=[
field(
"comment_uid",
"path",
"string",
True,
"COMMENT_UID",
"UID of the comment.",
),
field(
"content",
"form",
"textarea",
True,
"Edited body.",
"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",
},
),
endpoint(
id="comments-delete",
method="POST",
path="/comments/delete/{comment_uid}",
title="Delete a comment",
summary="Delete a comment you own; administrators may delete any user's comment. Soft-deleted (hidden everywhere but restorable from admin trash).",
auth="user",
destructive=True,
params=[
field(
"comment_uid",
"path",
"string",
True,
"COMMENT_UID",
"UID of the comment.",
)
],
),
endpoint(
id="projects-list",
method="GET",
path="/projects",
title="Browse projects",
summary="List projects. Returns an HTML page.",
auth="public",
interactive=True,
params=[
field(
"tab",
"query",
"enum",
False,
"recent",
"Sort selector.",
["recent", "popular", "released"],
),
field(
"search",
"query",
"string",
False,
"",
"Search project title, description, and author username.",
),
field(
"project_type",
"query",
"enum",
False,
"",
"Filter by type.",
PROJECT_TYPES,
),
field("before", "query", "string", False, "", "Pagination cursor."),
],
),
endpoint(
id="projects-detail",
method="GET",
path="/projects/{project_slug}",
title="View a project",
summary="Render a project with comments. Returns an HTML page.",
auth="public",
interactive=True,
params=[
field(
"project_slug",
"path",
"string",
True,
"PROJECT_SLUG",
"Slug or UID of the project.",
)
],
),
endpoint(
id="projects-create",
method="POST",
path="/projects/create",
title="Create a project",
summary="Publish a project. Redirects to the new project.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"title",
"form",
"string",
True,
"My Project",
"Title, 1-200 characters.",
),
field(
"description",
"form",
"textarea",
True,
"What it does.",
"Description, 1-5000 characters.",
),
field(
"project_type",
"form",
"enum",
False,
"software",
"Project type.",
PROJECT_TYPES,
),
field(
"status",
"form",
"string",
False,
"In Development",
"Free-form status label.",
),
field(
"platforms",
"form",
"string",
False,
"Linux, Web",
"Comma-separated platforms.",
),
field(
"release_date",
"form",
"string",
False,
"31/12/2026",
"Optional release date in DD/MM/YYYY format.",
),
field(
"demo_date",
"form",
"string",
False,
"31/12/2026",
"Optional demo date in DD/MM/YYYY format.",
),
],
),
endpoint(
id="projects-edit",
method="POST",
path="/projects/edit/{project_slug}",
title="Edit a project",
summary="Update an owned project. Redirects to the project.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"project_slug",
"path",
"string",
True,
"my-project-1a2b3c4d",
"Project slug or uid.",
),
field(
"title",
"form",
"string",
True,
"My Project",
"Title, 1-200 characters.",
),
field(
"description",
"form",
"textarea",
True,
"What it does.",
"Description, 1-5000 characters.",
),
field(
"project_type",
"form",
"enum",
False,
"software",
"Project type.",
PROJECT_TYPES,
),
field(
"status",
"form",
"string",
False,
"In Development",
"Free-form status label.",
),
field(
"platforms",
"form",
"string",
False,
"Linux, Web",
"Comma-separated platforms.",
),
field(
"release_date",
"form",
"string",
False,
"31/12/2026",
"Optional release date in DD/MM/YYYY format.",
),
field(
"demo_date",
"form",
"string",
False,
"31/12/2026",
"Optional demo date in DD/MM/YYYY format.",
),
],
),
endpoint(
id="projects-delete",
method="POST",
path="/projects/delete/{project_slug}",
title="Delete a project",
summary="Delete a project you own; administrators may delete any user's project. Soft-deleted with all of its files (hidden everywhere but restorable from admin trash).",
auth="user",
destructive=True,
params=[
field(
"project_slug",
"path",
"string",
True,
"PROJECT_SLUG",
"Slug or UID of the project.",
)
],
),
endpoint(
id="projects-private",
method="POST",
path="/projects/{project_slug}/private",
title="Set project visibility",
summary="Mark a project you own private or public. Send value=1 for private, value=0 for public. A project you hide as a member stays visible to administrators; a project you hide as an administrator is visible only to you, not to other administrators (this also hides its files and any attached containers).",
auth="user",
encoding="form",
params=[
field(
"project_slug",
"path",
"string",
True,
"PROJECT_SLUG",
"Slug or UID of the project.",
),
field(
"value",
"form",
"boolean",
True,
"1",
"1 to make the project private, 0 to make it public.",
),
],
),
endpoint(
id="projects-readonly",
method="POST",
path="/projects/{project_slug}/readonly",
title="Set project read-only",
summary="Mark a project you own read-only so all of its files become immutable (no writes, edits, moves, deletes, or uploads succeed), or writable again. Send value=1 for read-only, value=0 for writable.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"project_slug",
"path",
"string",
True,
"PROJECT_SLUG",
"Slug or UID of the project.",
),
field(
"value",
"form",
"boolean",
True,
"1",
"1 to make the project read-only, 0 to make it writable.",
),
],
),
endpoint(
id="projects-zip",
method="POST",
path="/projects/{project_slug}/zip",
title="Queue a project zip",
summary="Start a background job that archives the whole project. Returns the job uid and status URL to poll.",
auth="public",
params=[
field(
"project_slug",
"path",
"string",
True,
"PROJECT_SLUG",
"Slug or UID of the project.",
)
],
sample_response={
"uid": "ZIP_JOB_UID",
"status_url": "/zips/ZIP_JOB_UID",
},
),
endpoint(
id="zips-status",
method="GET",
path="/zips/{uid}",
title="Zip job status",
summary="Poll a zip job. While pending or running download_url is null; once done it points at the archive.",
auth="public",
params=[
field(
"uid",
"path",
"string",
True,
"ZIP_JOB_UID",
"Zip job uid returned when the job was queued.",
)
],
sample_response={
"uid": "ZIP_JOB_UID",
"kind": "zip",
"status": "done",
"preferred_name": "my-project",
"download_url": "/zips/ZIP_JOB_UID/download",
"error": None,
"bytes_in": 20480,
"bytes_out": 8192,
"item_count": 12,
"file_count": 10,
"dir_count": 2,
"created_at": "2026-06-09T10:00:00+00:00",
"completed_at": "2026-06-09T10:00:03+00:00",
},
),
endpoint(
id="zips-download",
method="GET",
path="/zips/{uid}/download",
title="Download a zip archive",
summary="Stream the finished archive as application/zip. Each access extends the retention window.",
auth="public",
interactive=True,
params=[
field(
"uid",
"path",
"string",
True,
"ZIP_JOB_UID",
"Zip job uid of a finished job.",
)
],
),
endpoint(
id="projects-fork",
method="POST",
path="/projects/{project_slug}/fork",
title="Queue a project fork",
summary="Start a background job that copies the whole project into a new project owned by you. Returns the job uid and status URL to poll.",
auth="user",
params=[
field(
"project_slug",
"path",
"string",
True,
"PROJECT_SLUG",
"Slug or UID of the project to fork.",
),
field(
"title",
"form",
"string",
True,
"My Fork",
"Title for the new forked project.",
),
],
sample_response={
"uid": "FORK_JOB_UID",
"status_url": "/forks/FORK_JOB_UID",
},
),
endpoint(
id="forks-status",
method="GET",
path="/forks/{uid}",
title="Fork job status",
summary="Poll a fork job. While pending or running project_url is null; once done it points at the new project.",
auth="public",
params=[
field(
"uid",
"path",
"string",
True,
"FORK_JOB_UID",
"Fork job uid returned when the job was queued.",
)
],
sample_response={
"uid": "FORK_JOB_UID",
"kind": "fork",
"status": "done",
"preferred_name": "My Fork",
"project_uid": "NEW_PROJECT_UID",
"project_url": "/projects/new-project-slug",
"source_project_uid": "SOURCE_PROJECT_UID",
"error": None,
"item_count": 12,
"created_at": "2026-06-09T10:00:00+00:00",
"completed_at": "2026-06-09T10:00:05+00:00",
},
),
endpoint(
id="gists-list",
method="GET",
path="/gists",
title="Browse gists",
summary="List code gists. Returns an HTML page.",
auth="public",
interactive=True,
params=[
field(
"language",
"query",
"enum",
False,
"",
"Filter by language.",
GIST_LANGUAGES,
),
field(
"user_uid",
"query",
"string",
False,
"",
"Filter by author UID.",
),
field(
"search",
"query",
"string",
False,
"",
"Search gist title, description, and author username.",
),
field("before", "query", "string", False, "", "Pagination cursor."),
],
),
endpoint(
id="gists-detail",
method="GET",
path="/gists/{gist_slug}",
title="View a gist",
summary="Render a gist with comments. Returns an HTML page.",
auth="public",
interactive=True,
params=[
field(
"gist_slug",
"path",
"string",
True,
"GIST_SLUG",
"Slug or UID of the gist.",
)
],
),
endpoint(
id="gists-create",
method="POST",
path="/gists/create",
title="Create a gist",
summary="Publish a code snippet. Redirects to the new gist.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"title",
"form",
"string",
True,
"Quick sort",
"Title, 1-200 characters.",
),
field(
"source_code",
"form",
"textarea",
True,
"print('hello')",
"Source, 1-400000 characters.",
),
field(
"language",
"form",
"enum",
False,
"python",
"Syntax language.",
GIST_LANGUAGES,
),
field(
"description",
"form",
"string",
False,
"",
"Optional description.",
),
],
),
endpoint(
id="gists-edit",
method="POST",
path="/gists/edit/{gist_slug}",
title="Edit a gist",
summary="Update a gist you own.",
auth="user",
encoding="form",
destructive=True,
params=[
field(
"gist_slug",
"path",
"string",
True,
"GIST_SLUG",
"Slug or UID of the gist.",
),
field(
"title",
"form",
"string",
True,
"Quick sort",
"Title, 1-200 characters.",
),
field(
"source_code",
"form",
"textarea",
True,
"print('hi')",
"Source, 1-400000 characters.",
),
field(
"language",
"form",
"enum",
False,
"python",
"Syntax language.",
GIST_LANGUAGES,
),
field(
"description",
"form",
"string",
False,
"",
"Optional description.",
),
],
),
endpoint(
id="gists-delete",
method="POST",
path="/gists/delete/{gist_slug}",
title="Delete a gist",
summary="Delete a gist you own; administrators may delete any user's gist. Soft-deleted (hidden everywhere but restorable from admin trash).",
auth="user",
destructive=True,
params=[
field(
"gist_slug",
"path",
"string",
True,
"GIST_SLUG",
"Slug or UID of the gist.",
)
],
),
endpoint(
id="news-list",
method="GET",
path="/news",
title="Browse news",
summary="Curated developer news. Returns an HTML page.",
auth="public",
interactive=True,
params=[
field(
"before",
"query",
"string",
False,
"",
"Pagination cursor (synced_at).",
)
],
),
endpoint(
id="news-detail",
method="GET",
path="/news/{news_slug}",
title="View a news article",
summary="Render a news article with comments. Returns an HTML page.",
auth="public",
interactive=True,
params=[
field(
"news_slug",
"path",
"string",
True,
"NEWS_SLUG",
"Slug or UID of the article.",
)
],
),
],
}