Comment
Universal threaded comments that attach to any resource by a typed reference: nested reply trees, a fixed reaction set, moderation states with an audit trail, anonymous and spoiler flags, soft deletion that preserves thread structure, @mention extraction that lands in Inbox, and hidden agent-audit notes. Every example on this page was executed against a live server.
When to reach for it: feedback on timeline events, items, worlds, or support cases; nested discussions with reactions; moderated community threads; hidden technical notes agents attach to resources.
When not to: chat-style realtime messaging belongs to Pub/Sub; persistent per-user notifications belong to Inbox (Comment already pushes mention and reply notifications there for you).
Concepts
- Targets and threads - a comment attaches to a
{type, id}target reference, likeitem/docs4-item-1. All comments on one target share a thread id of the formthr_{type}_{id}. The target is not validated against the owning service; any type/id pair opens a thread. - Replies form a tree - a reply carries
parent_comment_id, adepth(0 for top-level), and a materializedpathof ancestor ids ending in itself. Nesting is capped at depth 10 by default. - Moderation states -
pending,visible,flagged,hidden,removed,deleted. New comments start inpending(automatic toxicity scoring is not yet wired) and still appear in threads;hiddenandremovedare tombstoned for regular viewers. See Moderation and visibility. - Reactions are a fixed set -
like,insightful,laugh,question,celebrate; anything else is400 invalid_reaction. Counts are per-comment, and each viewer sees their own reactions inviewer_reactions. - Anonymous and spoiler flags - an anonymous comment hides
authorfrom other users (served asnull) while moderators and the author still see it.spoileris carried on the body for clients to blur. - Mentions notify -
@identifiertokens are extracted (deduplicated, alphanumeric plus_and-), returned inmentions, and delivered as Inbox notifications to the mentioned users; replies notify the parent's author the same way.
Post, reply, read
snug comment post --target docs4-item-1 --type item \
--body "First pass looks good, @docs4-other should confirm."
snug comment reply --parent hvTsEKZZQmqhxTfuYxDP --target docs4-item-1 \
--type item --body "Confirmed, the fix holds."
snug --output json comment thread --target docs4-item-1 --type item
snug --output json comment get --comment-id hvTsEKZZQmqhxTfuYxDPpost also takes --spoiler, --anonymous, and --idempotency-key;
thread pages with --page/--page-size and takes --max-depth,
--sort-order asc|desc (by created_at), and --pinned-first. Over HTTP,
creation is one endpoint - a reply is just a create with
parent_comment_id:
curl -X POST -H "Authorization: Bearer $SNUG_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"target": {"type": "item", "id": "docs4-item-1"},
"body": {"text": "Shipping this today."}}' \
http://localhost:4000/api/v1/comments{
"status": 201,
"msg": "Created",
"data": {
"comment_id": "yxjegyesTDVYJqSPvhYz",
"thread_id": "thr_item_docs4-item-1",
"moderation_state": "pending",
"mentions": [],
"created_at": "2026-08-28T05:02:38.060063Z"
}
}A comment in a thread or get response carries the full record: body,
author, depth and path, moderation_state, pinned,
reaction_counts, and viewer_reactions. Bodies are markdown by
default (plain is the alternative), up to 10,000 characters - a blank or
oversized body is rejected with 400.
Passing an idempotency_key on create makes retries safe: repeating the
same key returns the original comment_id instead of posting a duplicate
(verified live; the dedup window is 24 hours).
Reactions
snug comment react --comment-id hvTsEKZZQmqhxTfuYxDP --reaction like
snug comment unreact --comment-id hvTsEKZZQmqhxTfuYxDP --reaction like
snug comment toggle --comment-id hvTsEKZZQmqhxTfuYxDP --reaction insightfultoggle adds the reaction if absent and removes it if present, returning
reacted plus the updated reaction_counts - convenient for a reaction
button. Reaction names are case-insensitive (INSIGHTFUL counts as
insightful), and one actor can hold several distinct reactions on the
same comment, up to a configured cap.
Edit and delete
snug comment edit --comment-id hvTsEKZZQmqhxTfuYxDP \
--body "First pass looks good, @docs4-other and @docs4-third please confirm."
snug comment delete --comment-id hvTsEKZZQmqhxTfuYxDPOnly the author can edit - anyone else, moderators included, gets
403 insufficient_permissions. An edit re-extracts mentions and notifies
only newly added ones. Deletion is available to the author or a moderator
and is a soft delete: the node stays in the tree with
moderation_state: "deleted", deleted_at set, the body redacted to
[deleted], and mentions cleared, so replies below it keep their context.
Deleted is terminal - all three reproduced live:
- editing a deleted comment fails with
409 comment_deleted, - replying under a deleted parent fails with
409 comment_deleted, - moderating it fails with
400 invalid_moderation_transition.
Moderation, pinning, agent audits
Moderator-gated operations - state transitions, the flagged queue, pinning, hidden-comment visibility, and agent-audit notes - have their own page: Moderation and visibility.
Limits and configuration
Body length (10,000 chars), reply depth (10), distinct reactions per actor
(5), thread page size cap (200), and the auto-flag toxicity threshold are
tunable via the COMMENT_* variables in the
Comment CONFIG reference.
Reference
- Comment API - every endpoint, callable
- Related: Inbox receives mention and reply notifications, Pub/Sub for chat-style realtime messaging