> For the complete documentation index, see [llms.txt](https://developer.celigo.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.celigo.com/api/api-reference/data-stores/file-server.md).

# File Server

Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.

### File server and account schemas

## The FileServer object

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"components":{"schemas":{"FileServer":{"type":"object","description":"The environment's SFTP file server — one per environment, resolved from the\ncaller's token (no id in the URL). Partners connect to `endpoint`, verify the\nserver against `hostKeyFingerprints`, and log in as `<username>@<identifier>`\nwith a partner account's password. On an entitled environment where the server\nhas never been enabled, reads answer 404 rather than returning this object; an\nenvironment without the file server add-on gets a reduced object carrying only\n`entitled: false`, `endpoint`, and `limits`.","required":["endpoint","limits","entitled"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the file server record. Absent on the reduced object returned to an environment without the add-on."},"enabled":{"type":"boolean","description":"When true, partners can connect and every enabled account can log in. Setting\nit to false on `PUT` keeps the identifier and all accounts, refuses new\nlogins, and ends any session currently open; the same body sent again is a\nno-op. Absent on the reduced object returned to an environment without the\nadd-on."},"identifier":{"type":"string","minLength":3,"maxLength":30,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,28}[a-z0-9])$","description":"The environment's name inside every partner login (`<username>@<identifier>`),\nunique across the region. Chosen on the `PUT` that first enables the server,\nlowercased on save, and permanent afterwards — a later `PUT` may echo it but a\ndifferent value is rejected with 422 `FILESERVER_IDENTIFIER_IMMUTABLE`.\nAbsent on the reduced object returned to an environment without the add-on."},"endpoint":{"type":"object","readOnly":true,"description":"Where partners connect. Shared by every environment in the region; the `identifier` in the login routes the session.","required":["hostname","port"],"properties":{"hostname":{"type":"string","description":"SFTP host partners connect to."},"port":{"type":"integer","description":"TCP port of the SFTP listener."}}},"hostKeyFingerprints":{"type":"array","readOnly":true,"description":"SHA-256 fingerprints of the server's SSH host keys, one per algorithm, for\npartners to pin before their first connection. Absent on the reduced object\nreturned to an environment without the add-on.","items":{"type":"object","required":["algorithm","fingerprint"],"properties":{"algorithm":{"type":"string","description":"SSH host-key algorithm the fingerprint belongs to."},"fingerprint":{"type":"string","description":"Fingerprint in OpenSSH `SHA256:<base64>` form."}}}},"loginFormat":{"type":"string","readOnly":true,"description":"Template for partner usernames on this server — the account's `username`, `@`, then the server `identifier`. Absent on the reduced object returned to an environment without the add-on."},"limits":{"type":"object","readOnly":true,"description":"Effective ceilings for this environment, from the license where it sets them and from platform defaults otherwise.","required":["maxAccounts","maxConcurrentSessionsPerAccount","maxFileSizeBytes"],"properties":{"maxAccounts":{"type":"integer","minimum":0,"description":"Number of enabled accounts the environment may hold. Creating or re-enabling an account past it answers 403 `FILESERVER_MAX_ACCOUNTS_EXCEEDED`."},"maxConcurrentSessionsPerAccount":{"type":"integer","minimum":0,"description":"Sessions one account may hold open at once; further logins are refused until one closes."},"maxFileSizeBytes":{"type":"integer","minimum":0,"description":"Largest single file a partner may upload, in bytes."}}},"entitled":{"type":"boolean","readOnly":true,"description":"When true, the account's license includes the file server add-on\n(`fileserver.enabled`) and the server can be enabled and managed. When false\nthe object is reduced to `endpoint` and `limits`, and every `PUT` and every\naccount write answers 403 `FILESERVER_NOT_ENTITLED`; existing accounts stay\nreadable."},"storageUsage":{"type":"object","readOnly":true,"description":"Only present on the response to a `PUT` with `enabled: true` — including a\nrepeat on a server that is already enabled: the account's Celigo Storage\nusage exactly as `GET /v1/storage/usage` reports it, read at enable time\nbecause partner uploads land in that storage. Absent on reads.","required":["activeUsage","recycleBinUsage","totalUsage","maxAllowedUsage","isOverage","hardLimit","overage"],"properties":{"activeUsage":{"type":"integer","format":"int64","description":"Bytes consumed by active and pending files."},"recycleBinUsage":{"type":"integer","format":"int64","description":"Bytes consumed by soft-deleted files still retained in the recycle bin."},"totalUsage":{"type":"integer","format":"int64","description":"Sum of `activeUsage` and `recycleBinUsage`; the figure checked against `hardLimit` when a partner uploads."},"maxAllowedUsage":{"type":"integer","format":"int64","description":"Storage quota in bytes granted by the account's license."},"isOverage":{"type":"boolean","description":"When true, `totalUsage` has passed `maxAllowedUsage` and uploads are consuming the overage buffer up to `hardLimit`."},"hardLimit":{"type":"integer","format":"int64","description":"Byte ceiling at which partner uploads are rejected, as `maxAllowedUsage` plus the licensed overage buffer."},"overage":{"type":"integer","format":"int64","description":"Bytes currently consumed beyond `maxAllowedUsage`. Zero when not in overage."}}}}}}}}
```

## The FileServerAccount object

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"components":{"schemas":{"FileServerAccount":{"type":"object","description":"A partner login on the environment's file server. The partner authenticates as\n`login` with the password set through `POST /v1/mft/fileserver/accounts/{_id}/password`\n(never returned — `passwordSet` only says whether one exists) and sees the\nCeligo Storage folder `_homeFolderId` as the root of the session. Disabling the\naccount, deleting it, changing its password, or moving its home folder ends any\nsession it has open.","required":["_id","username","login","_homeFolderId","enabled","passwordSet","overCap","createdAt","lastModified"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the account. Not derived from the username, so a rename keeps it."},"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$","description":"Partner's name on this server, unique per environment even while the account\nis disabled. Lowercased on save; a value outside the pattern is rejected with\n422 `FILESERVER_USERNAME_INVALID` and a name another account already holds\nwith 409 `FILESERVER_USERNAME_TAKEN`."},"login":{"type":"string","readOnly":true,"description":"The exact SFTP username the partner types — `username`, `@`, then the server `identifier`."},"_homeFolderId":{"type":"string","format":"objectId","description":"Celigo Storage folder that is the root of every session for this account —\nthe partner can reach nothing outside it. Must be an active folder in the same\nenvironment; otherwise the write answers 422 `FILESERVER_HOME_FOLDER_NOT_FOUND`\nor `FILESERVER_HOME_FOLDER_NOT_FOLDER`. While the account exists, this folder\nand every folder above it refuse `DELETE /v1/storage/items/{_id}` with 403\n`STORAGE_SFTP_HOME_FOLDER_PROTECTED`; renaming or moving them stays allowed.\nMoving an account to another folder ends its open sessions."},"homeFolderPath":{"type":["string","null"],"readOnly":true,"description":"Absolute path of `_homeFolderId` from the storage root, for display — a\nleading `/` then the folder names on the way down, following renames and\nmoves of those folders. Null when the path could not be resolved for this\nresponse — the folder id is still authoritative."},"enabled":{"type":"boolean","description":"When true, the partner can log in. Disabling keeps the account and its password\nbut refuses logins and ends open sessions; re-enabling counts against\n`limits.maxAccounts` and requires a password to be set (422\n`FILESERVER_LAST_CREDENTIAL` otherwise). New accounts start enabled."},"passwordSet":{"type":"boolean","readOnly":true,"description":"When true, the account has a password. The password itself is never returned by\nany route; change or clear it through `POST /v1/mft/fileserver/accounts/{_id}/password`.\nClearing is allowed only while the account is disabled."},"overCap":{"type":"boolean","readOnly":true,"description":"When true, this enabled account ranks above the environment's `limits.maxAccounts`\nafter the cap was lowered, so its logins are refused until enough other accounts\nare disabled or deleted. Computed on every read; always false while the\nenvironment is within its cap."},"lastConnectedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp of the partner's most recent successful login. Null until the first one."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the account was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the last change to the account, including logins updating `lastConnectedAt`."}}}}}}
```

