Appearance
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:
| key | value |
|---|---|
| key | authorization api key |
| hostname | hostname |
| template_id | id of the template |
| entity_id | the authorize.userId from your options (not currentUser.userId) |
| current_user_id | user 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:
typeis required and must be"block"or"section".resolvedacceptstrue/falseor1/0(handy for MySQL-backed integrations).created_byandread_byare required on every comment; if either is missing, the response fails to parse and no comments are shown.user_idvalues 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.
