---
title: "Post a comment or reply"
method: POST
path: "/media/{mediaId}/comments"
tags: ["Comments"]
---

# Post a comment or reply

`POST /media/{mediaId}/comments`

## Request

- Base URL: `https://api.speakai.co/v1`
- URL: `https://api.speakai.co/v1/media/{mediaId}/comments`
- Auth: API key in header `x-speakai-key` and API key in header `x-access-token`

Requires the **Comments: Create** permission (on by default for admins and members). The media must belong to your company and be one you can open. Owners and members with the folder **Access all** permission can open any file; everyone else can open files in folders assigned to them or their groups, and files they own. Any other media returns 404, the same as a media id that does not exist.

#### Three kinds of comment

- **On a passage**: send `range` and `expectedTranscriptRevision` together. `expectedTranscriptRevision` is the `transcriptRevision` you read the transcript at, from `GET /media/transcript/{mediaId}`, `GET /media/insight/{mediaId}`, or the list endpoints on this page. The server builds the anchor from its own copy of the transcript. If the transcript has been saved since, the request returns 409 "This transcript changed since you opened it. Reload to keep editing." and nothing is written: read the transcript again and recompute the word range.
- **On the whole file**: leave out both `range` and `expectedTranscriptRevision`.
- **A reply**: send `parentId`, the `commentId` of a thread's first comment. A reply cannot carry `range` or `mediaLabelId`; it belongs to its thread. Replies go one level deep, so replying to a reply returns 400.

Add `mediaLabelId` to link a new thread to a labelled passage on the same media.

A reply sends an in-app notification to the person who started the thread, unless they wrote the reply. A transcript holds at most 5,000 comments on passages; whole-file comments and replies are not counted.

#### Errors

- `400` when a field fails validation, the rules above are broken, the range runs past the transcript, or the limit is reached.
- `403` without the permission.
- `404` when the media, the parent comment or the labelled passage does not exist.
- `409` when `expectedTranscriptRevision` is stale.

## Path parameters

- `mediaId` string, required

## Request body

- object
  - `body` string, required: Comment text. Trimmed, then 1 to 5,000 characters.
  - `range` object: The passage to comment on. Send it with `expectedTranscriptRevision`; leave both out for a whole-file comment. Not allowed on a reply. Inclusive range of word positions. Word positions count every word of the transcript in order, starting at 0. Within each sentence, words come from `entities` in order (an entity that holds several words, such as "New York", counts as several), or from the sentence `text` split on whitespace when the sentence has no entities. A token that is only punctuation is not counted.
    - `start` integer, required: Position of the first word. Integer, 0 or more.
    - `end` integer, required: Position of the last word, inclusive. Integer, at least `start`. A range that runs past the last word returns 400 "The selected words are outside this transcript."
  - `expectedTranscriptRevision` integer: Required when you send `range`, not allowed without it. The `transcriptRevision` you read the transcript at. Integer, 0 or more. A stale value returns 409.
  - `parentId` string: `commentId` of the thread's first comment, to post a reply. Trimmed, then 1 to 64 characters from `A-Z`, `a-z`, `0-9`, `_` and `-` (pattern `^[A-Za-z0-9_-]{1,64}$`). Anything else returns 400.
  - `mediaLabelId` string: A labelled passage on this media to link the thread to. Not allowed on a reply. Trimmed, then 1 to 64 characters from `A-Z`, `a-z`, `0-9`, `_` and `-` (pattern `^[A-Za-z0-9_-]{1,64}$`). Anything else returns 400.

## Response `200`

Success

- object
  - `status` 'success': `success` on every 2xx response.
  - `data` object: A comment.
    - `commentId` string: Id of the comment.
    - `mediaId` string: Id of the media.
    - `anchor` object, nullable: The passage the comment is on, or `null` for a comment on the whole file. Replies carry `null`.
      - `startWord` integer: Position of the first word.
      - `endWord` integer: Position of the last word, inclusive.
      - `exact` string: The quoted words, joined by single spaces.
      - `prefix` string: Up to 32 characters of transcript text before the quote.
      - `suffix` string: Up to 32 characters of transcript text after the quote.
      - `startInSec` number: Start time of the first word, in seconds.
      - `endInSec` number: End time of the last word, in seconds.
      - `speakerIds` string[]: Speakers of the quoted words.
      - `transcriptRevision` integer: The transcript revision these positions refer to.
    - `parentId` string, nullable: `commentId` of the thread's first comment for a reply, `null` for the first comment.
    - `body` string: Comment text. Empty on a deleted first comment that still has replies.
    - `mediaLabelId` string: Labelled passage the comment is linked to. Present only when one was linked.
    - `status` 'active' | 'shifted' | 'needs_review': Present only on comments with an anchor. `active`: the same words, possibly at new positions after an edit. `shifted`: most of the words survived an edit near the same time, but the text changed. `needs_review`: the words changed too much to follow; `anchor` then holds a suggested passage on the new transcript and `lastResolved` the last position a person confirmed.
    - `lastResolved` object: Present only while `status` is `needs_review`: the last position a person confirmed.
      - `startWord` integer: Position of the first word.
      - `endWord` integer: Position of the last word, inclusive.
      - `exact` string: The quoted words, joined by single spaces.
      - `prefix` string: Up to 32 characters of transcript text before the quote.
      - `suffix` string: Up to 32 characters of transcript text after the quote.
      - `startInSec` number: Start time of the first word, in seconds.
      - `endInSec` number: End time of the last word, in seconds.
      - `speakerIds` string[]: Speakers of the quoted words.
      - `transcriptRevision` integer: The transcript revision these positions refer to.
    - `isResolved` boolean: `true` once the thread is resolved. Only set on the first comment of a thread.
    - `resolvedBy` string: Id of the person who resolved the thread. Present only while it is resolved.
    - `isDeleted` boolean: `true` on a deleted first comment kept because it has replies.
    - `userId` string: Id of the author.
    - `createdAt` string, date-time: When it was posted.
    - `updatedAt` string, date-time: When it last changed.

## Other responses

- `400`: Validation, reply depth, range or limit problem
- `403`: You lack the Comments: Create permission
- `404`: The media, parent comment or labelled passage does not exist
- `409`: The transcript changed since you read it

---

[API](https://skmtc.dev/speakai/apis/speak-ai-api.md) · [All operations](https://skmtc.dev/speakai/apis/speak-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/speakai/apis/speak-ai-api/revisions/52d777e6d46e?raw)
