Comment events are published when comments are created, updated, or deleted on various entities throughout the platform. Comments can be attached to tickets, account tasks, account activities, and account notes. All comment events are published to the AWS_SNS_TICKET_TOPIC_ARN topic.

Event types by entity

Comments can be associated with different entity types, and the event names reflect this:

Ticket comments

ticket:comment:created

Triggered when a comment is added to a ticket.

ticket:comment:updated

Triggered when a comment on a ticket is updated.

ticket:comment:deleted

Triggered when a comment is deleted from a ticket.

Account task comments

account-task:comment:created

Triggered when a comment is added to an account task.

account-task:comment:updated

Triggered when a comment on an account task is updated.

account-task:comment:deleted

Triggered when a comment is deleted from an account task.

Account activity comments

account-activity:comment:created

Triggered when a comment is added to an account activity.

account-activity:comment:updated

Triggered when a comment on an account activity is updated.

account-activity:comment:deleted

Triggered when a comment is deleted from an account activity.

Account note comments

account-note:comment:created

Triggered when a comment is added to an account note.

account-note:comment:updated

Triggered when a comment on an account note is updated.

account-note:comment:deleted

Triggered when a comment is deleted from an account note.

Comment event structure

Standard comment events (created/deleted)

interface CommentEventPayload {
  comment: {
    id: string;
    content?: string; // May be omitted for certain visibility rules
    contentHtml?: string;
    contentMarkdown?: string;
    contentJson?: string;
    commentType: string;
    parentCommentId?: string;
    teamId?: string; // For ticket comments
    accountId?: string; // For account-related comments
    metadata: Record<string, unknown>;
    customerContact?: {
      id: string;
      email: string;
      avatarUrl: string;
    };
    author: {
      id: string;
      name: string;
      email: string;
      avatarUrl: string;
    };
    attachments: Array<{
      id: string;
      name: string;
      url: string;
      size: number;
      contentType: string;
      createdAt: Date;
      updatedAt: Date;
      deletedAt?: Date;
    }>;
    commentVisibility: string;
    createdAt: Date;
    updatedAt: Date;
    deletedAt?: Date;
    shouldSendEmail?: boolean; // Only for created events
    createdWithTicket?: boolean; // Only for created events
  };
  // Entity-specific payload additions
  ticket?: {
    id: string;
    title: string;
    description?: string;
    requestorEmail?: string;
    submitterEmail?: string;
    source: string;
    isProactive?: boolean;
    proactiveChannels?: string[];
  };
  accountTask?: {
    id: string;
    title: string;
  };
  accountActivity?: {
    id: string;
  };
  accountNote?: {
    id: string;
  };
}

Comment update events

For update events, the payload structure includes both previous and updated states:
interface CommentUpdateEventPayload {
  comment: {
    previous: CommentData; // Previous state of the comment
    updated: CommentData;  // Updated state of the comment
  };
  // Same entity-specific additions as above
}

Reaction events

Comments can have reactions (emoji responses) which generate their own events:

ticket:comment:reaction:added

Triggered when a reaction is added to a ticket comment.

ticket:comment:reaction:removed

Triggered when a reaction is removed from a ticket comment. Reaction event payload:
interface ReactionEventPayload {
  reaction: {
    name: string; // Emoji name (e.g., "thumbs_up", "heart")
    author: {
      id: string;
      name: string;
      avatarUrl: string;
      email: string;
    };
    metadata: Record<string, any>;
  };
  comment: {
    id: string;
  };
}

Content filtering and privacy

Email source content protection

For comments on tickets with email source and public visibility, certain content fields may be omitted from the platform payload for privacy reasons:
  • content
  • contentMarkdown
  • contentJson
Only contentHtml is included in these cases.

Large content truncation

Comments with large content (approaching the 256KB platform limit) will have their content fields truncated and replaced with “Payload too large. Use API instead”.

Event structure

All comment events follow the standard platform event structure:
interface CommentSNSEvent {
  eventId: string;
  eventType: string; // e.g., "ticket:comment:created"
  timestamp: string;
  orgId: string;
  actor: {
    id: string;
    type: string;
    email: string;
  };
  payload: CommentEventPayload | CommentUpdateEventPayload | ReactionEventPayload;
}

Special metadata

Mention metadata

When a comment contains user mentions, the metadata may include:
{
  mentions: {
    users: Array<{
      id: string;
      email: string;
      name: string;
    }>;
  };
  ignoreSelf: boolean; // Indicates if the author should be excluded from notifications
}

Integration metadata

Comments created through integrations may include additional metadata:
{
  source: "slack" | "email" | "api" | "web";
  integrationId?: string;
  externalId?: string;
  syncStatus?: string;
}

