Comments
Inline comments anchored to a doc range, table cell, row, html element, surface, or whole workspace. The universal create endpoint accepts a discriminated target union; the legacy row-only endpoint is kept for older clients.
Generated from src/lib/api-paths/comments.ts. Body schemas come from CreateUniversalCommentSchema and CreateCommentSchema in the runtime.
get/api/workspaces/{slug}/comments
List comments on a workspace
Returns comments on this workspace, scoped + filtered by query params. Use `target_type` + `target_id` to scope to a row, cell, doc range, or surface; pass `mentioning=me` to surface only threads where the caller is mentioned; `status` defaults to `open` (pass `all` for resolved + open). Access-gated against the caller's workspace permissions, with the `commentsOnPublicShare` toggle on the workspace controlling whether unauthenticated viewers on public/unlisted workspaces can read.
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
target_type | "row" | "cell" | "doc_range" | "html_element" | "surface" | "workspace" | "file" | no | |
target_id | string | no | |
surface | string | no | |
status | "open" | "resolved" | "all" | no | |
mentioning | "me" | no | |
author | string | no | |
limit | integer | no | |
offset | integernull | no |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Comment list. |
403 | Error | Caller has no read access to this workspace. |
post/api/workspaces/{slug}/comments
Create a comment (universal)
Create a comment anchored to a doc range, table cell, row, or whole workspace. The `target` discriminated union picks the anchor type.
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
target | object | object | object | object | object | object | object | yes | |
body | string | yes | |
parentId | string | no | |
mentions | object[] | no | |
correlationId | string | no |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Created. |
get/api/workspaces/{slug}/rows/{id}/comments
List comments on a row (legacy)
Legacy row-scoped reader. Kept for back-compat with API clients that followed the older docs at `/docs/api/overview`. New clients should use `GET /api/workspaces/{slug}/comments?target_type=row&target_id=…` which carries the full v1 shape (reactions, resolvedAt, surfaceSlug, mentions in canonical form).
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes | |
id | string | yes |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Comment list, oldest-first. |
post/api/workspaces/{slug}/rows/{id}/comments
Create a comment on a row (legacy)
Legacy row-only comment endpoint. New clients should use the universal endpoint above with `target: { kind: "row", rowId }`.
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes | |
id | string | yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
body | string | yes | |
parentId | string | no | |
mentions | string[] | no |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Created. |
get/api/comments/{id}
Get a single comment
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Comment detail. |
404 | Error | Not found. |
delete/api/comments/{id}
Delete a comment
Author only.
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Deleted. |
403 | Error | Not the author. |
post/api/comments/{id}/resolve
Resolve a comment thread
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Resolved. |
post/api/comments/{id}/unresolve
Re-open a resolved comment
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Re-opened. |
post/api/comments/{id}/reactions
Add an emoji reaction
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
emoji | string | yes | example: "👍" |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Reacted. |
delete/api/comments/{id}/reactions/{emoji}
Remove your reaction
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | |
emoji | string | yes |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Reaction removed. |
Target shapes
Every create call carries a polymorphic target object whose type field picks the anchor. Six kinds, each with its own payload:
target.type = "row"
POST /api/workspaces/{slug}/comments
{
"target": { "type": "row", "rowId": "cmp_row123" },
"body": "Status looks off here."
}target.type = "cell"
POST /api/workspaces/{slug}/comments
{
"target": {
"type": "cell",
"rowId": "cmp_row123",
"columnKey": "media_url"
},
"body": "Swap to the 16:9 crop?"
}target.type = "doc_range"
POST /api/workspaces/{slug}/comments
{
"target": {
"type": "doc_range",
"surfaceSlug": "notes",
"anchor": {
"from": 120,
"to": 184,
"text": "Ship a kill-switch flag before the partner-comms email."
}
},
"body": "Who owns the kill-switch?"
}The text snapshot lets the server detect anchor drift on read. If the doc body has changed by the time someone opens the thread, the response carries currentText vs expectedText so clients can re-anchor without a second round trip.
target.type = "html_element"
POST /api/workspaces/{slug}/comments
{
"target": {
"type": "html_element",
"surfaceSlug": "mockup",
"anchor": {
"selector": "section.hero > h1",
"text": "Workspaces built for both of you."
}
},
"body": "Love this headline."
}Pin a comment to a specific element inside an html-kind Surface. selector is the CSS path the resolver tries first; the optional 60-char text snippet is a fuzzy fallback for when the DOM is regenerated and the selector no longer resolves (the in-iframe capture script produces both on every click). Threads on the same surface share targetId (the surface id), so list responses scope replies by parent id.
target.type = "surface"
POST /api/workspaces/{slug}/comments
{
"target": { "type": "surface", "surfaceSlug": "schedule" },
"body": "Q4 cadence review thread starts here."
}target.type = "workspace"
POST /api/workspaces/{slug}/comments
{
"target": { "type": "workspace" },
"body": "Workspace-wide retro thread."
}target.type = "file"
Comments anchored on a single file in a files surface. The file uploader becomes the target author for the notification fan-out; any team mentions are appended to the audience.
POST /api/workspaces/{slug}/comments
{
"target": { "type": "file", "fileId": "cmp_file123" },
"body": "Replace the hero image, the contrast is too low."
}Replying to a cell comment
The common failure mode: an agent receives a cell-anchored comment notification, tries to reply via the row-scoped endpoint POST /api/workspaces/{slug}/rows/{id}/comments, and the server 400s because that endpoint hard-codes target.type = "row". Use the universal endpoint with a matching target.type + the parent comment id:
POST /api/workspaces/{slug}/comments
{
"target": {
"type": "cell",
"rowId": "cmp_row123",
"columnKey": "media_url"
},
"parentId": "cmp_parent_cell_comment_id",
"body": "Updated the crop, thanks for catching."
}Single-depth threading: replying to a reply auto-flattens to the root. Replying to a resolved thread auto-reopens it and fires comment.unresolved on the workspace stream.
Row-scoped endpoint (deprecated)
The earlier POST /api/workspaces/{slug}/rows/{id}/comments endpoint still works for older clients but always resolves target.type = "row". Responses now carry an RFC 8594 Deprecation: true header + an RFC 8288 Link: </docs/api/comments>; rel="successor-version" pointing at this page. Use the universal endpoint above for any new agent code. Cross-target parent ids on the row-scoped endpoint return a 400 whose message names the parent’s actual target type and points back here.
MCP equivalents
Every endpoint on this page has a matching MCP tool:
add_comment, universal create with the same polymorphictargetshape.list_comments, query with the same filters as the GET endpoint.get_comment_thread: fetch a parent + replies in one call.reply_to_comment: shortcut when you already have a parentcomment_idand don’t want to rebuild the target shape.resolve_comment/unresolve_comment: close or reopen a thread.react_to_comment: emoji reactions.
Frequently asked questions
- How do I add a comment to a Dock row, cell, doc range, or html element via the API?
- POST `/api/workspaces/:slug/comments` with `{ target: { type: 'row' | 'cell' | 'doc_range' | 'html_element' | 'surface' | 'workspace', ... }, body, mentions? }`. The `target` discriminated union picks the anchor; mentions trigger inbox notifications.
- How do I list comments on a Dock workspace?
- GET `/api/workspaces/:slug/comments`. Filter by `?target_type=row|cell|doc_range|html_element|surface|workspace&target_id=…` to scope to one anchor, `?surface=<slug>` for everything on one surface, `?status=open|resolved|all` (default open), `?mentioning=me` for thread your principal is mentioned in.
- How do I @mention a Dock agent or person in a comment?
- Include `@<name>` in the body AND pass `mentions: [{ kind: 'user' | 'agent', id, label }]` so the server fans out notifications without re-parsing. The body's `@<name>` is the human-readable rendering; `mentions[]` is the machine-readable mapping that drives inbox row + email (humans) or webhook (agents). The two arrays are filtered server-side: only mentions whose `@<label>` literal still appears in the body actually fire (so deleting `@Sarah` from the body before submit won't ping Sarah).
- How do I resolve a Dock comment thread?
- POST `/api/comments/:id/resolve`. Marks the thread resolved without deleting; resolved threads are excluded from the default list (pass `?status=resolved` or `?status=all` to include). POST `/api/comments/:id/unresolve` reverses it.
- How do I add a reaction (emoji) to a Dock comment?
- POST `/api/comments/:id/reactions` with `{ emoji }`. Unique by (comment, principal, emoji) so re-add is idempotent. DELETE the same path to remove. Supports any UTF-8 emoji string up to 16 chars.
- How do I reply to a Dock comment via the API?
- POST `/api/workspaces/:slug/comments` with the same target, plus `{ parentId: <root-comment-id> }`. Threading is one level deep; replies all attach to the root. Threads notify everyone who's already commented on the thread.
- Can my AI agent leave a comment in a Dock workspace?
- Yes, the same endpoints. The agent's principal id stamps `authorId + authorPrincipalType`. Useful for code-review-style agents that want to flag findings on specific rows or doc paragraphs for human review.
- How does my Dock agent know it was @mentioned in a comment or doc?
- Two webhook events cover both surfaces. Comments: subscribe to `comment.added`, read `mentions: [{kind, id, label}]`, filter on `mention.kind === 'agent' && mention.id === your-agent-id`. Doc bodies: subscribe to `doc.mention_added`, same `mentions` array shape, fires only on newly-added mentions (not on every save). The agent then reads the comment / doc context and replies via POST. Learn more →
- Can outsiders see comments on a Dock public workspace?
- Off by default. Set the workspace's `commentsOnPublicShare = true` (PATCH `/api/workspaces/:slug`) to let unauthenticated viewers on public/unlisted workspaces read comments. Default off because comments often contain context the owner doesn't want public.
- How do I get notified when someone comments on something I authored in Dock?
- Automatic. The Dock inbox routes target-author notifications: every comment on a row or doc you authored lands in your inbox without you needing to subscribe. For machine-readable, register a webhook on `comment.added` with author-filter logic in your handler. Learn more →
Related
- Web → Comments: UI flow + visibility model + reactions.
- Web → Inbox: every comment routed to a recipient lands here.
- Rows API: anchor a comment on a cell or row.
- Doc body API: anchor a comment on a doc range.
- HTML body API: anchor a comment on an element inside an html surface.
- Webhooks API ,
comment.added/comment.deletedevents. - Agent attribution: every comment write is stamped with
authorPrincipalType+authorId.