---
title: "Comments | Fareeqy API"
description: "Comments on a task list or task, read and written, and the text of files attached to them. The Comments endpoints in the Fareeqy REST API: parameters"
url: https://fareeqy.com/api/v1/docs/comments/en
language: en
---

# Comments

Comments on a task list or task, read and written, and the text of files attached to them.

Base URL `https://app.fareeqy.com/api/v1`

Contents

On this page

-   The resource
-   List a task list's comments
-   Comment on a task list
-   Read a file attached to a task list comment
-   List a task's comments
-   Comment on a task
-   Read a file attached to a task comment
-   List a task list's comments, by ref
-   Comment on a task list, by ref
-   Read a file attached to a task list comment, by ref
-   List a task's comments, by ref
-   Comment on a task, by ref
-   Read a file attached to a task comment, by ref

Endpoints

-   [Account](https://fareeqy.com/api/v1/docs/account/en)
-   [Events](https://fareeqy.com/api/v1/docs/events/en)
-   [Projects](https://fareeqy.com/api/v1/docs/projects/en)
-   [Members](https://fareeqy.com/api/v1/docs/members/en)
-   [Task lists](https://fareeqy.com/api/v1/docs/task-lists/en)
-   [Tasks](https://fareeqy.com/api/v1/docs/tasks/en)
-   [Comments](https://fareeqy.com/api/v1/docs/comments/en)
-   [Discussions](https://fareeqy.com/api/v1/docs/discussions/en)
-   [Files](https://fareeqy.com/api/v1/docs/files/en)
-   [Folders](https://fareeqy.com/api/v1/docs/folders/en)

More

-   [Changelog](https://fareeqy.com/api/v1/docs/changelog/en)

On this page

-   The resource
-   List a task list's comments
-   Comment on a task list
-   Read a file attached to a task list comment
-   List a task's comments
-   Comment on a task
-   Read a file attached to a task comment
-   List a task list's comments, by ref
-   Comment on a task list, by ref
-   Read a file attached to a task list comment, by ref
-   List a task's comments, by ref
-   Comment on a task, by ref
-   Read a file attached to a task comment, by ref

Endpoints

-   [Account](https://fareeqy.com/api/v1/docs/account/en)
-   [Events](https://fareeqy.com/api/v1/docs/events/en)
-   [Projects](https://fareeqy.com/api/v1/docs/projects/en)
-   [Members](https://fareeqy.com/api/v1/docs/members/en)
-   [Task lists](https://fareeqy.com/api/v1/docs/task-lists/en)
-   [Tasks](https://fareeqy.com/api/v1/docs/tasks/en)
-   [Comments](https://fareeqy.com/api/v1/docs/comments/en)
-   [Discussions](https://fareeqy.com/api/v1/docs/discussions/en)
-   [Files](https://fareeqy.com/api/v1/docs/files/en)
-   [Folders](https://fareeqy.com/api/v1/docs/folders/en)

More

-   [Changelog](https://fareeqy.com/api/v1/docs/changelog/en)

## The resource

The fields returned inside `data` by the endpoints in this group.

`id` integer

Numeric id (comments have no slug).

`url` string | null

The page the comment is read on, which is its parent's page. Every comment in one thread carries the same address.

`content` string | null

Plain-text rendering of the rich-text content.

`author` string | null

`created_at` string (date-time)

`updated_at` string (date-time)

Equal to created\_at until the comment is edited.

`attachments` object\[\]

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`.

Comment Copy

 `{   "id": 512,   "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",   "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",   "author": "عبدالله المطيري",   "created_at": "2026-07-15T09:48:03.000+03:00",   "updated_at": "2026-07-15T09:48:03.000+03:00",   "attachments": [     {       "id": 9031,       "name": "error-state.png",       "content_type": "image/png",       "byte_size": 184320     }   ] }`

### List a task list's comments

GET /projects/ {project\_id} /lists/ {task\_list\_id} /comments Requires `comments:read`

Oldest first.

#### Path parameters

`project_id` string required

The project REF, or its slug.

`task_list_id` string required

The task-list REF, or its slug.

#### Query parameters

`limit` integer

Page size (default 50, max 100, clamped).

Default: `50`

`offset` integer

Number of records to skip (default 0).

Default: `0`

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/projects/prj-8f3kd/lists/lst-4m2qp/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": [     {       "id": 512,       "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",       "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",       "author": "عبدالله المطيري",       "created_at": "2026-07-15T09:48:03.000+03:00",       "updated_at": "2026-07-15T09:48:03.000+03:00",       "attachments": [         {           "id": 9031,           "name": "error-state.png",           "content_type": "image/png",           "byte_size": 184320         }       ]     }   ],   "meta": {     "total": 4,     "limit": 50,     "offset": 0,     "count": 4   } }`

Refusals: 401 403 404 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Comment on a task list

POST /projects/ {project\_id} /lists/ {task\_list\_id} /comments Requires `comments:write`

#### Path parameters

`project_id` string required

The project REF, or its slug.

`task_list_id` string required

The task-list REF, or its slug.

#### Request body

`content` string required

Rich-text comment body (plain text in).

#### Request

cURL Copy

 `curl -X POST "https://app.fareeqy.com/api/v1/projects/prj-8f3kd/lists/lst-4m2qp/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY" \   -H "Content-Type: application/json" \   -d '{"content":"خلصنا الهيدر، باقي قسم المزايا."}'`

#### Response

201 Copy

 `{   "data": {     "id": 512,     "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",     "author": "عبدالله المطيري",     "created_at": "2026-07-15T09:48:03.000+03:00",     "updated_at": "2026-07-15T09:48:03.000+03:00",     "attachments": [       {         "id": 9031,         "name": "error-state.png",         "content_type": "image/png",         "byte_size": 184320       }     ]   } }`

Refusals: 401 403 404 409 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

409 `default`

A uniqueness/record conflict; retry.

default Copy

 `{   "error": {     "code": "conflict",     "message": "تعذّر إتمام العملية بسبب تعارض. أعد المحاولة."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Read a file attached to a task list comment

GET /projects/ {project\_id} /lists/ {task\_list\_id} /comments/ {comment\_id} /attachments/ {id} Requires `comments:read`

Returns the TEXT of one file attached inside a comment. `id` is the attachment's numeric id, read from the comment's `attachments`. Only text files are readable here (extensions such as `md`, `txt`, `csv`, `tsv`, `json`, `yml`, `xml`, `html`, `log`, `sql` and common source files), up to 256 KB. An image, a PDF, an oversize file, an empty one, or one whose stored file is missing is refused with a 422 that names it; open it from the comment's page instead. An attachment id that belongs to another comment is a 404.

#### Path parameters

`project_id` string required

The project REF, or its slug.

`task_list_id` string required

The task-list REF, or its slug.

`comment_id` integer required

The comment's NUMERIC id (comments have no ref and no slug).

`id` integer required

The attachment's NUMERIC id, from the comment's `attachments`.

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/projects/prj-8f3kd/lists/lst-4m2qp/comments/4821/attachments/1937" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": {     "id": 9044,     "name": "أخطاء-الاستيراد.csv",     "content_type": "text/csv",     "byte_size": 412,     "comment_id": 518,     "comment_url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "row,error\n14,missing due date\n22,unknown assignee\n"   } }`

Refusals: 401 403 404 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### List a task's comments

GET /projects/ {project\_id} /lists/ {task\_list\_id} /tasks/ {task\_id} /comments Requires `comments:read`

Oldest first.

#### Path parameters

`project_id` string required

The project REF, or its slug.

`task_list_id` string required

The task-list REF, or its slug.

`task_id` string required

The task REF, or its slug.

#### Query parameters

`limit` integer

Page size (default 50, max 100, clamped).

Default: `50`

`offset` integer

Number of records to skip (default 0).

Default: `0`

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/projects/prj-8f3kd/lists/lst-4m2qp/tasks/tsk-9wb7t/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": [     {       "id": 512,       "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",       "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",       "author": "عبدالله المطيري",       "created_at": "2026-07-15T09:48:03.000+03:00",       "updated_at": "2026-07-15T09:48:03.000+03:00",       "attachments": []     },     {       "id": 518,       "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",       "content": "راجعت الهيدر على ثلاثة مقاسات، وعندي ثلاث ملاحظات. رفعت لقطة للحالات الثلاث.",       "author": "نورة الحربي",       "created_at": "2026-07-15T11:20:41.000+03:00",       "updated_at": "2026-07-15T11:34:09.000+03:00",       "attachments": [         {           "id": 9037,           "name": "الحالات-الثلاث.png",           "content_type": "image/png",           "byte_size": 612352         }       ]     },     {       "id": 524,       "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",       "content": "خذوا ملاحظات نورة كلها، وابدأوا بالثانية لأنها تكسر الاستخدام لا الشكل. نراجع الاثنين القادم.",       "author": "سارة العتيبي",       "created_at": "2026-07-15T14:05:12.000+03:00",       "updated_at": "2026-07-15T14:05:12.000+03:00",       "attachments": []     }   ],   "meta": {     "total": 3,     "limit": 50,     "offset": 0,     "count": 3   } }`

Refusals: 401 403 404 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Comment on a task

POST /projects/ {project\_id} /lists/ {task\_list\_id} /tasks/ {task\_id} /comments Requires `comments:write`

#### Path parameters

`project_id` string required

The project REF, or its slug.

`task_list_id` string required

The task-list REF, or its slug.

`task_id` string required

The task REF, or its slug.

#### Request body

`content` string required

Rich-text comment body (plain text in).

#### Request

cURL Copy

 `curl -X POST "https://app.fareeqy.com/api/v1/projects/prj-8f3kd/lists/lst-4m2qp/tasks/tsk-9wb7t/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY" \   -H "Content-Type: application/json" \   -d '{"content":"خلصنا الهيدر، باقي قسم المزايا."}'`

#### Response

201 · A quick reply Copy

 `{   "data": {     "id": 512,     "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",     "author": "عبدالله المطيري",     "created_at": "2026-07-15T09:48:03.000+03:00",     "updated_at": "2026-07-15T09:48:03.000+03:00",     "attachments": []   } }`

201 · A long review comment from another member Copy

 `{   "data": {     "id": 518,     "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "راجعت الهيدر على ثلاثة مقاسات، وعندي ثلاث ملاحظات.\n\nالأولى أن الشعار يقفز سطراً كاملاً تحت 380 بكسل لأن العنصر المجاور له لا ينكمش، فيظهر فراغ أبيض في أعلى الصفحة على أجهزة قديمة ما زالت تمثل نسبة معتبرة من زوارنا.\n\nالثانية أن القائمة المنسدلة تفتح لليسار في الوضع العربي، وهذا يخالف اتجاه القراءة ويجعلها تخرج خارج الشاشة.\n\nالثالثة بسيطة: وزن الخط في الروابط 500 بينما بقية الموقع على 400، فيبدو الهيدر أثقل مما حوله. رفعت لقطة للحالات الثلاث في مجلد «التصاميم».",     "author": "نورة الحربي",     "created_at": "2026-07-15T11:20:41.000+03:00",     "updated_at": "2026-07-15T11:20:41.000+03:00",     "attachments": [       {         "id": 9037,         "name": "الحالات-الثلاث.png",         "content_type": "image/png",         "byte_size": 612352       }     ]   } }`

201 · A closing note from the project owner Copy

 `{   "data": {     "id": 524,     "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "خذوا ملاحظات نورة كلها، وابدأوا بالثانية لأنها تكسر الاستخدام لا الشكل. نراجع الاثنين القادم.",     "author": "سارة العتيبي",     "created_at": "2026-07-15T14:05:12.000+03:00",     "updated_at": "2026-07-15T14:05:12.000+03:00",     "attachments": []   } }`

Refusals: 401 403 404 409 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

409 `default`

A uniqueness/record conflict; retry.

default Copy

 `{   "error": {     "code": "conflict",     "message": "تعذّر إتمام العملية بسبب تعارض. أعد المحاولة."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Read a file attached to a task comment

GET /projects/ {project\_id} /lists/ {task\_list\_id} /tasks/ {task\_id} /comments/ {comment\_id} /attachments/ {id} Requires `comments:read`

Returns the TEXT of one file attached inside a comment. `id` is the attachment's numeric id, read from the comment's `attachments`. Only text files are readable here (extensions such as `md`, `txt`, `csv`, `tsv`, `json`, `yml`, `xml`, `html`, `log`, `sql` and common source files), up to 256 KB. An image, a PDF, an oversize file, an empty one, or one whose stored file is missing is refused with a 422 that names it; open it from the comment's page instead. An attachment id that belongs to another comment is a 404.

#### Path parameters

`project_id` string required

The project REF, or its slug.

`task_list_id` string required

The task-list REF, or its slug.

`task_id` string required

The task REF, or its slug.

`comment_id` integer required

The comment's NUMERIC id (comments have no ref and no slug).

`id` integer required

The attachment's NUMERIC id, from the comment's `attachments`.

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/projects/prj-8f3kd/lists/lst-4m2qp/tasks/tsk-9wb7t/comments/4821/attachments/1937" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": {     "id": 9044,     "name": "أخطاء-الاستيراد.csv",     "content_type": "text/csv",     "byte_size": 412,     "comment_id": 518,     "comment_url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "row,error\n14,missing due date\n22,unknown assignee\n"   } }`

Refusals: 401 403 404 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### List a task list's comments, by ref

GET /lists/ {task\_list\_id} /comments Requires `comments:read`

Oldest first.

#### Path parameters

`task_list_id` string required

The task list REF (lst-4m2qp). A slug is not accepted here.

#### Query parameters

`limit` integer

Page size (default 50, max 100, clamped).

Default: `50`

`offset` integer

Number of records to skip (default 0).

Default: `0`

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/lists/lst-4m2qp/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": [     {       "id": 512,       "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",       "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",       "author": "عبدالله المطيري",       "created_at": "2026-07-15T09:48:03.000+03:00",       "updated_at": "2026-07-15T09:48:03.000+03:00",       "attachments": [         {           "id": 9031,           "name": "error-state.png",           "content_type": "image/png",           "byte_size": 184320         }       ]     }   ],   "meta": {     "total": 4,     "limit": 50,     "offset": 0,     "count": 4   } }`

Refusals: 401 403 404 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Comment on a task list, by ref

POST /lists/ {task\_list\_id} /comments Requires `comments:write`

#### Path parameters

`task_list_id` string required

The task list REF (lst-4m2qp). A slug is not accepted here.

#### Request body

`content` string required

Rich-text comment body (plain text in).

#### Request

cURL Copy

 `curl -X POST "https://app.fareeqy.com/api/v1/lists/lst-4m2qp/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY" \   -H "Content-Type: application/json" \   -d '{"content":"خلصنا الهيدر، باقي قسم المزايا."}'`

#### Response

201 Copy

 `{   "data": {     "id": 512,     "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",     "author": "عبدالله المطيري",     "created_at": "2026-07-15T09:48:03.000+03:00",     "updated_at": "2026-07-15T09:48:03.000+03:00",     "attachments": [       {         "id": 9031,         "name": "error-state.png",         "content_type": "image/png",         "byte_size": 184320       }     ]   } }`

Refusals: 401 403 404 409 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

409 `default`

A uniqueness/record conflict; retry.

default Copy

 `{   "error": {     "code": "conflict",     "message": "تعذّر إتمام العملية بسبب تعارض. أعد المحاولة."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Read a file attached to a task list comment, by ref

GET /lists/ {task\_list\_id} /comments/ {comment\_id} /attachments/ {id} Requires `comments:read`

Returns the TEXT of one file attached inside a comment. `id` is the attachment's numeric id, read from the comment's `attachments`. Only text files are readable here (extensions such as `md`, `txt`, `csv`, `tsv`, `json`, `yml`, `xml`, `html`, `log`, `sql` and common source files), up to 256 KB. An image, a PDF, an oversize file, an empty one, or one whose stored file is missing is refused with a 422 that names it; open it from the comment's page instead. An attachment id that belongs to another comment is a 404.

#### Path parameters

`task_list_id` string required

The task list REF (lst-4m2qp). A slug is not accepted here.

`comment_id` integer required

The comment's NUMERIC id (comments have no ref and no slug).

`id` integer required

The attachment's NUMERIC id, from the comment's `attachments`.

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/lists/lst-4m2qp/comments/4821/attachments/1937" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": {     "id": 9044,     "name": "أخطاء-الاستيراد.csv",     "content_type": "text/csv",     "byte_size": 412,     "comment_id": 518,     "comment_url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "row,error\n14,missing due date\n22,unknown assignee\n"   } }`

Refusals: 401 403 404 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### List a task's comments, by ref

GET /tasks/ {task\_id} /comments Requires `comments:read`

Oldest first.

#### Path parameters

`task_id` string required

The task REF (tsk-9wb7t). A slug is not accepted here.

#### Query parameters

`limit` integer

Page size (default 50, max 100, clamped).

Default: `50`

`offset` integer

Number of records to skip (default 0).

Default: `0`

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/tasks/tsk-9wb7t/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": [     {       "id": 512,       "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",       "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",       "author": "عبدالله المطيري",       "created_at": "2026-07-15T09:48:03.000+03:00",       "updated_at": "2026-07-15T09:48:03.000+03:00",       "attachments": [         {           "id": 9031,           "name": "error-state.png",           "content_type": "image/png",           "byte_size": 184320         }       ]     }   ],   "meta": {     "total": 4,     "limit": 50,     "offset": 0,     "count": 4   } }`

Refusals: 401 403 404 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Comment on a task, by ref

POST /tasks/ {task\_id} /comments Requires `comments:write`

#### Path parameters

`task_id` string required

The task REF (tsk-9wb7t). A slug is not accepted here.

#### Request body

`content` string required

Rich-text comment body (plain text in).

#### Request

cURL Copy

 `curl -X POST "https://app.fareeqy.com/api/v1/tasks/tsk-9wb7t/comments" \   -H "Authorization: Bearer $FAREEQY_API_KEY" \   -H "Content-Type: application/json" \   -d '{"content":"خلصنا الهيدر، باقي قسم المزايا."}'`

#### Response

201 Copy

 `{   "data": {     "id": 512,     "url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "راجعت النسخة الأخيرة، ينقصنا حالة الخطأ في النموذج.",     "author": "عبدالله المطيري",     "created_at": "2026-07-15T09:48:03.000+03:00",     "updated_at": "2026-07-15T09:48:03.000+03:00",     "attachments": [       {         "id": 9031,         "name": "error-state.png",         "content_type": "image/png",         "byte_size": 184320       }     ]   } }`

Refusals: 401 403 404 409 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

409 `default`

A uniqueness/record conflict; retry.

default Copy

 `{   "error": {     "code": "conflict",     "message": "تعذّر إتمام العملية بسبب تعارض. أعد المحاولة."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

### Read a file attached to a task comment, by ref

GET /tasks/ {task\_id} /comments/ {comment\_id} /attachments/ {id} Requires `comments:read`

Returns the TEXT of one file attached inside a comment. `id` is the attachment's numeric id, read from the comment's `attachments`. Only text files are readable here (extensions such as `md`, `txt`, `csv`, `tsv`, `json`, `yml`, `xml`, `html`, `log`, `sql` and common source files), up to 256 KB. An image, a PDF, an oversize file, an empty one, or one whose stored file is missing is refused with a 422 that names it; open it from the comment's page instead. An attachment id that belongs to another comment is a 404.

#### Path parameters

`task_id` string required

The task REF (tsk-9wb7t). A slug is not accepted here.

`comment_id` integer required

The comment's NUMERIC id (comments have no ref and no slug).

`id` integer required

The attachment's NUMERIC id, from the comment's `attachments`.

#### Request

cURL Copy

 `curl "https://app.fareeqy.com/api/v1/tasks/tsk-9wb7t/comments/4821/attachments/1937" \   -H "Authorization: Bearer $FAREEQY_API_KEY"`

#### Response

200 Copy

 `{   "data": {     "id": 9044,     "name": "أخطاء-الاستيراد.csv",     "content_type": "text/csv",     "byte_size": 412,     "comment_id": 518,     "comment_url": "https://app.fareeqy.com/tasks/tsk-9wb7t",     "content": "row,error\n14,missing due date\n22,unknown assignee\n"   } }`

Refusals: 401 403 404 422 429 Show failure examples Hide failure examples

401 `default`

Missing or invalid API key.

default Copy

 `{   "error": {     "code": "unauthorized",     "message": "مفتاح الـ API غير صالح أو غير مُرسَل."   } }`

403 `forbidden`

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.

This key may not perform this operation Copy

 `{   "error": {     "code": "forbidden",     "message": "هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية."   } }`

The company's plan carries no API access Copy

 `{   "error": {     "code": "plan_upgrade_required",     "message": "خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله."   } }`

404 `not_found`

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.

A record that is missing or out of reach Copy

 `{   "error": {     "code": "not_found",     "message": "غير موجود، أو لا تملك صلاحية الوصول إليه."   } }`

A path that matches no endpoint Copy

 `{   "error": {     "code": "unknown_endpoint",     "message": "لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API."   } }`

422 `default`

A caller-fixable bad request (validation error, bad date, bad enum).

default Copy

 `{   "error": {     "code": "unprocessable_entity",     "message": "قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل."   } }`

429 `rate_limit_exceeded`

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.

Today's daily allowance is spent (application envelope) Copy

 `{   "error": {     "code": "rate_limit_exceeded",     "message": "استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh."   } }`

Over 300 requests in a minute (Rack::Attack body, flat error) Copy

 `{   "error": "Rate limit exceeded. Please try again later." }`

[Errors](https://fareeqy.com/api/v1/docs/en#errors)

[Previous Tasks](https://fareeqy.com/api/v1/docs/tasks/en) [Next Discussions](https://fareeqy.com/api/v1/docs/discussions/en)
