# Files.fm API documentation > Generated from the published Markdown sources. API keys and account-specific IDs must be supplied by the integrator. # Files.fm Cloud Files and Storage API Documentation Official documentation: **https://api.files.fm/api/docs/**. The same Markdown sources produce the interactive documentation, complete [HTML pages](README.html), and public MCP documentation resources. JavaScript and authentication are not required to read documentation. ## Start an integration - **DVS tasks:** start with the [tasks quickstart](filesfm_api_dvs_tasks.md), then the [DMS reference](filesfm_api_dms.md). DMS and DVS refer to the same data-management API. Discover project, user, and dropdown IDs before creating records. - **Storage:** start with the [storage quickstart](filesfm_api_storage.md). Prefer V2 where an equivalent exists; V1 remains supported for compatibility. - **AI clients:** connect to `https://api.files.fm/api/mcp` to search and fetch this documentation without a token. Private file tools require their own authentication. MCP does not read structured DVS records or create/update tasks. REST V2 uses POST form fields; DVS uses POST JSON with `user` and `user_access_token`. MCP uses its documented Bearer authentication. DAV/FTP credentials are protocol-specific; do not interchange these formats. ## References - [Files.fm API documentation](filesfm_api_v2.md): the complete, merged API reference. Covers REST API V2 (data storage, CRUD, e-signature, AI API, upload form, embedding, previews, conversion, end-to-end encryption keys) and all V1 endpoints, plus authentication methods and ROOT folder/account info. - [MCP](filesfm_mcp.md): hosted Model Context Protocol connector for AI tools, with public documentation tools and read-only Files.fm file access/search. - [DMS API](filesfm_api_dms.md): Document Management System API — currently tasks (create/read/update/comment), projects (read), contact persons (create/read/update), and clients (read). Covers authentication, field schema discovery, filtering, pagination, and request/response examples. - [CalDAV and CardDAV](filesfm_api_dav.md): calendar and address-book APIs over HTTPS, including authentication, discovery, event/contact CRUD, synchronization, and shared-calendar permissions. - [WebDAV and FTP/FTPS](filesfm_api_protocols.md): network-drive and file-transfer protocols, authentication, locking, and operational limits. ## Machine-readable sources - [OpenAPI YAML](openapi.yaml) and [OpenAPI JSON](openapi.json): REST V2, legacy V1, and public DVS actions. DAV protocols use their own XML contracts; MCP tool schemas are discovered through MCP. - [Swagger UI](swagger/): interactive REST explorer. Requests can change data; use a dedicated test account for write examples. - [Documentation index](llms.txt), [full Markdown collection](llms-full.txt), and [document catalog](docs-manifest.json). The index is a discovery aid, not a guarantee that every AI product fetches links or supports a particular content type. If a client cannot retrieve Markdown, use the linked HTML version. Never invent API endpoints or organization-specific IDs when retrieval fails. For production integrations, prefer API V2 where available. V1 endpoints remain for compatibility with existing integrations and can be used, while no V2 endpoint exists. For DMS API details that are not yet published here, [contact Files.fm](https://files.fm/contacts). # Files.fm Cloud Storage API Files.fm is a cloud storage platform with an HTTPS REST API (GET/POST), plus FTP/FTPS, WebDAV, and [CalDAV/CardDAV calendar and address-book APIs](filesfm_api_dav.md). This document is the single source of truth for the Files.fm API. It merges the modern REST API V2 (recommended for new integrations) with the legacy V1 endpoints (kept for compatibility), and is intended for both human programmers and LLM/AI agents. The content is verified against the live code: the V2 router `api/v2/index.php`, the V1 scripts under `api/`, and the shared authenticator `api/authenticate_user.php`. The REST V2 and integration sections also derive from `Files.fm_API_docs_EN_v.1.9.5.pdf` (version 1.9.5, dated 2026-05-01); legacy V1 endpoints derive from the public API reference page and the live `/api/` scripts. Concrete access tokens and hashes in examples are placeholders. For new integrations, prefer API V2 where available. The legacy V1 endpoints remain documented because many of them have no V2 equivalent (thumbnails, recursive tree, ZIP download, versions, sharing, embedding, and more). ## REST API Overview Base API host: ```text https://api.files.fm ``` Web links and the file browser use the base web host `https://files.fm`. Localized web subdomains are supported for share links, for example `https://es.files.fm/u/{folder_hash}` and `https://de.files.fm/u/{folder_hash}`. ### Routing model The API is reachable through three URL styles. Knowing which style an endpoint uses avoids most "404 / wrong path" problems. | Style | URL pattern | Maps to | Used by | | --- | --- | --- | --- | | V2 REST action | `https://api.files.fm/api_v2/{action}` | `api/v2/index.php?action={action}` (server rewrite) | All API V2 actions | | V1 script | `https://api.files.fm/api/{script}.php` | `api/{script}.php` | Legacy V1 endpoints (login, keys, listing, tree, undelete, etc.) | | Root-level script | `https://api.files.fm/{script}.php` | top-level script | `save_file.php`, `down.php`, `thumb*.php`, ZIP streamer | The `api_v2/{action}` form is a clean-URL rewrite to the single dispatcher `api/v2/index.php`; the `action` may also be passed as a `GET`/`POST` parameter. Prefer `POST` for API parameters unless an endpoint explicitly documents `GET` as accepted. Successful REST responses are JSON unless the endpoint returns raw file content (such as `download_file`); a few legacy endpoints return plain text. ### Common Integration Flow 1. Generate an API key at `https://files.fm/account#api_keys` or use a dedicated user/service account. 2. Create a folder with `create_folder`; store `folder_hash`, `folder_edit_key`, and `folder_upload_key` in your own database. 3. Upload files with `upload_file` using authentication or the folder upload key. 4. List or inspect content with `get_folder_content`, `get_file_info`, and `get_folder_info`. 5. Download files with `download_file`. 6. Rename, move, delete, or restore files/folders as needed. ### Important Concepts - Every file and folder has a unique hash. Treat file and folder hashes as opaque, variable-length strings: preserve and transmit the complete value returned by the API, and do not validate or branch on its length. Store hashes and the corresponding edit/upload/delete keys in your own system. - Keys and their meaning: - **Folder hash** identifies a folder (also called an "upload"). Example: `hash=k7m2q9r4t6v8w3xy`. - **File hash** identifies a file. Example: `i=p4s7u2w9x3y6z8ab` in download links. - **add_key** / `folder_upload_key` / `AddFileKey` is an opaque capability that allows uploading new files into a folder without full access. - **delete_key** / `folder_edit_key` / `DeleteKey` is an opaque capability that allows editing/deleting the folder and its files (subject to access rights). - Newly created folder keys are independent 32-character lowercase hexadecimal values. Preserve and transmit them exactly; older folders can still have shorter keys. - **File edit/delete key** (`file_delete_key`) is used for file edits/replacements; newly created keys are 32-character lowercase hexadecimal capabilities, while legacy shorter keys remain valid. A folder edit key also works where supported. - **ROOT folder**: every account has exactly one ROOT folder (`DisplayName = "ROOT"`, `ParentID = 0`). It is always private, cannot be renamed or deleted, and is the default parent when you create a folder without `parent_folder_hash`. Its hash and keys are returned at login (see "ROOT folder and account info"). - Access types: `PRIVATE`, `LINK`, `PASSWORD`, and `PUBLIC` where supported. - `LINK` allows reading/listing/downloading by anyone who knows the hash/link. - `PRIVATE` requires authorization and explicit rights. - `PASSWORD` requires the folder password (passed as `password`). - Files and folders created through the API can also be managed in the Files.fm UI at `https://files.fm/filebrowser`. - API requests may be rate-limited or blocked by automated web firewall checks if request patterns look abusive. - Business and Enterprise integrations needing higher limits, dedicated access rules, or production support should contact `support@files.fm`. ### Authorization Authentication is handled centrally by `api/authenticate_user.php`, which is included by both the V2 router and the V1 scripts. It accepts the following methods (checked in roughly this order). Any one of them is enough; always use HTTPS. | Method | How to send credentials | Notes | | --- | --- | --- | | `user` + `pass` (GET) | `?user=...&pass=...` | Username or email + password. Supported for compatibility; avoid putting secrets in URLs/logs. | | `user` + `pass` (POST) | POST fields `user`, `pass` | Preferred over GET for credentials. | | HTTP Basic authentication | `Authorization: Basic base64(user:pass)` header | Also read from the FastCGI `Authorization` header when `PHP_AUTH_USER`/`PHP_AUTH_PW` are not populated. | | API access token | `user` + `user_access_token` (GET or POST) | Token for a user or dedicated service account. Generate/revoke at `https://files.fm/account#api_keys`. Recommended for automation. | | Existing session cookie | Standard `PHPSESSID` cookie from a prior login | Supported by authenticated actions. | | Session id parameter | `?PHPSESSID=...` or `?SessionID=...` | Use the session id without a cookie jar. | | Google OAuth | `google_token` + `google_email` (GET or POST) | Verified ID token. First-time logins may auto-create an account (`first_name`, `last_name` optional). | | Apple SSO | `apple_code` (GET or POST) | Authorization code exchanged with Apple; may auto-create an account on first login. | When `authenticate_user.php` is called as a standalone endpoint (no `API_APP` constant), it returns a JSON object: ```json { "Success": true, "Message": null, "SessionID": "PHPSESSID_VALUE" } ``` #### Generating an API access token Pass `get_user_access_token` to a successful login call (for example `api/login.php` or `api/authenticate_user.php`) to mint a token for the authenticated user; it is returned as `UserData.user_access_token`. Tokens can also be created and revoked in account settings at `https://files.fm/account#api_keys`. Keep tokens confidential and rotate them if leaked. #### Two-factor authentication (2FA) If the account requires 2FA, a successful credential check does not immediately grant a session. The API sends a confirmation link by email and waits briefly for the same IP to be confirmed: - Standalone JSON response while pending: `{"Success": false, "Status": "two_factor_auth_required", "Message": "Two-factor auth required"}`. - When included by another API script: the request exits with HTTP `403` and a `TWO_FACTOR_REQURIED: 1` header. Confirm the emailed link from the same IP, then retry the request. A token-based login for a service account bypasses the 2FA demand. #### Security notes - Abusive or blacklisted source IPs and password-guessing patterns are throttled or blocked. - Account creation via `api/register.php` is disabled; OAuth/SSO first-login auto-provisioning is the only API path that creates accounts. ### HTTP Status Codes | Status | Meaning | Action | | --- | --- | --- | | `200 OK` | Request succeeded. | Process the returned JSON or binary content. | | `400 Bad Request` | Invalid or incomplete parameters. | Check required parameters and formats. | | `401 Unauthorized` | Authorization failed or is missing. | Check credentials, token, or session. | | `403 Forbidden` | Authenticated user does not have access rights. | Change access type or share the resource with the user. | | `405 Method Not Allowed` | Requested operation is not permitted for this resource. | Check file/folder hash and permissions. | | `429 Too Many Requests` | Rate limit hit. | Wait for the rate limit to expire and retry later. | | `500 Internal Server Error` | Backend/API service error. | Retry later or contact Files.fm support. | ### Endpoint Summary These are all the actions accepted by the V2 router `api/v2/index.php`. All use the `/api_v2/{action}` URL. All require authorization except `upload_file` and `download_file` (which can also work with a folder upload key or LINK access respectively). | Action | Method | Endpoint | Purpose | | --- | --- | --- | --- | | `create_folder` | `POST` | `/api_v2/create_folder` | Create a folder in ROOT or a parent folder. | | `upload_file` | `POST multipart/form-data` | `/api_v2/upload_file` | Upload a file to an existing folder. | | `download_file` | `POST` | `/api_v2/download_file` | Download raw file contents by file hash. | | `delete_folder` | `POST` | `/api_v2/delete_folder` | Delete a folder. | | `delete_file` | `POST` | `/api_v2/delete_file` | Delete a file. | | `get_folder_content` | `POST` | `/api_v2/get_folder_content` | List files and subfolders in a folder. | | `move_file` | `POST` | `/api_v2/move_file` | Move a file into another folder. | | `get_file_info` | `POST` | `/api_v2/get_file_info` | Read file metadata. | | `get_folder_info` | `POST` | `/api_v2/get_folder_info` | Read folder metadata. | | `rename_file` | `POST` | `/api_v2/rename_file` | Rename a file. | | `rename_folder` | `POST` | `/api_v2/rename_folder` | Rename a folder. | | `get_esignatures` | `POST` | `/api_v2/get_esignatures` | List e-signatures for a file. | | `esignature_share` | `POST` | `/api_v2/esignature_share` | Share a file for signing. | | `save_user_encryption_keys` | `POST` | `/api_v2/save_user_encryption_keys` | Store a user's public encryption keys (E2E). | | `save_file_encryption_key` | `POST` | `/api_v2/save_file_encryption_key` | Store the wrapped encryption key for a file (E2E). | | `get_file_encryption_key` | `POST` | `/api_v2/get_file_encryption_key` | Retrieve the wrapped encryption key for a file (E2E). | | `save_upload_encryption_key` | `POST` | `/api_v2/save_upload_encryption_key` | Store the wrapped encryption key for a folder (E2E). | | `get_upload_encryption_key` | `POST` | `/api_v2/get_upload_encryption_key` | Retrieve the wrapped encryption key for a folder (E2E). | | `get_share_encryption_key` | `POST` | `/api_v2/get_share_encryption_key` | Get a re-wrapped key for sharing a file/folder with another user (E2E). | Restore/undelete is not a V2 action; use the legacy scripts `api/undelete_folder.php` and `api/undelete_file.php` (see "Legacy V1 Endpoints"). The metadata/listing actions `get_file_info`, `get_folder_info`, and `get_folder_content` accept an optional `get_access_rights` flag that adds an `access_rights` object to each item (see those endpoints). ## Quick Start For new integrations, use the [V2 storage quickstart](filesfm_api_storage.md), with token authentication in POST form fields. DVS uses a separate [JSON API and tasks quickstart](filesfm_api_dvs_tasks.md); MCP Bearer auth is not REST V2 auth. V2 does not decode arbitrary JSON request bodies. Use `application/x-www-form-urlencoded` or multipart upload as documented. V2 errors can have an empty body; check HTTP status before parsing JSON. No universal REST request quota or idempotency-key contract is published. Reconcile uncertain writes before retrying. A legacy create -> upload -> list -> download flow for existing V1 integrations. For new integrations, use the V2 quickstart linked above. Passwords in URL examples below are compatibility illustrations; use token authentication in POST fields for automation. ### 1. Create a folder ```text GET /api/get_upload_id.php ``` ```text https://api.files.fm/api/get_upload_id.php?user=demo&pass=demo&folder_name=my_folder_name&access_type=LINK ``` The response includes a folder `hash` and keys (`add_key`, `delete_key`). Store them for later calls. ```json {"hash":"k7m2q9r4t6v8w3xy","delete_key":"4f27a1d86c90be3571432aef69d8c504","add_key":"a93c520b7408ed16f25d69401b7ace38","UserID":11315} ``` Public folder link: `https://files.fm/u/{folder_hash}`. Public file link: `https://files.fm/f/{file_hash}`. ### 2. Upload a file ```text POST /save_file.php?up_id={folder_hash}&key={add_key}&get_file_hash ``` ```bash curl -i -F 'file=@/tmp/local-file.txt' 'https://api.files.fm/save_file.php?up_id=k7m2q9r4t6v8w3xy&key=a93c520b7408ed16f25d69401b7ace38&get_file_hash' ``` Upload one file per request. With `get_file_hash`, the response is the uploaded file hash. ### 3. List folder contents ```text GET /api/get_file_list_for_upload.php?hash={folder_hash}&include_folders=1 ``` ### 4. Download a file ```text GET /down.php?i={file_hash} ``` ### 5. Log in (session + ROOT keys) ```text GET|POST /api/login.php?user=demo&pass=demo ``` ## ROOT Folder and Account Info Every account has a single ROOT folder: `DisplayName = "ROOT"`, `ParentID = 0`. It is always private, cannot be renamed or deleted, and is the implicit parent when `create_folder` is called without `parent_folder_hash`. To obtain the ROOT hash/keys and account details, authenticate and request user data. Calling `api/authenticate_user.php` (standalone, with any auth method) returns a `UserData` object. Add `get_user_access_token` to also mint an API token. ```text GET https://api.files.fm/api/authenticate_user.php?user=demo&pass=demo&get_user_access_token=1 ``` `UserData` fields: | Field | Description | | --- | --- | | `ID` | User id. | | `UserName` | Username. | | `Email` | Account email. | | `first_name`, `last_name` | Account holder name. | | `avatar_path`, `profile_background` | Public profile assets. | | `root_upload_hash` | Hash of the ROOT folder. | | `root_upload_key` | ROOT folder delete/edit key. | | `root_add_key` | ROOT folder add-files key. | | `mobile_camera_upload_hash` | Hash of the mobile camera upload folder, if any. | | `current_location_country_code` | Lowercased country code derived from the request IP. | | `file_size_limit` | Per-file upload size limit (bytes) for the account. | | `queue_size_limit` | Upload queue size limit for the account. | | `user_access_token` | Present only when `get_user_access_token` was requested. | Note: the lightweight `api/login.php` endpoint returns the ROOT info as a plain-text cookie string (`PHPSESSID`, `user`, `hash`, `ID`, `email`, `root_upload_hash`, `root_upload_key`), not JSON. Use `api/authenticate_user.php` for the structured `UserData` object. ## REST Endpoints ### create_folder Creates a new folder in the user's account and sets its access rights. ```text POST https://api.files.fm/api_v2/create_folder ``` Authorization: mandatory. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `parent_folder_hash` | string | No | Parent folder hash. If omitted, the new folder is created in the authenticated user's ROOT folder. ROOT exists for all accounts and contains metadata such as `subfolder_count` and `file_count`. | | `folder_name` | string | No | Folder name. If omitted, a random name is generated in `YYYY-MM-DD_folderhash` format. If the same name exists in the target path, Files.fm adds a suffix such as `NAME (1)`. | | `access_type` | string | No | `LINK` or `PRIVATE`. `LINK` allows read/list access to anyone who knows the hash/link. `PRIVATE` allows access only to the owner and users with shared rights. | Example: ```bash curl -sS --max-time 25 -X POST 'https://api.files.fm/api_v2/create_folder' \ -d 'user=YOUR_USERNAME' \ -d 'user_access_token=YOUR_API_TOKEN' \ -d 'folder_name=MY_FOLDER_NAME' \ -d 'access_type=PRIVATE' ``` Success response: ```json { "folder_hash": "k7m2q9r4t6v8w3xy", "folder_edit_key": "xxxxxxxx", "folder_upload_key": "xxxxx", "folder_name": "Name given" } ``` Response fields: | Name | Type | Description | | --- | --- | --- | | `folder_hash` | string | Identifier of the created folder. | | `folder_edit_key` | string | Key that grants permission to modify the folder. | | `folder_upload_key` | string | Key that grants permission to upload files to the folder. | | `folder_name` | string | Final folder name. If the requested name already existed, this may include a uniqueness suffix. | Folder access link example: ```text https://files.fm/u/{folder_hash} ``` Errors: standard status codes from the REST overview. ### upload_file Uploads a file to an existing folder. Create a target folder before uploading. Do not upload files directly to ROOT; it is always private. Upload limits may apply by account tier. ```text POST https://api.files.fm/api_v2/upload_file Content-Type: multipart/form-data ``` Authorization: optional if `folder_upload_key` is provided and valid; otherwise authenticate the user. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `folder_hash` | string | Yes | Target folder hash. | | `folder_upload_key` | string | No | Folder key that grants upload permission without full folder access. Required when not authenticating and using a known folder upload key. | | `file` | binary | Yes | File content. PDF examples also show multipart fields such as `Filedata` and `fileToUpload`; use `file` unless your integration is based on an older example flow. | | `watermark_text` | string | No | Text watermark for JPG/PNG uploads. | | `watermark_position` | string | No | `TOP_LEFT`, `TOP_RIGHT`, `BOTTOM_LEFT`, or `BOTTOM_RIGHT`. | | `watermark_color` | string | No | Watermark color. Default: `F0FF3C`. | Upload using folder upload key: ```bash curl -X POST 'https://api.files.fm/api_v2/upload_file' \ -F 'folder_hash=TARGET_FOLDER_HASH' \ -F 'folder_upload_key=TARGET_ADD_KEY' \ -F 'file=@/path/to/local/file.pdf' ``` Upload using account credentials/token: ```bash printf 'upload_file API test at %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" | \ curl -sS --max-time 40 -X POST 'https://api.files.fm/api_v2/upload_file' \ -F 'user=YOUR_USERNAME' \ -F 'user_access_token=YOUR_API_TOKEN' \ -F 'folder_hash=TARGET_FOLDER_HASH' \ -F 'file=@-;filename=api_v2_upload_test.txt;type=text/plain' ``` Success response: ```json { "file_hash": "p4s7u2w9x3y6z8ab", "file_delete_key": "70f25e31fa7963c4a2bb761e1f9d8095", "file_name": "api_v2_upload_test (2).txt", "timestamp_uploaded": 1777638342, "timestamp_last_modified": 1777638342 } ``` Response fields: | Name | Type | Description | | --- | --- | --- | | `file_hash` | string | Identifier of the uploaded file. | | `file_delete_key` | string | Opaque file edit/delete capability. Newly generated values contain 32 lowercase hexadecimal characters; preserve legacy shorter values unchanged. | | `file_name` | string | Final stored file name. If a duplicate exists, Files.fm may add a suffix such as `(1)`. | | `timestamp_uploaded` | integer | Upload timestamp. | | `timestamp_last_modified` | integer | Last-modified timestamp. | Additional upload-specific errors: | Status | Message | Meaning | Action | | --- | --- | --- | --- | | `490` | Virus detected | Uploaded file was detected as infected and was not stored. | Do not retry unchanged; show an appropriate error. | | `491` | File Name Double Extension | File name contains a double extension, for example `invoice.pdf.exe`. | Do not retry unchanged; show an appropriate error. | | `492` | Folder Size Limit Exceeded | Upload would exceed the folder size limit. | Do not retry unchanged; reduce size or use a suitable account plan. | | `493` | File Size Limit Exceeded | Uploaded file exceeds the file size limit. | Do not retry unchanged; reduce size or use a suitable account plan. | | `494` | Partial upload | Full-size file was not received. | Retry after checking client/network timeout limits. | Documented file-size limits: | Account type | File-size limit | | --- | --- | | Enterprise | 50 GB or larger by agreement | | Business | 50 GB or larger by agreement | | Pro | 20 GB | | Lifetime | 10 GB | | Personal | 10 GB | | Free | 5 GB | Documented folder-size limits: Enterprise, Business, Pro, Lifetime, and Personal are unlimited; Free is 5 GB. ### download_file Downloads raw file contents by file hash. ```text POST https://api.files.fm/api_v2/download_file ``` Authorization: | File access | Authorization | | --- | --- | | `PRIVATE` | Required. | | `LINK` | Not required. The file can be downloaded or embedded without authentication. | Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `file_hash` | string | Yes | File hash. | Response: - Success: raw binary file contents. - `Content-Type`: automatically set based on file type. - `Content-Disposition`: inline or attachment depending on client and access settings. Public LINK example: ```text https://files.fm/f/demo ``` Legacy V1 compatibility endpoint, available but not recommended for new integrations: ```text https://api.files.fm/down.php?i=p4s7u2w9x3y6z8ab ``` Direct file path links may also be used where configured: ```text https://files.fm/pa/username/folder1/subfolder/filename.jpg ``` Errors: standard status codes from the REST overview. ### delete_folder Deletes a folder from a user account. ```text POST https://api.files.fm/api_v2/delete_folder ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `folder_hash` | string | Yes | Folder hash to delete. | Response: ```json {"status":"ok"} ``` or: ```json {"status":"error"} ``` To restore a deleted folder, use the legacy `api/undelete_folder.php` script (see "Legacy V1 Endpoints"). Undelete is available for PRO, BUSINESS, and ENTERPRISE accounts. Errors: standard status codes from the REST overview. ### delete_file Deletes a file. ```text POST https://api.files.fm/api_v2/delete_file ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `file_hash` | string | Yes | File hash to delete. | Response: no additional data documented. To restore a deleted file, use the legacy `api/undelete_file.php` script (see "Legacy V1 Endpoints"). Undelete is available for PRO, BUSINESS, and ENTERPRISE accounts. Errors: standard status codes from the REST overview. ### get_folder_content Returns the direct contents of a folder, including files and subfolders. ```text POST https://api.files.fm/api_v2/get_folder_content ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `folder_hash` | string | Yes | Folder hash whose contents should be listed. | | `get_access_rights` | bool | No | When truthy, each item also includes an `access_rights` object (see below). | Response: JSON array of file/folder objects. ```json [ { "type": "file", "hash": "p4s7u2w9x3y6z8ab", "name": "example.pdf", "size": 12345, "timestamp_uploaded": 1777638342, "timestamp_last_modified": 1777638342, "delete_key": "xxxxxx", "add_files_key": null }, { "type": "folder", "hash": "c8d3f7h2j9k4m6np", "name": "Subfolder", "size": 0, "timestamp_uploaded": 1777638342, "timestamp_last_modified": 1777638342, "delete_key": "yyyyyy", "add_files_key": "zzzzz" } ] ``` Response fields: | Name | Type | Description | | --- | --- | --- | | `type` | string | `file` or `folder`. | | `hash` | string | File or subfolder hash. | | `name` | string | File or subfolder name. | | `size` | integer | File or folder size in bytes. | | `timestamp_uploaded` | integer | Upload timestamp. | | `timestamp_last_modified` | integer | Last modification timestamp. | | `delete_key` | string | File or folder edit/delete key if available. | | `add_files_key` | string | Folder upload/add key for subfolders if available. | | `access_rights` | object | Only when `get_access_rights` is set. For folders: `allow_view`, `allow_delete`, `allow_add`, `allow_edit`, `allow_download`. For files: `allow_view`, `allow_delete`, `allow_edit`, `allow_download`. | Errors: standard status codes from the REST overview. ### move_file Moves a file into another folder in the same account. ```text POST https://api.files.fm/api_v2/move_file ``` Authorization: mandatory. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `file_hash` | string | Yes | File hash to move. | | `folder_hash` | string | Yes | Target folder hash. | Response: no additional data documented. Errors: standard status codes. Status `405` may be returned if the target folder does not exist or the operation is not allowed. ### get_file_info Returns metadata for a file. ```text POST https://api.files.fm/api_v2/get_file_info ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `file_hash` | string | Yes | File hash. | | `get_access_rights` | bool | No | When truthy, the response also includes an `access_rights` object (`allow_view`, `allow_delete`, `allow_edit`, `allow_download`). | Success response: ```json { "hash": "p4s7u2w9x3y6z8ab", "name": "example.pdf", "size": 12345, "timestamp_uploaded": 1777638342, "timestamp_last_modified": 1777638342, "delete_key": "xxxxxx", "parent_folder_hash": "c8d3f7h2j9k4m6np", "access_type": "LINK", "password_protected": false, "price_set": false } ``` Response fields: | Name | Type | Description | | --- | --- | --- | | `hash` | string | File hash. | | `name` | string | File name. | | `size` | integer | File size in bytes. | | `timestamp_uploaded` | integer | Upload timestamp. | | `timestamp_last_modified` | integer | Last modification timestamp. | | `delete_key` | string or null | File edit/delete key. `null` is returned if the caller has no delete permission. | | `parent_folder_hash` | string or null | Parent folder hash. `null` is returned if the caller cannot view the parent folder. | | `access_type` | string | File access type (`PRIVATE`, `LINK`, `PASSWORD`, `PUBLIC`). If the file has no own access type, the parent folder's access type is returned. | | `password_protected` | boolean | `true` when the parent folder has a password set (the password value is never returned). | | `price_set` | boolean | `true` when the file has a download/sale price configured in `file_download_price` (the price amount is not returned). | Errors: standard status codes from the REST overview. ### get_folder_info Returns metadata for a folder. ```text POST https://api.files.fm/api_v2/get_folder_info ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `folder_hash` | string | Yes | Folder hash. | | `get_access_rights` | bool | No | When truthy, the response also includes an `access_rights` object (`allow_view`, `allow_delete`, `allow_add`, `allow_edit`, `allow_download`). | Success response: ```json { "hash": "k7m2q9r4t6v8w3xy", "name": "Folder name", "size": 12345, "timestamp_uploaded": 1777638342, "timestamp_last_modified": 1777638342, "delete_key": "xxxxxx", "parent_folder_hash": "c8d3f7h2j9k4m6np", "add_files_key": "zzzzz", "file_count": 10, "subfolder_count": 2, "access_type": "LINK", "password_protected": false, "price_set": false } ``` Response fields: | Name | Type | Description | | --- | --- | --- | | `hash` | string | Folder hash. | | `name` | string | Folder name. | | `size` | integer | Folder size in bytes. | | `timestamp_uploaded` | integer | Upload/creation timestamp. | | `timestamp_last_modified` | integer | Last modification timestamp. | | `delete_key` | string or null | Folder edit/delete key. `null` is returned if the caller cannot delete the folder. | | `parent_folder_hash` | string or null | Parent folder hash. `null` is returned if the caller cannot view the parent folder. | | `add_files_key` | string or null | Upload/add key. `null` is returned if the caller cannot modify the folder. | | `file_count` | integer | Number of files in the folder and subfolders. | | `subfolder_count` | integer | Number of subfolders. | | `access_type` | string | Folder access type (`PRIVATE`, `LINK`, `PASSWORD`, `PUBLIC`). | | `password_protected` | boolean | `true` when the folder has a password set (the password value is never returned). | | `price_set` | boolean | `true` when the folder has a download/sale price configured in `file_download_price` (the price amount is not returned). | Errors: standard status codes from the REST overview. ### get_esignatures Returns e-signature records for a file. ```text POST https://api.files.fm/api_v2/get_esignatures ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `file_hash` | string | Yes | File hash whose e-signatures should be returned. | | `from_date` | string | No | Inclusive start date filter for signature timestamps. | | `to_date` | string | No | Inclusive end date filter for signature timestamps. | Success response: ```json [ { "file_hash": "p4s7u2w9x3y6z8ab", "signing_timestamp": "xxxxxxxxx", "signer_name": "Signer Name" } ] ``` Response fields: | Name | Type | Description | | --- | --- | --- | | `file_hash` | string | File hash. | | `signing_timestamp` | string | Datetime when the file was signed. | | `signer_name` | string | Signer's name and surname. | Errors: standard status codes from the REST overview. ### esignature_share Shares a file to be signed by other users. Optional `next_share_emails` can prefill the next share dialog so the recipient can re-share the file. ```text POST https://api.files.fm/api_v2/esignature_share ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `file_hash` | string | Yes | File hash that should be shared for signing. | | `text` | string | No | Message included in the sharing email. | | `emails` | string[] | Yes | Recipients that receive the signing request email. | | `next_share_emails` | string[] | No | Email addresses prefilled for recipients that may re-share the signing request. | Success response: ```json { "status": "ok", "sent_emails": "person@example.com", "next_share_emails": "next@example.com" } ``` Response fields: | Name | Type | Description | | --- | --- | --- | | `status` | string | Request status. | | `sent_emails` | string | Comma-separated list of emails that received the signing request. | | `next_share_emails` | string | Comma-separated list of emails prefilled for the next share step. | Errors: standard status codes from the REST overview. ### rename_file Renames an existing file and returns the final stored filename. ```text POST https://api.files.fm/api_v2/rename_file ``` Authorization: required. Supported auth methods are the same as other API V2 actions, including `user` + `pass`, valid session, and token/session flows supported by Files.fm authentication. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `file_hash` | string | Yes | File hash to rename. | | `file_name` | string | Yes | Desired new file name. | Access and behavior: - Caller must have edit rights for the file. - Deleted or missing files return an error. - The filename is normalized and made unique inside the folder. - If the requested name equals the current name, API returns success with unchanged name. Success response: ```json { "file_hash": "p4s7u2w9x3y6z8ab", "file_name": "renamed_from_curl_test.txt" } ``` `file_name` is the final name after normalization and uniqueness handling. Common errors: | Status | Meaning | | --- | --- | | `400 Bad Request` | Missing `file_hash` or `file_name`. | | `401 Unauthorized` | Caller is not authenticated. | | `405 Method Not Allowed` | File not found or caller has no edit permission. | | `500 Internal Server Error` | Unexpected backend error. | Example: ```bash curl -X POST 'https://api.files.fm/api_v2/rename_file' \ --data-urlencode 'file_hash=YOUR_FILE_HASH' \ --data-urlencode 'file_name=New file name.pdf' \ --data-urlencode 'user=YOUR_USERNAME' \ --data-urlencode 'pass=YOUR_PASSWORD' ``` ### rename_folder Renames an existing folder that the authenticated user is allowed to edit. ```text POST https://api.files.fm/api_v2/rename_folder ``` Authorization: required. Parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `folder_hash` | string | Yes | Folder hash to rename. | | `folder_name` | string | Yes | Desired new folder name. | | `name` | string | No | Alias for `folder_name`. If both are sent, `folder_name` is used. | Access and behavior: - Caller must be authenticated. - Folder must exist and must not be deleted. - Caller must have folder `allow_edit` rights. - ROOT folder (`ParentID = 0`) cannot be renamed. - Folder name is normalized and made unique inside the parent folder. - Successful rename triggers the standard folder change event pipeline, including ETag/update side effects. Success response: ```json { "folder_hash": "k7m2q9r4t6v8w3xy", "folder_name": "rf_evt_renamed_1777640861" } ``` `folder_name` is the final name after normalization and uniqueness handling. Common errors: | Status | Meaning | | --- | --- | | `400 Bad Request` | Missing `folder_hash` or `folder_name`. | | `401 Unauthorized` | Authentication missing or invalid. | | `405 Method Not Allowed` | Folder does not exist, user has no edit rights, or root folder rename was attempted. | | `500 Internal Server Error` | Unexpected server-side failure, such as DB/event processing error. | Example: ```bash curl -X POST 'https://api.files.fm/api_v2/rename_folder' \ --data-urlencode 'folder_hash=YOUR_FOLDER_HASH' \ --data-urlencode 'folder_name=New folder name' \ --data-urlencode 'user=YOUR_USERNAME' \ --data-urlencode 'pass=YOUR_PASSWORD' ``` ## End-to-End Encryption Key Endpoints These advanced V2 actions support client-side end-to-end encryption (E2E) of files and folders. They store and retrieve wrapped (encrypted) symmetric keys and the public keys needed to unwrap them. Files.fm uses a hybrid post-quantum scheme: each user publishes a KEM public key (key encapsulation), a DSA public key (signatures), plus classical `x25519` and `ed25519` public keys. The server only stores public keys and already-wrapped key material; it never sees plaintext content keys. These endpoints are primarily used by the Files.fm web client. They are documented here for completeness; most integrations do not need them. Common rules for all six actions: - `POST` to `https://api.files.fm/api_v2/{action}`. - Authorization is required, and `userID` must equal the authenticated user's id, otherwise `403`. - Standard error status codes apply (`400` missing parameter, `403` mismatch/permission, `404` not found, `500` failure). ### save_user_encryption_keys Stores the authenticated user's public encryption keys. ```text POST https://api.files.fm/api_v2/save_user_encryption_keys ``` | Name | Type | Required | Description | | --- | --- | --- | --- | | `userID` | integer | Yes | Must match the authenticated user. | | `kem` | string | Yes | KEM public key. | | `dsa` | string | Yes | DSA public key. | | `x25519` | string | Yes | X25519 public key. | | `ed25519` | string | Yes | Ed25519 public key. | Success response: ```json {"success": true} ``` ### save_file_encryption_key Stores the wrapped encryption key for a file (owner is both sender and receiver). Marks the file as encrypted. Requires edit rights on the file. ```text POST https://api.files.fm/api_v2/save_file_encryption_key ``` | Name | Type | Required | Description | | --- | --- | --- | --- | | `userID` | integer | Yes | Must match the authenticated user. | | `fileHash` | string | Yes | Target file hash. | | `encryptionKey` | string | Yes | Wrapped (encrypted) content key. | | `keySignature` | string | Yes | Signature over the wrapped key. | | `fileSignature` | string | No | Optional signature over the file. | Success response: ```json {"success": true} ``` ### get_file_encryption_key Returns the wrapped encryption key and the sender's public keys for a file. Requires download rights on the file. Set `isEncryptedFolder` when the file lives in an encrypted folder so the owner-created key is returned. ```text POST https://api.files.fm/api_v2/get_file_encryption_key ``` | Name | Type | Required | Description | | --- | --- | --- | --- | | `userID` | integer | Yes | Must match the authenticated user. | | `fileHash` | string | Yes | Target file hash. | | `isEncryptedFolder` | bool | No | If true, fetch the key created by the encrypting user. | Success response: ```json { "encryptionKey": "...", "keySignature": "...", "fileSignature": "...", "senderPublicKEMKey": "...", "senderPublicDSAKey": "...", "senderPublicX25519Key": "...", "senderPublicED25519Key": "...", "senderUserID": 11315 } ``` ### save_upload_encryption_key Stores the wrapped encryption key for a folder (upload). Marks the folder as encrypted. Requires edit rights on the folder. ```text POST https://api.files.fm/api_v2/save_upload_encryption_key ``` | Name | Type | Required | Description | | --- | --- | --- | --- | | `userID` | integer | Yes | Must match the authenticated user. | | `uploadHash` | string | Yes | Target folder hash. | | `encryptionKey` | string | Yes | Wrapped (encrypted) content key. | | `keySignature` | string | Yes | Signature over the wrapped key. | Success response: ```json {"success": true} ``` ### get_upload_encryption_key Returns the wrapped encryption key and the sender's public keys for a folder. Requires download rights on the folder. Set `isEncryptedFolder` to fetch the owner-created key. ```text POST https://api.files.fm/api_v2/get_upload_encryption_key ``` | Name | Type | Required | Description | | --- | --- | --- | --- | | `userID` | integer | Yes | Must match the authenticated user. | | `uploadHash` | string | Yes | Target folder hash. | | `isEncryptedFolder` | bool | No | If true, fetch the key created by the encrypting user. | Success response: ```json { "encryptionKey": "...", "keySignature": "...", "senderPublicKEMKey": "...", "senderPublicDSAKey": "...", "senderPublicX25519Key": "...", "senderPublicED25519Key": "...", "senderUserID": 11315 } ``` ### get_share_encryption_key Returns the caller's wrapped key for a file or folder plus the recipient's public keys, so the client can re-wrap the content key for sharing. Provide exactly one of `fileHash` or `uploadHash`. Requires download rights on the shared item. ```text POST https://api.files.fm/api_v2/get_share_encryption_key ``` | Name | Type | Required | Description | | --- | --- | --- | --- | | `userID` | integer | Yes | Must match the authenticated user. | | `receiverEmail` | string | Yes | Email of the user to share with (must have published encryption keys). | | `fileHash` | string | Conditional | File hash to share (use this or `uploadHash`). | | `uploadHash` | string | Conditional | Folder hash to share (use this or `fileHash`). | Success response: ```json { "encryptionKey": "...", "keySignature": "...", "receiverKEMKey": "...", "receiverDSAKey": "...", "receiverX25519Key": "...", "receiverED25519Key": "...", "receiverUserID": 44961 } ``` ## Legacy V1 Endpoints These endpoints predate API V2. They remain fully supported and many have no V2 equivalent (thumbnails, recursive tree, ZIP download, versions, sharing, embedding). They live under `https://api.files.fm/api/{script}.php` (or at the root for `save_file.php`, `down.php`, `thumb*.php`). All of them authenticate through `api/authenticate_user.php`, so any auth method from the Authorization section works. Most return JSON; a few return plain text (noted per endpoint). ### Authentication and session (V1) #### login.php ```text GET|POST https://api.files.fm/api/login.php?user=demo&pass=demo ``` Logs the user in and returns a plain-text cookie string with the session and ROOT folder info: ```text cookie:PHPSESSID=...; user=demo; hash=...; ID=11315; email=demo@example.com; root_upload_hash=...; root_upload_key=... ``` Returns `error: incorrect login` on failure. For a structured JSON `UserData` object (and to mint an API token), call `api/authenticate_user.php` instead (see "ROOT Folder and Account Info"). #### sesslogin.php ```text GET|POST https://api.files.fm/api/sesslogin.php?PHPSESSID=...&username=demo&redirect_url=https://files.fm/filebrowser ``` Cross-domain web login helper: validates the session and `redirect_url` (must be a `failiem.lv`/`files.fm`/`files.me`/`file.fm` host), logs the user in on the current and partner domains, then issues an HTTP redirect. Intended for browser SSO flows, not REST clients. #### validate_session.php ```text GET https://api.files.fm/api/validate_session.php ``` Returns whether the current session is logged in: ```json {"Success": true, "Message": null, "SessionID": "..."} ``` #### test_session.php ```text GET https://api.files.fm/api/test_session.php ``` Prints the `_COOKIE` and `_SESSION` arrays. Used only for internal development/diagnostics. NA. ### Folders: create and keys (V1) #### get_upload_id.php (create folder) ```text GET https://api.files.fm/api/get_upload_id.php ``` Creates a folder and returns its hash and keys. This is the backend behind the V2 `create_folder` action. Parameters: `user`, `pass` (optional), `folder_name` (optional), `parent_hash` (optional, defaults to ROOT when authenticated), `access_type` (`PRIVATE` or `LINK`). ```text https://api.files.fm/api/get_upload_id.php?user=demo&pass=demo&folder_name=My_Folder&parent_hash=k7m2q9r4t6v8w3xy ``` ```json {"hash":"c8d3f7h2j9k4m6np","delete_key":"4f27a1d86c90be3571432aef69d8c504","add_key":"a93c520b7408ed16f25d69401b7ace38","UserID":44961,"FolderName":"my_foldername"} ``` If used anonymously, `UserID` may be `false`, and the anonymous folder is auto-deleted after about one month. #### get_upload_keys.php ```text GET https://api.files.fm/api/get_upload_keys.php?hash=k7m2q9r4t6v8w3xy&user=demo&pass=demo ``` Returns the folder's add and delete keys: ```json {"AddFileKey":"a93c520b7408ed16f25d69401b7ace38","DeleteKey":"4f27a1d86c90be3571432aef69d8c504"} ``` #### get_add_key.php ```text GET https://api.files.fm/api/get_add_key.php?hash=k7m2q9r4t6v8w3xy ``` Requires an active user session. Returns only the folder add key: ```json {"add_key":"a93c520b7408ed16f25d69401b7ace38"} ``` #### get_file_keys.php ```text GET https://api.files.fm/api/get_file_keys.php ``` Returns file edit keys (a folder edit key also works where supported). Contact Files.fm for full documentation and enabled access. #### get_ids_and_keys_from_paths.php ```text POST https://api.files.fm/api/get_ids_and_keys_from_paths.php?upload_hash={parent_hash}&add_key={parent_add_key} ``` Resolves a list of directory paths under a parent folder to their ids and add keys, creating any missing folders. POST `Directories[]` entries (one path per entry). Optional `access_type` (`PRIVATE`, `LINK`, `PASSWORD`, `PUBLIC`) and `addto`/`addhash` for public-upload contexts. ```json { "Success": true, "Directories": { "/test": {"id": 123, "add_key": "a93c520b7408ed16f25d69401b7ace38"}, "/test/sub1": {"id": 234, "add_key": "c016a72f8395bd40e17846ca53f29ed7"} }, "Error": null } ``` ### Files: upload and replace (V1) #### save_file.php (upload) ```text POST https://api.files.fm/save_file.php?up_id={folder_hash}&key={add_key}&get_file_hash ``` Uploads one file (multipart field `file`). This is the backend behind the V2 `upload_file` action. With `get_file_hash`, returns the new file hash. Optional `watermark_text` (and related watermark params) for image uploads. Upload one file per request. ```bash curl -i -F 'file=@/tmp/local-file.txt' 'https://api.files.fm/save_file.php?up_id=k7m2q9r4t6v8w3xy&key=a93c520b7408ed16f25d69401b7ace38&get_file_hash' ``` #### save_file.php (replace) ```text POST https://api.files.fm/save_file.php?up_id={folder_hash}&key={add_key} ``` Pass `do=update` and `replace_file_hash={file_hash}` to replace an existing file. Supports file versions for BUSINESS and ENTERPRISE accounts; the ETag changes after replacement. Contact Files.fm for the full replacement flow. ### Listing and metadata (V1) #### get_file_list_for_upload.php (List folder child items) ```text GET https://api.files.fm/api/get_file_list_for_upload.php?hash={folder_hash}&include_folders=1 ``` Returns direct child files/folders and metadata for one folder (not recursive). This is the backend behind the V2 `get_folder_content` action. ```json [{"hash":"p4s7u2w9x3y6z8ab","name":"File1.jpg","Size":"106361"},{"hash":"q2r5t8v3w7x4y9za","name":"File2.jpg","Size":"62249"}] ``` Empty folder returns `[]`. **Anonymous link access.** This V1 endpoint also accepts unauthenticated requests for folders whose access type is `LINK` or `PUBLIC`: pass only the folder `hash` (no credentials). The listing is strictly view-only — `delete_key`/`add_key` are never returned, `PRIVATE` folders and password-protected folders (without a session password) are rejected, and `PRIVATE` files inside a public folder are filtered out. To deter automated enumeration, anonymous requests are rate-limited per IP (HTTP `429` / `{"error":"rate_limited"}` when exceeded) and intentionally delayed by ~2 seconds each. Authenticated requests (session, `user`+`pass`, or `user`+`user_access_token`) are not throttled or delayed. The V2 `get_folder_content` action does **not** allow anonymous access — it always requires authentication. #### get_upload_tree.php (recursive tree / search) ```text GET https://api.files.fm/api/get_upload_tree.php?Hash={folder_hash}&DeleteKey={folder_delete_key} ``` Returns all subdirectories and their contents recursively, with file/folder metadata. Also used as the basis for folder search. ```json { "error": null, "data": { "Type": "Folder", "ID": "4759592", "Hash": "k7m2q9r4t6v8w3xy", "AddFileKey": "a93c520b7408ed16f25d69401b7ace38", "DeleteKey": "4f27a1d86c90be3571432aef69d8c504", "ParentID": "4962316", "CreatedDate": "2014-02-20 22:55:30", "ModifiedDate": null, "Name": "Example folder", "Children": [ {"Type": "Folder", "ID": "4799549", "Hash": "c8d3f7h2j9k4m6np", "AddFileKey": "c016a72f8395bd40e17846ca53f29ed7", "DeleteKey": "e2836407b5ad19fc83617e20a94d52cb", "ParentID": "4759592", "CreatedDate": "2014-03-01 18:50:13", "ModifiedDate": null, "Name": "Child folder", "Children": false} ] } } ``` #### upload_list.php (ROOT children) ```text GET https://api.files.fm/api/upload_list.php?user=demo&pass=demo ``` Returns the direct children of the user's ROOT folder (metadata only, no add/delete keys): `hash`, `date`, `description`, `files`, `size`, `views`, `valid_to_date`. ```json [{"hash":"k7m2q9r4t6v8w3xy","date":"2012-12-05 19:00:19","description":"","files":"2","size":"168610","views":"1","valid_to_date":""}] ``` #### get_file_info.php Two modes: ```text GET https://api.files.fm/api/get_file_info.php?hash={file_hash}&del_key={file_delete_key} POST https://api.files.fm/api/get_file_info.php (action=get_file_hash_by_name) ``` The GET mode returns rich file metadata (requires the file's delete key): ```json {"success": true, "file_info": {"Name": "photo.jpg", "ContentType": "image/jpeg", "Extension": "jpg", "ExtensionType": "image", "Size": "106361", "RealSize": 106361, "CustomInfo": {"Width": 1024, "Height": 768}}} ``` The POST mode (`action=get_file_hash_by_name`, `upload_hash`, `file_name`; requires a logged-in user with view rights) resolves a file name to its hash: ```json {"success": true, "file_info": {"hash": "p4s7u2w9x3y6z8ab"}} ``` #### get_uploads_exist.php ```text POST https://api.files.fm/api/get_uploads_exist.php ``` Authenticated. POST a JSON body `{"hashes": ["...","..."]}` and receive a map of folder hash to existence (only folders owned by the user with an add key count as existing): ```json {"k7m2q9r4t6v8w3xy": true, "c8d3f7h2j9k4m6np": true, "b6c9d2f5h8j3k7mn": false} ``` #### get_storage_data.php ```text GET https://api.files.fm/api/get_storage_data.php ``` Authenticated. Returns the signed-in user's storage usage and quota (MB): ```json {"success": true, "message": null, "data": {"used_mb": 5.39, "quota_mb": "2000", "available_mb": "2000", "free_mb": 1994.61}} ``` ### Search (web/ajax) #### ajax/search.php ```text GET|POST https://files.fm/ajax/search.php ``` Full-text search across the authenticated user's files and folders (own, shared, in a specific folder, and optionally deleted items). Served from the `files.fm` web host and authenticated by session, so pass `PHPSESSID` as a cookie or query parameter. Storage subscribers are searched via the Sphinx full-text index; other accounts fall back to a SQL `LIKE` search. Two request styles are supported. 1. Simplified API mode (recommended for integrations). Pass `result_format=JSON` and `query`; the script builds the search definition and returns a clean JSON array. Optionally pass `folder_hash` to restrict the search to one folder (subfolders included). When `folder_hash` is omitted or set to ROOT, it searches your own files plus organization-shared files. ```text GET https://files.fm/ajax/search.php?result_format=JSON&query=document&PHPSESSID=YOUR_SESSION_ID ``` 2. Full mode (used by the file browser UI). Pass a URL-encoded `search_json` object plus table paging/sorting parameters. ```text GET https://files.fm/ajax/search.php?search_json=%7B...%7D&PHPSESSID=...&table_page=0&table_size=1000&table_column[3]=1 ``` The decoded `search_json` from the file-browser example: ```json { "Mode": "simple", "Query": "document", "Types": {}, "Sections": { "MY": true, "SHARED_WITH_ME": true, "DELETED": false, "THIS_FOLDER": false }, "ThisFolder": "" } ``` `search_json` fields: | Field | Type | Required | Description | | --- | --- | --- | --- | | `Mode` | string | Yes | `simple` or `advanced`. | | `Query` | string | Yes (simple) | Search phrase, minimum 2 characters. | | `Types` | object | Yes (simple) | Type filter. Keys: `DOCUMENTS`, `IMAGES`, `VIDEO`, `MUSIC`, `FOLDERS`, `FILES` (booleans). Empty `{}` searches all types. | | `Sections` | object | Yes | Where to search. Keys: `MY`, `SHARED_WITH_ME`, `SHARED_WITH_ME_ONLY_ORGANIZATION`, `DELETED`, `THIS_FOLDER` (booleans). At least one must be true. `DELETED` is ignored unless the user is a storage subscriber. | | `ThisFolder` | string | Conditional | Folder hash to search in. Required when `Sections.THIS_FOLDER` is true. | | `Operator` | string | Yes (advanced) | `AND` or `OR`. | | `Criteria` | array | Yes (advanced) | Advanced criteria, each `{ Field, Operator, Query, AdditionalParam }`. Contact Files.fm for the full field/operator list. | Other query parameters: | Name | Type | Description | | --- | --- | --- | | `result_format` | string | `JSON` (clean array, recommended), `OWNCLOUD` (Nextcloud/ownCloud unified-search structure), or omitted for `WEB_TABLESORTER` (pre-rendered HTML rows used by the web UI). | | `query` | string | Simplified-mode search phrase (alternative to `search_json`). | | `folder_hash` | string | Simplified-mode folder restriction. | | `table_size` | integer | Results per page, `1`-`1000` (default `1000`). | | `table_page` | integer | Zero-based page number. | | `table_column[]` | integer | Sort column and direction (`0` = ascending, otherwise descending). Index: `2` = name, `4` = size, `5` = owner, `6` = views; other indexes sort by modification date. | | `NoSubfolders` | bool | Do not search subfolders. | | `get_access_rights` | bool | With `result_format=JSON`, add an `access_rights` object to each item. | | `PHPSESSID` | string | Session id (if not sent as a cookie). | `result_format=JSON` response: a JSON array of result items. ```json [ { "type": "file", "name": "document.pdf", "hash": "p4s7u2w9x3y6z8ab", "size": 12345, "mod_date": "2026-05-01 12:00:00", "path": "/folder/sub/document.pdf", "public_access_type": "PRIVATE", "parent_hash": "k7m2q9r4t6v8w3xy", "content_type": "application/pdf", "file_count": null, "subfolder_count": null, "access_rights": null } ] ``` Response fields (`result_format=JSON`): | Name | Type | Description | | --- | --- | --- | | `type` | string | `file` or `folder`. | | `name` | string | File or folder name. | | `hash` | string | File or folder hash. | | `size` | integer | Size in bytes. | | `mod_date` | string | Last modification datetime. | | `path` | string | Full path of the item. | | `public_access_type` | string | `PUBLIC` (LINK/PUBLIC) or `PRIVATE`. | | `parent_hash` | string | Parent folder hash. | | `content_type` | string or null | MIME type for files; `null` for folders. | | `file_count` | integer or null | File count for folders; `null` for files. | | `subfolder_count` | integer or null | Subfolder count for folders; `null` for files. | | `access_rights` | object or null | Present only with `get_access_rights`. | The default `WEB_TABLESORTER` response is an object `{ "status": "ok", "total_rows": N, "headers": [...], "rows": [...], "page_count": N, "total_time": 0.12 }` where `rows` contain pre-rendered HTML for the web UI. Errors return `{ "status": "error" }`. ### Rename and access type (V1) #### change_file.php (rename file) ```text GET|POST https://api.files.fm/api/change_file.php?file_hash={file_hash}&folder_hash={folder_hash}&file_display_name=NewName.txt ``` Updates a file's display name. ```json {"Success":true,"Error":null,"Message":"Fields updated"} ``` #### change_upload.php (rename folder) ```text GET|POST https://api.files.fm/api/change_upload.php?Hash={folder_hash}&upload_display_name=New%20Name ``` Authenticated; requires edit rights. Renames a folder (`DisplayName`). The ROOT folder cannot be renamed. You may pass `UploadID` instead of `Hash`. ```json {"Success":true,"Error":null,"Message":null} ``` #### upload_access_type.php (change folder access type) ```text GET https://api.files.fm/api/upload_access_type.php?h={folder_hash}&type=PRIVATE ``` Authenticated; requires permission. `type` is one of `PRIVATE`, `PASSWORD`, `LINK`, `PUBLIC`. ```json {"success": true, "error": ""} ``` ### Delete and restore (V1) #### delete_file.php ```text GET|POST https://api.files.fm/api/delete_file.php?hash={file_hash}&del_key={delete_key} ``` Deletes a file by hash and delete key (the key may be the parent folder delete key or the file's own key). This is the backend behind the V2 `delete_file` action. ```json {"success":1} ``` Failure returns `{"success":0}`. Legacy combined endpoint: `GET https://api.files.fm/list.php?i={folder_hash}&k={delete_key}&f={file_hash}`. #### delete_upload.php (delete folder) ```text GET|POST https://api.files.fm/api/delete_upload.php?u={folder_hash}&del_key={delete_key} ``` Deletes a folder (and its files/subfolders) owned by the authenticated user. This is the backend behind the V2 `delete_folder` action. Optional `delete_only_subfolders=1` deletes only the subfolders. Supports `jsonp`/`callback`. ```json {"success":1,"error":0,"deleted_id":1234} ``` Failure returns `{"success":0,"error":1,"deleted_id":null}`. Legacy combined endpoint: `GET https://api.files.fm/list.php?i={folder_hash}&k={delete_key}&t=ok`. #### multi_delete.php ```text POST https://api.files.fm/api/multi_delete.php ``` Authenticated. Deletes many files and folders in one transaction. POST JSON `{"items": {"files": ["..."], "folders": ["..."]}}`, up to 200 items per call. Rolls back if any item fails or the user lacks delete rights. ```json {"success": true, "error": "", "error_code": null} ``` #### undelete_file.php ```text GET|POST https://api.files.fm/api/undelete_file.php?hash={file_hash} ``` Restores a deleted file. Requires a logged-in paying user (storage subscriber). Optional `return_new_path=true` and `return_new_folder_id=true` add path/folder info to the response. ```json {"status":"ok"} ``` #### undelete_folder.php ```text GET|POST https://api.files.fm/api/undelete_folder.php?hash={folder_hash} ``` Restores a deleted folder (and its recoverable files). Requires a logged-in paying user. Optional `return_new_path=true`. Returns `{"status":"ok"}` or `{"status":"error"}`. Undelete is available for PRO, BUSINESS, and ENTERPRISE accounts; the restoration window depends on account type. #### multi_restore.php ```text POST https://api.files.fm/api/multi_restore.php ``` Authenticated. Restores many deleted files/folders owned by the user. POST JSON `{"items": {"files": ["..."], "folders": ["..."]}}`, up to 200 items per call. ```json {"success": true, "error": "", "error_code": null} ``` #### delete_items_from_recyclebin.php (permanent delete) ```text POST https://api.files.fm/api/delete_items_from_recyclebin.php ``` Permanently deletes items from the recycle bin. Authenticated; requires the account `password` for confirmation. POST `password` and `items` = `{"files": ["..."], "folders": ["..."]}`. ```json {"status":"ok"} ``` Incorrect password returns `{"status":"error","error_code":1}`. This is destructive and not recoverable. #### restore_version_file.php (file versions) ```text GET https://api.files.fm/api/restore_version_file.php?hash={file_hash}&del_key={delete_key}&version_id={version_id} ``` Restores a previous version of a file as the current version. Requires a logged-in user. File versions are a BUSINESS/ENTERPRISE feature. ### Download, thumbnails, and previews (V1) #### down.php (download) ```text GET https://api.files.fm/down.php?i={file_hash} ``` Downloads a file by hash. This is the backend behind the V2 `download_file` action. Direct download limits depend on account type and security settings. #### thumb.php (small thumbnail) ```text GET https://api.files.fm/thumb.php?i={file_hash} ``` Small image thumbnail, approximately `400x271 px`. #### Large thumbnails ```text GET https://api.files.fm/thumb_show.php?i={file_or_folder_hash} GET https://api.files.fm/thumb_video_picture.php?i={file_hash} GET https://api.files.fm/thumb_folder.php?i={folder_hash} ``` Large thumbnails (approximately `1500 px`) for images, video poster frames, and folder covers. #### Streamable video preview / document PDF preview Files.fm can generate an MP4 streaming preview (HD or custom quality; watermarked when a price is set) and PDF previews for document/archive formats (DOC, XLS, EDOC, ZIP, etc.). Requires the file hash and enabled access. Contact Files.fm for full documentation. See also the "previews and conversion" integration section. #### Download folder as ZIP ```text GET https://api.files.fm/server_scripts/zip/zip_streamer/upload_zip_streamer.php?uhash={folder_hash} ``` Streams all files in a folder as a ZIP. For password-protected folders add `&password=my_password`. Limits may apply. #### embed_urls.php (path-access URL) ```text GET https://api.files.fm/api/embed_urls.php?i={file_hash} ``` Returns the human-readable path-access URL for a file: ```json {"path_access_url": "https://files.fm/pa/username/folder1/subfolder/filename.jpg"} ``` #### Share links (browser preview) ```text https://files.fm/u/{folder_hash} https://files.fm/f/{file_hash} ``` Localized domains are supported, for example `https://es.files.fm/u/{folder_hash}` and `https://de.files.fm/u/{folder_hash}`. ### Sharing management Files.fm sharing grants named recipients (registered users, email addresses, or — for enterprise organizations — organization user groups) fine-grained access to a file or folder. Access rights are stored per recipient in `upload_sharing`: `CanView`, `CanRead` (download), `CanWrite` (edit), `CanAdd`, `CanDelete`, and `CanShare`. Two endpoints cover the lifecycle: `ajax/share.php` creates shares (and sends notification emails), and `api/sharing.php` lists and adjusts existing shares. Both are session-authenticated (pass `PHPSESSID`); they are the same pipeline the V2 `esignature_share` action builds on. #### ajax/share.php (grant access) ```text POST https://files.fm/ajax/share.php ``` Grants access to one or more recipients on a single file or folder, optionally sends share-notification emails, and (for end-to-end encrypted items) stores per-recipient wrapped encryption keys. Served from the `files.fm` web host and authenticated by session. Target the item with one of: - `file_view_hash` (or `file_hash`) — share a single file. - `upload_hash` — share a folder. Parameters (POST): | Name | Type | Required | Description | | --- | --- | --- | --- | | `items[]` | array | Yes | Recipients. Each entry is an object `{ value, item_type }`. `item_type` is `email` (then `value` is an email address) or `org_user` (then `value` is a Files.fm user id in your organization). | | `file_view_hash` / `file_hash` | string | Conditional | File hash to share (use this or `upload_hash`). | | `upload_hash` | string | Conditional | Folder hash to share. | | `rights[view]` | bool | No | Allow preview/view (maps to `CanView`). | | `rights[download]` | bool | No | Allow download (`CanRead`). | | `rights[write]` | bool | No | Allow edit/upload (`CanWrite`). | | `rights[add]` | bool | No | Allow adding files (`CanAdd`). | | `rights[delete]` | bool | No | Allow delete (`CanDelete`). | | `rights[share]` | bool | No | Allow re-sharing (`CanShare`). | | `description` | string | No | Message included in the share email. | | `submit` | string | Yes | Must be `1` to perform the share; other values validate recipients only. | | `send_share_email` | string | No | `0` to suppress the notification email (default sends). | | `send_notification_once_open` | string | No | `0` to disable the "link opened" notification (default on). | | `save_share_message` | string | No | `1` to save the message as the user's default. | | `date_active_from` / `date_active_to` | string | No | Time-limited access window; `from` must be `<=` `to`. | | `next_share_emails[]` | array | No | Additional emails to chain-share to the same item. | | `external_user_group` | int | No | Enterprise only: external user group that newly added external users are placed in. | | `encryption_key`, `key_signature` | string | No | For E2E-encrypted items: the recipient-wrapped key and its signature. | Granting the delete right (and folder add/write rights) causes the generated share URL to embed the corresponding delete/add keys. Response: ```json { "status": "ok", "items": { "user@example.com": { "item_type": "email", "value": "user@example.com", "email": "user@example.com", "is_valid_email": true, "user_id": 12345, "org_id": 0, "can_email": true, "have_to_send_enterprise_invite_email": false, "enterprise_invite_email_link": "" } }, "message": null, "invalid_emails": "", "unsubscribed_emails": "", "sent_emails": "user@example.com" } ``` `status` is `ok` on success or `error` on failure. `message` may be `null`, `no-access-rights`, `already_shared`, `marked_as_spam`, `free_emails_exhausted`, or `wrong_dates`. `items` is keyed by each recipient's `value`. Security note: the share URLs returned and emailed can embed file/folder delete and add keys depending on the granted rights — treat the response and the emails as sensitive. #### sharing.php (sharing modal and management) ```text GET https://api.files.fm/api/sharing.php?Action={action}&... ``` Session-authenticated endpoint that backs the sharing dialog: it lists existing shares for an item and toggles individual access rights. The session user must hold the `allow_sharing` right on the target item. Select the item with one of `UploadID`, `FileID`, `UploadHash`, `FileHash`, or `DvsDocumentId`. Actions (`Action` parameter): | Action | Returns | Description | | --- | --- | --- | | `GetModalWindow` | `{ Success, HTML }` | Rendered HTML of the share modal for the item. | | `GetSharingTable` | `{ Success, HTML, ItemNameHTML, ProjectTeamInfo }` | Rendered HTML table of all current shares (users, emails, organization user groups, external groups) with each assignee's rights and active dates. Enterprise accounts also receive project team info. | | `UpdateUploadSharing` | `{ Success, AccessRights }` | Toggle a single access right for one assignee. | `UpdateUploadSharing` parameters: | Name | Type | Required | Description | | --- | --- | --- | --- | | `AssigneeType` | string | Yes | One of `USER` (organization user), `ANY_USER`, `EMAIL`, `ORG_USER_GROUP`, `ORG_EXTERN_USER_GROUP`. | | `AssigneeUserID` | int | Yes | Id of the assignee (user id, email id, or group id). | | `AccessRightName` | string | Yes | One of `CanRead`, `CanWrite`, `CanDelete`, `CanView`, `CanShare`, `CanAdd`. | | `AccessRightStatus` | int | Yes | `1` to grant, `0` to revoke. | ```json {"Success": true, "AccessRights": {"CanView": 1, "CanRead": 1, "CanWrite": 0, "CanDelete": 0, "CanShare": 0, "CanAdd": 0}} ``` Setting all rights to `0` effectively removes an assignee's access — there is no separate delete-share endpoint. `GetModalWindow` and `GetSharingTable` return pre-rendered HTML for the web UI rather than structured data, so for programmatic listing build on `UpdateUploadSharing` plus your own bookkeeping. > Organization role/module permissions (the org RBAC table updated by `api/permissions.php`) are a separate concern from per-file sharing and belong to the Organization Management API (out of scope here). #### save_sharing_confirmation.php ```text GET https://api.files.fm/api/save_sharing_confirmation.php?item_type=FILE&item_id={id}&confirm=1 ``` Confirms (or declines) a pending share for the authenticated user (or via a signed `user_id`+`hash` link). `item_type` is `FILE` or `UPLOAD`; set `confirm=1` to accept. Optional `redirect_after_save=1` redirects to the file browser instead of returning JSON. ```json {"Success": true, "Error": null, "Message": null} ``` ### Utility and account (V1) #### Disabled endpoints Account creation via `api/register.php` is disabled. New accounts are created through the website or OAuth/SSO first-login auto-provisioning. ### Other Files.fm APIs Calendar and address-book integrations use the [CalDAV and CardDAV API](filesfm_api_dav.md) at `https://calendar.failiem.lv/`. It supports discovery, event/contact CRUD, and synchronization using DAV XML and iCalendar/vCard data. These are separate protocol endpoints, not REST V2 actions; the REST OpenAPI specification does not describe them. The calendar workspace's `/calendar/api.php` is an internal session/CSRF-based JSON interface, not a documented public REST API. Organization and dealer management has its own separate API, documented in `Files.fm_OrgManagement_API.pdf` (served from the `api/` directory) and implemented by `api/manage_organizations.php`. It is out of scope for this document. See the public [DMS/DVS reference](filesfm_api_dms.md), [MCP connector](filesfm_mcp.md), and [WebDAV/FTP protocols](filesfm_api_protocols.md). Internal web AJAX endpoints are not a substitute for the documented public API; additional integration requirements should be discussed with `support@files.fm`. ## Supplemental Integration Sections The remaining sections come from the same PDF and document related options: embedded e-signature forms, Files.fm AI endpoint usage, pre-built upload forms, embedding, previews, conversion, watermarking, and server-side AI analysis. ## E-Signature Form Integration The Files.fm e-signature form can be integrated into your system using JavaScript and embedded as an iframe within your website or web application. The embedded form communicates with your application through callback events, enabling seamless interaction and status handling during the signing process. This allows users within your IT environment to digitally sign files stored in Files.fm using supported electronic signature methods, including the Latvian State Radio and Television Centre (LVRTC) e-signature and Smart-ID, when enabled for the corresponding business account. In addition to embedded usage, Files.fm also supports generating secure signing links that can be shared with external parties. These links allow recipients to sign documents without requiring access to your internal systems or a Files.fm user account, making the solution suitable for both internal workflows and external document signing scenarios. https://files.fm/api/demo/esigning_api_demo_v.2.0/ The integration example is written in the web programming language PHP and can be downloaded here: https://files.fm/u/4dcrp5wy3 How to use the example: 1) Download the file "esigning_api_demo_v.2.0.zip". 2) Unzip this file and place it on your computer in web server's ( Apache + PHP ) folder. 3) Open the config.php file and enter your Files.fm account username and password. 4) Open the index.php file through your browser. ### Initializing the form To initialize the e-signature form on the client side in the browser, on your page: 1) JavaScript resources must be included (for example, in the HTML head tag of the page): ```html ``` 2) Create a JavaScript object of the class `FailiemlvEsignatureForm`, passing the form settings. All settings are optional. ```js objMyFailiemlvEsignatureForm = new FailiemlvEsignatureForm({ callbacks: { on_signing_finished: function (result) { /* ... */ }, on_form_closed: function (data) { /* ... */ } }, emobile_signing: { open_new_tab: false }, allowed_signing_formats: { edoc: true, pdf: true, asice: false }, default_signing_format: "edoc", signature_validation: { enabled: true }, sign_files_separate: { allow_select: true, default: false } }); ``` #### Form settings parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `callbacks` | object | No | Your JavaScript functions called after specific events in the form. | | `callbacks.on_signing_finished` | function | No | Called when the user finishes signing (see "Receiving the signing result"). | | `callbacks.on_form_closed` | function | No | Called when the user closes the form (see "Receiving the form close event"). | | `emobile_signing` | object | No | Settings for the eParaksts mobile signing process. | | `emobile_signing.open_new_tab` | boolean | No | Whether eParaksts mobile signing opens eparaksts.lv in a new browser tab instead of redirecting the current page. | | `allowed_signing_formats` | object | No | Allowed file signing formats. | | `allowed_signing_formats.edoc` | boolean | No | Allow signing files in `edoc` format. | | `allowed_signing_formats.pdf` | boolean | No | Allow signing files in `pdf` format. | | `allowed_signing_formats.asice` | boolean | No | Allow signing files in `asice` format. | | `default_signing_format` | string | No | Default signing format: `edoc`, `pdf`, or `asice`. | | `signature_validation` | object | No | Settings for checking existing signatures in a file. | | `signature_validation.enabled` | boolean | No | Whether to check and show existing signatures when the form opens. | | `sign_files_separate` | object | No | Settings for signing multiple files: all in one file, or each in a separate file. | | `sign_files_separate.allow_select` | boolean | No | Whether the user may choose the multi-file signing type. | | `sign_files_separate.default` | boolean | No | Default multi-file method. `false` = all in one file; `true` = each in a separate file. | ### Opening the form Before opening the form on the server side of the client system, the files to be signed must be uploaded using the Files.fm data API. If you need to sign multiple files : 1) a new folder must be created (see Request for folder creation - create_folder ); 2) The files to be signed must be uploaded to the created folder (see Request for file upload - upload_file ). If you need to sign a single file : 1) select an existing folder in your Files.fm account or create a new folder (see Request for folder creation - create_folder ) to which to upload the file; 2) The file to be signed must be uploaded to the created or existing folder (see Request for file upload - upload_file ). The form can be opened by calling the showForm function on the previously created object of the class FailiemLv_eSignature_Form . To sign multiple files, pass the folder of the uploaded files: ```js objMyFailiemlvEsignatureForm.showForm( folder_hash, folder_edit_key ); ``` To sign a single file, pass the file instead: ```js objMyFailiemlvEsignatureForm.showForm( null, null, file_hash, file_delete_key ); ``` #### Function parameters | Name | Type | Required | Description | | --- | --- | --- | --- | | `folder_hash` | string | Conditional | Folder identifier. Required when signing multiple files. | | `folder_edit_key` | string | Conditional | Folder edit key. Required when signing multiple files. | | `file_hash` | string | Conditional | File identifier. Required when signing a single file. | | `file_delete_key` | string | Conditional | File edit/delete key. Required when signing a single file. | ### Receiving the signing result As mentioned above, Initializing the Form , when initializing the form (creating a JavaScript object of the form), you must specify a callback function on your page in the parameters , which should be called when the signing process is complete ("on_signing_finished"). When calling this function, its first parameter will be passed an object with data about the signing result. The signing can be successful or unsuccessful. #### Signing result object data | Name | Type | Description | | --- | --- | --- | | `success` | boolean | Whether signing was successful. | | `folder_hash` | string | Folder identifier of the signed files. Only when signing multiple files. | | `file_hash` | string | File identifier of the signed file. Only when signing a single file. | | `signed_file_format` | string | Format/extension of the signed file. Only on success. | | `signed_file_hash` | string | Identifier of the signed file. Returned on success when signing a single file, or multiple files as "all in one file". | | `signed_file_delete_key` | string | Signed file edit/delete key. Same conditions as `signed_file_hash`. | | `signed_file_hashes` | array | Array of signed file data. Returned on success when signing multiple files "each file individually". | | `signed_file_hashes[].original_file_name` | string | Name of the original file. | | `signed_file_hashes[].signed_file_hash` | string | Identifier of the signed file. | | `signed_file_hashes[].signed_file_delete_key` | string | Signed file edit/delete key. | Upon receiving a notification that the signing has been completed, if the signing has been successful, you can download the signed files from the Files.fm system and save them to your system . It is recommended to download the file on the server side of your system with an API request (see Request for file download - download_file ). After downloading the signed files, it is recommended to delete the previously uploaded file folder or file from the Files.fm system by making a Delete API request from your system server . (see Request for deleting a folder - delete_folder ). The folder or file identifier is passed in the function call parameter ("folder_hash", "file_hash"). It is also recommended to delete the folder after an unsuccessful signing. ### Receiving the form close event As mentioned above , Initializing a Form , when initializing a form (creating a JavaScript object of the form), you must specify a callback function on your page in the parameters that should be called when the user closes the form (" on_form_closed "). When calling this function, its first parameter will be passed an object with data about the file folder or file opened in the form: - if signing is performed for multiple files , an object with data about the folder opened in the form will be submitted; - If signing is performed for a single file , an object with data about the file opened in the form will be passed. #### File or folder data opened in the form | Name | Type | Description | | --- | --- | --- | | `folder_hash` | string | Identifier of the folder of files to be signed. | | `file_hash` | string | Identifier of the file to be signed. | Upon receiving a form closing notification, it is recommended to delete the previously uploaded file folder or file from the Files.fm system via your system's server side with an API request (see Request to delete a folder - delete_folder ). The folder or file identifier is passed in the function call parameter ("folder_hash", "file_hash"). ## Files.fm AI API (Ollama-compatible) Files.fm provides a hosted Ollama-compatible Large Language Model API at `https://ai.files.fm`. It is a drop-in replacement for Ollama's HTTP interface, so existing tools, SDKs, and automation platforms (such as n8n) can use Files.fm-hosted models without running local GPU infrastructure, in a GDPR-compliant way. It follows the standard Ollama `/api/generate` and `/api/chat` schema and semantics. For automations, prefer the `https://ai.files.fm/api/generate` endpoint. Generate your API keys at `https://files.fm/account#api_keys`. #### Supported models | Model | Purpose | | --- | --- | | `gpt-oss:20b` | General-purpose text generation and reasoning (text only, tools, thinking). | | `granite4.2:8b` | Compact text model with tools and thinking. | | `gemma4:12b` | Multimodal model: image, audio and text input, tools, thinking. | | `phi4:14b` | Plain text completion, no tools or thinking. | | `qwen3.8:27b` | Vision-language model (image + text understanding), tools, thinking. | | `muse-glimmer:30b` | Multimodal model: image + text understanding, tools, thinking. | If you need other models, ask us. #### Authentication Authentication uses a Bearer token in the HTTP `Authorization` header: ```text Authorization: Bearer UserID:RandomString ``` - `UserID` is the Files.fm user id (for example, a service account used for automation). - `RandomString` is the API token (generated in account settings or issued by a Files.fm account manager). - Keep tokens confidential and stored securely. Disable old tokens and generate new ones if a token is lost. #### Endpoints ```text POST https://ai.files.fm/api/generate POST https://ai.files.fm/api/chat ``` Headers: ```text Content-Type: application/json Authorization: Bearer UserID:RandomString ``` #### Request format (Ollama-compatible) The request body follows the standard Ollama format (see `https://docs.ollama.com/api/introduction`). | Field | Description | | --- | --- | | `model` | Model name (see supported models above). | | `prompt` | Prompt text (used by `/api/generate`). | | `messages` | Chat messages array of `{role, content}` (used by `/api/chat`). | | `think` | Reasoning level (`low` recommended for automation). | | `stream` | Streaming mode (set `false` for most integrations). | Example — `/api/generate` with `gpt-oss:20b`: ```bash curl --location 'https://ai.files.fm/api/generate' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer UserID:RandomString' \ --data '{ "model": "gpt-oss:20b", "prompt": "What is Files.fm? Answer in one sentence.", "think": "low", "stream": false }' ``` Example — `/api/chat`: ```bash curl --location 'https://ai.files.fm/api/chat' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer UserID:RandomString' \ --data '{ "model": "gpt-oss:20b", "messages": [ { "role": "user", "content": "What is Files.fm? Answer in one sentence." } ], "think": "low", "stream": false }' ``` Example — `gemma4:12b`: ```bash curl --location 'https://ai.files.fm/api/chat' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer UserID:RandomString' \ --data '{ "model": "gemma4:12b", "messages": [ { "role": "user", "content": "What is Files.fm? Answer in one sentence." } ], "stream": false }' ``` #### `/api/generate` response format When `stream` is `false`, the API returns a JSON object (see `https://docs.ollama.com/api/generate`): ```json { "response": "Generated text output from the model.", "done": true } ``` | Field | Type | Description | | --- | --- | --- | | `response` | string or object | Generated output from the model. | | `done` | boolean | Indicates that generation has completed. | #### `/api/chat` response format The response matches Ollama's standard structure: ```json { "message": { "role": "assistant", "content": "Files.fm is a secure cloud storage and file sharing platform for individuals and businesses." } } ``` The generated content is available at `message.content`. ### Using Files.fm AI with n8n Use Files.fm AI inside n8n for automation tasks such as text summarization, classification, OCR post-processing, internal AI assistants, and document/form analysis. The HTTP Request node provides maximum compatibility and control. n8n HTTP Request configuration: | Setting | Value | | --- | --- | | Method | `POST` | | URL | `https://ai.files.fm/api/generate` | | Header `Content-Type` | `application/json` | | Header `Authorization` | `Bearer UserID:RandomString` | | Body Content Type | JSON | Body example: ```json { "model": "gpt-oss:20b", "prompt": "Summarize the following text in one concise sentence", "think": "low", "stream": false } ``` Response: ```json { "response": "generated text" } ``` Extract the generated output with the expression `{{$json.response}}`. It can be forwarded to email nodes, Slack/Microsoft Teams, databases or CRMs, Files.fm document workflows, or webhooks/APIs. Operational notes: - The API is stateless; no session handling is required. - Streaming is optional but not recommended for automation workflows. - Designed for server-side and backend use. - Suitable for GDPR-sensitive environments (EU-hosted). - No local GPU, model downloads, or runtime management required. ## Pre-Built File Upload Form For rapid and straightforward file upload integration, Files.fm provides a pre-built, configurable file upload form that can be embedded into your website or application-similar in concept to the e-signature form. This approach enables you to receive uploaded files quickly without implementing a full custom upload flow, while still allowing configuration and customization per use case. #### Key Capabilities The pre-built upload form supports: - Single-button or multi-step file uploads - Custom input fields for collecting metadata alongside uploaded files - Folder-based file organization - Secure, link-based access control - Seamless integration with existing Files.fm storage and workflows #### Use Cases The upload form is well suited for scenarios such as: - Collecting files from external users or customers - Receiving documents, images, or videos with structured metadata - Rapid prototyping or low-code integrations - Replacing custom upload implementations with a managed solution The form can be configured to match your specific requirements, including the number of fields, validation rules, and destination folders. Learn more and configure your form: https://files.fm/upload-form See an example in action: https://files.fm/demopro/?upload ## Additional Resources and Examples If you need further clarification, contact `support@files.fm`. The V2 format is preferred; feel free to request additional information or features. - Basic file upload example (raw code): `https://files.fm/api/demo/` - Inspect the upload form flow (e.g. in Chrome DevTools): `https://files.fm/upload_iframe.php?uid=15802530&uid_hash=YOUR_UID_HASH` ### Embedding folder contents (gallery) Embed a folder as a slideshow iframe: ```html ``` Or embed via the gallery script tag: ```html ``` ### File conversion and previews Files.fm automatically generates previews and converted formats for a wide range of file types. Previews enable fast viewing, embedding, and content inspection without downloading the original files. All preview generation is handled server-side within the Files.fm infrastructure. #### Image previews and thumbnails For image files (including RAW formats), Files.fm generates JPEG-based previews in multiple sizes: - Thumbnail (small) - optimized for lists and galleries. - Preview (normal) - resized image, typically up to ~1500 px on the longest edge. These are available for embedding and quick viewing while preserving the original file unchanged. #### Document previews PDF documents can be previewed directly in the browser using the built-in PDF viewer, without downloads. Files.fm can generate PDF previews for common office and document formats, including but not limited to: ```text .doc, .docx, .odp, .ods, .odt, .pps, .ppsx, .ppt, .pptx, .rtf, .xls, .xlsx, .dotx, .asice, .edoc, .zip ``` Text-based files can be previewed directly in the browser or downloaded as-is. Supported formats include: ```text .txt, .php, .js, .html, .css, .xml, .csv, .ini, .log, .asp, .asm, .c, .cc, .cmake, .cpp, .cs, .h, .java, .jsp, .json, .pl, .py, .rb, .script, .sh, .swift, .sql, .vb, .xhtml, .xsd, .xsl, .yaml, .yml, .inf ``` #### Video previews and streaming For video files, Files.fm provides automatic preview and streaming support: - If a video is not in MP4 format, or the original MP4 exceeds supported bitrate thresholds, Files.fm can generate a streaming-optimized MP4 preview. - Preview videos are suitable for in-browser playback and embedding. For large-scale or high-traffic video hosting, Files.fm recommends the Files.fm JavaScript Video Player, which supports P2P-assisted streaming to optimize bandwidth usage and performance. ### Applying a watermark Files.fm supports text-based watermarks on JPEG and PNG images. Apply one by including the `watermark_text` parameter in the `save_file.php` request, for example `&watermark_text=YourTextHere`. When a price is set or removed for an image or video file, Files.fm automatically regenerates the file previews (thumbnails) and applies a watermark using the file owner's username. This ensures consistent branding and content protection for monetized or restricted-access media. ### AI analysis (server-side) Files.fm provides server-side AI processing for images, videos, audio, and text content stored on the platform. All analysis runs within the Files.fm infrastructure - no client-side processing or external AI services are required. Depending on account type and configuration, the platform can perform: - Optical Character Recognition (OCR) - extracting text and numbers from images and video frames. - Image and video content analysis - object detection, face detection, and scene understanding. - Automatic content description - AI-generated summaries and captions. - Auto-tagging and categorization - generation of searchable metadata. - Sentiment and emotion analysis - of detected faces or textual content. - Text analysis with LLMs - summarization, classification, translation, and semantic analysis of documents and text files. Processing model: - All AI features operate asynchronously and server-side. - Results are stored as metadata associated with files and can be used for search, filtering, moderation, or automation workflows. - No raw data is sent to third-party AI providers unless explicitly agreed. Availability - AI features are optional and may require specific account plans or add-ons, feature enablement by Files.fm, and additional configuration depending on use case (e.g. face recognition, moderation, OCR pipelines). For availability, configuration, or API-level access, contact `support@files.fm`. # 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, comment), **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`. --- ## 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`. 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. --- ## 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`, `get_document`, `get_documents`, `create_document`, `update_document`, `add_comment` | | `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` is **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` | Assignable users (for tasks: scoped to a project). | | `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. | | `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. | `{dvs_section}` is `task`, `project_2`, `contact_person`, or `client` for the read actions (`get_users` is `task`, `project_2`, and `client` only). Write actions are `task` and `contact_person` (`add_comment` is `task` only). --- ## 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": false, "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" } ] } ``` #### 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 is scoped to workers of the specified project. ```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" } ``` 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 are ordered by creation date descending (newest first). 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 } ``` --- ### `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 `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 } ``` # DVS tasks integration quickstart Use this workflow to discover IDs and prepare a correct task request without guessing. DVS and DMS name the same public data-management API. The [full DMS reference](filesfm_api_dms.md) describes the actions and field types. ## Prerequisites - A dedicated Files.fm user in an organization with DVS enabled. - An API access token from the account's API-key settings. - Section **write** permission on both `task` and `project_2`, even for API reads. Contact/client lookup needs the corresponding section permission. - The name of the existing project. `project_2` is the DVS projects register; it is not the separate Objects API. Base URL: `https://api.files.fm`. Every DVS action uses **POST JSON** and includes `user` and `user_access_token`. A website session alone and an MCP Bearer token are not this authentication format. Never put credentials in URLs. ## 1. Discover fields and projects ```http POST /api_dvs/task/get_fields Content-Type: application/json {"user":"YOUR_USER","user_access_token":"YOUR_API_TOKEN"} ``` Read `fields[].name`, `type`, `required`, `filter`, `options`, `section`, and `depends_on`. Required task fields currently include `name` and `dvs_project_id`. Dropdown option IDs belong to this organization; example values from documentation are not reusable IDs. ```http POST /api_dvs/project_2/get_documents Content-Type: application/json {"user":"YOUR_USER","user_access_token":"YOUR_API_TOKEN","limit":200,"offset":0} ``` Match the requested project against `items[].name` and retain its integer `dvs_document_id`. Continue with `offset += items.length` until `offset >= total`. Stop and ask for a choice if a name is absent or ambiguous. A short page is not itself the documented completion condition; use `total`. Filters use field names with `filter: true` from `get_fields`. Unknown filter names are ignored, not rejected. Multiple values for a field are OR; different fields are AND. Lists omit HTML descriptions; use `get_document` for full content. ## 2. Discover assignable users and status options ```http POST /api_dvs/task/get_users Content-Type: application/json {"user":"YOUR_USER","user_access_token":"YOUR_API_TOKEN","dvs_project_id":123} ``` Here `123` represents the project ID just discovered. Resolve `users[].display_name` to an integer `id`; do not substitute email addresses or usernames in task user fields. Only use the users returned for that project. Resolve `dropdown_status`, `dropdown_priority`, and other dropdown values from the task field's `options`. For external followers, look up `contact_person` document IDs; those are different from platform user IDs. [Contact and client relationships](filesfm_api_dms.md#supported-registers) ## 3. Create a task After discovery, construct this body using the real IDs and any additional required fields: ```json { "user": "YOUR_USER", "user_access_token": "YOUR_API_TOKEN", "create_params": { "dvs_project_id": 123, "name": "Review integration", "responsible": 456, "dropdown_status": 789, "description": "

