---
title: "Comments in Template"
description: "Let users open comment threads on sections and blocks, mention teammates, react to comments and resolve conversations, backed by your own API."
url: https://docs.topol.io/email-editor/guide/comments.html
---

# Comments in Template

> **This feature is available with the Plugin for Business plan and higher.**
>
> If you're on the Plugin for Startup or Plugin Expansion plan, consider upgrading to access this feature. For more details, visit our [pricing page](https://topol.io/pricing) or contact support.

**Comments attach a conversation thread to any section or block in the template.** Users can reply, mention teammates, react with a thumb up or down, and resolve a thread once it is handled. Every conversation is stored through your own API endpoints.

To enable the feature, set the following option inside Topol Options:

```js
enableComments: true;
```

## Identifying users and the template

Comments work without the options below, but attribution and unread tracking depend on them. Without a current user the editor sends an empty `current_user_id`, and it can no longer tell which comments are unread.

```js
currentUser: {
  userId: "current user user_id",
  name: "current user name",
  profilePhotoUrl:
    "link to the current user's profile picture",
};
templateId: "id";
```

`teamUsers` lists the colleagues who can be mentioned in a thread.

```js
teamUsers: [
  {
    userId: "1",
    name: "Jane Doe",
    profilePhotoUrl: "...",
  },
  {
    userId: "2",
    name: "John Doe",
    profilePhotoUrl: "...",
  },
];
```

**All three properties are required** on both `currentUser` and every entry in `teamUsers`, and `userId` has to be a string. The current user is filtered out of `teamUsers` automatically, so passing the whole team is fine; that person simply cannot mention themselves.

