Route Management
Section titled “Route Management”The Route Management API exposes the routing engine used by Hoody’s script executor. Use these endpoints to resolve a URL path to its underlying script, discover all available routes, batch-test routing rules, and manage the OpenAPI specifications generated from your user scripts and imported SDKs.
Endpoints are grouped into three areas:
- SDK Management — list, import, retrieve, and delete SDK bundles.
- User OpenAPI — inspect, generate, validate, and merge OpenAPI specs derived from user scripts.
- Route Operations — discover, resolve, and batch-test URL routes.
SDK Management
Section titled “SDK Management”GET /api/v1/exec/sdk/:id
Section titled “GET /api/v1/exec/sdk/:id”Retrieve the metadata for a single SDK bundle, including its source URL, file inventory, and pre/post middleware configuration.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | SDK identifier |
Response
Section titled “Response”{ "id": "sdk_01HV8Z3KQ4M5N6P7R8S9T", "type": "sdk", "source_url": "https://github.com/example-org/example-sdk", "path": "/scripts/sdk/example-sdk", "marker": "// @hoody-sdk", "middleware": { "pre": { "exists": true, "path": "/scripts/sdk/example-sdk/pre.ts", "hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" }, "post": { "exists": false, "path": null, "hash": null } }, "files": { "total": 42, "endpoints": 18, "list": [ "/scripts/sdk/example-sdk/index.ts", "/scripts/sdk/example-sdk/users/[id].ts" ] }}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "field": "id", "reason": "must be a non-empty string" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "NOT_FOUND", "code": "NOT_FOUND", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "resource": "sdk", "id": "sdk_does_not_exist" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
NOT_FOUND | Resource not found | The requested resource does not exist | Verify the resource identifier |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const sdk = await client.exec.sdk.get({ id: "sdk_01HV8Z3KQ4M5N6P7R8S9T" });GET /api/v1/exec/sdk/list
Section titled “GET /api/v1/exec/sdk/list”List every SDK currently registered with the executor. Returns a flat array of SDK summaries plus a total count.
This endpoint takes no parameters.
Response
Section titled “Response”{ "sdks": [ { "id": "sdk_01HV8Z3KQ4M5N6P7R8S9T", "source_url": "https://github.com/example-org/example-sdk", "files": 42, "middleware": { "pre": true, "post": false }, "marker": "// @hoody-sdk" }, { "id": "sdk_02AB3CD4EF5GH6JK7LM8N", "source_url": "https://github.com/example-org/another-sdk", "files": 17, "middleware": { "pre": false, "post": true }, "marker": "// @hoody-sdk" } ], "total": 2}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const { sdks, total } = await client.exec.sdk.list();POST /api/v1/exec/sdk/import
Section titled “POST /api/v1/exec/sdk/import”Import an SDK bundle from a remote source. Returns a per-file diff summary (new, updated, conflicts) plus the stored SDK metadata.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Name | Type | Required | Default | Description |
|---|---|---|---|---|
execId | string | Yes | — | Exec identifier that owns this SDK |
source_url | string | Yes | — | Git URL or archive URL to import from |
source_auth | string | No | — | Authentication string for private sources |
middleware | string | No | — | Optional middleware bundle reference |
magic_comments | string | No | — | Magic comment directives for import |
force | boolean | No | false | Overwrite conflicting files |
{ "execId": "exec_01HV8Z3KQ4M5N6P7R8S9T", "source_url": "https://github.com/example-org/example-sdk", "source_auth": "ghp_xxx", "force": false}Response
Section titled “Response”{ "action": "imported", "summary": { "new": 36, "updated": 4, "conflicts": 2, "total": 42 }, "sdk": { "id": "sdk_01HV8Z3KQ4M5N6P7R8S9T", "source_url": "https://github.com/example-org/example-sdk", "path": "/scripts/sdk/example-sdk", "files": { "endpoints": 18, "pre": "/scripts/sdk/example-sdk/pre.ts", "post": "", "marker": "// @hoody-sdk" } }}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "field": "source_url", "reason": "must be a valid URL" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”await client.exec.sdk.importSDK({ execId: "exec_01HV8Z3KQ4M5N6P7R8S9T", source_url: "https://github.com/example-org/example-sdk",});DELETE /api/v1/exec/sdk/:id
Section titled “DELETE /api/v1/exec/sdk/:id”Remove an SDK bundle from the executor and report what was deleted (marker, file count, directory).
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | SDK identifier |
Response
Section titled “Response”{ "message": "SDK deleted", "removed": { "marker": "// @hoody-sdk", "files": 42, "directory": "/scripts/sdk/example-sdk" }}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "field": "id", "reason": "must be a non-empty string" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "NOT_FOUND", "code": "NOT_FOUND", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "resource": "sdk", "id": "sdk_does_not_exist" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
NOT_FOUND | Resource not found | The requested resource does not exist | Verify the resource identifier |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”await client.exec.sdk.delete({ id: "sdk_01HV8Z3KQ4M5N6P7R8S9T" });User OpenAPI
Section titled “User OpenAPI”GET /api/v1/exec/user-openapi/list
Section titled “GET /api/v1/exec/user-openapi/list”List available user scripts along with their schema metadata. Useful for discovering which scripts have OpenAPI schemas and what path parameters each one declares.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
directory | query | string | No | scripts | Script directory to list (absolute or relative to scripts-dir) |
dir | query | string | No | — | Alias of directory. Ignored when directory is provided |
subdomain | query | string | No | — | Limit scan to scripts under this subdomain. Falls back to the Host header when omitted |
execId | query | string | No | — | Limit scan to scripts under this execId. Falls back to the Host header when omitted |
Response
Section titled “Response”{ "success": true, "data": { "directory": "scripts", "totalScripts": 12, "withSchemas": 8, "scripts": [ { "path": "default/api/users/[id].ts", "routePath": "/api/users/[id]", "hasSchema": true, "schemaFormat": "zod", "pathParameters": ["id"] }, { "path": "default/api/posts/index.ts", "routePath": "/api/posts", "hasSchema": false, "schemaFormat": null, "pathParameters": [] } ] }}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "field": "directory", "reason": "path is not accessible" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const { data } = await client.exec.openapi.listScripts({ directory: "scripts",});GET /api/v1/exec/user-openapi/schema
Section titled “GET /api/v1/exec/user-openapi/schema”Serve a single script’s schema file directly. The response body is the raw schema (Zod or JSON Schema) without any envelope.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
file | query | string | No | Absolute or scripts-dir-relative path to the target script (e.g. default/api/users/[id].ts). Either file or path must be provided |
path | query | string | No | Alias of file. Either file or path must be provided |
Response
Section titled “Response”{ "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "name"]}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "field": "file", "reason": "must be provided" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const schema = await client.exec.openapi.serveSchema({ file: "default/api/users/[id].ts",});GET /api/v1/exec/user-openapi/spec
Section titled “GET /api/v1/exec/user-openapi/spec”Generate and serve the full OpenAPI specification for the user script directory on-the-fly. Output format is selectable via the format parameter.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
dir | query | string | No | scripts | Script directory to scan (absolute or relative to scripts-dir) |
directory | query | string | No | — | Alias of dir. Ignored when dir is provided |
format | query | string | No | json | Output format. One of json or yaml |
subdomain | query | string | No | — | Limit scan to scripts under this subdomain. Falls back to the Host header when omitted |
execId | query | string | No | — | Limit scan to scripts under this execId. Falls back to the Host header when omitted |
Response
Section titled “Response”{ "openapi": "3.1.0", "info": { "title": "User API", "version": "1.0.0" }, "paths": { "/api/users/{id}": { "get": { "summary": "Get user by id", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "field": "format", "reason": "must be one of: json, yaml" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const spec = await client.exec.openapi.serve({ dir: "scripts", format: "json",});POST /api/v1/exec/user-openapi/generate
Section titled “POST /api/v1/exec/user-openapi/generate”Trigger a fresh OpenAPI generation pass over the user script directory. The response contains the generated spec and metadata about the scan.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”This endpoint accepts a JSON request body. No specific body fields are documented.
{}Response
Section titled “Response”{ "success": true, "data": { "openapi": "3.1.0", "info": { "title": "User API", "version": "1.0.0" }, "paths": {} }, "meta": { "pathCount": 12, "scanDirectory": "scripts", "generatedAt": "2025-01-15T12:00:00.000Z" }}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const { data, meta } = await client.exec.openapi.generate({});POST /api/v1/exec/user-openapi/merge
Section titled “POST /api/v1/exec/user-openapi/merge”Merge multiple OpenAPI specifications into a single document. Useful for combining generated user specs with third-party SDK specs.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”This endpoint accepts a JSON request body. No specific body fields are documented.
{}Response
Section titled “Response”{ "success": true, "data": { "openapi": "3.1.0", "info": { "title": "Combined API", "version": "1.0.0" }, "paths": { "/api/users/{id}": {}, "/api/posts": {} } }}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const { data } = await client.exec.openapi.merge({});POST /api/v1/exec/user-openapi/validate
Section titled “POST /api/v1/exec/user-openapi/validate”Validate a script’s schema file. Returns a success flag, optional structured data, and a list of human-readable error messages when validation fails.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”This endpoint accepts a JSON request body. No specific body fields are documented.
{}Response
Section titled “Response”{ "success": true, "data": { "valid": true, "warnings": [] }, "errors": []}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const result = await client.exec.openapi.validateSchema({});Route Operations
Section titled “Route Operations”POST /api/v1/exec/route/discover
Section titled “POST /api/v1/exec/route/discover”Discover every route available in the script directory. Each route is classified by Next.js-style type (static, dynamic, catchall, or optional-catchall), with extracted parameter names. Optionally include file metadata (size, modification time).
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Name | Type | Required | Default | Description |
|---|---|---|---|---|
baseDir | string | No | "" | Base directory to scan |
includeMetadata | boolean | No | false | When true, includes per-file size and modification time |
{ "baseDir": "scripts", "includeMetadata": true}Response
Section titled “Response”{ "baseDir": "scripts", "count": 3, "routes": [ { "pattern": "/api/posts", "filePath": "scripts/default/api/posts/index.ts", "type": "static", "params": [] }, { "pattern": "/api/users/[id]", "filePath": "scripts/default/api/users/[id].ts", "type": "dynamic", "params": ["id"] }, { "pattern": "/api/docs/[...slug]", "filePath": "scripts/default/api/docs/[...slug].ts", "type": "catchall", "params": ["slug"] } ]}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "field": "baseDir", "reason": "must be a string" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "NOT_FOUND", "code": "NOT_FOUND", "timestamp": "2025-01-15T12:00:00.000Z", "details": { "resource": "baseDir", "path": "scripts/does-not-exist" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
NOT_FOUND | Resource not found | The requested resource does not exist | Verify the resource identifier |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const { routes, count } = await client.exec.route.discover({ baseDir: "scripts", includeMetadata: true,});POST /api/v1/exec/route/resolve
Section titled “POST /api/v1/exec/route/resolve”Resolve a single URL path to the script file that would handle it. Supports static routes, [param], [...slug], and [[...path]]. Returns the matched script path, the extracted route parameters, the route type, and the directories that were searched.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”This endpoint requires a JSON request body. No specific body fields are documented.
{}Response
Section titled “Response”{ "matched": true, "path": "/api/users/42", "hostname": "api.example.com", "execId": "exec_01HV8Z3KQ4M5N6P7R8S9T", "triedDirectories": [ "scripts/api.example.com/exec_01HV8Z3KQ4M5N6P7R8S9T", "scripts/api.example.com", "scripts/exec_01HV8Z3KQ4M5N6P7R8S9T", "scripts" ]}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const result = await client.exec.route.resolve({});POST /api/v1/exec/route/test
Section titled “POST /api/v1/exec/route/test”Batch-test multiple URL paths against the routing system in a single request. Each path is resolved using the same priority and scoping rules as resolveRoute. The response includes per-path results plus aggregate matched / notMatched counts.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”This endpoint requires a JSON request body. No specific body fields are documented.
{}Response
Section titled “Response”{ "tested": 3, "matched": 2, "notMatched": 1, "results": [ { "path": "/api/posts", "matched": true, "scriptPath": "scripts/default/api/posts/index.ts", "type": "static", "params": {} }, { "path": "/api/users/42", "matched": true, "scriptPath": "scripts/default/api/users/[id].ts", "type": "dynamic", "params": { "id": "42" } }, { "path": "/api/does-not-exist", "matched": false, "scriptPath": null, "type": null, "params": {} } ]}{ "error": "VALIDATION_ERROR", "code": "VALIDATION_ERROR", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-15T12:00:00.000Z", "details": {}}SDK Usage
Section titled “SDK Usage”const { matched, notMatched, results } = await client.exec.route.test({});