monoesdocs
Menu

API reference

All endpoints

Every endpoint is under https://monoes.me. Base path for all resources below: /api/community (except the file server, which is called out). See Authentication for what the auth badges mean.

Feed

The unified, cross-resource activity feed shown on the community homepage.

GET/api/community/feedPublic

List recent activity across posts, bugs, features, and org uploads.

Request

Query params: sort ('latest' | 'popular'), page (number, 0-indexed), authorId (string, optional)

Response

{ items: FeedItem[], hasMore: boolean }

Session is read if present (to compute the viewer's own vote on each item) but not required. A Bearer token without community:read is silently treated as anonymous rather than rejected.

Bugs

Bug reports, voting, comments, and moderation labels.

POST/api/community/bugscommunity:write

File a bug report.

Request

{ title: string, description: string, severity: 'low'|'medium'|'high'|'critical' }

Response

201 { id, title, description, authorId, status: 'open', severity, createdAt, updatedAt }

title 1-100 chars, description 1-1000 chars.

PATCH/api/community/bugs/{id}community:write · admin/moderator

Update a bug's status and/or severity.

Request

{ status?: 'open'|'in_progress'|'resolved'|'wontfix', severity?: 'low'|'medium'|'high'|'critical' }

Response

{ status?, severity? }

At least one field required.

DELETE/api/community/bugs/{id}community:write · admin/moderator

Delete a bug report.

Response

{ id }
POST/api/community/bugs/{id}/votecommunity:write

Upvote, downvote, or clear your vote on a bug.

Request

{ value: 1 | -1 | 0 }

Response

{ score: number, myVote: 1|-1|0 }

0 removes an existing vote. Idempotent upsert per (bug, user).

POST/api/community/bugs/{id}/commentscommunity:write

Post a comment on a bug.

Request

{ body: string }

Response

201 { id, bugId, authorId, authorUsername, body, createdAt }

body 1-1000 chars.

DELETE/api/community/bugs/{id}/comments/{commentId}community:write

Delete your own comment (or any comment, as a moderator/admin).

Response

{ id }
POST/api/community/bugs/{id}/labelscommunity:write · admin/moderator

Attach a label to a bug.

Request

{ labelId: string }

Response

{ bugId, labelId }

Idempotent: attaching an already-attached label is a no-op.

DELETE/api/community/bugs/{id}/labels/{labelId}community:write · admin/moderator

Detach a label from a bug.

Response

{ bugId, labelId }

Labels

Moderator-managed labels attachable to bug reports.

POST/api/community/labelscommunity:write · admin/moderator

Create a new label.

Request

{ name: string, color: string }

Response

201 { id, name, color }

name 1-30 chars; color is a #rrggbb hex string; 409 if the name is already taken.

Feature requests

Community feature requests and voting.

POST/api/community/featurescommunity:write

Submit a feature request.

Request

{ title: string, description: string }

Response

201 { id, title, description, authorId, status: 'open', createdAt, updatedAt }

title 1-100 chars, description 1-1000 chars.

DELETE/api/community/features/{id}community:write · admin/moderator

Delete a feature request.

Response

{ id }
PATCH/api/community/features/{id}/statuscommunity:write · admin

Change a feature request's status.

Request

{ status: 'open'|'planned'|'shipped'|'declined' }

Response

{ status }
POST/api/community/features/{id}/votecommunity:write

Upvote, downvote, or clear your vote on a feature request.

Request

{ value: 1 | -1 | 0 }

Response

{ score: number, myVote: 1|-1|0 }

Org gallery

Uploaded agent-org definitions, voting, comments, banner images, and run outputs.

POST/api/community/orgscommunity:write

Upload an org definition (JSON matching the org schema).

Request

{ orgJson: string }, a JSON-encoded org definition, max 500 KB

Response

201 { id, name, goal, topology, roleCount, createdAt }

orgJson is validated against a Zod schema before insert. The request also needs a numeric Content-Length header under ~1 MB — a chunked-transfer client with no Content-Length is rejected outright, regardless of the actual body size.

PATCH/api/community/orgs/{id}community:write

Update an org's name, tagline, description, body, or banner image.

Request

{ name?, tagline?, description?, body?, bannerUrl? } (all optional, at least one required)

Response

{ id, ...updatedFields }

Requires ownership (or moderator/admin). Limits: name 1-100 chars, tagline ≤150, description ≤1000, body ≤20000. bannerUrl must start with /api/images/org/ or be null; empty strings on tagline/description/body are coerced to null.

DELETE/api/community/orgs/{id}community:write

Delete an org upload.

Response

{ id }
POST/api/community/orgs/{id}/votecommunity:write

Upvote, downvote, or clear your vote on an org.

Request

{ value: 1 | -1 | 0 }

Response

{ score: number, myVote: 1|-1|0 }
POST/api/community/orgs/{id}/commentscommunity:write

Post a comment on an org.

Request

{ body: string }

Response

201 { id, orgUploadId, authorId, authorUsername, body, createdAt }

body 1-1000 chars.

DELETE/api/community/orgs/{id}/comments/{commentId}community:write

Delete your own comment (or any comment, as a moderator/admin).

Response

{ id }
POST/api/community/orgs/{id}/imagescommunity:write

Upload an image (used for the org body and/or banner).

Request

multipart/form-data, field 'image': PNG/JPEG/WebP, max 2 MB

Response

201 { url }
POST/api/community/orgs/{id}/runscommunity:write

Upload a run's output files (Markdown and/or HTML).

Request

multipart/form-data: field 'label' (string, optional, ≤100 chars), field 'files' (up to 10 .md/.html files, 2 MB each)

Response

201 { id, label: string | null, createdAt, files: [{ id, filename, fileType, sizeBytes }] }

label is optional despite the field existing — a blank or missing label is stored as null.

DELETE/api/community/orgs/{id}/runs/{runId}community:write

Delete a run and its files.

Response

{ id }

Requires ownership of the run (or moderator/admin).

GET/api/community/org-run-files/{fileId}Public

Fetch a run output file's raw content.

Response

The raw file body, Content-Type text/markdown or text/html.

Unauthenticated: anyone with the file id can view it, same as the linked org.

Posts

Free-form community posts and voting.

POST/api/community/postscommunity:write

Create a post.

Request

{ title: string, body: string }

Response

201 { id, title, body, authorId, createdAt }

title 1-100 chars, body 1-2000 chars.

POST/api/community/posts/{id}/votecommunity:write

Upvote, downvote, or clear your vote on a post.

Request

{ value: 1 | -1 | 0 }

Response

{ score: number, myVote: 1|-1|0 }

Blog comments

Comments on monoes.me blog posts.

POST/api/community/blog/{slug}/commentscommunity:write

Post a comment on a blog post.

Request

{ body: string }

Response

201 { id, postSlug, authorId, authorUsername, body, createdAt }

slug must match a real post; body 1-1000 chars.

DELETE/api/community/blog/{slug}/comments/{commentId}community:write

Delete your own comment (or any comment, as a moderator/admin).

Response

{ id }

Profile & identity

The authenticated user's own profile: identity, avatar, and username.

GET/api/community/mecommunity:read

Get the authenticated user's id, username, name, and avatar URL.

Response

{ id, username, name: string | null, avatarUrl: string | null }

avatarUrl (when set) is /api/images/avatar/<key>?v=<updatedAt ms> — the query param is a cache-buster, not part of the key.

PATCH/api/community/profilecommunity:write

Update your profile: name, tagline, job, tags, and social links.

Request

{ name, tagline?, jobTitle?, company?, tags?: string[], githubUrl?, twitterUrl?, linkedinUrl?, websiteUrl? }

Response

{ name, tagline, jobTitle, company, tags, githubUrl, twitterUrl, linkedinUrl, websiteUrl }

name required, 1-100 chars. tagline ≤140, jobTitle ≤80, company ≤80. Up to 10 tags (1-24 chars, alnum/underscore/hyphen). githubUrl/linkedinUrl must be https and match their platform's domain; twitterUrl accepts twitter.com or x.com (or a subdomain of either); websiteUrl just needs to be https.

POST/api/community/profile/avatarcommunity:write

Upload a new avatar image.

Request

multipart/form-data, field 'avatar': PNG/JPEG/WebP, max 2 MB

Response

{ avatarKey, updatedAt }

Stored at a fixed per-user key (avatars/<userId>) — re-uploading overwrites the previous image.

POST/api/community/usernamecommunity:write

Set or change your username (required once, during onboarding).

Request

{ username: string }

Response

{ username }

3-24 chars, letters/numbers/underscore/hyphen; must be unique — a taken username is a 400, not a 409 (see /docs/errors).

Admin

Admin-only user management (blocking is also available to moderators).

GET/api/community/admin/userscommunity:read · admin

List all users.

Response

{ users: [{ id, email, username, role, blockedAt, createdAt }] }

Admin-only — a moderator token gets 403 here.

PATCH/api/community/admin/users/{id}/blockcommunity:write · admin/moderator

Block or unblock a user.

Request

{ blocked: boolean }

Response

{ blockedAt: string | null }

blockedAt is an ISO timestamp when blocking, null when unblocking.

PATCH/api/community/admin/users/{id}/rolecommunity:write · admin

Change a user's role.

Request

{ role: 'member'|'moderator'|'admin' }

Response

{ role }