Storage shares
Section titled “Storage shares”Create, manage, and mount shared storage between containers. A storage share exposes a directory from a source container to either a specific target container (1:1 share) or every container in a target project (project-wide share). Source containers control what to share, while target mount paths are determined automatically by the server.
Incoming shares
Section titled “Incoming shares”Incoming shares describe storage that the calling container or account is configured to receive. They include direct shares and project-level shares, with deduplication and filtering applied.
Get incoming shares
Section titled “Get incoming shares”GET /api/v1/containers/{id}/storage/incoming
Get all shares targeting this container (both direct shares and project-level shares). Shows what storage this container is configured to receive. Includes deduplication (direct shares take priority over project shares) and filters out self-shares and expired shares.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Container ID |
curl -X GET "https://api.hoody.com/api/v1/containers/{id}/storage/incoming" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.storage.shares.listIncomingByContainer(id);{ "statusCode": 200, "message": "Incoming shares retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439011", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/etc/app/config", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null, "target_type": "container", "mode": "readonly", "enabled": true, "status": "active", "status_message": null, "expires_at": null, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-15T10:30:00.000Z", "updated_at": "2025-01-15T10:30:00.000Z" }, { "id": "507f1f77bcf86cd799439077", "source_container_id": "507f1f77bcf86cd799439088", "source_path": "/opt/shared-libs", "target_project_id": "507f1f77bcf86cd799439055", "target_container_id": null, "target_type": "project", "mode": "readonly", "enabled": true, "status": "active", "status_message": null, "expires_at": null, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-01T00:00:00.000Z", "updated_at": "2025-01-01T00:00:00.000Z" } ]}The response wraps the share list in a data array. Each entry represents an incoming share, omitting the creator’s organizational fields (alias, label, description) and the expiry_notified flag.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the share |
source_container_id | string | Container ID that owns the source directory |
source_path | string | Absolute path in the source container |
target_container_id | string | Target container ID for 1:1 container share (mutually exclusive with target_project_id) |
target_project_id | string | Target project ID for project-wide share (mutually exclusive with target_container_id) |
target_type | string | container for 1:1 share, project for project-wide share |
mode | string | readonly or readwrite |
enabled | boolean | Whether the share is enabled |
status | string | active or failed |
status_message | string | Status message (error details if failed) |
expires_at | number | Unix timestamp when share auto-deletes, or null to never expire |
created_by | string | User ID who created the share |
created_at | string | ISO 8601 timestamp |
updated_at | string | ISO 8601 timestamp |
{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Container not found"}Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}Toggle incoming share mount
Section titled “Toggle incoming share mount”PATCH /api/v1/containers/{id}/storage/incoming/{shareId}/mount
Enable or disable mounting of an incoming share. Allows the target container owner to accept or reject incoming shares. Both the creator’s enabled flag and the receiver’s mount flag must be true for the share to be mounted.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Target container ID (receiver container) |
shareId | path | string | Yes | Share ID to toggle |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
mount | boolean | Yes | Set to true to accept and mount the share, false to reject or unmount it |
curl -X PATCH "https://api.hoody.com/api/v1/containers/{id}/storage/incoming/{shareId}/mount" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"mount": true}'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.storage.shares.mountIncoming(id, shareId);This operation is reachable through two facade methods that set the mount body field for you, so the caller never passes it:
client.api.storage.shares.mountIncoming(id, shareId)sendsmount: trueto accept and mount the shareclient.api.storage.shares.unmountIncoming(id, shareId)sendsmount: falseto reject or unmount the share
{ "statusCode": 200, "message": "Share enabled for mounting successfully", "data": { "share": { "id": "507f1f77bcf86cd799439011", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/home/app/shared-data", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null, "target_type": "container", "mode": "readonly", "alias": "prod-data-share", "label": "production", "description": "Shared application data directory", "enabled": true, "status": "active", "status_message": null, "expires_at": 1735689599, "expiry_notified": false, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-15T10:30:00.000Z", "updated_at": "2025-01-15T14:45:00.000Z" }, "override": { "id": "507f1f77bcf86cd799439099", "share_id": "507f1f77bcf86cd799439011", "container_id": "507f1f77bcf86cd799439033", "mount": true, "created_at": "2025-01-15T14:45:00.000Z", "updated_at": "2025-01-15T14:45:00.000Z" } }}The data object contains the updated share and a per-receiver override record that records the mount preference for this container.
| Field | Type | Description |
|---|---|---|
data.share | object | The updated share record |
data.override | object | The per-container mount override |
data.override.id | string | Override record ID |
data.override.share_id | string | Share ID |
data.override.container_id | string | Container ID (receiver) |
data.override.mount | boolean | Mount preference for this container |
data.override.created_at | string | ISO 8601 timestamp |
data.override.updated_at | string | ISO 8601 timestamp |
{ "statusCode": 400, "error": "VALIDATION_ERROR", "message": "This share does not target the specified container", "data": { "share_id": "507f1f77bcf86cd799439011", "container_id": "507f1f77bcf86cd799439099", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null }}{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Container not found"}Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}Global incoming and outgoing lists
Section titled “Global incoming and outgoing lists”The endpoints in this section return shares aggregated across every container you own. Use them for account-wide dashboards, auditing, or cross-project queries.
Get all incoming shares
Section titled “Get all incoming shares”GET /api/v1/storage/incoming
Get all shares targeting your containers across all projects. Shows what storage you are receiving from others.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
realm_id | query | string | No | Filter by realm ID. Alternative to using realm subdomain in URL. |
curl -X GET "https://api.hoody.com/api/v1/storage/incoming" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
for await (const item of client.api.storage.shares.listIncomingIterator()) { console.log(item);}{ "statusCode": 200, "message": "All incoming shares retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439011", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/home/app/shared-data", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null, "target_type": "container", "mode": "readonly", "enabled": true, "status": "active", "status_message": null, "expires_at": 1735689599, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-15T10:30:00.000Z", "updated_at": "2025-01-15T10:30:00.000Z" } ]}{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "INSUFFICIENT_PERMISSIONS", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}List all your storage shares
Section titled “List all your storage shares”GET /api/v1/storage/shares
List all storage shares you have created across all your containers. This returns shares you are providing to others.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
realm_id | query | string | No | Filter by realm ID. Alternative to using realm subdomain in URL. |
curl -X GET "https://api.hoody.com/api/v1/storage/shares" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
for await (const item of client.api.storage.shares.listIterator()) { console.log(item);}{ "statusCode": 200, "message": "All storage shares retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439011", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/home/app/shared-data", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null, "target_type": "container", "mode": "readonly", "alias": "prod-data-share", "label": "production", "description": "Shared application data directory", "enabled": true, "status": "active", "status_message": null, "expires_at": 1735689599, "expiry_notified": false, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-15T10:30:00.000Z", "updated_at": "2025-01-15T10:30:00.000Z" } ]}{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "INSUFFICIENT_PERMISSIONS", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}Per-container share management
Section titled “Per-container share management”These endpoints are scoped to a specific source container and let you list, retrieve, create, update, and delete the shares originating from that container.
List storage shares
Section titled “List storage shares”GET /api/v1/containers/{id}/storage/shares
List all shares from this source container. Use query filters to narrow results.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Source container ID |
target_type | query | string | No | Filter by target type (container or project) |
label | query | string | No | Filter by label |
status | query | string | No | Filter by status (active or failed) |
enabled | query | boolean | No | Filter by enabled status |
include_expired | query | boolean | No | Include expired shares (default: false) |
realm_id | query | string | No | Filter by realm ID. Alternative to using realm subdomain in URL. |
curl -X GET "https://api.hoody.com/api/v1/containers/{id}/storage/shares?target_type=container&status=active" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
for await (const item of client.api.storage.shares.listByContainerIterator(id, { target_type: 'container', status: 'active' })) { console.log(item);}{ "statusCode": 200, "message": "Storage shares retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439011", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/home/app/shared-data", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null, "target_type": "container", "mode": "readonly", "alias": "prod-data-share", "label": "production", "description": "Shared application data directory", "enabled": true, "status": "active", "status_message": null, "expires_at": 1735689599, "expiry_notified": false, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-15T10:30:00.000Z", "updated_at": "2025-01-15T10:30:00.000Z" }, { "id": "507f1f77bcf86cd799439066", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/var/log/app", "target_project_id": "507f1f77bcf86cd799439055", "target_container_id": null, "target_type": "project", "mode": "readwrite", "alias": null, "label": "logs", "description": "Application logs shared with project", "enabled": true, "status": "active", "status_message": null, "expires_at": null, "expiry_notified": false, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-10T08:00:00.000Z", "updated_at": "2025-01-10T08:00:00.000Z" } ]}{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}Get storage share
Section titled “Get storage share”GET /api/v1/containers/{id}/storage/shares/{shareId}
Retrieve details of a specific storage share.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Source container ID |
shareId | path | string | Yes | Share ID |
curl -X GET "https://api.hoody.com/api/v1/containers/{id}/storage/shares/{shareId}" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.storage.shares.get(id, shareId);{ "statusCode": 200, "message": "Storage share retrieved successfully", "data": { "id": "507f1f77bcf86cd799439011", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/home/app/shared-data", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null, "target_type": "container", "mode": "readonly", "alias": "prod-data-share", "label": "production", "description": "Shared application data directory", "enabled": true, "status": "active", "status_message": null, "expires_at": 1735689599, "expiry_notified": false, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-15T10:30:00.000Z", "updated_at": "2025-01-15T10:30:00.000Z" }}{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Resource not found"}Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}Create storage share
Section titled “Create storage share”POST /api/v1/containers/{id}/storage/shares
Share a directory from the source container with a target container or an entire project. The share is mounted automatically on the target(s).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Source container ID |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
source_path | string | Yes | Absolute path in the source container. Path is normalized, blocks system paths (/proc/*, /sys/*, /dev/*, /boot/*, /run/*, /var/run/*), rejects path traversal and null bytes |
target_container_id | string | No | Target container ID for a 1:1 share. Mutually exclusive with target_project_id |
target_project_id | string | No | Target project ID for a project-wide share that auto-mounts on every container. Mutually exclusive with target_container_id |
mode | string | Yes | readonly or readwrite |
alias | string | No | Optional human-friendly alias (lowercase alphanumeric with hyphens or underscores, 3 to 63 characters) |
label | string | No | Optional label for grouping shares (3 to 63 characters) |
description | string | No | Optional description (up to 1000 characters) |
enabled | boolean | No | Whether to enable the share (default: true) |
expires_at | number | No | Unix timestamp (seconds) when the share should expire |
curl -X POST "https://api.hoody.com/api/v1/containers/{id}/storage/shares" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "source_path": "/home/shared/documents", "target_container_id": "507f1f77bcf86cd799439033", "mode": "readonly", "alias": "shared-docs", "label": "documentation", "description": "Read-only access to team documentation" }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.storage.shares.create(id, { source_path: '/home/shared/documents', target_container_id: '507f1f77bcf86cd799439033', mode: 'readonly', alias: 'shared-docs', label: 'documentation', description: 'Read-only access to team documentation'});{ "statusCode": 201, "message": "Storage share created successfully", "data": { "id": "507f1f77bcf86cd799439020", "source_container_id": "507f1f77bcf86cd799439012", "source_path": "/data/shared", "target_project_id": "507f1f77bcf86cd799439010", "target_container_id": null, "target_type": "project", "mode": "readonly", "alias": "team-shared-data", "label": "production", "description": "Shared project files for team collaboration", "enabled": true, "status": "active", "status_message": null, "expires_at": 1738252800, "expiry_notified": false, "created_by": "507f1f77bcf86cd799439001", "created_at": "2025-01-29T15:00:00.000Z", "updated_at": "2025-01-29T15:00:00.000Z" }}{ "statusCode": 400, "error": "Bad Request", "message": "Validation failed"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input parameters | One or more request parameters failed validation | Check the error message for specific field requirements and correct your input |
INVALID_PATH | Invalid Path | The provided source or destination path is invalid. Kernel paths like /proc, /sys, /dev are not allowed. | Provide a valid, non-kernel path for the storage share. |
SELF_SHARE | Cannot Share to Self | A container cannot share a directory with itself. | Choose a different target container or project. |
{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Container not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CONTAINER_NOT_FOUND | Container not found | The requested container does not exist or you do not have permission to access it. | Verify the container ID is correct and that you have access to the project it belongs to. |
RESOURCE_NOT_FOUND | Resource not found | The requested resource does not exist or has been deleted | Verify the resource ID and ensure it exists |
{ "statusCode": 409, "error": "Conflict", "message": "Storage share already exists."}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SHARE_ALREADY_EXISTS | Share Already Exists | A share with the same source path and target already exists. | Update the existing share or choose a different target. |
Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}{ "statusCode": 429, "error": "Too Many Requests", "message": "Rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RATE_LIMIT_EXCEEDED | Rate limit exceeded | You have exceeded the rate limit for this endpoint | Wait before making additional requests, or upgrade your plan for higher limits |
Update storage share
Section titled “Update storage share”PATCH /api/v1/containers/{id}/storage/shares/{shareId}
Update share properties such as mode, alias, label, description, enabled state, or expiration. Only the fields you provide are updated. To clear an optional string field, send it as null.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Source container ID |
shareId | path | string | Yes | Share ID |
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
mode | string | No | readonly or readwrite |
alias | string | No | Alias (3 to 63 characters, lowercase alphanumeric with hyphens or underscores). Send null to remove |
label | string | No | Label (3 to 63 characters). Send null to remove |
description | string | No | Description (up to 1000 characters). Send null to remove |
enabled | boolean | No | Enable or disable the share |
expires_at | number | No | Unix timestamp (seconds) when the share expires. Send null to never expire |
curl -X PATCH "https://api.hoody.com/api/v1/containers/{id}/storage/shares/{shareId}" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "mode": "readwrite", "alias": "prod-data-rw", "description": "Updated to read-write access", "expires_at": null }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.storage.shares.update(id, shareId, { mode: 'readwrite', alias: 'prod-data-rw', description: 'Updated to read-write access', expires_at: null});{ "statusCode": 200, "message": "Storage share updated successfully", "data": { "id": "507f1f77bcf86cd799439011", "source_container_id": "507f1f77bcf86cd799439022", "source_path": "/home/app/shared-data", "target_container_id": "507f1f77bcf86cd799439033", "target_project_id": null, "target_type": "container", "mode": "readwrite", "alias": "prod-data-rw", "label": "production", "description": "Updated to read-write access", "enabled": true, "status": "active", "status_message": null, "expires_at": null, "expiry_notified": false, "created_by": "507f1f77bcf86cd799439044", "created_at": "2025-01-15T10:30:00.000Z", "updated_at": "2025-01-15T14:45:00.000Z" }}{ "statusCode": 400, "error": "Bad Request", "message": "Validation failed"}{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Resource not found"}Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}Global share deletion
Section titled “Global share deletion”Delete storage share
Section titled “Delete storage share”DELETE /api/v1/storage/shares/{shareId}
Remove a storage share by ID. Share IDs are globally unique, so the source container ID is not required. The share is unmounted from the target container(s) automatically.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
shareId | path | string | Yes | Share ID (globally unique, no container ID needed) |
curl -X DELETE "https://api.hoody.com/api/v1/storage/shares/{shareId}" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://api.hoody.com', token: process.env.HOODY_TOKEN });
await client.api.storage.shares.delete(shareId);{ "statusCode": 200, "message": "Storage share deleted successfully"}{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request | Include a valid JWT token in the Authorization header as Bearer <token> |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid | Obtain a new token by logging in again or using a valid auth token |
TOKEN_EXPIRED | Authentication token expired | The provided authentication token has expired | Obtain a new token by logging in again or refreshing your session |
AUTH_TOKEN_LINEAGE_REVOKED | Parent auth token no longer valid | The auth token was created by another auth token, and that token (or one above it) has been revoked, disabled or has expired, so this one no longer authenticates | Create a new auth token from a valid one, or sign in again |
INVALID_CREDENTIALS | Invalid credentials | The provided username/email or password is incorrect | Verify your credentials and try again, or use the password reset feature |
EMAIL_NOT_VERIFIED | Email not verified | Returned by the login endpoint when the password is correct but the account’s email address has not been verified yet. It is only returned after the password has been checked. The response carries data.email so the client can offer a ‘resend verification email’ action without asking again. | Complete email verification by clicking the link sent to your inbox, or call /auth/resend-verification to receive a new link, or complete a password reset which also implicitly verifies the email. |
INVALID_OTP_CODE | Invalid OTP code | The provided 2FA code is incorrect or has expired | Generate a new code from your authenticator app and try again |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action | Ask the resource owner to grant you access |
ACCOUNT_BANNED | Account banned | Your account has been banned and cannot access this resource | Contact support for information about your account status |
{ "statusCode": 404, "error": "Not Found", "message": "Resource not found"}Validation Error: the request does not match the operation schema, or its JSON body cannot be parsed. message names each failing check, and data, when present, lists them one entry per failing field.
{ "statusCode": 422, "error": "Validation Error", "message": "Validation failed: /name must NOT have fewer than 3 characters"}