Run: Jobs
Section titled “Run: Jobs”The Jobs API queues candidate searches as background jobs and lets you discover, inspect, and cancel them. Use POST /api/v1/run/search/jobs to start an async search and receive a job handle, then poll GET /api/v1/run/jobs/{job_id} to retrieve results, or list recently created jobs with GET /api/v1/run/jobs. Use POST /api/v1/run/jobs/{job_id}/cancel to stop a queued or running search job. The status endpoint supports long-polling with wait=done to block until the job completes or a timeout is reached.
List background jobs
Section titled “List background jobs”List the async background jobs the service still holds (search jobs for 10 minutes, source syncs for 1 hour after their last status change), newest first. Each entry omits result; read a finished search job’s result with getJobStatus.
GET /api/v1/run/jobs
Section titled “GET /api/v1/run/jobs”Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
kind | query | string | No | Only jobs of this kind. One of search-resolve, source-sync |
status | query | string | No | Only jobs in this status. One of queued, running, done, error, cancelled |
Example request
Section titled “Example request”curl -X GET "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/jobs?kind=search-resolve&status=running" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const { data: jobs } = await client.run.jobs.list({ kind: "search-resolve", status: "running"});Responses
Section titled “Responses”Live jobs, newest first, without results.
[ { "job_id": "550e8400-e29b-41d4-a716-446655440000", "kind": "search-resolve", "status": "running", "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T10:30:02Z" }, { "job_id": "660e8400-e29b-41d4-a716-446655440001", "kind": "source-sync", "status": "queued", "created_at": "2025-01-15T10:29:50Z", "updated_at": "2025-01-15T10:29:50Z" }]The provided kind or status value is not recognised.
{ "error": "unknown kind value", "code": "INVALID_QUERY", "status": 400}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_QUERY | Invalid query parameter | kind or status is not one of its listed values | Use a value from the parameter’s enum |
Create search job
Section titled “Create search job”Queue a candidate search in the background and return a job handle that can be polled through the jobs endpoint.
POST /api/v1/run/search/jobs
Section titled “POST /api/v1/run/search/jobs”This endpoint takes no parameters.
Request body
Section titled “Request body”The request body is a Selector object. Only app is required; all other fields are optional filters and output controls.
| Name | Type | Required | Description |
|---|---|---|---|
app | string | Yes | Primary name query (aliases q, name) |
os | string | No | Target app runtime OS. One of linux, windows, any |
kind | string | No | App kind. One of gui, cli, any |
source | array of string | No | Source kind filter (repeatable). Empty means no filter (default []) |
arch | string | No | CPU architecture. One of amd64, arm64, any |
tags | array of string | No | Free-form selector tags (default []). Accepted and passed through to sources and recipe templates; they do not filter or rank candidates |
profile | string | No | Named profile for default preferences |
channel | string | No | Release channel hint (e.g. stable, beta, nightly) |
version | string | No | Exact version or constraint (provider-defined) |
variant | string | No | Provider-defined variant (e.g. portable, headless) |
publisher | string | No | Publisher hint for curated registries |
repo | string | No | Repository hint (e.g. owner/name for GitHub sources) |
release | string | No | Release hint (e.g. tag for release-based sources) |
asset | string | No | Desired asset name or pattern |
pick | string | No | Selection mode. One of ask, first, index, id |
pick_index | integer | No | Candidate index (required when pick=index) |
candidate_id | string | No | Specific candidate ID (required when pick=id) |
set_id | string | No | Bind pick to a specific candidate set to avoid races |
terminal_id | integer | No | Terminal session ID for execution (default 1) |
display | string | No | X11 DISPLAY number (defaults to terminal_id) |
origin | string | No | Origin identifier for observability propagation |
format | string | No | Output format override. One of json, html. Takes precedence over Accept header |
dry_run | boolean | No | Force command-only output. hoody-run never executes, so this is always in effect |
print_curl | string | No | Curl command generation mode. Currently only hoody-run |
limit | integer | No | Maximum candidates to return (default 25) |
Example request
Section titled “Example request”curl -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/search/jobs" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "app": "firefox", "os": "linux", "kind": "gui" }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const { data: job } = await client.run.jobs.createSearch({ app: "firefox", os: "linux", kind: "gui"});Responses
Section titled “Responses”Search job accepted. The response includes a job_id that can be polled through the jobs endpoint.
{ "job_id": "550e8400-e29b-41d4-a716-446655440000", "kind": "search-resolve", "status": "queued", "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T10:30:00Z"}The request body failed validation (for example, a missing app, an unparsable selector field or an unknown profile).
{ "error": "missing app", "code": "MISSING_APP", "status": 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 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 profiles.list or config.get |
The job could not be started.
{ "error": "search backend unavailable", "code": "JOB_START_FAILED", "status": 503}| Error Code | Title | Description | Resolution |
|---|---|---|---|
JOB_START_FAILED | Job start failed | The background search job could not be started | Retry, or run the same search synchronously: GET /api/v1/run/search (the synchronous search operation), or client.run.search() for its paged form |
Get job status
Section titled “Get job status”Retrieve the current status of an async background job. Supports long-polling with wait=done to block until the job completes or a timeout is reached.
GET /api/v1/run/jobs/{job_id}
Section titled “GET /api/v1/run/jobs/{job_id}”Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
job_id | path | string | Yes | Job identifier (UUID) |
wait | query | string | No | Set to done to long-poll until job completes |
timeout_ms | query | integer | No | Long-poll timeout in milliseconds (default 0, max 120000) |
Example request
Section titled “Example request”curl -X GET "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/jobs/550e8400-e29b-41d4-a716-446655440000?wait=done&timeout_ms=30000" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const { data: status } = await client.run.jobs.get( "550e8400-e29b-41d4-a716-446655440000", { wait: "done", timeout_ms: 30000 });Responses
Section titled “Responses”Job status retrieved successfully.
{ "job_id": "550e8400-e29b-41d4-a716-446655440000", "kind": "search-resolve", "status": "done", "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T10:30:05Z", "result_type": "search-response"}No job exists with the requested identifier.
{ "error": "job not found", "code": "JOB_NOT_FOUND", "status": 404}| Error Code | Title | Description | Resolution |
|---|---|---|---|
JOB_NOT_FOUND | Job not found | No job exists with the requested identifier | Use the job_id returned by jobs.createSearch, sources.sync or sources.syncAll, or find it with jobs.list |
Cancel a search job
Section titled “Cancel a search job”Stop a queued or running search job. The background search is aborted (provider lookups stop and any provider child process is killed) and the job moves to status cancelled, which is final. Source-sync jobs cannot be cancelled.
POST /api/v1/run/jobs/{job_id}/cancel
Section titled “POST /api/v1/run/jobs/{job_id}/cancel”Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
job_id | path | string | Yes | Job identifier (UUID) |
Example request
Section titled “Example request”curl -X POST "https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com/api/v1/run/jobs/550e8400-e29b-41d4-a716-446655440000/cancel" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-run-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
const { data: job } = await client.run.jobs.cancel( "550e8400-e29b-41d4-a716-446655440000");Responses
Section titled “Responses”The job has been stopped and moved to status cancelled.
{ "job_id": "550e8400-e29b-41d4-a716-446655440000", "kind": "search-resolve", "status": "cancelled", "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T10:30:02Z"}No job exists with the requested identifier.
{ "error": "job not found", "code": "JOB_NOT_FOUND", "status": 404}| Error Code | Title | Description | Resolution |
|---|---|---|---|
JOB_NOT_FOUND | Job not found | No job exists with the requested identifier | Use the job_id returned by jobs.createSearch, or find it with jobs.list |
The job cannot be cancelled in its current state.
{ "error": "job already finished", "code": "JOB_ALREADY_FINISHED", "status": 409}| Error Code | Title | Description | Resolution |
|---|---|---|---|
JOB_ALREADY_FINISHED | Job already finished | The job is already done, failed or cancelled | Read its outcome with jobs.get |
JOB_NOT_CANCELLABLE | Job cannot be cancelled | Only search jobs can be cancelled; source syncs run to completion | Wait for the sync with jobs.get |