Skip to content
Hoody.com

These endpoints manage three capabilities of the exec service: Next.js-style URL routing for user-authored scripts, on-the-fly generation of user OpenAPI specs, and lifecycle management of imported SDKs. They all operate on the container’s mounted scripts root (under /hoody/storage/hoody-exec/scripts). Path fields are reported exactly as each endpoint’s spec describes: route discovery reports each entry’s path in a field named file (scripts-root-relative, with baseDir echoed from the request), route resolution reports scriptPath and baseDir (both scripts-root-relative), user-openapi list returns path relative to the scanned directory (for example, path: "api/users.ts" beside a directory that ends in default/1), and SDK responses return path/directory as default/<sdkId>, any middleware the import’s middleware supplied as default/<sdkId>/pre.js and post.js, and marker as the metadata file path default/<sdkId>/.sdk.json (a path, never the inline /* hoody-sdk:... */ comment string). Use the routing endpoints to map a URL to its handler and inspect what a script directory can serve; use the user-openapi endpoints to generate, merge, and validate schemas; use the SDK endpoints to import an external OpenAPI spec, inspect its generated middleware, and delete it.

Discover all available routes in the script directory, classified as static, dynamic [param], catch-all [...slug], or optional catch-all [[...path]]. Each entry reports its pattern (relative to baseDir), file path (scripts-root-relative), type, and extracted parameter names, and route: the URL path on which the runtime serves that file for the request’s scope (hostname and execId, from the body or else the Host header, as in route/resolve), or null when no route could be verified: no URL reaches it in that scope, or (rarely, a URL may then still reach it) routes overlap past the checking budget. Set includeMetadata to true to include size and modification time per file.

This endpoint takes no parameters.

FieldTypeRequiredDefaultDescription
baseDirstringNo""Scripts-root-relative directory to scan. Pass a per-instance scope like "default/1" to limit discovery to one subdomain/execId pair; empty scans the full root.
includeMetadatabooleanNofalseWhen true, each route entry includes file size and modification time.
hostnamestringNo—Hostname scope route is computed for. When omitted, it is taken from the Host header’s subdomain, then defaults to default; lowercased.
execIdstringNo—Exec ID scope route is computed for. When omitted, it is taken from the Host header; lowercased.
Terminal window
curl -X POST https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/route/discover \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"baseDir":"default/1","includeMetadata":true}'
{
"baseDir": "default/1",
"hostname": "default",
"execId": "1",
"count": 2,
"routes": [
{
"pattern": "api/users/[id]",
"route": "/api/users/[id]",
"file": "default/1/api/users/[id].ts",
"type": "dynamic",
"parameters": ["id"],
"metadata": { "size": 512, "modified": "2026-01-01T00:00:00.000Z" }
},
{
"pattern": "api/users",
"route": "/api/users",
"file": "default/1/api/users.ts",
"type": "static",
"parameters": [],
"metadata": { "size": 348, "modified": "2026-01-01T00:00:00.000Z" }
}
]
}
FieldTypeDescription
baseDirstringThe directory scanned.
hostnamestringThe hostname scope route was computed for (body hostname, else the Host header’s subdomain, else default; lowercased).
execIdstring | nullThe execId scope route was computed for (body execId, else the Host header’s; lowercased), or null for none.
countintegerNumber of routes returned.
routes[].patternstringThe route pattern relative to baseDir (the scripts root when none is given), [param] segments as written. Not a URL unless baseDir is the deployment’s own directory: route is.
routes[].routestring | nullThe URL path on which the runtime serves this file for the request’s scope, [param] segments as written: fill each with your value, a catch-all with one or more segments ([[...x]]: none or more). A value or catch-all segment count that a narrower route also matches goes to that route, which is listed with its own route. null when no route could be verified, which is mostly when no URL reaches the file in this scope (other routes take every URL it matches, it is outside an exclusive tree, it is a .js beside its .ts twin, a search directory of the scope is a symlink, and similar), or when the routes overlap so much that checking this one would take more than a fixed amount of work. A file that only the hostname or root index fallback serves is null too. When unsure the answer is null, never a path that serves another file.
routes[].filestringThe script file, relative to the scripts directory.
routes[].typestringOne of static, dynamic, catchall, optional-catchall.
routes[].parametersstring[]Parameter names of the dynamic segments.
routes[].metadataobjectWith includeMetadata: the file’s size (size, integer) and modification time (modified, date-time).

