Fareeqy Files API

Project files and external links. Numeric ids; the flat delete address takes a fil- ref.

GitHub OpenAPI

Operations 5

GET /projects/{project_id}/files List one level of a project's file browser #
POST /projects/{project_id}/files Upload a file from inline content #
DELETE /projects/{project_id}/files/{id} Delete a file #
DELETE /files/{id} Delete a file by ref #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/fareeqy-com:fareeqy-com-files-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

fareeqy-com-files-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Fareeqy REST Files API
  version: 1.3.0
  summary: Authenticated public REST API for Fareeqy project management.
  description: 'The Fareeqy public REST API (`/api/v1`) lets a company automate its projects,

    task lists, tasks, majlis discussions, files, folders and calendar events over

    plain JSON with a Bearer API key. **Preview.** This surface is gated behind the per-company Flipper feature flag

    `rest_api` (off by default). While the flag is disabled for a company, every

    authenticated endpoint returns **404** — the surface is hidden, not merely

    forbidden. Authentication is checked first (a bad key is still 401), then the

    flag, so the 404 only appears once a valid key has been presented. With the flag on, the PLAN decides access: Free and Pro carry no API calls, so

    a key on either is refused **403** `plan_upgrade_required`. There is deliberately no "my tasks" or "my personal tasks" endpoint. A key

    belongs to the COMPANY and the person on it merely created it, so "assigned

    to me" has no answer a company credential can give. Both lists remain on the

    MCP server, whose Personal Access Token really is personal. ## Vocabulary


    Product vocabulary and the internal schema differ, and this API follows the

    **product**:


    | API term | Path segment | What it is |

    |---|---|---|

    | Task list | `/lists` | A container that groups tasks within a project (schema model `Task`) |

    | Task | `/tasks` | The actionable item a user checks off (schema model `Todo`) |

    | Majlis topic / discussion | `/discussions` | A project discussion thread |


    ## Identifiers


    Every nested path segment accepts **any** shape below: the ref, the slug, or

    the record''s numeric id. The flat addresses take the ref only. Reads hand back

    the ref, so a client that follows its own output stays on refs without

    deciding anything. - **Refs** are the current identifier: a short ASCII handle like `prj-8f3kd`,

    `lst-4m2qp`, `tsk-9wb7t`, `mjl-6khz2`, `fil-3qd8n` — the same string the

    فريقي web app shows in its address bar, so a person can paste a link

    straight into a script. Three properties are worth knowing: a ref never

    changes when the record is renamed; it needs no percent-encoding; and the

    prefix is checked, so `/lists/tsk-…` is a 404 rather than a wrong

    record of the wrong kind. This is what the `id` field of every project,

    task list, task and discussion response contains, and the `id` of a file

    hit in search. A file response itself carries its numeric id. - **Slugs** are the older identifier and keep working on every nested

    address, including slugs retired by a rename. The flat addresses refuse them. A slug is unique only **within its parent**, so

    URLs stay fully nested and every slug is resolved through its parent (a

    task-list slug can repeat across projects). Never assume a slug is globally

    unique, and remember an Arabic slug must be percent-encoded. - **Numeric ids** address the records that have no ref: folders, events,

    comments, and the files attached inside a comment. A file accepts both its

    `fil-` ref and its numeric id on the nested routes, and projects, task

    lists, tasks and discussions accept their numeric id there too. These ids

    are global, so the API always drills a lookup through the parent project

    (or, for events, the key''s company): a numeric id from another company

    resolves to 404, never a leak. Parameters are still **named** `*_slug` in the paths and the schemas below. That is compatibility, not a constraint: renaming a public parameter would

    break every caller, and widening what one accepts breaks nobody. Read them as

    "handle". The same holds for the `*_slug` fields in responses (`project_slug`,

    `task_list_slug`, `task_slug`, `discussion_slug`): they carry the ref, not a

    slug. ## Links


    Every project, task list, task, discussion, comment, file, folder, event and

    search hit carries `url`: the absolute address of the page a person opens in

    فريقي. A comment has no page of its own, so its `url` is its parent''s page; a

    folder''s is the files page scoped to that folder. A project member is the one

    record without a `url`. Link to it rather than building the address yourself.'
  x-operation-catalog:
    description: 'The full operation catalog (Api::OperationRegistry). read needs the read scope; write needs the write scope; destructive needs the write scope AND is excluded from the write preset''s default grant (opt-in per key). `account` has no destructive operation.

      '
    account:
      read:
      - account:read
    projects:
      read:
      - projects:read
      write:
      - projects:write
      destructive:
      - projects:destructive
    task_lists:
      read:
      - task_lists:read
      write:
      - task_lists:write
      destructive:
      - task_lists:destructive
    tasks:
      read:
      - tasks:read
      write:
      - tasks:write
      destructive:
      - tasks:destructive
    discussions:
      read:
      - discussions:read
      write:
      - discussions:write
      destructive:
      - discussions:destructive
    comments:
      read:
      - comments:read
      write:
      - comments:write
    files:
      read:
      - files:read
      write:
      - files:write
      destructive:
      - files:destructive
    events:
      read:
      - events:read
      write:
      - events:write
      destructive:
      - events:destructive
