Work item discussion
- Read a work item's discussion
- List the thread starters
- Read one thread's replies
- Read a single comment
- Comment on a work item
- Reply in a thread
- Comment on a group-scoped work item
- Edit a comment
- Delete a comment
- Resolve a thread
- Reopen a resolved thread
- React to a comment
- Withdraw a reaction
- See who reacted
- Follow a work item's timeline
Overview
Every work item carries a discussion: a list of comments, each of which can be a thread with replies underneath it. You use it to ask the question that unblocks the work, to record the decision that was made, and to mark a thread finished once it has been answered so nobody re-reads it. Reactions let a team agree without adding a comment nobody needed, and @-mentioning somebody puts the item in their attention inbox.
The same discussion is readable from the web app, the CLI and the API, which is what makes it useful to a bot: a pipeline that fails can say so on the item that asked for the change, and a script can sweep the threads nobody resolved.
Before you start
- A work item you can read.Reading a discussion follows the visibility of the repository or group the item lives in: a public one answers anyone, including a caller who is not signed in; an internal one answers anyone signed in; a private one answers only its members, and answers
404to everybody else so its existence stays private. - Reporter access to take part.Commenting, replying, editing your own comment, deleting your own comment, resolving a thread and reacting all need real membership at Reporter or above. The visibility shortcut that lets a stranger read a public repository does not let them write in it, so a public work item is readable by the world and writable by its members.
- Maintainer access to touch somebody else's comment.Editing or deleting a comment you did not write is a moderation action, and it needs Maintainer — two levels above the Reporter that lets you write and manage your own.
- The item's number and where it lives.A work item is addressed by its per-container number — the
412in#412, or the17in a group-scoped&17— plus the repository or group that owns it. The number is unique within that one container, so the same number means different things in two repositories. - A token with the right scopes, if you are calling the API or the CLI rather than clicking. Five scopes cover this page, and they are chosen when the token is minted:
| Scope | What it lets a token do |
|---|---|
issue_comment:read | read comments, threads, replies, reactions and the timeline |
issue_comment:create | post a comment or a reply |
issue_comment:update | edit a comment, and resolve or reopen a thread |
issue_comment:delete | delete a comment |
issue_reaction:manage | add and withdraw reactions |
How it works
Threading is one level deep
A discussion is a list of threads. Each thread has one root comment and any number of replies under it, and a reply cannot itself have replies. That is a deliberate ceiling: a two-level tree is a conversation, and a deeper one is an argument nobody can read in order.
work item #412├── thread ← root comment, can be resolved│ ├── reply (oldest first)│ └── reply├── thread ← root comment, resolved ✓│ └── reply└── thread ← root comment, no replies yetA reply is created by naming the root comment it belongs to. Naming a reply instead is refused rather than flattened, so a thread never quietly loses its shape.
Four ways to read the same discussion
They answer different questions, and picking the wrong one is the most common surprise on this page — a plain comment listing returns thread starters only, so replies appear to be missing when they are simply somewhere else.
| What you want | Use | What comes back |
|---|---|---|
| The whole discussion, ready to render | the discussions listing | every thread, each with its root and all of its replies, oldest thread first |
| The thread starters, a page at a time | the comments listing | root comments only, oldest first, with a cursor — filterable by whether the thread is resolved |
| One thread's replies | the replies listing | the replies under one root, oldest first, with a cursor |
| Everything that happened to the item | the timeline | comments, resolutions, status changes, assignments, links to code — newest first, with a cursor |
The discussions listing returns the discussion whole and takes no cursor, so it is the right call for rendering an item and the wrong one for sweeping an item with thousands of comments. The other three are cursor-paged: each answers with a next_token when there is another page, and you hand that back to get it.
Resolution is a property of the thread
Resolving marks the root comment resolved, and with it the thread. Replies cannot be resolved on their own — half a resolved thread would mean the conversation had two answers. Resolving something already resolved is refused rather than treated as a no-op, and so is reopening something that was never resolved, so a script that loops over threads cannot report success for work it did not do.
Resolution is a flag, not a deletion: a resolved thread stays in the discussion, keeps its replies, and can be reopened by anyone with Reporter access. Both transitions land on the item's timeline.
The discussion belongs to the item, not to its number
A work item can be moved into another repository, and when it moves it is renumbered. Its comments, threads, reactions and timeline travel with it — they are attached to the item itself, not to the address you reached it by. The old number stops answering, which is the point: a comment that has moved into a private repository must not stay readable at the number it used to have.
Work items on a group
An item filed on a group rather than on a repository — an epic, or an incident that spans more than one repository — has exactly the same discussion, reached through the group instead of a repository. Every operation on this page exists twice for that reason, once under a repository and once under a group, with identical behaviour, identical scopes and identical errors: the address swaps /repositories/{repo_id} for /groups/{group_id} and nothing else changes. Pick the one that matches where the item lives.
A work item has no diff
A comment on a merge request can pin itself to a line of a file. A work item is not a change, so it has no diff to pin to, and asking for a line-anchored comment on one is refused rather than stored with an empty anchor. Every comment on a work item is a plain comment. If you want the item to point at a specific place in the code, that is a code anchor, which lives on the code links page.
How to
Read a work item's discussion
The whole discussion in one call: every thread, each with its root and its replies in the order they were written, threads ordered by when they were started. Each comment carries its author, when it was written, whether it has been edited, how many replies it has, and its reactions with a flag saying which ones you placed yourself.
Open the work item and stay on its Overview tab — Discussion is a section in the main column, below the description. Every thread hangs off one timeline broken into days, each showing its root comment with its replies indented beneath it and Reply and Resolve underneath. A resolved thread folds to a single line — its author, a Resolved badge and how many comments are in it — and clicking that line opens it again.
gitmoap issue-comments list-issue-discussions --repo-id <id> --issue-id <id>curl -X GET https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/discussions \ -H "Authorization: Bearer $GITMOAP_TOKEN"List the thread starters
Use this when you want to page through a long discussion, or to find the threads nobody has resolved. It returns root comments only — the replies live behind each root.
Add resolved=false to get the open threads and resolved=true to get the finished ones. Leaving it off returns both.
The Discussion heading carries the count: how many threads the item has, and how many of those are still unresolved. Because settled threads fold to one line, what is still open is what is still expanded — which is the fastest way to see what a long item owes an answer to.
gitmoap issue-comments list --repo-id <id> --issue-id <id>curl -X GET https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments \ -H "Authorization: Bearer $GITMOAP_TOKEN"Read one thread's replies
Ask for the replies by naming the thread's root comment. Naming a reply is refused, because a reply has no replies of its own to list.
A thread arrives whole: every reply is shown under the root it answers, indented behind its own rule, and consecutive comments from the same person within ten minutes of each other are drawn as one turn. There is nothing to click to load the rest, so this call is for a script that wants one thread a page at a time.
gitmoap issue-comments list-replies \ --repo-id <id> \ --issue-id <id> \ --comment-id <id>curl -X GET https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/replies \ -H "Authorization: Bearer $GITMOAP_TOKEN"Read a single comment
One comment, with its author, its edit flag and its reactions. Useful when you already hold a comment's id — from an earlier listing, or from the commented entry on the item's timeline — and want the current state of that one comment rather than the whole discussion.
The web app renders the discussion whole rather than one comment at a time, so this is the call a script reaches for once it is holding an id. In the item view, a comment's header carries the same things this call returns: its author, when it was written, and an edited marker once it has been changed.
gitmoap issue-comments get --repo-id <id> --issue-id <id> --comment-id <id>curl -X GET https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id} \ -H "Authorization: Bearer $GITMOAP_TOKEN"Comment on a work item
A comment needs a body and nothing else. To make it a reply instead of a new thread, name the root comment it belongs to.
The body is markdown, between 1 and 10,000 characters after trimming. Leading and trailing whitespace is removed before it is stored, so a comment made entirely of spaces is an empty comment and is refused.
Who hears about it. A comment puts the item in the attention inbox of whoever filed it and of everybody assigned to it, whether or not you name them — so you do not have to @ the person who reported a bug to reach them. You are never raised against your own comment, and closing the item clears both those items and the mentions, so a finished item stops nagging.
Mentioning people. Writing @username in the body puts the item in that person's attention inbox as well, and their name is resolved at the moment you post — so an @ that names nobody quietly resolves to nobody rather than failing the comment. At most 25 distinct people are notified per comment, in the order they first appear. Three things reach nobody: an @ in a place the platform does not read names — inside a code span, a fenced or indented code block, a markdown heading, or a URL — which is what keeps an email address in a stack trace from paging a stranger; your own name, because you are never raised against your own comment; and a name an /assign or /unassign in the same body already claimed, so /assign @sam reaches Sam neither as an assignment nor as a mention. Write the name in an ordinary sentence.
A closed work item still takes comments. The post-mortem on a finished incident is usually the discussion that matters most, so closing an item does not close its discussion.
The composer sits at the bottom of the Discussion section. It is a markdown editor with Rich, Source and Preview modes — what it stores is markdown whichever one you write in — and Cmd-Enter (Ctrl-Enter on Windows and Linux) sends it without reaching for the Comment button. Whatever you have typed is kept as an unsent draft if you reload or navigate away, and is cleared once the comment is posted. Comment starts a new thread; to reply to an existing one, use Reply underneath that thread instead.
gitmoap issue-comments create --repo-id <id> --issue-id <id> --body <body>curl -X POST https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "body": "<string>" }'Reply in a thread
A reply is the same call as a comment with one field added: the id of the thread's root comment. It joins the end of that thread rather than starting a new one, and it raises the same attention items and resolves the same mentions as any other comment.
A reply cannot carry a parent that is itself a reply, and it cannot be resolved on its own — resolution belongs to the thread. If you do not have the root's id to hand, every reply carries the id of the thread it belongs to, so any comment in the thread will tell you.
Choose Reply underneath the thread you want to answer. A composer opens indented under that thread, with Reply as its own button and Cancel beside it; posting closes it and the reply appears at the end of the thread.
gitmoap issue-comments create --repo-id <id> --issue-id <id> --body <body>curl -X POST https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "body": "<string>" }'Comment on a group-scoped work item
Identical in every respect, addressed through the group rather than a repository. Use it for items that span several repositories.
Open the group and choose Issues in its navigation, then the item. Its discussion behaves exactly as a repository item's does — the same editor, the same threads, the same resolve control.
gitmoap issue-comments create-group \ --group-id <id> \ --issue-id <id> \ --body <body>curl -X POST https://api.gitmoap.example/groups/{group_id}/issues/{issue_id}/comments \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "body": "<string>" }'Edit a comment
Editing replaces the body and marks the comment edited; the flag is permanent and visible to everyone, so a silently rewritten argument is not a thing that can happen here. The comment's position in the thread, its replies and its reactions are untouched.
You can always edit your own comment with Reporter access. Editing somebody else's needs Maintainer.
Note
The web app offers the edit and delete controls on the comments you wrote. Moderating somebody else's — which is what Maintainer buys you — is done from the CLI or the API.
Open the ⋯ menu in the comment's header and choose Edit comment. The comment turns into the same markdown editor in place; Save replaces it, and an edited marker appears beside the timestamp for everyone.
gitmoap issue-comments edit \ --repo-id <id> \ --issue-id <id> \ --comment-id <id> \ --body <body>curl -X PATCH https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id} \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "body": "<string>" }'Delete a comment
Warning
Deleting is immediate and cannot be undone. Deleting a reply removes that one comment. Deleting a thread's root removes the entire thread — the root, every reply written under it by anybody, and every reaction on all of them — with one confirmation and no second chance. Deleting the work item is the largest version of the same thing: its whole discussion goes with it, every thread, every reply and every reaction, and so does its timeline. Nothing is archived and nothing is recoverable through Gitmoap, so if a thread holds a decision you need, quote it into a new comment before you delete it.
You can always delete your own comment with Reporter access. Deleting somebody else's needs Maintainer.
Deleting does not leave a tombstone in the discussion — the comment is gone rather than greyed out. The item's timeline keeps the entry recording that the comment was posted, so the fact of the conversation survives even when its text does not.
Open the ⋯ menu in the comment's header and choose Delete comment. The confirmation says the deletion is permanent and cannot be undone; Delete is immediate. It does not warn you how many replies a thread root takes with it, so check the thread before you confirm.
gitmoap issue-comments delete \ --repo-id <id> \ --issue-id <id> \ --comment-id <id>curl -X DELETE https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id} \ -H "Authorization: Bearer $GITMOAP_TOKEN"Resolve a thread
Mark a thread answered so the next reader knows not to reopen the question. Any member at Reporter or above can resolve a thread, not only its author — the person who got the answer is often not the person who asked.
Name the thread's root comment. Resolving a reply, or resolving a thread that is already resolved, is refused.
Resolve sits underneath each thread, beside Reply. Choosing it folds the thread to a single line carrying the root author's name, a Resolved badge and the number of comments in it.
gitmoap issue-comments resolve-issue-thread \ --repo-id <id> \ --issue-id <id> \ --comment-id <id>curl -X POST https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/resolve \ -H "Authorization: Bearer $GITMOAP_TOKEN"Reopen a resolved thread
The answer was wrong, or the question came back. Reopening restores the thread to the open list and clears the record of who resolved it.
Click the folded line to expand the thread, then choose Reopen where Resolve used to be. The thread stays open and counts again towards the unresolved tally beside the Discussion heading.
gitmoap issue-comments unresolve-issue-thread \ --repo-id <id> \ --issue-id <id> \ --comment-id <id>curl -X DELETE https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/resolve \ -H "Authorization: Bearer $GITMOAP_TOKEN"React to a comment
Eight reactions are available: thumbs_up, thumbs_down, laugh, hooray, confused, heart, rocket and eyes. Anything else is refused, which keeps a discussion's reactions countable and sortable rather than a free-text field.
Each person may place each reaction on each comment once. Placing one you have already placed changes nothing and is not an error, so a retrying script does not double-count. The call answers with the comment's full reaction summary — each reaction, how many people placed it, who they are, and whether you are one of them.
Every comment header carries an Add reaction button — always visible, not revealed on hover, so it works from a touchscreen and from the keyboard — which opens a row of the eight. Once placed, a reaction becomes a pill under the comment carrying the emoji and a count; the ones you placed are highlighted.
gitmoap issue-comments add-reaction \ --repo-id <id> \ --issue-id <id> \ --comment-id <id> \ --reaction thumbs_up|thumbs_down|laugh|hooray|confused|heart|rocket|eyescurl -X POST https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactions \ -H "Authorization: Bearer $GITMOAP_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "reaction": "<one of confused | eyes | heart | hooray | laugh | rocket | thumbs_down | thumbs_up>" }'Withdraw a reaction
Removes only your reaction of that kind. Withdrawing one you never placed answers 404 rather than pretending, so a script can tell the difference between "removed it" and "there was nothing there".
Click the highlighted pill in the comment's reaction row — the one you placed. The count drops by one, and the pill disappears entirely once you were the last person holding it.
gitmoap issue-comments remove-reaction \ --repo-id <id> \ --issue-id <id> \ --comment-id <id> \ --reaction <reaction>curl -X DELETE https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactions/{reaction} \ -H "Authorization: Bearer $GITMOAP_TOKEN"See who reacted
The full summary for one comment: every reaction placed on it, the count for each, who placed it, and whether you are one of them. The listings that return comments already attach the same summary to each one, so reach for the dedicated call when you have a comment id and want nothing else.
Hover any reaction pill under a comment to see who placed it: the first eight names, and a count of however many more there are.
gitmoap issue-comments list-reactions \ --repo-id <id> \ --issue-id <id> \ --comment-id <id>curl -X GET https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactions \ -H "Authorization: Bearer $GITMOAP_TOKEN"Follow a work item's timeline
The timeline is everything that has happened to the item in one stream, newest first — comments and thread transitions alongside status changes, assignments, moves, links to commits and merge requests, and criteria passing or regressing. Each entry names its actor and when it happened.
The timeline is a record rather than a view: an entry keeps the scope and number the item had at the time, so moving an item does not rewrite its history into a place it never was. It is read with issue_comment:read, the same scope as the discussion.
Activity is its own section on the same Overview tab, directly below Discussion in the item's main column. Where Discussion shows only what people wrote, Activity lists everything the item has been through, each entry naming who did it and when, with Load more activity at the end of the page you have.
gitmoap issue-comments list-issue-activities --repo-id <id> --issue-id <id>curl -X GET https://api.gitmoap.example/repositories/{repo_id}/issues/{issue_id}/activities \ -H "Authorization: Bearer $GITMOAP_TOKEN"Reference
Every operation exists twice — once addressed through a repository, once through a group — with the same behaviour on both.
Reading a discussion
8 endpoints
GET/repositories/{repo_id}/issues/{issue_id}/discussionsissue_comment:read
List a work item's discussion threads
list_issue_discussionsGET /repositories/{repo_id}/issues/{issue_id}/discussionsOpen in the API referenceToken scope
issue_comment:read
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found
Receive
[]object1 field
- rootobject
GET/groups/{group_id}/issues/{issue_id}/discussionsissue_comment:read
List a group-scoped work item's discussion threads
list_group_issue_discussionsGET /groups/{group_id}/issues/{issue_id}/discussionsOpen in the API referenceToken scope
issue_comment:read
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found
Receive
The same shape list_issue_discussions returns — 1 field. Go to it
Show it here
[]object1 field
- rootobject
GET/repositories/{repo_id}/issues/{issue_id}/commentsissue_comment:read
List a work item's comments
Token scope
issue_comment:read
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
Query
- resolvedbooleanoptionalFilter by thread resolution
- limitint32optionalPage size (default 20, max 100)
- next_tokenstringoptionalPagination cursor
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found
Receive
- next_tokenobjectoptional
GET/groups/{group_id}/issues/{issue_id}/commentsissue_comment:read
List a group-scoped work item's comments
Token scope
issue_comment:read
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
Query
- resolvedbooleanoptionalFilter by thread resolution
- limitint32optionalPage size (default 20, max 100)
- next_tokenstringoptionalPagination cursor
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found
Receive
The same shape list_issue_comments returns — 1 field. Go to it
Show it here
- next_tokenobjectoptional
GET/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/repliesissue_comment:read
List the replies to a comment on a work item
list_issue_comment_repliesGET /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/repliesOpen in the API referenceToken scope
issue_comment:read
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidThe root comment
Query
- limitint32optionalPage size (default 20, max 100)
- next_tokenstringoptionalPagination cursor
Refuses
400The comment is itself a reply401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found
Receive
The same shape list_issue_comments returns — 1 field. Go to it
Show it here
- next_tokenobjectoptional
GET/groups/{group_id}/issues/{issue_id}/comments/{comment_id}/repliesissue_comment:read
List the replies to a comment on a group-scoped work item
list_group_issue_comment_repliesGET /groups/{group_id}/issues/{issue_id}/comments/{comment_id}/repliesOpen in the API referenceToken scope
issue_comment:read
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidThe root comment
Query
- limitint32optionalPage size (default 20, max 100)
- next_tokenstringoptionalPagination cursor
Refuses
400The comment is itself a reply401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found
Receive
The same shape list_issue_comments returns — 1 field. Go to it
Show it here
- next_tokenobjectoptional
GET/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}issue_comment:read
Get one comment on a work item
get_issue_commentGET /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}Open in the API referenceToken scope
issue_comment:read
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found
Receive
No body.
GET/groups/{group_id}/issues/{issue_id}/comments/{comment_id}issue_comment:read
Get one comment on a group-scoped work item
get_group_issue_commentGET /groups/{group_id}/issues/{issue_id}/comments/{comment_id}Open in the API referenceToken scope
issue_comment:read
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found
Receive
No body.
Writing
6 endpoints
POST/repositories/{repo_id}/issues/{issue_id}/commentsissue_comment:create
Comment on a work item in a repository
create_issue_commentPOST /repositories/{repo_id}/issues/{issue_id}/commentsOpen in the API referenceToken scope
issue_comment:create
Counts against
comments_per_issue
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
Body
- bodystring
- file_pathobjectoptional
- new_lineint32optional
- old_lineint32optional
- parent_idobjectoptional
Refuses
400Invalid request401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found409The request conflicts with the resource's current state; `reason` says how429Comment limit reached
Receive
No body.
POST/groups/{group_id}/issues/{issue_id}/commentsissue_comment:create
Comment on a group-scoped work item
create_group_issue_commentPOST /groups/{group_id}/issues/{issue_id}/commentsOpen in the API referenceToken scope
issue_comment:create
Counts against
comments_per_issue
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
Body
- bodystring
- file_pathobjectoptional
- new_lineint32optional
- old_lineint32optional
- parent_idobjectoptional
Refuses
400Invalid request401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found409The request conflicts with the resource's current state; `reason` says how429Comment limit reached
Receive
No body.
PATCH/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}issue_comment:update
Edit a comment on a work item
edit_issue_commentPATCH /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}Open in the API referenceToken scope
issue_comment:update
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Body
- bodystring
Refuses
400Empty body401Unauthenticated — no credential, or an invalid one403Not the author, and not a maintainer404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
PATCH/groups/{group_id}/issues/{issue_id}/comments/{comment_id}issue_comment:update
Edit a comment on a group-scoped work item
edit_group_issue_commentPATCH /groups/{group_id}/issues/{issue_id}/comments/{comment_id}Open in the API referenceToken scope
issue_comment:update
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Body
- bodystring
Refuses
400Empty body401Unauthenticated — no credential, or an invalid one403Not the author, and not a maintainer404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
DELETE/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}issue_comment:delete
Delete a comment on a work item
delete_issue_commentDELETE /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}Open in the API referenceToken scope
issue_comment:delete
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Not the author, and not a maintainer404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
DELETE/groups/{group_id}/issues/{issue_id}/comments/{comment_id}issue_comment:delete
Delete a comment on a group-scoped work item
delete_group_issue_commentDELETE /groups/{group_id}/issues/{issue_id}/comments/{comment_id}Open in the API referenceToken scope
issue_comment:delete
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Not the author, and not a maintainer404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
Thread resolution
4 endpoints
POST/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/resolveissue_comment:update
Resolve a discussion thread on a work item
resolve_issue_threadPOST /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/resolveOpen in the API referenceToken scope
issue_comment:update
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidThe thread's root comment
Refuses
400A reply, or already resolved401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
POST/groups/{group_id}/issues/{issue_id}/comments/{comment_id}/resolveissue_comment:update
Resolve a discussion thread on a group-scoped work item
resolve_group_issue_threadPOST /groups/{group_id}/issues/{issue_id}/comments/{comment_id}/resolveOpen in the API referenceToken scope
issue_comment:update
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidThe thread's root comment
Refuses
400A reply, or already resolved401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
DELETE/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/resolveissue_comment:update
Reopen a resolved discussion thread on a work item
unresolve_issue_threadDELETE /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/resolveOpen in the API referenceToken scope
issue_comment:update
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidThe thread's root comment
Refuses
400A reply, or not resolved401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
DELETE/groups/{group_id}/issues/{issue_id}/comments/{comment_id}/resolveissue_comment:update
Reopen a resolved discussion thread on a group-scoped work item
unresolve_group_issue_threadDELETE /groups/{group_id}/issues/{issue_id}/comments/{comment_id}/resolveOpen in the API referenceToken scope
issue_comment:update
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidThe thread's root comment
Refuses
400A reply, or not resolved401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
Reactions
6 endpoints
POST/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactionsissue_reaction:manage
React to a comment on a work item
add_issue_comment_reactionPOST /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactionsOpen in the API referenceToken scope
issue_reaction:manage
Counts against
reactions_per_comment
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Body
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe emoji reactions that can be placed on a comment — the same set GitHub offers.
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found409The request conflicts with the resource's current state; `reason` says how429Reaction limit reached
Receive
- comment_iduuid
reactions[]object3 fields
- countint32
- mebooleanWhether you placed this reaction.
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe emoji reactions that can be placed on a comment — the same set GitHub offers.
POST/groups/{group_id}/issues/{issue_id}/comments/{comment_id}/reactionsissue_reaction:manage
React to a comment on a group-scoped work item
add_group_issue_comment_reactionPOST /groups/{group_id}/issues/{issue_id}/comments/{comment_id}/reactionsOpen in the API referenceToken scope
issue_reaction:manage
Counts against
reactions_per_comment
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Body
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe emoji reactions that can be placed on a comment — the same set GitHub offers.
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found409The request conflicts with the resource's current state; `reason` says how429Reaction limit reached
Receive
The same shape add_issue_comment_reaction returns — 2 fields. Go to it
Show it here
- comment_iduuid
reactions[]object3 fields
- countint32
- mebooleanWhether you placed this reaction.
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe emoji reactions that can be placed on a comment — the same set GitHub offers.
DELETE/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactions/{reaction}issue_reaction:manage
Withdraw a reaction from a comment on a work item
remove_issue_comment_reactionDELETE /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactions/{reaction}Open in the API referenceToken scope
issue_reaction:manage
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe reaction to withdraw
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404The caller had not reacted this way409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
DELETE/groups/{group_id}/issues/{issue_id}/comments/{comment_id}/reactions/{reaction}issue_reaction:manage
Withdraw a reaction from a comment on a group-scoped work item
remove_group_issue_comment_reactionDELETE /groups/{group_id}/issues/{issue_id}/comments/{comment_id}/reactions/{reaction}Open in the API referenceToken scope
issue_reaction:manage
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe reaction to withdraw
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404The caller had not reacted this way409The request conflicts with the resource's current state; `reason` says how
Receive
No body.
GET/repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactionsissue_comment:read
List the reactions on a comment on a work item
list_issue_comment_reactionsGET /repositories/{repo_id}/issues/{issue_id}/comments/{comment_id}/reactionsOpen in the API referenceToken scope
issue_comment:read
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found
Receive
The same shape add_issue_comment_reaction returns — 2 fields. Go to it
Show it here
- comment_iduuid
reactions[]object3 fields
- countint32
- mebooleanWhether you placed this reaction.
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe emoji reactions that can be placed on a comment — the same set GitHub offers.
GET/groups/{group_id}/issues/{issue_id}/comments/{comment_id}/reactionsissue_comment:read
List the reactions on a comment on a group-scoped work item
list_group_issue_comment_reactionsGET /groups/{group_id}/issues/{issue_id}/comments/{comment_id}/reactionsOpen in the API referenceToken scope
issue_comment:read
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
- comment_iduuidComment ID
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Not found
Receive
The same shape add_issue_comment_reaction returns — 2 fields. Go to it
Show it here
- comment_iduuid
reactions[]object3 fields
- countint32
- mebooleanWhether you placed this reaction.
- reactionconfusedeyeshearthooraylaughrocketthumbs_downthumbs_upThe emoji reactions that can be placed on a comment — the same set GitHub offers.
Timeline
2 endpoints
GET/repositories/{repo_id}/issues/{issue_id}/activitiesissue_comment:read
List a work item's timeline
list_issue_activitiesGET /repositories/{repo_id}/issues/{issue_id}/activitiesOpen in the API referenceToken scope
issue_comment:read
Send
Path
- repo_iduuidRepository ID
- issue_idint64The per-scope iid
Query
- limitint32optionalPage size (default 20, max 100)
- next_tokenstringoptionalPagination cursor
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found
Receive
items[]object11 fields
- activity_iduuid
- activity_typeassignedbranch_createdclosedclosed_by_marker_removalcommentedcommit_linkedcreatedcriterion_failedcriterion_stalecriterion_verifiedmerge_request_linkedmovedquick_action_deniedregressedrelatedreopenedreparentedstatus_changedthread_resolvedthread_unresolvedtransition_skippedunassignedunrelatedupdatedverifiedWhat happened, in one entry of a work item's timeline. Deleting a work item deletes its timeline, so there is no entry for a deletion.
actorobject4 fields
- avatar_urlobjectoptional
- emailstring
- idstring
- usernamestring
- created_atstring
- iidint64
- issue_uiduuid
- metadataobject
- scopeobjectA group or a repository, whichever a setting belongs to.
- next_tokenobjectoptional
GET/groups/{group_id}/issues/{issue_id}/activitiesissue_comment:read
List a group-scoped work item's timeline
list_group_issue_activitiesGET /groups/{group_id}/issues/{issue_id}/activitiesOpen in the API referenceToken scope
issue_comment:read
Send
Path
- group_iduuidGroup ID
- issue_idint64The per-scope iid
Query
- limitint32optionalPage size (default 20, max 100)
- next_tokenstringoptionalPagination cursor
Refuses
400Bad request — a path parameter is not a well-formed value of its type401Unauthenticated — no credential, or an invalid one403Forbidden — the caller's access level or token scope is insufficient404Work item not found
Receive
The same shape list_issue_activities returns — 2 fields. Go to it
Show it here
items[]object11 fields
- activity_iduuid
- activity_typeassignedbranch_createdclosedclosed_by_marker_removalcommentedcommit_linkedcreatedcriterion_failedcriterion_stalecriterion_verifiedmerge_request_linkedmovedquick_action_deniedregressedrelatedreopenedreparentedstatus_changedthread_resolvedthread_unresolvedtransition_skippedunassignedunrelatedupdatedverifiedWhat happened, in one entry of a work item's timeline. Deleting a work item deletes its timeline, so there is no entry for a deletion.
actorobject4 fields
- avatar_urlobjectoptional
- emailstring
- idstring
- usernamestring
- created_atstring
- iidint64
- issue_uiduuid
- metadataobject
- scopeobjectA group or a repository, whichever a setting belongs to.
- next_tokenobjectoptional
Limits
Two caps apply here, and both are per-plan: one on the comments a work item can carry, one on the reactions a single comment can carry. They bite on posting a comment and on placing a reaction, on a repository item and a group item alike; everything else on this page is uncapped. A capped request is refused with 429 TOO_MANY_REQUESTS and nothing is written — no partial comment and no half-placed reaction.
| Cap | Free | Pro | Team | Enterprise |
|---|---|---|---|---|
| Comments on one work item | 500 | 2,000 | 5,000 | 10,000 |
| Reactions on one comment | 100 | 250 | 500 | 1,000 |
Replies count. The comment cap is the total number of comments on the item, threads and replies together, not the number of threads. Deleting brings an item back under its ceiling, and deleting a thread root takes the whole thread out at once, so pruning is the way back from a wall of comments.
The reaction cap is per comment across everybody, not per person: it is the number of reactions the comment carries in total. A comment that a hundred people have given a thumbs-up is at the Free ceiling.
Three more bounds apply regardless of plan, because they are shapes rather than allowances:
| Bound | Value |
|---|---|
| Comment body | 1–10,000 characters, after trimming |
| People notified per comment | the first 25 distinct @-mentions |
| Threading depth | one level — a thread root and its replies |
Raising a cap. Plan limits are a starting point, not a ceiling. An Owner can ask for a single limit to be raised for the namespace without changing plan; moving to a higher plan raises every limit at once. Both are on the billing and plans page.
Troubleshooting
400 BAD_REQUESTmentioning body and must be 1-10000 characters
- Cause
- The comment is empty, is nothing but whitespace, or is longer than ten thousand characters
- What to do
- Write something, or split a very long comment. Whitespace is trimmed before the length is measured, so a body of spaces counts as empty
400 BAD_REQUEST— Cannot reply to a reply; only one level of threading is supported
- Cause
- You named a reply as the parent of a new reply
- What to do
- Name the thread's root comment instead. Every comment carries the id of the thread it belongs to; use that
400 BAD_REQUEST— Inline comments need a diff to anchor to, and a work item has none
- Cause
- You sent a file path with a comment on a work item
- What to do
- Drop the file path and line fields. To point at a place in the code from a work item, add a code anchor — see linking work to code
400 BAD_REQUEST— Inline fields (file_path, old_line, new_line) are not allowed on replies
- Cause
- A reply carried line-anchoring fields
- What to do
- Send the reply with a body and a parent and nothing else
400 BAD_REQUEST— old_line and new_line require file_path to be set
- Cause
- Line numbers were sent without a file
- What to do
- Remove them; a work item's comments are not line-anchored
400 BAD_REQUEST— Cannot list replies for a reply; provide the root comment ID
- Cause
- You asked for the replies of a reply
- What to do
- Use the thread's root comment id, which every reply carries
400 BAD_REQUEST— Can only resolve root comments, not replies (or Can only unresolve…, if you were reopening)
- Cause
- You aimed a resolve or a reopen at a reply
- What to do
- Send it at the thread's root instead — every reply carries the id of the thread it belongs to. Resolution is a property of the whole thread
400 BAD_REQUEST— Thread is already resolved
- Cause
- Somebody resolved it first, or the call was retried
- What to do
- Treat it as success if you were resolving; re-read the thread if you expected it to be open, because somebody answered it
400 BAD_REQUEST— Thread is not resolved
- Cause
- You reopened a thread that was already open
- What to do
- Nothing to do. Check the resolved flag on the root before calling if a script loops over threads
400 BAD_REQUESTmentioning reaction
- Cause
- The reaction is not one of the eight the platform accepts
- What to do
- Use
thumbs_up,thumbs_down,laugh,hooray,confused,heart,rocketoreyes
400 BAD_REQUEST— Invalid repo_id, Invalid group_id, Invalid issue_id or Invalid comment_id
- Cause
- The value in that position of the URL is not a well-formed one — usually a path like
my-group/my-repopasted where an id belongs, or an id pasted where the item's number belongs - What to do
- Use the repository's or group's id, and the item's small per-container number for
issue_id. A comment id comes from a listing or from the item's timeline, never from the address bar
401 UNAUTHORIZEDon a write
- Cause
- You are not signed in, or the credential is expired or malformed
- What to do
- Sign in, or mint a fresh token. Reading a public item works without a credential; writing never does
403 FORBIDDENon commenting, replying, resolving or reacting
- Cause
- You can read the item but you are not a member — reading a public or internal item resolves you to a level below Reporter
- What to do
- Ask an Owner or Maintainer to add you as a Reporter on the repository or group. Read access alone does not carry write access
403 FORBIDDENon editing or deleting, where your own comments work
- Cause
- The comment belongs to somebody else, which needs Maintainer
- What to do
- Ask a Maintainer to remove it, or ask the author to edit it. Your own comments need nothing beyond Reporter
403 INSUFFICIENT_SCOPE
- Cause
- The token is valid but was minted without the scope the call needs
- What to do
- Mint a new token carrying the scope from the table in Before you start. Scopes are fixed when a token is created and cannot be added afterwards
404 NOT_FOUNDon a work item you are sure exists
- Cause
- The number is wrong for this container, the item was moved and renumbered, or the item is in a private repository you are not a member of
- What to do
- Check the container in the URL against where the item lives now. A private resource answers
404rather than403so that its existence is not leaked
404 NOT_FOUNDon a comment
- Cause
- The comment was deleted, or it belongs to a different work item than the one in the URL
- What to do
- Re-read the discussion. A comment id is only valid under the item that owns it, so an id copied from another item never resolves
404 NOT_FOUNDwhen withdrawing a reaction
- Cause
- You had not placed that reaction on that comment
- What to do
- Nothing to withdraw. Check which reactions are yours — each one in a comment's summary says whether you placed it
429 TOO_MANY_REQUESTS— comment limit
- Cause
- The work item has reached its plan's comment ceiling, counting replies
- What to do
- Delete comments the item no longer needs, or raise the limit. See Limits above
429 TOO_MANY_REQUESTS— reaction limit
- Cause
- The comment has reached its plan's reaction ceiling, counting everybody's
- What to do
- Withdraw reactions, or raise the limit
Replies are missing from the comment listing
- Cause
- The comments listing returns thread roots only
- What to do
- Use the discussions listing for the whole discussion, or the replies listing for one thread
A name in a comment's reactions shows as deleted-user
- Cause
- The account that placed that reaction no longer exists
- What to do
- Nothing to do. The count is still right and the comment is untouched; only that one identity is gone. A comment's author does not read this way — an account cannot be removed while a comment it wrote is still standing
A whole thread disappeared
- Cause
- Somebody deleted its root comment, which takes every reply and reaction with it
- What to do
- Deletion is not recoverable. The item's timeline still shows that the comment was posted and by whom, but it records no deletion — an Owner can find who deleted it in the audit log
A slash command in a comment did nothing
- Cause
- Commands like
/assignand/closeare read out of commit messages, not comment bodies - What to do
- Set the field on the work item directly, or put the command in the commit message that does the work — see linking work to code
An @-mention did not reach anybody
- Cause
- The name does not match a username, the person was the 26th mentioned in one comment, the
@sat somewhere names are not read (a code span, a code block, a heading or a URL), an/assignor/unassignin the same comment already claimed the name, or you named yourself - What to do
- Check the spelling against the person's username rather than their display name, keep a comment under 25 distinct mentions, and put the name in an ordinary sentence — not in a heading, not in backticks, and not as the argument of
/assign
A comment you posted is not on the timeline yet
- Cause
- The timeline is written just behind the comment
- What to do
- Re-read it a moment later. The comment itself is stored the instant the call succeeds; the timeline entry follows
FAQ
Can I comment on a closed work item?
Yes. Closing an item does not close its discussion, and the conversation after the fact is often the one worth keeping.
Who can resolve a thread — only the person who started it?
Anybody with Reporter access on the item's repository or group. The person who receives the answer is frequently not the person who asked, and requiring the author to come back is how threads stay open forever.
Does editing a comment re-notify the people it mentions?
No. The mentions are resolved when the comment is posted. Adding an @ in an edit changes the text everyone sees but does not put the item in that person's inbox — post a new comment when you want somebody's attention.
Can I comment on a specific line of a file from a work item?
Not as a comment. A work item is not a change and has no diff for a comment to pin to. A code anchor is the durable way to point a work item at a place in the code, and it survives the code moving; see linking work to code.
Are merge request comments the same thing?
They behave the same way — the same threading, the same resolution, the same eight reactions — but they are a separate discussion with their own operations and their own scopes, they ask for Developer on the repository rather than Reporter, and they can additionally pin to a line of the diff. A merged or closed merge request also stops taking comments, which a closed work item does not. See merge request discussion.
Does a resolved thread hide from the discussion?
It collapses rather than disappears, and it is still returned by every listing. Filter the comment listing by resolution when you want only one or the other.
Related
- Work itemscreating the item the discussion hangs off, its status, assignees and hierarchy
- Linking work to codecommit-message actions, code anchors, and how a commit closes an item
- Merge request discussionthe same threading model on a change, with line-anchored comments
- Your dashboardthe attention inbox a mention lands in, and the activity feed
- Access tokensminting a token with the comment and reaction scopes
- Audit logwhere a comment edit or deletion is recorded, and who can read it
- Billing and plansplan tiers, and asking for a single limit to be raised