> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sublay.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Comment

> Create a new comment or reply on an entity

Creates a new comment on an entity, or a reply to an existing comment. The comment is attributed to the authenticated user. Notifications are sent to the entity owner (for a top-level comment), the parent comment's author (for a reply), and mentioned users.

**Authentication required.**

## Body Parameters

<ParamField body="entityId" type="string" required>
  The UUID of the entity this comment belongs to.
</ParamField>

<ParamField body="content" type="string">
  The text content of the comment. At least one of `content`, `gif`, or `attachments` must be provided.
</ParamField>

<ParamField body="gif" type="object">
  A GIF to attach to the comment. At least one of `content`, `gif`, or `attachments` must be provided.

  ```json theme={null}
  {
    "id": "gif-id",
    "altText": "laughing cat",
    "gifUrl": "https://media.giphy.com/...",
    "gifPreviewUrl": "https://media.giphy.com/...",
    "url": "https://giphy.com/...",
    "aspectRatio": "1.5"
  }
  ```

  `aspectRatio` is width ÷ height as a decimal string. Colon notation like `"16:9"` is rejected by clients that compute layout from it.
</ParamField>

<ParamField body="attachments" type="object[]">
  An array of generic attachment objects. At least one of `content`, `gif`, or `attachments` must be provided.
</ParamField>

<ParamField body="parentId" type="string">
  The UUID of the parent comment. When provided, this comment is treated as a reply to that comment. The parent comment must be on the entity given in `entityId` — otherwise the request returns `404 comment/entity-not-found`. The parent comment's author receives a reply notification.

  The parent comment can't be one its author deleted (`403 comment/parent-deleted`) or one a moderator removed (`403 comment/parent-removed`) — see [Deleted and removed parents and quotes](#deleted-and-removed-parents-and-quotes).
</ParamField>

<ParamField body="referencedCommentId" type="string">
  The UUID of a comment being quoted or referenced. Used for quote-replies. The quoted comment may be on any entity, but it must be visible to the commenting user — otherwise the request returns `404 comment/entity-not-found`.

  The quoted comment can't be one its author deleted (`403 comment/referenced-deleted`) or one a moderator removed (`403 comment/referenced-removed`) — see [Deleted and removed parents and quotes](#deleted-and-removed-parents-and-quotes).
</ParamField>

<ParamField body="foreignId" type="string">
  Your application's identifier for this comment. Must be unique across the project's comments — a `foreignId` already in use returns `409 comment/foreign-id-taken`. A `foreignId` stays reserved after its comment is deleted: the deleted comment remains in its thread as a placeholder and is still found by that `foreignId`.
</ParamField>

<ParamField body="mentions" type="object[]">
  Users or spaces mentioned in the comment. Each mentioned user receives one in-app notification, however many times they are mentioned, except the person the comment already notifies: the entity owner for a top-level comment, or the parent comment's author for a reply. A space mention sends no notification, and a commenter mentioning themselves is not notified.

  * `{ type: "user", id: string, username: string, foreignId?: string }`
  * `{ type: "space", id: string, slug: string }`
</ParamField>

<ParamField body="metadata" type="object">
  A free-form JSON object for any custom data your application needs alongside the comment. Up to 10 KB (10,240 bytes) as JSON — a larger object returns `400 comment/invalid-body`.
</ParamField>

<ParamField body="userId" type="string">
  Attribute the comment to a specific user. **Service or master key only**, and required with one — a key has no user of its own, so leaving it out returns `400 comment/missing-user-id`. Useful for backfilling comments from external systems. A user token is always its own user: naming a different user returns `403 comment/unauthorized`.

  A `userId` that doesn't match a user returns `404 comment/user-not-found`.
</ParamField>

<ParamField body="createdAt" type="string">
  Datetime to backfill a creation timestamp. **Service or master key only** — with a user token the value is ignored and the comment gets the current time.

  Must be a UTC ISO 8601 datetime ending in `Z`, such as `2026-01-15T09:30:00Z`. A value with an offset (`+02:00`) or a date without a time returns `400 comment/invalid-body` (`createdAt: Invalid ISO datetime`), whichever credential sends it.
</ParamField>

<ParamField body="updatedAt" type="string">
  Datetime for `updatedAt`, in the same format as `createdAt` — UTC ISO 8601 ending in `Z`; anything else returns `400 comment/invalid-body`. **Service or master key only** — with a user token the value is ignored.

  Requires `createdAt`, and must be the same as or later than it; otherwise the request returns `400 comment/invalid-timestamp`. When only `createdAt` is sent, `updatedAt` is set to the same time.
</ParamField>

## Blocks

When the `moderation` bundle is installed, three people are checked: the entity's author, the parent comment's author (for replies), and the quoted comment's author (with `referencedCommentId`).

* **If the commenting user blocked any of them**, the comment is allowed — the block is their own choice. The blocked person isn't notified.
* **If any of them blocked the commenting user** (or both blocked each other), the request is refused with `404 comment/entity-not-found` — the same response as a missing entity, so it never reveals a block.

