Read and post comments on deployment requests, including inline comments on the diff.
Read and post comments on deployment requests, including inline comments on the diff.
The Comments API lets external tools take part in the review conversation on a deployment request. A CI pipeline, static analysis tool, or bot can read the discussion, post findings as comments, point at specific lines of the diff, reply to threads, and resolve them when the issue is fixed.
Comments posted through the API appear in the deployment request's discussion in the app, exactly like comments written by a teammate.
Who the comment is from
Every comment is authored as the user that owns the OAuth client making the request. If you want comments to be clearly attributed to an automation, create the OAuth client under a dedicated user, such as "CI Bot". Any user can resolve any thread, so a dedicated user can resolve threads that an earlier run, or a person, opened.
Top-level and inline comments
There are two kinds of comments:
- Top-level comments are posted on the deployment request as a whole. Send only a
body. They cannot be replied to or resolved, and theirresolvablefield isfalse. - Inline comments are attached to a line of the deployment's diff. Send a
pathand the line, as described below. Each inline comment starts a thread: itsresolvablefield istrue, and it can be replied to and resolved.
Comment bodies are Markdown. Mention a user with @ followed by their email address, for example @[email protected]; mentioned users are notified by email.
Which line fields to send
An inline comment can only be anchored to a line that is part of the diff: a changed line, or one of the unchanged context lines shown around a change. Which field to send depends on the kind of line. These are the same rules the diff view in the app follows.
| Line | Send |
|---|---|
| Added line | newLine only |
| Removed line | oldLine only |
| Unchanged (context) line | both oldLine and newLine |
A position that does not follow these rules, or a line outside the diff, returns 400 with the reason in error.
Anchoring inline comments to the diff
An inline comment is pinned to a specific diff, identified by a base and head commit. By default the comment uses the deployment's current diff: the targetCommit and mergeCommit returned by Get a deployment by number. You can pass baseSha and headSha yourself to pin the comment to the exact commits your tool analyzed. You must pass both or neither.
Both commits are null until the deployment has been built. Posting an inline comment without baseSha/headSha before then returns 409. mergeCommit changes whenever the request is rebuilt, for example after an out-of-date request is updated, so re-read the deployment before each review run.
Threads
Each comment has a discussionId that identifies its thread. Replies share the discussionId of the comment they reply to. Use this value with the discussion endpoints:
POST /deployments/{deploymentNumber}/discussions/{discussionId}/commentsadds a reply.PUT /deployments/{deploymentNumber}/discussions/{discussionId}with{"resolved": true}resolves the thread, and{"resolved": false}reopens it.
Only inline comment threads accept replies and can be resolved. Using a top-level comment's discussionId returns 400.
Every comment also carries a url: a link to its thread on the deployment request's Discussion tab in the app, which scrolls the thread into view. Use it to point people at a finding from a pull request, a chat message or a ticket.
Example: report a finding and resolve it later
- Post an inline comment on the offending line:
curl -X POST "https://COMPANY.my.bluecanvas.io/apis/rest/v1/deployments/154/comments" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"body": "Coverage dropped below 75% in this class.", "path": "src/classes/Invoice.cls", "newLine": 42}'
-
Save the
idanddiscussionIdfrom the response. -
On a later run, once the issue is fixed, resolve the thread:
curl -X PUT "https://COMPANY.my.bluecanvas.io/apis/rest/v1/deployments/154/discussions/6a9c41d2e8b0f3a7c5e1d9b2a4f6c8e0d1b3a5c7" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"resolved": true}'
Keeping comments up to date
Editing. PATCH /deployments/{deploymentNumber}/comments/{commentId} with {"body": "<markdown>"} replaces the body of a comment you posted earlier, so a review summary can be updated in place on each run instead of posted again. Only the comment's author can edit it (403 otherwise).
Avoiding duplicates. There is no idempotency key or client-supplied identifier on POST. Comments are stored as discussion notes in the repository service behind each deployment request, which has no field for one, so the API cannot recognise a retry of a request it already received. Design your client to list the comments first and skip findings it already posted, and if a POST fails without a response, list before retrying rather than posting again.
Notifications
Each comment, whether posted in the app or through the API, triggers the notifications the workspace has configured for new comments (Slack, Teams, or email), plus an email to every user mentioned in it. Notifications are sent per comment and are not grouped, so a review that posts many comments produces as many notifications. Where it fits, prefer one summary comment and a small number of inline threads.
To react to comments from your own systems, subscribe to the deployments/commented webhook. It is published for every comment, whether posted in the app or through the API, and carries the comment in the same shape as this API returns it.
When comments become available
The discussion behind a deployment request is created asynchronously, a few moments after the deployment request itself, and never for drafts. Until then:
- Listing comments returns an empty list.
- Posting, replying, resolving, or deleting returns
409with"Deployment has no merge request yet. Retry shortly". Retry after a short delay.
Deleting comments
Delete a comment removes a single comment by its id. Only the comment's author can delete it, so a client can delete its own comments but not those of other users (403).
Errors
Errors return a JSON body with an error message:
{
"error": "Discussion not found"
}
| Status | Meaning |
|---|---|
400 | Invalid request body, an inline position that is not on the diff, or an operation that is not allowed on a top-level comment. |
403 | Editing or deleting a comment written by another user. |
404 | The deployment, comment, or discussion does not exist. |
409 | The deployment's discussion or diff is not ready yet. Retry shortly. |