Resolve a URL path to the script that handles it, using Next.js-style matching in priority order: static, dynamic, catch-all, optional catch-all. The lookup is scoped by hostname and execId and falls back through {hostname}/{execId}/, then {hostname}/, then {execId}/, then root. When nothing matches, suggestions lists up to 3 routes of the same scope near the path (leading folders dropped, or a folder of the deployment added), each with the script it runs.

This endpoint takes no parameters.

FieldTypeRequiredDefaultDescription
pathstringYes—URL path to resolve, e.g. /api/users/42. A non-string is refused with a 400.
hostnamestringNo—Hostname namespace: letters, digits, dots, hyphens and underscores, not . or .. and not a reserved directory name (node_modules, .git, .hoody-cache, _sdk, _hoody). Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header, then defaults to default. Any other value is refused with a 400.
execIdstringNo—Exec ID namespace: 1 to 64 letters or digits, with no dots, hyphens or underscores (a hostname cannot carry them). Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
Terminal window
curl -X POST https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/route/resolve \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"path":"/api/users/42","hostname":"default","execId":"1"}'
{
"matched": true,
"path": "/api/users/42",
"scriptPath": "default/1/api/users/[id].ts",
"routePattern": "api/users/[id]",
"parameters": {
"id": "42"
},
"type": "dynamic",
"baseDir": "default/1"
}

When no route matches, the response carries matched: false and lists the directories the resolver tried:

{
"matched": false,
"path": "/api/missing",
"hostname": "default",
"execId": "1",
"triedDirectories": [
"default/1",
"default",
"1",
""
],
"suggestions": []
}

Batch-test multiple URL paths against the routing system in a single request. Each path is resolved with the same priority and scoping rules as route resolve. The response carries per-path results plus aggregate matched and notMatched counts. The first 20 unmatched results carry suggestions.

This endpoint takes no parameters.

FieldTypeRequiredDefaultDescription
pathsarray of stringsYes—URL paths to resolve, e.g. ["/api/users", "/api/users/42"]. A non-string entry is refused with a 400.
hostnamestringNo—Hostname namespace: letters, digits, dots, hyphens and underscores, not . or .. and not a reserved directory name (node_modules, .git, .hoody-cache, _sdk, _hoody). Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header, then defaults to default. Any other value is refused with a 400.
execIdstringNo—Exec ID namespace: 1 to 64 letters or digits, with no dots, hyphens or underscores (a hostname cannot carry them). Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
Terminal window
curl -X POST https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/route/test \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"paths":["/api/users/42","/api/posts","/api/missing"],"hostname":"default","execId":"1"}'
{
"tested": 3,
"matched": 2,
"notMatched": 1,
"results": [
{
"path": "/api/users/42",
"matched": true,
"scriptPath": "default/1/api/users/[id].ts",
"parameters": { "id": "42" }
},
{
"path": "/api/posts",
"matched": true,
"scriptPath": "default/1/api/posts.ts",
"parameters": {}
},
{
"path": "/api/missing",
"matched": false,
"suggestions": []
}
]
}

These endpoints generate, merge, validate, and serve OpenAPI specifications derived from your script directory.

List available scripts with their extracted schema information, including route path, schema format (when present), and any dynamic path parameters. The path field of each entry is relative to the scanned directory.