A comment marked [`isUnavailable`](/v8/data-models/comment#blocked-authors) can't be replied to. Commenting can also be refused when the entity's author has blocked the commenting user, which no comment flag shows.

## Deleted and removed parents and quotes

A comment its author deleted, or a moderator removed, can't be replied to or quoted:

| The comment was | As `parentId` | As `referencedCommentId` |
| - | - | - |
| Deleted by its author | `403 comment/parent-deleted` | `403 comment/referenced-deleted` |
| Removed by a moderator | `403 comment/parent-removed` | `403 comment/referenced-removed` |

This applies to everyone, including the author of a removed comment, who still sees it in full. A comment that is both deleted and removed returns the deleted code. When the parent and the quoted comment are both refused, the response is the parent's.

For replies, the rule covers the direct parent only. Replies that already sit under a deleted or removed comment can still be replied to, and quoted.

These responses are only returned for a comment the commenting user can see. A deleted or removed comment that a block hides from them (see [Blocks](#blocks)) returns `404 comment/entity-not-found`, like any other.

## Response

Returns `201 Created` with the new [Comment](/v8/data-models/comment) object, populated with the author's user profile.

## Error Responses

<AccordionGroup>
  <Accordion title="Entity Not Found — 404">
    Returned when the entity doesn't exist, when `parentId` doesn't exist or belongs to another entity, when the quoted comment isn't visible to the commenting user, or when a block refuses the comment (see [Blocks](#blocks)).

    ```json theme={null}
    { "error": "Entity not found", "code": "comment/entity-not-found" }
    ```
  </Accordion>

  <Accordion title="Missing User — 400">
    Returned when a service or master key sends no `userId`.

    ```json theme={null}
    { "error": "Missing user ID", "code": "comment/missing-user-id" }
    ```
  </Accordion>

  <Accordion title="Missing token — 401">
    No `Authorization` header was sent. The body is `text/plain` (`Unauthorized`), not JSON. An expired or invalid token returns `403` instead — see [Authentication](/v8/api-reference/authentication).
  </Accordion>

  <Accordion title="Invalid Body — 400">
    Returned when the body fails validation. `error` names the field and the problem:

    * none of `content`, `gif` or `attachments` is provided — `content` that is empty or only whitespace, and an empty `attachments` array, don't count;
    * `entityId`, `parentId`, `referencedCommentId` or `userId` isn't a UUID (a missing `entityId` included);
    * `gif` is missing one of its six fields, or one has the wrong type;
    * a `mentions` item doesn't match one of the two shapes;
    * `foreignId` is an empty string or longer than 255 characters;
    * `metadata` isn't an object, or is larger than 10 KB;
    * `createdAt` or `updatedAt` isn't a UTC ISO 8601 datetime ending in `Z`.

    ```json theme={null}
    { "error": "At least one of content, gif, or attachments must be provided", "code": "comment/invalid-body" }
    ```

    ```json theme={null}
    { "error": "entityId: Invalid UUID format", "code": "comment/invalid-body" }
    ```
  </Accordion>

  <Accordion title="Acting as another user — 403">
    Returned when a request authenticated with a user token sends a `userId` other than that user's own ID.

    ```json theme={null}
    { "error": "Unauthorized", "code": "comment/unauthorized" }
    ```
  </Accordion>

  <Accordion title="User Not Found — 404">
    Returned when a service or master key names a `userId` that doesn't exist.

    ```json theme={null}
    { "error": "User not found", "code": "comment/user-not-found" }
    ```
  </Accordion>

  <Accordion title="Invalid Timestamp — 400">
    Returned to a service or master key when `updatedAt` is sent without `createdAt`, or is earlier than it.

    ```json theme={null}
    { "error": "Cannot set updatedAt without also setting createdAt.", "code": "comment/invalid-timestamp" }
    ```

    ```json theme={null}
    { "error": "updatedAt must be the same or after createdAt.", "code": "comment/invalid-timestamp" }
    ```
  </Accordion>

  <Accordion title="Rejected by Webhook — 400">
    Returned when your project's `comment.created` [validation webhook](/v8/webhooks#validation-events-blocking) doesn't approve the comment. Nothing is created. `error` is the `message` your webhook responded with, when it sent one.

    ```json theme={null}
    { "error": "Validation rejected", "code": "comment/webhook-validation-failed" }
    ```
  </Accordion>

  <Accordion title="Parent Deleted — 403">
    Returned when `parentId` is a comment its author deleted (see [Deleted and removed parents and quotes](#deleted-and-removed-parents-and-quotes)).

    ```json theme={null}
    { "error": "This comment was deleted and can't be replied to.", "code": "comment/parent-deleted" }
    ```
  </Accordion>

  <Accordion title="Parent Removed — 403">
    Returned when `parentId` is a comment a moderator removed — for every requester, including that comment's author (see [Deleted and removed parents and quotes](#deleted-and-removed-parents-and-quotes)).

    ```json theme={null}
    { "error": "This comment was removed and can't be replied to.", "code": "comment/parent-removed" }
    ```
  </Accordion>

  <Accordion title="Quoted Comment Deleted — 403">
    Returned when `referencedCommentId` is a comment its author deleted (see [Deleted and removed parents and quotes](#deleted-and-removed-parents-and-quotes)).

    ```json theme={null}
    { "error": "This comment was deleted and can't be quoted.", "code": "comment/referenced-deleted" }
    ```
  </Accordion>

  <Accordion title="Quoted Comment Removed — 403">
    Returned when `referencedCommentId` is a comment a moderator removed — for every requester, including that comment's author (see [Deleted and removed parents and quotes](#deleted-and-removed-parents-and-quotes)).

    ```json theme={null}
    { "error": "This comment was removed and can't be quoted.", "code": "comment/referenced-removed" }
    ```
  </Accordion>

  <Accordion title="Foreign ID Taken — 409">
    Returned when another comment already uses the supplied `foreignId`.

    ```json theme={null}
    { "error": "A comment with this foreignId already exists.", "code": "comment/foreign-id-taken" }
    ```
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.