Feed & Posts
The feed, writing and editing posts with media, polls and recognitions, reactions, bookmarks, shares, follows and comments.
The feed, writing and editing posts with media, polls and recognitions, reactions, bookmarks, shares, follows and comments.
Generated from the OpenAPI spec by
npm run docs:api -w api. Do not edit this page — change the decorators inapps/api/src/and regenerate.
Conventions
- Auth — a session on every route: the cookie on web, the bearer token on mobile. See Authentication.
- Errors — every non-2xx response has the body described in Errors.
- Types — a
?after a field name means it is optional;| nullmeans it is present but may be null. - Guide — The Content Model.
Endpoints
GET /api/v1/posts — The feed
Cursor-paged, and the single most important route in the app. scope picks the tab: all, following, announcements, saved, polls or recognitions. Pass authorId instead to read one person’s posts. Every counter and viewer flag on a post (viewerReaction, hasBookmarked, author.isFollowed) is resolved for the caller, so the client renders straight from the response without a second lookup.
| Query | Type | Required | Notes |
|---|---|---|---|
authorId | string | no | One person’s posts. |
scope | all | following | announcements | saved | polls | recognitions | no | Defaults to all. An unrecognised value falls back to it. |
sort | latest | top | no | |
tag | string | no | A hashtag, without the #. |
q | string | no | Free text over the post body. |
cursor | string | no | The nextCursor from the previous page. Omit for the first. |
limit | number | no | Rows per page. Defaults to 20, capped at 50. |
Returns 200 → Page<PostResponse> — see Paging
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
POST /api/v1/posts — Write a post
Upload any media first (POST /posts/media, /posts/videos, /posts/documents) and send back what those returned. A post must carry something — text, an attachment, a poll or a recognition — but not necessarily text. Mention somebody by putting @[userId] in content; hashtags are parsed out of the body, so do not send tags. isAnnouncement is ignored unless the author is a super admin.
Body — WritePostDto
Returns 201 → PostResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
GET /api/v1/posts/tags — Trending hashtags
Lowercase, without the leading #. Feed them back as ?tag=.
Returns 200 → string[]
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
GET /api/v1/posts/counts — How many posts sit behind each tab
One call for every badge on the feed’s tab bar.
Returns 200 → FeedCountsResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
GET /api/v1/posts/saved/count — How many posts the viewer has saved
Returns 200 → CountResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
POST /api/v1/posts/media — Upload a post image
JPG, PNG or WebP, up to 10MB. One call per image; put the returned URLs in images on the post, in the order they should appear. The original is stored whole, and 1200px and 400px WebP renditions are generated alongside it — the feed is served those, so the URL returned here is the fallback rather than what a reader downloads.
Body — multipart/form-data, one part named file.
Returns 201 → UploadedImageResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
413 | The file is larger than 10MB. |
POST /api/v1/posts/videos/upload-url — Ask where to upload a video
Returns a short-lived URL to PUT one clip straight into object storage, skipping this API entirely — so a 50MB upload is one hop rather than two and nothing is buffered here. MP4, WebM or MOV, up to 50MB. 1. Call this with the file’s name, type and size. 2. PUT the file to url with no credentials and with headers set exactly as returned — they are signed into the URL, so the bucket refuses a PUT that sends anything else. 3. Call POST /posts/videos/complete with the key, and put what it returns in videos on the post. A deployment with no bucket configured returns url: null; fall back to the multipart POST /posts/videos when that happens.
Body — VideoUploadUrlDto
Returns 201 → VideoUploadUrlResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
POST /api/v1/posts/videos/complete — Record a directly-uploaded video
Call this once the PUT from POST /posts/videos/upload-url has succeeded. The object is checked to exist and its real size and content type are read back from storage. Send the result as-is in videos on the post.
Body — CompleteVideoUploadDto
Returns 201 → UploadedFileResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
POST /api/v1/posts/videos — Upload a post video (through the API)
MP4, WebM or MOV, up to 50MB. The whole object comes back — send it as-is in videos. Uploads are buffered in memory, so on a phone send the file on a foreground task and show progress; a dropped connection means starting over. Prefer POST /posts/videos/upload-url, which puts the bytes straight into object storage without passing them through this API. This route remains for clients that cannot, and for deployments with no bucket configured.
Body — multipart/form-data, one part named file.
Returns 201 → UploadedFileResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
413 | The file is larger than 50MB. |
POST /api/v1/posts/documents — Upload a post document
PDF, Word, Excel, PowerPoint, CSV or plain text, up to 20MB. The whole object comes back — send it as-is in documents.
Body — multipart/form-data, one part named file.
Returns 201 → UploadedFileResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
413 | The file is larger than 20MB. |
GET /api/v1/posts/{postId} — One post
The same shape the feed returns. Use it to open a post from a notification.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 200 → PostResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
PATCH /api/v1/posts/{postId} — Edit a post
The author only. Send the whole post, not a patch: omitting images clears them. The result comes back with isEdited: true.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Body — WritePostDto
Returns 200 → PostResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
DELETE /api/v1/posts/{postId} — Delete a post
The author, or any super admin.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 204 → no body
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
POST /api/v1/posts/{postId}/upvote — React to a post
One reaction per person per post — sending another replaces it. The body is optional and a missing reaction reads as like. Returns the whole post with the tallies recomputed, so the card can be redrawn from the response.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Body (optional)
| Field | Type | Notes |
|---|---|---|
reaction? | like | dislike | celebrate | support | love | insightful | funny |
Returns 201 → PostResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
DELETE /api/v1/posts/{postId}/upvote — Take back a reaction
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 200 → PostResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
GET /api/v1/posts/{postId}/upvotes — Who reacted, and with what
Most recent first. Visible to everyone — the count was already public.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
| Query | Type | Required | Notes |
|---|---|---|---|
limit | number | no | Rows per page. Defaults to 20, capped at 50. |
Returns 200 → LikerResponse[]
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
POST /api/v1/posts/{postId}/poll/vote — Vote in a poll
Voting for the option you already hold takes the vote back, so one route both casts and clears — the client never has to know which it is doing. Refused once the poll has closed.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Body
| Field | Type | Notes |
|---|---|---|
optionId | string |
Returns 201 → PostResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post, or it has no poll. |
DELETE /api/v1/posts/{postId}/poll/vote — Take back a poll vote
The same as voting for the option you already hold.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 200 → PostResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post, or it has no poll. |
GET /api/v1/posts/{postId}/poll/voters — Who voted for what
Refused with 403 until the poll has closed — a poll whose votes are visible while it is running is not a secret ballot.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 200 → PollVotersResponse[]
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Votes are revealed when the poll closes. |
404 | No such post, or it has no poll. |
POST /api/v1/posts/{postId}/poll/close — End voting early
The author only, and one way — a closed poll cannot be reopened.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 201 → PostResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post, or it has no poll. |
GET /api/v1/recognitions — Recent recognitions, company-wide
Returns the posts that carry a recognition, newest first — read recognition off each one.
| Query | Type | Required | Notes |
|---|---|---|---|
limit | number | no | Rows per page. Defaults to 20, capped at 50. |
Returns 200 → PostResponse[]
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
GET /api/v1/people/{userId}/recognitions — Everything one person has been recognised for
Newest first, as posts carrying a recognition.
| Path parameter | Type | Notes |
|---|---|---|
userId | string |
| Query | Type | Required | Notes |
|---|---|---|---|
limit | number | no | Rows per page. Defaults to 20, capped at 50. |
Returns 200 → PostResponse[]
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
POST /api/v1/posts/{postId}/bookmark — Save a post
Shows up under ?scope=saved. Idempotent.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 201 → PostResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
DELETE /api/v1/posts/{postId}/bookmark — Unsave a post
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 200 → PostResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
POST /api/v1/posts/{postId}/share — Record a share
Bumps shareCount. Call it when the native share sheet is opened — there is nothing to undo afterwards.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Returns 201 → PostResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
POST /api/v1/people/{userId}/follow — Follow somebody
Their posts then appear under ?scope=following. Idempotent.
| Path parameter | Type | Notes |
|---|---|---|
userId | string |
Returns 204 → no body
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
DELETE /api/v1/people/{userId}/follow — Unfollow somebody
| Path parameter | Type | Notes |
|---|---|---|
userId | string |
Returns 204 → no body
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
GET /api/v1/posts/{postId}/comments — A post’s comments
Top-level only, oldest first, cursor-paged. Each carries its own replyCount; fetch those from GET /comments/{commentId}/replies. Threads are one level deep — a reply to a reply is folded into the same thread.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
| Query | Type | Required | Notes |
|---|---|---|---|
cursor | string | no | The nextCursor from the previous page. Omit for the first. |
limit | number | no | Rows per page. Defaults to 20, capped at 50. |
Returns 200 → Page<CommentResponse> — see Paging
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
POST /api/v1/posts/{postId}/comments — Comment on a post
Pass parentId to reply to a comment. Mentions work as they do in a post: @[userId] in the body, resolved into mentions on read.
| Path parameter | Type | Notes |
|---|---|---|
postId | string |
Body — WriteCommentDto
Returns 201 → CommentResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such post. |
GET /api/v1/comments/{commentId}/replies — One comment’s replies
Oldest first, same shape as the parents.
| Path parameter | Type | Notes |
|---|---|---|
commentId | string |
| Query | Type | Required | Notes |
|---|---|---|---|
cursor | string | no | The nextCursor from the previous page. Omit for the first. |
limit | number | no | Rows per page. Defaults to 20, capped at 50. |
Returns 200 → Page<CommentResponse> — see Paging
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
POST /api/v1/comments/{commentId}/vote — Vote on a comment
Up or down — deliberately not the post reaction set: a comment gets a verdict, not a mood. Voting the way you already voted clears it.
| Path parameter | Type | Notes |
|---|---|---|
commentId | string |
Body
| Field | Type | Notes |
|---|---|---|
vote | like | dislike |
Returns 201 → CommentResponse
| Error | When |
|---|---|
400 | The request was rejected. message is an array of field errors when validation failed. |
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such comment. |
DELETE /api/v1/comments/{commentId}/vote — Take back a comment vote
| Path parameter | Type | Notes |
|---|---|---|
commentId | string |
Returns 200 → CommentResponse
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such comment. |
DELETE /api/v1/comments/{commentId} — Delete a comment
The author, or any super admin.
| Path parameter | Type | Notes |
|---|---|---|
commentId | string |
Returns 204 → no body
| Error | When |
|---|---|
401 | No valid session. Clear the stored token and sign in again. |
403 | Signed in, but not allowed to do this. |
404 | No such comment. |
Data models
PostResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
content | string | The body as stored. Mentions are @[userId] tokens, not names — swap each one for the matching entry in mentions when rendering. Hashtags are plain #text in the body and are also listed in tags. |
isEdited | boolean | |
isAnnouncement | boolean | Posted to the Announcements tab. Super admins only. |
websiteStatus | pending | approved | rejected | unpublished | Where the post stands for the public Office Updates page on superlabs.co. Only approved is shown there. Editing an approved post sets it back to pending until a super admin looks again. |
tags | string[] | |
mentions | MentionResponse[] | |
images | string[] | Image URLs, in display order, at most four — each one the optimised rendition where the image has one and the original where it does not. Display these; imageVariants carries the same list with the smaller rendition, the untouched original, and the pixel size to lay out against. |
imageVariants | PostImageResponse[] | |
videos | PostVideoResponse[] | At most one. |
documents | PostDocumentResponse[] | |
poll | PollResponse | null | |
recognition | RecognitionResponse | null | |
createdAt | string ISO 8601 | |
updatedAt | string ISO 8601 | |
author | AuthorResponse | |
upvoteCount | number | Total across every reaction — the number shown next to the button. |
reactions | ReactionTallyResponse[] | Per-reaction counts, busiest first; empty when nobody has reacted. |
viewerReaction | like | dislike | celebrate | support | love | insightful | funny | null | What the viewer picked, or null. |
commentCount | number | |
shareCount | number | |
hasUpvoted | boolean | Whether the viewer reacted at all, regardless of which one. |
hasBookmarked | boolean |
MentionResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
image | string | null |
PostImageResponse
| Field | Type | Notes |
|---|---|---|
url | string | The optimised rendition — 1200px WebP — or the original for an image that has none yet. Points at object storage, not at this API. |
thumbnailUrl | string | null | The 400px rendition, or null when the original was already smaller. |
sourceUrl | string | The original as stored, and the only stable URL here — url and thumbnailUrl carry a signature and expire. Send this back in images when updating a post; it is the value images has always held. |
width | number | null | Null on images uploaded before variants existed. |
height | number | null |
PostVideoResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
url | string | A signed URL onto the storage bucket, good for about a day and identical for every viewer within it, so it caches. It carries the right Content-Type and supports range requests. Send no credentials with it — the signature is the authorisation. |
sourceUrl | string | The stable identifier, and the only URL here that can be written back. url is signed and expires, so an editor round-tripping a post sends this in videos[].url — sending a delivery URL back would store a signature as the post's video. |
name | string | The name it was uploaded under; in storage it is a uuid. |
mimeType | string | |
sizeBytes | number | |
thumbnailUrl | string | null |
PostDocumentResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
url | string | |
name | string | |
mimeType | string | |
sizeBytes | number |
PollResponse
| Field | Type | Notes |
|---|---|---|
question | string | |
description | string | Optional context under the question. Empty when there is none. |
options | PollOptionResponse[] | |
totalVotes | number | The denominator every bar is drawn against. |
viewerOptionId | string | null | Which option the viewer picked, or null. |
closesAt | string ISO 8601 | null | |
isClosed | boolean | Derived from closesAt on read — do not compute it yourself. |
PollOptionResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
label | string | |
votes | number |
RecognitionResponse
| Field | Type | Notes |
|---|---|---|
recipient | RecognitionRecipientResponse | |
category | teamwork | innovation | above_and_beyond | leadership | customer_focus | learning | |
message | string | What was said about them. Empty when nothing was written. |
RecognitionRecipientResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
image | string | null | |
title | string |
AuthorResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
image | string | null | |
title | string | |
role | member | super_admin | |
topSkill | string | null | Their most-used skill, shown where a department would go. |
isFollowed | boolean | Whether the viewer follows them — drives the Follow button's state. |
ReactionTallyResponse
| Field | Type | Notes |
|---|---|---|
reaction | like | dislike | celebrate | support | love | insightful | funny | |
count | number |
WritePostDto
| Field | Type | Notes |
|---|---|---|
content? | string | The post body. Mention somebody with @[userId]; hashtags are parsed out of the text, so do not send tags. Optional — a post may carry only a poll, a recognition or an attachment — but a post with nothing at all is rejected. · ≤ 2000 chars |
images? | string[] | URLs from POST /posts/media, in display order. At most 4. |
documents? | PostDocumentDto[] | Documents from POST /posts/documents. At most 5. |
videos? | PostVideoDto[] | Videos from POST /posts/videos. At most 1. |
isAnnouncement? | boolean | Post to the Announcements tab. Silently ignored unless the author is a super admin, so it is safe to send either way. |
poll? | WritePollDto | |
recognition? | WriteRecognitionDto |
PostDocumentDto
| Field | Type | Notes |
|---|---|---|
url | string | |
name | string | ≤ 255 chars |
mimeType | string | |
sizeBytes | number |
PostVideoDto
| Field | Type | Notes |
|---|---|---|
url | string | |
name | string | ≤ 255 chars |
mimeType | string | |
sizeBytes | number | |
thumbnailUrl? | string | null |
WritePollDto
| Field | Type | Notes |
|---|---|---|
options | string[] | Between 2 and 6 options, 80 characters each. Ids are assigned on write. |
question | string | ≤ 200 chars |
description? | string | Optional context under the question. · ≤ 300 chars |
closesAt? | string | null | When voting stops. Absent or null leaves the poll open indefinitely. |
WriteRecognitionDto
| Field | Type | Notes |
|---|---|---|
recipientId | string | |
category | teamwork | innovation | above_and_beyond | leadership | customer_focus | learning | |
message? | string | What is said about them, quoted on the card. · ≤ 500 chars |
FeedCountsResponse
| Field | Type | Notes |
|---|---|---|
all | number | |
following | number | |
announcements | number | |
saved | number | |
polls | number | |
recognitions | number |
CountResponse
| Field | Type | Notes |
|---|---|---|
count | number |
UploadedImageResponse
| Field | Type | Notes |
|---|---|---|
url | string |
VideoUploadUrlDto
| Field | Type | Notes |
|---|---|---|
name | string | The filename, kept only so the post can show it. · ≤ 255 chars |
mimeType | video/mp4 | video/webm | video/quicktime | The clip’s own type. Signed into the upload URL, so the object cannot land as anything else — and the bucket rejects a PUT that sends a different Content-Type. |
sizeBytes | number | The clip’s size. Checked here so an oversized upload is refused before the bytes are sent rather than after. |
VideoUploadUrlResponse
| Field | Type | Notes |
|---|---|---|
url | string | null | PUT the file here, with no credentials and with headers set exactly as given. Null when the deployment has no bucket configured — fall back to POST /posts/videos then. |
key | string | Send this back to POST /posts/videos/complete. |
headers | object | Signed into url. Send them verbatim on the PUT; a mismatch is a 403 from the bucket, not a warning. |
expiresIn | number | Seconds url stays valid. |
CompleteVideoUploadDto
| Field | Type | Notes |
|---|---|---|
key | string | The key from POST /posts/videos/upload-url. |
name | string | ≤ 255 chars |
UploadedFileResponse
| Field | Type | Notes |
|---|---|---|
url | string | |
name | string | |
mimeType | string | |
sizeBytes | number |
LikerResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
name | string | |
image | string | null | |
title | string | |
likedAt | string ISO 8601 | |
reaction | like | dislike | celebrate | support | love | insightful | funny |
PollVotersResponse
| Field | Type | Notes |
|---|---|---|
optionId | string | |
voters | MentionResponse[] |
CommentResponse
| Field | Type | Notes |
|---|---|---|
id | string | |
postId | string | |
parentId | string | null | The comment this answers, or null for a top-level one. |
content | string | |
isEdited | boolean | |
createdAt | string ISO 8601 | |
author | AuthorResponse | |
mentions | MentionResponse[] | |
likeCount | number | |
dislikeCount | number | |
replyCount | number | Replies to this one. Always 0 on a reply — threads are one level deep. |
viewerVote | like | dislike | null | What the viewer chose, or null. |
WriteCommentDto
| Field | Type | Notes |
|---|---|---|
content | string | ≤ 1000 chars |
parentId? | string | The comment this answers. Absent for a top-level one; a reply to a reply is folded into its thread by the service rather than rejected here. |