Add Comment

Add a comment to a record with rich HTML content, file attachments, @mentions, and threaded replies.


Use the createComment mutation to post a comment on a record. Comments accept rich HTML content, file attachments, and @mentions, and can be posted as threaded replies to an existing comment. Each comment is added to the record’s activity feed and triggers the relevant notifications, subscriptions, and webhooks. Records are Record objects in the API.

The same mutation comments on a record (category: TODO), a discussion (category: DISCUSSION), or a status update (category: STATUS_UPDATE); this page focuses on commenting on records.

Request

The smallest call posts a comment on one record. Pass the record ID as categoryId with category: TODO, and supply both the rendered html and a plain-text text fallback.

mutation AddComment {
  createComment(
    input: {
      category: TODO
      categoryId: "todo_123"
      html: "<p>This task is progressing well.</p>"
      text: "This task is progressing well."
    }
  ) {
    id
    html
    createdAt
    user {
      id
      fullName
    }
  }
}

Parameters

CreateCommentInput

ParameterTypeRequiredDescription
categoryCommentCategory!YesThe kind of entity being commented on. Use TODO for records.
categoryIdString!YesID of the entity being commented on. For TODO, this is the record (Record) ID.
htmlString!YesRendered HTML of the comment. Sanitized on the server before it is stored.
textString!YesPlain-text version of the comment, used as a fallback and for search.
tiptapBooleanNoEnables TipTap-aware sanitization. Set true to parse embedded file-attachment <div> elements. Without it, attachment markup is stripped.
parentIdStringNoID of the comment to reply to. Omit for a top-level comment; set it to nest this comment as a threaded reply.

CommentCategory

ValueDescription
TODOA comment on a record.
DISCUSSIONA comment on a discussion thread.
STATUS_UPDATEA comment on a status update.

Response

{
  "data": {
    "createComment": {
      "id": "clm4n8qwx000008l0g4oxdqn7",
      "html": "<p>This task is progressing well.</p>",
      "createdAt": "2026-05-29T14:03:11.000Z",
      "user": {
        "id": "clm4n8qwx000108l0d2k1abcd",
        "fullName": "Ada Lovelace"
      }
    }
  }
}

Returns

createComment returns the created Comment.

FieldTypeDescription
idID!Unique identifier for the comment.
uidString!Short public identifier.
htmlString!Sanitized HTML content of the comment.
textString!Plain-text content.
categoryCommentCategory!The entity type this comment belongs to.
createdAtDateTime!When the comment was created.
updatedAtDateTime!When the comment was last updated.
deletedAtDateTimeWhen the comment was deleted, or null if active.
deletedByUserThe user who deleted the comment, if any.
userUser!The author of the comment. Select id, fullName, and image { … } (there is no name or avatar field on User).
activityActivityThe activity-feed entry created for this comment.
todoRecordThe record this comment is on, when category is TODO.
discussionDiscussionThe discussion, when category is DISCUSSION.
statusUpdateStatusUpdateThe status update, when category is STATUS_UPDATE.
parentIdStringID of the parent comment, or null for a top-level comment.
parentCommentThe parent comment when this is a threaded reply.
replies[Comment!]!The threaded replies to this comment.
replyCountInt!Number of replies on this comment.
reactions[ReactionGroup!]!Emoji reactions, grouped by emoji (emoji, count, users { … }, reactedByMe).
isReadBooleanWhether the current user has read this comment.
isSeenBooleanWhether the current user has seen this comment.
aiSummaryBooleanWhether this comment was generated as an AI summary.

Full example

Post a richly formatted comment as a threaded reply, and read back the threading and reaction fields:

mutation AddReply {
  createComment(
    input: {
      category: TODO
      categoryId: "todo_123"
      parentId: "comment_123"
      tiptap: true
      html: "<p>Agreed — here is my <strong>feedback</strong>:</p><ul><li>Great progress on the design</li><li>Need to review the API integration</li></ul>"
      text: "Agreed — here is my feedback: - Great progress on the design - Need to review the API integration"
    }
  ) {
    id
    html
    createdAt
    parentId
    replyCount
    reactions {
      emoji
      count
      reactedByMe
    }
    user {
      id
      fullName
      image {
        thumbnail
        small
      }
    }
  }
}

File attachments

To attach an uploaded file so it renders as a clickable attachment (the same way files appear when attached in the Blue UI), embed it as a <div> in the html field and set tiptap: true. Without tiptap: true, the attachment markup is stripped by sanitization.

Step 1 — Upload the file

Upload the file with the uploadFile mutation to get its metadata (uid, name, size, type, extension).

Step 2 — Embed the attachment

Each attachment is a <div> carrying a JSON file attribute:

<div
  class="attachment"
  file='{"uid":"FILE_UID","name":"FILENAME","size":SIZE_IN_BYTES,"type":"MIME_TYPE","extension":"EXT"}'
></div>

Step 3 — Post the comment

mutation AddCommentWithFile {
  createComment(
    input: {
      category: TODO
      categoryId: "todo_123"
      tiptap: true
      html: "<p>Here is the report.</p><div class=\"attachment\" file='{\"uid\":\"cm8qujq3b01p22lrv9xbb2q62\",\"name\":\"report.pdf\",\"size\":204800,\"type\":\"application/pdf\",\"extension\":\"pdf\"}'></div>"
      text: "Here is the report."
    }
  ) {
    id
    html
    createdAt
  }
}

For an end-to-end walkthrough (upload + comment, with Python), see Attaching files to comments. Once uploaded, a file’s download URL is https://api.blue.app/uploads/{uid}/{url_encoded_filename} — see File URLs for thumbnail and inline variants.

Side effects

Posting a comment creates an activity-feed entry, fires any configured project webhooks, publishes to GraphQL subscriptions for live updates, and sends notifications — including targeted notifications for any @mentioned users.

Errors

CodeWhen
FORBIDDENThe user is not a member of the record’s workspace, has VIEW_ONLY access, holds a custom role with records disabled, or the workspace is archived. The same code is returned when categoryId does not resolve to a record the user can access.
BAD_USER_INPUTA required field is missing, category is not a valid CommentCategory, or the input is otherwise malformed.
{
  "errors": [
    {
      "message": "You don't have permission to perform this action.",
      "extensions": { "code": "FORBIDDEN" }
    }
  ]
}

Permissions

Any workspace member can comment on a record except those with VIEW_ONLY access. A custom role that has records disabled (isRecordsEnabled: false) cannot comment, and no one can comment on a record in an archived workspace.

Access levelCan comment
OWNERYes
ADMINYes
MEMBERYes
CLIENTYes
COMMENT_ONLYYes
VIEW_ONLYNo