## Get the environment's file server

> Returns the environment's SFTP file server: whether it is enabled, its permanent\
> \`identifier\`, the shared \`endpoint\` and host-key fingerprints partners connect\
> with, the \`loginFormat\`, and the effective \`limits\`. The server is a singleton\
> resolved from the caller's token — there is no id in the URL. An entitled\
> environment whose server was never enabled answers 404; an environment without\
> the file server add-on gets the reduced object (\`entitled: false\`, \`endpoint\`,\
> \`limits\`) with 200. Requires a full-access API token.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"FileServer":{"type":"object","description":"The environment's SFTP file server — one per environment, resolved from the\ncaller's token (no id in the URL). Partners connect to `endpoint`, verify the\nserver against `hostKeyFingerprints`, and log in as `<username>@<identifier>`\nwith a partner account's password. On an entitled environment where the server\nhas never been enabled, reads answer 404 rather than returning this object; an\nenvironment without the file server add-on gets a reduced object carrying only\n`entitled: false`, `endpoint`, and `limits`.","required":["endpoint","limits","entitled"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the file server record. Absent on the reduced object returned to an environment without the add-on."},"enabled":{"type":"boolean","description":"When true, partners can connect and every enabled account can log in. Setting\nit to false on `PUT` keeps the identifier and all accounts, refuses new\nlogins, and ends any session currently open; the same body sent again is a\nno-op. Absent on the reduced object returned to an environment without the\nadd-on."},"identifier":{"type":"string","minLength":3,"maxLength":30,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,28}[a-z0-9])$","description":"The environment's name inside every partner login (`<username>@<identifier>`),\nunique across the region. Chosen on the `PUT` that first enables the server,\nlowercased on save, and permanent afterwards — a later `PUT` may echo it but a\ndifferent value is rejected with 422 `FILESERVER_IDENTIFIER_IMMUTABLE`.\nAbsent on the reduced object returned to an environment without the add-on."},"endpoint":{"type":"object","readOnly":true,"description":"Where partners connect. Shared by every environment in the region; the `identifier` in the login routes the session.","required":["hostname","port"],"properties":{"hostname":{"type":"string","description":"SFTP host partners connect to."},"port":{"type":"integer","description":"TCP port of the SFTP listener."}}},"hostKeyFingerprints":{"type":"array","readOnly":true,"description":"SHA-256 fingerprints of the server's SSH host keys, one per algorithm, for\npartners to pin before their first connection. Absent on the reduced object\nreturned to an environment without the add-on.","items":{"type":"object","required":["algorithm","fingerprint"],"properties":{"algorithm":{"type":"string","description":"SSH host-key algorithm the fingerprint belongs to."},"fingerprint":{"type":"string","description":"Fingerprint in OpenSSH `SHA256:<base64>` form."}}}},"loginFormat":{"type":"string","readOnly":true,"description":"Template for partner usernames on this server — the account's `username`, `@`, then the server `identifier`. Absent on the reduced object returned to an environment without the add-on."},"limits":{"type":"object","readOnly":true,"description":"Effective ceilings for this environment, from the license where it sets them and from platform defaults otherwise.","required":["maxAccounts","maxConcurrentSessionsPerAccount","maxFileSizeBytes"],"properties":{"maxAccounts":{"type":"integer","minimum":0,"description":"Number of enabled accounts the environment may hold. Creating or re-enabling an account past it answers 403 `FILESERVER_MAX_ACCOUNTS_EXCEEDED`."},"maxConcurrentSessionsPerAccount":{"type":"integer","minimum":0,"description":"Sessions one account may hold open at once; further logins are refused until one closes."},"maxFileSizeBytes":{"type":"integer","minimum":0,"description":"Largest single file a partner may upload, in bytes."}}},"entitled":{"type":"boolean","readOnly":true,"description":"When true, the account's license includes the file server add-on\n(`fileserver.enabled`) and the server can be enabled and managed. When false\nthe object is reduced to `endpoint` and `limits`, and every `PUT` and every\naccount write answers 403 `FILESERVER_NOT_ENTITLED`; existing accounts stay\nreadable."},"storageUsage":{"type":"object","readOnly":true,"description":"Only present on the response to a `PUT` with `enabled: true` — including a\nrepeat on a server that is already enabled: the account's Celigo Storage\nusage exactly as `GET /v1/storage/usage` reports it, read at enable time\nbecause partner uploads land in that storage. Absent on reads.","required":["activeUsage","recycleBinUsage","totalUsage","maxAllowedUsage","isOverage","hardLimit","overage"],"properties":{"activeUsage":{"type":"integer","format":"int64","description":"Bytes consumed by active and pending files."},"recycleBinUsage":{"type":"integer","format":"int64","description":"Bytes consumed by soft-deleted files still retained in the recycle bin."},"totalUsage":{"type":"integer","format":"int64","description":"Sum of `activeUsage` and `recycleBinUsage`; the figure checked against `hardLimit` when a partner uploads."},"maxAllowedUsage":{"type":"integer","format":"int64","description":"Storage quota in bytes granted by the account's license."},"isOverage":{"type":"boolean","description":"When true, `totalUsage` has passed `maxAllowedUsage` and uploads are consuming the overage buffer up to `hardLimit`."},"hardLimit":{"type":"integer","format":"int64","description":"Byte ceiling at which partner uploads are rejected, as `maxAllowedUsage` plus the licensed overage buffer."},"overage":{"type":"integer","format":"int64","description":"Bytes currently consumed beyond `maxAllowedUsage`. Zero when not in overage."}}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/mft/fileserver":{"get":{"operationId":"getFileServer","tags":["File Server"],"summary":"Get the environment's file server","description":"Returns the environment's SFTP file server: whether it is enabled, its permanent\n`identifier`, the shared `endpoint` and host-key fingerprints partners connect\nwith, the `loginFormat`, and the effective `limits`. The server is a singleton\nresolved from the caller's token — there is no id in the URL. An entitled\nenvironment whose server was never enabled answers 404; an environment without\nthe file server add-on gets the reduced object (`entitled: false`, `endpoint`,\n`limits`) with 200. Requires a full-access API token.","responses":{"200":{"description":"The file server, or the reduced object when the add-on is not licensed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServer"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`). A\npersonal access token is refused the same way as a scoped token, and a manage, monitor\nor integration-level user gets `access_restricted`: only the owner and administrators\nmanage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The environment is entitled but its server has never been enabled (`FILESERVER_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Enable or disable the file server

> Enables the environment's file server — creating it, and claiming the permanent\
> \`identifier\`, on the first call — or disables it. The body must carry \`enabled\`\
> as a JSON boolean; anything else is refused rather than interpreted, because a\
> disable ends every open partner session. Every \`enabled: true\` call, including\
> one on a server that is already enabled, checks the account's Celigo Storage\
> entitlement (partner uploads land there) and returns the current \`storageUsage\`\
> alongside the server; \`identifier\` may be omitted once the server exists.\
> Disabling keeps the identifier and every account, refuses new logins, and ends\
> open sessions; repeating a disable is a no-op. Sandbox environments cannot\
> enable a server (403 \`FILESERVER\_NOT\_ENTITLED\`). Requires a full-access API\
> token.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"FileServerRequest":{"type":"object","description":"Body of `PUT /v1/mft/fileserver`. `enabled` must be an explicit boolean — an\nabsent or non-boolean value is rejected (422 `FILESERVER_ENABLED_REQUIRED`)\nrather than read as a disable, because a disable ends every open partner\nsession.","required":["enabled"],"properties":{"enabled":{"type":"boolean","description":"When true, enables the server (creating it on the first call). When false,\ndisables it: the identifier and all accounts are kept, new logins are refused,\nand open sessions end."},"identifier":{"type":"string","minLength":3,"maxLength":30,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,28}[a-z0-9])$","description":"Required on the `PUT` that first enables the server; permanent afterwards.\nLowercased on save and unique across the region (409\n`FILESERVER_IDENTIFIER_TAKEN` when another environment holds it). On later\ncalls omit it or echo the current value — any other value answers 422\n`FILESERVER_IDENTIFIER_IMMUTABLE`, even on a disable."}}},"FileServer":{"type":"object","description":"The environment's SFTP file server — one per environment, resolved from the\ncaller's token (no id in the URL). Partners connect to `endpoint`, verify the\nserver against `hostKeyFingerprints`, and log in as `<username>@<identifier>`\nwith a partner account's password. On an entitled environment where the server\nhas never been enabled, reads answer 404 rather than returning this object; an\nenvironment without the file server add-on gets a reduced object carrying only\n`entitled: false`, `endpoint`, and `limits`.","required":["endpoint","limits","entitled"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the file server record. Absent on the reduced object returned to an environment without the add-on."},"enabled":{"type":"boolean","description":"When true, partners can connect and every enabled account can log in. Setting\nit to false on `PUT` keeps the identifier and all accounts, refuses new\nlogins, and ends any session currently open; the same body sent again is a\nno-op. Absent on the reduced object returned to an environment without the\nadd-on."},"identifier":{"type":"string","minLength":3,"maxLength":30,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,28}[a-z0-9])$","description":"The environment's name inside every partner login (`<username>@<identifier>`),\nunique across the region. Chosen on the `PUT` that first enables the server,\nlowercased on save, and permanent afterwards — a later `PUT` may echo it but a\ndifferent value is rejected with 422 `FILESERVER_IDENTIFIER_IMMUTABLE`.\nAbsent on the reduced object returned to an environment without the add-on."},"endpoint":{"type":"object","readOnly":true,"description":"Where partners connect. Shared by every environment in the region; the `identifier` in the login routes the session.","required":["hostname","port"],"properties":{"hostname":{"type":"string","description":"SFTP host partners connect to."},"port":{"type":"integer","description":"TCP port of the SFTP listener."}}},"hostKeyFingerprints":{"type":"array","readOnly":true,"description":"SHA-256 fingerprints of the server's SSH host keys, one per algorithm, for\npartners to pin before their first connection. Absent on the reduced object\nreturned to an environment without the add-on.","items":{"type":"object","required":["algorithm","fingerprint"],"properties":{"algorithm":{"type":"string","description":"SSH host-key algorithm the fingerprint belongs to."},"fingerprint":{"type":"string","description":"Fingerprint in OpenSSH `SHA256:<base64>` form."}}}},"loginFormat":{"type":"string","readOnly":true,"description":"Template for partner usernames on this server — the account's `username`, `@`, then the server `identifier`. Absent on the reduced object returned to an environment without the add-on."},"limits":{"type":"object","readOnly":true,"description":"Effective ceilings for this environment, from the license where it sets them and from platform defaults otherwise.","required":["maxAccounts","maxConcurrentSessionsPerAccount","maxFileSizeBytes"],"properties":{"maxAccounts":{"type":"integer","minimum":0,"description":"Number of enabled accounts the environment may hold. Creating or re-enabling an account past it answers 403 `FILESERVER_MAX_ACCOUNTS_EXCEEDED`."},"maxConcurrentSessionsPerAccount":{"type":"integer","minimum":0,"description":"Sessions one account may hold open at once; further logins are refused until one closes."},"maxFileSizeBytes":{"type":"integer","minimum":0,"description":"Largest single file a partner may upload, in bytes."}}},"entitled":{"type":"boolean","readOnly":true,"description":"When true, the account's license includes the file server add-on\n(`fileserver.enabled`) and the server can be enabled and managed. When false\nthe object is reduced to `endpoint` and `limits`, and every `PUT` and every\naccount write answers 403 `FILESERVER_NOT_ENTITLED`; existing accounts stay\nreadable."},"storageUsage":{"type":"object","readOnly":true,"description":"Only present on the response to a `PUT` with `enabled: true` — including a\nrepeat on a server that is already enabled: the account's Celigo Storage\nusage exactly as `GET /v1/storage/usage` reports it, read at enable time\nbecause partner uploads land in that storage. Absent on reads.","required":["activeUsage","recycleBinUsage","totalUsage","maxAllowedUsage","isOverage","hardLimit","overage"],"properties":{"activeUsage":{"type":"integer","format":"int64","description":"Bytes consumed by active and pending files."},"recycleBinUsage":{"type":"integer","format":"int64","description":"Bytes consumed by soft-deleted files still retained in the recycle bin."},"totalUsage":{"type":"integer","format":"int64","description":"Sum of `activeUsage` and `recycleBinUsage`; the figure checked against `hardLimit` when a partner uploads."},"maxAllowedUsage":{"type":"integer","format":"int64","description":"Storage quota in bytes granted by the account's license."},"isOverage":{"type":"boolean","description":"When true, `totalUsage` has passed `maxAllowedUsage` and uploads are consuming the overage buffer up to `hardLimit`."},"hardLimit":{"type":"integer","format":"int64","description":"Byte ceiling at which partner uploads are rejected, as `maxAllowedUsage` plus the licensed overage buffer."},"overage":{"type":"integer","format":"int64","description":"Bytes currently consumed beyond `maxAllowedUsage`. Zero when not in overage."}}}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/mft/fileserver":{"put":{"operationId":"updateFileServer","tags":["File Server"],"summary":"Enable or disable the file server","description":"Enables the environment's file server — creating it, and claiming the permanent\n`identifier`, on the first call — or disables it. The body must carry `enabled`\nas a JSON boolean; anything else is refused rather than interpreted, because a\ndisable ends every open partner session. Every `enabled: true` call, including\none on a server that is already enabled, checks the account's Celigo Storage\nentitlement (partner uploads land there) and returns the current `storageUsage`\nalongside the server; `identifier` may be omitted once the server exists.\nDisabling keeps the identifier and every account, refuses new logins, and ends\nopen sessions; repeating a disable is a no-op. Sandbox environments cannot\nenable a server (403 `FILESERVER_NOT_ENTITLED`). Requires a full-access API\ntoken.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServerRequest"}}}},"responses":{"200":{"description":"The server after the change. Includes `storageUsage` whenever `enabled: true` was sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServer"}}}},"400":{"description":"The body is not a JSON object sent as `application/json` (`FILESERVER_JSON_BODY_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`),\nthe account's license lacks the file server add-on or the environment is a\nsandbox (`FILESERVER_NOT_ENTITLED`). A personal access token is refused the same way as a scoped token, and a manage, monitor or integration-level user gets `access_restricted`: only the owner and administrators manage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"A disable was sent for an environment whose server was never enabled (`FILESERVER_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The identifier is already held by another environment in the region (`FILESERVER_IDENTIFIER_TAKEN`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`enabled` is missing or not a boolean (`FILESERVER_ENABLED_REQUIRED`), the\nfirst-enable `identifier` is missing or malformed (`FILESERVER_IDENTIFIER_INVALID`),\nor the body names an identifier different from the server's permanent one\n(`FILESERVER_IDENTIFIER_IMMUTABLE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Enabling requires a live check of the environment's Celigo Storage entitlement;\nwhen that check cannot complete the enable is refused with\n`FILESERVER_STORAGE_UNAVAILABLE` and nothing changes. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## List file server accounts

> Returns the environment's partner accounts oldest first, wrapped in an\
> \`accounts\` array, with \`overCap\` computed for each against the current\
> \`limits.maxAccounts\`. Paging is cursor-based through the \`Link\` header, which\
> lists bare absolute URLs separated by \`, \` rather than RFC 8288 \`\<url>; rel=\`\
> pairs: a URL carrying \`after\` (listed first) is present when more accounts\
> follow the page, and a URL carrying \`before\` is present when accounts precede\
> it — so the first page offers only \`after\`, the last page only \`before\`. The\
> header is absent when every account fits in one page. Cursors are opaque —\
> pass them back exactly as received; anything else answers 400\
> \`FILESERVER\_INVALID\_CURSOR\`. Accounts stay listable after the add-on is removed\
> from the license; only writes are blocked then. Requires a full-access API\
> token and a server that has been enabled at least once.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"FileServerAccount":{"type":"object","description":"A partner login on the environment's file server. The partner authenticates as\n`login` with the password set through `POST /v1/mft/fileserver/accounts/{_id}/password`\n(never returned — `passwordSet` only says whether one exists) and sees the\nCeligo Storage folder `_homeFolderId` as the root of the session. Disabling the\naccount, deleting it, changing its password, or moving its home folder ends any\nsession it has open.","required":["_id","username","login","_homeFolderId","enabled","passwordSet","overCap","createdAt","lastModified"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the account. Not derived from the username, so a rename keeps it."},"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$","description":"Partner's name on this server, unique per environment even while the account\nis disabled. Lowercased on save; a value outside the pattern is rejected with\n422 `FILESERVER_USERNAME_INVALID` and a name another account already holds\nwith 409 `FILESERVER_USERNAME_TAKEN`."},"login":{"type":"string","readOnly":true,"description":"The exact SFTP username the partner types — `username`, `@`, then the server `identifier`."},"_homeFolderId":{"type":"string","format":"objectId","description":"Celigo Storage folder that is the root of every session for this account —\nthe partner can reach nothing outside it. Must be an active folder in the same\nenvironment; otherwise the write answers 422 `FILESERVER_HOME_FOLDER_NOT_FOUND`\nor `FILESERVER_HOME_FOLDER_NOT_FOLDER`. While the account exists, this folder\nand every folder above it refuse `DELETE /v1/storage/items/{_id}` with 403\n`STORAGE_SFTP_HOME_FOLDER_PROTECTED`; renaming or moving them stays allowed.\nMoving an account to another folder ends its open sessions."},"homeFolderPath":{"type":["string","null"],"readOnly":true,"description":"Absolute path of `_homeFolderId` from the storage root, for display — a\nleading `/` then the folder names on the way down, following renames and\nmoves of those folders. Null when the path could not be resolved for this\nresponse — the folder id is still authoritative."},"enabled":{"type":"boolean","description":"When true, the partner can log in. Disabling keeps the account and its password\nbut refuses logins and ends open sessions; re-enabling counts against\n`limits.maxAccounts` and requires a password to be set (422\n`FILESERVER_LAST_CREDENTIAL` otherwise). New accounts start enabled."},"passwordSet":{"type":"boolean","readOnly":true,"description":"When true, the account has a password. The password itself is never returned by\nany route; change or clear it through `POST /v1/mft/fileserver/accounts/{_id}/password`.\nClearing is allowed only while the account is disabled."},"overCap":{"type":"boolean","readOnly":true,"description":"When true, this enabled account ranks above the environment's `limits.maxAccounts`\nafter the cap was lowered, so its logins are refused until enough other accounts\nare disabled or deleted. Computed on every read; always false while the\nenvironment is within its cap."},"lastConnectedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp of the partner's most recent successful login. Null until the first one."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the account was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the last change to the account, including logins updating `lastConnectedAt`."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/mft/fileserver/accounts":{"get":{"operationId":"listFileServerAccounts","tags":["File Server"],"summary":"List file server accounts","description":"Returns the environment's partner accounts oldest first, wrapped in an\n`accounts` array, with `overCap` computed for each against the current\n`limits.maxAccounts`. Paging is cursor-based through the `Link` header, which\nlists bare absolute URLs separated by `, ` rather than RFC 8288 `<url>; rel=`\npairs: a URL carrying `after` (listed first) is present when more accounts\nfollow the page, and a URL carrying `before` is present when accounts precede\nit — so the first page offers only `after`, the last page only `before`. The\nheader is absent when every account fits in one page. Cursors are opaque —\npass them back exactly as received; anything else answers 400\n`FILESERVER_INVALID_CURSOR`. Accounts stay listable after the add-on is removed\nfrom the license; only writes are blocked then. Requires a full-access API\ntoken and a server that has been enabled at least once.","parameters":[{"name":"pageSize","in":"query","required":false,"description":"Accounts per page. Out-of-range and non-numeric values are not rejected — `0` and non-numbers fall back to the default.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"after","in":"query","required":false,"description":"Opaque cursor copied from the `after` URL in the previous page's `Link` header. Returns the accounts created after it.","schema":{"type":"string"}},{"name":"before","in":"query","required":false,"description":"Opaque cursor copied from the `before` URL in a page's `Link` header. Returns the accounts created before it. Ignored when `after` is also sent.","schema":{"type":"string"}}],"responses":{"200":{"description":"One page of accounts.","headers":{"Link":{"description":"Pagination links as bare absolute URLs separated by `, ` — not the RFC 8288\n`<url>; rel=` form. The URL carrying `after` (listed first) is present when\nmore accounts follow this page; the URL carrying `before` is present when\naccounts precede it. Both repeat the request's `pageSize`. Absent when the\nwhole list fits in one page.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["accounts"],"properties":{"accounts":{"type":"array","items":{"$ref":"#/components/schemas/FileServerAccount"}}}}}}},"400":{"description":"The `after` or `before` cursor is not one this list issued (`FILESERVER_INVALID_CURSOR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`), or\nthe environment is a sandbox (`FILESERVER_NOT_ENTITLED`). A personal access token is\nrefused the same way as a scoped token, and a manage, monitor or integration-level user\ngets `access_restricted`: only the owner and administrators manage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The environment's server has never been enabled (`FILESERVER_NOT_ENABLED`); a disabled server is still manageable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Create a file server account

> Creates an enabled partner account with the given \`username\`, \`password\`, and\
> Celigo Storage home folder, and returns it (the password is never returned).\
> Checks run in order and the first failure is answered: username shape, password\
> presence, home-folder presence, the enabled-account cap, then the folder is\
> verified to be an active folder in this environment. From then on the folder\
> and every folder above it refuse \`DELETE /v1/storage/items/{\_id}\` with 403\
> \`STORAGE\_SFTP\_HOME\_FOLDER\_PROTECTED\` until the account is deleted or moved.\
> Requires a full-access API token, the file server add-on on the license, and a\
> server that has been enabled at least once.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"FileServerAccountCreateRequest":{"type":"object","description":"Body of `POST /v1/mft/fileserver/accounts`. The account is created enabled, so\nall three fields are needed for a partner to be able to log in. Checks run in\nthis order and the first failure is returned: `username` shape, `password`\npresence, `_homeFolderId` presence, the enabled-account cap, then the folder's\nexistence and type in this environment.","required":["username","password","_homeFolderId"],"properties":{"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$","description":"Partner's name on this server; the SFTP login becomes `<username>@<identifier>`.\nLowercased on save. Unique per environment, including against disabled accounts\n(409 `FILESERVER_USERNAME_TAKEN`)."},"password":{"type":"string","format":"password","minLength":12,"maxLength":256,"description":"Partner's password. Needs upper- and lower-case letters, a digit, and a symbol;\nanything weaker is rejected with 422 `FILESERVER_WEAK_PASSWORD` and the message\nlists every rule broken. A non-string value counts as no password at all (422\n`FILESERVER_LAST_CREDENTIAL`). Never returned by any route — rotate or clear it\nlater through `POST /v1/mft/fileserver/accounts/{_id}/password`."},"_homeFolderId":{"type":"string","format":"objectId","description":"Celigo Storage folder that becomes the root of the partner's sessions. Must be\nan active folder in this environment — create one with\n`POST /v1/storage/items` first. Once the account exists the folder and its\nancestors cannot be deleted from storage until the account is deleted or\nmoved elsewhere."}}},"FileServerAccount":{"type":"object","description":"A partner login on the environment's file server. The partner authenticates as\n`login` with the password set through `POST /v1/mft/fileserver/accounts/{_id}/password`\n(never returned — `passwordSet` only says whether one exists) and sees the\nCeligo Storage folder `_homeFolderId` as the root of the session. Disabling the\naccount, deleting it, changing its password, or moving its home folder ends any\nsession it has open.","required":["_id","username","login","_homeFolderId","enabled","passwordSet","overCap","createdAt","lastModified"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the account. Not derived from the username, so a rename keeps it."},"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$","description":"Partner's name on this server, unique per environment even while the account\nis disabled. Lowercased on save; a value outside the pattern is rejected with\n422 `FILESERVER_USERNAME_INVALID` and a name another account already holds\nwith 409 `FILESERVER_USERNAME_TAKEN`."},"login":{"type":"string","readOnly":true,"description":"The exact SFTP username the partner types — `username`, `@`, then the server `identifier`."},"_homeFolderId":{"type":"string","format":"objectId","description":"Celigo Storage folder that is the root of every session for this account —\nthe partner can reach nothing outside it. Must be an active folder in the same\nenvironment; otherwise the write answers 422 `FILESERVER_HOME_FOLDER_NOT_FOUND`\nor `FILESERVER_HOME_FOLDER_NOT_FOLDER`. While the account exists, this folder\nand every folder above it refuse `DELETE /v1/storage/items/{_id}` with 403\n`STORAGE_SFTP_HOME_FOLDER_PROTECTED`; renaming or moving them stays allowed.\nMoving an account to another folder ends its open sessions."},"homeFolderPath":{"type":["string","null"],"readOnly":true,"description":"Absolute path of `_homeFolderId` from the storage root, for display — a\nleading `/` then the folder names on the way down, following renames and\nmoves of those folders. Null when the path could not be resolved for this\nresponse — the folder id is still authoritative."},"enabled":{"type":"boolean","description":"When true, the partner can log in. Disabling keeps the account and its password\nbut refuses logins and ends open sessions; re-enabling counts against\n`limits.maxAccounts` and requires a password to be set (422\n`FILESERVER_LAST_CREDENTIAL` otherwise). New accounts start enabled."},"passwordSet":{"type":"boolean","readOnly":true,"description":"When true, the account has a password. The password itself is never returned by\nany route; change or clear it through `POST /v1/mft/fileserver/accounts/{_id}/password`.\nClearing is allowed only while the account is disabled."},"overCap":{"type":"boolean","readOnly":true,"description":"When true, this enabled account ranks above the environment's `limits.maxAccounts`\nafter the cap was lowered, so its logins are refused until enough other accounts\nare disabled or deleted. Computed on every read; always false while the\nenvironment is within its cap."},"lastConnectedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp of the partner's most recent successful login. Null until the first one."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the account was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the last change to the account, including logins updating `lastConnectedAt`."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}}}},"paths":{"/v1/mft/fileserver/accounts":{"post":{"operationId":"createFileServerAccount","tags":["File Server"],"summary":"Create a file server account","description":"Creates an enabled partner account with the given `username`, `password`, and\nCeligo Storage home folder, and returns it (the password is never returned).\nChecks run in order and the first failure is answered: username shape, password\npresence, home-folder presence, the enabled-account cap, then the folder is\nverified to be an active folder in this environment. From then on the folder\nand every folder above it refuse `DELETE /v1/storage/items/{_id}` with 403\n`STORAGE_SFTP_HOME_FOLDER_PROTECTED` until the account is deleted or moved.\nRequires a full-access API token, the file server add-on on the license, and a\nserver that has been enabled at least once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServerAccountCreateRequest"}}}},"responses":{"201":{"description":"The new account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServerAccount"}}}},"400":{"description":"The body is not a JSON object sent as `application/json` (`FILESERVER_JSON_BODY_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`);\nthe license lacks the add-on or the environment is a sandbox\n(`FILESERVER_NOT_ENTITLED`); or the environment already holds\n`limits.maxAccounts` enabled accounts (`FILESERVER_MAX_ACCOUNTS_EXCEEDED`). A personal access token is refused the same way as a scoped token, and a manage, monitor or integration-level user gets `access_restricted`: only the owner and administrators manage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another account in this environment already has that username, enabled or not (`FILESERVER_USERNAME_TAKEN`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The server has never been enabled (`FILESERVER_NOT_ENABLED`); `username` is\nmissing or malformed (`FILESERVER_USERNAME_INVALID`); `password` is missing\nor not a string (`FILESERVER_LAST_CREDENTIAL`) or too weak\n(`FILESERVER_WEAK_PASSWORD` — the message lists every rule broken);\n`_homeFolderId` is missing or does not exist in this environment\n(`FILESERVER_HOME_FOLDER_NOT_FOUND`) or is a file rather than an active folder\n(`FILESERVER_HOME_FOLDER_NOT_FOLDER`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The home folder is verified against Celigo Storage at create time; when that\ncheck cannot complete the create is refused with `FILESERVER_STORAGE_UNAVAILABLE`\nand no account is made. Retry later. A `_homeFolderId` that is not a\nwell-formed 24-character hex id is answered this way too, not with 422 —\nvalidate the id before sending.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Get a file server account

> Returns one partner account with \`overCap\` computed against the current cap.\
> An id that is unknown, belongs to another environment, or is not a valid\
> identifier all answer the same 404. Requires a full-access API token and a\
> server that has been enabled at least once.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"AccountId":{"name":"_id","in":"path","required":true,"description":"The file server account. A malformed value answers 404 like an unknown one.","schema":{"type":"string","format":"objectId"}}},"schemas":{"FileServerAccount":{"type":"object","description":"A partner login on the environment's file server. The partner authenticates as\n`login` with the password set through `POST /v1/mft/fileserver/accounts/{_id}/password`\n(never returned — `passwordSet` only says whether one exists) and sees the\nCeligo Storage folder `_homeFolderId` as the root of the session. Disabling the\naccount, deleting it, changing its password, or moving its home folder ends any\nsession it has open.","required":["_id","username","login","_homeFolderId","enabled","passwordSet","overCap","createdAt","lastModified"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the account. Not derived from the username, so a rename keeps it."},"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$","description":"Partner's name on this server, unique per environment even while the account\nis disabled. Lowercased on save; a value outside the pattern is rejected with\n422 `FILESERVER_USERNAME_INVALID` and a name another account already holds\nwith 409 `FILESERVER_USERNAME_TAKEN`."},"login":{"type":"string","readOnly":true,"description":"The exact SFTP username the partner types — `username`, `@`, then the server `identifier`."},"_homeFolderId":{"type":"string","format":"objectId","description":"Celigo Storage folder that is the root of every session for this account —\nthe partner can reach nothing outside it. Must be an active folder in the same\nenvironment; otherwise the write answers 422 `FILESERVER_HOME_FOLDER_NOT_FOUND`\nor `FILESERVER_HOME_FOLDER_NOT_FOLDER`. While the account exists, this folder\nand every folder above it refuse `DELETE /v1/storage/items/{_id}` with 403\n`STORAGE_SFTP_HOME_FOLDER_PROTECTED`; renaming or moving them stays allowed.\nMoving an account to another folder ends its open sessions."},"homeFolderPath":{"type":["string","null"],"readOnly":true,"description":"Absolute path of `_homeFolderId` from the storage root, for display — a\nleading `/` then the folder names on the way down, following renames and\nmoves of those folders. Null when the path could not be resolved for this\nresponse — the folder id is still authoritative."},"enabled":{"type":"boolean","description":"When true, the partner can log in. Disabling keeps the account and its password\nbut refuses logins and ends open sessions; re-enabling counts against\n`limits.maxAccounts` and requires a password to be set (422\n`FILESERVER_LAST_CREDENTIAL` otherwise). New accounts start enabled."},"passwordSet":{"type":"boolean","readOnly":true,"description":"When true, the account has a password. The password itself is never returned by\nany route; change or clear it through `POST /v1/mft/fileserver/accounts/{_id}/password`.\nClearing is allowed only while the account is disabled."},"overCap":{"type":"boolean","readOnly":true,"description":"When true, this enabled account ranks above the environment's `limits.maxAccounts`\nafter the cap was lowered, so its logins are refused until enough other accounts\nare disabled or deleted. Computed on every read; always false while the\nenvironment is within its cap."},"lastConnectedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp of the partner's most recent successful login. Null until the first one."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the account was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the last change to the account, including logins updating `lastConnectedAt`."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-full-access-required":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`) --\na scoped API token, or any personal access token, which never counts as full access --\nor the license lacks the add-on / the environment is a sandbox (`FILESERVER_NOT_ENTITLED`).\nA manage, monitor or integration-level user is refused with `access_restricted`: only the\naccount owner and administrators manage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-account-not-found":{"description":"No account with this id in the caller's environment, or the id is not a valid identifier (`FILESERVER_ACCOUNT_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-server-not-enabled":{"description":"The environment's server has never been enabled (`FILESERVER_NOT_ENABLED`); a disabled server is still manageable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/mft/fileserver/accounts/{_id}":{"get":{"operationId":"getFileServerAccount","tags":["File Server"],"summary":"Get a file server account","description":"Returns one partner account with `overCap` computed against the current cap.\nAn id that is unknown, belongs to another environment, or is not a valid\nidentifier all answer the same 404. Requires a full-access API token and a\nserver that has been enabled at least once.","parameters":[{"$ref":"#/components/parameters/AccountId"}],"responses":{"200":{"description":"The account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServerAccount"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-full-access-required"},"404":{"$ref":"#/components/responses/404-account-not-found"},"422":{"$ref":"#/components/responses/422-server-not-enabled"}}}}}}
```

## Update a file server account

> Changes the fields sent and returns the account; fields left out keep their\
> values, and \`{}\` returns the account unchanged without touching \`lastModified\`.\
> Renaming keeps \`\_id\` and updates \`login\`. Moving the home folder is verified\
> against Celigo Storage, ends the account's open sessions (as does disabling\
> it), and shifts the delete protection from the old folder to the new one.\
> Re-enabling counts against \`limits.maxAccounts\` and requires a password to be\
> set. The password cannot be changed here — use\
> \`POST /v1/mft/fileserver/accounts/{\_id}/password\`. Requires a full-access API\
> token, the file server add-on on the license, and a server that has been\
> enabled at least once.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"AccountId":{"name":"_id","in":"path","required":true,"description":"The file server account. A malformed value answers 404 like an unknown one.","schema":{"type":"string","format":"objectId"}}},"schemas":{"FileServerAccountUpdateRequest":{"type":"object","description":"Body of `PUT /v1/mft/fileserver/accounts/{_id}`. Partial: only the fields sent\nchange, and an empty object is a no-op that returns the current account. The\npassword is not part of this body — use the dedicated password route.","properties":{"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$","description":"New name for the partner; `login` changes with it and `_id` does not. Same shape and uniqueness rules as on create."},"_homeFolderId":{"type":"string","format":"objectId","description":"Move the account to another active folder in this environment. Ends the\naccount's open sessions, which were rooted in the old folder, and moves the\nstorage delete protection from the old folder to the new one. The same\nvalidation and error answers apply as on create, including the 503 for a\nmalformed id."},"enabled":{"type":"boolean","description":"Must be a JSON boolean when present (422 `FILESERVER_ENABLED_REQUIRED` for\n`\"false\"` or other non-booleans). False keeps the account but refuses logins\nand ends open sessions. True re-enables it — subject to `limits.maxAccounts`\n(403 `FILESERVER_MAX_ACCOUNTS_EXCEEDED`) and only if a password is set (422\n`FILESERVER_LAST_CREDENTIAL`)."}}},"FileServerAccount":{"type":"object","description":"A partner login on the environment's file server. The partner authenticates as\n`login` with the password set through `POST /v1/mft/fileserver/accounts/{_id}/password`\n(never returned — `passwordSet` only says whether one exists) and sees the\nCeligo Storage folder `_homeFolderId` as the root of the session. Disabling the\naccount, deleting it, changing its password, or moving its home folder ends any\nsession it has open.","required":["_id","username","login","_homeFolderId","enabled","passwordSet","overCap","createdAt","lastModified"],"properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Identifier of the account. Not derived from the username, so a rename keeps it."},"username":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$","description":"Partner's name on this server, unique per environment even while the account\nis disabled. Lowercased on save; a value outside the pattern is rejected with\n422 `FILESERVER_USERNAME_INVALID` and a name another account already holds\nwith 409 `FILESERVER_USERNAME_TAKEN`."},"login":{"type":"string","readOnly":true,"description":"The exact SFTP username the partner types — `username`, `@`, then the server `identifier`."},"_homeFolderId":{"type":"string","format":"objectId","description":"Celigo Storage folder that is the root of every session for this account —\nthe partner can reach nothing outside it. Must be an active folder in the same\nenvironment; otherwise the write answers 422 `FILESERVER_HOME_FOLDER_NOT_FOUND`\nor `FILESERVER_HOME_FOLDER_NOT_FOLDER`. While the account exists, this folder\nand every folder above it refuse `DELETE /v1/storage/items/{_id}` with 403\n`STORAGE_SFTP_HOME_FOLDER_PROTECTED`; renaming or moving them stays allowed.\nMoving an account to another folder ends its open sessions."},"homeFolderPath":{"type":["string","null"],"readOnly":true,"description":"Absolute path of `_homeFolderId` from the storage root, for display — a\nleading `/` then the folder names on the way down, following renames and\nmoves of those folders. Null when the path could not be resolved for this\nresponse — the folder id is still authoritative."},"enabled":{"type":"boolean","description":"When true, the partner can log in. Disabling keeps the account and its password\nbut refuses logins and ends open sessions; re-enabling counts against\n`limits.maxAccounts` and requires a password to be set (422\n`FILESERVER_LAST_CREDENTIAL` otherwise). New accounts start enabled."},"passwordSet":{"type":"boolean","readOnly":true,"description":"When true, the account has a password. The password itself is never returned by\nany route; change or clear it through `POST /v1/mft/fileserver/accounts/{_id}/password`.\nClearing is allowed only while the account is disabled."},"overCap":{"type":"boolean","readOnly":true,"description":"When true, this enabled account ranks above the environment's `limits.maxAccounts`\nafter the cap was lowered, so its logins are refused until enough other accounts\nare disabled or deleted. Computed on every read; always false while the\nenvironment is within its cap."},"lastConnectedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp of the partner's most recent successful login. Null until the first one."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the account was created."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp of the last change to the account, including logins updating `lastConnectedAt`."}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"404-account-not-found":{"description":"No account with this id in the caller's environment, or the id is not a valid identifier (`FILESERVER_ACCOUNT_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/mft/fileserver/accounts/{_id}":{"put":{"operationId":"updateFileServerAccount","tags":["File Server"],"summary":"Update a file server account","description":"Changes the fields sent and returns the account; fields left out keep their\nvalues, and `{}` returns the account unchanged without touching `lastModified`.\nRenaming keeps `_id` and updates `login`. Moving the home folder is verified\nagainst Celigo Storage, ends the account's open sessions (as does disabling\nit), and shifts the delete protection from the old folder to the new one.\nRe-enabling counts against `limits.maxAccounts` and requires a password to be\nset. The password cannot be changed here — use\n`POST /v1/mft/fileserver/accounts/{_id}/password`. Requires a full-access API\ntoken, the file server add-on on the license, and a server that has been\nenabled at least once.","parameters":[{"$ref":"#/components/parameters/AccountId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServerAccountUpdateRequest"}}}},"responses":{"200":{"description":"The account after the change.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServerAccount"}}}},"400":{"description":"The body is not a JSON object sent as `application/json` (`FILESERVER_JSON_BODY_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`);\nthe license lacks the add-on or the environment is a sandbox\n(`FILESERVER_NOT_ENTITLED`); or re-enabling would exceed `limits.maxAccounts`\n(`FILESERVER_MAX_ACCOUNTS_EXCEEDED`). A personal access token is refused the same way as a scoped token, and a manage, monitor or integration-level user gets `access_restricted`: only the owner and administrators manage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/404-account-not-found"},"409":{"description":"The new username belongs to another account in this environment (`FILESERVER_USERNAME_TAKEN`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The server has never been enabled (`FILESERVER_NOT_ENABLED`); `username` is\nmalformed (`FILESERVER_USERNAME_INVALID`); `enabled` is present but not a\nboolean (`FILESERVER_ENABLED_REQUIRED`); re-enabling an account with no\npassword (`FILESERVER_LAST_CREDENTIAL`); or the new home folder does not exist\nor is not an active folder (`FILESERVER_HOME_FOLDER_NOT_FOUND`,\n`FILESERVER_HOME_FOLDER_NOT_FOLDER`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The new home folder could not be verified against Celigo Storage\n(`FILESERVER_STORAGE_UNAVAILABLE`); nothing changed. Retry later. A\n`_homeFolderId` that is not a well-formed 24-character hex id is answered\nthis way too, not with 422 — validate the id before sending.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Delete a file server account

> Permanently removes the account and ends any session it has open. The home\
> folder and its files are untouched, and the folder (with its ancestors) becomes\
> deletable again through \`DELETE /v1/storage/items/{\_id}\`. There is no recycle\
> bin for accounts — recreate one to restore access; a repeated delete answers\
> 404\. Requires a full-access API token, the file server add-on on the license,\
> and a server that has been enabled at least once.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"AccountId":{"name":"_id","in":"path","required":true,"description":"The file server account. A malformed value answers 404 like an unknown one.","schema":{"type":"string","format":"objectId"}}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-full-access-required":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`) --\na scoped API token, or any personal access token, which never counts as full access --\nor the license lacks the add-on / the environment is a sandbox (`FILESERVER_NOT_ENTITLED`).\nA manage, monitor or integration-level user is refused with `access_restricted`: only the\naccount owner and administrators manage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-account-not-found":{"description":"No account with this id in the caller's environment, or the id is not a valid identifier (`FILESERVER_ACCOUNT_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-server-not-enabled":{"description":"The environment's server has never been enabled (`FILESERVER_NOT_ENABLED`); a disabled server is still manageable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/mft/fileserver/accounts/{_id}":{"delete":{"operationId":"deleteFileServerAccount","tags":["File Server"],"summary":"Delete a file server account","description":"Permanently removes the account and ends any session it has open. The home\nfolder and its files are untouched, and the folder (with its ancestors) becomes\ndeletable again through `DELETE /v1/storage/items/{_id}`. There is no recycle\nbin for accounts — recreate one to restore access; a repeated delete answers\n404. Requires a full-access API token, the file server add-on on the license,\nand a server that has been enabled at least once.","parameters":[{"$ref":"#/components/parameters/AccountId"}],"responses":{"204":{"description":"Account deleted."},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-full-access-required"},"404":{"$ref":"#/components/responses/404-account-not-found"},"422":{"$ref":"#/components/responses/422-server-not-enabled"}}}}}}
```

## Set or clear an account's password

> Sets a new password for the partner account, or clears it when \`password\` is\
> \`null\`. Clearing is refused while the account is enabled, because an enabled\
> account must keep a credential — disable it first. Either change ends the\
> account's open sessions and stamps the change for session revalidation. The\
> route answers 204 and never echoes a credential; \`passwordSet\` on the account\
> reflects the result. Requires a full-access API token, the file server add-on on\
> the license, and a server that has been enabled at least once.

```json
{"openapi":"3.2.0","info":{"title":"File Server","version":"1.0.0"},"tags":[{"name":"File Server","description":"Each environment can run a Celigo-hosted SFTP file server. Enabling it claims a permanent `identifier` that becomes part of every partner login (`<username>@<identifier>`) and exposes the shared endpoint and host-key fingerprints partners need to connect. Partner accounts are created under the server, each with its own password and a Celigo Storage folder that becomes the account's root, so files a partner uploads appear in that folder and files placed there are what the partner can download. The account's license must include the file server add-on (`fileserver.enabled`), and every call needs a full-access API token: scoped tokens and personal access tokens are refused, and so are manage, monitor and integration-level users.\n\n## File server and account schemas\n\n{% openapi-schemas spec=\"fileserver\" schemas=\"FileServer,FileServerAccount\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"AccountId":{"name":"_id","in":"path","required":true,"description":"The file server account. A malformed value answers 404 like an unknown one.","schema":{"type":"string","format":"objectId"}}},"schemas":{"FileServerAccountPasswordRequest":{"type":"object","description":"Body of `POST /v1/mft/fileserver/accounts/{_id}/password`. Sets a new password or,\nwith `null`, clears the current one. Either change ends the account's open\nsessions. Nothing is echoed back — the route answers 204.","required":["password"],"properties":{"password":{"type":["string","null"],"format":"password","description":"The new password, meeting the same policy as on create (422\n`FILESERVER_WEAK_PASSWORD` otherwise), or `null` to remove the password —\nallowed only while the account is disabled, because an enabled account must\nkeep a credential (422 `FILESERVER_LAST_CREDENTIAL`). Any other type, or an\nempty body, answers 422 `FILESERVER_WEAK_PASSWORD` with the message \"Password\nmust be a string (or null to clear).\""}}},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-full-access-required":{"description":"The token is not a full-access API token (`FILESERVER_FULL_ACCESS_TOKEN_REQUIRED`) --\na scoped API token, or any personal access token, which never counts as full access --\nor the license lacks the add-on / the environment is a sandbox (`FILESERVER_NOT_ENTITLED`).\nA manage, monitor or integration-level user is refused with `access_restricted`: only the\naccount owner and administrators manage the file server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-account-not-found":{"description":"No account with this id in the caller's environment, or the id is not a valid identifier (`FILESERVER_ACCOUNT_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/mft/fileserver/accounts/{_id}/password":{"post":{"operationId":"setFileServerAccountPassword","tags":["File Server"],"summary":"Set or clear an account's password","description":"Sets a new password for the partner account, or clears it when `password` is\n`null`. Clearing is refused while the account is enabled, because an enabled\naccount must keep a credential — disable it first. Either change ends the\naccount's open sessions and stamps the change for session revalidation. The\nroute answers 204 and never echoes a credential; `passwordSet` on the account\nreflects the result. Requires a full-access API token, the file server add-on on\nthe license, and a server that has been enabled at least once.","parameters":[{"$ref":"#/components/parameters/AccountId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileServerAccountPasswordRequest"}}}},"responses":{"204":{"description":"Password set or cleared. No body."},"400":{"description":"The body is not a JSON object sent as `application/json` (`FILESERVER_JSON_BODY_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-full-access-required"},"404":{"$ref":"#/components/responses/404-account-not-found"},"422":{"description":"The server has never been enabled (`FILESERVER_NOT_ENABLED`); the new password\nbreaks the policy or `password` is neither a string nor `null`\n(`FILESERVER_WEAK_PASSWORD` — the message names the rule); or `null` was sent\nfor an enabled account (`FILESERVER_LAST_CREDENTIAL`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```
