Comments API Threading, mentions, reactions, and reference anchoring for workspace and share entities.
Comments use JSON request bodies (Content-Type: application/json), unlike most other Fast.io endpoints which use application/x-www-form-urlencoded.
Endpoint Summary
| Method | Endpoint | Description |
|---|---|---|
| GET | /current/comments/{entity_type}/{parent_id}/ | List comments for an entity |
| GET | /current/comments/{entity_type}/{parent_id}/{node_id}/ | List comments for a specific node |
| POST | /current/comments/{entity_type}/{parent_id}/ | Create or update a comment on an entity |
| POST | /current/comments/{entity_type}/{parent_id}/{node_id}/ | Create or update a comment on a node |
| GET | /current/comments/{comment_id}/details/ | Get a single comment's details |
| DELETE | /current/comments/{comment_id}/delete/ | Soft-delete a comment and its replies |
| POST | /current/comments/bulk/delete/ | Bulk soft-delete multiple comments |
| POST | /current/comments/{comment_id}/reactions/ | Add or change an emoji reaction |
| DELETE | /current/comments/{comment_id}/reactions/ | Remove an emoji reaction |
Path Parameters
Comments are scoped to an entity (workspace or share) and optionally to a specific node (file or folder).
| Parameter | Type | Format | Description |
|---|---|---|---|
| {entity_type} | string | "workspace" or "share" |
Entity type discriminator |
| {parent_id} | string | 20-digit numeric | Workspace or share profile ID |
| {node_id} | string | Alphanumeric opaque ID | File or folder ID within the entity (optional) |
| {comment_id} | string | Alphanumeric opaque ID | Comment identifier |
Comment Object
Every comment returned by the API has this structure:
| Field | Type | Description |
|---|---|---|
| id | string | Alphanumeric opaque ID of the comment |
| entity_id | string | Opaque ID of the entity (workspace, share, or node) |
| profile_id | string | 20-digit numeric ID of the comment author |
| parent_id | string|null | Opaque ID of the parent comment (for replies), or null for top-level |
| body | string | Comment text content (may include mention markup) |
| properties | object | Metadata properties (includes mentions, content_filtered if applicable) |
| reference | object|null | Anchoring reference to a position in a file (see Reference Anchoring) |
| reactions | object | Map of emoji character to total reaction count (e.g., {"👍": 3}) |
| user_reaction | string|null | The current authenticated user's emoji reaction, or null |
| created | string | ISO 8601 creation timestamp |
| updated | string | ISO 8601 last-updated timestamp |
| deleted | string|null | ISO 8601 deletion timestamp, or null if active |
List Comments
/current/comments/{entity_type}/{parent_id}/
List comments for an entity.
/current/comments/{entity_type}/{parent_id}/{node_id}/
List comments for a specific node within an entity.
Auth: Required (JWT). Rate limited.
Query Parameters
| Parameter | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
| sort | string | No | asc |
"asc" or "desc" |
Sort order by creation time |
| limit | integer | No | — | Min: 2, Max: 200 | Number of comments to return |
| offset | integer | No | 0 |
Min: 0 | Number of comments to skip |
| page | integer | No | — | Min: 1 | Page number (alternative to offset-based pagination) |
| include_deleted | boolean | No | false |
— | Include soft-deleted comments in results |
| reference_type | string | No | — | — | Filter by reference anchor type (e.g., "page", "timestamp") |
| include_total | boolean | No | false |
— | Include total count and pagination metadata in response |
Request Example
curl -X GET "https://api.fast.io/current/comments/workspace/1234567890123456789/?limit=50&include_total=true" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"comments": [
{
"id": "abc123opaqueid",
"entity_id": "xyz789opaqueid",
"profile_id": "1234567890123456789",
"parent_id": null,
"body": "This looks great!",
"properties": {},
"reference": null,
"reactions": {"👍": 2, "❤️": 1},
"user_reaction": "👍",
"created": "2025-01-15T10:30:00+00:00",
"updated": "2025-01-15T10:30:00+00:00",
"deleted": null
}
],
"count": 1,
"allowed": true,
"remaining": 95,
"total": 5,
"limit": 50,
"offset": 0
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.comments | array | Array of comment objects |
| response.count | int | Number of comments in this response |
| response.allowed | bool | Whether the current user can post new comments (based on plan limits) |
| response.remaining | int | Remaining comments allowed under plan limit (only present if plan has a limit) |
| response.total | int | Total number of comments (only if include_total=true) |
| response.limit | int | Limit used in query (only if include_total=true) |
| response.offset | int | Offset used in query (only if include_total=true) |
Access Levels
| Role | Access |
|---|---|
| Workspace Owner/Member | Full access — sees all comments |
| Share Owner | Full access — sees all comments |
| Share Guest | Filtered — sees own comments; visibility of owner and other guest comments depends on share permissions |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
APP_ERROR_INPUT_INVALID | 400 | Invalid API format... | Missing entity type or parent ID |
APP_ERROR_INPUT_INVALID | 400 | Entity type must be "workspace" or "share" | Invalid entity type |
APP_DENIED | 403 | Invalid entity or insufficient permissions | User lacks access |
APP_ERROR_NOT_FOUND | 404 | Invalid entity or insufficient permissions | Entity not found |
APP_ERROR_GENERAL | 500 | Failed to retrieve comments | Internal error |
Create or Update Comment
/current/comments/{entity_type}/{parent_id}/
Create a new comment or update an existing one on an entity.
/current/comments/{entity_type}/{parent_id}/{node_id}/
Create a new comment or update an existing one on a specific node.
Auth: Required (JWT). Rate limited.
Content-Type: application/json
Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| body | string | Required | 1–8192 chars (raw); display text excluding mentions max 500 chars | Comment text content |
| comment_id | string | No | Valid opaque ID | Include to update an existing comment; omit to create new |
| parent_id | string | No | Valid opaque ID | Parent comment ID for threaded reply (single-level only) |
| properties | object | No | — | Arbitrary key-value metadata to attach |
| reference | object | No | See Reference Anchoring | Anchoring reference to a position in a file |
Request Example — Create a new comment
curl -X POST "https://api.fast.io/current/comments/workspace/1234567890123456789/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"body": "Great work on this document!"
}'
Request Example — Reply to a comment
curl -X POST "https://api.fast.io/current/comments/workspace/1234567890123456789/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"body": "Thanks for the feedback!",
"parent_id": "abc123opaqueid"
}'
Request Example — Comment with mention and page reference
curl -X POST "https://api.fast.io/current/comments/workspace/1234567890123456789/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"body": "Hey @[user:9876543210987654321:Jane Smith], can you review page 3?",
"reference": {"type": "page", "page": 3}
}'
Request Example — Update an existing comment
curl -X POST "https://api.fast.io/current/comments/workspace/1234567890123456789/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"comment_id": "def456opaqueid",
"body": "Updated: This looks great after the revision."
}'
Response
{
"result": "yes",
"response": {
"comment": {
"id": "abc123opaqueid",
"entity_id": "xyz789opaqueid",
"profile_id": "1234567890123456789",
"parent_id": null,
"body": "Great work on this document!",
"properties": {},
"reference": null,
"reactions": {},
"user_reaction": null,
"created": "2025-01-15T10:30:00+00:00",
"updated": "2025-01-15T10:30:00+00:00",
"deleted": null
}
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.comment | object | The created or updated comment object (full schema above) |
Access Levels
| Role | Access |
|---|---|
| Workspace Owner/Member | Can create and edit own comments |
| Share Owner | Can create and edit own comments |
| Share Guest | Can only post if comments are enabled on the share |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
APP_DENIED | 403 | Authentication required to post comments | User not authenticated |
APP_ERROR_INPUT_INVALID | 400 | Invalid JSON in request body | Malformed JSON |
APP_ERROR_INPUT_INVALID | 400 | Comment body must be between 1 and 8192 characters | Body length out of range |
APP_ERROR_INPUT_INVALID | 400 | Comment text (excluding mentions) must not exceed 500 characters | Display text too long |
APP_ERROR_INPUT_INVALID | 400 | Your comment appears to contain spam content... | Spam detected |
APP_ERROR_INPUT_INVALID | 400 | Invalid parent comment ID format | Malformed parent_id |
APP_ERROR_NOT_FOUND | 404 | Parent comment not found | Referenced parent does not exist |
APP_ERROR_INPUT_INVALID | 400 | Parent comment belongs to different entity | Parent is on a different entity |
APP_ERROR_INPUT_INVALID | 400 | Invalid reference data: ... | Reference failed validation |
APP_ERROR_INPUT_INVALID | 400 | Comment limit exceeded for your plan | Plan comment limit reached |
APP_ERROR_NOT_FOUND | 404 | Comment not found | Comment ID for update not found |
APP_DENIED | 403 | You do not have permission to edit this comment | User is not the comment author |
APP_ERROR_GENERAL | 500 | Failed to save comment | Internal error |
Notes
- Content filtering: Bodies are sanitized (HTML stripped) and filtered for profanity, spam, and PII. Spam is blocked. Other filtered content is modified and
content_filteredis set inproperties. - Mentions: Mentioned users are parsed and validated against entity membership. Valid mention profile IDs are stored in
properties.mentions. - Plan limits: Number of comments per entity is limited by the organization's plan.
- Updating: Only the original author can edit. Editing sets
edited_atin properties.
Get Comment Details
/current/comments/{comment_id}/details/
Get a single comment's full details.
Auth: Required (JWT). Rate limited.
Request Example
curl -X GET "https://api.fast.io/current/comments/abc123opaqueid/details/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"comment": {
"id": "abc123opaqueid",
"entity_id": "xyz789opaqueid",
"profile_id": "1234567890123456789",
"parent_id": null,
"body": "This looks great!",
"properties": {},
"reference": null,
"reactions": {},
"user_reaction": null,
"created": "2025-01-15T10:30:00+00:00",
"updated": "2025-01-15T10:30:00+00:00",
"deleted": null
}
},
"current_api_version": "1.0"
}
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
APP_ERROR_INPUT_INVALID | 400 | Comment ID is required | Missing comment ID |
APP_ERROR_INPUT_INVALID | 400 | Invalid comment ID format | Malformed opaque ID |
APP_ERROR_NOT_FOUND | 404 | Comment not found | Comment does not exist |
APP_DENIED | 403 | You do not have permission to view this comment | User lacks access to the entity |
Delete Comment
/current/comments/{comment_id}/delete/
Soft-delete a comment. Recursive — also deletes all replies to this comment.
Auth: Required (JWT). Rate limited.
Request Example
curl -X DELETE "https://api.fast.io/current/comments/abc123opaqueid/delete/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"message": "Comment deleted successfully",
"comment": {
"id": "abc123opaqueid",
"entity_id": "xyz789opaqueid",
"profile_id": "1234567890123456789",
"parent_id": null,
"body": "This looks great!",
"properties": {},
"reference": null,
"reactions": {},
"user_reaction": null,
"created": "2025-01-15T10:30:00+00:00",
"updated": "2025-01-15T10:30:00+00:00",
"deleted": "2025-01-16T08:00:00+00:00"
}
},
"current_api_version": "1.0"
}
Access Levels
| Role | Access |
|---|---|
| Comment Author | Can delete own comments |
| Entity Owner | Can delete any comment on their entity |
| Other Users | Denied |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
APP_ERROR_INPUT_INVALID | 400 | Comment ID is required | Missing comment ID |
APP_ERROR_INPUT_INVALID | 400 | Invalid comment ID format | Malformed opaque ID |
APP_ERROR_NOT_FOUND | 404 | Comment not found | Comment does not exist |
APP_ERROR_INPUT_INVALID | 400 | Comment is already deleted | Comment was previously deleted |
APP_DENIED | 403 | You do not have permission to delete this comment | Not author or entity owner |
APP_ERROR_GENERAL | 500 | Failed to delete comment | Internal error |
Bulk Delete Comments
/current/comments/bulk/delete/
Bulk soft-delete multiple comments. Each comment is processed independently — partial success is possible. NOT recursive — does not delete replies.
Auth: Required (JWT). Rate limited.
Content-Type: application/json
Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| comment_ids | array<string> | Required | Max 100 items | Array of comment opaque IDs to delete |
Request Example
curl -X POST "https://api.fast.io/current/comments/bulk/delete/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{"comment_ids": ["abc123opaqueid", "def456opaqueid", "ghi789opaqueid"]}'
Response
{
"result": "yes",
"response": {
"success": true,
"deleted_count": 3,
"failed_count": 0,
"total_count": 3,
"results": {
"abc123opaqueid": {"id": "abc123opaqueid", "success": true, "error": null},
"def456opaqueid": {"id": "def456opaqueid", "success": true, "error": null},
"ghi789opaqueid": {"id": "ghi789opaqueid", "success": true, "error": null}
}
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.success | bool | true only if all comments were deleted (failed_count === 0) |
| response.deleted_count | int | Number of successfully deleted comments |
| response.failed_count | int | Number that failed to delete |
| response.total_count | int | Total number of IDs provided |
| response.results | object | Per-comment results keyed by comment ID |
| response.results.{id}.success | bool | Whether this comment was deleted |
| response.results.{id}.error | string|null | Error message if failed, null on success |
Per-Comment Error Messages
| Error | Cause |
|---|---|
| Invalid comment ID format | Not a valid opaque ID |
| Comment not found | No comment with this ID |
| Comment already deleted | Previously soft-deleted |
| Permission denied | User is not author or entity owner |
| Failed to delete comment | Internal error |
Request-Level Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
APP_ERROR_INPUT_INVALID | 400 | Invalid JSON request body | Malformed JSON |
APP_ERROR_INPUT_INVALID | 400 | comment_ids array is required | Missing or non-array field |
APP_ERROR_INPUT_INVALID | 400 | comment_ids array cannot be empty | Empty array |
APP_ERROR_INPUT_INVALID | 400 | Maximum 100 comments can be deleted at once | Exceeds bulk limit |
Important: Unlike single delete, bulk delete does not recursively delete replies.
Add or Change Reaction
/current/comments/{comment_id}/reactions/
Add an emoji reaction to a comment. One reaction per user per comment — sending a new reaction replaces any previous one.
Auth: Required (JWT). Rate limited.
Content-Type: application/json
Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| emoji | string | Required | Single emoji character; max 2 UTF-8 characters; must match Unicode emoji ranges | Emoji to react with |
Request Example
curl -X POST "https://api.fast.io/current/comments/abc123opaqueid/reactions/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{"emoji": "👍"}'
Response
{
"result": "yes",
"response": {
"reactions": {"👍": 3, "❤️": 1},
"user_reaction": "👍"
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.reactions | object | Map of emoji to total reaction count across all users |
| response.user_reaction | string|null | The current user's active reaction emoji |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
APP_ERROR_INPUT_INVALID | 400 | Comment ID is required | Missing comment ID |
APP_ERROR_INPUT_INVALID | 400 | Invalid comment ID format | Malformed opaque ID |
APP_ERROR_NOT_FOUND | 404 | Comment not found | Comment does not exist or is deleted |
APP_DENIED | 403 | You do not have permission to react to this comment | User lacks entity access |
APP_ERROR_INPUT_INVALID | 400 | emoji parameter is required | Missing or non-string emoji |
APP_ERROR_INPUT_INVALID | 400 | Invalid emoji character | Does not match Unicode emoji pattern |
APP_ERROR_INPUT_INVALID | 400 | Only single emoji allowed | String exceeds 2 UTF-8 characters |
APP_ERROR_GENERAL | 500 | Failed to save reaction | Internal error |
Remove Reaction
/current/comments/{comment_id}/reactions/
Remove an emoji reaction from a comment.
Auth: Required (JWT). Rate limited.
Content-Type: application/json
Request Body
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
| emoji | string | No | Must match Unicode emoji ranges if provided | Specific emoji to remove. Omit to remove any reaction by the current user |
Request Example — Remove specific emoji
curl -X DELETE "https://api.fast.io/current/comments/abc123opaqueid/reactions/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{"emoji": "👍"}'
Request Example — Remove any reaction
curl -X DELETE "https://api.fast.io/current/comments/abc123opaqueid/reactions/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{}'
Response
{
"result": "yes",
"response": {
"reactions": {"❤️": 1},
"user_reaction": null
},
"current_api_version": "1.0"
}
Response Fields
| Field | Type | Description |
|---|---|---|
| response.reactions | object | Updated map of emoji to total reaction count |
| response.user_reaction | string|null | Current user's reaction after removal (null if removed) |
Error Responses
| Error Code | HTTP Status | Message | Cause |
|---|---|---|---|
APP_ERROR_INPUT_INVALID | 400 | Comment ID is required | Missing comment ID |
APP_ERROR_INPUT_INVALID | 400 | Invalid comment ID format | Malformed opaque ID |
APP_ERROR_NOT_FOUND | 404 | Comment not found | Comment does not exist or is deleted |
APP_DENIED | 403 | You do not have permission to react to this comment | User lacks entity access |
APP_ERROR_INPUT_INVALID | 400 | emoji must be a string | Non-string emoji parameter |
APP_ERROR_INPUT_INVALID | 400 | Invalid emoji character | Does not match Unicode emoji pattern |
APP_ERROR_GENERAL | 500 | Failed to remove reaction | Internal error |
Notes: Removing a reaction that does not exist returns success (idempotent). A "removed" event is triggered only when a reaction was actually removed.
Threading
Comments support single-level threading only.
- Set
parent_idto the comment you are replying to. - Replies to a top-level comment appear as children of that comment.
- Replies to a reply are auto-flattened — they become siblings of the original reply (children of the same top-level parent). The API does not support nested threading beyond one level.
Comment A (top-level)
├── Comment B (reply to A)
├── Comment C (reply to B → auto-flattened to reply to A)
└── Comment D (reply to A)
Mentions
Use mention markup in the body field to tag other users:
@[user:USER_ID:Display Name]
| Component | Description |
|---|---|
user | Literal prefix (always user) |
USER_ID | The user's 20-digit numeric profile ID |
Display Name | Display name to show in the UI |
Character limit details
- The full mention tag markup (e.g.,
@[user:1234567890123456789:Jane Smith]) counts toward the 8192-character body limit. - Display text (excluding all mention markup) is separately limited to 500 characters.
- Mentioned users are validated against entity membership. Valid mentions are stored in
properties.mentions.
Example body with mentions
Hey @[user:1234567890123456789:Jane Smith] and @[user:9876543210987654321:Bob Jones], please review this file.
Reference Anchoring
Comments can be anchored to specific positions within a file using the reference object.
Reference object fields
| Field | Type | Description |
|---|---|---|
| type | string | Required. The anchor type: "page", "timestamp", "region", "text" |
| timestamp | number | For video/audio — position in seconds |
| page | integer | For documents — page number |
| region | object | For images — coordinates {x, y, width, height} defining a rectangular area |
| text_snippet | string | For text files — the referenced text passage |
Example — Video timestamp
{
"body": "The transition at this point needs work.",
"reference": {"type": "timestamp", "timestamp": 42.5}
}
Example — Document page
{
"body": "Typo in the second paragraph.",
"reference": {"type": "page", "page": 7}
}
Example — Image region
{
"body": "This area needs higher contrast.",
"reference": {"type": "region", "region": {"x": 120, "y": 80, "width": 200, "height": 150}}
}
Example — Text snippet
{
"body": "This sentence should be rephrased.",
"reference": {"type": "text", "text_snippet": "The quick brown fox jumps over the lazy dog."}
}
Content Filtering
Comment bodies undergo automatic filtering:
- HTML sanitization: All HTML tags are stripped.
- Spam detection: Comments identified as spam are blocked entirely (400 error).
- Profanity/PII filtering: Detected content is modified in-place and
properties.content_filteredis set totrue.