> **WARNING**
>
> **Before implementing the endpoints, check [how to work with API endpoints.](https://docs.topol.io/email-editor/guide/how-to-work-with-your-api.html)**

## List conversations

-   URL: `/{API.CONVERSATIONS}`
-   Method: `GET`
-   Content-Type: `application-json`

Params:

| key | value |
| --- | --- |
| key | authorization api key |
| hostname | hostname |
| template\_id | id of the template |
| entity\_id | entity\_id corresponds to userId in Options |
| current\_user\_id | user id of current user |
| sort\_by | "date" or "unread" |
| show\_resolved | boolean |

The editor calls this endpoint on load, whenever sorting or the resolved-display preference changes, after a comment is marked read or unread, and on every `TopolPlugin.refreshComments(key)` call.

Response:

```json
{
  "success": true,
  "data": [
    {
      "key": "conversation_key", //section or block uid on which the conversation is based
      "type": "block", // or section
      "resolved": false,
      "delete": false,
      "involved_users": [
        {
          "user_id": "user_id",
          "profile_photo_url": "link to the user's profile picture",
          "name": "user name"
        }
      ], // list of involved users, maximum of the first 7 users will be displayed
      "comments": [
        {
          "id": "comment_id",
          "created_by": {
            "user_id": "user_id",
            "profile_photo_url": "link to the user's profile picture",
            "name": "user name"
          },
          "created_at": "ISO 8601 time",
          "content": "text content of the comment",
          "read_by": ["user_id of the user who saw the comment"],
          "reactions": [
            {
              "created_by": {
                "user_id": "user_id",
                "profile_photo_url": "link to the user's profile picture",
                "name": "user name"
              },
              "type": "thumb-up" // or thumb-down
            }
          ]
        }
      ]
    }
  ]
}
```

A few details are worth knowing before implementing this response:

-   `resolved` accepts `true`, `false`, `0` or `1`, which suits a MySQL-backed store directly. Both `resolved` and `delete` may be omitted.
-   `reactions` may be omitted. Only `thumb-up` and `thumb-down` are rendered; any other value is accepted and then ignored.
-   Every object under `created_by` needs all three of `user_id`, `name` and `profile_photo_url`. The list is validated as a whole, so one comment with an incomplete author blanks the entire panel.
-   Unread state is derived from the **last comment** of each thread. `read_by` on earlier comments is never inspected.

## Detail conversation

-   URL: `/{API.CONVERSATIONS}/{conversation_key}`
-   Method: `GET`
-   Content-Type: `application-json`
-   Params: `key`, `hostname`, `template_id`, `entity_id`, `current_user_id`

The response is the **same conversation object as above, returned on its own rather than inside an array**. The `involved_users` list is not rendered in this view.

This endpoint is called when a conversation is opened, when the user selects a different block or section, right after a comment is posted, and when `TopolPlugin.refreshComments("conversation_key")` is called with the key of the open conversation.

Unread messages should be marked as read.

## Resolve or unresolve conversation

-   URL: `/{API.CONVERSATIONS}/{conversation_key}`
-   Method: `PATCH`

This endpoint is called when you click to resolve button.

Request:

```json
{
  "template_id": "id of the template",
  "entity_id": "entity user_id",
  "current_user_id": "current user user_id",
  "hostname": "origin",
  "key": "api key",
  "resolved": true // or false depending on whether we want resolved or unresolved
}
```

Expected Response: STATUS 200

## Delete conversation

-   URL: `/{API.CONVERSATIONS}/{conversation_key}`
-   Method: `DELETE`
-   Params: `key`, `hostname`, `template_id`, `entity_id`, `current_user_id`

This endpoint is called when user deletes a conversation.

Consider whether to delete the conversation or just soft delete.

Expected Response: STATUS 200

## Add comment

-   URL: `/{API.CONVERSATIONS}/{conversation_key}`
-   Method: `POST`

This endpoint is called when new comment is entered.

Request:

```json
{
  "template_id": "id of the template",
  "entity_id": "entity user_id",
  "current_user_id": "current user user_id",
  "hostname": "origin",
  "key": "api key",
  "type": "block", // or section, is for setting up a conversation
  "content": "text content of comment",
  "mentions": ["user_id of the user who is mentioned in the comment"]
}
```

`type` carries `block` or `section` when the comment opens a new conversation, and an **empty string when replying to an existing one**, so the endpoint has to tolerate all three. The values in `mentions` are the `userId` strings from `teamUsers`.

Expected Response: STATUS 200

## Edit, react, read or unread the comment

-   URL: `/{API.COMMENTS}/{comment_id}`
-   Method: `POST`

This endpoint is called when edit specific comment, react (add thumbs up or down) to specific comment or click to mark as read/unread conversation (will call for last comment of conversation).

Request:

```json
{
  "template_id": "id of the template",
  "entity_id": "entity user_id",
  "current_user_id": "current user user_id",
  "hostname": "origin",
  "key": "api key",
  // if edit the comment
  "content": "new text content of comment",
  "mentions": ["user_id of the user who is mentioned in the comment"],
  // if react to the comment
  "react": "thumb-up", // or thumb-down
  // if click on mark as read/unread on the parent conversation
  "action": "mark_as_read" // or mark_as_unread
}
```

Expected Response: STATUS 200

## Delete user's comment

-   URL: `/{API.COMMENTS}/{comment_id}`
-   Method: `DELETE`
-   Params: `key`, `hostname`, `template_id`, `entity_id`, `current_user_id`

This endpoint is called when user deletes a comment.

Expected Response: STATUS 200

## refreshComments

Comments do not poll, so after your application adds or deletes a comment the editor needs to be told to reload.

```js
// reload the list, and the open thread if this key matches it
TopolPlugin.refreshComments("conversation_key");

// reload the conversation list only
TopolPlugin.refreshComments("");
```

**The key is required.** The conversation list reloads on every call whatever key is passed, so an empty string is the way to refresh only the list. A key matching the currently open conversation additionally reloads that thread's detail view, which makes the affected key the right value to pass after a specific thread changed.

## toggleComments

Opens and closes the comments panel from your own UI.

```js
TopolPlugin.toggleComments();
```
