Skip to content

Post a comment (or reply) on a post

POST
/posts/{id}/comments

Top-level comment when parent_id is omitted; reply when set. Depth and root_id are computed server-side from the parent’s row. Requires posts.comment capability (Base default).

THE POST’S OWN SETTING IS A SEPARATE GATE (#1119 sprint 21d). When Post.comments_enabled is false the request is refused with 409 and the stable error value comments_disabled, for a top-level comment and a reply alike, and nothing is written: no comment row, no comment_count change, no activity, no notification, no @-mention. system.admin bypasses the capability check as before and does NOT bypass this one; it is the author’s decision about their post, not a permission.

The refusal is evaluated AFTER the read gate, so an unreadable post still answers 404 and its setting is never disclosed. It is read under the post’s row lock inside the insert transaction, so a disable that has committed cannot be raced by a comment that started earlier.

id
required
string format: uuid
object
body
required
string
>= 1 characters <= 10000 characters
parent_id

When set, the new comment is a reply to this parent. Depth and root_id are computed server-side from the parent row.

string format: uuid
nullable

Comment created

A single comment in a thread. Threading is via parent_id + root_id + depth (caller renders nested replies indented by depth). annotation_* fields are NULL for plain comments; the review-mode feature reuses the same row shape.

Federation: exactly one of {author_user_ref} XOR {peer_id + actor_uri} is set per row. Local-authored comments have author_user_ref set + peer_id NULL. Remote-authored comments (from a paired peer per ADR 0043 / phase 1.22.D) have author_user_ref NULL + peer_id + actor_uri + display_name populated. Clients render “<display_name> @ <peer’s host>” for remote rows.

object
id
required
string format: uuid
target_kind
required
string
Allowed values: post asset collection
target_id
required
string format: uuid
parent_id
string format: uuid
nullable
root_id
required
string format: uuid
depth
required
integer
author_user_ref

Local author user.ref. NULL for remote-authored comments (see peer_id / actor_uri).

integer format: int64
nullable
body
required
string
body_html
required

Server-rendered safe HTML; clients should render this rather than reformatting body.

string
annotation_type
string
nullable
Allowed values: point rect timestamp frame whiteboard text-range
annotation_data
object
key
additional properties
any
like_count
required
integer format: int64
edited_at
string format: date-time
nullable
created_at
required
string format: date-time
updated_at
required
string format: date-time
peer_id

The federation peer that originated this comment. NULL for local-authored rows. Set in tandem with actor_uri

  • display_name when the comment came in via the federation inbox.
string format: uuid
nullable
actor_uri

The remote actor’s URI on their home instance (e.g. https://studio-b.example/users/bob). NULL for local-authored rows.

string
nullable
display_name

Display name resolved from federation_remote_actors cache for remote-authored comments. May be empty if the remote instance hasn’t shipped display hints yet; clients should fall back to the actor_uri’s host + local-part when this is empty.

string
nullable

Malformed request

object
error
required

Human-readable error summary

string
Example
the request could not be completed

Authentication required, missing, or invalid

object
error
required

Human-readable error summary

string
Example
the request could not be completed
Example
{
"error": "authentication required: sign in and retry with a valid session or API token"
}

Authenticated but missing required capabilities

object
error
required

Human-readable error summary

string
Example
the request could not be completed

Resource not found

object
error
required

Human-readable error summary

string
Example
the request could not be completed

The post does not accept comments. error is the stable value comments_disabled. Nothing was written; the thread, its count and every existing comment are as they were. A client holding an older representation of the post that still said comments_enabled: true should withdraw its composer on this answer.

object
error
required

Human-readable error summary

string
Example
the request could not be completed