NameInTypeRequiredDescription
directoryquerystringNoScript directory to list (absolute or relative to scripts-dir). Default: scripts.
dirquerystringNoAlias of directory. Ignored when directory is provided.
subdomainquerystringNoLimit scan to scripts under this subdomain. Falls back to the Host header when omitted.
execIdquerystringNoLimit scan to scripts under this execId. Falls back to the Host header when omitted.
Terminal window
curl "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/user-openapi/list?directory=scripts&subdomain=default&execId=1" \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"success": true,
"data": {
"directory": "scripts",
"totalScripts": 2,
"withSchemas": 1,
"scripts": [
{
"path": "default/1/api/users/[id].ts",
"routePath": "api/users/[id]",
"hasSchema": true,
"schemaFormat": "zod",
"pathParameters": ["id"]
},
{
"path": "default/1/api/posts.ts",
"routePath": "api/posts",
"hasSchema": false
}
]
}
}

Serve the schema file (Zod or JSON Schema) associated with a single script directly. Provide either file or path as the scripts-root-relative path.

NameInTypeRequiredDescription
filequerystringNoAbsolute or scripts-dir-relative path to the target script (e.g. default/api/users/[id].ts). Either file or path must be provided.
pathquerystringNoAlias of file. Either file or path must be provided.
subdomainquerystringNoResolve file under this subdomain. Falls back to the Host header when omitted.
execIdquerystringNoResolve file under this execId. Falls back to the Host header when omitted.
Terminal window
curl "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/user-openapi/schema?file=default/1/api/users/[id].ts" \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"format": "zod",
"file": "default/1/api/users/[id].ts",
"schema": {
"type": "object",
"properties": {
"id": { "type": "string" }
},
"required": ["id"]
}
}

Generate and serve an OpenAPI specification on-the-fly from the script directory. Pass format=yaml to receive YAML instead of the default JSON.

NameInTypeRequiredDescription
dirquerystringNoScript directory to scan (absolute or relative to scripts-dir). Default: scripts.
directoryquerystringNoAlias of dir. Ignored when dir is provided.
formatquerystringNoOutput format. json (default) or yaml.
subdomainquerystringNoLimit scan to scripts under this subdomain. Falls back to the Host header when omitted.
execIdquerystringNoLimit scan to scripts under this execId. Falls back to the Host header when omitted.
Terminal window
curl "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/user-openapi/spec?dir=scripts&format=json&subdomain=default&execId=1" \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"openapi": "3.0.3",
"info": {
"title": "User scripts",
"version": "1.0.0"
},
"paths": {
"/api/users/{id}": {
"get": {
"parameters": [
{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
]
}
}
}
}

Generate an OpenAPI specification from user scripts and return it inline (without persisting it).

This endpoint takes no parameters.

FieldTypeRequiredDefaultDescription
directorystringNo"scripts"Directory to scan, relative to the scripts directory. Default: the whole scripts directory, or the namespace when one applies.
directoriesarray of stringsNo—Alternative to directory. Only the first entry is scanned, and only when directory is not given; use /user-openapi/merge to combine several directories.
subdomainstringNo—Subdomain namespace: letters, digits, dots, hyphens and underscores, not made only of dots and not a reserved directory name (node_modules, .git, .hoody-cache, _sdk, _hoody, in any case); it is lowercased. Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
execIdstringNo—Exec ID namespace: 1 to 64 letters or digits, with no dots, hyphens or underscores (a hostname cannot carry them); it is lowercased. Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
recursivebooleanNotrueScan subdirectories.
includePatternsarray of stringsNo—File-name globs to include. Default ["*.js", "*.ts"].
excludePatternsarray of stringsNo—File-name globs to skip. Default ["*.test.js", "*.spec.ts", "*.test.ts", "*.spec.js"].
titlestringNo"Generated API"Document info.title.
descriptionstringNo"Auto-generated from user scripts"Document info.description.
versionstringNo"1.0.0"Document info.version.
baseUrlstringNo"http://localhost:8081"URL of the document’s only servers entry.
Terminal window
curl -X POST https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/user-openapi/generate \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"directory":"scripts","subdomain":"default","execId":"1"}'
{
"success": true,
"data": {
"openapi": "3.0.3",
"info": {
"title": "User scripts",
"version": "1.0.0"
},
"paths": {}
},
"meta": {
"pathCount": 0,
"scanDirectory": "scripts",
"generatedAt": "2026-01-01T00:00:00.000Z"
}
}

