# Hoody Run: Resolve

**Page:** api/run/resolve

[Download Raw Markdown](./api/run/resolve.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



# Hoody Run: Resolve

The Hoody Run service is a pure application resolver. It maps a request for an application (by name, source, version, channel, or other selector fields) to an exact shell command that can be run on a terminal. The service never executes anything itself; it returns the resolved command plus, when relevant, a `preview` handoff that points at the browser display and terminal viewer where the app will appear.

Use these endpoints when you want to look up a program by name and get a deterministic command back. Pair them with the search endpoint to obtain a stable `set_id`, then resolve a specific candidate from that set race-free.

## Resolve an application

### `GET /api/v1/run/resolve`

Resolve and select an application using query parameters, then return the exact shell command to run. Supports all selector fields plus pick mode and output control.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `app` | query | string | Yes | Primary name query |
| `os` | query | string | No | Target OS filter |
| `source` | query | array | No | Source kind filter (repeatable) |
| `kind` | query | string | No | App kind filter |
| `arch` | query | string | 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 | string | 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 | If true, force command-only response (hoody-run never executes) |
| `print_curl` | query | string | No | Generate curl command (hoody-run) |
| `format` | query | string | No | Output format (`json` or `html`) |
| `limit` | query | integer | No | Max candidates (default `25`) |



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/resolve?app=firefox&pick=first"
```


```ts
await client.run.resolveGet({ app: "firefox", pick: "first" })
```


```json
{
  "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"],
    "run_plan": {
      "command": "nix run nixpkgs#firefox"
    },
    "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.icu/api/v1/run/preview/display/a1b2c3d4e5f6",
    "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preview/terminal/1"
  },
  "warnings": []
}
```


```json
{
  "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` |


```json
{
  "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 |


```json
{
  "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 |


```json
{
  "error": "internal error",
  "code": 500
}
```


```json
{
  "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`

Same behavior as `GET /api/v1/run/resolve` but accepts the full Selector as a JSON request body. Useful for programmatic clients and complex selectors.

#### Request Body

The request body follows the `Selector` schema. Only `app` is required.

```json
{
  "app": "firefox",
  "os": "linux",
  "kind": "gui",
  "source": ["nix"],
  "pick": "first"
}
```



```bash
curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/resolve" \
  -H "Content-Type: application/json" \
  -d '{"app":"firefox","os":"linux","kind":"gui","pick":"first"}'
```


```ts
await client.run.resolve({ app: "firefox", os: "linux", kind: "gui", pick: "first" })
```


```json
{
  "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,
    "run_plan": {
      "command": "nix run nixpkgs#firefox"
    },
    "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.icu/api/v1/run/preview/display/a1b2c3d4e5f6",
    "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preview/terminal/1"
  },
  "warnings": []
}
```


```json
{
  "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` |


```json
{
  "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 |


```json
{
  "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 |


```json
{
  "error": "internal error",
  "code": 500
}
```


```json
{
  "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}`

Resolve an application using clean, bookmarkable path-based 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

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `rest` | path | string | Yes | Path segments for positional or key-value app specification |
| `os` | query | string | No | Target OS filter when not supplied in the path |
| `source` | query | array | No | Source kind filter (repeatable) |
| `kind` | query | string | No | App kind filter when not supplied in the path |
| `arch` | query | string | 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 | string | 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 | If true, force command-only response (hoody-run never executes) |
| `print_curl` | query | string | No | Generate curl command (hoody-run) |
| `format` | query | string | No | Output format (`json` or `html`) |
| `limit` | query | integer | No | Max candidates (default `25`) |



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/go/linux/nix/gui/firefox?pick=first"
```


```ts
await client.run.runPathBased("linux/nix/gui/firefox", { pick: "first" })
```


```json
{
  "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"
    },
    "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.icu/api/v1/run/preview/display/a1b2c3d4e5f6",
    "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preview/terminal/1"
  },
  "warnings": []
}
```


```json
{
  "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` |


```json
{
  "error": "internal error",
  "code": 500
}
```


```json
{
  "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}`

Same as `/api/v1/run/go/{rest}` but with `terminal_id` extracted from the path prefix. Allows clean URLs that specify both the target terminal and the application in a single path. Example: `/api/v1/run/t/2/go/linux/nix/firefox` runs Firefox in terminal 2.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `terminal_id` | path | integer | Yes | Terminal session ID (1-65535) |
| `rest` | path | string | Yes | Path segments for app specification |
| `os` | query | string | No | Target OS filter when not supplied in the path |
| `source` | query | array | No | Source kind filter (repeatable) |
| `kind` | query | string | No | App kind filter when not supplied in the path |
| `arch` | query | string | 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 | string | 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 | If true, force command-only response (hoody-run never executes) |
| `print_curl` | query | string | No | Generate curl command (hoody-run) |
| `format` | query | string | No | Output format (`json` or `html`) |
| `limit` | query | integer | No | Max candidates (default `25`) |



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/t/2/go/linux/nix/firefox?pick=first"
```


```ts
await client.run.runTerminalAnchored(2, "linux/nix/firefox", { pick: "first" })
```


```json
{
  "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"
    },
    "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.icu/api/v1/run/preview/display/a1b2c3d4e5f6",
    "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preview/terminal/2"
  },
  "warnings": []
}
```


```json
{
  "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` |


```json
{
  "error": "internal error",
  "code": 500
}
```


```json
{
  "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

### `GET /api/v1/run/search`

Search for runnable application candidates across all configured and enabled package sources. Returns a ranked list of candidates with stable ordering for pick-by-index operations.

The returned `set_id` can be used with subsequent run requests to ensure race-free candidate selection.

#### Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `app` | query | string | Yes | Primary name query (aliases `q`, `name`) |
| `os` | query | string | No | Target OS filter |
| `source` | query | array | No | Source kind filter (repeatable) |
| `kind` | query | string | No | App kind filter (`gui`, `cli`, `any`) |
| `arch` | query | string | 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`) |



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/search?app=firefox&kind=gui"
```


```ts
await client.run.searchCandidates({ app: "firefox", kind: "gui" })
```


```json
{
  "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",
      "homepage": "https://www.mozilla.org/firefox/",
      "tags": ["browser", "web"],
      "provider": "nix",
      "source_id": "nixpkgs",
      "score": 95,
      "reasons": ["exact name match", "high priority source"],
      "run_plan": {
        "command": "nix run nixpkgs#firefox"
      },
      "shell_command": "nix run nixpkgs#firefox"
    },
    {
      "candidate_id": "pkgx-firefox-128",
      "title": "Firefox (pkgx)",
      "description": "Mozilla Firefox web browser via pkgx",
      "kind": "gui",
      "version": "128.0.3",
      "provider": "pkgx",
      "source_id": "pkgx-main",
      "score": 80,
      "run_plan": {
        "command": "pkgx firefox"
      },
      "shell_command": "pkgx firefox"
    }
  ]
}
```


```json
{
  "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 |


```json
{
  "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`

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.

#### Request Body

The request body follows the `PagedSearchRequest` schema. The `selector` field is required; `cursor` and `page_size` are optional for subsequent pages.

```json
{
  "selector": {
    "app": "node",
    "kind": "cli"
  },
  "page_size": 50
}
```



```bash
curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/search/paged" \
  -H "Content-Type: application/json" \
  -d '{"selector":{"app":"node","kind":"cli"},"page_size":50}'
```


```ts
await client.run.searchCandidatesPagedIterator({ selector: { app: "node", kind: "cli" }, page_size: 50 })
```


```json
{
  "set_id": "a1b2c3d4e5f6",
  "total_count": 137,
  "items": [
    {
      "candidate_id": "nix-node-22",
      "title": "Node.js 22 (nixpkgs)",
      "description": "Node.js JavaScript runtime via nixpkgs",
      "kind": "cli",
      "version": "22.11.0",
      "provider": "nix",
      "source_id": "nixpkgs",
      "score": 90,
      "run_plan": {
        "command": "nix run nixpkgs#nodejs"
      },
      "shell_command": "nix run nixpkgs#nodejs"
    },
    {
      "candidate_id": "pkgx-node-22",
      "title": "Node.js 22 (pkgx)",
      "description": "Node.js JavaScript runtime via pkgx",
      "kind": "cli",
      "version": "22.11.0",
      "provider": "pkgx",
      "source_id": "pkgx-main",
      "score": 78,
      "run_plan": {
        "command": "pkgx node"
      },
      "shell_command": "pkgx node"
    }
  ],
  "next_cursor": "eyJzZXRfaWQiOiJhMWIyYzNkNGU1ZjYiLCJvZmZzZXQiOjUwfQ=="
}
```


```json
{
  "error": "invalid selector",
  "code": 400
}
```


```json
{
  "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 |


```json
{
  "error": "source resolution failed",
  "code": 502
}
```



## Preflight and batch

### `POST /api/v1/run/preflight`

Resolve, optionally pick, and normalize the execution plan for a selector without scheduling execution. Returns the recommended mode, the selected candidate (if any), the shell command, and any missing requirements or warnings.

#### Request Body

The request body follows the `Selector` schema. Only `app` is required.

```json
{
  "app": "firefox",
  "kind": "gui",
  "profile": "default"
}
```



```bash
curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preflight" \
  -H "Content-Type: application/json" \
  -d '{"app":"firefox","kind":"gui","profile":"default"}'
```


```ts
await client.run.preflightRun({ app: "firefox", kind: "gui", profile: "default" })
```


```json
{
  "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"
    },
    "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.icu/api/v1/run/preview/display/a1b2c3d4e5f6",
    "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preview/terminal/1"
  },
  "missing_requirements": [],
  "warnings": [],
  "effective_policy": {
    "require_verified": false,
    "require_integrity": false,
    "deny_providers": [],
    "deny_source_ids": []
  }
}
```


```json
{
  "error": "missing app",
  "code": 400
}
```


```json
{
  "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 |


```json
{
  "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 |


```json
{
  "error": "source resolution failed",
  "code": 502
}
```



### `POST /api/v1/run/batch`

Process multiple search or command-only run items in one request. Each item produces its own success or error payload.

#### Request Body

The request body follows the `BatchRequest` schema. The `items` array contains individual `BatchItemRequest` entries, each carrying a `request_id`, a `mode` (`search` or `run`), and a `selector`.

```json
{
  "items": [
    {
      "request_id": "req-1",
      "mode": "search",
      "selector": { "app": "firefox", "kind": "gui" }
    },
    {
      "request_id": "req-2",
      "mode": "run",
      "selector": { "app": "node", "kind": "cli", "pick": "first" }
    }
  ]
}
```



```bash
curl -sS -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/batch" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"request_id":"req-1","mode":"search","selector":{"app":"firefox","kind":"gui"}},{"request_id":"req-2","mode":"run","selector":{"app":"node","kind":"cli","pick":"first"}}]}'
```


```ts
await client.run.runBatch({
  items: [
    { request_id: "req-1", mode: "search", selector: { app: "firefox", kind: "gui" } },
    { request_id: "req-2", mode: "run", selector: { app: "node", kind: "cli", pick: "first" } }
  ]
})
```


```json
{
  "items": [
    {
      "result": "search",
      "request_id": "req-1",
      "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" },
            "shell_command": "nix run nixpkgs#firefox"
          }
        ]
      }
    },
    {
      "result": "run",
      "request_id": "req-2",
      "run": {
        "status": "dry-run",
        "set_id": "b7c8d9e0f1a2",
        "selected": {
          "candidate_id": "nix-node-22",
          "title": "Node.js 22 (nixpkgs)",
          "description": "Node.js JavaScript runtime via nixpkgs",
          "kind": "cli",
          "version": "22.11.0",
          "provider": "nix",
          "source_id": "nixpkgs",
          "score": 90,
          "run_plan": { "command": "nix run nixpkgs#nodejs" },
          "shell_command": "nix run nixpkgs#nodejs"
        },
        "shell_command": "nix run nixpkgs#nodejs",
        "handoff": {
          "state": "preview",
          "terminal_id": 1,
          "display": ":1",
          "preview_display_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preview/display/b7c8d9e0f1a2",
          "preview_terminal_url": "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/preview/terminal/1"
        },
        "warnings": []
      }
    }
  ]
}
```



## Configuration and health

### `GET /api/v1/run/config`

Returns the full persisted runtime configuration including sources, profiles, and the currently selected profile.

This endpoint takes no parameters.



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/config"
```


