Skip to content

Comments API

Comments are threaded discussions on issues. Adding a comment notifies the issue assignee and any @mentioned users.

Permissions

ActionRequired PermissionScope
ListComments: ReadComments on issues within accessible projects
CreateComments: CreateOn issues within accessible projects
UpdateComments: Update (Own/All)Own: only your comments. All: any comment
DeleteComments: Delete (Own/All)Own: only your comments. All: any comment
Pin / UnpinIssues: UpdatePinning curates the issue, so it follows issue-management permission — not comment authorship
Like / UnlikeComments: ReadAny project member who can see the comment can like it — including the comment's own author

Admins and project owners bypass all permission checks for project-scoped resources.

Endpoints

MethodEndpointDescription
GET/api/projects/{projectId}/issues/{issueId}/commentsList comments
POST/api/projects/{projectId}/issues/{issueId}/commentsCreate a comment
PUT/api/projects/{projectId}/issues/{issueId}/comments/{commentId}Update a comment
DELETE/api/projects/{projectId}/issues/{issueId}/comments/{commentId}Delete a comment
POST/api/projects/{projectId}/issues/{issueId}/comments/{commentId}/pinPin a comment
DELETE/api/projects/{projectId}/issues/{issueId}/comments/{commentId}/pinUnpin a comment
POST/api/projects/{projectId}/issues/{issueId}/comments/{commentId}/likeToggle the current user's like on a comment

Threaded Replies

Comments support one level of threading. A reply is a comment with a parent_id pointing to a root comment on the same issue.

  • Max depth: 1. Replies cannot themselves be replied to — parent_id must reference a root comment (one with parent_id: null). Passing a parent_id that points to an existing reply returns 422.
  • Cascade delete. Deleting a root comment also deletes all its replies.
  • Reply notifications. Creating a reply notifies the parent comment's author (unless they are the replier, or already notified via @mention).

Create Comment

POST /api/projects/{projectId}/issues/{issueId}/comments

Request Fields

FieldTypeRequiredDescription
contentstringYesComment text, max 10,000 characters. Supports Markdown and @mentions.
parent_idintegerNoID of the root comment to reply to. Must be a root comment (not a reply) on the same issue. Omit for top-level comments.
bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/issues/42/comments \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "The pagination is working, but we should add a `per_page` parameter. @jasper what do you think?"
  }'
bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/issues/42/comments \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Agreed — I can add that in the follow-up.",
    "parent_id": 10
  }'
json
{
  "id": 11,
  "content": "Agreed — I can add that in the follow-up.",
  "parent_id": 10,
  "is_pinned": false,
  "user_id": 2,
  "issue_id": 42,
  "created_at": "2026-03-13T14:05:00.000000Z",
  "liker_ids": []
}

Update Comment

PUT /api/projects/{projectId}/issues/{issueId}/comments/{commentId}

Request Fields

FieldTypeRequiredDescription
contentstringYesUpdated comment text, max 10,000 characters
bash
curl -X PUT https://{tenant}.kendo.dev/api/projects/1/issues/42/comments/10 \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "The pagination is working. Added a `per_page` parameter in the follow-up PR."
  }'
json
{
  "id": 10,
  "content": "The pagination is working. Added a `per_page` parameter in the follow-up PR.",
  "parent_id": null,
  "is_pinned": false,
  "user_id": 1,
  "issue_id": 42,
  "created_at": "2026-03-13T14:00:00.000000Z",
  "liker_ids": []
}

Delete Comment

DELETE /api/projects/{projectId}/issues/{issueId}/comments/{commentId}

Returns 204 No Content on success.

bash
curl -X DELETE https://{tenant}.kendo.dev/api/projects/1/issues/42/comments/10 \
  -H "Authorization: Bearer your-token"

Pin / Unpin Comment

Each issue can have at most one pinned comment, surfaced at the top of the comment section regardless of pagination. Pinning a different comment unpins the previous one. Pinning requires Issues: Update permission — it curates the issue, so comment authorship alone does not grant it.

POST /api/projects/{projectId}/issues/{issueId}/comments/{commentId}/pin — pin DELETE /api/projects/{projectId}/issues/{issueId}/comments/{commentId}/pin — unpin

Both return 200 OK with the updated comment (is_pinned reflects the new state).

bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/issues/42/comments/10/pin \
  -H "Authorization: Bearer your-token"
bash
curl -X DELETE https://{tenant}.kendo.dev/api/projects/1/issues/42/comments/10/pin \
  -H "Authorization: Bearer your-token"
json
{
  "id": 10,
  "content": "Decision: we ship the per_page parameter in this PR.",
  "parent_id": null,
  "is_pinned": true,
  "user_id": 1,
  "issue_id": 42,
  "created_at": "2026-03-13T14:00:00.000000Z",
  "liker_ids": []
}

Like / Unlike Comment

Liking is a single toggle — one request likes an unliked comment, the next unlikes it. Unlike pinning, any number of members can like the same comment, and a comment's own author can like it too. Both root comments and replies can be liked.

POST /api/projects/{projectId}/issues/{issueId}/comments/{commentId}/like — toggle

Returns 200 OK with the updated comment. liker_ids lists the IDs of every user who currently likes it, including the caller after a like.

bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/issues/42/comments/10/like \
  -H "Authorization: Bearer your-token"
json
{
  "id": 10,
  "content": "Decision: we ship the per_page parameter in this PR.",
  "parent_id": null,
  "is_pinned": true,
  "user_id": 1,
  "issue_id": 42,
  "created_at": "2026-03-13T14:00:00.000000Z",
  "liker_ids": [2]
}

See Also