WebDAV Operations
Section titled “WebDAV Operations”The WebDAV endpoints on this page expose a container’s own storage as a WebDAV share. Any WebDAV-capable tool (Finder, Explorer, or a standalone WebDAV client) can mount the share using the container’s files URL and credentials. The verbs OPTIONS, COPY, MOVE, LOCK, UNLOCK, PROPFIND, and PROPPATCH are served at /{path} on the files service URL, with no /api/files/webdav/ prefix. The share has no credentials of its own: a WebDAV request is authorised exactly like every other request to the container’s files URL.
GET /{path}?type=webdav is a separate, one-off read of a remote WebDAV server using the credentials supplied on the request. Nothing is stored and it configures nothing that the verbs below use.
WebDAV verbs
Section titled “WebDAV verbs”The verbs below are served at /{path} and address the container’s own storage. Mounting the share from a WebDAV client uses the same URL.
OPTIONS /{path}
Section titled “OPTIONS /{path}”Returns the HTTP methods and WebDAV capabilities supported at a given path. WebDAV clients use this for capability discovery before issuing other verbs. The response is answered the same way for any query, including a request that names a remote backend with the type query parameter: this verb connects to nothing, so it is never refused while the service cannot connect to remote backends.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Path to query for capabilities |
Response
Section titled “Response”The response carries no body. Advertised methods and DAV compliance class live in the response headers.
| Header | Type | Description |
|---|---|---|
Allow | string | Comma-separated list of supported HTTP methods. WebDAV-root paths advertise the full WebDAV verb set; /api/v1/* paths advertise the simple HTTP set. |
DAV | string | WebDAV compliance class. Set to 1 on WebDAV-root OPTIONS responses; absent on /api/v1/* OPTIONS responses. |
MS-Author-Via | string | Authoring-protocol hint for Microsoft WebDAV clients. Set to DAV on WebDAV-root OPTIONS responses. |
curl -X OPTIONS 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.getOptions('/data');COPY /{path}
Section titled “COPY /{path}”Copies a file to a new location specified by the Destination header. Send Overwrite: F to refuse a destination that already exists. The source must be a regular file: copying a directory with COPY is refused.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Source file path |
Destination | header | string | Yes | Destination URL for the copy |
Overwrite | header | string | No | T (default) replaces an existing destination; F refuses one, answering 412 and leaving it untouched. A request refused for another reason is answered for that reason instead, whatever this header says: copying a resource onto itself is 403, and so is a source that is not a regular file. |
Response
Section titled “Response”The destination did not exist and was created.
An existing destination was replaced.
The response has no body.
{ "code": "INVALID_PARAMETER", "error": "Invalid Overwrite header (expected T or F)", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETER | Invalid request | A query parameter, a header or the request body is not one this operation accepts, or a copy or a move names a destination inside the directory it copies or moves; the message names what was refused | Correct the request |
INVALID_PATH | Invalid path | The request path is not valid: it does not decode, it has a . or .. component, it is outside the configured path prefix, or it is over the filesystem’s limits (a name over 255 bytes, a path over 4095 bytes) | Send a path of plain components under the served directory, within the filesystem’s limits |
Copy not allowed: the feature is disabled, the caller cannot write the destination, the source is not a regular file (a directory, a symbolic link to one, a pipe, a socket, a device), the source and destination are the same resource, the destination is the served directory itself, or the path leads through a symbolic link this deployment does not follow.
{ "code": "ACCESS_FORBIDDEN", "error": "Forbidden", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ACCESS_FORBIDDEN | Access forbidden | The request is refused: the account may not access this path or where it leads (a symlink to a path the account may not access), the deployment has not enabled this operation (uploads, deletes, touches, permission and owner changes, renames, extraction, thumbnails), or the path is one this server does not write or follow (the served directory itself, a symlink when symlinks are not allowed, a read-only or immutable file, a pipe, a socket or a device, which has no content to read or copy) | Use an account with access to the path, or ask the deployment’s administrator to enable the operation; a pipe, a socket or a device is never read |
The source does not exist or is a path this deployment will not read (one reached through a symbolic link, where symbolic links are not followed), or the destination is not a path this server will write to.
{ "code": "RESOURCE_NOT_FOUND", "error": "Not Found", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Not found | Nothing is at this path, or the path is private to the server and answers as if absent | Check the path |
The copy cannot be carried out as asked: a destination that exists and cannot be replaced by a file, a destination path that cannot be created, a destination nothing is listening on (a pipe with no reader), or a file at either end that another process holds a lease on; or the path holds service storage, or a folder on it changed while the request started. The body carries one of the error codes below.
{ "code": "PATH_CONFLICT", "error": "Cannot replace the destination: Is a directory (os error 21)", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONTAINS_SERVICE_STORAGE | Path holds service storage | The path, or the destination, is a directory that holds the service’s own storage (the directory of attached mounts, the directory of service credentials, or a directory above private storage), or an entry directly in the directory of attached mounts. It can be read, but not created, deleted, renamed, replaced or have its permissions or owner changed | Act on the entries inside the directory instead, or choose another destination |
FILE_PATH_CHANGED | Path changed | A folder on the path was renamed or replaced while the request started, or the source changed while a copy between a mount and local storage ran. Nothing was changed. | Retry |
MOUNT_PATH_RESERVED | Path reserved for a mount | A mount that is being created or updated holds this path, so nothing was changed. | Retry when the mount has been created or updated, or choose another path |
PATH_CONFLICT | Path conflict | The filesystem refused the paths of this request, so nothing was changed: the destination is of the wrong kind or not empty, a component of the path is a file where a directory has to be, the source and the destination are the same file or on different filesystems, a directory would move into itself, or a file the request needs cannot be opened now (nothing is listening on it, or another program holds a lease on it) | Choose another destination, or retry when the file is free |
The destination exists and Overwrite is F. The destination itself is untouched; missing parent directories named by the destination may already have been created.
{ "code": "OVERWRITE_REFUSED", "error": "Destination exists and Overwrite is F", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
OVERWRITE_REFUSED | Destination exists | The destination exists and the request’s Overwrite header is F, so nothing was changed | Send Overwrite: T to replace the destination, or choose another one |
{ "code": "FILE_PATH_BUSY", "error": "This path is in use by another operation", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
FILE_PATH_BUSY | Path in use | This path is in use by another operation; retry. Another operation is using this path, a folder above it, or something inside it, so nothing was changed. Changes to the content or attributes of one file do not conflict with each other: two uploads, appends, truncations, permission or owner changes, or touches of the same file each run, and the last one wins. | Retry when the other operation has finished |
The copy failed for a reason that is not about the request.
curl -X COPY 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/report.txt' \ -H 'Authorization: Bearer <token>' \ -H 'Destination: https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/report-backup.txt' \ -H 'Overwrite: T'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.copy('/data/report.txt', { Destination: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/report-backup.txt', Overwrite: 'T',});MOVE /{path}
Section titled “MOVE /{path}”Moves a file or directory to a new location specified by the Destination header. Send Overwrite: F to refuse a destination that already exists. Requires both upload and delete permissions.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Source file or directory path |
Destination | header | string | Yes | Destination URL for the move |
Overwrite | header | string | No | T (default) replaces an existing destination; F refuses one, answering 412 and leaving it untouched. A request refused for another reason is answered for that reason instead, whatever this header says: moving a resource onto itself is 403. |
Response
Section titled “Response”The destination did not exist and was created.
An existing destination was replaced.
The response has no body.
{ "code": "INVALID_PARAMETER", "error": "Invalid Overwrite header (expected T or F)", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETER | Invalid request | A query parameter, a header or the request body is not one this operation accepts, or a copy or a move names a destination inside the directory it copies or moves; the message names what was refused | Correct the request |
INVALID_PATH | Invalid path | The request path is not valid: it does not decode, it has a . or .. component, it is outside the configured path prefix, or it is over the filesystem’s limits (a name over 255 bytes, a path over 4095 bytes) | Send a path of plain components under the served directory, within the filesystem’s limits |
Move not allowed: it requires upload and delete permissions, the caller cannot write the destination, the caller cannot remove the source, the source and destination are the same resource, the source or the destination is the served directory itself, or the path leads through a symbolic link this deployment does not follow.
{ "code": "ACCESS_FORBIDDEN", "error": "Forbidden", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ACCESS_FORBIDDEN | Access forbidden | The request is refused: the account may not access this path or where it leads (a symlink to a path the account may not access), the deployment has not enabled this operation (uploads, deletes, touches, permission and owner changes, renames, extraction, thumbnails), or the path is one this server does not write or follow (the served directory itself, a symlink when symlinks are not allowed, a read-only or immutable file, a pipe, a socket or a device, which has no content to read or copy) | Use an account with access to the path, or ask the deployment’s administrator to enable the operation; a pipe, a socket or a device is never read |
The source does not exist or is a path this deployment will not read (one reached through a symbolic link, where symbolic links are not followed), or the destination is not a path this server will write to.
{ "code": "RESOURCE_NOT_FOUND", "error": "Not Found", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Not found | Nothing is at this path, or the path is private to the server and answers as if absent | Check the path |
The move cannot be carried out as asked: a destination that exists and cannot be replaced (a non-empty directory, or a directory where a file would go), a destination inside the source, a destination on another filesystem, or a destination path that cannot be created; or the path holds service storage, or a folder on it changed while the request started. The body carries one of the error codes below.
{ "code": "PATH_CONFLICT", "error": "Cannot replace the destination: Directory not empty (os error 39)", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONTAINS_SERVICE_STORAGE | Path holds service storage | The path, or the destination, is a directory that holds the service’s own storage (the directory of attached mounts, the directory of service credentials, or a directory above private storage), or an entry directly in the directory of attached mounts. It can be read, but not created, deleted, renamed, replaced or have its permissions or owner changed | Act on the entries inside the directory instead, or choose another destination |
FILE_PATH_CHANGED | Path changed | A folder on the path was renamed or replaced while the request started, or the source changed while a copy between a mount and local storage ran. Nothing was changed. | Retry |
MOUNT_PATH_RESERVED | Path reserved for a mount | A mount that is being created or updated holds this path, so nothing was changed. | Retry when the mount has been created or updated, or choose another path |
PATH_CONFLICT | Path conflict | The filesystem refused the paths of this request, so nothing was changed: the destination is of the wrong kind or not empty, a component of the path is a file where a directory has to be, the source and the destination are the same file or on different filesystems, a directory would move into itself, or a file the request needs cannot be opened now (nothing is listening on it, or another program holds a lease on it) | Choose another destination, or retry when the file is free |
The destination exists and Overwrite is F. The destination itself is untouched; missing parent directories named by the destination may already have been created.
{ "code": "OVERWRITE_REFUSED", "error": "Destination exists and Overwrite is F", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
OVERWRITE_REFUSED | Destination exists | The destination exists and the request’s Overwrite header is F, so nothing was changed | Send Overwrite: T to replace the destination, or choose another one |
{ "code": "FILE_PATH_BUSY", "error": "This path is in use by another operation", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
FILE_PATH_BUSY | Path in use | This path is in use by another operation; retry. Another operation is using this path, a folder above it, or something inside it, so nothing was changed. Changes to the content or attributes of one file do not conflict with each other: two uploads, appends, truncations, permission or owner changes, or touches of the same file each run, and the last one wins. | Retry when the other operation has finished |
The move failed for a reason that is not about the request.
Overwrite: F cannot be honoured on this filesystem, which offers no way to create the destination without possibly replacing something. No resource was replaced; directories on the way to the destination may have been created.
curl -X MOVE 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/old-name.txt' \ -H 'Authorization: Bearer <token>' \ -H 'Destination: https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/new-name.txt' \ -H 'Overwrite: T'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.move('/data/old-name.txt', { Destination: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/new-name.txt', Overwrite: 'T',});LOCK /{path}
Section titled “LOCK /{path}”Returns a lock token for WebDAV client compatibility. This is a compatibility stub: locks are not enforced server-side. The Depth header is accepted but ignored.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Path of the file to lock |
Depth | header | string | No | Depth of the lock: 0 (resource only) or infinity (recursive). Declared for WebDAV compatibility; the handler does not read this header. |
Request body
Section titled “Request body”The request may carry an XML body describing the requested lock. The server does not read the body and answers the same with or without one.
<?xml version="1.0" encoding="utf-8"?><D:lockinfo xmlns:D="DAV:"> <D:locktype><D:write/></D:locktype> <D:lockscope><D:exclusive/></D:lockscope> <D:depth>infinity</D:depth> <D:owner> <D:href>mailto:user@example.com</D:href> </D:owner> <D:timeout>Second-1800</D:timeout></D:lockinfo>Response
Section titled “Response”A lock token is issued. The response body is XML carrying only the lock token and the lock root, and the lock-token response header carries the same token wrapped in angle brackets (lock-token: <opaquelocktoken:b7f3c2a1-5d4e-4f6a-9b8c-0d1e2f3a4b5c>).
<?xml version="1.0" encoding="utf-8"?><D:prop xmlns:D="DAV:"><D:lockdiscovery><D:activelock><D:locktoken><D:href>opaquelocktoken:b7f3c2a1-5d4e-4f6a-9b8c-0d1e2f3a4b5c</D:href></D:locktoken><D:lockroot><D:href>/data/report.txt</D:href></D:lockroot></D:activelock></D:lockdiscovery></D:prop>| Header | Type | Description |
|---|---|---|
lock-token | string | Opaque lock token issued by the server. Returned on LOCK responses for WebDAV client compatibility. |
{ "code": "INVALID_PATH", "error": "Invalid request", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PATH | Invalid path | The request path is not valid: it does not decode, it has a . or .. component, it is outside the configured path prefix, or it is over the filesystem’s limits (a name over 255 bytes, a path over 4095 bytes) | Send a path of plain components under the served directory, within the filesystem’s limits |
{ "code": "ACCESS_FORBIDDEN", "error": "Forbidden", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ACCESS_FORBIDDEN | Access forbidden | The request is refused: the account may not access this path or where it leads (a symlink to a path the account may not access), the deployment has not enabled this operation (uploads, deletes, touches, permission and owner changes, renames, extraction, thumbnails), or the path is one this server does not write or follow (the served directory itself, a symlink when symlinks are not allowed, a read-only or immutable file, a pipe, a socket or a device, which has no content to read or copy) | Use an account with access to the path, or ask the deployment’s administrator to enable the operation; a pipe, a socket or a device is never read |
{ "code": "RESOURCE_NOT_FOUND", "error": "File not found", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Not found | Nothing is at this path, or the path is private to the server and answers as if absent | Check the path |
curl -X LOCK 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/report.txt' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/xml' \ -d '<?xml version="1.0" encoding="utf-8"?><D:lockinfo xmlns:D="DAV:"> <D:locktype><D:write/></D:locktype> <D:lockscope><D:exclusive/></D:lockscope> <D:depth>infinity</D:depth> <D:owner> <D:href>mailto:user@example.com</D:href> </D:owner> <D:timeout>Second-1800</D:timeout></D:lockinfo>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.lock('/data/report.txt');UNLOCK /{path}
Section titled “UNLOCK /{path}”Releases a lock token. This is a no-op compatibility stub: the server reads no headers on this verb, so no lock token is required.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Path of the file to unlock |
Response
Section titled “Response”Lock released (no-op).
The response has no body.
{ "code": "INVALID_PATH", "error": "Invalid request", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PATH | Invalid path | The request path is not valid: it does not decode, it has a . or .. component, it is outside the configured path prefix, or it is over the filesystem’s limits (a name over 255 bytes, a path over 4095 bytes) | Send a path of plain components under the served directory, within the filesystem’s limits |
{ "code": "ACCESS_FORBIDDEN", "error": "Forbidden", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ACCESS_FORBIDDEN | Access forbidden | The request is refused: the account may not access this path or where it leads (a symlink to a path the account may not access), the deployment has not enabled this operation (uploads, deletes, touches, permission and owner changes, renames, extraction, thumbnails), or the path is one this server does not write or follow (the served directory itself, a symlink when symlinks are not allowed, a read-only or immutable file, a pipe, a socket or a device, which has no content to read or copy) | Use an account with access to the path, or ask the deployment’s administrator to enable the operation; a pipe, a socket or a device is never read |
{ "code": "RESOURCE_NOT_FOUND", "error": "Resource not found", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Not found | Nothing is at this path, or the path is private to the server and answers as if absent | Check the path |
curl -X UNLOCK 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/report.txt' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.unlock('/data/report.txt');PROPFIND /{path}
Section titled “PROPFIND /{path}”Retrieves properties (metadata) for a file or directory. Returns a 207 Multi-Status response. Supports the Depth header (default 1) to control recursion into directories.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Path of the file or directory |
Depth | header | string | No | Depth of property retrieval: 0 (resource only) or 1 (immediate children). Defaults to 1. Any other value, including infinity, is refused with 400 (“Invalid depth: only 0 and 1 are allowed.”). |
Request body
Section titled “Request body”The request may carry an XML body listing the properties to retrieve. The server does not read the body and answers the same with or without one.
<?xml version="1.0" encoding="utf-8"?><D:propfind xmlns:D="DAV:"> <D:prop> <D:getcontentlength/> <D:getlastmodified/> <D:resourcetype/> <D:getetag/> </D:prop></D:propfind>Response
Section titled “Response”Multi-Status response with resource properties in WebDAV XML format.
<?xml version="1.0" encoding="utf-8"?><D:multistatus xmlns:D="DAV:"> <D:response> <D:href>/data/report.txt</D:href> <D:propstat> <D:prop> <D:getcontentlength>1024</D:getcontentlength> <D:getlastmodified>Mon, 01 Jan 2024 12:00:00 GMT</D:getlastmodified> <D:resourcetype/> <D:getcontenttype>text/plain</D:getcontenttype> <D:getetag>"abc123"</D:getetag> </D:prop> <D:status>HTTP/1.1 200 OK</D:status> </D:propstat> </D:response></D:multistatus>{ "code": "INVALID_PATH", "error": "Invalid request", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETER | Invalid request | A query parameter, a header or the request body is not one this operation accepts, or a copy or a move names a destination inside the directory it copies or moves; the message names what was refused | Correct the request |
INVALID_PATH | Invalid path | The request path is not valid: it does not decode, it has a . or .. component, it is outside the configured path prefix, or it is over the filesystem’s limits (a name over 255 bytes, a path over 4095 bytes) | Send a path of plain components under the served directory, within the filesystem’s limits |
{ "code": "ACCESS_FORBIDDEN", "error": "Forbidden", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ACCESS_FORBIDDEN | Access forbidden | The request is refused: the account may not access this path or where it leads (a symlink to a path the account may not access), the deployment has not enabled this operation (uploads, deletes, touches, permission and owner changes, renames, extraction, thumbnails), or the path is one this server does not write or follow (the served directory itself, a symlink when symlinks are not allowed, a read-only or immutable file, a pipe, a socket or a device, which has no content to read or copy) | Use an account with access to the path, or ask the deployment’s administrator to enable the operation; a pipe, a socket or a device is never read |
{ "code": "RESOURCE_NOT_FOUND", "error": "Resource not found", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Not found | Nothing is at this path, or the path is private to the server and answers as if absent | Check the path |
curl -X PROPFIND 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data' \ -H 'Authorization: Bearer <token>' \ -H 'Depth: 1' \ -H 'Content-Type: application/xml' \ -d '<?xml version="1.0" encoding="utf-8"?><D:propfind xmlns:D="DAV:"> <D:prop> <D:getcontentlength/> <D:getlastmodified/> <D:resourcetype/> <D:getetag/> </D:prop></D:propfind>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.getProperties('/data');PROPPATCH /{path}
Section titled “PROPPATCH /{path}”Modifies properties on a file or directory. Returns a 207 Multi-Status response. This is a compatibility response for WebDAV clients: the server stores nothing, and every property in the multistatus is reported as HTTP/1.1 403 Forbidden.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Path of the file or directory |
Request body
Section titled “Request body”The request may carry an XML body with set and remove instructions. The server does not read the body and answers the same with or without one.
<?xml version="1.0" encoding="utf-8"?><D:propertyupdate xmlns:D="DAV:"> <D:set> <D:prop> <D:displayname>New Name</D:displayname> </D:prop> </D:set></D:propertyupdate>Response
Section titled “Response”Multi-Status response confirming property changes. Every property in the response is reported as HTTP/1.1 403 Forbidden.
<?xml version="1.0" encoding="utf-8" ?><D:multistatus xmlns:D="DAV:"><D:response><D:href>/data/report.txt</D:href><D:propstat><D:prop></D:prop><D:status>HTTP/1.1 403 Forbidden</D:status></D:propstat></D:response></D:multistatus>{ "code": "INVALID_PATH", "error": "Invalid request", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PATH | Invalid path | The request path is not valid: it does not decode, it has a . or .. component, it is outside the configured path prefix, or it is over the filesystem’s limits (a name over 255 bytes, a path over 4095 bytes) | Send a path of plain components under the served directory, within the filesystem’s limits |
{ "code": "ACCESS_FORBIDDEN", "error": "Forbidden", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ACCESS_FORBIDDEN | Access forbidden | The request is refused: the account may not access this path or where it leads (a symlink to a path the account may not access), the deployment has not enabled this operation (uploads, deletes, touches, permission and owner changes, renames, extraction, thumbnails), or the path is one this server does not write or follow (the served directory itself, a symlink when symlinks are not allowed, a read-only or immutable file, a pipe, a socket or a device, which has no content to read or copy) | Use an account with access to the path, or ask the deployment’s administrator to enable the operation; a pipe, a socket or a device is never read |
{ "code": "RESOURCE_NOT_FOUND", "error": "File not found", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Not found | Nothing is at this path, or the path is private to the server and answers as if absent | Check the path |
curl -X PROPPATCH 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/data/report.txt' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/xml' \ -d '<?xml version="1.0" encoding="utf-8"?><D:propertyupdate xmlns:D="DAV:"> <D:set> <D:prop> <D:displayname>New Name</D:displayname> </D:prop> </D:set></D:propertyupdate>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.updateProperties('/data/report.txt');Remote WebDAV access
Section titled “Remote WebDAV access”The endpoint in this section reads from a remote WebDAV server (Nextcloud, ownCloud, etc.) using the credentials supplied on the request. Nothing is stored and nothing is configured for the verbs above.
GET /{path}?type=webdav
Section titled “GET /{path}?type=webdav”Reads a file or directory listing from a remote WebDAV server. The server, user, and pass query parameters identify the remote server and the account to authenticate as. The webdav_path parameter (default /) names the path on the remote server. The service connects to https://{server}{webdav_path} and reads path relative to webdav_path.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
path | path | string | Yes | Path on the remote WebDAV server, relative to webdav_path |
type | query | string | Yes | Must be webdav |
server | query | string | Yes | Bare host or host:port of the remote WebDAV server. Do not include a scheme (the service connects to https://{server}{webdav_path}; a value such as https://... fails as an invalid port) |
user | query | string | No | Username for the remote server |
pass | query | string | No | Password for the remote server, base64-encoded |
webdav_path | query | string | No | WebDAV endpoint path on the remote server. Defaults to /. |
Response
Section titled “Response”File content or directory listing from the remote WebDAV server.
{ "code": "INVALID_PARAMETER", "error": "A query parameter, a header or the request body is not one this operation accepts", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_PARAMETER | Invalid request | A query parameter, a header or the request body is not one this operation accepts, or a copy or a move names a destination inside the directory it copies or moves; the message names what was refused | Correct the request |
INVALID_PATH | Invalid path | The request path is not valid: it does not decode, it has a . or .. component, it is outside the configured path prefix, or it is over the filesystem’s limits (a name over 255 bytes, a path over 4095 bytes) | Send a path of plain components under the served directory, within the filesystem’s limits |
{ "code": "ACCESS_FORBIDDEN", "error": "Forbidden", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
ACCESS_FORBIDDEN | Access forbidden | The request is refused: the account may not access this path or where it leads (a symlink to a path the account may not access), the deployment has not enabled this operation (uploads, deletes, touches, permission and owner changes, renames, extraction, thumbnails), or the path is one this server does not write or follow (the served directory itself, a symlink when symlinks are not allowed, a read-only or immutable file, a pipe, a socket or a device, which has no content to read or copy) | Use an account with access to the path, or ask the deployment’s administrator to enable the operation; a pipe, a socket or a device is never read |
{ "code": "RESOURCE_NOT_FOUND", "error": "Not found", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Not found | Nothing is at this path, or the path is private to the server and answers as if absent | Check the path |
{ "error": "Uploads from before a service restart are still pending on this backend; retry later", "success": false}| Header | Type | Description |
|---|---|---|
Retry-After | integer | Seconds to wait before retrying. Sent when uploads from before a service restart are still pending on this backend. |
{ "code": "MOUNT_STORAGE_UNAVAILABLE", "error": "Mounts unavailable", "success": false}| Header | Type | Description |
|---|---|---|
Retry-After | integer | Seconds to wait before retrying. Sent with MOUNT_STORAGE_UNAVAILABLE. |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
MOUNT_STORAGE_UNAVAILABLE | Mounts unavailable | Mounts are unavailable until the server’s storage check passes; retry later. The server’s storage check did not pass (for example, the mount directory and the directory of the backends’ own mounts overlap), so no mount or backend connection can start, and backend file operations are refused. The refusal comes before the mount or backend is looked up, so an unknown id is answered 503, not 404, until the check passes. Existing mounts show the error, and the mount list still answers. Other files are served as usual. Nothing was changed. | Retry after the time given in Retry-After |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
BACKEND_UNREACHABLE | Remote backend unreachable | A read of a remote (through ?backend=, or one-off through ?type=) could not reach it: the remote did not answer within 30 seconds (504), or it refused the connection, its TLS or the credentials (502). Nothing was read. | Check the connection details and credentials (for a saved backend, GET /api/v1/backends/{id}/test, and PUT /api/v1/backends/{id} to update them), or retry once the remote is reachable |
| Error Code | Title | Description | Resolution |
|---|---|---|---|
BACKEND_UNREACHABLE | Remote backend unreachable | A read of a remote (through ?backend=, or one-off through ?type=) could not reach it: the remote did not answer within 30 seconds (504), or it refused the connection, its TLS or the credentials (502). Nothing was read. | Check the connection details and credentials (for a saved backend, GET /api/v1/backends/{id}/test, and PUT /api/v1/backends/{id} to update them), or retry once the remote is reachable |
curl 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/remote/file.txt?type=webdav&server=nextcloud.example.com&user=admin&pass=c2VjcmV0&webdav_path=/' \ -H 'Authorization: Bearer <token>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.files.webdav.get('/remote/file.txt', { type: 'webdav', server: 'nextcloud.example.com', user: 'admin', pass: 'c2VjcmV0', webdav_path: '/',});