---
title: "Reply to a majlis topic"
method: POST
path: "/projects/{project_id}/discussions/{id}/reply"
tags: ["Discussions"]
---

# Reply to a majlis topic

`POST /projects/{project_id}/discussions/{id}/reply`

## Request

- Base URL: `https://app.fareeqy.com/api/v1`
- URL: `https://app.fareeqy.com/api/v1/projects/{project_id}/discussions/{id}/reply`
- Auth: HTTP bearer

Adds a reply. The reply is serialized with the same shape as a Comment.
Requires operation `discussions:write`.

## Request body

- object
  - `content` string, required: Rich-text reply body (plain text in).

## Response `201`

The created reply.

- CommentEnvelope
  - `data` Comment: A comment (on a task list or task) or a majlis reply.
    - `id` integer: Numeric id (comments have no slug).
    - `url` string, nullable: The page the comment is read on, which is its parent's page. Every comment in one thread carries the same address.
    - `content` string, nullable: Plain-text rendering of the rich-text content.
    - `author` string, nullable
    - `created_at` string, date-time
    - `updated_at` string, date-time: Equal to created_at until the comment is edited.
    - `attachments` CommentAttachmentRef[]: Files embedded in the comment body. The plain-text `content` drops attachments entirely, so without these a comment that refers to a screenshot reads as a sentence pointing at nothing. Empty when there are none. Read a text attachment through the comment attachment endpoint, by its `id`.
      - `id` integer: The attachment's numeric id, the handle the comment attachment endpoint takes.
      - `name` string: The file name. Not unique within one comment.
      - `content_type` string, nullable: Null when the stored file is missing.
      - `byte_size` integer, nullable: Null when the stored file is missing.

## Other responses

- `401`: Missing or invalid API key.
- `403`: Two different refusals share this status, and a client must tell them apart by `error.code`. `forbidden` means the key's scope or allowlist does not permit this operation, or Pundit denied the action. `plan_upgrade_required` means the company's plan carries no API access at all, so no key on it can ever succeed and there is nothing to retry.
- `404`: Resource not found or not accessible — also returned for EVERY endpoint when the company's `rest_api` feature flag is disabled (the surface is hidden). Lookups drill through the URL hierarchy, so another company's record is a 404 and never a leak. A path that matches no route at all answers 404 with the distinct code `unknown_endpoint` and echoes the path back, so a mistyped or half-built URL is told apart from a record that is missing or out of reach.
- `409`: A uniqueness/record conflict; retry.
- `422`: A caller-fixable bad request (validation error, bad date, bad enum).
- `429`: Either the company's daily API allowance is spent (`rate_limit_exceeded`), or the per-key / per-IP burst throttle of 300 requests per minute fired. Both come back after a wait, so `Retry-After` is honest here. **The two bodies are not the same shape.** The daily-quota refusal uses the standard error envelope. The burst throttle is served by Rack::Attack ahead of the application, so its body is a flat `{"error": "<string>"}` with no `code`. A client that reads `error.code` has to tolerate `error` being a plain string.

---

[API](https://skmtc.dev/fareeqy/apis/fareeqy-rest-api.md) · [All operations](https://skmtc.dev/fareeqy/apis/fareeqy-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/fareeqy/apis/fareeqy-rest-api/revisions/dc964e05e3d2?raw)
