Skip to content

Collaboration & Comments

Comments add threaded discussions to specific sections and blocks, so a page can be reviewed where the work happens. The editor renders the threads, mentions, and reactions; your backend stores them through the conversation and comment endpoints described below.

Enabling comments

One option turns the feature on (boolean, default false):

typescript
{
  enableComments: true;
}

With comments enabled, users can comment on specific elements or sections, reply in threaded discussions, mention teammates with @mentions, and resolve threads once addressed. With it off, the commenting UI and related collaboration actions are not available.

Comments need to know who is talking. A working setup pairs enableComments with the user options: currentUser identifies the author, teamUsers populates the mention list, and templateId scopes the threads to a page:

typescript
{
  enableComments: true,
  currentUser: {
    userId: "user-123",
    name: "John Doe",
    profilePhotoUrl: "https://example.com/photos/john.jpg"
  },
  templateId: "template-456",
  teamUsers: [
    { userId: "user-123", name: "John Doe", profilePhotoUrl: "..." },
    { userId: "user-456", name: "Jane Smith", profilePhotoUrl: "..." }
  ]
}

API Implementation

Before implementing the endpoints, check how to work with API endpoints.

In the URLs below, {API.CONVERSATIONS} and {API.COMMENTS} stand for the base URLs you configured under the api option; the editor appends the conversation key or comment id to them. Every read endpoint must answer with a { "success": true, "data": ... } envelope — a response without success: true is treated as a failure and no comments render.

List Conversations

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

Params:

keyvalue
keyauthorization api key
hostnamehostname
template_idid of the template
entity_idthe authorize.userId from your options (not currentUser.userId)
current_user_iduser id of current user (currentUser.userId)
sort_by"date" or "unread"
show_resolved"true" or "false"

This endpoint is called when the editor is initially loaded, when sorting or display preference changes, automatically after any comment mutation (add, edit, react, delete, resolve), and whenever LPE.refreshComments("conversation_key") is called on the editor instance.

Response:

json
{
  "success": true,
  "data": [
    {
      "key": "conversation_key",
      "type": "block",
      "resolved": false,
      "delete": false,
      "involved_users": [
        {
          "user_id": "user_id",
          "profile_photo_url": "link to the user's profile picture",
          "name": "user name"
        }
      ],
      "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"
            }
          ]
        }
      ]
    }
  ]
}

Notes on the response fields:

  • type is required and must be "block" or "section".
  • resolved accepts true/false or 1/0 (handy for MySQL-backed integrations).
  • created_by and read_by are required on every comment; if either is missing, the response fails to parse and no comments are shown.
  • user_id values in responses may be strings or numbers.

Detail Conversation

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

This endpoint is called in two situations:

  • When a user opens the conversation
  • When the conversation is currently open and LPE.refreshComments("conversation_key") is called on the editor instance

Unread messages should be marked as read.

Response:

json
{
  "success": true,
  "data": {
    "key": "conversation_key",
    "type": "block",
    "resolved": false,
    "delete": false,
    "involved_users": [
      {
        "user_id": "user_id",
        "profile_photo_url": "link to the user's profile picture",
        "name": "user name"
      }
    ],
    "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"
          }
        ]
      }
    ]
  }
}

Resolve or Unresolve Conversation

  • URL: /{API.CONVERSATIONS}/{conversation_key}
  • Method: PATCH

This endpoint is called when the resolve button is clicked.

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
}

Response:

HTTP 200 OK

Delete Conversation

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

This endpoint is called when a user deletes a conversation.

Consider whether to delete the conversation or just soft delete.

Response:

HTTP 200 OK

Add Comment

  • URL: /{API.CONVERSATIONS}/{conversation_key}
  • Method: POST

This endpoint is called both when starting a new conversation on an element (the editor generates a new conversation_key) and when replying to an existing one; treat it as an upsert on conversation_key.

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",
  "content": "text content of comment",
  "mentions": ["user_id of the user who is mentioned in the comment"]
}

Response:

HTTP 200 OK

Edit, React, Read or Unread the Comment

  • URL: /{API.COMMENTS}/{comment_id}
  • Method: POST

This endpoint is called when editing a specific comment, reacting to a specific comment, or marking a conversation as read/unread (called for the last comment of the conversation). Each action sends its own request shape; the three variants are mutually exclusive and always include the common fields template_id, entity_id, current_user_id, hostname, and key.

Edit comment:

json
{
  "content": "new text content of comment",
  "mentions": ["user_id of the user who is mentioned in the comment"]
}

React to comment:

json
{
  "react": "thumb-up"
}

Mark conversation as read or unread:

json
{
  "action": "mark_as_read"
}

action is either "mark_as_read" or "mark_as_unread".

Response:

HTTP 200 OK

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 a user deletes a comment.

Response:

HTTP 200 OK

refreshComments

Changes made inside the editor refresh automatically. Call refreshComments on the editor instance when comments change outside the editor, typically when your backend receives a new comment from another user and pushes a realtime notification to your app:

js
const LPE = LandingPageEditor({ config: TOPOL_OPTIONS });
LPE.render("#landing-page-editor");

// when your backend signals that a conversation changed:
LPE.refreshComments("conversation_key");

This reloads the full conversation list and refreshes the currently opened conversation detail.

Troubleshooting

Comments not appearing: confirm that enableComments is true and that the CONVERSATIONS endpoint returns { "success": true, "data": [...] }. Comments render even without currentUser, but every request then sends an empty current_user_id and read/unread tracking does not work, so always set currentUser.

Mentions not working: the teamUsers array has to include every mentionable user, and each entry requires all three of userId (a string), name, and profilePhotoUrl. A missing value prevents the mention system from identifying the user.