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

# Documents

> Read and manage client document metadata.

Documents have a dedicated section in the API Reference. The current Public API
operates on documents belonging to a specific client in the authenticated company.

| Action | Endpoint | Scope |
| :- | :- | :- |
| List client folders | `GET /public/v1/clients/{clientId}/document-folders` | `documents:read` |
| List client documents | `GET /public/v1/clients/{clientId}/documents` | `documents:read` |
| Register an external document | `POST /public/v1/clients/{clientId}/documents/links` | `documents:write` |
| Update document metadata | `PATCH /public/v1/clients/{clientId}/documents/{documentId}` | `documents:write` |
| Archive document metadata | `PATCH /public/v1/clients/{clientId}/documents/{documentId}/archive` | `documents:write` |

All writes require an `Idempotency-Key` header. Registering a document stores an
existing HTTP(S) URL; it does not upload a file. Archiving hides the document from
Public API and company MCP document listings; it does not delete the document or
its file from FITsociety or the external host.

Company-wide document listing and management are not currently exposed through
the Public API. Client routes do not provide a company-wide document collection.

## List document folders

```http theme={null}
GET /public/v1/clients/{clientId}/document-folders
Authorization: Bearer <access_token>
```

Required scope: `documents:read`

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.page` | integer | no | Current page. |
| `data.limit` | integer | no | Page size after cap. |
| `data.total` | integer | no | Total matching folders. |
| `data.totalPages` | integer | no | Total pages. |
| `data.hasNextPage` | boolean | no | Next page availability. |
| `data.hasPrevPage` | boolean | no | Previous page availability. |
| `data.folders[].folderId` | string | no | Folder ID. |
| `data.folders[].name` | string | no | Folder name. |
| `data.folders[].clientId` | string | yes | Client ID. |
| `data.folders[].sharedWithAllClients` | boolean | no | Shared-default flag. |
| `data.folders[].createdAt` | string | yes | Creation timestamp. |

## List documents

```http theme={null}
GET /public/v1/clients/{clientId}/documents?folderId={folderId}
Authorization: Bearer <access_token>
```

Required scope: `documents:read`

Validation:

| Parameter | Type | Required | Rule |
| :- | :- | :- | :- |
| `page` | integer | no | Minimum `1`. |
| `limit` | integer | no | Default `100`, maximum `100`. |
| `folderId` | string | no | Valid folder ObjectId. |

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.page` | integer | no | Current page. |
| `data.limit` | integer | no | Page size after cap. |
| `data.total` | integer | no | Total matching documents. |
| `data.totalPages` | integer | no | Total pages. |
| `data.hasNextPage` | boolean | no | Next page availability. |
| `data.hasPrevPage` | boolean | no | Previous page availability. |
| `data.documents[].documentId` | string | no | Document ID. |
| `data.documents[].name` | string | no | Document name. |
| `data.documents[].mimeType` | string | no | MIME type. |
| `data.documents[].fileSize` | string | no | File size as stored. |
| `data.documents[].folderId` | string | yes | Folder ID. |
| `data.documents[].clientId` | string | yes | Client ID. |
| `data.documents[].sharedWithAllClients` | boolean | no | Shared-default flag. |
| `data.documents[].addedByRole` | string | no | Uploader role. |
| `data.documents[].createdAt` | string | yes | Creation timestamp. |

Not exposed in v1: upload, delete, folder mutation, signed document download
URLs, raw `media`, provider metadata, message IDs, or file contents.

## Link external document metadata