Review the integration.

" } } ``` Send to `POST /api_dvs/task/create_document`. Optional fields may be omitted. IDs `123`, `456`, and `789` above are illustrative; replace them with discovery results. ```json {"dvs_document_id":4822} ``` Retain the returned task ID. Creation and updates can send notifications. The API has no documented idempotency key: after a timeout or uncertain write outcome, reconcile with task reads before retrying. Invalid relation IDs may fail after part of a write has occurred; a failure is not proof that nothing was saved. ## 4. Read, update, and comment Call `task/get_document` with auth and `dvs_document_id` to read the full saved record. IDs in read responses are often objects such as `{ "id": 456, "display_name": "..." }`; write requests need the scalar IDs. ```json { "user": "YOUR_USER", "user_access_token": "YOUR_API_TOKEN", "dvs_document_id": 4822, "update_params": { "dropdown_status": 790 } } ``` Send to `POST /api_dvs/task/update_document`. Discover `790` from `get_fields`. This is a partial update: omitted fields keep their values; arrays supplied for workers/relations replace that field's selection. The task's project cannot be changed through this API. ```json { "user": "YOUR_USER", "user_access_token": "YOUR_API_TOKEN", "dvs_document_id": 4822, "comment": "Integration reviewed." } ``` Send to `POST /api_dvs/task/add_comment`. Mention a platform user with `@[USER:456]`; mentions can notify users. There is no documented task delete action in this API. Clean up test records through the authorized DVS UI. ## curl Set credentials in your local environment. The following uses Node to write JSON to stdin, keeping the token out of the URL and curl arguments: ```bash export FILESFM_USER='YOUR_USER' export FILESFM_API_TOKEN='YOUR_API_TOKEN' node -e 'process.stdout.write(JSON.stringify({user:process.env.FILESFM_USER,user_access_token:process.env.FILESFM_API_TOKEN}))' | curl --silent --show-error --fail-with-body --max-time 30 \ 'https://api.files.fm/api_dvs/task/get_fields' \ -H 'Content-Type: application/json' --data-binary @- ``` For write calls, prepare the JSON body from discovered IDs in a local private file and use `--data-binary @request.json`. Protect and remove credential-bearing files after use. Do not use verbose HTTP traces with real credentials. ## JavaScript (Node 18+ or a trusted server) ```js async function dvs(section, action, params = {}) { const response = await fetch(`https://api.files.fm/api_dvs/${section}/${action}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...params, user: process.env.FILESFM_USER, user_access_token: process.env.FILESFM_API_TOKEN }), signal: AbortSignal.timeout(30000) }); const text = await response.text(); if (!response.ok) throw new Error(`DVS HTTP ${response.status}`); if (!text) throw new Error('Missing success response'); return JSON.parse(text); } const { fields } = await dvs('task', 'get_fields'); const projects = []; let offset = 0; while (true) { const page = await dvs('project_2', 'get_documents', { limit: 200, offset }); projects.push(...page.items); offset += page.items.length; if (offset >= page.total) break; if (!page.items.length) throw new Error('Incomplete project listing'); } const matches = projects.filter(p => p.name === process.env.FILESFM_PROJECT_NAME); if (matches.length !== 1) throw new Error('Choose one existing project'); const projectId = matches[0].dvs_document_id; const { users } = await dvs('task', 'get_users', { dvs_project_id: projectId }); const statusOptions = fields.find(f => f.name === 'dropdown_status')?.options || []; // Select a user and status from users/statusOptions; omit them if not needed. const createParams = { dvs_project_id: projectId, name: 'Review integration' }; console.log({ endpoint: '/api_dvs/task/create_document', create_params: createParams }); // Only run the following block when task creation and notifications are authorized: // const created = await dvs('task', 'create_document', { create_params: createParams }); // const task = await dvs('task', 'get_document', { dvs_document_id: created.dvs_document_id }); // await dvs('task', 'update_document', { dvs_document_id: task.dvs_document_id, // update_params: { description: '

