Agent: Skills
Section titled “Agent: Skills”The agent exposes endpoints for searching, installing, scaffolding, reading, writing, renaming, deleting, toggling, and trusting skills. skills.list reports the installed skills for the requesting working-directory and config-dir, with their enabled and trust state. The skill-hub routes search, preview, install, and cache skills fetched from a remote hub. The skill-import routes scan well-known locations on disk (for claude or codex) and apply discovered skills to the local store. Skill identity is the daemon’s root_dir + rel_dir pair (with friendly aliases root and rel), except where the route is name-scoped. Realm selectors are handled per operation: skills.list ignores a client-supplied X-Hoody-Realm (or ?realm=) and uses the gateway’s own realm; the hub, import, and per-skill routes accept or reject a realm selector as each operation’s parameters describe.
List and read skills
Section titled “List and read skills”GET /api/v1/agent/skills
Section titled “GET /api/v1/agent/skills”Lists the installed skills (identity = root_dir + rel_dir) for the requesting working-directory and config-dir, with their enabled and trust state. The result is realm-scoped to the gateway’s OWN realm: skills.list ignores any client-supplied X-Hoody-Realm (or ?realm=) and uses the gateway’s own realm.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
page | query | integer | No | 1-based page number for pagination. |
limit | query | integer | No | Maximum items per page (0 = no pagination). |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against. |
X-Hoody-Realm | header | string | No | Ignored by this route; skills.list uses the gateway’s own realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm. Ignored by this route; skills.list uses the gateway’s own realm. |
SDK example
Section titled “SDK example”The endpoint answers one page per request. The SDK’s skills.listIterator is an async iterator helper that sends no request until it is iterated.
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
for await (const item of client.agent.skills.listIterator({ page: 1, limit: 20 })) { // item is a single skill row from the daemon}curl -X GET 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills?page=1&limit=20' \ -H "Authorization: Bearer <token>" \ -H "X-Hoody-Cwd: /home/user/project"{ "items": [ { "description": "Search the web and return the top results.", "dir": "/home/user/.hoody/skills/web-search", "name": "web-search", "realm": "global", "rel_dir": "web-search", "root_dir": "/home/user/.hoody/skills", "root_id": "user" } ], "meta": { "total": 1, "page": 1, "limit": 20 }}The response is the kit list envelope: a page of items plus pagination metadata. items carries every SKILL.md across the scope’s roots (shadowed and unparsable ones included); each element is the daemon’s skill entry, tagged with the realm it was listed in. meta.total is the full count across all pages; meta.page and meta.limit are present only when limit > 0.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
GET /api/v1/agent/skills/source
Section titled “GET /api/v1/agent/skills/source”Reads a skill’s source body. Identify the skill by the daemon keys root_dir + rel_dir in the query string (the friendly aliases ?root= and ?rel= are also accepted and translated server-side). The reply carries content, gen, and path. gen is an opaque integer token over the content and the mtime, exact in a JSON number (at most 2^53-1); pass it back as base_gen on skills.setSource to detect conflicts. A skill that does not exist is 404 not_found "skill not found", and so is a realm this login does not serve.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
root_dir | query | string | No | Skill root directory (identity; alias: root). |
rel_dir | query | string | No | Skill relative directory (identity; alias: rel). |
root | query | string | No | Friendly alias of root_dir (translated to root_dir server-side). |
rel | query | string | No | Friendly alias of rel_dir (translated to rel_dir server-side). |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” (not tied to a realm) or a 24-hex realm id. On a session route it names the realm the session is looked up in (a session in another realm, or in a realm this login does not serve, is 404 not_found). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const skill = await client.agent.skills.getSource({ root_dir: '/home/user/.hoody/skills', rel_dir: 'web-search' });curl -X GET 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/source?root_dir=%2Fhome%2Fuser%2F.hoody%2Fskills&rel_dir=web-search' \ -H "Authorization: Bearer <token>" \ -H "X-Hoody-Cwd: /home/user/project"{ "content": "---\nname: web-search\ndescription: Search the web and return the top results.\n---\n\n# Web search\n\nSearch the web for the given query and summarise the top results.\n", "gen": 1234567890, "path": "/home/user/.hoody/skills/web-search/SKILL.md"}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "skill not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The skill does not exist, or the realm is not one this login serves. | Check the skill name and the realm. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. The agent’s log (GET /logs) records a “store failure” entry with the store, the failure kind, and a path-free cause. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
Create, modify, and delete custom skills
Section titled “Create, modify, and delete custom skills”POST /api/v1/agent/skills
Section titled “POST /api/v1/agent/skills”Scaffolds a new local skill. name is the only input: the daemon creates the skill directory in the writable skills root and writes a fixed SKILL.md template with frontmatter carrying that name and a literal description: TODO placeholder, plus a stub body. The route does not accept the skill’s description or content. A new skill is unusable until you replace them, because the description is what the model reads to decide whether to load the skill. This is step one of two: create the skill here, then take the returned root_dir + rel_dir, read the scaffold with skills.getSource, and write the real frontmatter and body with skills.setSource. The create fails when a skill of that name (or that target directory) already exists in ANY root, and when the scope has no writable skills root. The route is realm-scoped to the gateway’s OWN realm: skills.list ignores client selectors, and skills.create accepts or rejects X-Hoody-Realm (or ?realm=) as the parameters below describe.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope: the .hoody project layer / record cwd / tool+workflow cwd. Required by routes that resolve a cwd. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override selecting which on-disk .hoody install a stateless read/write resolves against. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” (not tied to a realm) or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Skill name (also the SKILL.md directory stem, and the frontmatter name). The description is NOT settable here. Must not collide with an existing skill in any root. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.create({ name: 'web-search' });curl -X POST 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "name": "web-search" }'{ "root_dir": "/home/user/.hoody/skills", "rel_dir": "web-search", "path": "/home/user/.hoody/skills/web-search/SKILL.md"}root_dir, rel_dir, and path identify the new SKILL.md for skills.setSource.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
PUT /api/v1/agent/skills/source
Section titled “PUT /api/v1/agent/skills/source”Overwrites a skill’s source body. Identify the skill by the daemon keys root_dir + rel_dir in the body (aliases root and rel accepted). The new body text is content (alias source). Pass the read’s gen as base_gen for conflict detection: if SKILL.md changed since that read (by content or by mtime), the save is refused with 409 revision_conflict, nothing is written, and details.current_gen carries the file’s current gen. Without base_gen (or with 0) the save is unconditional. The reply’s gen is the new file’s gen.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
root_dir | string | Yes | Skill root directory (identity; alias: root). |
rel_dir | string | Yes | Skill relative directory (identity; alias: rel). |
content | string | Yes | New SKILL.md body (alias: source). |
base_gen | integer | No | The gen returned by skills.getSource (or a previous skills.setSource). A save whose SKILL.md changed since is refused with 409 revision_conflict and writes nothing. Omit or pass 0 to save unconditionally. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.setSource({ root_dir: '/home/user/.hoody/skills', rel_dir: 'web-search', content: '---\nname: web-search\ndescription: Search the web.\n---\n\n# Web search\n', base_gen: 1234567890});curl -X PUT 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/source' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "root_dir": "/home/user/.hoody/skills", "rel_dir": "web-search", "content": "---\nname: web-search\ndescription: Search the web.\n---\n\n# Web search\n", "base_gen": 1234567890 }'{ "gen": 1234567891}The body is the daemon’s reply, passed through verbatim. gen is the new file’s generation token.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "skill not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The skill does not exist, or the realm is not one this login serves. | Check the skill name and the realm. |
{ "code": "revision_conflict", "message": "conflict: SKILL.md changed since it was opened (reload and retry)", "details": { "current_gen": 1234567892 }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
revision_conflict | Skill source conflict | SKILL.md changed since base_gen was read (by content or by mtime), so nothing was written. details.current_gen carries the file’s current gen. | Re-read skills.getSource, re-apply the change, and retry with its gen (or with details.current_gen after merging). |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
POST /api/v1/agent/skills/delete
Section titled “POST /api/v1/agent/skills/delete”Deletes a skill. Identify the skill by the daemon keys root_dir + rel_dir in the body (aliases root and rel accepted). A skill that does not exist is 404 not_found "skill not found", and so is a realm this login does not serve.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
root_dir | string | Yes | Skill root directory (identity; alias: root). |
rel_dir | string | Yes | Skill relative directory (identity; alias: rel). |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.delete({ root_dir: '/home/user/.hoody/skills', rel_dir: 'web-search' });curl -X POST 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/delete' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "root_dir": "/home/user/.hoody/skills", "rel_dir": "web-search" }'{}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "skill not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The skill does not exist, or the realm is not one this login serves. | Check the skill name and the realm. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
POST /api/v1/agent/skills/rename
Section titled “POST /api/v1/agent/skills/rename”Renames a skill. Identify the skill by the daemon keys root_dir + rel_dir in the body (aliases root and rel accepted) and pass the new name as new_name. A skill that does not exist is 404 not_found "skill not found", and so is a realm this login does not serve.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
root_dir | string | Yes | Skill root directory (identity; alias: root). |
rel_dir | string | Yes | Skill relative directory (identity; alias: rel). |
new_name | string | Yes | New skill directory name. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.rename({ root_dir: '/home/user/.hoody/skills', rel_dir: 'web-search', new_name: 'web-search-v2'});curl -X POST 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/rename' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "root_dir": "/home/user/.hoody/skills", "rel_dir": "web-search", "new_name": "web-search-v2" }'{}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "skill not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The skill does not exist, or the realm is not one this login serves. | Check the skill name and the realm. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
POST /api/v1/agent/skills/toggle
Section titled “POST /api/v1/agent/skills/toggle”Toggles a skill’s enabled state. This route is name-scoped: it gates the effective skill name in the runtime registry. Identify the skill by name in the body, and pass disabled (boolean) for the new state. Disabling a skill that does not exist is 404 not_found "skill not found", and so is a realm this login does not serve.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Effective skill name (not root/rel; toggle is name-scoped). |
disabled | boolean | No | true to disable the skill, false to enable it. |
SDK example
Section titled “SDK example”The SDK reaches this operation through two methods:
client.agent.skills.enable(...): setsdisabled: false.client.agent.skills.disable(...): setsdisabled: true.
import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.disable({ name: 'web-search' });curl -X POST 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/toggle' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "name": "web-search", "disabled": true }'{}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "skill not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The skill to disable does not exist, or the realm is not one this login serves. | Check the skill name and the realm. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
POST /api/v1/agent/skills/trust
Section titled “POST /api/v1/agent/skills/trust”Sets a skill’s trust state. Identify the skill by the daemon keys root_dir + rel_dir in the body (aliases root and rel accepted); the trust flag is trusted (boolean). Trust gates arbitrary code execution. A skill that does not exist or has no trust record is 404 not_found "skill not found", and so is a realm this login does not serve.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
root_dir | string | Yes | Skill root directory (identity; alias: root). |
rel_dir | string | Yes | Skill relative directory (identity; alias: rel). |
trusted | boolean | Yes | true to grant execution trust, false to revoke. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.trust({ root_dir: '/home/user/.hoody/skills', rel_dir: 'web-search', trusted: true });curl -X POST 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/trust' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "root_dir": "/home/user/.hoody/skills", "rel_dir": "web-search", "trusted": true }'{}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "skill not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The skill does not exist or has no trust record, or the realm is not one this login serves. | Check the skill name and the realm. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
Skill hub
Section titled “Skill hub”GET /api/v1/agent/skills/hub/search
Section titled “GET /api/v1/agent/skills/hub/search”Searches the remote skill hub (the only hub provider is skills.sh). Pass the search text as ?query= (required; an empty query is refused). Each result’s ref carries the provider, source, and skill_id that skills.hub.preview takes.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
query | query | string | Yes | Search text. Required and non-empty. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const results = await client.agent.skills.hub.search({ query: 'web search' });curl -X GET 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/hub/search?query=web+search' \ -H "Authorization: Bearer <token>" \ -H "X-Hoody-Cwd: /home/user/project"{ "results": [ { "ref": { "provider": "skills.sh", "source": "owner/repo", "skill_id": "web-search" }, "name": "web-search", "description": "Search the web and return the top results." } ]}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters (an empty query is rejected here). | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
GET /api/v1/agent/skills/hub/preview
Section titled “GET /api/v1/agent/skills/hub/preview”Fetches a preview of a hub skill before install (the only hub provider is skills.sh). Name the skill with ?provider=, ?source=, and ?skill_id= (all three required), copied from a skills.hub.search result’s ref. The reply’s package_digest is what skills.hub.install installs.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
provider | query | string | Yes | Hub provider of the skill (a search result’s ref.provider). |
source | query | string | Yes | Source of the skill on the hub, such as owner/repo (a search result’s ref.source). |
skill_id | query | string | Yes | Hub skill id (a search result’s ref.skill_id). |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const preview = await client.agent.skills.hub.preview({ provider: 'skills.sh', source: 'owner/repo', skill_id: 'web-search'});curl -X GET 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/hub/preview?provider=skills.sh&source=owner%2Frepo&skill_id=web-search' \ -H "Authorization: Bearer <token>" \ -H "X-Hoody-Cwd: /home/user/project"{ "package_digest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "name": "web-search", "version": "1.0.0", "description": "Search the web and return the top results.", "files": ["SKILL.md"]}The body is the daemon’s reply, passed through verbatim. Use the returned package_digest as the install input.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters (any of provider, source, skill_id missing is rejected here; a provider other than skills.sh returns unknown skill hub provider). | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested hub skill does not exist. | Verify the provider, source, and skill_id. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
POST /api/v1/agent/skills/hub/install
Section titled “POST /api/v1/agent/skills/hub/install”Installs a skill from the hub. The install takes the package the cached preview refers to, identified by package_digest (returned by skills.hub.preview). A digest no longer in the cache is refused; preview again.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
package_digest | string | Yes | The package_digest returned by skills.hub.preview. The install takes exactly that cached package, so preview first; a digest no longer in the cache is refused (preview again). |
overwrite | boolean | No | Replace a skill already installed under the same name. Without it (default false) such an install is refused. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.hub.install({ package_digest: 'sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', overwrite: false});curl -X POST 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/hub/install' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "package_digest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "overwrite": false }'{}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The cached package_digest is no longer in the hub cache (or the target skill directory could not be created). | Re-preview the skill and retry with the new digest. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
GET /api/v1/agent/skills/hub/cache
Section titled “GET /api/v1/agent/skills/hub/cache”Returns the skill-hub fetch cache statistics.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const stats = await client.agent.skills.hub.getCacheStats();curl -X GET 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/hub/cache' \ -H "Authorization: Bearer <token>" \ -H "X-Hoody-Cwd: /home/user/project"{ "entries": 12, "bytes": 1048576, "pins": 3}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
DELETE /api/v1/agent/skills/hub/cache
Section titled “DELETE /api/v1/agent/skills/hub/cache”Clears the skill-hub fetch cache, including pins.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.hub.clearCache();curl -X DELETE 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/hub/cache' \ -H "Authorization: Bearer <token>" \ -H "X-Hoody-Cwd: /home/user/project"{}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters. | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
store_failed | Agent store failed | One of the agent’s own stores could not be read, parsed, locked, or written. The message names the store; a damaged store is left as it was rather than rewritten. | Retry once; if it persists, repair the named store inside the container. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
Skill imports
Section titled “Skill imports”GET /api/v1/agent/skills/import/scan
Section titled “GET /api/v1/agent/skills/import/scan”Scans well-known locations for importable skills. ?source= picks which tool’s skills to scan: claude or codex (required).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
source | query | string | Yes | Which tool’s skills to scan. One of claude or codex. |
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). Rejected (400) on routes with no container dimension. |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const found = await client.agent.skills.scan({ source: 'claude' });curl -X GET 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/import/scan?source=claude' \ -H "Authorization: Bearer <token>" \ -H "X-Hoody-Cwd: /home/user/project"{ "entries": [ { "source": "claude", "rel_dir": "web-search", "name": "web-search", "description": "Search the web and return the top results." } ]}The body is the daemon’s reply, passed through verbatim. Pass the chosen entries to skills.import to import them.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters (a source other than claude or codex is rejected here). | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |
POST /api/v1/agent/skills/import/apply
Section titled “POST /api/v1/agent/skills/import/apply”Imports a discovered skill into the local store. Pass the import source and the selected entries returned by the import scan; the daemon re-derives every source location from the source and the selected entry, so a client-supplied filesystem path is not an input. The top-level source is required; an entry’s own source is optional (it defaults to the request source, and must equal it when set).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Hoody-Cwd | header | string | No | Per-request working-directory scope. |
X-Hoody-Config-Dir | header | string | No | Per-request --config-dir override. |
X-Hoody-Container | header | string | No | Per-request bound remote container (omitted = local). |
X-Hoody-Realm | header | string | No | Per-request realm selector: “global” or a 24-hex realm id (also accepted as ?realm=). Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
realm | query | string | No | In-query alias of X-Hoody-Realm (read only when the header is absent): “global” or a 24-hex realm id. Rejected (400 realm_scope_unsupported) on active-only / no-realm routes, and for any other realm on an agent pinned to one realm. |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
source | string | Yes | The import source the entries were scanned from. One of claude or codex. |
items | array | Yes | The selected scan entries to import. At least one entry is required. |
overwrite | boolean | No | Replace an already-installed skill at the same destination instead of refusing that entry. Defaults to false. |
Each items entry carries:
| Name | Type | Required | Description |
|---|---|---|---|
source | string | No | The entry’s import source. Optional; when set it must equal the request source, or that entry is refused. |
rel_dir | string | Yes | The entry’s directory, as the import scan reported it (a single directory name, not a path). |
SDK example
Section titled “SDK example”import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.agent.skills.import({ source: 'claude', items: [{ rel_dir: 'web-search' }], overwrite: false});curl -X POST 'https://{projectId}-{containerId}-agent-1.{server}.containers.hoody.com/api/v1/agent/skills/import/apply' \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -H "X-Hoody-Cwd: /home/user/project" \ -d '{ "source": "claude", "items": [{ "rel_dir": "web-search" }], "overwrite": false }'{ "imported": [ { "rel_dir": "web-search" } ]}The body is the daemon’s reply, passed through verbatim.
{ "code": "bad_request", "message": "invalid request"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
bad_request | Bad request | The request was malformed or carried invalid parameters (an empty items, or an entry whose source differs from the request source, is rejected here). | Correct the request body or query parameters. |
realm_scope_unsupported | Realm scope unsupported | A per-request scope header this route does not take (a realm header on a route without that dimension, or a different realm on an agent pinned to one realm). | Omit the header, or name the realm the agent is pinned to. |
invalid_realm | Invalid realm selector | The realm selector is malformed (not ""/“global”/a 24-hex id). | Pass a valid realm selector. |
{ "code": "forbidden", "message": "request must arrive through the Hoody proxy"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
forbidden | Forbidden (Source IP Guard) | The request did not come through the program’s URL. Hoody Kit programs are reached through their URLs only. | Call the agent through its URL, for example with hoody agent …. |
{ "code": "not_found", "message": "resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
not_found | Not found | The requested resource does not exist. | Verify the path and identifier. |
{ "code": "payload_too_large", "message": "request body exceeds the configured size limit"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
payload_too_large | Payload too large | The request body exceeds the configured size cap (MaxBodyBytes). The gateway rejects an oversized body at the edge before the handler reads it. | Reduce the request body below the configured limit (default 8 MiB); split a large payload into smaller requests. |
{ "code": "rate_limited", "message": "request rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
rate_limited | Too many requests | The per-client request rate limit was exceeded; the gateway throttled the request before dispatch. | Honor the Retry-After header and retry; reduce the request rate. |
{ "code": "internal_error", "message": "internal server error"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
internal_error | Internal error | An unexpected error occurred while handling the request. | Retry; if persistent, inspect the daemon logs. |
{ "code": "service_unavailable", "message": "service unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
service_unavailable | Service unavailable | The daemon could not service the request (too busy, or a per-client stream concurrency cap was hit). | Honor Retry-After and retry. |