Integration examples

Comment notification system

function handleCommentCreated(payload) {
  const { comment, ticket, accountTask } = payload;
  
  // Determine notification recipients
  const recipients = [];
  
  if (ticket) {
    // Notify ticket participants
    recipients.push(ticket.assignedAgent);
    recipients.push(ticket.customer);
  } else if (accountTask) {
    // Notify task assignee and watchers
    recipients.push(accountTask.assignedUser);
    recipients.push(...accountTask.watchers);
  }
  
  // Send notifications
  recipients.forEach(recipient => {
    if (comment.shouldSendEmail) {
      sendEmailNotification(recipient, comment, { ticket, accountTask });
    }
    sendInAppNotification(recipient, comment);
  });
}

Comment synchronization

function handleCommentUpdate(payload) {
  const { comment } = payload;
  const { previous, updated } = comment;
  
  // Sync to external systems
  if (previous.content !== updated.content) {
    // Content changed
    syncContentToExternalSystems(updated);
    
    // Log edit history
    logCommentEdit({
      commentId: updated.id,
      previousContent: previous.content,
      newContent: updated.content,
      editedBy: payload.actor.id,
      editedAt: updated.updatedAt
    });
  }
  
  // Update search index
  updateSearchIndex(updated);
}

Reaction aggregation

function handleReactionEvent(payload) {
  const { reaction, comment } = payload;
  
  // Update reaction counts
  updateReactionCounts(comment.id, reaction.name, payload.eventType);
  
  // Notify comment author (if not self-reaction)
  if (reaction.author.id !== comment.author.id) {
    notifyCommentAuthor(comment.author.id, {
      type: 'reaction',
      reaction: reaction.name,
      reactorName: reaction.author.name,
      commentId: comment.id
    });
  }
  
  // Track engagement metrics
  trackEngagement({
    type: 'reaction',
    reactionType: reaction.name,
    commentId: comment.id,
    userId: reaction.author.id
  });
}

Mention processing

function handleCommentWithMentions(payload) {
  const { comment, metadata } = payload;
  
  if (metadata?.mentions?.users) {
    metadata.mentions.users.forEach(mentionedUser => {
      // Skip self-mentions if ignoreSelf is true
      if (metadata.ignoreSelf && mentionedUser.id === comment.author.id) {
        return;
      }
      
      // Send mention notification
      sendMentionNotification(mentionedUser, {
        comment,
        mentionedBy: comment.author,
        context: payload.ticket || payload.accountTask || payload.accountActivity
      });
      
      // Track mention metrics
      trackMention({
        mentionedUserId: mentionedUser.id,
        mentionedByUserId: comment.author.id,
        commentId: comment.id
      });
    });
  }
}

Comment threading

Comments support threading through the parentCommentId field:
function handleThreadedComment(payload) {
  const { comment } = payload;
  
  if (comment.parentCommentId) {
    // This is a reply to another comment
    const parentComment = getCommentById(comment.parentCommentId);
    
    // Notify parent comment author
    notifyCommentAuthor(parentComment.author.id, {
      type: 'reply',
      replyId: comment.id,
      replyContent: comment.content,
      repliedBy: comment.author
    });
    
    // Update thread metrics
    updateThreadMetrics(comment.parentCommentId);
  }
}

Attachment handling

Comments can include file attachments:
function handleCommentAttachments(payload) {
  const { comment } = payload;
  
  comment.attachments?.forEach(attachment => {
    // Process attachment
    processAttachment({
      id: attachment.id,
      name: attachment.name,
      size: attachment.size,
      contentType: attachment.contentType,
      url: attachment.url,
      commentId: comment.id
    });
    
    // Virus scan for new attachments
    if (payload.eventType.includes('created')) {
      scheduleVirusScan(attachment.id);
    }
    
    // Update storage metrics
    updateStorageMetrics(attachment.size);
  });
}

Best practices

  1. Content safety: Always sanitize comment content before displaying
  2. Mention handling: Respect user notification preferences for mentions
  3. Threading: Maintain proper parent-child relationships for threaded comments
  4. Privacy: Be aware of content filtering for email sources
  5. Performance: Consider comment volume for high-traffic tickets/entities
  6. Attachments: Implement proper security scanning for file attachments

Event frequency

Comment events can be high-frequency, especially for:
  • Active support tickets
  • Collaborative account management tasks
  • Popular discussion threads
Consider implementing:
  • Rate limiting for notification systems
  • Batching for non-critical integrations
  • Proper indexing for search systems
  • Efficient storage for comment history