# Files.fm Document Management System (DMS) API Files.fm provides a **Document Management System (DMS)** for organizing business documents with metadata, workflows, reminders, e-signatures, and access control integrated with cloud file storage. The DMS API currently covers **tasks** (create, read, update, add/read comments, attach files, organization user search), **projects** (read-only: fields, users, and document lists), **contact persons** (create, read, update), and **clients / parties** (read-only: fields, users, and document lists). Other DMS registers are not available through this API yet. Data created or updated here is the same data shown in the DMS web interface. > **Note:** Only `dvs_section=task`, `dvs_section=project_2`, `dvs_section=contact_person`, and `dvs_section=client` are supported. Additional registers and actions are planned. --- ## Base URL ```text https://api.files.fm/api_dvs/{dvs_section}/{action} ``` Example: ```text POST https://api.files.fm/api_dvs/task/create_document ``` --- ## Authentication Authenticate every request with `user` and `user_access_token`. Always use HTTPS. | Field | Description | | --- | --- | | `user` | Files.fm username or email | | `user_access_token` | API access token. Generate/revoke at `https://files.fm/account#api_keys`. | Send both in a JSON body (`Content-Type: application/json`) next to the action parameters. Do not put credentials in the URL. Do not send `pass`. A website login cookie by itself is not accepted. ```bash curl -X POST 'https://api.files.fm/api_dvs/task/get_fields' \ -H 'Content-Type: application/json' \ -d '{"user":"YOUR_USER","user_access_token":"YOUR_API_TOKEN"}' ``` ### Organization and DMS requirements Beyond authentication, the DMS API has two additional requirements: 1. **Organization membership:** the authenticated user must belong to an organization that has DMS enabled. 2. **Section write permission:** the user's user-group must have `write` permission on the target DMS register (`dvs_section`). Read-only users cannot currently reach the DMS API. Missing section access or an unavailable section returns HTTP `405`. If authentication does not establish a session organization, the API returns HTTP `401`. ### MCP access AI clients can use tasks and projects through the [Files.fm MCP server](filesfm_mcp.md#dvs-tools) instead of calling this API directly. The MCP DVS tools run the same organization, register and write-permission checks and use the same field names and IDs. They additionally require the `dvs:read` OAuth scope (`dvs:write` for task changes, plus `files:write` for attaching files) or a Files.fm API key. --- ## Request format The URL names the register and the action. Send action-specific parameters in a JSON body (`Content-Type: application/json`). `user` and `user_access_token` in that JSON are used for authentication and are not treated as document fields. ```text https://api.files.fm/api_dvs/{dvs_section}/{action} ``` | Path segment | Description | | --- | --- | | `{dvs_section}` | Register name: `task`, `project_2`, `contact_person`, or `client` (see [Supported registers](#supported-registers)) | | `{action}` | Operation name (see [Endpoint summary](#endpoint-summary)) | For actions with no extra parameters (for example `get_fields`), the JSON body only needs `user` and `user_access_token`. The only exception is [`upload_document_file`](#upload_document_file), which sends the same fields and the file as `multipart/form-data`. Prefer the URL path for `action` and `dvs_section`; do not rely on overriding them from the body. --- ## HTTP status codes | Status | Meaning | Action | | --- | --- | --- | | `200 OK` | Request succeeded. | Process the returned JSON. | | `400 Bad Request` | Invalid or incomplete parameters. | Check required parameters and formats. | | `401 Unauthorized` | Authorization failed or is missing. | Check `user` and `user_access_token`. | `405 Method Not Allowed` | Operation not permitted (missing section write permission, section not enabled for the org, or wrong HTTP method). | Check permissions, `dvs_section`, and method. | | `500 Internal Server Error` | Backend/API service error. | Retry later or contact Files.fm support. | On error, the response body is empty. Use the HTTP status code (and status text) to determine the failure; there is no JSON error payload. Exception: when [`upload_document_file`](#upload_document_file) cannot save the file itself (for example file too large or quota exceeded), it returns `200` with `{"success": false, "error_code": ..., "error_message": "..."}` and no `file_hash`. --- ## Supported registers The `{dvs_section}` path segment must be one of the values below. Any other section returns HTTP `400` (`Invalid DVS section`). | `dvs_section` | Register | Supported actions | | --- | --- | --- | | `task` | Tasks (project tasks) | `get_fields`, `get_users`, `search_users`, `get_document`, `get_documents`, `get_comments`, `create_document`, `update_document`, `add_comment`, `upload_document_file` | | `project_2` | Projects (parent container for tasks) | `get_fields`, `get_users`, `get_document`, `get_documents` | | `contact_person` | Contact persons (external notification receivers on tasks) | `get_fields`, `get_document`, `get_documents`, `create_document`, `update_document` | | `client` | Clients / parties (companies and partners) | `get_fields`, `get_users`, `get_document`, `get_documents` | `create_document` and `update_document` are available for **tasks** and **contact persons**. `add_comment`, `get_comments`, `search_users` and `upload_document_file` are **tasks only**. Projects and clients are read-only through this API. Use `contact_person` `get_documents` / `get_document` to look up contact IDs, then pass those IDs as `ext_notif_receiver` when creating or updating a task. You can also create or update contact persons through this API. Use `client` `get_documents` / `get_document` to look up client document IDs for contact `company` (and other relation fields that reference `client`). --- ## Endpoint summary | Action | Method | Endpoint | Purpose | | --- | --- | --- | --- | | `get_fields` | `POST` | `/api_dvs/{dvs_section}/get_fields` | Field schema and dropdown options for create/update/filter. | | `get_users` | `POST` | `/api_dvs/{dvs_section}/get_users` | Users that may be assigned (for tasks: assignable in one project). | | `search_users` | `POST` | `/api_dvs/task/search_users` | Find any active organization user by name; bounded, no paging. | | `get_document` | `POST` | `/api_dvs/{dvs_section}/get_document` | Read one document by ID. | | `get_documents` | `POST` | `/api_dvs/{dvs_section}/get_documents` | List/filter documents with pagination. | | `get_comments` | `POST` | `/api_dvs/task/get_comments` | Read a task's comment history with pagination. | | `create_document` | `POST` | `/api_dvs/{dvs_section}/create_document` | Create a document (`task`, `contact_person`). | | `update_document` | `POST` | `/api_dvs/{dvs_section}/update_document` | Partially update a document (`task`, `contact_person`). | | `add_comment` | `POST` | `/api_dvs/task/add_comment` | Add a comment on a task (same as the web UI). Use `@[USER:user_id]` in the text to mention a user. | | `upload_document_file` | `POST` (multipart) | `/api_dvs/task/upload_document_file` | Attach a new file to a task. | `{dvs_section}` is `task`, `project_2`, `contact_person`, or `client` for the read actions (`get_users` is `task`, `project_2`, and `client` only; `get_comments` and `search_users` are `task` only). Write actions are `task` and `contact_person` (`add_comment` and `upload_document_file` are `task` only). --- ## Finding IDs All IDs are organization-specific. Read them from the API at run time; do not hard-code status, project or user IDs between organizations or environments. | You need | Where to get it | | --- | --- | | Your own user ID and organization | `get_fields` → `actor.id`, `actor.org_id` | | Which actions this server supports | `get_fields` → `actions` (for example, check for `search_users` before calling it) | | Dropdown value IDs (status, type, priority, …) | `get_fields` → the field's `options` (`{id, display_name}`); match `display_name`, send `id` | | Project ID | `project_2/get_documents` (filter or page through), or `dvs_project_id` of an existing task | | User ID to **assign** (`responsible`, `followers`) | `task/get_users` with `dvs_project_id` — only these IDs are accepted on create/update | | User ID of **anyone** in the organization (queue filter, `@[USER:id]` mention) | `task/search_users` with a name `pattern` | | User IDs already on records | `responsible` / `followers` / `creator` in `get_document(s)`, `author.id` in `get_comments` | | Contact person / client IDs | `contact_person/get_documents`, `client/get_documents` | Example — open tasks of one person, oldest first: 1. `task/get_fields` → read `dropdown_status.options` and pick the "open" status ID; check that `actions` contains `search_users`. 2. `task/search_users` with `{"pattern": "Anna Smith"}` → user ID (or use `actor.id` for your own queue). 3. `task/get_documents` with `{"filters": {"responsible": 1042, "dropdown_status": 12}, "order": "oldest", "limit": 20}`. --- ## Actions ### `get_fields` Returns the field schema for a register: field names, labels, types, whether the field is required, and the available options for dropdown fields. Use this to discover what fields to pass to `create_document` / `update_document` and which fields support filtering in `get_documents`. ```text POST https://api.files.fm/api_dvs/task/get_fields ``` Request body: authorization only. **Response:** ```json { "fields": [ { "name": "dvs_project_id", "label": "Project", "type": "relation", "required": true, "filter": true, "section": "project_2", "list_action": "get_documents" }, { "name": "name", "label": "Name", "type": "text", "required": true, "filter": false }, { "name": "dropdown_status", "label": "Status", "type": "dropdown", "required": false, "filter": true, "options": [ { "id": 12, "display_name": "Open" }, { "id": 13, "display_name": "In progress" }, { "id": 14, "display_name": "Done" } ] }, { "name": "responsible", "label": "Responsible", "type": "user_id", "required": false, "filter": true, "depends_on": "dvs_project_id", "list_action": "get_users" }, { "name": "followers", "label": "Followers", "type": "user_ids", "required": false, "filter": false, "depends_on": "dvs_project_id", "list_action": "get_users" }, { "name": "ext_notif_receiver", "label": "External followers", "type": "relation_ids", "required": false, "filter": false, "section": "contact_person", "list_action": "get_documents" } ], "actions": ["get_fields", "get_users", "search_users", "get_document", "get_documents", "get_comments", "create_document", "update_document", "add_comment"], "actor": { "id": 1042, "org_id": 450 } } ``` `actions` lists the actions this section supports on the server, so clients can detect newly deployed capabilities. `actor` is the authenticated account's user ID and organization; use `actor.id` as the `responsible` filter for "my tasks". #### Field types | Type | Description | Input format (create/update) | Output format (get_document/get_documents) | | --- | --- | --- | --- | | `text` | Plain text or numeric string | string | `"value"` or `null` | | `date` | Calendar date | `"YYYY-MM-DD"` string | `"YYYY-MM-DD"` or `null` | | `html` | Rich text (HTML markup) | HTML string | Full HTML string (only in `get_document`; omitted in `get_documents`) | | `dropdown` | Org-specific enum value | integer id from `options` | `{ "id": N, "display_name": "..." }` or `null` | | `dropdown_ids` | Multiple org-specific enum values | array of integer ids from `options` | array of `{ "id", "display_name" }` objects | | `relation` | Link to a document in another register | integer document id | `{ "id": N, "display_name": "..." }` or `null` | | `relation_ids` | Multiple links to documents in another register | array of integer document ids | array of `{ "id", "display_name" }` objects | | `user_id` | Single assignable user | integer user id from `get_users` | `{ "id": N, "display_name": "Full Name" }` or `null` | | `user_ids` | Multiple assignable users | array of integer user ids | array of `{ "id", "display_name" }` objects | Fields with `"filter": true` can be used as filters in `get_documents`. Fields with `"depends_on"` require the named field's value to be supplied when calling `get_users`. Fields with `"list_action": "get_users"` contain user references; call `get_users` with the relevant context parameters to get the allowed values. Fields with `"list_action": "get_documents"` contain references to documents in another register; call `get_documents` on that register (see `"section"`) to list possible values. --- ### `get_users` Returns assignable platform user IDs for `user_id` and `user_ids` fields in the current register. Only available on registers that expose those field types (`task`, `project_2`, `client`); not on `contact_person` (use `get_documents` there for contact document IDs). For tasks, the list contains users who may be assigned in the specified project: users with task read or write permission, plus users with own-record permission who are workers of that project. It is not an organization-wide directory — use [`search_users`](#search_users) to find any organization user by name. ```text POST https://api.files.fm/api_dvs/task/get_users Content-Type: application/json ``` **Request:** ```json { "dvs_project_id": 51 } ``` | Parameter | Required for | Description | | --- | --- | --- | | `dvs_project_id` | `task` | ID of the project whose workers are listed. Required for task; not needed for `project_2` or `client`. | **Response:** ```json { "users": [ { "id": 1042, "display_name": "Anna Smith" }, { "id": 1105, "display_name": "John Doe" } ] } ``` --- ### `get_document` Returns a single document by its DMS document ID. ```text POST https://api.files.fm/api_dvs/task/get_document Content-Type: application/json ``` **Request:** ```json { "dvs_document_id": 4821 } ``` **Response:** ```json { "dvs_document_id": 4821, "full_number": "PROJ-7", "dvs_project_id": { "id": 51, "display_name": "Website redesign" }, "dropdown_type": null, "name": "Write API documentation", "description": "
Document all DMS API endpoints.
", "dropdown_priority": { "id": 17, "display_name": "High" }, "dropdown_status": { "id": 12, "display_name": "In progress" }, "responsible": { "id": 1042, "display_name": "Anna Smith" }, "followers": [ { "id": 1105, "display_name": "John Doe" } ], "ext_notif_receiver": [ { "id": 901, "display_name": "Jane Client" } ], "date_due": "2026-09-30", "estimated_hours": "4", "spent_hours": null, "creator": { "id": 980, "display_name": "Maria Admin" }, "date_created": "2026-08-10 14:22:05", "date_updated": "2026-08-24 09:45:11", "upload_hash": "k3n8vz2qpw7r5xyt" } ``` `upload_hash` (`get_document` only) is the task's attachment folder, or `null` when the task has no attachments yet or you cannot view the folder. List its files with the Files.fm API or the MCP `filesfm_list_folder` tool. The `html` field type (`description`) is returned in full only for `get_document`. It is omitted from `get_documents` list results. The `creator`, `date_created`, and `date_updated` fields are read-only metadata returned on get, not accepted in `get_fields` for create/update. --- ### `get_documents` Returns a paginated list of documents in a register, with optional filtering. ```text POST https://api.files.fm/api_dvs/task/get_documents Content-Type: application/json ``` **Request:** ```json { "limit": 20, "offset": 0, "filters": { "dvs_project_id": 51, "dropdown_status": [12, 13] } } ``` | Parameter | Type | Default | Maximum | Description | | --- | --- | --- | --- | --- | | `limit` | integer | 50 | 200 | Number of results to return | | `offset` | integer | 0 | — | Number of results to skip | | `filters` | object | — | — | Only fields marked `"filter": true` in `get_fields`. Multiple values for one field are OR; multiple fields are AND. | **Response:** ```json { "items": [ { "dvs_document_id": 4821, "full_number": "PROJ-7", "dvs_project_id": { "id": 51, "display_name": "Website redesign" }, "dropdown_type": null, "name": "Write API documentation", "dropdown_priority": { "id": 17, "display_name": "High" }, "dropdown_status": { "id": 12, "display_name": "In progress" }, "responsible": { "id": 1042, "display_name": "Anna Smith" }, "followers": [], "ext_notif_receiver": [], "date_due": "2026-09-30", "estimated_hours": "4", "spent_hours": null, "creator": { "id": 980, "display_name": "Maria Admin" }, "date_created": "2026-08-10 14:22:05", "date_updated": "2026-08-24 09:45:11" } ], "total": 1, "limit": 20, "offset": 0 } ``` Results default to creation date and document ID descending (newest first). Use `"order": "oldest"` for ascending creation date and document ID, or `"order": "newest"` for the default. Other order values return 400. The response includes the effective `order`. Task `responsible` is filterable: combine `"filters": {"responsible": 1042, "dropdown_status": 12}` to select an assignee's queue on the server. This filters active RESPONSIBLE workers, not followers, while retaining organization access boundaries. User IDs and dropdown IDs must come from the applicable organization. Continue with `offset += items.length` until the offset reaches `total`. HTML fields are omitted from lists. Unknown filters are ignored; use only fields marked `filter: true`. No universal public DVS request quota is promised; infrastructure may apply additional limits. To look up a contact ID for task `ext_notif_receiver`, call `get_documents` on `contact_person`. The contact list can be filtered by `company` and `multi_dropdown_group` (`"filter": true` in `get_fields`). To look up a client ID for contact `company`, call `get_documents` on `client`. The client list can be filtered by dropdown fields, `multi_dropdown_group`, and `projectmanager` (`"filter": true` in `get_fields`). --- ### `create_document` Creates a new document. Only fields declared by `get_fields` are accepted; unknown fields are ignored. Available for `task` and `contact_person`. ```text POST https://api.files.fm/api_dvs/task/create_document Content-Type: application/json ``` Auth: `user` and `user_access_token` in this JSON. **Request:** ```json { "user": "YOUR_USER", "user_access_token": "YOUR_API_TOKEN", "create_params": { "dvs_project_id": 51, "name": "Review pull request", "dropdown_priority": 17, "dropdown_status": 12, "responsible": 1042, "followers": [1105], "ext_notif_receiver": [901], "date_due": "2026-09-15", "estimated_hours": "2" } } ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `create_params` | object | Yes | Document field values keyed by field name | All fields declared as `"required": true` by `get_fields` must be present and non-empty. Fields marked `"required": false` may be omitted; the register sets sensible defaults. User fields (`user_id`, `user_ids`) must contain IDs returned by `get_users`. Dropdown fields (`dropdown`, `dropdown_ids`) must contain option IDs returned by `get_fields`. Relation fields (`relation`, `relation_ids`) must contain document IDs from the register named in `"section"` (for task `ext_notif_receiver`: `contact_person`; for contact `company`: `client`). Invalid IDs return `400`. Look up client IDs with `client` `get_documents` / `get_document`. **Response:** ```json { "dvs_document_id": 4822 } ``` Use the returned `dvs_document_id` with `get_document` to retrieve the full record including auto-generated fields such as `full_number`. Creation and updates can send notifications. There is no documented idempotency key. Reconcile an uncertain write with reads before retrying; invalid relation IDs can fail after part of a write has occurred. A failure does not guarantee rollback. --- ### `update_document` Updates an existing document. Only the fields provided in `update_params` are changed; all other fields keep their current values. Available for `task` and `contact_person`. ```text POST https://api.files.fm/api_dvs/task/update_document Content-Type: application/json ``` Auth: `user` and `user_access_token` in this JSON. **Request:** ```json { "user": "YOUR_USER", "user_access_token": "YOUR_API_TOKEN", "dvs_document_id": 4822, "update_params": { "dropdown_status": 14, "spent_hours": "2.5" } } ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `dvs_document_id` | integer | Yes | ID of the document to update | | `update_params` | object | Yes | Fields to change, keyed by field name | The same field-level validation rules as `create_document` apply. The project (`dvs_project_id`) of a task cannot be changed through the API. Omitted fields keep their current values. Supplied array fields replace that field's selected values; read the record first when preserving followers or relations. See the [tasks quickstart](filesfm_api_dvs_tasks.md) for complete curl, PHP and JavaScript workflows. **Response:** ```json { "dvs_document_id": 4822 } ``` --- ### `get_comments` Read existing task comments using `POST /api_dvs/task/get_comments` with JSON `user`, `user_access_token`, `dvs_document_id`, and optional `limit` (default 50, range 1–200) and `offset` (default 0, nonnegative). Requires the same organization, section write permission and active document lookup as `get_document`. Returns `{items, total, limit, offset}`. Items are sorted by comment ID ascending; deleted comments are omitted. Each item contains `id`, sanitized and decoded `html`, `date_created`, and `author`: `{id, display_name, org_id, is_active}`. Unknown or foreign authors are `null`. Inactive same-organization authors remain identifiable with `is_active: false`. Continue pagination until `offset` reaches `total`; a page is not the full history unless all pages have been read. This read does not notify people. It can verify an `add_comment` acknowledgement by checking author, content and time. It provides no locking, conditional write, or atomic snapshot guarantee. --- ### `search_users` Finds active users of your organization by name. Use it to get a user ID for the `responsible` filter in `get_documents` or for an `@[USER:id]` mention in `add_comment`, when you do not know a project the person works in. Available for `task` only; requires the same section write permission as the other actions. ```text POST https://api.files.fm/api_dvs/task/search_users Content-Type: application/json ``` **Request:** ```json { "user": "YOUR_USER", "user_access_token": "YOUR_API_TOKEN", "pattern": "anna smi", "limit": 10 } ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `pattern` | string | Yes | 2–100 characters after trimming. Split into words; every word must occur (case-insensitive substring) in the user's login name, first name or surname. `%` and `_` are literal characters. The separate email field is not searched. | | `limit` | integer | No | Maximum number of users, 1–20 (default 10). There is no `offset`: refine the pattern instead of paging. | | `dvs_document_id` | integer | No | Active task ID in your organization. Makes `has_access` specific to that task (own-record access through its project). Unknown or foreign IDs return `405`. | **Response:** ```json { "users": [ { "id": 1042, "display_name": "Anna Smith", "has_access": true }, { "id": 1310, "display_name": "Annija Smiltēna", "has_access": false } ], "limit": 10 } ``` Users are sorted by first name, surname and ID. `display_name` is the first name and surname, or the login name when both are empty. `has_access` is `true` when the user can read tasks (or the given task) — the same rule the DMS web interface uses when it marks mention suggestions with `*`. A user with `has_access: false` can still be mentioned and receives the mention email, but cannot open the task. `search_users` does not change what may be **assigned**: `responsible` and `followers` on create/update still accept only IDs returned by `get_users` for the task's project. Invalid `pattern`, `limit` or `dvs_document_id` values return `400`. The action reads data only and sends no notifications. --- ### `add_comment` Adds a comment on a task. Same storage and notifications as the DMS web interface (`htmlspecialchars` on save; HTML is shown in comment emails like task descriptions). Available for `task` only. To mention a user, put `@[USER:user_id]` in the comment at the position you want (`USER` is the type prefix; `user_id` is the same integer `id` as in `search_users` / `get_users` / `get_document`). You can mention several users, including in the middle of a sentence. Plain `@name`, email addresses, and `@[1042]` without `USER:` stay as normal text. A mention id must belong to a user in the same organization. Mentions are not limited to project workers — the same as mentioning someone in the DMS web interface (they get the mention email even if they cannot open the task). ```text POST https://api.files.fm/api_dvs/task/add_comment Content-Type: application/json ``` Auth: `user` and `user_access_token` in this JSON. **Request:** ```json { "user": "YOUR_USER", "user_access_token": "YOUR_API_TOKEN", "dvs_document_id": 4821, "comment": "Please ask @[USER:1042] to review the draft before Friday." } ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `dvs_document_id` | integer | Yes | ID of the task to comment on | | `comment` | string | Yes | Comment body (same as the web editor). Must be non-empty after trimming. Insert `@[USER:user_id]` where a mention should appear. | **Response:** ```json { "dvs_document_id": 4821 } ``` ### `upload_document_file` Attaches a new file to a task, the same as adding an attachment in the DMS web interface. The task's attachment folder is created on the first upload. The upload is recorded in the task history and the usual task notification is sent. Existing files are not replaced: if the task already has a file with the same name, the new file is saved with a numbered name, for example `offer (1).pdf`. Available for `task` only. Unlike the other actions, this request is `multipart/form-data`: send the credentials and `dvs_document_id` as form fields and the file in the `file` field. ```bash curl -X POST 'https://api.files.fm/api_dvs/task/upload_document_file' \ -F user=YOUR_USER \ -F user_access_token=YOUR_API_TOKEN \ -F dvs_document_id=4821 \ -F file=@offer.pdf ``` | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `dvs_document_id` | integer | Yes | ID of the task | | `file` | file | Yes | The file to attach (multipart field), one file per request. The account's storage and file-size limits apply. | **Response:** ```json { "file_hash": "q7x2m9kd4wz1p8ht", "upload_hash": "k3n8vz2qpw7r5xyt" } ``` `file_hash` identifies the saved file; `upload_hash` is the task's attachment folder (also returned by `get_document`). Errors: a missing, empty or failed file field, or more than one file, returns `400`; an unknown task, a task in another organization, or missing upload permission returns `405`, like other actions. Errors raised while saving the file itself (for example file too large, storage quota exceeded, or virus detected) currently return HTTP `200` with a JSON body `{"success": false, "error_code": ..., "error_message": "..."}`, so check for `file_hash` in the response.