Leave a comment on a site
create_comment
Leaves a new review comment pinned to a spot on the preview of a site.
x, y and zoom are required and place the pin. x and y are fractions of the preview between 0 and 1, and zoom is the preview zoom the pin was placed at. Pass anchor as well when the comment is about a specific element, so the pin follows that element rather than a fixed spot.
The anchor needs both Selector and PagePath, and PagePath must start with /. An anchor missing either, or with any other PagePath, is dropped silently and the comment is created unanchored, with the call still reporting success.
The comment is attributed to you. Every workspace member sees it, as does anyone holding the site's shared link while viewers can comment. The text is at most 1,000 characters, and text that is empty or longer than that is refused.
Mentioning a teammate does not email them from here. A mention only sends mail when it is written in the product, so anyone you name sees it when they next open the thread. For the mention to resolve to a person, it takes the composer token form @Name{userId} with their real user id from list_workspace_members, and a plain @name is just text. The tool tells the client to mention someone only when you asked it to. See Comments.
Request
Call it with a POST to https://api.modulify.ai/v1/create_comment, sending the inputs below as a JSON object. The token needs the comments:write scope.
It makes changes, so send an Idempotency-Key header whenever you might retry it. A retry with the same key gets the first answer back instead of running again.
curl -X POST https://api.modulify.ai/v1/create_comment \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"projectId":"PROJECT_ID","content":"CONTENT","x":1,"y":1,"zoom":1}'Over MCP, the same method is the create_comment tool.
Inputs
| Input | Type | Required | Description |
|---|---|---|---|
projectId |
string | Yes | The site id. |
content |
string | Yes | The comment text, at most 1,000 characters. An empty string is refused. |
x |
number | Yes | Where the pin sits across the preview, 0 at the left edge and 1 at the right. |
y |
number | Yes | Where the pin sits down the preview, 0 at the top and 1 at the bottom. |
zoom |
number | Yes | The preview zoom the pin was placed at, normally 1. |
anchor |
object | No | Ties the pin to one element so it survives layout changes. Its fields are PagePath (the page the element is on, starting with /, such as /pricing), Selector (a CSS selector for the element), OffsetX and OffsetY (where inside the element the pin sits, 0 to 1), and DocX and DocY (a fallback position on the page, 0 to 1, used when the element is gone). Leave it out for a comment about the page in general. |
Response
Every call answers with the JSON envelope of success, message, data, code and version. data holds the result described above, and on a method that returns a total, count carries it. The response headers carry the call's X-Request-Id and what is left of your per-minute budget in X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Errors explains every status code a call can answer with.