```http theme={null}
POST /public/v1/clients/{clientId}/documents/links
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `documents:write`

This endpoint stores metadata for a document managed outside FITsociety. It does
not upload binary files and does not expose raw download links in list/read
responses.

Request body:

| Field | Type | Required | Rule |
| :- | :- | :- | :- |
| `name` | string | yes | Trimmed, maximum 160 characters. |
| `documentUrl` or `externalUrl` | URL string | yes | Must be an absolute `http` or `https` URL. |
| `mimeType` or `type` | string | no | Trimmed, maximum 120 characters. Defaults to `application/octet-stream`. |
| `fileSize` | string | no | Trimmed, maximum 40 characters. |
| `folderId` | ObjectId string | no | Folder must belong to the client, be company-shared, or be a shared default folder. |

Validation:

| Rule | Behavior |
| :- | :- |
| Client access | Client must belong to the authenticated company. |
| Coach context | The OAuth client must have a usable default coach context for attribution. |
| Folder access | Invalid or inaccessible folders return a document-folder error. |
| URL protocol | Non-HTTP(S) URLs are rejected. |

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.document.documentId` | string | no | Linked document ID. |
| `data.document.name` | string | no | Document name. |
| `data.document.mimeType` | string | no | MIME type. |
| `data.document.fileSize` | string | no | File size as stored. |
| `data.document.folderId` | string | yes | Folder ID. |
| `data.document.clientId` | string | yes | Client ID. |
| `data.document.sharedWithAllClients` | boolean | no | Shared-default flag. |
| `data.document.addedByRole` | string | no | Uploader role. |
| `data.document.createdAt` | string | yes | Creation timestamp. |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.fitsociety.io/public/v1/clients/64b64c0f2f5f4c0012345678/documents/links" \
    -H "Authorization: Bearer $FITSOCIETY_ACCESS_TOKEN" \
    -H "Idempotency-Key: document-link-training-agreement-20260823" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Training agreement",
      "documentUrl": "https://files.example.com/agreements/jane-doe.pdf",
      "mimeType": "application/pdf",
      "fileSize": "182 KB",
      "folderId": "64b64c0f2f5f4c00123456e0"
    }'
  ```

  ```javascript JavaScript theme={null}
  const clientId = "64b64c0f2f5f4c0012345678";
  const response = await fetch(
    `https://api.fitsociety.io/public/v1/clients/${clientId}/documents/links`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.FITSOCIETY_ACCESS_TOKEN}`,
        "Idempotency-Key": "document-link-training-agreement-20260823",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "Training agreement",
        documentUrl: "https://files.example.com/agreements/jane-doe.pdf",
        mimeType: "application/pdf",
        fileSize: "182 KB",
        folderId: "64b64c0f2f5f4c00123456e0",
      }),
    },
  );

  const body = await response.json();
  if (response.status === 201) {
    console.log("Linked document", body.data.document.documentId);
  }
  ```

  ```python Python theme={null}
  import os
  import requests

  client_id = "64b64c0f2f5f4c0012345678"
  response = requests.post(
      f"https://api.fitsociety.io/public/v1/clients/{client_id}/documents/links",
      headers={
          "Authorization": f"Bearer {os.environ['FITSOCIETY_ACCESS_TOKEN']}",
          "Idempotency-Key": "document-link-training-agreement-20260823",
      },
      json={
          "name": "Training agreement",
          "documentUrl": "https://files.example.com/agreements/jane-doe.pdf",
          "mimeType": "application/pdf",
          "fileSize": "182 KB",
          "folderId": "64b64c0f2f5f4c00123456e0",
      },
  )
  response.raise_for_status()

  body = response.json()
  if response.status_code == 201:
      print("Linked document", body["data"]["document"]["documentId"])
  ```
</CodeGroup>

Response (`201 Created`):

```json theme={null}
{
  "data": {
    "document": {
      "documentId": "64b64c0f2f5f4c00123456e1",
      "name": "Training agreement",
      "mimeType": "application/pdf",
      "fileSize": "182 KB",
      "folderId": "64b64c0f2f5f4c00123456e0",
      "clientId": "64b64c0f2f5f4c0012345678",
      "sharedWithAllClients": false,
      "addedByRole": "coach",
      "createdAt": "2026-08-23T10:00:00.000Z"
    }
  },
  "meta": {
    "requestId": "req_0123456789abcdef",
    "rateLimit": { "limit": 10, "remaining": 8, "resetSeconds": 1 }
  }
}
```

## Update document metadata

```http theme={null}
PATCH /public/v1/clients/{clientId}/documents/{documentId}
Authorization: Bearer <access_token>
Content-Type: application/json
```

Required scope: `documents:write`

Allowed fields: `name`, `documentUrl`, `externalUrl`, `mimeType`, `type`,
`fileSize`, and `folderId`. At least one field is required. The same name, URL,
type, file-size, client, and folder validations as Link external document
metadata apply.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.document.documentId` | string | no | Document ID. |
| `data.document.name` | string | no | Document name. |
| `data.document.mimeType` | string | no | MIME type. |
| `data.document.fileSize` | string | no | File size as stored. |
| `data.document.folderId` | string | yes | Folder ID. |
| `data.document.clientId` | string | yes | Client ID. |
| `data.document.sharedWithAllClients` | boolean | no | Shared-default flag. |
| `data.document.addedByRole` | string | no | Uploader role. |
| `data.document.createdAt` | string | yes | Creation timestamp. |

## Archive document metadata

```http theme={null}
PATCH /public/v1/clients/{clientId}/documents/{documentId}/archive
Authorization: Bearer <access_token>
```

Required scope: `documents:write`

Archive is a metadata-only soft archive. The document is excluded from Public
API list responses after archiving. It does not delete a binary file from
external storage.

Response fields:

| Field | Type | Nullable | Description |
| :- | :- | :- | :- |
| `data.document.documentId` | string | no | Document ID. |
| `data.document.archived` | boolean | no | Always `true` for successful responses. |
| `data.document.updatedAt` | string | yes | Update timestamp. |


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