openapi: 3.0.3 tags: - name: healthcheck - name: token - name: maintenance - name: admins - name: API keys - name: connections - name: IP Lists - name: defender - name: quota - name: folders - name: groups - name: roles - name: users - name: data retention - name: events - name: metadata - name: user APIs - name: public shares - name: event manager info: title: SFTPGo Enterprise description: | **Base URL:** `/api/v2` SFTPGo is a fully-featured managed file transfer (MFT) platform. This REST API provides full programmatic control over the system and is split into two distinct areas: - **Admin API** — user, group, virtual folder, quota, event rule, and server management. Requires an administrator token or an admin-scoped API key. - **User API** — file operations, share management, and profile settings on behalf of an individual user. Requires a user token or a user-scoped API key. Storage backends (local filesystem, encrypted local, S3-compatible, Google Cloud Storage, Azure Blob, remote SFTP, HTTP) can be configured per user and per virtual folder, so a single installation can span multiple storage sources. Groups let you apply shared settings (filesystem, quota, share policies, WebClient options) to many users at once. The Event Manager reacts to filesystem events, provider mutations, cron schedules, IDP logins and more, and can run notifications, webhooks, commands, antivirus scans, PGP operations, cross-backend copies, and other actions. Reference documentation for the platform lives at [sftpgo.com](https://sftpgo.com). The public copy of this API reference is at [sftpgo.com/rest-api](https://sftpgo.com/rest-api) and may lag the version shipped with your installation. ## Authentication Two authentication mechanisms are supported: - **JWT bearer token** — obtained via `POST /token` (admin) or `POST /user/token` (user) using HTTP basic authentication. Send subsequent requests with `Authorization: Bearer `. Tokens are short-lived and must be refreshed periodically. - **API key** — a long-lived credential sent via the `X-SFTPGO-API-KEY` header. Keys are scoped (admin or user), optionally bound to a specific account, and may have an expiration date. Keys bound to an account act as that account; unbound keys must append the target username with a dot (e.g. `Abcd1234.alice`). API key authentication must be explicitly enabled on the bound user / admin and on the HTTPD binding. HTTPS is strongly recommended for all API traffic. API key authentication is subject to the same defender / rate-limiting rules as other authentication methods. ## Conventions for API consumers (including AI agents) ### Discriminated objects Several schemas act as tagged unions: a numeric `type`, `provider`, or similar field selects **exactly one** active sub-configuration; the others must be empty / zero-valued. Server-side validation clears the unused fields on save, so round-tripping with extra data is safe but misleading. - `Filesystem.provider` (0 local, 1 S3, 2 GCS, 3 Azure Blob, 4 encrypted local, 5 SFTP, 6 HTTP) selects `s3config` / `gcsconfig` / `azblobconfig` / `cryptconfig` / `sftpconfig` / `httpconfig`. - `BaseEventAction.type` (1 HTTP, 2 Command, 3 Email, 8 Retention, 9 Filesystem, 11 Password expiration, 13 IDP account, 14 User inactivity, 15 Rotate logs, 16 IMAP, 17 ICAP, 18 Share expiration, 19 Event report) selects the matching `options.*_config` sub-object. Types 4–7 and 12 have no sub-config. Types 10 is reserved. - `EventRule.trigger` (1 fs, 2 provider, 3 schedule, 4 IP blocked, 5 certificate, 6 on demand, 7 IDP login) selects which `conditions.*` sub-filter is meaningful. - `EventActionFilesystemConfig.type` (1 rename, 2 delete, 3 mkdirs, 4 exist, 5 compress, 6 copy, 7 PGP, 8 metadata check, 9 decompress) selects the matching paths / renames / copy / compress / pgp sub-object. ### Go templates in Event Actions Action fields marked as *template-rendered* accept Go `text/template` syntax. The dot prefix is mandatory (`{{.Name}}`, not `{{Name}}`). A shared context exposes event metadata (`.Name`, `.VirtualPath`, `.Event`, `.Status`, `.FileSize`, `.Timestamp`, `.IP`, `.Role`, `.Email`, `.IDPFields`, `.Initiator.User`, `.Initiator.Admin`, `.Object.User`, `.Object.Admin`, `.Object.JSON`, `.RetentionChecks`, `.EventReports`, etc.). A custom function map adds helpers (`toJson`, `toJsonUnquoted`, `toBase64`, `urlEscape`, `pathBase`, `pathDir`, `pathJoin`, `stringJoin`, `stringReplace`, `stringContains`, `slicesContains`, `humanizeBytes`, `fromMillis`, `fromNanos`, ...). Which fields of the context are populated depends on the rule's `trigger`. Authoring these templates correctly is covered by the **[sftpgo](https://github.com/sftpgo/sftpgo-skill)** reference skill, which documents the full context, all registered functions, enum↔sub-config mappings, and ready-to-paste examples (including OIDC JIT provisioning). Load it into your AI assistant (Claude Code, Cursor, Copilot, Gemini, ChatGPT …) before authoring templates — the skill repository's `reference.md` is a single-file self-contained reference suitable for any LLM context. ### Templated fields — summary by action type - HTTP (`type: 1`): `http_config.endpoint`, `http_config.body`, `http_config.headers[].value`, `http_config.query_parameters[].value`, `http_config.parts[].filepath`, `http_config.parts[].body`, `http_config.parts[].headers[].value`. - Command (`type: 2`): `cmd_config.args[]` (each entry), `cmd_config.env_vars[].value`. - Email (`type: 3`): `email_config.recipients[]`, `email_config.bcc[]`, `email_config.subject`, `email_config.body`, `email_config.attachments[]`. - Filesystem (`type: 9`): path fields inside `fs_config.*` (renames, deletes, mkdirs, exist, compress.paths, copy, pgp.paths, decompress source/target). - IDP account check (`type: 13`): `idp_config.template_user`, `idp_config.template_admin` — whole JSON payloads rendered as templates. - IMAP (`type: 16`): `imap_config.email[]`, `imap_config.subject`, `imap_config.body`, `imap_config.attachments[]`, `imap_config.target_folder`. - ICAP (`type: 17`): `icap_config.paths[]`. ### Pitfalls worth surfacing in generated code - `{{.IDPFields}}` only contains the OIDC claims explicitly listed in the OIDC binding's `custom_fields` array. The role claim is in `{{.Role}}` (populated from the binding's `role_field` setting) — **not** in `{{.IDPFields}}` unless separately listed. - `{{.Initiator}}` is never nil, but exactly one of `{{.Initiator.User}}` / `{{.Initiator.Admin}}` is nil for any given event — always guard with `{{if ...}}...{{end}}`. - `{{.Object}}` is a lazy wrapper with typed accessors (`.User`, `.Admin`, `.Group`, `.Share`) and `.JSON` — not the concrete object. - Inside JSON bodies, wrap dynamic strings with `{{. | toJsonUnquoted}}` (pre-quoted positions) or `{{toJson .}}` (unquoted positions) — never concatenate raw. version: v2.7 contact: name: API support url: 'https://sftpgo.com' license: name: License url: 'https://sftpgo.com/assets/LICENSE.pdf' servers: - url: /api/v2 security: - BearerAuth: [] - APIKeyAuth: [] paths: /healthz: get: security: [] servers: - url: / tags: - healthcheck summary: Health check description: Checks whether the application is running and responding to requests. Returns "ok" if the service is healthy operationId: healthz responses: '200': description: successful operation content: text/plain; charset=utf-8: schema: type: string example: ok /shares/{id}: parameters: - name: id in: path description: the share id required: true schema: type: string get: security: - BasicAuth: [] tags: - public shares summary: Download shared files and folders as a single zip file description: Generates and returns a zip archive containing the shared files and folders. Only regular files and directories are included. The share must have the read scope, and the associated user must have list and download permissions operationId: get_share parameters: - in: query name: compress schema: type: boolean default: true required: false responses: '200': description: successful operation content: '*/*': schema: type: string format: binary '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: security: - BasicAuth: [] tags: - public shares summary: Upload files to the shared path description: Uploads one or more files to the shared path using multipart/form-data. The share must have the write scope, and the associated user must have the upload permission operationId: upload_to_share requestBody: content: multipart/form-data: schema: type: object properties: filenames: type: array items: type: string format: binary minItems: 1 uniqueItems: true required: true responses: '201': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '413': $ref: '#/components/responses/RequestEntityTooLarge' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /shares/{id}/files: parameters: - name: id in: path description: the share id required: true schema: type: string get: security: - BasicAuth: [] tags: - public shares summary: Download a single file from share description: Returns the file contents as response body. The share must have exactly one path defined and it must point to a directory. Use the path query parameter to specify which file within the shared directory to download operationId: download_share_file parameters: - in: query name: path required: true description: Path to the file to download. It must be URL encoded, for example the path "my dir/àdir/file.txt" must be sent as "my%20dir%2F%C3%A0dir%2Ffile.txt" schema: type: string - in: query name: inline required: false description: 'If set, the response will not have the Content-Disposition header set to `attachment`' schema: type: string responses: '200': description: successful operation content: '*/*': schema: type: string format: binary '206': description: successful operation content: '*/*': schema: type: string format: binary '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /shares/{id}/dirs: parameters: - name: id in: path description: the share id required: true schema: type: string get: security: - BasicAuth: [] tags: - public shares summary: Read shared directory contents description: Returns the contents of the specified directory within the share. The share must have exactly one path defined and it must point to a directory operationId: get_share_dir_contents parameters: - in: query name: path description: Path to the folder to read. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir". If empty or missing the user's start directory is assumed. If relative, the user's start directory is used as the base schema: type: string responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/DirEntry' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /shares/{id}/actions/mkdir: parameters: - name: id in: path description: the share id required: true schema: type: string post: security: - BasicAuth: [] tags: - public shares summary: Create a directory in shared path description: Creates a new directory within the shared path. The share must have the write scope operationId: create_share_dir parameters: - in: query name: path description: Path to the folder to create. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true - in: query name: mkdir_parents description: Create parent directories if they do not exist? schema: type: boolean required: false responses: '201': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /shares/{id}/files/{filePath}: parameters: - name: id in: path description: the share id required: true schema: type: string - name: filePath in: path description: | The name of the new file, including the relative path (e.g. `subdir/image.png`). Subdirectories are accepted. Standard URL encoding rules apply for special characters in filenames. required: true schema: type: string - name: X-SFTPGO-MTIME in: header schema: type: integer description: File modification time as unix timestamp in milliseconds post: security: - BasicAuth: [] tags: - public shares summary: Upload a single file to the shared path description: Uploads a single file using the raw request body (no multipart). The file path is specified in the URL. The share must have the write scope, and the associated user must have upload/overwrite permissions operationId: upload_single_to_share requestBody: content: '*/*': schema: type: string format: binary required: true responses: '201': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '413': $ref: '#/components/responses/RequestEntityTooLarge' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /shares-chunked-uploads: security: - BasicAuth: [] options: tags: - public shares summary: TUS Discovery description: Returns the server's capabilities. operationId: tus_share_discovery responses: '200': headers: Tus-Version: description: Supported protocol versions (e.g., 1.0.0). schema: type: string example: "1.0.0" Tus-Extension: description: Supported extensions. schema: type: string example: "creation,creation-with-upload" post: tags: - public shares summary: Create Upload (Creation) description: >- Initializes a new upload resource. The server will respond with a `Location` header containing the specific URL to send data chunks to via PATCH. operationId: tus_share_create_upload parameters: - in: header name: Tus-Resumable schema: type: string enum: ["1.0.0"] required: true description: TUS protocol version. - in: header name: Upload-Length schema: type: integer required: true description: The total size of the file in bytes. - in: header name: Upload-Metadata schema: type: string required: true description: >- File metadata encoded as Key/Value pairs. Format: `key Base64Value,key2 Base64Value`. **Mandatory Fields:** - `path`: The absolute destination path (e.g., `/folder/file.txt`). - `share_id`: Public Share ID. **Optional Fields:** - `mtime`: Last modification timestamp (Unix/String). - `mkdir_parents`: Set to "true" to automatically create parent directories. example: "path L215ZGlyL2ZpbGUuYmlu,share_id MTIzNA==,mkdir_parents ZmFsc2U=,mtime MTc2NzIyNDg3ODU0NQ==" responses: '201': description: Upload created successfully. headers: Location: description: The absolute or relative URL where data patches must be sent. schema: type: string example: "http://localhost:8080/user/files/chunked-upload/c9306132f81e42a9" Tus-Resumable: schema: type: string example: "1.0.0" '413': description: The file exceeds the maximum allowed size. '400': description: Missing headers or malformed metadata (e.g., missing 'path'). '401': description: Unauthorized (Invalid Token or Cookie). /shares-chunked-uploads/{id}: security: - BasicAuth: [] parameters: - in: path name: id schema: type: string required: true description: Unique upload ID generated by the server during the POST request. head: summary: Upload Status (Offset Check) description: Returns the current offset (how many bytes have been uploaded so far). Used for resuming. operationId: tus_share_upload_status tags: - public shares parameters: - in: header name: Tus-Resumable schema: type: string enum: ["1.0.0"] required: true responses: '200': description: Status retrieved successfully. headers: Upload-Offset: description: Current offset in bytes. schema: type: integer Upload-Length: description: Total expected length. schema: type: integer Tus-Resumable: schema: type: string example: "1.0.0" '404': description: Upload not found or expired. '403': description: Forbidden (User does not have permission to access this upload). patch: tags: - public shares summary: Upload Chunk (Core Transfer) description: Uploads a chunk of binary data or the entire file. operationId: tus_share_upload_chunk parameters: - in: header name: Tus-Resumable schema: type: string enum: ["1.0.0"] required: true - in: header name: Upload-Offset schema: type: integer required: true description: Must match exactly the current offset on the server. - in: header name: Content-Type schema: type: string enum: ["application/offset+octet-stream"] required: true description: Strictly required to be `application/offset+octet-stream`. - in: header name: Content-Length schema: type: integer required: true description: The size of the chunk in bytes being sent in this request body. requestBody: description: The binary data of the chunk. required: true content: application/offset+octet-stream: schema: type: string format: binary responses: '204': description: Chunk accepted successfully. headers: Upload-Offset: description: The new offset after writing the chunk. schema: type: integer Tus-Resumable: schema: type: string '409': description: >- Conflict. The provided `Upload-Offset` does not match the current server offset. The client should perform a HEAD request to get the correct offset and retry. '404': description: Invalid Upload ID. '415': description: Unsupported Media Type. Content-Type must be `application/offset+octet-stream`. /token: get: security: - BasicAuth: [] tags: - token summary: Get a new admin access token description: Authenticates an admin using HTTP Basic Auth and returns a JWT access token with its expiration. Use this token in the Authorization header as "Bearer " for subsequent API calls operationId: get_token parameters: - in: header name: X-SFTPGO-OTP schema: type: string required: false description: 'If you have 2FA configured for the admin attempting to log in you need to set the authentication code using this header parameter' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/Token' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /logout: get: security: - BearerAuth: [] tags: - token summary: Invalidate an admin access token description: Invalidates the current admin access token before its natural expiration, effectively logging the admin out operationId: logout responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/token: get: security: - BasicAuth: [] tags: - token summary: Get a new user access token description: Authenticates a user using HTTP Basic Auth and returns a JWT access token with its expiration. Use this token in the Authorization header as "Bearer " for subsequent user API calls operationId: get_user_token parameters: - in: header name: X-SFTPGO-OTP schema: type: string required: false description: 'If you have 2FA configured, for the HTTP protocol, for the user attempting to log in you need to set the authentication code using this header parameter' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/Token' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/logout: get: security: - BearerAuth: [] tags: - token summary: Invalidate a user access token description: Invalidates the current user access token before its natural expiration, effectively logging the user out operationId: client_logout responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /version: get: tags: - maintenance summary: Get version details description: Returns version details including the version number, build date, commit hash and enabled features operationId: get_version responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/VersionInfo' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admin/changepwd: put: security: - BearerAuth: [] tags: - admins summary: Change admin password description: Changes the password for the currently authenticated admin. Requires the current password for verification operationId: change_admin_password requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PwdChange' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admin/profile: get: security: - BearerAuth: [] tags: - admins summary: Get admin profile description: Returns the profile for the currently authenticated admin operationId: get_admin_profile responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/AdminProfile' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: security: - BearerAuth: [] tags: - admins summary: Update admin profile description: Updates the profile for the currently authenticated admin operationId: update_admin_profile requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AdminProfile' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admin/2fa/recoverycodes: get: security: - BearerAuth: [] tags: - admins summary: Get recovery codes description: 'Returns the recovery codes for the logged in admin. Recovery codes can be used if the admin loses access to their second factor auth device. Recovery codes are returned unencrypted' operationId: get_admin_recovery_codes responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/RecoveryCode' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: security: - BearerAuth: [] tags: - admins summary: Generate recovery codes description: 'Generates new recovery codes for the logged in admin. Generating new recovery codes you automatically invalidate old ones' operationId: generate_admin_recovery_codes responses: '200': description: successful operation content: application/json: schema: type: array items: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admin/totp/configs: get: security: - BearerAuth: [] tags: - admins summary: Get available TOTP configurations description: Returns the available TOTP (Time-based One-Time Password) configurations that the admin can use for two-factor authentication operationId: get_admin_totp_configs responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/TOTPConfig' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admin/totp/generate: post: security: - BearerAuth: [] tags: - admins summary: Generate a new TOTP secret description: 'Generates a new TOTP secret, including the QR code as png, using the specified configuration for the logged in admin' operationId: generate_admin_totp_secret requestBody: required: true content: application/json: schema: type: object properties: config_name: type: string description: 'name of the configuration to use to generate the secret' responses: '200': description: successful operation content: application/json: schema: type: object properties: config_name: type: string issuer: type: string secret: type: string url: type: string qr_code: type: string format: byte description: 'QR code png encoded as BASE64' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admin/totp/validate: post: security: - BearerAuth: [] tags: - admins summary: Validate a one time authentication code description: 'Checks if the given authentication code can be validated using the specified secret and config name' operationId: validate_admin_totp_secret requestBody: required: true content: application/json: schema: type: object properties: config_name: type: string description: 'name of the configuration to use to validate the passcode' passcode: type: string description: 'passcode to validate' secret: type: string description: 'secret to use to validate the passcode' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Passcode successfully validated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admin/totp/save: post: security: - BearerAuth: [] tags: - admins summary: Save a TOTP config description: 'Saves the specified TOTP config for the logged in admin' operationId: save_admin_totp_config requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AdminTOTPConfig' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: TOTP configuration saved '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /connections: get: tags: - connections summary: Get active connections description: Returns a list of currently active connections, including details about each connected user and their ongoing uploads or downloads operationId: get_connections responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/ConnectionStatus' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/connections/{connectionID}': delete: tags: - connections summary: Close a connection description: Forcefully terminates an active connection. Any in-progress transfers will be interrupted operationId: close_connection parameters: - name: connectionID in: path description: ID of the connection to close required: true schema: type: string responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Connection closed '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /iplists/{type}: parameters: - name: type in: path description: IP list type required: true schema: $ref: '#/components/schemas/IPListType' get: tags: - IP Lists summary: Get IP list entries description: Returns the entries in the specified IP list. Use the filter parameter to search for specific IP addresses or networks operationId: get_ip_list_entries parameters: - in: query name: filter schema: type: string description: restrict results to ipornet matching or starting with this filter - in: query name: from schema: type: string description: ipornet to start from required: false - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering entries by ipornet field. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/IPListEntry' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - IP Lists summary: Add a new IP list entry description: Adds an IP address or a CIDR network (e.g., 192.168.1.0/24) to the specified list operationId: add_ip_list_entry requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/IPListEntry' responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Entry added '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /iplists/{type}/{ipornet}: parameters: - name: type in: path description: IP list type required: true schema: $ref: '#/components/schemas/IPListType' - name: ipornet in: path required: true schema: type: string get: tags: - IP Lists summary: Get IP list entry by IP/network description: Returns the details of the specified IP address or network entry operationId: get_ip_list_by_ipornet responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/IPListEntry' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - IP Lists summary: Update IP list entry description: Updates an existing IP list entry operationId: update_ip_list_entry requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/IPListEntry' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Entry updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - IP Lists summary: Delete IP list entry description: Deletes an existing IP list entry operationId: delete_ip_list_entry responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Entry deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /defender/hosts: get: tags: - defender summary: Get defender hosts description: Returns a list of hosts that are currently banned or have recorded violations operationId: get_defender_hosts responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/DefenderEntry' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /defender/hosts/{id}: parameters: - name: id in: path description: host id required: true schema: type: string get: tags: - defender summary: Get defender host by ID description: Returns ban status and violation details for the specified host operationId: get_defender_host_by_id responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/DefenderEntry' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - defender summary: Remove a host from the defender lists description: Unbans the specified host and clears all its recorded violations operationId: delete_defender_host_by_id responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /retention/users/checks: get: tags: - data retention summary: Get active retention checks description: Returns a list of retention checks that are currently in progress operationId: get_users_retention_checks responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/RetentionCheck' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /quotas/users/scans: get: tags: - quota summary: Get active user quota scans description: Returns a list of user quota scans that are currently in progress operationId: get_users_quota_scans responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/QuotaScan' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /quotas/users/{username}/scan: parameters: - name: username in: path description: the username required: true schema: type: string post: tags: - quota summary: Start a user quota scan description: Starts a new quota scan for the given user. A quota scan recalculates the number of files and their total size for the specified user, including any virtual folders in their quota. Returns 409 if a scan is already running for this user operationId: start_user_quota_scan responses: '202': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Scan started '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /quotas/users/{username}/usage: parameters: - name: username in: path description: the username required: true schema: type: string - in: query name: mode required: false description: the update mode specifies if the given quota usage values should be added or replace the current ones schema: type: string enum: - add - reset description: | Update type: * `add` - add the specified quota limits to the current used ones * `reset` - reset the values to the specified ones. This is the default example: reset put: tags: - quota summary: Update disk quota usage limits description: Manually sets or adjusts the current disk quota usage (files count and total size) for the given user. Use "reset" mode to set exact values, or "add" mode to increment/decrement the current usage operationId: user_quota_update_usage requestBody: required: true description: 'If used_quota_size and used_quota_files are missing they will default to 0, this means that if mode is "add" the current value, for the missing field, will remain unchanged, if mode is "reset" the missing field is set to 0' content: application/json: schema: $ref: '#/components/schemas/QuotaUsage' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Quota updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /quotas/users/{username}/transfer-usage: parameters: - name: username in: path description: the username required: true schema: type: string - in: query name: mode required: false description: the update mode specifies if the given quota usage values should be added or replace the current ones schema: type: string enum: - add - reset description: | Update type: * `add` - add the specified quota limits to the current used ones * `reset` - reset the values to the specified ones. This is the default example: reset put: tags: - quota summary: Update transfer quota usage limits description: Manually sets or adjusts the current transfer quota usage (upload and download data) for the given user. Use "reset" mode to set exact values, or "add" mode to increment/decrement the current usage operationId: user_transfer_quota_update_usage requestBody: required: true description: 'If used_upload_data_transfer and used_download_data_transfer are missing they will default to 0, this means that if mode is "add" the current value, for the missing field, will remain unchanged, if mode is "reset" the missing field is set to 0' content: application/json: schema: $ref: '#/components/schemas/TransferQuotaUsage' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Quota updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /quotas/folders/scans: get: tags: - quota summary: Get active folder quota scans description: Returns a list of virtual folder quota scans that are currently in progress operationId: get_folders_quota_scans responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/FolderQuotaScan' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /quotas/folders/{name}/scan: parameters: - name: name in: path description: folder name required: true schema: type: string post: tags: - quota summary: Start a folder quota scan description: Starts a new quota scan for the given virtual folder. A quota scan recalculates the number of files and their total size for the specified folder. Returns 409 if a scan is already running for this folder operationId: start_folder_quota_scan responses: '202': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Scan started '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /quotas/folders/{name}/usage: parameters: - name: name in: path description: folder name required: true schema: type: string - in: query name: mode required: false description: the update mode specifies if the given quota usage values should be added or replace the current ones schema: type: string enum: - add - reset description: | Update type: * `add` - add the specified quota limits to the current used ones * `reset` - reset the values to the specified ones. This is the default example: reset put: tags: - quota summary: Update folder quota usage limits description: Manually sets or adjusts the current disk quota usage (files count and total size) for the given virtual folder. Use "reset" mode to set exact values, or "add" mode to increment/decrement the current usage operationId: folder_quota_update_usage parameters: - in: query name: mode required: false description: the update mode specifies if the given quota usage values should be added or replace the current ones schema: type: string enum: - add - reset description: | Update type: * `add` - add the specified quota limits to the current used ones * `reset` - reset the values to the specified ones. This is the default example: reset requestBody: required: true description: 'If used_quota_size and used_quota_files are missing they will default to 0, this means that if mode is "add" the current value, for the missing field, will remain unchanged, if mode is "reset" the missing field is set to 0' content: application/json: schema: $ref: '#/components/schemas/QuotaUsage' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Quota updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/Conflict' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /folders: get: tags: - folders summary: Get folders description: Returns a list of virtual folders. Virtual folders can be mapped into user home directories to provide access to additional storage locations operationId: get_folders parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering folders by name. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/BaseVirtualFolder' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - folders summary: Add folder operationId: add_folder description: Adds a new virtual folder. After creation, a quota scan is required to populate the used files/size counters parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BaseVirtualFolder' examples: local_folder: summary: Local filesystem folder description: Local folders use `mapped_path` (required) as the storage root. value: name: "shared-data" mapped_path: "/srv/sftpgo/shared" description: "Shared data folder" s3_folder: summary: S3-backed virtual folder (provider 1) description: For cloud folders `mapped_path` is irrelevant — storage is identified via the provider-specific config. value: name: "s3-archive" description: "Archive on S3" filesystem: provider: 1 s3config: bucket: "my-archive-bucket" region: "eu-west-1" key_prefix: "archive/" access_key: "AKIAIOSFODNN7EXAMPLE" access_secret: status: "Plain" payload: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" azure_folder: summary: Azure Blob folder (provider 3) value: name: "corporate-backup-azure" description: "Corporate backup container" filesystem: provider: 3 azblobconfig: container: "backups" account_name: "mystorageaccount" account_key: status: "Plain" payload: "base64-storage-account-key" key_prefix: "sftpgo/" encrypted_local_folder: summary: Encrypted local folder (provider 4) description: Server-side-encrypted storage. Different folders may use different passphrases, giving per-folder key separation. value: name: "encrypted-sensitive" mapped_path: "/srv/sftpgo/encrypted" description: "Sensitive documents (server-side encryption)" filesystem: provider: 4 cryptconfig: passphrase: status: "Plain" payload: "" sftp_backend_folder: summary: SFTP-to-SFTP virtual folder (provider 5) description: Mounts a remote SFTP directory as a virtual folder. Useful for presenting multiple backend SFTP servers to users through a single SFTPGo endpoint. value: name: "partner-sftp" description: "Partner's SFTP server mounted as a shared folder" filesystem: provider: 5 sftpconfig: endpoint: "partner.example.com:22" username: "exchange" password: status: "Plain" payload: "partner-password" prefix: "/incoming" fingerprints: ["SHA256:..."] responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/BaseVirtualFolder' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/folders/{name}': parameters: - name: name in: path description: folder name required: true schema: type: string get: tags: - folders summary: Get folder by name description: Returns the virtual folder with the given name if it exists operationId: get_folder_by_name parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/BaseVirtualFolder' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - folders summary: Update folder description: Updates an existing folder operationId: update_folder requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BaseVirtualFolder' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Folder updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - folders summary: Delete folder description: Deletes an existing folder operationId: delete_folder responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Folder deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /groups: get: tags: - groups summary: Get groups description: Returns a list of groups. Groups allow you to apply shared settings to multiple users at once, reducing configuration overhead operationId: get_groups parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering groups by name. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/Group' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - groups summary: Add group operationId: add_group description: Adds a new group parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Group' examples: minimal_group: summary: Minimal group (name only) description: All settings can be left empty; the group becomes a pure membership marker. Useful for grouping users to target event-rule conditions (`conditions.options.group_names[]`). value: name: "ops" description: "Operations team" group_with_fs_and_quota: summary: Group supplying a filesystem and quota description: "When a user joins this group as a primary group (type 1 in User.groups[]), these settings are merged into the user's effective configuration at login." value: name: "standard-users" description: "Default user settings" user_settings: home_dir: "/srv/sftpgo/data/%username%" permissions: /: ["list", "download", "upload", "overwrite", "delete", "rename", "create_dirs"] max_sessions: 5 quota_size: 10737418240 quota_files: 0 upload_bandwidth: 10240 download_bandwidth: 20480 group_with_s3_backend: summary: Group supplying an S3 backend description: Used as a primary group, this gives all members isolated prefixes in the same bucket. `%username%` is interpolated at login time. value: name: "s3-users" description: "Users on S3 storage" user_settings: permissions: /: ["*"] filesystem: provider: 1 s3config: bucket: "sftpgo-users" region: "eu-west-1" access_key: "AKIA..." access_secret: status: "Plain" payload: "secret" key_prefix: "users/%username%/" group_with_share_policy: summary: Group that enforces a share governance policy description: "Restricts what members can do when creating Web shares. `sharing_policy.mode = 1` makes the listed permissions mandatory (not merely suggested). Pair with `webclient_options` to hide scopes users cannot use." value: name: "read-only-sharers" description: "Users may only create read-only shares" user_settings: filters: default_shares_policies: - path: "/" permissions: ["read"] webclient: - share_no_password_disabled - share_policy_change_disabled group_with_virtual_folder: summary: Group supplying a shared virtual folder description: The folder `corporate-archive` must already exist (POST /folders). All members of this group get `/archive` mounted read-only from that folder. value: name: "archive-viewers" description: "Users with archive access" virtual_folders: - virtual_path: "/archive" name: "corporate-archive" quota_size: -1 quota_files: -1 user_settings: permissions: /archive: ["list", "download"] responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/Group' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/groups/{name}': parameters: - name: name in: path description: group name required: true schema: type: string get: tags: - groups summary: Get group by name description: Returns the group with the given name if it exists operationId: get_group_by_name parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/Group' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - groups summary: Update group description: Updates an existing group operationId: update_group requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Group' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Group updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - groups summary: Delete group description: Deletes an existing group operationId: delete_group responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Group deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /roles: get: tags: - roles summary: Get roles description: Returns a list of roles. Roles can be assigned to users and admins to control their permissions and scope operationId: get_roles parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering roles by name. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/Role' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - roles summary: Add role operationId: add_role description: Adds a new role requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Role' responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/Role' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/roles/{name}': parameters: - name: name in: path description: role name required: true schema: type: string get: tags: - roles summary: Get role by name description: Returns the role with the given name if it exists operationId: get_role_by_name responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/Role' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - roles summary: Update role description: Updates an existing role operationId: update_role requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Role' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Role updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - roles summary: Delete role description: Deletes an existing role operationId: delete_role responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Role deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /eventactions: get: tags: - event manager summary: Get event actions description: Returns a list of event actions. Event actions define what happens when an event rule is triggered (e.g., send email, make HTTP call, execute command) operationId: get_event_actions parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering actions by name. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/BaseEventAction' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - event manager summary: Add event action operationId: add_event_action description: Adds a new event action parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BaseEventAction' examples: email_action: summary: Email notification action description: | Email sent on filesystem events. `subject` and `body` are Go templates; always use the `.` prefix (`{{.Event}}`, `{{.Name}}`, `{{.VirtualPath}}`). value: name: "send-upload-email" type: 3 description: "Notify admin on file upload" options: email_config: recipients: ["admin@example.com"] subject: "File uploaded: {{.Event}}" body: "User {{.Name}} uploaded file {{.VirtualPath}} ({{humanizeBytes .FileSize}}) from {{.IP}}" http_action: summary: HTTP webhook action (JSON body) description: | HTTP webhook with a JSON body. Wrap dynamic strings with `toJsonUnquoted` inside quoted positions, or use `toJson` in unquoted positions. value: name: "webhook-notify" type: 1 description: "Send upload event to webhook" options: http_config: endpoint: "https://hooks.example.com/sftpgo" method: "POST" timeout: 30 headers: - key: "Content-Type" value: "application/json" body: '{"user":{{toJson .Name}},"path":{{toJson .VirtualPath}},"size":{{.FileSize}},"when":{{toJson (.Timestamp.UTC.Format "2006-01-02T15:04:05Z")}}}' idp_jit_action: summary: OIDC JIT user provisioning (IDP account check) description: | Premium-tier action. `template_user` is a Go template that renders a User JSON payload. Use `.Role` (from the OIDC binding's `role_field`) for role-based branching — not `{{index .IDPFields "..."}}`, unless the claim is also listed in the binding's `custom_fields`. value: name: "idp-provision" type: 13 description: "JIT provision user on first IDP login" options: idp_config: mode: 1 template_user: | { "username": {{toJson .Name}}, "email": {{if .Email}}{{toJson .Email}}{{else}}""{{end}}, "status": 1, "permissions": {"/":["*"]}, "groups": [ {{if eq .Role "app.power-user"}} {"type": 1, "name": "power-users"} {{else}} {"type": 1, "name": "standard-users"} {{end}} ] } command_action: summary: Command hook (external script) description: | Runs a binary on the SFTPGo host. The command must be listed in `SFTPGO_HOOK__ENABLED_ACTION_COMMANDS`. Each element of `args` and each `env_vars[].value` is template-rendered. value: name: "log-upload" type: 2 description: "Log upload to external tool" options: cmd_config: cmd: "/usr/local/bin/log-upload.sh" timeout: 10 args: - "--user={{.Name}}" - "--path={{.VirtualPath}}" - "--size={{.FileSize}}" env_vars: - key: "SFTPGO_EVENT_ID" value: "{{.UID}}" fs_delete_action: summary: Filesystem delete (trailing-slash = empty directory) description: | A trailing `/` on a delete path empties the directory while keeping it in place. Without the trailing slash the directory is removed recursively. value: name: "clear-staging" type: 9 description: "Empty the user's staging folder contents" options: fs_config: type: 2 deletes: - "/staging/{{.Name}}/" icap_action: summary: ICAP antivirus scan on upload description: | Premium-tier action. Each entry of `paths[]` is a template. For on-upload scans, `{{.VirtualPath}}` resolves to the file just uploaded (including while it sits at the staged-upload temp path). `block_action` controls what happens to an infected file (1 ignore, 2 delete, 3 quarantine). `failure_policy` controls what happens when the ICAP scan itself fails (1 ignore, 2 delete, 3 quarantine). `adapt_action` handles the case where the ICAP server returns a modified file (1 ignore, 2 delete, 3 quarantine, 4 overwrite with the modified content). value: name: "icap-scan" type: 17 options: icap_config: endpoint: "icap://icap.example.com:1344/virus_scan" method: "REQMOD" timeout: 30 paths: - "{{.VirtualPath}}" block_action: 2 adapt_action: 1 failure_policy: 1 responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/BaseEventAction' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/eventactions/{name}': parameters: - name: name in: path description: action name required: true schema: type: string get: tags: - event manager summary: Get event action by name description: Returns the event action with the given name if it exists operationId: get_event_action_by_name parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/BaseEventAction' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - event manager summary: Update event action description: Updates an existing event action operationId: update_event_action requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BaseEventAction' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Event action updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - event manager summary: Delete event action description: Deletes an existing event action operationId: delete_event_action responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Event action deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /eventrules: get: tags: - event manager summary: Get event rules description: Returns a list of event rules. Event rules define triggers (e.g., file upload, schedule, provider events) and link them to one or more actions operationId: get_event_rules parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering rules by name. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/EventRule' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - event manager summary: Add event rule operationId: add_event_rule description: Adds a new event rule parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EventRuleMinimal' examples: upload_notification: summary: Email notification on filesystem upload (trigger 1) description: | Filesystem trigger scoped to usernames matching the pattern `user_*`. `conditions.fs_events[]` selects which filesystem events fire the rule. value: name: "notify-on-upload" status: 1 description: "Send an email when a file is uploaded" trigger: 1 conditions: fs_events: ["upload", "first-upload"] options: names: - pattern: "user_*" actions: - name: "send-upload-email" order: 1 relation_options: stop_on_failure: false execute_sync: false icap_scan_on_upload: summary: Synchronous ICAP scan on upload (trigger 1, execute_sync) description: | `execute_sync: true` + `stop_on_failure: true` makes the ICAP result gate the upload: if the scan fails or flags the file, the upload is aborted and the file is deleted. value: name: "icap-scan" status: 1 trigger: 1 conditions: fs_events: ["upload"] actions: - name: "icap-scan-action" order: 1 relation_options: execute_sync: true stop_on_failure: true provider_event_audit: summary: Audit webhook on user provider events (trigger 2) description: | Fires on any user add/update/delete. The webhook body should include `{{.Object.JSON}}` to forward the full user state. value: name: "audit-users" status: 1 trigger: 2 conditions: provider_events: ["add", "update", "delete"] options: provider_objects: ["user"] actions: - name: "audit-webhook" order: 1 scheduled_retention: summary: Nightly retention + email report (trigger 3) description: | Chains two actions: a retention action populates `{{.RetentionChecks}}`, then an email action formats it into a report. Order matters — `order: 1` runs before `order: 2`. value: name: "nightly-retention" status: 1 trigger: 3 conditions: schedules: - hour: "2" minute: "0" day_of_week: "*" day_of_month: "*" month: "*" actions: - name: "retention-home-90d" order: 1 - name: "retention-email" order: 2 ip_blocked_alert: summary: Alert on IP block (trigger 4) description: | Fires whenever the defender blocks an IP. No `conditions` filters are applicable. value: name: "ip-blocked-alert" status: 1 trigger: 4 conditions: {} actions: - name: "slack-security" order: 1 oidc_jit_provisioning: summary: OIDC JIT user provisioning (trigger 7) description: | Pairs with an IDP account-check action (`type: 13`). `idp_login_event: 1` restricts the rule to user logins (vs admin logins). `execute_sync: true` is required so the action completes before SFTPGo decides whether to allow the login. value: name: "idp-jit" status: 1 trigger: 7 conditions: idp_login_event: 1 actions: - name: "idp-provision" order: 1 relation_options: execute_sync: true stop_on_failure: true staged_upload_virus_scan: summary: Pre-publish ICAP scan (staged upload, trigger 1) description: | `execute_before_file_publish: true` runs the action while the file is still at its atomic-upload temp path. On failure the upload is rejected and the temp file is removed. Requires atomic uploads to be enabled. Mutually exclusive with `execute_sync`. value: name: "staged-icap-scan" status: 1 trigger: 1 conditions: fs_events: ["upload"] actions: - name: "icap-scan-action" order: 1 relation_options: execute_before_file_publish: true stop_on_failure: true responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/EventRule' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/eventrules/{name}': parameters: - name: name in: path description: rule name required: true schema: type: string get: tags: - event manager summary: Get event rule by name description: Returns the event rule with the given name if it exists operationId: get_event_rule_by_name parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/EventRule' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - event manager summary: Update event rule description: Updates an existing event rule operationId: update_event_rule requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EventRuleMinimal' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Event rule updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - event manager summary: Delete event rule description: Deletes an existing event rule operationId: delete_event_rule responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Event rule deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/eventrules/run/{name}': parameters: - name: name in: path description: on-demand rule name required: true schema: type: string post: tags: - event manager summary: Run an on-demand event rule description: Triggers the specified on-demand event rule. The rule's actions will execute asynchronously in the background. To receive a notification when execution completes, add an appropriate action (e.g., email or HTTP callback) to the rule operationId: run_event_rule responses: '202': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Event rule started '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /events/fs: get: tags: - events summary: Get filesystem events description: 'Returns filesystem events (uploads, downloads, deletes, etc.) matching the specified filters. Requires the "eventsearcher" plugin to be configured. Supports cursor-based pagination via the from_id parameter' operationId: get_fs_events parameters: - in: query name: start_timestamp schema: type: integer format: int64 minimum: 0 default: 0 required: false description: 'the event timestamp, unix timestamp in nanoseconds, must be greater than or equal to the specified one. 0 or missing means omit this filter' - in: query name: end_timestamp schema: type: integer format: int64 minimum: 0 default: 0 required: false description: 'the event timestamp, unix timestamp in nanoseconds, must be less than or equal to the specified one. 0 or missing means omit this filter' - in: query name: actions schema: type: array items: $ref: '#/components/schemas/FsEventAction' description: 'the event action must be included among those specified. Empty or missing means omit this filter. Actions must be specified comma separated' explode: false required: false - in: query name: username schema: type: string description: 'the event username must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: ip schema: type: string description: 'the event IP must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: ssh_cmd schema: type: string description: 'the event SSH command must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: fs_provider schema: $ref: '#/components/schemas/FsProviders' description: 'the event filesystem provider must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: bucket schema: type: string description: 'the bucket must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: endpoint schema: type: string description: 'the endpoint must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: protocols schema: type: array items: $ref: '#/components/schemas/EventProtocols' description: 'the event protocol must be included among those specified. Empty or missing means omit this filter. Values must be specified comma separated' explode: false required: false - in: query name: statuses schema: type: array items: $ref: '#/components/schemas/FsEventStatus' description: 'the event status must be included among those specified. Empty or missing means omit this filter. Values must be specified comma separated' explode: false required: false - in: query name: instance_ids schema: type: array items: type: string description: 'the event instance id must be included among those specified. Empty or missing means omit this filter. Values must be specified comma separated' explode: false required: false - in: query name: from_id schema: type: string description: 'the event id to start from. This is useful for cursor based pagination. Empty or missing means omit this filter.' required: false - in: query name: role schema: type: string description: 'User role. Empty or missing means omit this filter. Ignored if the admin has a role' required: false - in: query name: csv_export schema: type: boolean default: false required: false description: 'If enabled, events are exported as a CSV file' - in: query name: limit schema: type: integer minimum: 1 maximum: 1000 default: 100 required: false description: 'The maximum number of items to return. Max value is 1000, default is 100' - in: query name: order required: false description: Ordering events by timestamp. Default DESC schema: type: string enum: - ASC - DESC example: DESC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/FsEvent' text/csv: schema: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /events/provider: get: tags: - events summary: Get provider events description: 'Returns provider events (user/admin/folder create, update, delete, etc.) matching the specified filters. Requires the "eventsearcher" plugin to be configured. Supports cursor-based pagination via the from_id parameter' operationId: get_provider_events parameters: - in: query name: start_timestamp schema: type: integer format: int64 minimum: 0 default: 0 required: false description: 'the event timestamp, unix timestamp in nanoseconds, must be greater than or equal to the specified one. 0 or missing means omit this filter' - in: query name: end_timestamp schema: type: integer format: int64 minimum: 0 default: 0 required: false description: 'the event timestamp, unix timestamp in nanoseconds, must be less than or equal to the specified one. 0 or missing means omit this filter' - in: query name: actions schema: type: array items: $ref: '#/components/schemas/ProviderEventAction' description: 'the event action must be included among those specified. Empty or missing means omit this filter. Actions must be specified comma separated' explode: false required: false - in: query name: username schema: type: string description: 'the event username must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: ip schema: type: string description: 'the event IP must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: object_name schema: type: string description: 'the event object name must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: object_types schema: type: array items: $ref: '#/components/schemas/ProviderEventObjectType' description: 'the event object type must be included among those specified. Empty or missing means omit this filter. Values must be specified comma separated' explode: false required: false - in: query name: instance_ids schema: type: array items: type: string description: 'the event instance id must be included among those specified. Empty or missing means omit this filter. Values must be specified comma separated' explode: false required: false - in: query name: from_id schema: type: string description: 'the event id to start from. This is useful for cursor based pagination. Empty or missing means omit this filter.' required: false - in: query name: role schema: type: string description: 'Admin role. Empty or missing means omit this filter. Ignored if the admin has a role' required: false - in: query name: csv_export schema: type: boolean default: false required: false description: 'If enabled, events are exported as a CSV file' - in: query name: omit_object_data schema: type: boolean default: false required: false description: 'If enabled, returned events will not contain the `object_data` field' - in: query name: limit schema: type: integer minimum: 1 maximum: 1000 default: 100 required: false description: 'The maximum number of items to return. Max value is 1000, default is 100' - in: query name: order required: false description: Ordering events by timestamp. Default DESC schema: type: string enum: - ASC - DESC example: DESC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/ProviderEvent' text/csv: schema: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /events/logs: get: tags: - events summary: Get log events description: 'Returns log events (login attempts, failed operations, etc.) matching the specified filters. Requires the "eventsearcher" plugin to be configured. Supports cursor-based pagination via the from_id parameter' operationId: get_log_events parameters: - in: query name: start_timestamp schema: type: integer format: int64 minimum: 0 default: 0 required: false description: 'the event timestamp, unix timestamp in nanoseconds, must be greater than or equal to the specified one. 0 or missing means omit this filter' - in: query name: end_timestamp schema: type: integer format: int64 minimum: 0 default: 0 required: false description: 'the event timestamp, unix timestamp in nanoseconds, must be less than or equal to the specified one. 0 or missing means omit this filter' - in: query name: events schema: type: array items: $ref: '#/components/schemas/LogEventType' description: 'the log events must be included among those specified. Empty or missing means omit this filter. Events must be specified comma separated' explode: false required: false - in: query name: username schema: type: string description: 'the event username must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: ip schema: type: string description: 'the event IP must be the same as the one specified. Empty or missing means omit this filter' required: false - in: query name: protocols schema: type: array items: $ref: '#/components/schemas/EventProtocols' description: 'the event protocol must be included among those specified. Empty or missing means omit this filter. Values must be specified comma separated' explode: false required: false - in: query name: instance_ids schema: type: array items: type: string description: 'the event instance id must be included among those specified. Empty or missing means omit this filter. Values must be specified comma separated' explode: false required: false - in: query name: from_id schema: type: string description: 'the event id to start from. This is useful for cursor based pagination. Empty or missing means omit this filter.' required: false - in: query name: role schema: type: string description: 'User role. Empty or missing means omit this filter. Ignored if the admin has a role' required: false - in: query name: csv_export schema: type: boolean default: false required: false description: 'If enabled, events are exported as a CSV file' - in: query name: limit schema: type: integer minimum: 1 maximum: 1000 default: 100 required: false description: 'The maximum number of items to return. Max value is 1000, default is 100' - in: query name: order required: false description: Ordering events by timestamp. Default DESC schema: type: string enum: - ASC - DESC example: DESC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/LogEvent' text/csv: schema: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /apikeys: get: security: - BearerAuth: [] tags: - API keys summary: Get API keys description: Returns a list of API keys. For security reasons, hashed keys are omitted from the response. API keys can be used as an alternative to JWT tokens for authentication operationId: get_api_keys parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering API keys by id. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/APIKey' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: security: - BearerAuth: [] tags: - API keys summary: Add API key description: Adds a new API key operationId: add_api_key requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/APIKey' responses: '201': description: successful operation headers: X-Object-ID: schema: type: string description: ID for the new created API key Location: schema: type: string description: URI to retrieve the details for the new created API key content: application/json: schema: type: object properties: mesage: type: string example: 'API key created. This is the only time the API key is visible, please save it.' key: type: string description: 'generated API key' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/apikeys/{id}': parameters: - name: id in: path description: the key id required: true schema: type: string get: security: - BearerAuth: [] tags: - API keys summary: Get API key by ID description: Returns the API key with the given ID if it exists. For security reasons, the hashed key is omitted from the response operationId: get_api_key_by_id responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/APIKey' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: security: - BearerAuth: [] tags: - API keys summary: Update API key description: Updates an existing API key. The key value, creation date and last use timestamp cannot be changed operationId: update_api_key requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/APIKey' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: API key updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: security: - BearerAuth: [] tags: - API keys summary: Delete API key description: Deletes an existing API key operationId: delete_api_key responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: API key deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /admins: get: tags: - admins summary: Get admins description: Returns a list of admin accounts. For security reasons, hashed passwords are omitted from the response operationId: get_admins parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering admins by username. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/Admin' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - admins summary: Add admin description: 'Adds a new admin. Recovery codes and TOTP configuration cannot be set using this API: each admin must use the specific APIs' operationId: add_admin requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Admin' responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/Admin' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/admins/{username}': parameters: - name: username in: path description: the admin username required: true schema: type: string get: tags: - admins summary: Get admin by username description: Returns the admin with the given username if it exists. For security reasons, the hashed password is omitted from the response operationId: get_admin_by_username responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/Admin' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - admins summary: Update admin description: 'Updates an existing admin. Recovery codes and TOTP configuration cannot be set/updated using this API: each admin must use the specific APIs. You are not allowed to update the admin impersonated using an API key' operationId: update_admin requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Admin' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Admin updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - admins summary: Delete admin description: Deletes an existing admin operationId: delete_admin responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Admin deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/admins/{username}/2fa/disable': parameters: - name: username in: path description: the admin username required: true schema: type: string put: tags: - admins summary: Disable admin two-factor authentication description: Disables two-factor authentication for the specified admin. Use this when an admin has lost access to their authenticator device and has no remaining recovery codes operationId: disable_admin_2fa responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: 2FA disabled '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/admins/{username}/forgot-password': parameters: - name: username in: path description: the admin username required: true schema: type: string post: security: [] tags: - admins summary: Send a password reset code by email description: 'You must set up an SMTP server and the account must have a valid email address, in which case SFTPGo will send a code via email to reset the password. If the specified admin does not exist, the request will be silently ignored (a success response will be returned) to avoid disclosing existing admins' operationId: admin_forgot_password responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/admins/{username}/reset-password': parameters: - name: username in: path description: the admin username required: true schema: type: string post: security: [] tags: - admins summary: Reset the password description: 'Set a new password using the code received via email' operationId: admin_reset_password requestBody: content: application/json: schema: type: object properties: code: type: string password: type: string required: true responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /users: get: tags: - users summary: Get users description: Returns a list of user accounts. For security reasons, hashed passwords are omitted from the response operationId: get_users parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering users by username. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/User' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - users summary: Add user description: 'Adds a new user. Recovery codes and TOTP configuration cannot be set using this API: each user must use the specific APIs' operationId: add_user parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the hash of the password and the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/User' examples: local_user: summary: Local filesystem user description: "Minimal viable user with the local backend (`provider = 0`). `home_dir` is required for local and encrypted-local backends." value: username: "example_user" password: "changeme" status: 1 home_dir: "/srv/sftpgo/data/example_user" permissions: /: ["*"] quota_size: 104857600 s3_user: summary: S3-backed user (provider 1) description: "Stores files in an S3 prefix. Secrets must be wrapped as `{status:\"Plain\", payload:\"...\"}` on write; on read they come back as `{status:\"AES-256-GCM\", payload:\"...\", ...}`." value: username: "s3_user" password: "changeme" status: 1 permissions: /: ["list", "download", "upload", "overwrite", "delete", "rename"] filesystem: provider: 1 s3config: bucket: "my-bucket" region: "us-east-1" access_key: "AKIAIOSFODNN7EXAMPLE" access_secret: status: "Plain" payload: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" key_prefix: "users/s3_user/" azure_user: summary: Azure Blob Storage user (provider 3) description: Storage-account key auth; for SAS or AAD-based auth see `azblobconfig.sas_url` and `azblobconfig.use_emulator`. value: username: "azure_user" password: "changeme" status: 1 permissions: /: ["*"] filesystem: provider: 3 azblobconfig: container: "sftpgo-data" account_name: "mystorageaccount" account_key: status: "Plain" payload: "base64-storage-account-key" key_prefix: "users/azure_user/" gcs_user: summary: Google Cloud Storage user (provider 2) description: Credentials must be the full service-account JSON, base64-encoded as the secret payload. value: username: "gcs_user" password: "changeme" status: 1 permissions: /: ["*"] filesystem: provider: 2 gcsconfig: bucket: "my-gcs-bucket" credentials: status: "Plain" payload: "" key_prefix: "users/gcs_user/" sftp_backend_user: summary: SFTP-backend user (provider 5, "SFTP-to-SFTP" gateway) description: Acts as an SFTPGo-hosted proxy into another SFTP server. Useful for consolidating access, adding 2FA, or applying event rules on top of an existing server. value: username: "proxy_user" password: "changeme" status: 1 permissions: /: ["*"] filesystem: provider: 5 sftpconfig: endpoint: "backend.example.com:22" username: "backend_user" password: status: "Plain" payload: "backend-password" prefix: "/home/backend_user" fingerprints: ["SHA256:AbCdEf..."] encrypted_local_user: summary: Encrypted local filesystem user (provider 4) description: Server-side encryption at rest using `cryptconfig.passphrase`. Home dir is required. Each file has a random per-file nonce; losing the passphrase loses all data. value: username: "crypto_user" password: "changeme" status: 1 home_dir: "/srv/sftpgo/data/crypto_user" permissions: /: ["*"] filesystem: provider: 4 cryptconfig: passphrase: status: "Plain" payload: "" group_only_user: summary: User whose settings are inherited from groups description: All non-essential fields can be left blank when the user is a member of groups that provide the settings. Only `username`, `groups`, and a password are required. The primary group supplies `home_dir`, `filesystem`, and `permissions`; secondary groups provide additional virtual folders; membership groups provide shared policies. value: username: "group_user" password: "changeme" status: 1 groups: - name: "ops" type: 1 - name: "auditors" type: 2 user_with_virtual_folders: summary: User with multiple virtual folders description: "Virtual folders can point at different backends — the user sees a unified tree. `quota_size = -1` makes the folder share the user's quota instead of having its own." value: username: "vfolder_user" password: "changeme" status: 1 home_dir: "/srv/sftpgo/data/vfolder_user" permissions: /: ["list", "download"] /uploads: ["*"] /backup: ["list", "download"] virtual_folders: - virtual_path: "/uploads" name: "shared-uploads-s3" quota_size: 0 quota_files: 0 - virtual_path: "/backup" name: "corporate-backup-gcs" quota_size: -1 quota_files: -1 responses: '201': description: successful operation headers: Location: schema: type: string description: 'URI of the newly created object' content: application/json: schema: $ref: '#/components/schemas/User' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/users/{username}': parameters: - name: username in: path description: the username required: true schema: type: string get: tags: - users summary: Get user by username description: Returns the user with the given username if it exists. For security reasons, the hashed password is omitted from the response operationId: get_user_by_username parameters: - in: query name: confidential_data schema: type: integer description: 'If set to 1 confidential data will not be hidden. This means that the response will contain the hash of the password and the key and additional data for secrets. If a master key is not set or an external KMS is used, the data returned are enough to get the secrets in cleartext. Ignored if the * permission is not granted.' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/User' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - users summary: Update user description: 'Updates an existing user and optionally disconnects it, if connected, to apply the new settings. The current password will be preserved if the password field is omitted in the request body. Recovery codes and TOTP configuration cannot be set/updated using this API: each user must use the specific APIs' operationId: update_user parameters: - in: query name: disconnect schema: type: integer enum: - 0 - 1 description: | Disconnect: * `0` The user will not be disconnected and it will continue to use the old configuration until connected. This is the default * `1` The user will be disconnected after a successful update. It must login again and so it will be forced to use the new configuration requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/User' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: User updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - users summary: Delete user description: Deletes an existing user operationId: delete_user responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: User deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/users/{username}/2fa/disable': parameters: - name: username in: path description: the username required: true schema: type: string put: tags: - users summary: Disable user two-factor authentication description: Disables two-factor authentication for the specified user. Use this when a user has lost access to their authenticator device and has no remaining recovery codes operationId: disable_user_2fa responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: 2FA disabled '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/users/{username}/forgot-password': parameters: - name: username in: path description: the username required: true schema: type: string post: security: [] tags: - users summary: Send a password reset code by email description: 'You must configure an SMTP server, the account must have a valid email address and must not have the "reset-password-disabled" restriction, in which case SFTPGo will send a code via email to reset the password. If the specified user does not exist, the request will be silently ignored (a success response will be returned) to avoid disclosing existing users' operationId: user_forgot_password responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/users/{username}/reset-password': parameters: - name: username in: path description: the username required: true schema: type: string post: security: [] tags: - users summary: Reset the password description: 'Set a new password using the code received via email' operationId: user_reset_password requestBody: content: application/json: schema: type: object properties: code: type: string password: type: string required: true responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /status: get: tags: - maintenance summary: Get server status description: Returns the current status of all active services (SFTP, FTP, WebDAV, HTTP) including their configuration and runtime information operationId: get_status responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ServicesStatus' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /saas/usage: get: tags: - maintenance summary: Retrieve SaaS usage metrics description: Returns the current usage statistics for your SaaS installation. This endpoint is available only when running SFTPGo as a service operationId: get_saas_usage responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/SaaSUsage' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /license: get: tags: - maintenance summary: Get license description: Returns the current license details including license type, expiration date and enabled features operationId: get_license responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/License' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - maintenance summary: Set license description: Sets or updates the license key. The new license takes effect immediately operationId: add_license requestBody: required: true content: application/json: schema: type: object properties: key: type: string responses: '200': description: successful operation headers: Location: schema: type: string content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: License key saved successfully '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /dumpdata: get: tags: - maintenance summary: Dump data description: 'Exports all SFTPGo data (users, folders, admins, etc.) as a provider-independent JSON backup. The backup can either be saved to a file on the server (to avoid exposing sensitive data over the network) or returned directly in the response body. The output can be imported via the loaddata API' operationId: dumpdata parameters: - in: query name: output-file schema: type: string description: Path for the file to write the JSON serialized data to. This path is relative to the configured "backups_path". If this file already exists it will be overwritten. To return the backup as response body set `output_data` to true instead. - in: query name: output-data schema: type: integer enum: - 0 - 1 description: | output data: * `0` or any other value != 1, the backup will be saved to a file on the server, `output_file` is required * `1` the backup will be returned as response body - in: query name: indent schema: type: integer enum: - 0 - 1 description: | indent: * `0` no indentation. This is the default * `1` format the output JSON - in: query name: scopes schema: type: array items: $ref: '#/components/schemas/DumpDataScopes' description: 'You can limit the dump contents to the specified scopes. Empty or missing means any supported scope. Scopes must be specified comma separated' explode: false required: false responses: '200': description: successful operation content: application/json: schema: oneOf: - $ref: '#/components/schemas/ApiResponse' - $ref: '#/components/schemas/BackupData' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /loaddata: parameters: - in: query name: scan-quota schema: type: integer enum: - 0 - 1 - 2 description: | Quota scan: * `0` no quota scan is done, the imported users/folders will have used_quota_size and used_quota_files = 0 or the existing values if they already exists. This is the default * `1` scan quota * `2` scan quota if the user has quota restrictions required: false - in: query name: mode schema: type: integer enum: - 0 - 1 - 2 description: | Mode: * `0` New objects are added, existing ones are updated. This is the default * `1` New objects are added, existing ones are not modified * `2` New objects are added, existing ones are updated and connected users are disconnected and so forced to use the new configuration get: tags: - maintenance summary: Load data from file description: 'Restores SFTPGo data from a JSON backup file stored on the server. Objects are restored one by one; if any object fails to import, the operation stops and a partial restore may result' operationId: loaddata_from_file parameters: - in: query name: input-file schema: type: string required: true description: Path for the file to read the JSON serialized data from. This can be an absolute path or a path relative to the configured "backups_path". The max allowed file size is 10MB responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Data restored '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - maintenance summary: Load data from request body description: 'Restores SFTPGo data from a JSON backup provided in the request body. Objects are restored one by one; if any object fails to import, the operation stops and a partial restore may result' operationId: loaddata_from_request_body requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BackupData' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Data restored '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /system/log-level: get: summary: Retrieves the current log level tags: - maintenance operationId: get_log_level description: >- Returns the log level currently in use by the running application. responses: '200': description: Current log level configuration content: application/json: schema: $ref: '#/components/schemas/LogLevelConfig' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: summary: Dynamically updates the log level tags: - maintenance operationId: update_log_level description: >- Updates the global log level for the running application instance. **Important:** This setting is **ephemeral** (non-persistent). The log level will revert to the default configuration (defined in config files or environment variables) upon application restart. This endpoint is intended for temporary debugging purposes on a live system. requestBody: description: The new log level configuration to apply required: true content: application/json: schema: $ref: '#/components/schemas/LogLevelConfig' responses: '200': description: Log level successfully updated content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/changepwd: put: security: - BearerAuth: [] tags: - user APIs summary: Change user password description: Changes the password for the currently authenticated user. Requires the current password for verification operationId: change_user_password requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PwdChange' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/profile: get: security: - BearerAuth: [] tags: - user APIs summary: Get user profile description: Returns the profile for the currently authenticated user operationId: get_user_profile responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/UserProfile' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: security: - BearerAuth: [] tags: - user APIs summary: Update user profile description: Updates the profile for the currently authenticated user operationId: update_user_profile requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserProfile' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/2fa/recoverycodes: get: security: - BearerAuth: [] tags: - user APIs summary: Get recovery codes description: 'Returns the recovery codes for the logged in user. Recovery codes can be used if the user loses access to their second factor auth device. Recovery codes are returned unencrypted' operationId: get_user_recovery_codes responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/RecoveryCode' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: security: - BearerAuth: [] tags: - user APIs summary: Generate recovery codes description: 'Generates new recovery codes for the logged in user. Generating new recovery codes you automatically invalidate old ones' operationId: generate_user_recovery_codes responses: '200': description: successful operation content: application/json: schema: type: array items: type: string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/totp/configs: get: security: - BearerAuth: [] tags: - user APIs summary: Get available TOTP configurations description: Returns the available TOTP (Time-based One-Time Password) configurations that the user can use for two-factor authentication operationId: get_user_totp_configs responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/TOTPConfig' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/totp/generate: post: security: - BearerAuth: [] tags: - user APIs summary: Generate a new TOTP secret description: 'Generates a new TOTP secret, including the QR code as png, using the specified configuration for the logged in user' operationId: generate_user_totp_secret requestBody: required: true content: application/json: schema: type: object properties: config_name: type: string description: 'name of the configuration to use to generate the secret' responses: '200': description: successful operation content: application/json: schema: type: object properties: config_name: type: string issuer: type: string secret: type: string qr_code: type: string format: byte description: 'QR code png encoded as BASE64' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/totp/validate: post: security: - BearerAuth: [] tags: - user APIs summary: Validate a one time authentication code description: 'Checks if the given authentication code can be validated using the specified secret and config name' operationId: validate_user_totp_secret requestBody: required: true content: application/json: schema: type: object properties: config_name: type: string description: 'name of the configuration to use to validate the passcode' passcode: type: string description: 'passcode to validate' secret: type: string description: 'secret to use to validate the passcode' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Passcode successfully validated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/totp/save: post: security: - BearerAuth: [] tags: - user APIs summary: Save a TOTP config description: 'Saves the specified TOTP config for the logged in user' operationId: save_user_totp_config requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserTOTPConfig' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: TOTP configuration saved '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/shares: get: tags: - user APIs summary: List user shares description: Returns all sharing links created by the logged in user operationId: get_user_shares parameters: - in: query name: offset schema: type: integer minimum: 0 default: 0 required: false description: 'Number of items to skip from the beginning of the result set. Used for pagination in combination with the limit parameter' - in: query name: limit schema: type: integer minimum: 1 maximum: 500 default: 100 required: false description: 'The maximum number of items to return. Max value is 500, default is 100' - in: query name: order required: false description: Ordering shares by ID. Default ASC schema: type: string enum: - ASC - DESC example: ASC responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/Share' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - user APIs summary: Add a share operationId: add_share description: Creates a new sharing link for files or folders. The share ID is auto-generated and returned in the response headers requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Share' examples: read_share: summary: Read-only share for a directory (scope 1) description: | `scope: 1` = read. Recipient can list and download the tree. `paths[]` holds **virtual** paths (as seen by the user). `max_tokens: 100` limits the total number of downloads. `expires_at: 0` means no expiry. value: name: "Project files" scope: 1 paths: ["/projects/docs"] password: "optional-password" max_tokens: 100 expires_at: 0 write_share: summary: Write-only upload portal (scope 2) description: | `scope: 2` = write. Recipient can only upload. `expires_at` is a Unix milliseconds timestamp. value: name: "Upload portal" scope: 2 paths: ["/uploads/incoming"] expires_at: 1735689600000 read_write_share: summary: Read/write collaboration share (scope 3) description: | `scope: 3` = read + write. The recipient can list, download, and upload in the same tree. value: name: "Team swap" scope: 3 paths: ["/collab/swap"] password: "team-password" expires_at: 1735689600000 email_auth_share: summary: Share with email authentication (scope 1 + email auth) description: | `options.emails[]` restricts access to listed recipients; `auth_mode: 1` requires the recipient to enter a one-time code delivered by email before seeing the files. `paths[]` may target a single file. value: name: "Confidential report" scope: 1 paths: ["/reports/q4.pdf"] options: emails: ["recipient@example.com"] auth_mode: 1 ip_restricted_share: summary: Share with IP allow-list description: | `allow_from[]` accepts CIDRs and single IPs. Combined with a password for defense in depth. value: name: "Partner drop" scope: 1 paths: ["/partner-drop"] password: "strong-password" allow_from: ["203.0.113.0/24", "198.51.100.42"] expires_at: 1735689600000 responses: '201': description: successful operation headers: X-Object-ID: schema: type: string description: ID for the new created share Location: schema: type: string description: URI to retrieve the details for the new created share content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' '/user/shares/{id}': parameters: - name: id in: path description: the share id required: true schema: type: string get: tags: - user APIs summary: Get share by ID description: Returns the details of a specific sharing link owned by the logged in user operationId: get_user_share_by_id responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/Share' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' put: tags: - user APIs summary: Update share description: 'Updates an existing share belonging to the logged in user' operationId: update_user_share requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Share' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Share updated '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - user APIs summary: Delete share description: 'Deletes an existing share belonging to the logged in user' operationId: delete_user_share responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' example: message: Share deleted '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/file-actions/copy: parameters: - in: query name: path description: Path to the file/folder to copy. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true - in: query name: target description: New name. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true post: tags: - user APIs summary: 'Copy a file or a directory' description: Copies the specified file or directory to the target path for the logged in user. If the target already exists, it will be overwritten operationId: copy_user_file responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/file-actions/move: parameters: - in: query name: path description: Path to the file/folder to rename. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true - in: query name: target description: New name. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true post: tags: - user APIs summary: 'Move (rename) a file or a directory' description: Moves or renames the specified file or directory to the target path for the logged in user operationId: move_user_file responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/dirs: get: tags: - user APIs summary: Read directory contents description: Returns the contents of the specified directory for the logged in user operationId: get_user_dir_contents parameters: - in: query name: path description: Path to the folder to read. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir". If empty or missing the user's start directory is assumed. If relative, the user's start directory is used as the base schema: type: string responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/DirEntry' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - user APIs summary: Create a directory description: Create a directory for the logged in user operationId: create_user_dir parameters: - in: query name: path description: Path to the folder to create. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true - in: query name: mkdir_parents description: Create parent directories if they do not exist? schema: type: boolean required: false responses: '201': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' patch: tags: - user APIs deprecated: true summary: 'Rename a directory. Deprecated, use "file-actions/move"' description: Rename a directory for the logged in user. The rename is allowed for empty directory or for non empty local directories, with no virtual folders inside operationId: rename_user_dir parameters: - in: query name: path description: Path to the folder to rename. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true - in: query name: target description: New name. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - user APIs summary: Delete a directory description: Delete a directory and any children it contains for the logged in user operationId: delete_user_dir parameters: - in: query name: path description: Path to the folder to delete. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir" schema: type: string required: true responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/files: get: tags: - user APIs summary: Download a single file description: Returns the file contents as the response body. Supports HTTP Range requests for partial downloads operationId: download_user_file parameters: - in: query name: path required: true description: Path to the file to download. It must be URL encoded, for example the path "my dir/àdir/file.txt" must be sent as "my%20dir%2F%C3%A0dir%2Ffile.txt" schema: type: string - in: query name: inline required: false description: 'If set, the response will not have the Content-Disposition header set to `attachment`' schema: type: string responses: '200': description: successful operation content: '*/*': schema: type: string format: binary '206': description: successful operation content: '*/*': schema: type: string format: binary '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' post: tags: - user APIs summary: Upload files description: Uploads one or more files using multipart/form-data. If a file with the same name already exists, it will be overwritten operationId: create_user_files parameters: - in: query name: path description: Parent directory for the uploaded files. It must be URL encoded, for example the path "my dir/àdir" must be sent as "my%20dir%2F%C3%A0dir". If empty or missing the root path is assumed. If a file with the same name already exists, it will be overwritten schema: type: string - in: query name: mkdir_parents description: Create parent directories if they do not exist? schema: type: boolean required: false requestBody: content: multipart/form-data: schema: type: object properties: filenames: type: array items: type: string format: binary minItems: 1 uniqueItems: true required: true responses: '201': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '413': $ref: '#/components/responses/RequestEntityTooLarge' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' patch: tags: - user APIs deprecated: true summary: Rename a file description: 'Rename a file for the logged in user. Deprecated, use "file-actions/move"' operationId: rename_user_file parameters: - in: query name: path description: Path to the file to rename. It must be URL encoded schema: type: string required: true - in: query name: target description: New name. It must be URL encoded schema: type: string required: true responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' delete: tags: - user APIs summary: Delete a file description: Delete a file for the logged in user. operationId: delete_user_file parameters: - in: query name: path description: Path to the file to delete. It must be URL encoded schema: type: string required: true responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/files/upload: post: tags: - user APIs summary: Upload a single file description: Uploads a single file using the raw request body (no multipart/form-data). This avoids creating temporary files on the server, making it more efficient for large files. Only one file can be uploaded per request operationId: create_user_file parameters: - in: query name: path description: Full file path. It must be path encoded, for example the path "my dir/àdir/file.txt" must be sent as "my%20dir%2F%C3%A0dir%2Ffile.txt". The parent directory must exist. If a file with the same name already exists, it will be overwritten schema: type: string required: true - in: query name: mkdir_parents description: Create parent directories if they do not exist? schema: type: boolean required: false - in: header name: X-SFTPGO-MTIME schema: type: integer description: File modification time as unix timestamp in milliseconds requestBody: content: application/*: schema: type: string format: binary text/*: schema: type: string format: binary image/*: schema: type: string format: binary audio/*: schema: type: string format: binary video/*: schema: type: string format: binary required: true responses: '201': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '413': $ref: '#/components/responses/RequestEntityTooLarge' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/files/metadata: patch: tags: - user APIs summary: Set metadata for a file/directory description: 'Set supported metadata attributes for the specified file or directory' operationId: setprops_user_file parameters: - in: query name: path description: Full file/directory path. It must be URL encoded, for example the path "my dir/àdir/file.txt" must be sent as "my%20dir%2F%C3%A0dir%2Ffile.txt" schema: type: string required: true requestBody: content: application/json: schema: type: object properties: modification_time: type: integer description: File modification time as unix timestamp in milliseconds required: true responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '413': $ref: '#/components/responses/RequestEntityTooLarge' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/files/chunked-upload: options: tags: - user APIs summary: TUS Discovery description: Returns the server's capabilities. operationId: tus_user_discovery responses: '200': headers: Tus-Version: description: Supported protocol versions (e.g., 1.0.0). schema: type: string example: "1.0.0" Tus-Extension: description: Supported extensions. schema: type: string example: "creation,creation-with-upload" post: tags: - user APIs summary: Create Upload (Creation) description: >- Initializes a new upload resource. The server will respond with a `Location` header containing the specific URL to send data chunks to via PATCH. operationId: tus_user_create_upload parameters: - in: header name: Tus-Resumable schema: type: string enum: ["1.0.0"] required: true description: TUS protocol version. - in: header name: Upload-Length schema: type: integer required: true description: The total size of the file in bytes. - in: header name: Upload-Metadata schema: type: string required: true description: >- File metadata encoded as Key/Value pairs. Format: `key Base64Value,key2 Base64Value`. **Mandatory Fields:** - `path`: The absolute destination path (e.g., `/folder/file.txt`). **Optional Fields:** - `mtime`: Last modification timestamp (Unix/String). - `mkdir_parents`: Set to "true" to automatically create parent directories. example: "path L215ZGlyL2ZpbGUuYmlu,mkdir_parents ZmFsc2U=,mtime MTc2NzIyNDg3ODU0NQ==" responses: '201': description: Upload created successfully. headers: Location: description: The absolute or relative URL where data patches must be sent. schema: type: string example: "http://localhost:8080/user/files/chunked-upload/c9306132f81e42a9" Tus-Resumable: schema: type: string example: "1.0.0" '413': description: The file exceeds the maximum allowed size. '400': description: Missing headers or malformed metadata (e.g., missing 'path'). '401': description: Unauthorized (Invalid Token or Cookie). /user/files/chunked-upload/{id}: parameters: - in: path name: id schema: type: string required: true description: Unique upload ID generated by the server during the POST request. head: summary: Upload Status (Offset Check) description: Returns the current offset (how many bytes have been uploaded so far). Used for resuming. operationId: tus_user_upload_status tags: - user APIs parameters: - in: header name: Tus-Resumable schema: type: string enum: ["1.0.0"] required: true responses: '200': description: Status retrieved successfully. headers: Upload-Offset: description: Current offset in bytes. schema: type: integer Upload-Length: description: Total expected length. schema: type: integer Tus-Resumable: schema: type: string example: "1.0.0" '404': description: Upload not found or expired. '403': description: Forbidden (User does not have permission to access this upload). patch: tags: - user APIs summary: Upload Chunk (Core Transfer) description: Uploads a chunk of binary data or the entire file. operationId: tus_user_upload_chunk parameters: - in: header name: Tus-Resumable schema: type: string enum: ["1.0.0"] required: true - in: header name: Upload-Offset schema: type: integer required: true description: Must match exactly the current offset on the server. - in: header name: Content-Type schema: type: string enum: ["application/offset+octet-stream"] required: true description: Strictly required to be `application/offset+octet-stream`. - in: header name: Content-Length schema: type: integer required: true description: The size of the chunk in bytes being sent in this request body. requestBody: description: The binary data of the chunk. required: true content: application/offset+octet-stream: schema: type: string format: binary responses: '204': description: Chunk accepted successfully. headers: Upload-Offset: description: The new offset after writing the chunk. schema: type: integer Tus-Resumable: schema: type: string '409': description: >- Conflict. The provided `Upload-Offset` does not match the current server offset. The client should perform a HEAD request to get the correct offset and retry. '404': description: Invalid Upload ID. '415': description: Unsupported Media Type. Content-Type must be `application/offset+octet-stream`. /user/files/checksum: get: tags: - user APIs summary: Get file checksum description: Returns the cryptographic hash (checksum) of a single file for the logged in user. The operation reads the file content to calculate the hash; for large files, this operation may take some time. operationId: get_user_file_checksum parameters: - in: query name: path required: true description: Path to the file to calculate the checksum for. It must be URL encoded, for example the path "my dir/file.txt" must be sent as "my%20dir%2Ffile.txt" schema: type: string - in: query name: algo required: false description: The hashing algorithm to use for the calculation. Currently, only sha256 is supported. schema: type: string enum: - sha256 default: sha256 responses: '200': description: successful operation content: application/json: schema: type: object properties: checksum: type: string description: The calculated hash as a hexadecimal string '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' /user/streamzip: post: tags: - user APIs summary: Download multiple files and folders as a zip archive description: Generates and returns a zip archive containing the specified files and folders. The archive is streamed on the fly without being stored on the server. Only regular files and directories are included operationId: streamzip requestBody: required: true content: application/json: schema: type: array items: type: string description: Absolute file or folder path responses: '200': description: successful operation content: 'application/zip': schema: type: string format: binary '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' default: $ref: '#/components/responses/DefaultResponse' components: responses: BadRequest: description: Bad Request content: application/json: schema: $ref: '#/components/schemas/ApiResponse' Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiResponse' Forbidden: description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiResponse' NotFound: description: Not Found content: application/json: schema: $ref: '#/components/schemas/ApiResponse' Conflict: description: Conflict content: application/json: schema: $ref: '#/components/schemas/ApiResponse' RequestEntityTooLarge: description: Request Entity Too Large, max allowed size exceeded content: application/json: schema: $ref: '#/components/schemas/ApiResponse' InternalServerError: description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/ApiResponse' DefaultResponse: description: Unexpected Error content: application/json: schema: $ref: '#/components/schemas/ApiResponse' schemas: Permission: type: string enum: - '*' - list - download - upload - overwrite - delete - delete_files - delete_dirs - rename - rename_files - rename_dirs - create_dirs - create_symlinks - chmod - chown - chtimes - copy description: | Permissions: * `*` - all permissions are granted * `list` - list items is allowed * `download` - download files is allowed * `upload` - upload files is allowed * `overwrite` - overwrite an existing file, while uploading, is allowed. upload permission is required to allow file overwrite * `delete` - delete files or directories is allowed * `delete_files` - delete files is allowed * `delete_dirs` - delete directories is allowed * `rename` - rename files or directories is allowed * `rename_files` - rename files is allowed * `rename_dirs` - rename directories is allowed * `create_dirs` - create directories is allowed * `create_symlinks` - create links is allowed * `chmod` changing file or directory permissions is allowed * `chown` changing file or directory owner and group is allowed * `chtimes` changing file or directory access and modification time is allowed * `copy`, copying files or directories is allowed AdminPermissions: type: string enum: - '*' - add_users - edit_users - del_users - view_users - view_conns - close_conns - view_status - manage_folders - manage_groups - quota_scans - manage_defender - view_defender - view_events - disable_mfa description: | Admin permissions: * `*` - super admin permissions are granted * `add_users` - add new users is allowed * `edit_users` - change existing users is allowed * `del_users` - remove users is allowed * `view_users` - list users is allowed * `view_conns` - list active connections is allowed * `close_conns` - close active connections is allowed * `view_status` - view the server status is allowed * `manage_folders` - manage folders is allowed * `manage_groups` - manage groups is allowed * `quota_scans` - view and start quota scans is allowed * `manage_defender` - remove ip from the dynamic blocklist is allowed * `view_defender` - list the dynamic blocklist is allowed * `view_events` - view and search filesystem and provider events is allowed * `disable_mfa` - allow to disable two-factor authentication for users and admins FsProviders: type: integer enum: - 0 - 1 - 2 - 3 - 4 - 5 - 6 description: | Filesystem providers. Selects which sub-config inside the parent `Filesystem` schema is read; unused sub-configs are ignored/cleared on save. * `0` - Local filesystem (no sub-config; uses the user's `home_dir`) * `1` - S3 Compatible Object Storage → `s3config` * `2` - Google Cloud Storage → `gcsconfig` * `3` - Azure Blob Storage → `azblobconfig` * `4` - Local filesystem encrypted → `cryptconfig` * `5` - SFTP → `sftpconfig` * `6` - HTTP filesystem → `httpconfig` EventActionTypes: type: integer enum: - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 description: | Supported event action types. The value selects which sub-config inside `BaseEventActionOptions` is active; all other sub-configs are zeroed on save. Types marked **Premium** require a Premium-tier license. * `1` - HTTP → `options.http_config` (templated: `endpoint`, `body`, `headers[].value`, `query_parameters[].value`, `parts[].filepath`, `parts[].body`, `parts[].headers[].value`) * `2` - Command → `options.cmd_config` (templated: `args[]`, `env_vars[].value`). The binary must be allow-listed via `SFTPGO_HOOK__ENABLED_ACTION_COMMANDS`. * `3` - Email → `options.email_config` (templated: `recipients[]`, `bcc[]`, `subject`, `body`, `attachments[]`) * `4` - Backup (no sub-config) * `5` - User quota reset (no sub-config) * `6` - Folder quota reset (no sub-config) * `7` - Transfer quota reset (no sub-config) * `8` - Data retention check → `options.retention_config`. Populates `{{.RetentionChecks}}` for downstream actions in the same rule. * `9` - Filesystem → `options.fs_config`. The nested `fs_config.type` picks the sub-action (see `FilesystemActionTypes`). * `11` - Password expiration check → `options.pwd_expiration_config` * `12` - User expiration check (no sub-config) * `13` - Identity Provider account check → `options.idp_config` (**Premium**; templated: `template_user`, `template_admin` — each is a Go template that renders a full User/Admin JSON payload. Intended for just-in-time provisioning after OIDC login.) * `14` - User inactivity check → `options.user_inactivity_config` * `15` - Rotate log file (no sub-config) * `16` - IMAP → `options.imap_config` (**Premium**; templated: `email[]`, `subject`, `body`, `attachments[]`, `target_folder`) * `17` - ICAP → `options.icap_config` (**Premium**; templated: `paths[]`) * `18` - Share expiration check → `options.share_expiration_config`. Populates `{{.ShareExpirationChecks}}` for downstream actions. * `19` - Event report → `options.event_report_config` (**Premium**). Populates `{{.EventReports}}` for downstream email/HTTP actions. See the **[sftpgo](https://github.com/sftpgo/sftpgo-skill)** reference skill for the full template context, registered functions, and worked examples. FilesystemActionTypes: type: integer enum: - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 description: | Supported filesystem action types. Used inside `EventActionFilesystemConfig.type` when the parent action is `type: 9` (Filesystem). Selects which sub-field of `fs_config` carries the work items. * `1` - Rename → `fs_config.renames[]` (each with `key` = old path, `value` = new path; both template-rendered) * `2` - Delete → `fs_config.deletes[]` (array of templated paths). **A trailing `/` empties the directory without removing it; no trailing `/` removes the directory recursively.** * `3` - Mkdirs → `fs_config.mkdirs[]` (templated paths) * `4` - Exist → `fs_config.exist[]` (templated paths; the action fails if any path does not exist) * `5` - Compress → `fs_config.compress.paths[]` + `compress.name` (all templated) * `6` - Copy → `fs_config.copy[]` (each with `key` = source, `value` = target; both templated) * `7` - PGP → `fs_config.pgp.paths[]` + key/passphrase config * `8` - Metadata Check → `fs_config.folders[]` (literal folder names; no templates) * `9` - Extract → `fs_config.decompress.source` + `decompress.target` (both templated) EventTriggerTypes: type: integer enum: - 1 - 2 - 3 - 4 - 5 - 6 - 7 description: | Supported event trigger types. Selects which `conditions.*` sub-filter is meaningful on the parent `EventRule`, and which keys of the template context are populated for actions of this rule. * `1` - Filesystem event → filter by `conditions.fs_events[]` (upload, pre-upload, first-upload, download, pre-download, first-download, delete, pre-delete, rename, mkdir, rmdir, copy, ssh_cmd). Populates path/size/elapsed fields in the template context. * `2` - Provider event → filter by `conditions.provider_events[]` (add, update, delete) plus `conditions.options.provider_objects[]` (user, folder, group, admin, api_key, share, event_rule, event_action). Populates `{{.Object}}`. * `3` - Schedule (cron) → filter by `conditions.schedules[]`. Populates only `{{.Timestamp}}` and chained data from earlier actions; no filesystem context. * `4` - IP blocked → no filter. Populates `{{.IP}}`, `{{.Protocol}}`, `{{.Errors}}`. * `5` - Certificate renewal → no filter. Populates `{{.Event}}` (`acme_ok` / `acme_error`), `{{.Errors}}`. * `6` - On demand → no trigger-time filter. Run via `POST /events/rules/{name}/run`; caller may inject a user list which populates `{{.Name}}` per user. * `7` - Identity provider login → filter by `conditions.idp_login_event` (0 any, 1 user only, 2 admin only). Populates `{{.Name}}`, `{{.ExtName}}`, `{{.Role}}`, `{{.Email}}`, `{{.IDPFields}}`, `{{.IP}}`, `{{.Protocol}}` = `OIDC`. Intended for JIT provisioning paired with action `type: 13`. LoginMethods: type: string enum: - publickey - password - password-over-SSH - keyboard-interactive - publickey+password - publickey+keyboard-interactive - TLSCertificate - TLSCertificate+password description: | Available login methods. To enable multi-step authentication you have to allow only multi-step login methods * `publickey` * `password`, password for all the supported protocols * `password-over-SSH`, password over SSH protocol (SSH/SFTP/SCP) * `keyboard-interactive` * `publickey+password` - multi-step auth: public key and password * `publickey+keyboard-interactive` - multi-step auth: public key and keyboard interactive * `TLSCertificate` * `TLSCertificate+password` - multi-step auth: TLS client certificate and password SupportedProtocols: type: string enum: - SSH - FTP - DAV - HTTP description: | Protocols: * `SSH` - includes both SFTP and SSH commands * `FTP` - plain FTP and FTPES/FTPS * `DAV` - WebDAV over HTTP/HTTPS * `HTTP` - WebClient/REST API MFAProtocols: type: string enum: - SSH - FTP - HTTP description: | Protocols: * `SSH` - includes both SFTP and SSH commands * `FTP` - plain FTP and FTPES/FTPS * `HTTP` - WebClient/REST API EventProtocols: type: string enum: - SSH - SFTP - SCP - FTP - DAV - HTTP - HTTPShare - DataRetention - EventAction - OIDC description: | Protocols: * `SSH` - SSH commands * `SFTP` - SFTP protocol * `SCP` - SCP protocol * `FTP` - plain FTP and FTPES/FTPS * `DAV` - WebDAV * `HTTP` - WebClient/REST API * `HTTPShare` - the event is generated in a public share * `DataRetention` - the event is generated by a data retention check * `EventAction` - the event is generated by an EventManager action * `OIDC` - OpenID Connect WebClientOptions: type: string enum: - publickey-change-disabled - tls-cert-change-disabled - write-disabled - mfa-disabled - password-change-disabled - api-key-auth-change-disabled - info-change-disabled - shares-disabled - password-reset-disabled - shares-without-password-disabled - shares-require-email-auth - wopi-disabled - rest-api-disabled description: | Options: * `publickey-change-disabled` - changing SSH public keys is not allowed * `tls-cert-change-disabled` - changing TLS certificates is not allowed * `write-disabled` - upload, rename, delete are not allowed even if the user has permissions for these actions * `mfa-disabled` - enabling multi-factor authentication is not allowed. This option cannot be set if the user has MFA already enabled * `password-change-disabled` - changing password is not allowed * `api-key-auth-change-disabled` - enabling/disabling API key authentication is not allowed * `info-change-disabled` - changing info such as email and description is not allowed * `shares-disabled` - sharing files and directories with external users is not allowed * `password-reset-disabled` - resetting the password is not allowed * `shares-without-password-disabled` - creating shares without password protection is not allowed * `shares-require-email-auth` - creating shares without enabling email authentication is not allowed * `wopi-disabled` - editing documents via WOPI integration is disabled * `rest-api-disabled` - REST API are disabled for this user APIKeyScope: type: integer enum: - 1 - 2 description: | Options: * `1` - admin scope. The API key will be used to impersonate an SFTPGo admin * `2` - user scope. The API key will be used to impersonate an SFTPGo user ShareScope: type: integer enum: - 1 - 2 - 3 description: | Scopes: * `1` - Read * `2` - Write * `3` - Read/Write TOTPHMacAlgo: type: string enum: - sha1 - sha256 - sha512 description: 'Supported HMAC algorithms for Time-based one time passwords' UserType: type: string enum: - '' - LDAPUser - OSUser description: This is an hint for authentication plugins. It is ignored when using SFTPGo internal authentication DumpDataScopes: type: string enum: - users - folders - groups - admins - api_keys - shares - actions - rules - roles - ip_lists - configs - license LogEventType: type: integer enum: - 1 - 2 - 3 - 4 - 5 - 6 description: > Event status: * `1` - Login failed * `2` - Login failed non-existent user * `3` - No login tried * `4` - Algorithm negotiation failed * `5` - Login succeeded * `6` - Share legal agreement accepted FsEventStatus: type: integer enum: - 1 - 2 - 3 description: > Event status: * `1` - no error * `2` - generic error * `3` - quota exceeded error FsEventAction: type: string enum: - download - upload - first-upload - first-download - delete - rename - mkdir - rmdir - ssh_cmd ProviderEventAction: type: string enum: - add - update - delete ProviderEventObjectType: type: string enum: - user - folder - group - admin - api_key - share - event_action - event_rule - role - ip_list_entry - configs - license SSHAuthentications: type: string enum: - publickey - password - keyboard-interactive - publickey+password - publickey+keyboard-interactive TLSVersions: type: integer enum: - 12 - 13 description: > TLS version: * `12` - TLS 1.2 * `13` - TLS 1.3 IPListType: type: integer enum: - 1 - 2 - 3 - 4 description: > IP List types: * `1` - allow list * `2` - defender * `3` - rate limiter safe list * `4` - trusted list IPListMode: type: integer enum: - 1 - 2 description: > IP list modes * `1` - allow * `2` - deny, supported for defender list type only TOTPConfig: type: object properties: name: type: string issuer: type: string algo: $ref: '#/components/schemas/TOTPHMacAlgo' RecoveryCode: type: object properties: secret: $ref: '#/components/schemas/Secret' used: type: boolean description: 'Recovery codes to use if the user loses access to their second factor auth device. Each code can only be used once, you should use these codes to login and disable or reset 2FA for your account' BaseTOTPConfig: type: object properties: enabled: type: boolean config_name: type: string description: 'This name must be defined within the "totp" section of the SFTPGo configuration file. You will be unable to save a user/admin referencing a missing config_name' secret: $ref: '#/components/schemas/Secret' AdminTOTPConfig: allOf: - $ref: '#/components/schemas/BaseTOTPConfig' UserTOTPConfig: allOf: - $ref: '#/components/schemas/BaseTOTPConfig' - type: object properties: protocols: type: array items: $ref: '#/components/schemas/MFAProtocols' description: 'TOTP will be required for the specified protocols. SSH protocol (SFTP/SCP/SSH commands) will ask for the TOTP passcode if the client uses keyboard interactive authentication. FTP has no standard way to support two factor authentication, if you enable the FTP support, you have to add the TOTP passcode after the password. For example if your password is "password" and your one time passcode is "123456" you have to use "password123456" as password. WebDAV is not supported since each single request must be authenticated and a passcode cannot be reused.' PatternsFilter: type: object properties: path: type: string description: 'virtual path as seen by users, if no other specific filter is defined, the filter applies for sub directories too. For example if filters are defined for the paths "/" and "/sub" then the filters for "/" are applied for any file outside the "/sub" directory' allowed_patterns: type: array items: type: string description: 'list of, case insensitive, allowed shell like patterns. Allowed patterns are evaluated before the denied ones' example: - '*.jpg' - a*b?.png denied_patterns: type: array items: type: string description: 'list of, case insensitive, denied shell like patterns' example: - '*.zip' deny_policy: type: integer enum: - 0 - 1 description: | Policies for denied patterns * `0` - default policy. Denied files/directories matching the filters are visible in directory listing but cannot be uploaded/downloaded/overwritten/renamed * `1` - deny policy hide. This policy applies the same restrictions as the default one and denied files/directories matching the filters will also be hidden in directory listing. This mode may cause performance issues for large directories HooksFilter: type: object properties: external_auth_disabled: type: boolean example: false description: If true, the external auth hook, if defined, will not be executed pre_login_disabled: type: boolean example: false description: If true, the pre-login hook, if defined, will not be executed check_password_disabled: type: boolean example: false description: If true, the check password hook, if defined, will not be executed description: User specific hook overrides BandwidthLimit: type: object properties: sources: type: array items: type: string description: 'Source networks in CIDR notation as defined in RFC 4632 and RFC 4291 for example `192.0.2.0/24` or `2001:db8::/32`. The limit applies if the defined networks contain the client IP' upload_bandwidth: type: integer format: int32 description: 'Maximum upload bandwidth as KB/s, 0 means unlimited' download_bandwidth: type: integer format: int32 description: 'Maximum download bandwidth as KB/s, 0 means unlimited' TimePeriod: type: object properties: day_of_week: type: integer enum: - 0 - 1 - 2 - 3 - 4 - 5 - 6 description: Day of week, 0 Sunday, 6 Saturday from: type: string description: Start time in HH:MM format to: type: string description: End time in HH:MM format BaseShareGovernanceRule: type: object properties: permissions: type: integer description: 'Bitmask representing the share permissions. 1 means Read, 2 Write, 4 Delete. Example: For Read + Write, the value is 3 (1 + 2).' mode: type: integer enum: - 1 - 2 description: | Mode: * `1` - suggested: the group policy is pre-selected but can be removed by the user * `2` - enforced: the group policy is mandatory and cannot be changed by the user BaseUserFilters: type: object properties: allowed_ip: type: array items: type: string description: 'only clients connecting from these IP/Mask are allowed. IP/Mask must be in CIDR notation as defined in RFC 4632 and RFC 4291, for example "192.0.2.0/24" or "2001:db8::/32"' example: - 192.0.2.0/24 - '2001:db8::/32' denied_ip: type: array items: type: string description: clients connecting from these IP/Mask are not allowed. Denied rules are evaluated before allowed ones example: - 172.16.0.0/16 denied_login_methods: type: array items: $ref: '#/components/schemas/LoginMethods' description: if null or empty any available login method is allowed denied_protocols: type: array items: $ref: '#/components/schemas/SupportedProtocols' description: if null or empty any available protocol is allowed file_patterns: type: array items: $ref: '#/components/schemas/PatternsFilter' description: 'filters based on shell like file patterns. These restrictions do not apply to files listing for performance reasons, so a denied file cannot be downloaded/overwritten/renamed but it will still be in the list of files. Please note that these restrictions can be easily bypassed' max_upload_file_size: type: integer format: int64 description: 'maximum allowed size, as bytes, for a single file upload. The upload will be aborted if/when the size of the file being sent exceeds this limit. 0 means unlimited' tls_username: type: string description: 'defines the TLS certificate field to use as username. For FTP clients it must match the name provided using the "USER" command. For WebDAV, if no username is provided, the CN will be used as username. For WebDAV clients it must match the implicit or provided username. Ignored if mutual TLS is disabled. Currently the only supported value is `CommonName`' hooks: $ref: '#/components/schemas/HooksFilter' disable_fs_checks: type: boolean example: false description: Disable checks for existence and automatic creation of home directory and virtual folders. SFTPGo requires that the user's home directory, virtual folder root, and intermediate paths to virtual folders exist to work properly. If you already know that the required directories exist, disabling these checks will speed up login. You could, for example, disable these checks after the first login web_client: type: array items: $ref: '#/components/schemas/WebClientOptions' description: WebClient/user REST API related configuration options allow_api_key_auth: type: boolean description: 'API key authentication allows to impersonate this user with an API key' user_type: $ref: '#/components/schemas/UserType' bandwidth_limits: type: array items: $ref: '#/components/schemas/BandwidthLimit' external_auth_cache_time: type: integer description: 'Defines the cache time, in seconds, for users authenticated using an external auth hook. 0 means no cache' start_directory: type: string description: 'Specifies an alternate starting directory. If not set, the default is "/". This option is supported for SFTP/SCP, FTP and HTTP (WebClient/REST API) protocols. Relative paths will use this directory as base.' two_factor_protocols: type: array items: $ref: '#/components/schemas/MFAProtocols' description: 'Defines protocols that require two factor authentication' ftp_security: type: integer enum: - 0 - 1 description: 'Set to `1` to require TLS for both data and control connection. his setting is useful if you want to allow both encrypted and plain text FTP sessions globally and then you want to require encrypted sessions on a per-user basis. It has no effect if TLS is already required for all users in the configuration file.' is_anonymous: type: boolean description: 'If enabled the user can login with any password or no password at all. Anonymous users are supported for FTP and WebDAV protocols and permissions will be automatically set to "list" and "download" (read only)' default_shares_expiration: type: integer description: 'Defines the default expiration for newly created shares as number of days. 0 means no expiration' max_shares_expiration: type: integer description: 'Defines the maximum allowed expiration, as a number of days, when a user creates or updates a share. 0 means no expiration' password_expiration: type: integer description: 'The password expires after the defined number of days. 0 means no expiration' password_strength: type: integer description: 'Minimum password entropy enforced when a password is set (admin create/update, user self-change, password reset, share password). 0 means no per-entity value — the primary group''s password_strength is used, otherwise the system-level data_provider.password_validation.*.min_entropy default. Any non-zero value overrides the system default (including toward a weaker setting). Values in the 50-70 range are suggested for common use cases.' password_policy: $ref: '#/components/schemas/PasswordPolicy' access_time: type: array items: $ref: '#/components/schemas/TimePeriod' enforce_secure_algorithms: type: boolean description: 'If enabled, only secure algorithms are allowed. This setting is currently enforced for SSH/SFTP.' denied_share_paths: type: array items: type: string description: 'Virtual paths that cannot be shared. If a path is denied, shares for that path and any sub-path are rejected. For example, denying "/" blocks sharing the root directory only, denying "/vfolder" blocks sharing /vfolder and /vfolder/sub etc.' denied_share_scopes: type: array items: type: string enum: - read - write - read_write description: 'Share scopes that users are not allowed to use. If all scopes are denied, sharing is completely disabled.' description: Additional user options UserFilters: allOf: - $ref: '#/components/schemas/BaseUserFilters' - type: object properties: require_password_change: type: boolean description: 'User must change password from WebClient/REST API at next login' totp_config: $ref: '#/components/schemas/UserTOTPConfig' recovery_codes: type: array items: $ref: '#/components/schemas/RecoveryCode' tls_certs: type: array items: type: string description: PEM-encoded X.509 certificates for TLS client certificate authentication. Minimum RSA key size is 2048 bits additional_emails: type: array items: type: string format: email description: Additional email addresses associated with this user account, beyond the primary email custom1: type: string description: 'Additional placeholder value available for use in group configurations. It can be referenced as %custom1%. Deprecated: use custom_placeholders instead' custom_placeholders: type: array items: type: string description: 'Additional placeholders available for use in group configurations. Each placeholder can be referenced as %custom1%, %custom2%, and so on' GroupFilters: allOf: - $ref: '#/components/schemas/BaseUserFilters' - type: object properties: share_policy: $ref: '#/components/schemas/BaseShareGovernanceRule' PasswordPolicy: type: object properties: length: type: integer description: Minimum password length uppers: type: integer description: Minimum number of uppercase characters required lowers: type: integer description: Minimum number of lowercase characters required digits: type: integer description: Minimum number of digit characters required specials: type: integer description: Minimum number of special characters required description: 'Character-class password rules enforced when a password is set. Each field is evaluated per user: a non-zero value overrides the primary group value for that field, which in turn overrides the system-level data_provider.password_validation default (overrides can be less strict than the system default). It is recommended to use the password_strength setting instead, as it evaluates the overall cryptographic strength of the password rather than relying on static rules.' Secret: type: object properties: status: type: string enum: - Plain - AES-256-GCM - Secretbox - GCP - AWS - VaultTransit - AzureKeyVault - Redacted description: 'Set to "Plain" to add or update an existing secret, set to "Redacted" to preserve the existing value' payload: type: string key: type: string additional_data: type: string mode: type: integer description: 1 means encrypted using a master key description: The secret is encrypted before saving, so to set a new secret you must provide a payload and set the status to "Plain". The encryption key and additional data will be generated automatically. If you set the status to "Redacted" the existing secret will be preserved S3Config: type: object properties: bucket: type: string minLength: 1 region: type: string minLength: 1 access_key: type: string description: AWS Access Key ID for authentication. Leave blank when using IAM roles or instance profiles access_secret: $ref: '#/components/schemas/Secret' sse_customer_key: $ref: '#/components/schemas/Secret' role_arn: type: string description: 'Optional IAM Role ARN to assume' session_token: type: string description: 'Optional Session token that is a part of temporary security credentials provisioned by AWS STS' endpoint: type: string description: optional endpoint storage_class: type: string description: 'S3 storage class for uploaded objects (e.g., STANDARD, STANDARD_IA, GLACIER). Leave empty for the default storage class' acl: type: string description: 'The canned ACL to apply to uploaded objects. Leave empty to use the default ACL. For more information and available ACLs, see here: https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl' upload_part_size: type: integer description: 'the buffer size (in MB) to use for multipart uploads. The minimum allowed part size is 5MB, and if this value is set to zero, the default value (5MB) for the AWS SDK will be used. The minimum allowed value is 5.' upload_concurrency: type: integer description: 'the number of parts to upload in parallel. If this value is set to zero, the default value (5) will be used' upload_part_max_time: type: integer description: 'the maximum time allowed, in seconds, to upload a single chunk (the chunk size is defined via "upload_part_size"). 0 means no timeout' download_part_size: type: integer description: 'the buffer size (in MB) to use for multipart downloads. The minimum allowed part size is 5MB, and if this value is set to zero, the default value (5MB) for the AWS SDK will be used. The minimum allowed value is 5. Ignored for partial downloads' download_concurrency: type: integer description: 'the number of parts to download in parallel. If this value is set to zero, the default value (5) will be used. Ignored for partial downloads' download_part_max_time: type: integer description: 'the maximum time allowed, in seconds, to download a single chunk (the chunk size is defined via "download_part_size"). 0 means no timeout. Ignored for partial downloads.' force_path_style: type: boolean description: 'Set this to "true" to force the request to use path-style addressing, i.e., "http://s3.amazonaws.com/BUCKET/KEY". By default, the S3 client will use virtual hosted bucket addressing when possible ("http://BUCKET.s3.amazonaws.com/KEY")' key_prefix: type: string description: 'key_prefix is similar to a chroot directory for a local filesystem. If specified the user will only see contents that starts with this prefix and so you can restrict access to a specific virtual folder. The prefix, if not empty, must not start with "/" and must end with "/". If empty the whole bucket contents will be available' example: folder/subfolder/ checksum_algorithm: type: string enum: - "" - crc32 - crc32c - crc64nvme - sha1 - sha256 description: 'Checksum algorithm to compute and send with uploads for end-to-end integrity verification. Currently applied to uploads and copies (PutObject, multipart upload, CopyObject). Leave empty to send no checksum; this is the most compatible setting across S3-compatible services' description: S3 Compatible Object Storage configuration details GCSConfig: type: object properties: bucket: type: string minLength: 1 credentials: $ref: '#/components/schemas/Secret' automatic_credentials: type: integer enum: - 0 - 1 description: | Automatic credentials: * `0` - disabled, explicit credentials, using a JSON credentials file, must be provided. This is the default value if the field is null * `1` - enabled, we try to use the Application Default Credentials (ADC) strategy to find your application's credentials hns: type: integer enum: - 0 - 1 description: | Hierarchical Namespace: * `0` - disabled, the bucket is not configured with Hierarchical Namespace * `1` - enabled, the bucket is configured with Hierarchical Namespace storage_class: type: string description: 'Google Cloud Storage class for uploaded objects (e.g., STANDARD, NEARLINE, COLDLINE, ARCHIVE). Leave empty for the default storage class' acl: type: string description: 'The ACL to apply to uploaded objects. Leave empty to use the default ACL. For more information and available ACLs, refer to the JSON API here: https://cloud.google.com/storage/docs/access-control/lists#predefined-acl' key_prefix: type: string description: 'key_prefix is similar to a chroot directory for a local filesystem. If specified the user will only see contents that starts with this prefix and so you can restrict access to a specific virtual folder. The prefix, if not empty, must not start with "/" and must end with "/". If empty the whole bucket contents will be available' example: folder/subfolder/ upload_part_size: type: integer description: 'The buffer size (in MB) to use for multipart uploads. The default value is 16MB. 0 means use the default' upload_part_max_time: type: integer description: 'The maximum time allowed, in seconds, to upload a single chunk. The default value is 32. 0 means use the default' universe_domain: type: string description: 'The universe domain to use for Google Cloud API requests. If omitted or empty, the default public domain (googleapis.com) is used. Set this value if you need to connect to a custom Google Cloud environment, such as Google Distributed Cloud or a Sovereign Cloud' description: 'Google Cloud Storage configuration details. The "credentials" field must be populated only when adding/updating a user. It will be always omitted, since there are sensitive data, when you search/get users' AzureBlobFsConfig: type: object properties: container: type: string description: Azure Blob Storage container name account_name: type: string description: 'Storage Account Name, leave blank to use SAS URL' account_key: $ref: '#/components/schemas/Secret' sas_url: $ref: '#/components/schemas/Secret' endpoint: type: string description: 'optional endpoint. Default is "blob.core.windows.net". If you use the emulator the endpoint must include the protocol, for example "http://127.0.0.1:10000"' upload_part_size: type: integer description: 'the buffer size (in MB) to use for multipart uploads. If this value is set to zero, the default value (5MB) will be used.' upload_concurrency: type: integer description: 'the number of parts to upload in parallel. If this value is set to zero, the default value (5) will be used' download_part_size: type: integer description: 'the buffer size (in MB) to use for multipart downloads. If this value is set to zero, the default value (5MB) will be used.' download_concurrency: type: integer description: 'the number of parts to download in parallel. If this value is set to zero, the default value (5) will be used' access_tier: type: string enum: - '' - Archive - Hot - Cool key_prefix: type: string description: 'key_prefix is similar to a chroot directory for a local filesystem. If specified the user will only see contents that starts with this prefix and so you can restrict access to a specific virtual folder. The prefix, if not empty, must not start with "/" and must end with "/". If empty the whole container contents will be available' example: folder/subfolder/ use_emulator: type: boolean description: If true, the Azure Storage Emulator (Azurite) will be used instead of the cloud service description: Azure Blob Storage configuration details OSFsConfig: type: object properties: read_buffer_size: type: integer minimum: 0 maximum: 10 description: "The read buffer size, as MB, to use for downloads. 0 means no buffering, that's fine in most use cases." write_buffer_size: type: integer minimum: 0 maximum: 10 description: "The write buffer size, as MB, to use for uploads. 0 means no buffering, that's fine in most use cases." CryptFsConfig: type: object properties: passphrase: $ref: '#/components/schemas/Secret' read_buffer_size: type: integer minimum: 0 maximum: 10 description: "The read buffer size, as MB, to use for downloads. 0 means no buffering, that's fine in most use cases." write_buffer_size: type: integer minimum: 0 maximum: 10 description: "The write buffer size, as MB, to use for uploads. 0 means no buffering, that's fine in most use cases." description: Crypt filesystem configuration details SFTPFsConfig: type: object properties: endpoint: type: string description: 'remote SFTP endpoint as host:port' username: type: string description: you can specify a password or private key or both. In the latter case the private key will be tried first. password: $ref: '#/components/schemas/Secret' private_key: $ref: '#/components/schemas/Secret' key_passphrase: $ref: '#/components/schemas/Secret' fingerprints: type: array items: type: string description: 'SHA256 fingerprints to use for host key verification. If you don''t provide any fingerprint the remote host key will not be verified, this is a security risk' prefix: type: string description: Specifying a prefix you can restrict all operations to a given path within the remote SFTP server. The specified path, if set, must already exist disable_concurrent_reads: type: boolean description: Concurrent reads are safe to use and disabling them will degrade performance. Some servers automatically delete files once they are downloaded. Using concurrent reads is problematic with such servers. buffer_size: type: integer minimum: 0 maximum: 16 example: 2 description: The size of the buffer (in MB) to use for transfers. By enabling buffering, the reads and writes, from/to the remote SFTP server, are split in multiple concurrent requests and this allows data to be transferred at a faster rate, over high latency networks, by overlapping round-trip times. With buffering enabled, resuming uploads is not supported and a file cannot be opened for both reading and writing at the same time. 0 means disabled. equality_check_mode: type: integer enum: - 0 - 1 description: | Defines how to check if this config points to the same server as another config. If different configs point to the same server the renaming between the fs configs is allowed: * `0` username and endpoint must match. This is the default * `1` only the endpoint must match socks_proxy: type: string description: 'SOCKS proxy address including schema, host, and port. Examples: socks5://127.0.0.1:1080, socks4://127.0.0.1:1080, socks4a://127.0.0.1:1080' socks_username: type: string description: Username for SOCKS5 proxy authentication. Leave empty if the proxy does not require authentication socks_password: $ref: '#/components/schemas/Secret' FTPFsConfig: type: object properties: endpoint: type: string description: 'remote FTP endpoint as host:port' username: type: string description: Username for FTP authentication password: $ref: '#/components/schemas/Secret' tls_mode: type: integer enum: - 0 - 1 - 2 description: | Defines the TLS mode to use: * `0` disabled * `1` explicit * `2` implicit skip_tls_verify: type: boolean description: If true, the TLS certificate of the FTP server will not be verified HTTPFsConfig: type: object properties: endpoint: type: string description: 'HTTP/S endpoint URL. SFTPGo will use this URL as base, for example for the `stat` API, SFTPGo will add `/stat/{name}`' username: type: string description: Username for HTTP Basic authentication password: $ref: '#/components/schemas/Secret' api_key: $ref: '#/components/schemas/Secret' skip_tls_verify: type: boolean description: If true, the TLS certificate of the HTTP endpoint will not be verified. Use with caution equality_check_mode: type: integer enum: - 0 - 1 description: | Defines how to check if this config points to the same server as another config. If different configs point to the same server the renaming between the fs configs is allowed: * `0` username and endpoint must match. This is the default * `1` only the endpoint must match FilesystemConfig: type: object description: | Storage filesystem details. The `provider` field selects which sub-config is active — all other sub-configs are ignored / cleared on save. See `FsProviders` for the mapping. * `provider: 0` → `osconfig` (local; root is the user's `home_dir`) * `provider: 1` → `s3config` * `provider: 2` → `gcsconfig` * `provider: 3` → `azblobconfig` * `provider: 4` → `cryptconfig` (encrypted local; root is the user's `home_dir`) * `provider: 5` → `sftpconfig` * `provider: 6` → `httpconfig` `ftpconfig` is reserved (legacy / internal) and is not selected by any public `provider` value. properties: provider: $ref: '#/components/schemas/FsProviders' osconfig: $ref: '#/components/schemas/OSFsConfig' s3config: $ref: '#/components/schemas/S3Config' gcsconfig: $ref: '#/components/schemas/GCSConfig' azblobconfig: $ref: '#/components/schemas/AzureBlobFsConfig' cryptconfig: $ref: '#/components/schemas/CryptFsConfig' sftpconfig: $ref: '#/components/schemas/SFTPFsConfig' ftpconfig: $ref: '#/components/schemas/FTPFsConfig' httpconfig: $ref: '#/components/schemas/HTTPFsConfig' BaseVirtualFolder: type: object required: - name properties: id: type: integer format: int32 minimum: 1 readOnly: true name: type: string description: Unique name for this virtual folder (server-side identifier; not shown to end users). mapped_path: type: string description: Absolute filesystem path used as the virtual folder's storage root. Only meaningful when `filesystem.provider` is 0 (local) or 4 (encrypted local); ignored for cloud providers, which reference a bucket/container via `filesystem.*config`. description: type: string description: Optional free-form description. used_quota_size: type: integer format: int64 used_quota_files: type: integer format: int32 last_quota_update: type: integer format: int64 description: Last quota update as unix timestamp in milliseconds users: type: array readOnly: true items: type: string description: list of usernames associated with this virtual folder groups: type: array readOnly: true items: type: string description: list of groups associated with this virtual folder filesystem: $ref: '#/components/schemas/FilesystemConfig' description: 'Defines the filesystem for the virtual folder and the used quota limits. The same folder can be shared among multiple users and each user can have different quota limits or a different virtual path.' VirtualFolder: allOf: - $ref: '#/components/schemas/BaseVirtualFolder' - type: object properties: virtual_path: type: string quota_size: type: integer format: int64 description: 'Quota as size in bytes. 0 means unlimited, -1 means included in user quota. Please note that quota is updated if files are added/removed via SFTPGo otherwise a quota scan or a manual quota update is needed' quota_files: type: integer format: int32 description: 'Quota as number of files. 0 means unlimited, , -1 means included in user quota. Please note that quota is updated if files are added/removed via SFTPGo otherwise a quota scan or a manual quota update is needed' required: - virtual_path description: 'A virtual folder is a mapping between a SFTPGo virtual path and a filesystem path outside the user home directory. The specified paths must be absolute and the virtual path cannot be "/", it must be a sub directory. The parent directory for the specified virtual path must exist. SFTPGo will try to automatically create any missing parent directory for the configured virtual folders at user login.' User: type: object required: - username - permissions description: | Represents an SFTPGo user. Storage is selected via `filesystem.provider` — see `FsProviders` for the enum. For provider `0` (local) or `4` (encrypted local) you must set `home_dir`; for cloud providers the corresponding `filesystem.*config` sub-object is required. At least one entry in `permissions` is required; the map key is a virtual path (use `/` for the root) and the value is an array of permission strings. For typical read/write access use `{"/":["*"]}`. On write, the `password` field accepts a plaintext password (stored hashed) or a pre-hashed value (bcrypt `$2a$…`, argon2id, pbkdf2, unix crypt — stored as-is). On read, `password` is always omitted; use `has_password` to check whether one is set. properties: id: type: integer format: int32 minimum: 1 readOnly: true status: type: integer enum: - 0 - 1 description: | Account status: * `0` user is disabled, login is not allowed * `1` user is enabled username: type: string description: Unique username. After creation it is immutable via the API. email: type: string format: email description: type: string description: 'optional description, for example the user full name' expiration_date: type: integer format: int64 description: expiration date as unix timestamp in milliseconds. An expired account cannot login. 0 means no expiration password: type: string format: password description: If the password has no known hashing algo prefix it will be stored, by default, using bcrypt, argon2id is supported too. You can send a password hashed as bcrypt ($2a$ prefix), argon2id, pbkdf2 or unix crypt and it will be stored as is. For security reasons this field is omitted when you search/get users public_keys: type: array items: type: string example: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEUWwDwEWhTbF0MqAsp/oXK1HR2cElhM8oo1uVmL3ZeDKDiTm4ljMr92wfTgIGDqIoxmVqgYIkAOAhuykAVWBzc= user@host description: Public keys in OpenSSH format. has_password: type: boolean description: Indicates whether the password is set home_dir: type: string description: path to the user home directory. The user cannot upload or download files outside this directory. SFTPGo tries to automatically create this folder if missing. Must be an absolute path virtual_folders: type: array items: $ref: '#/components/schemas/VirtualFolder' description: mapping between virtual SFTPGo paths and virtual folders uid: type: integer format: int32 minimum: 0 maximum: 2147483647 description: 'if you run SFTPGo as root user, the created files and directories will be assigned to this uid. 0 means no change, the owner will be the user that runs SFTPGo. Ignored on windows' gid: type: integer format: int32 minimum: 0 maximum: 2147483647 description: 'if you run SFTPGo as root user, the created files and directories will be assigned to this gid. 0 means no change, the group will be the one of the user that runs SFTPGo. Ignored on windows' max_sessions: type: integer format: int32 description: Limit the sessions that a user can open. 0 means unlimited quota_size: type: integer format: int64 description: Quota as size in bytes. 0 means unlimited. Please note that quota is updated if files are added/removed via SFTPGo otherwise a quota scan or a manual quota update is needed quota_files: type: integer format: int32 description: Quota as number of files. 0 means unlimited. Please note that quota is updated if files are added/removed via SFTPGo otherwise a quota scan or a manual quota update is needed permissions: type: object additionalProperties: type: array items: $ref: '#/components/schemas/Permission' minItems: 1 minProperties: 1 description: 'hash map with directory as key and an array of permissions as value. Directories must be absolute paths, permissions for root directory ("/") are required' example: /: - '*' /somedir: - list - download used_quota_size: type: integer format: int64 used_quota_files: type: integer format: int32 last_quota_update: type: integer format: int64 description: Last quota update as unix timestamp in milliseconds upload_bandwidth: type: integer description: 'Maximum upload bandwidth as KB/s, 0 means unlimited' download_bandwidth: type: integer description: 'Maximum download bandwidth as KB/s, 0 means unlimited' upload_data_transfer: type: integer description: 'Maximum data transfer allowed for uploads as MB. 0 means no limit' download_data_transfer: type: integer description: 'Maximum data transfer allowed for downloads as MB. 0 means no limit' total_data_transfer: type: integer description: 'Maximum total data transfer as MB. 0 means unlimited. You can set a total data transfer instead of the individual values for uploads and downloads' used_upload_data_transfer: type: integer description: 'Uploaded size, as bytes, since the last reset' used_download_data_transfer: type: integer description: 'Downloaded size, as bytes, since the last reset' created_at: type: integer format: int64 description: 'creation time as unix timestamp in milliseconds. It will be 0 for users created before v2.2.0' updated_at: type: integer format: int64 description: last update time as unix timestamp in milliseconds last_login: type: integer format: int64 description: Last user login as unix timestamp in milliseconds. It is saved at most once every 10 minutes first_download: type: integer format: int64 description: first download time as unix timestamp in milliseconds first_upload: type: integer format: int64 description: first upload time as unix timestamp in milliseconds last_password_change: type: integer format: int64 description: last password change time as unix timestamp in milliseconds filters: $ref: '#/components/schemas/UserFilters' filesystem: $ref: '#/components/schemas/FilesystemConfig' additional_info: type: string description: Free form text field for external systems groups: type: array items: $ref: '#/components/schemas/GroupMapping' oidc_custom_fields: type: object additionalProperties: true description: 'This field is passed to the pre-login hook if custom OIDC token fields have been configured. Field values can be of any type (this is a free form object) and depend on the type of the configured OIDC token fields' role: type: string description: 'Role name for the user. When set, role admins can only manage users with matching roles' AdminPreferences: type: object properties: hide_user_page_sections: type: integer description: 'Allow to hide some sections from the user page. These are not security settings and are not enforced server side in any way. They are only intended to simplify the user page in the WebAdmin UI. 1 means hide groups section, 2 means hide filesystem section, "users_base_dir" must be set in the config file otherwise this setting is ignored, 4 means hide virtual folders section, 8 means hide profile section, 16 means hide ACLs section, 32 means hide disk and bandwidth quota limits section, 64 means hide advanced settings section. The settings can be combined' default_users_expiration: type: integer description: 'Defines the default expiration for newly created users as number of days. 0 means no expiration' AdminFilters: type: object properties: allow_list: type: array items: type: string description: 'only clients connecting from these IP/Mask are allowed. IP/Mask must be in CIDR notation as defined in RFC 4632 and RFC 4291, for example "192.0.2.0/24" or "2001:db8::/32"' example: - 192.0.2.0/24 - '2001:db8::/32' allow_api_key_auth: type: boolean description: 'API key auth allows to impersonate this administrator with an API key' require_two_factor: type: boolean description: If true, the admin must configure two-factor authentication (TOTP) before being able to log in require_password_change: type: boolean description: If true, the admin will be required to change their password at the next login disable_password_auth: type: boolean description: If true, password-based authentication is disabled. The admin must use alternative methods such as API keys or OIDC totp_config: $ref: '#/components/schemas/AdminTOTPConfig' recovery_codes: type: array items: $ref: '#/components/schemas/RecoveryCode' preferences: $ref: '#/components/schemas/AdminPreferences' Admin: type: object properties: id: type: integer format: int32 minimum: 1 status: type: integer enum: - 0 - 1 description: | status: * `0` user is disabled, login is not allowed * `1` user is enabled username: type: string description: username is unique description: type: string description: 'optional description, for example the admin full name' password: type: string format: password description: Admin password. For security reasons this field is omitted when you search/get admins email: type: string format: email permissions: type: array items: $ref: '#/components/schemas/AdminPermissions' filters: $ref: '#/components/schemas/AdminFilters' additional_info: type: string description: Free form text field groups: type: array items: $ref: '#/components/schemas/AdminGroupMapping' description: 'Groups automatically selected for new users created by this admin. The admin will still be able to choose different groups. These settings are only used for this admin UI and they will be ignored in REST API/hooks.' created_at: type: integer format: int64 description: 'creation time as unix timestamp in milliseconds. It will be 0 for admins created before v2.2.0' updated_at: type: integer format: int64 description: last update time as unix timestamp in milliseconds last_login: type: integer format: int64 description: Last user login as unix timestamp in milliseconds. It is saved at most once every 10 minutes role: type: string description: 'If set the admin can only administer users with the same role. Role admins cannot have the "*" permission' AdminProfile: type: object properties: email: type: string format: email description: type: string allow_api_key_auth: type: boolean description: 'If enabled, you can impersonate this admin, in REST API, using an API key. If disabled admin credentials are required for impersonation' UserProfile: type: object properties: email: type: string format: email description: type: string allow_api_key_auth: type: boolean description: 'If enabled, you can impersonate this user, in REST API, using an API key. If disabled user credentials are required for impersonation' public_keys: type: array items: type: string example: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEUWwDwEWhTbF0MqAsp/oXK1HR2cElhM8oo1uVmL3ZeDKDiTm4ljMr92wfTgIGDqIoxmVqgYIkAOAhuykAVWBzc= user@host description: Public keys in OpenSSH format APIKey: type: object properties: id: type: string description: unique key identifier name: type: string description: User friendly key name key: type: string format: password description: We store the hash of the key. This is just like a password. For security reasons this field is omitted when you search/get API keys scope: $ref: '#/components/schemas/APIKeyScope' created_at: type: integer format: int64 description: creation time as unix timestamp in milliseconds updated_at: type: integer format: int64 description: last update time as unix timestamp in milliseconds last_use_at: type: integer format: int64 description: last use time as unix timestamp in milliseconds. It is saved at most once every 10 minutes expires_at: type: integer format: int64 description: expiration time as unix timestamp in milliseconds. Once set, the expiration date cannot be extended. It can only be shortened. description: type: string description: optional description user: type: string description: username associated with this API key. If empty and the scope is "user scope" the key can impersonate any user admin: type: string description: admin associated with this API key. If empty and the scope is "admin scope" the key can impersonate any admin QuotaUsage: type: object properties: used_quota_size: type: integer format: int64 used_quota_files: type: integer format: int32 TransferQuotaUsage: type: object properties: used_upload_data_transfer: type: integer format: int64 description: 'The value must be specified as bytes' used_download_data_transfer: type: integer format: int64 description: 'The value must be specified as bytes' Transfer: type: object properties: operation_type: type: string enum: - upload - download description: | Operations: * `upload` * `download` path: type: string description: file path for the upload/download start_time: type: integer format: int64 description: start time as unix timestamp in milliseconds size: type: integer format: int64 description: bytes transferred ConnectionStatus: type: object properties: username: type: string description: connected username connection_id: type: string description: unique connection identifier client_version: type: string description: client version remote_address: type: string description: Remote address for the connected client connection_time: type: integer format: int64 description: connection time as unix timestamp in milliseconds command: type: string description: Last SSH/FTP command or WebDAV method last_activity: type: integer format: int64 description: last client activity as unix timestamp in milliseconds protocol: type: string enum: - SFTP - SCP - SSH - FTP - DAV active_transfers: type: array items: $ref: '#/components/schemas/Transfer' node: type: string description: 'Node identifier, omitted for single node installations' FolderRetention: type: object properties: path: type: string description: 'virtual directory path as seen by users, if no other specific retention is defined, the retention applies for sub directories too. For example if retention is defined for the paths "/" and "/sub" then the retention for "/" is applied for any file outside the "/sub" directory' example: '/' retention: type: integer description: retention time in hours. All the files with a modification time older than the defined value will be deleted. 0 means exclude this path example: 24 delete_empty_dirs: type: boolean description: if enabled, empty directories will be deleted RetentionCheck: type: object properties: username: type: string description: username to which the retention check refers folders: type: array items: $ref: '#/components/schemas/FolderRetention' start_time: type: integer format: int64 description: check start time as unix timestamp in milliseconds QuotaScan: type: object properties: username: type: string description: username to which the quota scan refers start_time: type: integer format: int64 description: scan start time as unix timestamp in milliseconds FolderQuotaScan: type: object properties: name: type: string description: folder name to which the quota scan refers start_time: type: integer format: int64 description: scan start time as unix timestamp in milliseconds DefenderEntry: type: object properties: id: type: string ip: type: string score: type: integer description: the score increases whenever a violation is detected, such as an attempt to log in using an incorrect password or invalid username. If the score exceeds the configured threshold, the IP is banned. Omitted for banned IPs ban_time: type: string format: date-time description: date time until the IP is blocked. For already blocked hosts, the block time is increased each time a new violation is detected. Omitted if the IP is not banned SSHHostKey: type: object properties: path: type: string fingerprint: type: string algorithms: type: array items: type: string SSHBinding: type: object properties: address: type: string description: TCP address the server listen on port: type: integer description: the port used for serving requests apply_proxy_config: type: boolean description: 'apply the proxy configuration, if any' WebDAVBinding: type: object properties: address: type: string description: TCP address the server listen on port: type: integer description: the port used for serving requests enable_https: type: boolean min_tls_version: $ref: '#/components/schemas/TLSVersions' client_auth_type: type: integer description: 1 means that client certificate authentication is required in addition to HTTP basic authentication tls_cipher_suites: type: array items: type: string description: 'List of supported cipher suites for TLS version 1.2. If empty a default list of secure cipher suites is used, with a preference order based on hardware performance' prefix: type: string description: 'Prefix for WebDAV resources, if empty WebDAV resources will be available at the `/` URI' proxy_allowed: type: array items: type: string description: 'List of IP addresses and IP ranges allowed to set proxy headers' PassiveIPOverride: type: object properties: networks: type: array items: type: string ip: type: string FTPDBinding: type: object properties: address: type: string description: TCP address the server listen on port: type: integer description: the port used for serving requests apply_proxy_config: type: boolean description: 'apply the proxy configuration, if any' tls_mode: type: integer enum: - 0 - 1 - 2 description: | TLS mode: * `0` - clear or explicit TLS * `1` - explicit TLS required * `2` - implicit TLS min_tls_version: $ref: '#/components/schemas/TLSVersions' force_passive_ip: type: string description: External IP address for passive connections passive_ip_overrides: type: array items: $ref: '#/components/schemas/PassiveIPOverride' client_auth_type: type: integer description: 1 means that client certificate authentication is required in addition to FTP authentication tls_cipher_suites: type: array items: type: string description: 'List of supported cipher suites for TLS version 1.2. If empty a default list of secure cipher suites is used, with a preference order based on hardware performance' passive_connections_security: type: integer enum: - 0 - 1 description: | Active connections security: * `0` - require matching peer IP addresses of control and data connection * `1` - disable any checks active_connections_security: type: integer enum: - 0 - 1 description: | Active connections security: * `0` - require matching peer IP addresses of control and data connection * `1` - disable any checks ignore_ascii_transfer_type: type: integer enum: - 0 - 1 description: | Ignore client requests to perform ASCII translations: * `0` - ASCII translations are enabled * `1` - ASCII translations are silently ignored debug: type: boolean description: 'If enabled any FTP command will be logged' SSHServiceStatus: type: object properties: is_active: type: boolean bindings: type: array items: $ref: '#/components/schemas/SSHBinding' nullable: true host_keys: type: array items: $ref: '#/components/schemas/SSHHostKey' nullable: true ssh_commands: type: array items: type: string authentications: type: array items: $ref: '#/components/schemas/SSHAuthentications' public_key_algorithms: type: array items: type: string macs: type: array items: type: string kex_algorithms: type: array items: type: string ciphers: type: array items: type: string FTPPassivePortRange: type: object properties: start: type: integer end: type: integer FTPServiceStatus: type: object properties: is_active: type: boolean bindings: type: array items: $ref: '#/components/schemas/FTPDBinding' nullable: true passive_port_range: $ref: '#/components/schemas/FTPPassivePortRange' WebDAVServiceStatus: type: object properties: is_active: type: boolean bindings: type: array items: $ref: '#/components/schemas/WebDAVBinding' nullable: true DataProviderStatus: type: object properties: is_active: type: boolean driver: type: string error: type: string MFAStatus: type: object properties: is_active: type: boolean totp_configs: type: array items: $ref: '#/components/schemas/TOTPConfig' ServicesStatus: type: object properties: ssh: $ref: '#/components/schemas/SSHServiceStatus' ftp: $ref: '#/components/schemas/FTPServiceStatus' webdav: $ref: '#/components/schemas/WebDAVServiceStatus' data_provider: $ref: '#/components/schemas/DataProviderStatus' defender: type: object properties: is_active: type: boolean mfa: $ref: '#/components/schemas/MFAStatus' allow_list: type: object properties: is_active: type: boolean rate_limiters: type: object properties: is_active: type: boolean protocols: type: array items: type: string example: SSH SaaSUsage: type: object properties: bandwidth_used: type: integer format: int64 bandwidth_allowed: type: integer format: int64 storage_used: type: integer format: int64 storage_allowed: type: integer format: int64 ShareGroupBinding: type: object properties: group_name: type: string example: "developers" permissions: type: integer description: 'Bitmask representing the share permissions. 1 means Read, 2 Write, 4 Delete. Example: For Read + Write, the value is 3 (1 + 2).' Share: type: object properties: id: type: string description: auto-generated unique share identifier name: type: string description: type: string description: optional description scope: $ref: '#/components/schemas/ShareScope' paths: type: array items: type: string description: 'paths to files or directories, for share scope write this array must contain exactly one directory. Paths will not be validated on save so you can also create them after creating the share' example: - '/dir1' - '/dir2/file.txt' - '/dir3/subdir' username: type: string description: 'Owner of the share. Only the owner can update paths and manage group membership' created_at: type: integer format: int64 description: 'creation time as unix timestamp in milliseconds' updated_at: type: integer format: int64 description: 'last update time as unix timestamp in milliseconds' last_use_at: type: integer format: int64 description: last use time as unix timestamp in milliseconds expires_at: type: integer format: int64 description: 'optional share expiration, as unix timestamp in milliseconds. 0 means no expiration' password: type: string description: 'optional password to protect the share. The special value "[**redacted**]" means that a password has been set, you can use this value if you want to preserve the current password when you update a share' max_tokens: type: integer description: 'maximum allowed access tokens. 0 means no limit' used_tokens: type: integer allow_from: type: array items: type: string description: 'Limit the share availability to these IP/Mask. IP/Mask must be in CIDR notation as defined in RFC 4632 and RFC 4291, for example "192.0.2.0/24" or "2001:db8::/32". An empty list means no restrictions' example: - 192.0.2.0/24 - '2001:db8::/32' groups: type: array items: $ref: '#/components/schemas/ShareGroupBinding' user_permissions: type: integer format: int32 readOnly: true description: 'Computed bitmask representing the share permissions. 1 means Read, 2 Write, 4 Delete' options: type: object properties: emails: type: array items: type: string format: email description: Email addresses for share recipients. Required when auth_mode is set to 1 (email authentication) auth_mode: type: integer description: 'Set to 1 to enable email authentication mode. In this mode the defined email addresses can be used to receive a one-time authentication code' legal_agreement: type: integer description: 'Set to 1 to require acceptance of a legal agreement before the share can be used. Only available if enabled by a system administrator' GroupUserSettings: type: object properties: home_dir: type: string description: 'Home directory for users in this group. Supports placeholders such as %username%. If empty, the user''s own home_dir is used' max_sessions: type: integer format: int32 description: Maximum number of concurrent sessions allowed. 0 means unlimited quota_size: type: integer format: int64 description: 'Maximum total storage size in bytes. 0 means unlimited, -1 means included in the user''s quota' quota_files: type: integer format: int32 description: 'Maximum number of files allowed. 0 means unlimited, -1 means included in the user''s quota' permissions: type: object additionalProperties: type: array items: $ref: '#/components/schemas/Permission' minItems: 1 minProperties: 1 description: 'hash map with directory as key and an array of permissions as value. Directories must be absolute paths, permissions for root directory ("/") are required' example: /: - '*' /somedir: - list - download upload_bandwidth: type: integer description: 'Maximum upload bandwidth as KB/s' download_bandwidth: type: integer description: 'Maximum download bandwidth as KB/s' upload_data_transfer: type: integer description: 'Maximum data transfer allowed for uploads as MB' download_data_transfer: type: integer description: 'Maximum data transfer allowed for downloads as MB' total_data_transfer: type: integer description: 'Maximum total data transfer as MB' expires_in: type: integer description: 'Account expiration in number of days from creation. 0 means no expiration' filters: $ref: '#/components/schemas/GroupFilters' filesystem: $ref: '#/components/schemas/FilesystemConfig' Role: type: object properties: id: type: integer format: int32 minimum: 1 name: type: string description: name is unique description: type: string description: 'optional description' created_at: type: integer format: int64 description: creation time as unix timestamp in milliseconds updated_at: type: integer format: int64 description: last update time as unix timestamp in milliseconds users: type: array items: type: string description: list of usernames associated with this group admins: type: array items: type: string description: list of admins usernames associated with this group Group: type: object required: - name properties: id: type: integer format: int32 minimum: 1 readOnly: true name: type: string description: Unique group name. Referenced from `User.groups[].name` with a type (primary/secondary/membership). description: type: string description: Optional free-form description. created_at: type: integer format: int64 readOnly: true description: Creation time as Unix timestamp in milliseconds. updated_at: type: integer format: int64 readOnly: true description: Last update time as Unix timestamp in milliseconds. user_settings: $ref: '#/components/schemas/GroupUserSettings' virtual_folders: type: array items: $ref: '#/components/schemas/VirtualFolder' description: mapping between virtual SFTPGo paths and folders users: type: array items: type: string description: list of usernames associated with this group admins: type: array items: type: string description: list of admins usernames associated with this group GroupMapping: type: object properties: name: type: string description: group name type: enum: - 1 - 2 - 3 description: | Group type: * `1` - Primary group * `2` - Secondary group * `3` - Membership only, no settings are inherited from this group type AdminGroupMappingOptions: type: object properties: add_to_users_as: enum: - 0 - 1 - 2 description: | Add to new users as: * `0` - the admin's group will be added as membership group for new users * `1` - the admin's group will be added as primary group for new users * `2` - the admin's group will be added as secondary group for new users AdminGroupMapping: type: object properties: name: type: string description: group name options: $ref: '#/components/schemas/AdminGroupMappingOptions' BackupData: type: object properties: users: type: array items: $ref: '#/components/schemas/User' folders: type: array items: $ref: '#/components/schemas/BaseVirtualFolder' groups: type: array items: $ref: '#/components/schemas/Group' admins: type: array items: $ref: '#/components/schemas/Admin' api_keys: type: array items: $ref: '#/components/schemas/APIKey' shares: type: array items: $ref: '#/components/schemas/Share' event_actions: type: array items: $ref: '#/components/schemas/EventAction' event_rules: type: array items: $ref: '#/components/schemas/EventRule' roles: type: array items: $ref: '#/components/schemas/Role' version: type: integer PwdChange: type: object properties: current_password: type: string new_password: type: string DirEntry: type: object properties: name: type: string description: name of the file (or subdirectory) described by the entry. This name is the final element of the path (the base name), not the entire path size: type: integer format: int64 description: file size, omitted for folders and non regular files mode: type: integer description: | File mode and permission bits. More details here: https://golang.org/pkg/io/fs/#FileMode. Let's see some examples: - for a directory mode&2147483648 != 0 - for a symlink mode&134217728 != 0 - for a regular file mode&2401763328 == 0 last_modified: type: string format: date-time FsEvent: type: object properties: id: type: string timestamp: type: integer format: int64 description: 'unix timestamp in nanoseconds' action: $ref: '#/components/schemas/FsEventAction' username: type: string external_username: type: string description: 'external username, e.g. the email of a user who accessed a public share with email authentication' fs_path: type: string fs_target_path: type: string virtual_path: type: string virtual_target_path: type: string ssh_cmd: type: string file_size: type: integer format: int64 elapsed: type: integer format: int64 description: elapsed time as milliseconds status: $ref: '#/components/schemas/FsEventStatus' protocol: $ref: '#/components/schemas/EventProtocols' ip: type: string session_id: type: string fs_provider: $ref: '#/components/schemas/FsProviders' bucket: type: string endpoint: type: string open_flags: type: string role: type: string instance_id: type: string ProviderEvent: type: object properties: id: type: string timestamp: type: integer format: int64 description: 'unix timestamp in nanoseconds' action: $ref: '#/components/schemas/ProviderEventAction' username: type: string ip: type: string object_type: $ref: '#/components/schemas/ProviderEventObjectType' object_name: type: string object_data: type: string format: byte description: 'base64 of the JSON serialized object with sensitive fields removed' role: type: string instance_id: type: string LogEvent: type: object properties: id: type: string timestamp: type: integer format: int64 description: 'unix timestamp in nanoseconds' event: $ref: '#/components/schemas/LogEventType' protocol: $ref: '#/components/schemas/EventProtocols' username: type: string ip: type: string message: type: string role: type: string instance_id: type: string KeyValue: type: object properties: key: type: string value: type: string RenameConfig: allOf: - $ref: '#/components/schemas/KeyValue' - type: object properties: update_modtime: type: boolean description: 'Update modification time. This setting is not recursive and only applies to storage providers that support changing modification times' CopyConfig: allOf: - $ref: '#/components/schemas/KeyValue' - type: object properties: on_source_copied: type: integer enum: [0, 1, 2] description: 'Source disposition after successful copy. 0 = none (default), 1 = delete source, 2 = move source to on_source_copied_move_path' on_source_copied_move_path: type: string description: 'Destination directory for moved sources. Required when on_source_copied is 2. Directory structure relative to source base is preserved' max_retries: type: integer minimum: 0 maximum: 5 description: 'Number of retry attempts per file. Each retry waits 5 seconds. 0 means no retry' HTTPPart: type: object properties: name: type: string headers: type: array items: $ref: '#/components/schemas/KeyValue' description: 'Additional headers. Content-Disposition header is automatically set. Content-Type header is automatically detect for files to attach' filepath: type: string description: 'path to the file to be sent as an attachment' body: type: string EventActionHTTPConfig: type: object properties: endpoint: type: string description: HTTP endpoint example: https://example.com username: type: string password: $ref: '#/components/schemas/Secret' headers: type: array items: $ref: '#/components/schemas/KeyValue' description: headers to add timeout: type: integer minimum: 1 maximum: 180 description: 'Ignored for multipart requests with files as attachments' skip_tls_verify: type: boolean description: 'if enabled the HTTP client accepts any TLS certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to man-in-the-middle attacks. This should be used only for testing.' method: type: string enum: - GET - POST - PUT - DELETE query_parameters: type: array items: $ref: '#/components/schemas/KeyValue' body: type: string description: HTTP POST/PUT body parts: type: array items: $ref: '#/components/schemas/HTTPPart' description: 'Multipart requests allow to combine one or more sets of data into a single body. For each part, you can set a file path or a body as text. Placeholders are supported in file path, body, header values.' EventActionIMAPConfig: type: object properties: endpoint: type: string description: 'IMAP endpoint in the format schema://host:port. Supported schemas: imap, imaps' example: imaps://imap.example.com:993 username: type: string password: $ref: '#/components/schemas/Secret' auth_type: type: integer enum: - 0 - 1 description: | Auth type: * `0` Login * `1` OAuth2 oauth2: $ref: '#/components/schemas/OAuth2Config' mailbox: type: string description: Leave empty to use INBOX (default inbox) path: type: string description: Directory where downloaded attachments will be stored post_process_action: type: integer enum: - 0 - 1 description: | Post process action: * `0` Mark messages as read * `1` Delete messages target_folder: type: string description: "When a target virtual folder is specified, attachments downloaded from the IMAP server are stored directly in that folder. If no folder is specified, attachments are downloaded to the user account associated with the action. IMAP actions are executed for a single user only, so a target folder or an appropriate filter in the related rule should be set" EventActionICAPConfig: type: object properties: endpoint: type: string description: 'ICAP endpoint in the format schema://host:port. Supported schemas: icap, icaps. The port is optional, if omitted, port 1344 is used' example: icap://icap.example.com/virus_scan timeout: type: integer description: 'Timeout to wait for the final ICAP response' skip_tls_verify: type: boolean paths: type: array items: type: string description: 'Paths to scan. Placeholders are supported, e.g. {{.VirtualPath}}' method: type: string enum: - REQMOD headers: type: array items: $ref: '#/components/schemas/KeyValue' description: headers to add to the ICAP request, for example X-Client-IP block_action: type: integer enum: - 1 - 2 - 3 description: | Action executed when the ICAP server detects an infected file: * `1` Ignore * `2` Delete * `3` Quarantine adapt_action: type: integer enum: - 1 - 2 - 3 - 4 description: | Action executed when the ICAP server returns a modified file: * `1` Ignore * `2` Delete * `3` Quarantine * `4` Overwrite failure_policy: type: integer enum: - 1 - 2 - 3 description: | Action executed when the ICAP scan fails, e.g ICAP server not reachable: * `1` Ignore * `2` Delete * `3` Quarantine quarantine_folder: type: string description: 'If set, files will be quarantined in this virtual folder' quarantine_path: type: string description: 'The path where quarantined files will be stored. Placeholders are supported' EventActionCommandConfig: type: object properties: cmd: type: string description: absolute path to the command to execute args: type: array items: type: string description: 'command line arguments' timeout: type: integer minimum: 1 maximum: 120 env_vars: type: array items: $ref: '#/components/schemas/KeyValue' EventActionEmailConfig: type: object properties: recipients: type: array items: type: string bcc: type: array items: type: string subject: type: string body: type: string content_type: type: integer enum: - 0 - 1 description: | Content type: * `0` text/plain * `1` text/html attachments: type: array items: type: string description: 'list of file paths to attach. The total size is limited to 10 MB' attach_event_report: type: boolean description: 'If enabled, the event report generated by a previous event report action is attached as a compressed CSV file' EventActionDataRetentionConfig: type: object properties: folders: type: array items: $ref: '#/components/schemas/FolderRetention' archive_folder: type: string description: 'If set, files will be archived to this virtual folder' archive_path: type: string description: 'The base path where archived files will be stored. Placeholders are supported' split_reports: type: boolean description: 'Generate individual retention reports per user, allowing one email notification per report' EventActionFsCompress: type: object properties: name: type: string description: 'Full path to the (zip) archive to create. The parent dir must exist' paths: type: array items: type: string description: 'paths to add the archive' EventActionFsExtract: type: object properties: name: type: string description: 'Full path to the (zip) archive to extract' extract_dir: type: string description: ' Directory to extract the archive into' EventActionPGP: type: object description: 'Configuration for PGP actions. Either a password or a key pair is required. For encryption, the public key is required, and the private, if provided, will be used for signing. For decryption, the private key is required, and the public key, if provided, will be used for signature verification' properties: mode: type: integer enum: - 1 - 2 description: | * `1` - Encrypt * `2` - Decrypt profile: type: integer enum: - 0 - 1 - 2 description: | * `0` - Default (widely implemented algorithms) * `1` - RFC 4880 * `2` - RFC 9580 paths: type: array items: type: string description: 'Paths to encrypt or decrypt' password: $ref: '#/components/schemas/Secret' private_key: $ref: '#/components/schemas/Secret' passphrase: $ref: '#/components/schemas/Secret' public_key: type: string EventActionMetadataCheck: type: object description: "This action verifies whether the metadata key matches the configured value or is absent for the specified path. Optionally, it can retry periodically until the specified timeout (in seconds) is reached" properties: path: type: string metadata: $ref: '#/components/schemas/KeyValue' timeout: type: integer minimum: 0 maximum: 300 EventActionFilesystemConfig: type: object properties: type: $ref: '#/components/schemas/FilesystemActionTypes' renames: type: array items: $ref: '#/components/schemas/RenameConfig' mkdirs: type: array items: type: string deletes: type: array items: type: string exist: type: array items: type: string copy: type: array items: $ref: '#/components/schemas/CopyConfig' continue_on_error: type: boolean description: 'Continue processing remaining files after a failure instead of stopping at the first error. Currently supported for copy actions only. Errors are collected and reported at the end' compress: $ref: '#/components/schemas/EventActionFsCompress' decompress: $ref: '#/components/schemas/EventActionFsExtract' pgp: $ref: '#/components/schemas/EventActionPGP' metadata_check: $ref: '#/components/schemas/EventActionMetadataCheck' folder: type: string description: "Actions triggered by filesystem events, such as uploads or downloads, use the filesystem associated with the user. By specifying a folder, you can control which filesystem is used. This is especially useful for events that aren't tied to a user, such as scheduled tasks and advanced workflows" target_folder: type: string description: "By specifying a target folder, you can use a different filesystem for target paths than the one associated with the user who triggered the action. This is useful for moving files to another storage backend, such as a different S3 bucket or an external SFTP server, accessing restricted areas of the same storage backend, supporting scheduled actions, or enabling more advanced workflows" EventActionPasswordExpiration: type: object properties: threshold: type: integer description: 'An email notification will be generated for users whose password expires in a number of days less than or equal to this threshold' EventActionUserInactivity: type: object properties: disable_threshold: type: integer description: 'Inactivity threshold, in days, before disabling the account' delete_threshold: type: integer description: 'Inactivity threshold, in days, before deleting the account' EventActionShareExpiration: type: object properties: advance_notice: type: integer description: 'How many days before the share expiration (real or calculated) the expiration event should be triggered. Set to 0 to disable pre-expiration checks' grace_period: type: integer description: 'How many days a share is kept in the database after it has expired. Set to 0 to disable share deletion' inactivity_threshold: type: integer description: 'Validity period (in days) for shares without an explicit expiration date. If a share has no expires_at set, it is considered expired if inactivity_threshold days have passed since the last use (or creation time if never used). Set to 0 to disable automatic expiration based on inactivity' split_events: type: boolean description: 'Controls how resulting events are passed to subsequent actions. For example, email actions can use this to send a separate notification for each share' EventActionIDPAccountCheck: type: object properties: mode: type: integer enum: - 0 - 1 description: | Account check mode: * `0` Create or update the account * `1` Create the account if it doesn't exist template_user: type: string description: 'SFTPGo user template in JSON format' template_admin: type: string description: 'SFTPGo admin template in JSON format' EventActionEventReportConfig: type: object properties: time_window: type: integer description: 'Time window in minutes. The query range is [now - time_window, now]' fs_actions: type: array items: type: string description: 'Filter events by filesystem action (e.g. upload, download, delete). If empty, all actions are included' statuses: type: array items: type: integer description: 'Filter events by status: 1=OK, 2=error, 3=quota exceeded. If empty, all statuses are included' split_reports: type: boolean description: 'If enabled, separate events are generated for each user, allowing subsequent actions to be executed individually per user' BaseEventActionOptions: type: object description: | Tagged union of action-specific sub-configurations. **Only the sub-object whose key matches the parent action's `type` is read**; the others are zeroed on save. Mapping (see `EventActionTypes`): type 1 → `http_config`, type 2 → `cmd_config`, type 3 → `email_config`, type 8 → `retention_config`, type 9 → `fs_config`, type 11 → `pwd_expiration_config`, type 13 → `idp_config`, type 14 → `user_inactivity_config`, type 16 → `imap_config`, type 17 → `icap_config`, type 18 → `share_expiration_config`, type 19 → `event_report_config`. Types 4, 5, 6, 7, 12, 15 have no sub-configuration. properties: http_config: $ref: '#/components/schemas/EventActionHTTPConfig' cmd_config: $ref: '#/components/schemas/EventActionCommandConfig' email_config: $ref: '#/components/schemas/EventActionEmailConfig' retention_config: $ref: '#/components/schemas/EventActionDataRetentionConfig' fs_config: $ref: '#/components/schemas/EventActionFilesystemConfig' pwd_expiration_config: $ref: '#/components/schemas/EventActionPasswordExpiration' user_inactivity_config: $ref: '#/components/schemas/EventActionUserInactivity' idp_config: $ref: '#/components/schemas/EventActionIDPAccountCheck' imap_config: $ref: '#/components/schemas/EventActionIMAPConfig' icap_config: $ref: '#/components/schemas/EventActionICAPConfig' share_expiration_config: $ref: '#/components/schemas/EventActionShareExpiration' event_report_config: $ref: '#/components/schemas/EventActionEventReportConfig' BaseEventAction: type: object required: - name - type properties: id: type: integer format: int32 minimum: 1 readOnly: true name: type: string description: Unique action name. Must be unique across all event actions. description: type: string description: Optional description of what the action does. type: $ref: '#/components/schemas/EventActionTypes' options: $ref: '#/components/schemas/BaseEventActionOptions' rules: type: array items: type: string readOnly: true description: Names of event rules referencing this action. Populated on read; ignored on write. EventActionOptions: type: object properties: is_failure_action: type: boolean description: If true, this action executes only when a previous action in the rule has failed stop_on_failure: type: boolean description: If true, no further actions in the rule will execute if this action fails execute_sync: type: boolean description: If true, the action is executed synchronously (the trigger waits for completion). Only supported for filesystem events and Identity Provider logins. Cannot be used with failure actions execute_before_file_publish: type: boolean description: >- If set, the action is executed on the temporary upload file before it is renamed to its final path. Requires atomic uploads to be enabled. Mutually exclusive with execute_sync. EventAction: allOf: - $ref: '#/components/schemas/BaseEventAction' - type: object properties: order: type: integer description: execution order relation_options: $ref: '#/components/schemas/EventActionOptions' EventActionMinimal: type: object properties: name: type: string order: type: integer description: execution order relation_options: $ref: '#/components/schemas/EventActionOptions' ConditionPattern: type: object properties: pattern: type: string description: 'Shell-like glob pattern to match against (e.g., "*.txt", "user_*", "/uploads/**")' inverse_match: type: boolean description: If true, the condition matches when the pattern does NOT match match_fs_path: type: boolean description: >- If enabled, the pattern is matched against the filesystem path instead of the virtual path. Only applicable to fs_paths condition patterns. Filesystem paths are backend-dependent: local paths use the OS format (normalized to forward slashes for matching), cloud storage paths have no leading slash (e.g. "bucket/prefix/file.txt"). Patterns can use backslashes (auto-converted to forward slashes on save) or forward slashes. Matching is case-sensitive on all platforms including Windows; use character classes (e.g. "[Dd]ata") if needed. Cloud storage paths have no leading slash, so "/**/*.exe" will not match them, use "**/*.exe" instead ConditionOptions: type: object properties: names: type: array items: $ref: '#/components/schemas/ConditionPattern' description: Username patterns to match. Only events from matching users will trigger the rule group_names: type: array items: $ref: '#/components/schemas/ConditionPattern' description: Group name patterns to match. Only events from users belonging to matching groups will trigger the rule role_names: type: array items: $ref: '#/components/schemas/ConditionPattern' description: Role name patterns to match. Only events from users with matching roles will trigger the rule fs_paths: type: array items: $ref: '#/components/schemas/ConditionPattern' description: Virtual path patterns to match. Only filesystem events on matching paths will trigger the rule protocols: type: array description: Only events from these protocols will trigger the rule. Empty means any protocol items: type: string enum: - SFTP - SCP - SSH - FTP - DAV - HTTP - HTTPShare - OIDC provider_objects: type: array items: $ref: '#/components/schemas/ProviderEventObjectType' min_size: type: integer format: int64 description: Minimum file size in bytes. Only filesystem events involving files of at least this size will trigger the rule. 0 means no minimum max_size: type: integer format: int64 description: Maximum file size in bytes. Only filesystem events involving files up to this size will trigger the rule. 0 means no maximum event_statuses: type: array description: Only events with these outcome statuses will trigger the rule. Empty means any status items: type: integer enum: - 1 - 2 - 3 description: | Event status: - `1` OK - `2` Failed - `3` Quota exceeded concurrent_execution: type: boolean description: allow concurrent execution from multiple nodes Schedule: type: object properties: minute: type: string hour: type: string day_of_week: type: string day_of_month: type: string month: type: string EventConditions: type: object description: 'Defines when a rule triggers. Exactly one of fs_events, provider_events, schedules, or idp_login_event must be configured' properties: fs_events: type: array description: 'Filesystem events that trigger this rule. Use "pre-" prefixed events for synchronous pre-action checks (e.g., pre-upload to validate before allowing the upload)' items: type: string enum: - upload - download - delete - rename - mkdir - rmdir - copy - ssh_cmd - pre-upload - pre-download - pre-delete - first-upload - first-download provider_events: type: array description: 'Data provider events that trigger this rule (e.g., when a user, admin, or folder is created, updated, or deleted)' items: type: string enum: - add - update - delete schedules: type: array items: $ref: '#/components/schemas/Schedule' idp_login_event: type: integer enum: - 0 - 1 - 2 description: | IDP login events: - `0` any login event - `1` user login event - `2` admin login event options: $ref: '#/components/schemas/ConditionOptions' BaseEventRule: type: object required: - name - trigger description: | Event rule — ties a trigger to an ordered list of actions. The `trigger` field determines which sub-fields of `conditions` are meaningful (see `EventTriggerTypes`). Actions are executed in the order defined by `actions[].order`; each action may be marked synchronous, failure-only, or pre-publish via `EventActionOptions`. properties: id: type: integer format: int32 minimum: 1 readOnly: true name: type: string description: Unique rule name. status: type: integer enum: - 0 - 1 description: | Rule status: * `0` disabled — rule is stored but never fires * `1` enabled description: type: string description: Optional free-form description. created_at: type: integer format: int64 readOnly: true description: Creation time as Unix timestamp in milliseconds. updated_at: type: integer format: int64 readOnly: true description: Last update time as Unix timestamp in milliseconds. trigger: $ref: '#/components/schemas/EventTriggerTypes' conditions: $ref: '#/components/schemas/EventConditions' EventRule: allOf: - $ref: '#/components/schemas/BaseEventRule' - type: object properties: actions: type: array items: $ref: '#/components/schemas/EventAction' EventRuleMinimal: allOf: - $ref: '#/components/schemas/BaseEventRule' - type: object properties: actions: type: array items: $ref: '#/components/schemas/EventActionMinimal' OAuth2Config: type: object properties: provider: type: integer enum: - 0 - 1 description: | Provider: * `0` Google * `1` Microsoft tenant: type: string client_id: type: string client_secret: $ref: '#/components/schemas/Secret' refresh_token: $ref: '#/components/schemas/Secret' IPListEntry: type: object properties: ipornet: type: string description: IP address or network in CIDR format, for example `192.168.1.2/32`, `192.168.0.0/24`, `2001:db8::/32` description: type: string description: optional description type: $ref: '#/components/schemas/IPListType' mode: $ref: '#/components/schemas/IPListMode' protocols: type: integer description: Defines the protocol the entry applies to. `0` means all the supported protocols, 1 SSH, 2 FTP, 4 WebDAV, 8 HTTP. Protocols can be combined, for example 3 means SSH and FTP created_at: type: integer format: int64 description: creation time as unix timestamp in milliseconds updated_at: type: integer format: int64 description: last update time as unix timestamp in milliseconds LicenseFeatures: type: object properties: max_concurrent_transfers: type: integer fs_poviders: type: array items: $ref: '#/components/schemas/FsProviders' event_actions: type: array items: $ref: '#/components/schemas/EventActionTypes' fs_actions: type: array items: $ref: '#/components/schemas/FilesystemActionTypes' plugins: type: integer description: '0 disabled, 1 enabled' ha: type: array items: type: integer description: 'HA is enabled if the array contains 1' wopi_users: type: integer description: '-1 unlimited, 0 disabled, > 0 number of allowed users' metering: type: integer description: '1 disabled' fips: type: integer description: '1 enabled' LogLevelConfig: type: object properties: level: type: string description: The severity level of the logs enum: - debug - info - warn - error example: info required: - level License: type: object properties: key: type: string type: type: integer enum: - 1 - 2 description: | * `1` Subscription * `2` Lifetime valid_from: type: integer description: Unix timestamp in seconds valid_to: type: integer description: Unix timestamp in seconds features: $ref: '#/components/schemas/LicenseFeatures' ApiResponse: type: object properties: message: type: string description: 'message, can be empty' error: type: string description: error description if any VersionInfo: type: object properties: version: type: string build_date: type: string commit_hash: type: string features: type: array items: type: string description: 'Features for the current build. Available features are `portable`, `bolt`, `mysql`, `sqlite`, `pgsql`, `s3`, `gcs`, `azblob`, `metrics`. If a feature is available it has a `+` prefix, otherwise a `-` prefix' Token: type: object properties: access_token: type: string expires_at: type: string format: date-time securitySchemes: BasicAuth: type: http scheme: basic BearerAuth: type: http scheme: bearer bearerFormat: JWT APIKeyAuth: type: apiKey in: header name: X-SFTPGO-API-KEY description: 'API key to use for authentication. API key authentication is intrinsically less secure than using a short lived JWT token. You should prefer API key authentication only for machine-to-machine communications in trusted environments. If no admin/user is associated to the provided key you need to add ".username" at the end of the key. For example if your API key is "6ajKLwswLccVBGpZGv596G.ySAXc8vtp9hMiwAuaLtzof" and you want to impersonate the admin with username "myadmin" you have to use "6ajKLwswLccVBGpZGv596G.ySAXc8vtp9hMiwAuaLtzof.myadmin" as API key. When using API key authentication you cannot manage API keys, update the impersonated admin, change password or public keys for the impersonated user.'