```ts
await client.run.configuration.getConfig()
```


```json
{
  "version": 3,
  "sources": [
    {
      "source_id": "nixpkgs",
      "enabled": true,
      "priority": 100,
      "provider": "nix",
      "source_type": "nix-pkgs",
      "config": { "flake": "nixpkgs" }
    },
    {
      "source_id": "pkgx-main",
      "enabled": true,
      "priority": 80,
      "provider": "pkgx",
      "source_type": "pkgx"
    }
  ],
  "profiles": [
    {
      "name": "default",
      "description": "Default profile (inherits global sources)",
      "defaults": {
        "os": "linux",
        "kind": "any",
        "pick": "ask",
        "terminal_id": 1,
        "limit": 25
      },
      "sources_mode": "inherit",
      "sources": []
    }
  ],
  "policy": {
    "require_verified": false,
    "require_integrity": false,
    "deny_providers": [],
    "deny_source_ids": []
  },
  "selected_profile": "default",
  "recipes": [],
  "webhooks": []
}
```



### `GET /api/v1/run/health`

Returns the standardized 9-field health response. Unauthenticated. Always returns HTTP 200 with `application/json` when the service is up.

This endpoint takes no parameters.



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/health"
```


```ts
await client.run.healthCheck()
```


```json
{
  "status": "ok",
  "service": "hoody-run",
  "built": "2026-01-15T08:30:00Z",
  "started": "2026-01-15T08:31:42Z",
  "memory": {
    "rss": 25165824
  },
  "fds": 12,
  "pid": 1234,
  "ip": "127.0.0.1",
  "userAgent": "hoody-cli/1.0"
}
```



## API documentation

### `GET /api/v1/run/openapi.json`

Returns the OpenAPI 3.0.3 specification for this API in JSON format. Converted from the canonical YAML source.

This endpoint takes no parameters.



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/openapi.json"
```


```ts
await client.run.documentation.getOpenApiJson()
```


```json
{
  "openapi": "3.0.3",
  "info": {
    "title": "hoody-run API",
    "version": "1.0.0"
  },
  "paths": {
    "/api/v1/run/resolve": {
      "get": { "summary": "Resolve an application and return exact shell command" }
    }
  }
}
```



### `GET /api/v1/run/openapi.yaml`

Returns the OpenAPI 3.0.3 specification for this API in YAML format.

This endpoint takes no parameters.



```bash
curl -sS "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.icu/api/v1/run/openapi.yaml"
```


```ts
await client.run.documentation.getOpenApiYaml()
```


```yaml
openapi: 3.0.3
info:
  title: hoody-run API
  version: 1.0.0
paths:
  /api/v1/run/resolve:
    get:
      summary: Resolve an application and return exact shell command
```