Integration verified.

' } }); // await dvs('task', 'add_comment', { dvs_document_id: task.dvs_document_id, // comment: 'Integration reviewed.' }); ``` Keep API tokens on a trusted server, not in shipped browser JavaScript. Client-side UI should call your own authorized integration backend. ## PHP 7.4+ ```php true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode($params, JSON_THROW_ON_ERROR), CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 30, ]); $body = curl_exec($curl); $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); $failed = $body === false; curl_close($curl); if ($failed) { throw new RuntimeException('DVS transport failed'); } if ($status !== 200) { throw new RuntimeException('DVS HTTP ' . $status); } return json_decode($body, true, 512, JSON_THROW_ON_ERROR); } $fields = dvs('task', 'get_fields')['fields']; $projects = []; $offset = 0; do { $page = dvs('project_2', 'get_documents', ['limit' => 200, 'offset' => $offset]); $projects = array_merge($projects, $page['items']); $offset += count($page['items']); if (!$page['items'] && $offset < $page['total']) { throw new RuntimeException('Incomplete project listing'); } } while ($offset < $page['total']); $matches = array_values(array_filter($projects, function ($p) { return $p['name'] === getenv('FILESFM_PROJECT_NAME'); })); if (count($matches) !== 1) { throw new RuntimeException('Choose one existing project'); } $projectId = $matches[0]['dvs_document_id']; $users = dvs('task', 'get_users', ['dvs_project_id' => $projectId])['users']; $createParams = ['dvs_project_id' => $projectId, 'name' => 'Review integration']; // After explicit write authorization: // $created = dvs('task', 'create_document', ['create_params' => $createParams]); // $task = dvs('task', 'get_document', ['dvs_document_id' => $created['dvs_document_id']]); // dvs('task', 'update_document', ['dvs_document_id' => $task['dvs_document_id'], // 'update_params' => ['description' => '