Merge multiple OpenAPI specifications into a single document.

This endpoint takes no parameters.

FieldTypeRequiredDefaultDescription
directoriesarray of stringsNo—Directories to generate a document from and merge, relative to the scripts directory.
specsarray of objectsNo—OpenAPI documents to merge, as objects; any other entry is refused with a 400. A document without a paths object contributes no paths.
titlestringNo"Merged API"Merged document info.title.
descriptionstringNo"Combined OpenAPI specification"Merged document info.description.
versionstringNo"1.0.0"Merged document info.version.
subdomainstringNo—Subdomain namespace: letters, digits, dots, hyphens and underscores, not made only of dots and not a reserved directory name (node_modules, .git, .hoody-cache, _sdk, _hoody, in any case); it is lowercased. Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
execIdstringNo—Exec ID namespace: 1 to 64 letters or digits, with no dots, hyphens or underscores (a hostname cannot carry them); it is lowercased. Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
Terminal window
curl -X POST https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/user-openapi/merge \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"specs":[{"openapi":"3.0.3","paths":{}},{"openapi":"3.0.3","paths":{}}]}'
{
"success": true,
"data": {
"openapi": "3.0.3",
"info": {
"title": "Merged specs",
"version": "1.0.0"
},
"paths": {}
}
}

Validate a script’s schema file and return any errors.

This endpoint takes no parameters.

At least one of file or path must be provided; they are alternatives, not both.

FieldTypeRequiredDefaultDescription
filestringConditional—Script path relative to the scripts directory, e.g. default/api/users.ts. Its .openapi.json companion is the file validated. A non-string is refused with a 400. Must be provided when path is absent.
pathstringConditional—Alias of file. Ignored when file is provided. A non-string is refused with a 400. Must be provided when file is absent.
subdomainstringNo—Subdomain namespace: letters, digits, dots, hyphens and underscores, not made only of dots and not a reserved directory name (node_modules, .git, .hoody-cache, _sdk, _hoody, in any case); it is lowercased. Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
execIdstringNo—Exec ID namespace: 1 to 64 letters or digits, with no dots, hyphens or underscores (a hostname cannot carry them); it is lowercased. Surrounding whitespace is ignored. When omitted, null or empty, it is taken from the Host header. Any other value is refused with a 400.
Terminal window
curl -X POST https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/user-openapi/validate \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"file":"default/1/api/users/[id].ts"}'
{
"success": true,
"data": {
"file": "default/1/api/users/[id].ts",
"format": "zod",
"valid": true
},
"errors": []
}

A failing validation looks like:

{
"success": false,
"data": {
"file": "default/1/api/users/[id].ts",
"format": "zod",
"valid": false
},
"errors": [
"required field 'id' missing from object schema",
"field 'id' is not a string"
]
}

Each SDK lives under a path of the form default/<sdkId>/ inside the scripts root. The service writes the proxy, schema and types files for every import. The pre.js and post.js middleware files are written only when the import’s middleware field supplies source for them; an import without middleware writes no pre.js or post.js. The marker field in responses is always a path to the metadata file default/<sdkId>/.sdk.json, never the inline /* hoody-sdk:... */ comment string.

List all imported SDKs. The marker field is the metadata file path (for example default/petstore/.sdk.json).

This endpoint takes no parameters.