servers:
- url: https://app.fareeqy.com/api/v1
  description: Production. The app runs on app.fareeqy.com (fareeqy.com is the marketing site and answers no API call). The base path (/api/v1) is part of the server URL, so operation paths below are written relative to it (e.g. /projects).
security:
- bearerAuth: []
tags:
- name: Files
  description: Project files and external links. Numeric ids; the flat delete address takes a fil- ref.
paths:
  /projects/{project_id}/files:
    parameters:
    - $ref: '#/components/parameters/ProjectId'
    get:
      tags:
      - Files
      operationId: listFiles
      summary: List one level of a project's file browser
      description: 'The folders and files directly inside `folder_id` (or the project root).

        Not recursive — walk down by passing a subfolder''s id back. Files are

        paginated; folders are not. Requires operation `files:read`.'
      parameters:
      - name: folder_id
        in: query
        description: The folder to list inside; omit for the project root.
        schema:
          type: integer
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Offset'
      responses:
        '200':
          description: One level of the file browser.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      folder:
                        oneOf:
                        - $ref: '#/components/schemas/Folder'
                        - type: 'null'
                        description: The folder being listed, or null at the project root.
                      folders:
                        type: array
                        items:
                          $ref: '#/components/schemas/Folder'
                      files:
                        type: array
                        items:
                          $ref: '#/components/schemas/ProjectFile'
                  meta:
                    type: object
                    properties:
                      total:
                        type: integer
                        description: Total files in this folder (folders are not paginated).
                      limit:
                        type: integer
                      offset:
                        type: integer
              example:
                data:
                  folder:
                    id: 42
                    url: https://app.fareeqy.com/projects/prj-8f3kd/files?folder_id=42
                    name: التصاميم
                    path: التصاميم
                    description: ملفات الهوية وواجهات الموقع.
                    parent_folder_id: null
                    created_at: '2026-07-05T08:20:00.000+03:00'
                  folders:
                  - id: 51
                    url: https://app.fareeqy.com/projects/prj-8f3kd/files?folder_id=51
                    name: الأيقونات
                    path: التصاميم/الأيقونات
                    description: null
                    parent_folder_id: 42
                    created_at: '2026-07-06T09:02:14.000+03:00'
                  files:
                  - id: 108
                    url: https://app.fareeqy.com/files/fil-3qd8n
                    name: الهوية-البصرية.pdf
                    path: التصاميم/الهوية-البصرية.pdf
                    folder_id: 42
                    content_type: application/pdf
                    size_bytes: 2418176
                    size_human: 2.31 م.بايت
                    external_url: null
                    source: null
                    scan_state: clean
                    downloadable: true
                    uploaded_by: سارة العتيبي
                    created_at: '2026-07-10T16:31:44.000+03:00'
                  - id: 131
                    url: https://app.fareeqy.com/files/fil-7mk2w
                    name: واجهات الصفحة الرئيسية
                    path: التصاميم/واجهات الصفحة الرئيسية
                    folder_id: 42
                    content_type: null
                    size_bytes: null
                    size_human: null
                    external_url: https://www.figma.com/design/9aZq/home-v3
                    source: Figma
                    scan_state: skipped
                    downloadable: true
                    uploaded_by: سارة العتيبي
                    created_at: '2026-07-12T11:04:20.000+03:00'
                  - id: 147
                    url: https://app.fareeqy.com/files/fil-2bz6r
                    name: لقطة-الصفحة-الرئيسية.png
                    path: التصاميم/لقطة-الصفحة-الرئيسية.png
                    folder_id: 42
                    content_type: image/png
                    size_bytes: 884736
                    size_human: 864 ك.بايت
                    external_url: null
                    source: null
                    scan_state: clean
                    downloadable: true
                    uploaded_by: عبدالله المطيري
                    created_at: '2026-07-14T10:22:07.000+03:00'
                  - id: 152
                    url: https://app.fareeqy.com/files/fil-9pd4h
                    name: عرض-التدفق-الجديد.mp4
                    path: التصاميم/عرض-التدفق-الجديد.mp4
                    folder_id: 42
                    content_type: video/mp4
                    size_bytes: 41943040
                    size_human: 40 م.بايت
                    external_url: null
                    source: null
                    scan_state: pending
                    downloadable: false
                    uploaded_by: نورة الحربي
                    created_at: '2026-07-15T13:47:58.000+03:00'
                meta:
                  total: 4
                  limit: 50
                  offset: 0
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
      - Files
      operationId: uploadFile
      summary: Upload a file from inline content
      description: 'Uploads a file from inline `content` (UTF-8 text) OR `content_base64`

        (binary, standard base64, no line breaks) — exactly one, not both. Capped at

        5 MB decoded. A freshly uploaded file is `pending` (not downloadable) until

        the virus scan clears it. Requires operation `files:write`.


        **`content` and the file name have to agree.** The name is a claim about

        what the bytes are, so `content` is accepted only for a plain-text

        extension — documents (`md`, `txt`, `rst`, `tex`), data and config

        (`csv`, `json`, `yml`, `xml`, `toml`, `ini`, `conf`, `env`), markup and

        styles (`html`, `svg`, `css`, `scss`), source (`js`, `ts`, `rb`, `py`,

        `sh`, `sql`, `graphql`) and the rest of that kind — or a name with no

        extension at all. A `.pdf` name is the one exception: the `content` is

        read as markdown and rendered into a real PDF document, capped at 256 KB

        of markdown because the render holds a request thread for its whole

        duration. Any other extension is refused with a 422 rather than stored,

        because text under a container extension such as `.docx` produces a file

        no viewer can open. Send genuine bytes as `content_base64` instead.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              properties:
                name:
                  type: string
                  description: The file name (with extension).
                folder_id:
                  type:
                  - integer
                  - 'null'
                  description: Destination folder; omit for the project root.
                content:
                  type:
                  - string
                  - 'null'
                  description: UTF-8 text content. Provide this OR content_base64.
                content_base64:
                  type:
                  - string
                  - 'null'
                  description: Base64-encoded bytes. Provide this OR content.
            examples:
              text_upload:
                summary: UTF-8 text, straight into the project root
                value:
                  name: ملاحظات-الإطلاق.md
                  content: '# ملاحظات الإطلاق

                    راجعنا الهيدر وقسم المزايا.

                    '
              binary_upload:
                summary: Binary bytes into a folder
                description: Standard base64, no line breaks. Capped at 5 MB decoded.
                value:
                  name: الهوية-البصرية.pdf
                  folder_id: 42
                  content_base64: JVBERi0xLjcKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c+PgplbmRvYmoK
      responses:
        '201':
          description: The uploaded file.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectFileEnvelope'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/RateLimited'
  /projects/{project_id}/files/link:
    parameters:
    - $ref: '#/components/parameters/ProjectId'
    post:
      tags:
      - Files
      operationId: addExternalLink
      summary: Add an external link
      description: 'Adds an external link (Figma, Drive, Notion...) — no storage, no scan, no

        size limit. The URL''s domain is validated against `source_type`. Requires

        operation `files:write`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              - url
              - source_type
              properties:
                name:
                  type: string
                url:
                  type: string
                  format: uri
                source_type:
                  $ref: '#/components/schemas/ExternalSourceType'
                folder_id:
                  type:
                  - integer
                  - 'null'
                  description: Destination folder; omit for the project root.
            examples:
              figma:
                summary: A Figma design file
                value:
                  name: واجهات الصفحة الرئيسية
                  url: https://www.figma.com/design/9aZq/home-v3
                  source_type: figma
                  folder_id: 42
              notion:
                summary: A Notion page
                description: 'The URL''s domain is validated against `source_type`, so a notion.so link declared as `google_drive` is a 422, not a silent mislabel.

                  '
                value:
                  name: خطة المحتوى
                  url: https://www.notion.so/fareeqy/content-plan-q3
                  source_type: notion
      responses:
        '201':
          description: The created link.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectFileEnvelope'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/RateLimited'
  /projects/{project_id}/files/{id}:
    parameters:
    - $ref: '#/components/parameters/ProjectId'
    - $ref: '#/components/parameters/FileId'
    delete:
      tags:
      - Files
      operationId: deleteFile
      summary: Delete a file
      description: Permanent. Requires operation `files:destructive`.
      responses:
        '200':
          description: Deletion result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletedFile'
              example:
                data:
                  deleted: true
                  file:
                    id: 108
                    name: الهوية-البصرية.pdf
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /files/{id}:
    parameters:
    - $ref: '#/components/parameters/FileRef'
    delete:
      tags:
      - Files
      operationId: deleteFileByRef
      summary: Delete a file by ref
      description: 'Permanent. There is no flat `get` or `patch`: a file''s listing hangs off

        its project (and folder), and a file has no update on this surface, so

        deleting it is the only thing its own address is for. Requires operation

        `files:destructive`.'
      responses:
        '200':
          description: Deletion result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletedFile'
              example:
                data:
                  deleted: true
                  file:
                    id: 41
                    name: عقد-الموردين.pdf
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  responses:
    Conflict:
      description: A uniqueness/record conflict; retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: conflict
              message: تعذّر إتمام العملية بسبب تعارض. أعد المحاولة.
    Forbidden:
      description: '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.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            forbidden:
              summary: This key may not perform this operation
              value:
                error:
                  code: forbidden
                  message: هذا الـ API key لا يملك صلاحية تنفيذ هذه العملية.
            plan_upgrade_required:
              summary: The company's plan carries no API access
              value:
                error:
                  code: plan_upgrade_required
                  message: خطة «الاحترافي» لا تشمل الوصول إلى API. رقِّ إلى «المتطور» أو «الإنتاجي» لتفعيله.
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: unauthorized
              message: مفتاح الـ API غير صالح أو غير مُرسَل.
    NotFound:
      description: '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.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            not_found:
              summary: A record that is missing or out of reach
              value:
                error:
                  code: not_found
                  message: غير موجود، أو لا تملك صلاحية الوصول إليه.
            unknown_endpoint:
              summary: A path that matches no endpoint
              value:
                error:
                  code: unknown_endpoint
                  message: لا يوجد endpoint يطابق GET /api/v1/projcts. راجع المسار في توثيق الـ API.
    Unprocessable:
      description: A caller-fixable bad request (validation error, bad date, bad enum).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: unprocessable_entity
              message: قيمة due_at غير صالحة؛ أرسل تاريخاً مثل 2026-07-20. المهمة تُستحق في يوم، وأي وقت ترسله يُهمل.
    RateLimited:
      description: '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.

        '
      headers:
        Retry-After:
          description: Seconds to wait before retrying. On a spent daily allowance this points at the company's next midnight.
          schema:
            type: integer
        X-RateLimit-Limit:
          description: The plan's daily API allowance, or `unlimited`.
          schema:
            type: string
        X-RateLimit-Remaining:
          description: Calls left in today's allowance, or `unlimited`.
          schema:
            type: string
        X-RateLimit-Reset:
          description: Unix timestamp of the next reset (the company's next midnight).
          schema:
            type: integer
      content:
        application/json:
          schema:
            oneOf:
            - $ref: '#/components/schemas/Error'
            - type: object
              description: The Rack::Attack burst-throttle body. Flat, with no error code.
              required:
              - error
              properties:
                error:
                  type: string
          examples:
            rate_limit_exceeded:
              summary: Today's daily allowance is spent (application envelope)
              value:
                error:
                  code: rate_limit_exceeded
                  message: استهلكت رصيد اليوم من طلبات API في خطة «المتطور» (1000 طلب يوميًا). يتجدد الرصيد عند منتصف الليل بتوقيت Asia/Riyadh.
            burst_throttled:
              summary: Over 300 requests in a minute (Rack::Attack body, flat error)
              value:
                error: Rate limit exceeded. Please try again later.
  parameters:
    Limit:
      name: limit
      in: query
      description: Page size (default 50, max 100, clamped).
      schema:
        type: integer
        default: 50
        minimum: 1
        maximum: 100
    FileId:
      name: id
      in: path
      required: true
      description: The file's REF (fil-8f3kd), or its numeric id. Files never had a slug.
      schema:
        type:
        - string
        - integer
    Offset:
      name: offset
      in: query
      description: Number of records to skip (default 0).
      schema:
        type: integer
        default: 0
        minimum: 0
    FileRef:
      name: id
      in: path
      required: true
      description: The file REF (fil-3qd8n). A numeric id is not accepted here.
      schema:
        type: string
        pattern: ^fil-[0-9a-zA-Z]{5,12}$
    ProjectId:
      name: project_id
      in: path
      required: true
      description: The project REF, or its slug.
      schema:
        type: string
  schemas:
    ProjectFile:
      type: object
      properties:
        id:
          type: integer
          description: Numeric id (files have no slug).
        url:
          type:
          - string
          - 'null'
          description: Absolute address of the page a person opens in فريقي.
        name:
          type: string
        path:
          type: string
          description: The folder path the file lives at.
        folder_id:
          type:
          - integer
          - 'null'
        content_type:
          type:
          - string
          - 'null'
        size_bytes:
          type:
          - integer
          - 'null'
        size_human:
          type:
          - string
          - 'null'
        external_url:
          type:
          - string
          - 'null'
          description: Set for an external link, null for an upload.
        source:
          type:
          - string
          - 'null'
          description: The source's display name.
        scan_state:
          type: string
          description: Virus-scan state (e.g. pending, clean).
        downloadable:
          type: boolean
          description: True once the scan has cleared the file.
        uploaded_by:
          type:
          - string
          - 'null'
        created_at:
          type: string
          format: date-time
      example:
        id: 108
        url: https://app.fareeqy.com/files/fil-3qd8n
        name: الهوية-البصرية.pdf
        path: التصاميم/الهوية-البصرية.pdf
        folder_id: 42
        content_type: application/pdf
        size_bytes: 2418176
        size_human: 2.31 م.بايت
        external_url: null
        source: null
        scan_state: clean
        downloadable: true
        uploaded_by: سارة العتيبي
        created_at: '2026-07-10T16:31:44.000+03:00'
    DeletedFile:
      type: object
      properties:
        data:
          allOf:
          - $ref: '#/components/schemas/DeletedBase'
          - type: object
            properties:
              file:
                type: object
                properties:
                  id:
                    type: integer
                  name:
                    type: string
    ExternalSourceType:
      type: string
      description: The provider an external link points to (its domain is validated).
      enum:
      - notion
      - google_drive
      - dropbox
      - onedrive
      - box
      - figma
      - sketch
      - zoho
      - other
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: A stable machine-readable slug.
              enum:
              - unauthorized
              - forbidden
              - plan_upgrade_required
              - not_found
              - unknown_endpoint
              - unprocessable_entity
              - conflict
              - project_in_use
              - bad_request
              - rate_limit_exceeded
            message:
              type: string
              description: A human-readable explanation in Arabic (may join several validation messages with "; ").
    Folder:
      type: object
      properties:
        id:
          type: integer
          description: Numeric id (folders have no slug).
        url:
          type:
          - string
          - 'null'
          description: The project's files page scoped to this folder.
        name:
          type: string
        path:
          type: string
        description:
          type:
          - string
          - 'null'
        parent_folder_id:
          type:
          - integer
          - 'null'
        created_at:
          type: string
          format: date-time
      example:
        id: 42
        url: https://app.fareeqy.com/projects/prj-8f3kd/files?folder_id=42
        name: التصاميم
        path: التصاميم
        description: ملفات الهوية وواجهات الموقع.
        parent_folder_id: null
        created_at: '2026-07-05T08:20:00.000+03:00'
    ProjectFileEnvelope:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/ProjectFile'
    DeletedBase:
      type: object
      required:
      - deleted
      properties:
        deleted:
          type: boolean
          const: true
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'An ApiKey presented as a Bearer credential. Keys carry the `frq_api_` prefix and are shown once at creation (only their SHA-256 digest is stored). Isolated from the MCP server''s Personal Access Tokens (`frq_`).


        A key belongs to the **company**, not to a person: it is administered from /settings/api_keys by the company owner or anyone granted the `manage_api_keys` permission, who can see and revoke every key the company has. Its capability is its own `allowed_operations` and its reach is the whole company; the person who created it is recorded as a signature and is not whose permissions the call runs with.


        So a key stops authenticating (401) for two reasons and no others: revocation and expiry. With the API switched off for the company, a valid key gets 404 instead: the surface is hidden. A key is explicitly unaffected by its creator changing role or leaving.

        '