Hoody Run: Resolve
Section titled “Hoody Run: Resolve”The Run service is a pure application resolver. It takes a Selector (an app name plus optional filters such as os, source, kind, arch, channel, version, pick, and terminal_id), searches every enabled package source, ranks the candidates, and returns an exact shell command. The service never starts the application itself — it returns status: "resolved" (candidates listed but none selected) or status: "dry-run" (a single candidate selected and its shell command returned). When a browser handoff preview is available, it is exposed only through handoff.preview_display_url and handoff.preview_terminal_url.
Use these endpoints whenever you need to map a human-friendly program name to an executable command, batch multiple resolutions, paginate large candidate sets, or read the live runtime configuration and OpenAPI documents.
The base URL for every operation below is:
https://{projectId}-{containerId}-run-1.{server}.containers.hoody.comSubstitute {projectId} and {containerId} with the 24-character hexadecimal project and container identifiers from your control plane, and {server} with the region node label (for example node-us).
Health
Section titled “Health”GET /api/v1/run/health
Section titled “GET /api/v1/run/health”Returns the standardized 9-field health response. Unauthenticated. The endpoint always returns HTTP 200 with application/json while the service is running.
This endpoint takes no parameters.
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/health"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.healthCheck();{ "status": "ok", "service": "hoody-run", "built": "2025-01-15T08:42:11Z", "started": "2025-01-15T09:00:03Z", "memory": { "rss": 33554432, "heap": null }, "fds": 17, "pid": 4821, "ip": "10.0.0.7", "userAgent": "hoody-run/0.4.2"}Runtime Configuration
Section titled “Runtime Configuration”GET /api/v1/run/config
Section titled “GET /api/v1/run/config”Returns the full persisted runtime configuration, including every configured source, every profile, the currently selected profile, and any saved recipes and webhooks.
This endpoint takes no parameters.
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/config"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.configuration.getConfig();{ "version": 3, "sources": [ { "source_id": "nixpkgs", "enabled": true, "priority": 100, "provider": "nix", "source_type": "nix-pkgs", "pin": { "url": "https://github.com/NixOS/nixpkgs" }, "config": { "flake": "nixpkgs" } } ], "profiles": [ { "name": "default", "description": "Default profile (inherits global sources)", "defaults": { "os": "linux", "kind": "any", "source": [], "pick": "first", "terminal_id": 1, "display": ":1", "limit": 25 }, "sources_mode": "inherit", "sources": [], "policy": { "require_verified": false, "require_integrity": false, "deny_providers": [], "deny_source_ids": [] } } ], "policy": { "require_verified": false, "require_integrity": false, "deny_providers": [], "deny_source_ids": [] }, "selected_profile": "default", "recipes": [], "webhooks": []}OpenAPI Specification
Section titled “OpenAPI Specification”GET /api/v1/run/openapi.json
Section titled “GET /api/v1/run/openapi.json”Returns the OpenAPI 3.0.3 specification for this API in JSON format. The document is generated from the canonical YAML source and reflects every operation on this page.
This endpoint takes no parameters.
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/openapi.json"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.documentation.getOpenApiJson();{ "openapi": "3.0.3", "info": { "title": "hoody-run", "version": "0.4.2" }, "paths": { "/api/v1/run/resolve": { "get": { "summary": "Resolve an application and return exact shell command" }, "post": { "summary": "Resolve an application via JSON body" } } }}GET /api/v1/run/openapi.yaml
Section titled “GET /api/v1/run/openapi.yaml”Returns the OpenAPI 3.0.3 specification for this API in YAML format.
This endpoint takes no parameters.
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/openapi.yaml"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.documentation.getOpenApiYaml();openapi: 3.0.3info: title: hoody-run version: 0.4.2paths: /api/v1/run/resolve: get: summary: Resolve an application and return exact shell command post: summary: Resolve an application via JSON bodyResolve an Application
Section titled “Resolve an Application”The four endpoints below all execute the same core algorithm: validate the selector, search every enabled source, rank candidates, and either return the ranked list or select one. Every candidate carries a kind field equal to gui, cli, or any — the last value means the source did not classify the candidate.
GET /api/v1/run/resolve
Section titled “GET /api/v1/run/resolve”Resolve and select an application using query parameters, then return the exact shell command. Supports every selector field plus pick mode and output control.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
app | query | string | Yes | Primary name query |
os | query | run_Os | No | Target OS filter |
source | query | array | No | Source kind filter (repeatable) |
kind | query | run_AppKind | No | App kind filter |
arch | query | run_Arch | No | Target CPU architecture filter |
tags | query | array | No | Free-form tags for filtering and ranking (repeatable) |
profile | query | string | No | Named profile for default preferences |
channel | query | string | No | Release channel hint |
version | query | string | No | Exact version or provider-defined version constraint |
variant | query | string | No | Provider-specific variant hint |
publisher | query | string | No | Publisher hint for curated registries |
repo | query | string | No | Repository hint such as owner/name |
release | query | string | No | Release hint such as a tag name |
asset | query | string | No | Desired asset name or pattern |
pick | query | run_PickMode | No | Candidate selection mode (ask, first, index, id) |
pick_index | query | integer | No | Candidate index (required when pick=index) |
candidate_id | query | string | No | Specific candidate ID (required when pick=id) |
set_id | query | string | No | Bind pick to a specific candidate set |
terminal_id | query | integer | No | Terminal session ID (default 1) |
display | query | string | No | X11 DISPLAY number |
origin | query | string | No | Origin identifier for observability propagation |
dry_run | query | boolean | No | Force command-only response (the Run service never executes) |
print_curl | query | run_PrintCurlMode | No | Generate curl command |
format | query | run_OutputFormat | No | Output format (json or html) |
limit | query | integer | No | Max candidates (default 25) |
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/resolve?app=firefox&os=linux&kind=any&pick=first"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.resolveGet({ app: 'firefox', os: 'linux', kind: 'any', pick: 'first' });{ "status": "dry-run", "set_id": "a1b2c3d4e5f6", "selected": { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "homepage": "https://www.mozilla.org/firefox/", "tags": ["browser", "web"], "provider": "nix", "source_id": "nixpkgs", "score": 95, "reasons": ["exact name match", "high priority source"], "verified": true, "run_plan": { "command": "nix run nixpkgs#firefox", "env": { "MOZ_ENABLE_WAYLAND": "0" }, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox", "href_path": "/api/v1/run/resolve?app=firefox&os=linux&kind=any&pick=id&candidate_id=nix-firefox-128&set_id=a1b2c3d4e5f6" }, "shell_command": "nix run nixpkgs#firefox", "handoff": { "state": "preview", "terminal_id": 1, "display": ":1", "preview_display_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/display/1", "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/terminal/1" }, "warnings": []}{ "error": "missing app", "code": 400}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_APP | Missing app query | No app name was provided in the request | Set app to the desired program name |
INVALID_SELECTOR | Invalid selector parameter | One or more selector parameters could not be parsed | Check enum values and numeric fields, then retry |
UNKNOWN_PROFILE | Unknown profile | The requested profile does not exist | Choose a profile returned by listProfiles or getConfig |
INVALID_PICK | Invalid pick request | The pick mode requirements were not satisfied or the selected candidate was not found | Search first, then supply a valid pick_index or candidate_id |
{ "error": "candidate denied by policy", "code": 403}| Error Code | Title | Description | Resolution |
|---|---|---|---|
POLICY_DENIED | Candidate denied by policy | A pick from an older cached set resolved to a candidate that the current effective policy forbids | Pick a candidate allowed by the active profile, or search again under the current policy |
{ "error": "candidate set expired", "code": 409}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SET_EXPIRED | Candidate set expired | The set_id-bound candidate set has expired; pick=index can no longer be resolved race-safely | Re-run search to obtain a fresh set_id, then retry the pick |
{ "error": "internal error", "code": 500}{ "error": "source resolution failed", "code": 502}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SOURCE_RESOLUTION_FAILED | Source resolution failed | Candidate resolution could not be completed because upstream source work failed | Retry or inspect provider/source health |
POST /api/v1/run/resolve
Section titled “POST /api/v1/run/resolve”Same behavior as GET /api/v1/run/resolve but accepts the full Selector as a JSON request body. Use this when the selector is too complex to encode cleanly in a URL.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”{ "app": "firefox", "os": "linux", "kind": "any", "source": ["nix"], "arch": "amd64", "tags": ["browser"], "profile": "default", "channel": "stable", "version": "128.0.3", "variant": "headless", "publisher": "mozilla", "repo": "mozilla/firefox", "release": "v128.0.3", "asset": "firefox-linux-x86_64.tar.bz2", "pick": "first", "pick_index": 0, "candidate_id": "nix-firefox-128", "set_id": "a1b2c3d4e5f6", "terminal_id": 1, "display": ":1", "origin": "cli", "format": "json", "dry_run": true, "print_curl": "hoody-run", "limit": 25}curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/resolve" \ -H "content-type: application/json" \ -d '{"app":"firefox","os":"linux","kind":"any","pick":"first","limit":25}'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.resolve({ app: 'firefox', os: 'linux', kind: 'any', pick: 'first', limit: 25 });{ "status": "dry-run", "set_id": "a1b2c3d4e5f6", "selected": { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "provider": "nix", "source_id": "nixpkgs", "score": 95, "reasons": ["exact name match"], "verified": true, "run_plan": { "command": "nix run nixpkgs#firefox", "env": { "MOZ_ENABLE_WAYLAND": "0" }, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" }, "shell_command": "nix run nixpkgs#firefox", "handoff": { "state": "preview", "terminal_id": 1, "display": ":1", "preview_display_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/display/1", "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/terminal/1" }, "warnings": []}{ "error": "missing app", "code": 400}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_APP | Missing app query | No app name was provided in the request body | Set app to the desired program name |
INVALID_SELECTOR | Invalid selector parameter | One or more selector fields could not be parsed | Check enum values and numeric fields, then retry |
UNKNOWN_PROFILE | Unknown profile | The requested profile does not exist | Choose a profile returned by listProfiles or getConfig |
INVALID_PICK | Invalid pick request | The pick mode requirements were not satisfied or the selected candidate was not found | Search first, then supply a valid pick_index or candidate_id |
{ "error": "candidate denied by policy", "code": 403}| Error Code | Title | Description | Resolution |
|---|---|---|---|
POLICY_DENIED | Candidate denied by policy | A pick from an older cached set resolved to a candidate that the current effective policy forbids | Pick a candidate allowed by the active profile, or search again under the current policy |
{ "error": "candidate set expired", "code": 409}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SET_EXPIRED | Candidate set expired | The set_id-bound candidate set has expired; pick=index can no longer be resolved race-safely | Re-run search to obtain a fresh set_id, then retry the pick |
{ "error": "internal error", "code": 500}{ "error": "source resolution failed", "code": 502}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SOURCE_RESOLUTION_FAILED | Source resolution failed | Candidate resolution could not be completed because upstream source work failed | Retry or inspect provider/source health |
GET /api/v1/run/go/{rest}
Section titled “GET /api/v1/run/go/{rest}”Path-based resolve that produces clean, bookmarkable URLs. Supports both positional and key-value path segments.
Positional forms:
/api/v1/run/go/{app}/api/v1/run/go/{os}/{app}/api/v1/run/go/{os}/{source}/{app}/api/v1/run/go/{os}/{source}/{kind}/{app}
Key-value form:
/api/v1/run/go/app/{app}/os/{os}/source/{source}/kind/{kind}/pick/{pick}/...
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
rest | path | string | Yes | Path segments for positional or key-value app specification |
os | query | run_Os | No | Target OS filter when not supplied in the path |
source | query | array | No | Source kind filter (repeatable) |
kind | query | run_AppKind | No | App kind filter when not supplied in the path |
arch | query | run_Arch | No | Target CPU architecture filter |
tags | query | array | No | Free-form tags for filtering and ranking (repeatable) |
profile | query | string | No | Named profile for default preferences |
channel | query | string | No | Release channel hint |
version | query | string | No | Exact version or provider-defined version constraint |
variant | query | string | No | Provider-specific variant hint |
publisher | query | string | No | Publisher hint for curated registries |
repo | query | string | No | Repository hint such as owner/name |
release | query | string | No | Release hint such as a tag name |
asset | query | string | No | Desired asset name or pattern |
pick | query | run_PickMode | No | Candidate selection mode (ask, first, index, id) |
pick_index | query | integer | No | Candidate index (required when pick=index) |
candidate_id | query | string | No | Specific candidate ID (required when pick=id) |
set_id | query | string | No | Bind pick to a specific candidate set |
terminal_id | query | integer | No | Terminal session ID when not supplied in the path |
display | query | string | No | X11 DISPLAY number |
origin | query | string | No | Origin identifier for observability propagation |
dry_run | query | boolean | No | Force command-only response (the Run service never executes) |
print_curl | query | run_PrintCurlMode | No | Generate curl command |
format | query | run_OutputFormat | No | Output format (json or html) |
limit | query | integer | No | Max candidates (default 25) |
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/go/linux/nix/firefox"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.runPathBased('linux/nix/firefox');{ "status": "dry-run", "set_id": "a1b2c3d4e5f6", "selected": { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "provider": "nix", "source_id": "nixpkgs", "score": 95, "run_plan": { "command": "nix run nixpkgs#firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" }, "shell_command": "nix run nixpkgs#firefox", "handoff": { "state": "preview", "terminal_id": 1, "display": ":1", "preview_display_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/display/1", "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/terminal/1" }, "warnings": []}{ "error": "missing app", "code": 400}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_APP | Missing app query | No app name was provided in the request | Set the app in the path or query string |
INVALID_SELECTOR | Invalid selector parameter | One or more selector parameters could not be parsed | Check enum values and numeric fields, then retry |
UNKNOWN_PROFILE | Unknown profile | The requested profile does not exist | Choose a profile returned by listProfiles or getConfig |
INVALID_PICK | Invalid pick request | The pick mode requirements were not satisfied or the selected candidate was not found | Search first, then supply a valid pick_index or candidate_id |
{ "error": "internal error", "code": 500}{ "error": "source resolution failed", "code": 502}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SOURCE_RESOLUTION_FAILED | Source resolution failed | Candidate resolution could not be completed because upstream source work failed | Retry or inspect provider/source health |
GET /api/v1/run/t/{terminal_id}/go/{rest}
Section titled “GET /api/v1/run/t/{terminal_id}/go/{rest}”Same as /api/v1/run/go/{rest} but with terminal_id extracted from the path prefix. The combined URL specifies both the target terminal and the application in a single path, for example /api/v1/run/t/2/go/linux/nix/firefox.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
terminal_id | path | integer | Yes | Terminal session ID (1 to 65535) |
rest | path | string | Yes | Path segments for app specification |
os | query | run_Os | No | Target OS filter when not supplied in the path |
source | query | array | No | Source kind filter (repeatable) |
kind | query | run_AppKind | No | App kind filter when not supplied in the path |
arch | query | run_Arch | No | Target CPU architecture filter |
tags | query | array | No | Free-form tags for filtering and ranking (repeatable) |
profile | query | string | No | Named profile for default preferences |
channel | query | string | No | Release channel hint |
version | query | string | No | Exact version or provider-defined version constraint |
variant | query | string | No | Provider-specific variant hint |
publisher | query | string | No | Publisher hint for curated registries |
repo | query | string | No | Repository hint such as owner/name |
release | query | string | No | Release hint such as a tag name |
asset | query | string | No | Desired asset name or pattern |
pick | query | run_PickMode | No | Candidate selection mode (ask, first, index, id) |
pick_index | query | integer | No | Candidate index (required when pick=index) |
candidate_id | query | string | No | Specific candidate ID (required when pick=id) |
set_id | query | string | No | Bind pick to a specific candidate set |
display | query | string | No | X11 DISPLAY number |
origin | query | string | No | Origin identifier for observability propagation |
dry_run | query | boolean | No | Force command-only response (the Run service never executes) |
print_curl | query | run_PrintCurlMode | No | Generate curl command |
format | query | run_OutputFormat | No | Output format (json or html) |
limit | query | integer | No | Max candidates (default 25) |
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/t/2/go/linux/nix/firefox"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.runTerminalAnchored(2, 'linux/nix/firefox');{ "status": "dry-run", "set_id": "a1b2c3d4e5f6", "selected": { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "provider": "nix", "source_id": "nixpkgs", "score": 95, "run_plan": { "command": "nix run nixpkgs#firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" }, "shell_command": "nix run nixpkgs#firefox", "handoff": { "state": "preview", "terminal_id": 2, "display": ":2", "preview_display_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/display/2", "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/terminal/2" }, "warnings": []}{ "error": "missing app", "code": 400}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_APP | Missing app query | No app name was provided in the request | Set the app in the path or query string |
INVALID_SELECTOR | Invalid selector parameter | One or more selector parameters could not be parsed | Check enum values and numeric fields, then retry |
UNKNOWN_PROFILE | Unknown profile | The requested profile does not exist | Choose a profile returned by listProfiles or getConfig |
INVALID_PICK | Invalid pick request | The pick mode requirements were not satisfied or the selected candidate was not found | Search first, then supply a valid pick_index or candidate_id |
{ "error": "internal error", "code": 500}{ "error": "source resolution failed", "code": 502}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SOURCE_RESOLUTION_FAILED | Source resolution failed | Candidate resolution could not be completed because upstream source work failed | Retry or inspect provider/source health |
Search Candidates
Section titled “Search Candidates”GET /api/v1/run/search
Section titled “GET /api/v1/run/search”Search for runnable application candidates across every configured and enabled package source. Returns a ranked list with stable ordering for pick-by-index operations. Pass the returned set_id to a follow-up resolve request to guarantee race-free candidate selection.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
app | query | string | Yes | Primary name query (aliases q, name) |
os | query | run_Os | No | Target OS filter |
source | query | array | No | Source kind filter (repeatable) |
kind | query | run_AppKind | No | App kind filter (gui, cli, any) |
arch | query | run_Arch | No | Target CPU architecture filter |
tags | query | array | No | Free-form tags for filtering and ranking (repeatable) |
profile | query | string | No | Named profile for default preferences |
channel | query | string | No | Release channel hint (for example stable or beta) |
version | query | string | No | Exact version or provider-defined version constraint |
variant | query | string | No | Provider-specific variant hint (for example portable or headless) |
publisher | query | string | No | Publisher hint for curated registries |
repo | query | string | No | Repository hint such as owner/name |
release | query | string | No | Release hint such as a tag name |
asset | query | string | No | Desired asset name or pattern |
limit | query | integer | No | Max candidates to return (default 25) |
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/search?app=firefox&os=linux&kind=any"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.searchCandidates({ app: 'firefox', os: 'linux', kind: 'any' });{ "set_id": "a1b2c3d4e5f6", "candidates": [ { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "provider": "nix", "source_id": "nixpkgs", "score": 95, "reasons": ["exact name match", "high priority source"], "run_plan": { "command": "nix run nixpkgs#firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" }, { "candidate_id": "pkgx-firefox-128", "title": "Firefox (pkgx)", "description": "Mozilla Firefox web browser via pkgx", "kind": "any", "version": "128.0.3", "provider": "pkgx", "source_id": "pkgx-main", "score": 72, "run_plan": { "command": "pkgx firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "pkgx firefox" } ]}{ "error": "missing app", "code": 400}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_APP | Missing app query | No app name was provided in the request | Set app, q, or name to the desired program |
INVALID_SELECTOR | Invalid selector parameter | One or more selector parameters could not be parsed | Check enum values and numeric fields, then retry |
UNKNOWN_PROFILE | Unknown profile | The requested profile does not exist | Call listProfiles or getConfig and choose a valid profile name |
{ "error": "source resolution failed", "code": 502}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SOURCE_RESOLUTION_FAILED | Source resolution failed | Candidate resolution could not be completed because upstream source work failed | Retry or inspect provider/source health |
POST /api/v1/run/search/paged
Section titled “POST /api/v1/run/search/paged”Resolve a full ranked candidate set under a bounded cap, then page through it with an opaque cursor. This is the stable pagination contract for large result sets.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”{ "selector": { "app": "firefox", "os": "linux", "kind": "any", "limit": 25 }, "cursor": "eyJzZXRfaWQiOiJhMWIyYzNkNGU1ZjYiLCJvZmZzZXQiOjI1fQ==", "page_size": 25}curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/search/paged" \ -H "content-type: application/json" \ -d '{"selector":{"app":"firefox","os":"linux","kind":"any"},"page_size":25}'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.searchCandidatesPagedIterator({ selector: { app: 'firefox', os: 'linux', kind: 'any' }, page_size: 25 });{ "set_id": "a1b2c3d4e5f6", "total_count": 142, "items": [ { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "provider": "nix", "source_id": "nixpkgs", "score": 95, "run_plan": { "command": "nix run nixpkgs#firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" } ], "next_cursor": "eyJzZXRfaWQiOiJhMWIyYzNkNGU1ZjYiLCJvZmZzZXQiOjI1fQ=="}{ "error": "bad request", "code": 400}{ "error": "cursor set expired", "code": 409}| Error Code | Title | Description | Resolution |
|---|---|---|---|
CURSOR_SET_EXPIRED | Cursor set expired | The cached candidate set referenced by the cursor is no longer available | Restart pagination from the first page |
{ "error": "source resolution failed", "code": 502}Preflight and Batch
Section titled “Preflight and Batch”POST /api/v1/run/preflight
Section titled “POST /api/v1/run/preflight”Resolve, optionally pick, and normalize the execution plan for a selector without scheduling execution. The response reports the recommended execution mode (search-only, dry-run, or printed-curl) and lists any missing requirements.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”{ "app": "firefox", "os": "linux", "kind": "any", "source": ["nix"], "profile": "default", "pick": "first", "terminal_id": 1, "display": ":1", "limit": 25}curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/preflight" \ -H "content-type: application/json" \ -d '{"app":"firefox","os":"linux","kind":"any","pick":"first"}'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.preflightRun({ app: 'firefox', os: 'linux', kind: 'any', pick: 'first' });{ "set_id": "a1b2c3d4e5f6", "selected": { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "provider": "nix", "source_id": "nixpkgs", "score": 95, "run_plan": { "command": "nix run nixpkgs#firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" }, "shell_command": "nix run nixpkgs#firefox", "recommended_mode": "dry-run", "handoff": { "state": "preview", "terminal_id": 1, "display": ":1", "preview_display_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/display/1", "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/terminal/1" }, "missing_requirements": [], "warnings": [ { "code": "PROFILE_DEFAULTS_APPLIED", "message": "Defaults from profile 'default' were applied to the selector" } ], "effective_policy": { "require_verified": false, "require_integrity": false, "deny_providers": [], "deny_source_ids": [] }}{ "error": "bad request", "code": 400}{ "error": "candidate denied by policy", "code": 403}| Error Code | Title | Description | Resolution |
|---|---|---|---|
POLICY_DENIED | Candidate denied by policy | A pick from an older cached set resolved to a candidate that the current effective policy forbids | Pick a candidate allowed by the active profile, or search again under the current policy |
{ "error": "candidate set expired", "code": 409}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SET_EXPIRED | Candidate set expired | The set_id-bound candidate set has expired; pick=index can no longer be resolved race-safely | Re-run search to obtain a fresh set_id, then retry the pick |
{ "error": "source resolution failed", "code": 502}POST /api/v1/run/batch
Section titled “POST /api/v1/run/batch”Process multiple search or command-only run items in one request. Each item in the request produces its own success or error payload in the response. Item-level errors do not abort the rest of the batch.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”{ "items": [ { "request_id": "req-001", "mode": "search", "selector": { "app": "firefox", "os": "linux", "kind": "any" } }, { "request_id": "req-002", "mode": "run", "selector": { "app": "firefox", "os": "linux", "kind": "any", "pick": "first" } } ]}curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/batch" \ -H "content-type: application/json" \ -d '{ "items": [ {"request_id":"req-001","mode":"search","selector":{"app":"firefox","os":"linux","kind":"any"}}, {"request_id":"req-002","mode":"run","selector":{"app":"firefox","os":"linux","kind":"any","pick":"first"}} ] }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.run.runBatch({ items: [ { request_id: 'req-001', mode: 'search', selector: { app: 'firefox', os: 'linux', kind: 'any' } }, { request_id: 'req-002', mode: 'run', selector: { app: 'firefox', os: 'linux', kind: 'any', pick: 'first' } } ]});{ "items": [ { "result": "search", "request_id": "req-001", "search": { "set_id": "a1b2c3d4e5f6", "candidates": [ { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "description": "Mozilla Firefox web browser via nixpkgs", "kind": "gui", "version": "128.0.3", "provider": "nix", "source_id": "nixpkgs", "score": 95, "run_plan": { "command": "nix run nixpkgs#firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" } ] } }, { "result": "run", "request_id": "req-002", "run": { "status": "dry-run", "set_id": "a1b2c3d4e5f6", "selected": { "candidate_id": "nix-firefox-128", "title": "Firefox (nixpkgs)", "kind": "gui", "provider": "nix", "source_id": "nixpkgs", "score": 95, "run_plan": { "command": "nix run nixpkgs#firefox", "env": {}, "cwd": "/home/user" }, "shell_command": "nix run nixpkgs#firefox" }, "shell_command": "nix run nixpkgs#firefox", "handoff": { "state": "preview", "terminal_id": 1, "display": ":1", "preview_display_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/display/1", "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/preview/terminal/1" }, "warnings": [] } } ]}