Terminal window
curl https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/sdk/list \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"sdks": [
{
"id": "petstore",
"source_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"files": 12,
"middleware": {
"pre": true,
"post": true
},
"marker": "default/petstore/.sdk.json"
}
],
"total": 1
}

Describe a single imported SDK by id, including its script directory, the marker file path, the generated pre.js/post.js middleware metadata (when present), and the list of generated endpoint scripts.

NameInTypeRequiredDescription
idpathstringYesSDK identifier.
Terminal window
curl https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/sdk/petstore \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"id": "petstore",
"type": "sdk",
"source_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"path": "default/petstore",
"marker": "default/petstore/.sdk.json",
"middleware": {
"pre": {
"exists": true,
"path": "default/petstore/pre.js",
"hash": "f6a1c0e8b2d4f5a7c9e0d1b2a3c4d5e6f7a8b9c0"
},
"post": {
"exists": true,
"path": "default/petstore/post.js",
"hash": "1a2b3c4d5e6f70819a0b1c2d3e4f5061728394a5"
}
},
"files": {
"total": 12,
"endpoints": 10,
"list": [
{
"path": "default/petstore/pet/getPetById.ts"
}
]
}
}

Import an OpenAPI specification from a URL and generate a managed SDK folder under the scripts root. The response includes a diff summary (new, updated, conflicts, total), a list of files removed because the prior spec no longer produces them, and the resulting SDK record with its directory, pre/post/marker paths.

This endpoint takes no parameters.

FieldTypeRequiredDefaultDescription
execIdstringNo—Exec ID the SDK is imported under: 1 to 64 lowercase letters or digits. When omitted, it is taken from the Host header.
source_urlstringYes—HTTPS URL of the OpenAPI document to import. It must name a domain (not an IP address), use port 443, and end in .json, .yaml, /documentation/json or /documentation/yaml.
source_authobjectNo—Credentials sent when fetching source_url: either {"type": "bearer", "token": "..."} or {"type": "basic", "username": "...", "password": "..."}. Credentials that could not be sent as given are refused with a 400, never dropped. Other members are ignored.
middlewareobjectNo—Middleware written next to the generated proxy files. Each member is JavaScript source, written as pre.js or post.js into every directory that receives a proxy. pre is the source of pre.js, post is the source of post.js. A member that is null or an empty string writes nothing.
magic_commentsobjectNo—Magic-comment directives written into every generated proxy file, keyed by directive name. Accepted members: enabled (boolean), token (shared secret, not [REDACTED]), mode (worker or serverless), timeout (integer ms or duration string), log-level (none, minimal, standard, full, debug), concurrent (boolean or integer), cors (origin strategy), websocket (boolean). Any other key is refused with a 400.
forcebooleanNofalseWhen true, overwrite existing generated files and apply other destructive update steps.
Terminal window
curl -X POST https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/sdk/import \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"execId":"1","source_url":"https://petstore3.swagger.io/api/v3/openapi.json"}'
{
"action": "imported",
"summary": {
"new": 10,
"updated": 2,
"conflicts": 0,
"total": 12
},
"removed": [],
"sdk": {
"id": "petstore",
"source_url": "https://petstore3.swagger.io/api/v3/openapi.json",
"path": "default/petstore",
"files": {
"endpoints": 10,
"pre": "default/petstore/pre.js",
"post": "default/petstore/post.js",
"marker": "default/petstore/.sdk.json"
}
}
}

Delete an imported SDK, removing its directory, all generated endpoint files, its pre.js/post.js middleware (when present), and the .sdk.json marker file. The response reports the marker path, removed directory, and number of files removed.

NameInTypeRequiredDescription
idpathstringYesSDK identifier.
Terminal window
curl -X DELETE https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/sdk/petstore \
-H "Authorization: Bearer $HOODY_TOKEN"
{
"message": "SDK deleted",
"removed": {
"marker": "default/petstore/.sdk.json",
"files": 12,
"directory": "default/petstore"
}
}