Comment
Reviewer feedback attached to a Droplet — page-level or pinned to a specific element. Powers the in-app review loop and the AI-edit loop (`drop comments create/list/resolve/delete`).
A Comment is a piece of reviewer feedback attached to a Droplet. Comments can be page-level or pinned to a specific element via a CSS selector; they can have replies (threads); and they have a resolved flag for closing the loop.
Comments are first-class on the API as of feat/043: create / list / get / resolve / unresolve / delete, plus thumbs-up / unthumbs-up for reactions. create inherits read permission — anyone who can see the Droplet can comment on it, matching the web UI. The AI-loop primary close verb is resolve, not delete — resolve preserves the conversation history; delete is for actual removal (spam, mistakes).
Reactions today are a single type (thumbs-up). The reactionType column on CommentReaction is a Postgres enum shaped for future expansion (👏 ❤️ 😄 etc.) without a destructive migration.
The Comment object
Every Comment carries these attributes.
Attributes
/api/comments/{id} paths and in MCP tool inputs.get but not by list (list is already scoped to a slug).comment.replies: Comment[]), so most callers never need to walk parentId themselves.@mentions rendered as @Jane Doe in markdown export; raw @[name](email) form is the storage convention. (Tracks the UI convention from #41 mentions.)null for page-level comments. The selector is captured at pin time; if the element later moves or is deleted, the stored selector may not resolve against the current HTML — that's the caller's problem to handle.More attributes+ Show 2− Hide
{
"id": "cmt_a1b2c3",
"appSlug": "my-droplet",
"parentId": null,
"authorId": "usr_dan",
"authorName": "Dan Perrera",
"authorEmail": "dan.perrera@himarley.com",
"body": "Can we add a third initiative around onboarding? See last week's retro.",
"targetSelector": null,
"targetText": null,
"targetVersion": null,
"resolved": false,
"thumbsUpCount": 2,
"viewerThumbedUp": true,
"createdAt": "2026-06-08T22:31:54.000Z",
"updatedAt": "2026-06-08T22:31:54.000Z"
}Methods
Endpoints to operate on a Comment. The example switches between curl, drop CLI, and MCP via the dropdown — your choice persists across the page.
Create a Comment
Post a comment on a Droplet. Allowed for anyone who can read it — comment creation inherits READ permission, matching the web UI. Pass parentId to reply into an existing thread, or targetSelector + targetText to pin the comment to an element.
Pinning durably takes both fields. The viewer resolves a pin by trying targetSelector first, then falling back to finding the element whose text matches targetText. A pin with a selector but no targetText has no fallback: once the selector stops resolving — which any re-deploy that shifts the DOM will do — the pin is dead. Get both from the live HTML (GET /api/apps/{slug}/versions/{v}/download, or drop download <slug>); targetText must be the element's textContent, trimmed, first 100 characters.
The Droplet version is stamped server-side. @mentions in body notify the mentioned users; any who lack access come back in inaccessibleMentions, and any auto-granted viewer access come back in grantedAccess. Returns 403 when comments are disabled on the Droplet.
Parameters
targetText or the pin breaks on re-deploy.drop comments create customer-forum-2026-product-deck --body "This slide needs a source citation for the 40% figure." --target-selector #d2-07-inquiry-assist --target-text "Inquiry Assist"{
"comment": {
"id": "clx9k2p0000008l3f5g7h9k2m",
"parentId": null,
"authorId": "usr_jackie",
"authorName": "Jackie Booth",
"authorEmail": "jackie.booth@himarley.com",
"body": "This slide needs a source citation for the 40% figure.",
"targetSelector": "#d2-07-inquiry-assist",
"targetText": "Inquiry Assist",
"targetVersion": 12,
"resolved": false,
"thumbsUpCount": 0,
"viewerThumbedUp": false,
"createdAt": "2026-08-24T14:02:11.000Z",
"updatedAt": "2026-08-24T14:02:11.000Z"
}
}List Comments on a Droplet
Return all comments on a Droplet the caller can read, in chronological order (oldest first). Each comment includes thumbsUpCount and viewerThumbedUp (computed on read). Threads are returned nested under replies. Filter by resolved=true|false to scope to open or resolved comments only; omit to include both.
Parameters
drop comments list my-droplet[
{
"id": "clx9k2p0000008l3f5g7h9k2m",
"parentId": null,
"authorId": "usr_dan",
"authorName": "Dan Perrera",
"authorEmail": "dan.perrera@himarley.com",
"body": "Can we add a third initiative around onboarding?",
"targetSelector": null,
"resolved": false,
"thumbsUpCount": 2,
"viewerThumbedUp": true,
"createdAt": "2026-06-08T22:31:54.000Z",
"updatedAt": "2026-06-08T22:31:54.000Z",
"replies": []
}
]Get a Comment
Fetch a single comment by id. Returns 404 if the caller cannot read the parent Droplet (no enumeration leak of comment ids).
Parameters
drop comments get clx9k2p0000008l3f5g7h9k2m{
"id": "clx9k2p0000008l3f5g7h9k2m",
"parentId": null,
"authorId": "usr_dan",
"authorName": "Dan Perrera",
"authorEmail": "dan.perrera@himarley.com",
"body": "Can we add a third initiative around onboarding?",
"targetSelector": null,
"resolved": false,
"thumbsUpCount": 2,
"viewerThumbedUp": true,
"createdAt": "2026-06-08T22:31:54.000Z",
"updatedAt": "2026-06-08T22:31:54.000Z"
}Resolve a Comment
Mark a comment as resolved. This is the AI-loop primary close verb — preserves the conversation history (unlike delete). Allowed for anyone who can read the parent Droplet (non-destructive). Idempotent: resolving an already-resolved comment returns 204 with no body.
Parameters
drop comments resolve clx9k2p0000008l3f5g7h9k2m{
"id": "clx9k2p0000008l3f5g7h9k2m",
"parentId": null,
"authorId": "usr_dan",
"authorName": "Dan Perrera",
"authorEmail": "dan.perrera@himarley.com",
"body": "Can we add a third initiative around onboarding?",
"targetSelector": null,
"resolved": true,
"thumbsUpCount": 2,
"viewerThumbedUp": false,
"createdAt": "2026-06-08T22:31:54.000Z",
"updatedAt": "2026-06-09T15:02:11.000Z"
}Unresolve a Comment
Reopen a previously-resolved comment. Allowed for anyone who can read the parent Droplet. Idempotent: unresolving an already-open comment returns 204.
Parameters
drop comments unresolve clx9k2p0000008l3f5g7h9k2m{
"id": "clx9k2p0000008l3f5g7h9k2m",
"parentId": null,
"authorId": "usr_dan",
"authorName": "Dan Perrera",
"authorEmail": "dan.perrera@himarley.com",
"body": "Can we add a third initiative around onboarding?",
"targetSelector": null,
"resolved": false,
"thumbsUpCount": 2,
"viewerThumbedUp": false,
"createdAt": "2026-06-08T22:31:54.000Z",
"updatedAt": "2026-06-09T15:30:00.000Z"
}Delete a Comment
Permanently delete a comment. Cascades to its replies AND their reactions. Allowed when the caller is the comment author OR has edit permission on the parent Droplet. For actual removal only (spam, mistakes) — NOT the AI-loop close verb. Use resolve for "this has been addressed".
Parameters
drop comments delete clx9k2p0000008l3f5g7h9k2m{
"deleted": true,
"id": "clx9k2p0000008l3f5g7h9k2m",
"repliesDeleted": 2,
"reactionsDeleted": 3
}Add a Thumbs-Up Reaction
Add the caller's thumbs-up to a comment. Allowed for anyone who can read the parent Droplet (inherits READ permission, not comment-create). Idempotent: calling when already reacted returns 204. External share-link viewers receive 403.
Parameters
drop comments thumbs-up clx9k2p0000008l3f5g7h9k2m{
"id": "clx9k2p0000008l3f5g7h9k2m",
"thumbsUpCount": 3,
"viewerThumbedUp": true
}Remove a Thumbs-Up Reaction
Remove the caller's thumbs-up from a comment. Idempotent: calling when no reaction exists returns 204.
Parameters
drop comments unthumbs-up clx9k2p0000008l3f5g7h9k2m{
"id": "clx9k2p0000008l3f5g7h9k2m",
"thumbsUpCount": 2,
"viewerThumbedUp": false
}