Integration verified.

']]); // dvs('task', 'add_comment', ['dvs_document_id' => $task['dvs_document_id'], // 'comment' => 'Integration reviewed.']); ``` ## Failure handling and MCP DVS errors can have an empty body. Check status first: `400` parameters, `401` authentication or missing session organization, `405` method/section/permission/not-found conditions, `500` backend failure. Do not log request bodies containing tokens. No universal public DVS request quota is promised; infrastructure can enforce additional limits. Public MCP `filesfm_search_docs` and `filesfm_get_doc` can find and retrieve this guide without auth. Use that content to prepare REST requests. MCP does not create tasks, enumerate DVS projects, or supply your organization's IDs. # Storage API integration quickstart Use REST V2 for new storage integrations where an equivalent exists. The [REST reference](filesfm_api_v2.md) also covers V1 capabilities that do not have V2 equivalents, including restore, version history, ZIPs, and thumbnails. ## Authentication and request format Base URL: `https://api.files.fm`. V2 actions use **POST form fields**, not a JSON request body. Use `user` and `user_access_token` from a dedicated integration account. File uploads use multipart form data. MCP Bearer and DVS JSON examples are separate contracts. ## Create, upload, list, and download ```bash # Examples contain placeholders. These commands create and upload data. curl --fail-with-body --silent --show-error --max-time 30 \ 'https://api.files.fm/api_v2/create_folder' \ --data-urlencode 'user=YOUR_USER' \ --data-urlencode 'user_access_token=YOUR_API_TOKEN' \ --data-urlencode 'folder_name=Integration test' --data-urlencode 'access_type=PRIVATE' ``` Retain the returned `folder_hash` and capability keys. Hashes and keys are opaque strings. A private folder is an appropriate default for business data. ```bash curl --fail-with-body --silent --show-error --max-time 120 \ 'https://api.files.fm/api_v2/upload_file' \ -F 'user=YOUR_USER' -F 'user_access_token=YOUR_API_TOKEN' \ -F 'folder_hash=RETURNED_FOLDER_HASH' -F 'file=@local-file.txt' ``` Retain the returned `file_hash`. Upload one file per request; account limits apply. List with `get_folder_content` using the folder hash and form auth. The result is an array, not a DVS `items/total` envelope. This V2 listing has no documented pagination parameters; do not invent `page` or `limit` support. ```bash curl --fail-with-body --silent --show-error --max-time 30 \ 'https://api.files.fm/api_v2/download_file' \ --data-urlencode 'user=YOUR_USER' --data-urlencode 'user_access_token=YOUR_API_TOKEN' \ --data-urlencode 'file_hash=RETURNED_FILE_HASH' --output downloaded-file.txt ``` Download returns raw file bytes. Verify the HTTP result before treating the output as a successful file. ## JavaScript on a trusted server ```js async function storage(action, params) { const body = new URLSearchParams({ ...params, user: process.env.FILESFM_USER, user_access_token: process.env.FILESFM_API_TOKEN }); const response = await fetch(`https://api.files.fm/api_v2/${action}`, { method: 'POST', body, signal: AbortSignal.timeout(30000) }); if (!response.ok) throw new Error(`Storage HTTP ${response.status}`); return response.json(); } // Creates a private folder; run only when authorized. const folder = await storage('create_folder', { folder_name: 'Integration test', access_type: 'PRIVATE' }); const items = await storage('get_folder_content', { folder_hash: folder.folder_hash }); ``` For multipart upload use `FormData` with `file` as a Blob and do not manually set its Content-Type boundary. Use `response.arrayBuffer()` for download instead of `.json()`. ## PHP 7.4+ ```php getenv('FILESFM_USER'), 'user_access_token' => getenv('FILESFM_API_TOKEN'), 'folder_hash' => getenv('FILESFM_FOLDER_HASH')]; $curl = curl_init('https://api.files.fm/api_v2/get_folder_content'); curl_setopt_array($curl, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => http_build_query($params), CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 30]); $body = curl_exec($curl); $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($body === false || $status !== 200) { throw new RuntimeException('Storage request failed: ' . $status); } $items = json_decode($body, true, 512, JSON_THROW_ON_ERROR); ``` ## Errors, limits, and retries V2 failures may have an empty body despite the JSON Content-Type. Some permission and not-found conditions use `405`, not `403/404`. Check the endpoint's documented statuses; legacy scripts can return plain text or endpoint-specific error objects. No universal REST quota is published. Anonymous V1 folder listing is separately throttled and delayed; authenticated V2 listing does not inherit that anonymous interface. Observe actual `429` and retry guidance. Retry reads with bounded backoff; reconcile create/upload outcomes before retrying writes. No general idempotency-key contract is documented. # Files.fm Hosted MCP Connector Files.fm exposes a hosted Model Context Protocol (MCP) endpoint for AI clients that need read-only access to Files.fm files and Files.fm API documentation. Endpoint: ```text https://api.files.fm/api/mcp ``` Transport: - Streamable HTTP request/response over `POST` - JSON-RPC 2.0 body - `Content-Type: application/json` - GET and SSE are not enabled in v1 Supported initialization-based revisions: `2025-03-26`, `2025-06-18`, and `2025-11-25`. The default initialization response remains `2025-06-18`. The server also supports `2026-07-28` with per-request metadata and `server/discover`; do not send an `initialize` handshake using the modern revision. Unknown protocol headers return `400` with supported-version information. The existing `2024-11-05` JSON messages remain accepted over this server's custom POST compatibility transport. This does not provide that revision's deprecated standard SSE transport; clients requiring SSE cannot connect through it. For modern HTTP requests include `MCP-Protocol-Version`, `Mcp-Method`, and, for tool/resource calls, `Mcp-Name`. The matching protocol version and client capabilities go in `params._meta`. Modern results include `resultType: "complete"`; the old response shape remains available to initialization-based clients. ## Authentication Private file tools require an API access token: ```http Authorization: Bearer : ``` Use a Files.fm user access token with type `API` or `ALL`. Tokens are managed in the Files.fm account API key UI. Do not put credentials in query strings. The MCP endpoint rejects query-string parameters. Public `filesfm_search_docs`, `filesfm_get_doc`, documentation resource reads, and discovery/list methods do not require a token. Private file access can additionally use the isolated OAuth profile **when operators have enabled it on that deployment**. OAuth tokens are short-lived, scoped to read-only files, and distinct from general API keys. If OAuth discovery reports unavailable, use public documentation or a supported client's configured API-key header; do not assume an OAuth login is available. ## Capabilities Supported JSON-RPC methods: - `initialize` - `tools/list` - `tools/call` - `resources/list` - `resources/read` - `resources/templates/list` - `ping` (initialization-based clients) - `server/discover` (modern clients) The connector is read-only. It does not upload, rename, move, delete, restore, share, or change files. It also does not enumerate structured DVS tasks/projects or perform DVS writes. Use public documentation to prepare the separate [DVS JSON API](filesfm_api_dms.md) requests with its own credentials and schema discovery. ## Tools ### `filesfm_get_root_folder` Returns the authenticated user's root folder metadata. ### `filesfm_list_folder` Lists visible files and direct child folders in a folder. Arguments: ```json { "folder_hash": "abc123", "limit": 100, "offset": 0 } ``` ### `filesfm_get_folder_info` Returns metadata for one visible folder. Arguments: ```json { "folder_hash": "abc123" } ``` ### `filesfm_get_file_info` Returns metadata for one visible file. Arguments: ```json { "file_hash": "abc123" } ``` ### `filesfm_read_file` Reads one visible file when the token has download rights. Arguments: ```json { "file_hash": "abc123", "max_bytes": 26214400 } ``` Text files are returned inline as UTF-8 text. Binary files below the inline limit are returned as MCP `blob` content with base64 data. Larger files return metadata plus a Files.fm URL instead of loading the full file into memory. Default and maximum inline size: 25 MB. ### `filesfm_search_files` Searches visible files and folders for the authenticated user through the same central Files.fm search used by the web UI. Simple search can match names plus indexed/searchable metadata such as descriptions, tags, image text/object labels, and common file metadata fields when available. Arguments: ```json { "query": "invoice", "limit": 20 } ``` ### `filesfm_search_docs` Searches local Files.fm API and MCP Markdown documentation. This tool is public and does not require bearer authentication. Arguments: ```json { "query": "upload file", "limit": 8 } ``` Search results contain a public `url`, a `filesfm-docs://` URI, title and snippet. Read the complete document before implementing a request. Small Latvian/English keyword aliases help tasks/project discovery; this is keyword search, not semantic retrieval. ### `filesfm_get_doc` Reads the complete published Markdown document or OpenAPI specification without authentication. This is useful in clients that expose tools but not MCP resources. ```json {"uri":"filesfm-docs://filesfm_api_dvs_tasks.md"} ``` The JSON result contains `uri`, canonical `url`, and complete `text`. Existing JSON tool results remain in text content and also provide `structuredContent`; declared output schemas apply to that structured object. File reads use text/embedded resources and do not declare a single JSON output schema. ## Resources Static documentation resources: - `filesfm-docs://README.md` - `filesfm-docs://filesfm_api_v2.md` - `filesfm-docs://filesfm_api_dms.md` - `filesfm-docs://filesfm_mcp.md` - `filesfm-docs://filesfm_api_dav.md` - `filesfm-docs://filesfm_api_protocols.md` - `filesfm-docs://filesfm_api_dvs_tasks.md` - `filesfm-docs://filesfm_api_storage.md` - `filesfm-docs://openapi.yaml` - `filesfm-docs://openapi.json` The [document catalog](docs-manifest.json) drives discovery, navigation, and this allow-list. Unpublished paths and arbitrary filesystem paths are rejected. Private resource templates: - `filesfm://folder/{folder_hash}` - `filesfm://file/{file_hash}` Private resources require bearer authentication and use the same access checks as the tools. ## Limits Request bodies are capped at 1 MB. Inline file reads are capped at 25 MB. The MCP endpoint applies fixed-window rate limits by client IP, authenticated user, and bearer token for private tools. Expensive operations such as `filesfm_search_files` and `filesfm_read_file` have stricter limits than metadata and folder listing calls. Public documentation search and documentation resource reads are IP-limited. When a limit is exceeded, the endpoint returns HTTP `429` with a JSON-RPC error message that includes the retry time in seconds. Current fixed-window limits per action: public document search/read 120 requests/minute and 2,400/hour per IP; private search 30/minute and 600/hour per token; private file read 40/minute and 700/hour per token; folder listing 90/minute and 1,800/hour per token. Additional user/IP buckets apply. Limits are operational settings, not a contractual capacity guarantee. Folder listing may return a short page after access-right filtering; its offset addresses the raw folder/file sequence, not just the visible result count. ## Example JSON-RPC Calls Initialize: ```json { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "example-client", "version": "1.0.0" } } } ``` Search docs: ```json { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "filesfm_search_docs", "arguments": { "query": "API authentication" } } } ``` Read a file: ```json { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "filesfm_read_file", "arguments": { "file_hash": "abc123" } } } ``` ## Client Setup Client setup and successful HTTP requests are different from a completed integration. The official configuration syntax below is checked against client documentation; operator/customer smoke tests must record the actual product/version and public/private tool result. ### ChatGPT Add the remote MCP URL `https://api.files.fm/api/mcp` through the custom MCP/plugin setup available to your account. For public documentation, choose no authentication and test `filesfm_search_docs` followed by `filesfm_get_doc`. Account/workspace policies determine whether custom connectors are available. Private file access requires a supported credential setup. For OAuth, the isolated server profile must first be enabled; then use its discovered OAuth settings. Do not place an API token in the server URL. Generic MCP tools do not by themselves implement ChatGPT company-knowledge/deep-research `search`/`fetch` compatibility. [Official OpenAI MCP setup](https://developers.openai.com/api/docs/mcp) ### Codex Example `config.toml` remote MCP server: ```toml [mcp_servers.filesfm] url = "https://api.files.fm/api/mcp" bearer_token_env_var = "FILESFMMCP_BEARER_TOKEN" ``` Environment variable value: ```bash export FILESFMMCP_BEARER_TOKEN=":" ``` ### Cursor Example `.cursor/mcp.json`: ```json { "mcpServers": { "filesfm": { "url": "https://api.files.fm/api/mcp", "headers": { "Authorization": "Bearer ${env:FILESFMMCP_BEARER_TOKEN}" } } } } ``` ### Opencode Example remote MCP configuration: ```json { "mcp": { "filesfm": { "type": "remote", "url": "https://api.files.fm/api/mcp", "headers": { "Authorization": "Bearer ${FILESFMMCP_BEARER_TOKEN}" } } } } ``` ### Claude Clients For **Claude Code**, use HTTP MCP with an environment-expanded header: ```bash claude mcp add --transport http filesfm https://api.files.fm/api/mcp \ --header "Authorization: Bearer ${FILESFMMCP_BEARER_TOKEN}" ``` For **Claude web/Desktop remote connectors**, add the same URL and choose no sign-in for public documentation. Current connector setup also supports fixed request headers for API-key services; where that setting is available, configure: ```http Authorization: Bearer : ``` Use the current Claude client UI or configuration syntax because remote MCP support differs by Claude product and release. Alternatively choose OAuth after the server's OAuth profile is enabled. Verify sign-in and a private read in the actual client. A local Claude Desktop stdio configuration is a different interface from a hosted remote connector. [Claude Code MCP](https://code.claude.com/docs/en/mcp) · [Claude remote connector setup](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) ### Gemini Gemini CLI supports remote HTTP MCP in its MCP server settings. Configure the endpoint and the documented Authorization header for private files, or omit the header for public documentation. The Gemini web application and Gemini API URL Context are separate products: a successful CLI MCP connection does not establish either web URL retrieval or native MCP support in Gemini web. [Gemini CLI MCP](https://geminicli.com/docs/tools/mcp-server/) · [Gemini URL Context](https://ai.google.dev/gemini-api/docs/url-context) ## Modern discovery example ```http POST /api/mcp Content-Type: application/json Accept: application/json, text/event-stream MCP-Protocol-Version: 2026-07-28 Mcp-Method: server/discover {"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{},"io.modelcontextprotocol/clientInfo":{"name":"example-client","version":"1.0"}}}} ``` For `tools/call` add `Mcp-Name: filesfm_get_doc` and put `name`, `arguments` and the same `_meta` object in `params`. The `Mcp-Name` value must match the body. GET returning `405` is intentional; a POST JSON response does not require SSE. [MCP transport](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http) · [Version compatibility](https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning) ## Security and Limits - HTTPS is required for production use. - Bearer auth is accepted only from the `Authorization` header. - Query-string credentials are rejected. - Origin is validated when an `Origin` header is present. - File operations are read-only. - File and folder results are filtered through Files.fm access-right checks. - Tool results do not include delete keys, add keys, passwords, storage paths, or private tokens. - Inline file content is capped at 25 MB. - Folder list and search calls have bounded limits. ## Related Documentation - [API overview](README.md) - [REST API V2 reference](filesfm_api_v2.md) - [DMS API](filesfm_api_dms.md) # Files.fm CalDAV and CardDAV API Files.fm provides CalDAV for calendars and CardDAV for address books through its SabreDAV server. Both support programmatic access over HTTPS and compatible DAV clients. Calendar events use iCalendar (`.ics`); contacts use vCard (`.vcf`). Discovery and queries use DAV XML. ## Server and authentication | Setting | Value | | --- | --- | | Base URL for both protocols | `https://calendar.failiem.lv/` | | Authentication | HTTP Basic over HTTPS with your Files.fm username and password | | Calendar home, typical path | `/calendars/{username}/` | | Address-book home, typical path | `/addressbooks/{username}/` | | Principal, typical path | `/principals/{username}/` | Use your canonical Files.fm username consistently. Discover resource URLs as described below rather than constructing them from email addresses or display names. Existing calendars associated with an email-based principal may require support to link them to the username used by the web workspace. The DAV authenticator also accepts a valid Files.fm **User Access Token in the Basic password field**. This is not an `Authorization: Bearer` interface: do not assume that REST API keys, folder access keys, or a calendar's public sharing hash are DAV credentials. Account/IP restrictions and password policies apply. Password-based connections requiring two-factor authentication may need the emailed IP confirmation before access succeeds; an unconfirmed attempt can return `403`. The examples use `curl --user 'YOUR_USERNAME'`, which prompts for the password. Replace placeholders with your own account and discovered URLs. Send credentials only to the intended HTTPS server. The PUT and DELETE examples change data in the authenticated account. ## Discover calendars and address books ### 1. Find the authenticated principal ```sh curl --user 'YOUR_USERNAME' -i -X PROPFIND \ 'https://calendar.failiem.lv/' \ -H 'Depth: 0' -H 'Content-Type: application/xml; charset=utf-8' \ --data-binary @- <<'XML' XML ``` A successful DAV property response normally uses `207 Multi-Status`. Read the `d:href` inside `d:current-user-principal`, for example `/principals/YOUR_USERNAME/`. XML prefixes can differ; match namespace URIs and element names. Check each `d:propstat/d:status`: a `207` response can contain missing or forbidden properties. ### 2. Find both home collections Send this request to the principal URL returned above: ```sh curl --user 'YOUR_USERNAME' -i -X PROPFIND \ 'https://calendar.failiem.lv/principals/YOUR_USERNAME/' \ -H 'Depth: 0' -H 'Content-Type: application/xml; charset=utf-8' \ --data-binary @- <<'XML' XML ``` Resolve returned `href` values against the server URL. Use explicit discovery from this base URL; do not depend on `.well-known` redirects or enumeration of other users. ### 3. List the collections in each home Send `PROPFIND` with `Depth: 1` to each discovered home, using this XML body: ```xml ``` Select responses whose `resourcetype` contains `calendar` in the CalDAV namespace or `addressbook` in the CardDAV namespace. Calendar-only properties may be absent on address books. Keep the collection `href`; its display name is not its URL. Calendar homes may also contain scheduling resources, which are not ordinary calendars. The backend can automatically create a default calendar or address book when listing a user's collections for the first time. Discovery is therefore not guaranteed to leave an unused account without collections. Do not hard-code `default` as the only collection. ## CalDAV: read and write events ### Query a date range Replace `CALENDAR_URI` with the discovered collection path segment. Dates below are UTC and select September 2026; choose the range your integration needs. ```sh curl --user 'YOUR_USERNAME' -i -X REPORT \ 'https://calendar.failiem.lv/calendars/YOUR_USERNAME/CALENDAR_URI/' \ -H 'Depth: 1' -H 'Content-Type: application/xml; charset=utf-8' \ --data-binary @- <<'XML' XML ``` Each matching response supplies an object `href`, `getetag`, and `calendar-data`. A recurring series can be returned as one iCalendar object with recurrence rules and exceptions, rather than one object per occurrence. Retain the complete object when editing it, including properties your integration does not understand. Check the collection's advertised component types before storing components other than `VEVENT`. ### Create an event Save this as `event.ics`, using iCalendar CRLF line endings. Generate a unique UID and object filename for each new event; the example values are placeholders. ```ical BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Example Integration//Calendar//EN BEGIN:VEVENT UID:integration-example-20260915@example.invalid DTSTAMP:20260909T120000Z DTSTART:20260915T090000Z DTEND:20260915T093000Z SUMMARY:Integration meeting END:VEVENT END:VCALENDAR ``` ```sh curl --user 'YOUR_USERNAME' -i -X PUT \ 'https://calendar.failiem.lv/calendars/YOUR_USERNAME/CALENDAR_URI/integration-example.ics' \ -H 'Content-Type: text/calendar; charset=utf-8' \ -H 'If-None-Match: *' --data-binary @event.ics ``` Creation normally returns `201 Created`. For updates and deletion, use the conditional requests described below. ## CardDAV: read and write contacts CardDAV stores address-book contacts. It is separate from DVS contact-person records; no automatic synchronization with the DVS API is implied. Calendar membership sharing does not grant address-book access. ### Query contacts Use the discovered address-book URL, replacing `ADDRESSBOOK_URI`: ```sh curl --user 'YOUR_USERNAME' -i -X REPORT \ 'https://calendar.failiem.lv/addressbooks/YOUR_USERNAME/ADDRESSBOOK_URI/' \ -H 'Depth: 1' -H 'Content-Type: application/xml; charset=utf-8' \ --data-binary @- <<'XML' Example XML ``` The response contains matching object URLs, ETags, and vCard data. To list all object URLs and ETags, use `PROPFIND` with `Depth: 1` on the address book and request `d:resourcetype` and `d:getetag`; skip the collection itself. Retrieve individual contacts with `GET`, or batches with `addressbook-multiget` when advertised. ### Create a contact Save this as `contact.vcf` with vCard CRLF line endings and a unique UID: ```vcard BEGIN:VCARD VERSION:3.0 UID:integration-contact-example@example.invalid FN:Example Contact N:Contact;Example;;; EMAIL:contact@example.invalid END:VCARD ``` ```sh curl --user 'YOUR_USERNAME' -i -X PUT \ 'https://calendar.failiem.lv/addressbooks/YOUR_USERNAME/ADDRESSBOOK_URI/integration-example.vcf' \ -H 'Content-Type: text/vcard; charset=utf-8' \ -H 'If-None-Match: *' --data-binary @contact.vcf ``` ## Retrieve, update, and delete an object These operations apply to both event and contact URLs. Use an actual object URL from discovery/query results, not a collection URL: ```sh # Fetch content and the current ETag response header. curl --user 'YOUR_USERNAME' -i 'https://calendar.failiem.lv/OBJECT_PATH' # Replace an event only if its ETag still matches. Preserve the ETag's quotes. curl --user 'YOUR_USERNAME' -i -X PUT \ 'https://calendar.failiem.lv/OBJECT_PATH' \ -H 'If-Match: "ETAG_FROM_SERVER"' \ -H 'Content-Type: text/calendar; charset=utf-8' --data-binary @event.ics # Delete this object only if its ETag still matches. curl --user 'YOUR_USERNAME' -i -X DELETE \ 'https://calendar.failiem.lv/OBJECT_PATH' \ -H 'If-Match: "ETAG_FROM_SERVER"' ``` For contact updates, use `text/vcard; charset=utf-8` and `@contact.vcf`. Updates normally return `204 No Content`; fetch the current representation/ETag after writing if needed. A failed ETag precondition returns `412`: refetch and reconcile changes before retrying. Do not retry without the precondition and overwrite another user's changes. Treat the resource URL, UID, and ETag as distinct values. ## Synchronization and collection management - Discover `DAV:supported-report-set` and `DAV:sync-token` on each collection. Where advertised, use `REPORT` with `DAV:sync-collection`, the saved token, `DAV:sync-level` of `1`, and `DAV:getetag`. An empty token requests the initial synchronization state. Save the returned token; subsequent reports identify changed objects and deleted object URLs (`404` statuses inside the multistatus). - If the sync token is rejected as invalid, perform a fresh synchronization. Fetch changed objects using `GET` or the advertised `calendar-multiget` / `addressbook-multiget` reports. Without sync support, compare an ETag listing with your previous complete listing; a date-filtered event query alone cannot establish all deletions. - Calendar creation uses `MKCALENDAR`; address-book creation uses extended `MKCOL` with `DAV:resourcetype` containing `DAV:collection` and CardDAV `addressbook`. Use `PROPPATCH` for supported collection properties. Check permissions and advertised capabilities before collection operations. - `DELETE` on an owned collection removes that calendar/address book and its objects. This differs from deleting one `.ics` or `.vcf` object. ## Shared calendars and the web workspace Owners manage calendar membership in the Files.fm calendar workspace at `https://files.fm/calendar/`. Members connect to CalDAV with their own credentials. Their calendar home exposes a `filesfm-shared-{calendarId}` collection pointing to the owner's original events and synchronization history. Members can read, create, edit, and delete events, including private events. They cannot manage membership or the owner's global calendar settings through this collection. Supported personal collection properties include display name, color, order, and description. Deleting the member's shared collection removes their membership; deleting an event inside it removes the event for everyone. Membership revocation takes effect on subsequent requests. Public calendar links and `/calendar/{hash}/calendar.ics` subscriptions are separate read-only interfaces. A public link alone grants no CalDAV access. The web workspace uses the same calendar data, but some imported events and recurrence patterns can only be edited through CalDAV. The workspace's `/calendar/api.php` requires a web session and CSRF token for writes and is not a public REST API contract. CalDAV and CardDAV are not routes under `api_v2/` and are not included in the REST OpenAPI specification. ## Responses and troubleshooting | HTTP status | Meaning / action | | --- | --- | | `200`, `201`, `204` | Successful retrieval, creation, or update/deletion, depending on the method | | `207` | DAV XML multistatus; inspect each response/property status | | `400` | Malformed request or data; inspect the DAV error body | | `401` | Authentication required or credentials rejected | | `403` | Insufficient permission, account/IP policy, unconfirmed 2FA, or DAV validation failure; inspect the error body | | `404` | Resource missing or no longer accessible; rediscover where appropriate | | `409` | Parent collection/state conflict; check the destination and current state | | `412` | Conditional request failed; retrieve the latest object and reconcile | | `503` | Service unavailable; retry with backoff | Use namespace-aware XML parsing. DAV error bodies are XML, not the REST API's JSON format. For account setup or integration support, contact `support@files.fm`. ## Implementation and protocol references Files.fm behavior was checked against `webdav_db_v2/app/caldavapp.php`, the custom authentication, calendar, and address-book backends, and `new/calendar/README.md`. This guide documents the implemented interface; it does not claim that every DAV extension or client-specific feature is supported. No production data was written to validate these examples. For further protocol details, see the official SabreDAV guides for [CalDAV clients](https://sabre.io/dav/building-a-caldav-client/) and [CardDAV clients](https://sabre.io/dav/building-a-carddav-client/). # Files.fm WebDAV and FTP / FTPS These protocols expose Files.fm files and folders. They are separate from REST V2, DVS records, and the [CalDAV/CardDAV calendar server](filesfm_api_dav.md). ## WebDAV | Setting | Value | | --- | --- | | Public base URL | `https://webdav.files.fm/` | | Authentication | HTTP Basic with the canonical Files.fm username and password, or a compatible User Access Token in the Basic password field | | File paths | Discover with `PROPFIND`; percent-encode path segments and preserve returned `href` values | | Resource formats | DAV XML metadata and raw file bytes | Use the WebDAV host directly. REST API hashes are not WebDAV file paths. A folder add/delete key or MCP Bearer token is not a DAV Basic credential. The DAV authenticator checks `APP`-compatible tokens and accepts `ALL`; an `API`-only or `FTP`-only key must not be assumed compatible. Account/IP policies and password-based 2FA can affect login. Discover the root and its immediate children: ```sh curl --user 'YOUR_USERNAME' --max-time 30 -i -X PROPFIND \ 'https://webdav.files.fm/' -H 'Depth: 1' \ -H 'Content-Type: application/xml; charset=utf-8' --data-binary @- <<'XML' XML ``` curl prompts for the password/token. A `207 Multi-Status` response must be parsed with namespace-aware XML and per-resource/per-property statuses; it does not mean every item succeeded. Avoid recursive `Depth: infinity` discovery. Read files with `GET`; conditional writes use the returned ETag and `If-Match`. The SabreDAV filesystem backend implements folder/file operations, permissions, and locking. Use `LOCK`/`UNLOCK` and lock tokens when a DAV client requires them. PUT, MKCOL, MOVE, COPY, DELETE, and LOCK can change persisted state; test against a dedicated account, not arbitrary production files. Limits and version handling follow the underlying Files.fm account and file operations. [Network-drive setup](https://files.fm/webdav-info) covers OS/client mounting. Client support is not proven merely by a server accepting DAV methods. ## FTP and explicit FTPS | Setting | Value | | --- | --- | | Public hosts | `ftp.files.fm`, `ftp.failiem.lv` | | Control port | `21` | | Recommended mode | Explicit TLS (`AUTH TLS`), protected data connection, passive mode | | Username | Canonical Files.fm username | | Password field | FTP User Access Token generated in account settings (`FTP` or `ALL` type) | Use **explicit FTPS**, not implicit TLS on port 990 or SFTP/SSH. The PHP FTP service authenticates an access token; the account password is not the documented FTP login credential. Plain FTP is only appropriate for devices that cannot use TLS, with a dedicated isolated account and awareness that credentials/data are unencrypted. Configure a compatible FTP client with passive transfers and TLS protection for both control and data channels. The repository daemon config has passive ports `50000–51000`; operators must verify the actual deployment/firewall range before providing network allow-list instructions. Do not hard-code a data port in an integration. Paths are filesystem paths discovered using FTP listing, not REST hashes. Uploads, renames, and deletes use the same underlying Files.fm file operations and access rights. Verify the client's resume, overwrite, and concurrent-transfer behavior before using it for backup workflows. Command availability is advertised by the server's `FEAT` response; do not assume every FTP extension exists. ## Implementation and verification boundaries DAV/FTP do not expose DVS tasks, clients, or projects as structured records. Use the [DMS API](filesfm_api_dms.md) for those integrations. General protocol semantics: [SabreDAV](https://sabre.io/dav/), [WebDAV RFC 4918](https://www.rfc-editor.org/rfc/rfc4918), [FTP TLS RFC 4217](https://www.rfc-editor.org/rfc/rfc4217).