Downloading Files
Section titled “Downloading Files”Download files from remote URLs into a container’s filesystem, track their progress, cancel them, and review past transfers. A download is started by a GET against a directory and writes a single file at the destination; the request answers once the file has been written. Each download carries a UUID tracked in the active list while it runs and in history afterwards; the successful response returns that UUID after completion. The download manager rejects with 429 when more than 10 downloads and extractions are running at once, so retry then.
Start a download from a URL
Section titled “Start a download from a URL”GET /{directory}?download
Section titled “GET /{directory}?download”Download a file from a remote URL into the named directory. The response is synchronous: the server reads the URL and creates the file before answering. The returned download_id (UUID) identifies the completed job in the history; while it runs, its id is available from the active downloads list. The destination filename is taken from the URL when filename is not given. A download may take up to 12 hours (timeout, default and maximum 43200 seconds). The SDK and CLI wait up to 12 hours and 1 minute for the answer unless you set your own timeout; if the client stops waiting, the download keeps running on the server. Follow it in the active downloads list.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
directory | path | string | Yes | Destination directory |
download | query | string | Yes | URL to download from |
filename | query | string | No | Custom filename for downloaded file |
timeout | query | integer | No | Download timeout in seconds. Default and maximum: 43200 (12 hours) |
This endpoint takes no request body.
The SDK’s create also accepts an optional owner, the create-time owner of the new file. With owner, create calls POST /api/v1/files/{directory}?download_from instead, which is where owner is read (see File operations).
Response
Section titled “Response”curl -X GET \ -H "Authorization: Bearer $HOODY_TOKEN" \ "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/Downloads?download=https%3A%2F%2Ffiles.example.com%2Fdata.csv&filename=renamed-data.csv&timeout=600"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.downloads.create('Downloads', { download: 'https://files.example.com/data.csv', filename: 'renamed-data.csv', timeout: 600});The file was downloaded successfully.
{ "download_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "filename": "renamed-data.csv", "message": "File downloaded successfully", "path": "Downloads/renamed-data.csv", "success": true}| Error Code | Title | Description | Resolution |
|---|---|---|---|
The request was refused: a query parameter, a header, or the path is not valid.
{ "code": "INVALID_PATH", "error": "Invalid Path", "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 |
The request is forbidden: the account may not access this path, downloads are not enabled, or the destination is read-only or reserved.
{ "code": "ACCESS_FORBIDDEN", "error": "Access 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 |
Nothing is at this path, or the path is private to the server and answers as if absent.
{ "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 download was cancelled, the destination path is busy or reserved, or a folder on the path changed while the request started. On a local disk filesystem a cancelled download removes the partial file it created; on a mount (FUSE) or a network filesystem the partial file is kept.
{ "code": "FILE_PATH_CHANGED", "download_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "error": "A folder on the path was renamed or replaced while the request started", "filename": "renamed-data.csv", "message": "Path changed", "path": "Downloads/renamed-data.csv", "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_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 |
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 |
Too many downloads and extractions are running (at most 10 at once).
{ "code": "JOB_COUNT_LIMIT", "error": "Too many concurrent downloads/extractions", "success": false}| Error Code | Title | Description | Resolution |
|---|---|---|---|
JOB_COUNT_LIMIT | Too many downloads and extractions | Downloads and extractions share a limit of 10 running at once, and it is reached, so this one was not started | Retry when a running download or extraction has ended |
Track active downloads
Section titled “Track active downloads”GET /api/v1/downloads
Section titled “GET /api/v1/downloads”Get progress for every running download, whether started through the REST surface or the WebDAV-style one. The result is filtered by the caller’s read access on every path the job touches.
This endpoint takes no parameters.
Response
Section titled “Response”curl -X GET \ -H "Authorization: Bearer $HOODY_TOKEN" \ "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/api/v1/downloads"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.downloads.list();{ "downloads": [ { "current_size": 524288, "directory": "Downloads", "expected_size": 1048576, "file_path": "Downloads/renamed-data.csv", "filename": "renamed-data.csv", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "progress_percentage": 50.0, "start_time": 1730000000, "status": "downloading", "url": "https://files.example.com/data.csv" } ]}| Error Code | Title | Description | Resolution |
|---|---|---|---|
| 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 |
GET /{directory}?downloads
Section titled “GET /{directory}?downloads”Get progress for the currently running downloads into a specific directory only.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
directory | path | string | Yes | Directory to list active downloads for |
downloads | query | string | Yes | Sentinel. Pass an empty string to enable the listing |
This endpoint takes no request body.
Response
Section titled “Response”curl -X GET \ -H "Authorization: Bearer $HOODY_TOKEN" \ "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/Downloads?downloads="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.downloads.listByDirectory('Downloads', { downloads: '' });{ "downloads": [ { "current_size": 0, "directory": "Downloads", "expected_size": null, "file_path": "Downloads/renamed-data.csv", "filename": "renamed-data.csv", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "progress_percentage": null, "start_time": 1730000000, "status": "starting", "url": "https://files.example.com/data.csv" } ]}| Error Code | Title | Description | Resolution |
|---|---|---|---|
The request was refused: the path or a parameter is not valid.
{ "code": "INVALID_PATH", "error": "Invalid path", "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 |
The request is forbidden: the account may not access this path, listings are not enabled, or the path is one this server does not write or follow.
{ "code": "ACCESS_FORBIDDEN", "error": "Access 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 |
Nothing is at this path, or the path is private to the server and answers as if absent.
{ "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 |
Cancel a running download
Section titled “Cancel a running download”DELETE /api/v1/downloads/{id}
Section titled “DELETE /api/v1/downloads/{id}”Stop a running download, whether it was started through the REST surface or the WebDAV-style one. The request that started the job then answers 409 with error naming the cancellation, and the history record carries status: "cancelled". On a local disk filesystem the partial file the download created is removed; on a mount (FUSE) or a network filesystem the partial file is kept. Requires write permission on the root.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | The id listed by GET /api/v1/downloads |
This endpoint takes no request body.
Response
Section titled “Response”curl -X DELETE \ -H "Authorization: Bearer $HOODY_TOKEN" \ "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/api/v1/downloads/a1b2c3d4-e5f6-7890-abcd-ef1234567890"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.downloads.cancel('a1b2c3d4-e5f6-7890-abcd-ef1234567890');The download was told to stop.
{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "message": "Download cancellation requested", "success": true}| Error Code | Title | Description | Resolution |
|---|---|---|---|
No download with this id is running.
{ "error": "No running download with this id", "success": false}Download history
Section titled “Download history”GET /?download_history
Section titled “GET /?download_history”Get history of past downloads, whether they completed, failed, or were cancelled. The result is filtered by the caller’s read access on every path the job touches.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
download_history | query | string | Yes | Sentinel. Pass an empty string to enable the listing |
This endpoint takes no request body.
Response
Section titled “Response”curl -X GET \ -H "Authorization: Bearer $HOODY_TOKEN" \ "https://{projectId}-{containerId}-files-1.{server}.containers.hoody.com/?download_history="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.downloads.listHistory({ download_history: '' });{ "history": [ { "directory": "Downloads", "duration": 12, "end_time": 1730000012, "error_message": null, "file_path": "Downloads/renamed-data.csv", "file_size": 1048576, "filename": "renamed-data.csv", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "start_time": 1730000000, "status": "completed", "url": "https://files.example.com/data.csv" } ]}| Error Code | Title | Description | Resolution |
|---|---|---|---|
The request was refused: the path or a parameter is not valid.
{ "code": "INVALID_PATH", "error": "Invalid path", "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 |
The request is forbidden: the account may not access the path, or history is not enabled for this deployment.
{ "code": "ACCESS_FORBIDDEN", "error": "Access 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 |
Nothing is at this path, or the path is private to the server and answers as if absent.
{ "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 |