Skip to content

The scans this core is holding, newest first.

GET
/api/v1/discovery/scans
curl --request GET \
--url https://example.com/api/v1/discovery/scans \
--header 'Authorization: Bearer <token>'

Exists so a sweep survives leaving the page: the scan id used to live only in the browser tab that started it, so navigating away lost a sweep the poller was still running. What bounds this list is the runner’s retention (finished scans age out, running ones are never capped away), not the caller’s limit.

A restarted core answers an empty list even while a poller is still sweeping — scan state is in memory by decision (ADR-068). That is why the WebUI must render “this core does not know that scan” for a 404 on a remembered id, rather than an empty page.

ManageConfig and 503 in skeleton mode, matching GET /discovery/scan/{id} rather than the candidates queue: this list is the Discovery screen’s own state, and a screen that cannot scan has no scans to list. (The candidates queue answers 200 [] instead because it backs a dashboard widget, where an error would break a page that otherwise works.)

limit
integer

Retained scans, newest first

Media typeapplication/json
Array<object>

One row of the scan list — everything [ScanStatus] has except the candidates themselves.

The omission is the point: 20 retained scans of up to 4096 candidates each would make listing them far more expensive than the question deserves. A caller that wants a scan’s candidates asks for that scan.

object
candidate_count
required

How many devices answered so far.

integer format: int32
pool

The pool the job was actually published to; null for the global subject.

string | null
probed
required
integer format: int32
scan_id
required
string format: uuid
started_at
required
string
state
required

Where a scan is in its life (ADR-068).

Deliberately has no Unknown variant, unlike the enums built by stored_enum::token_enum!: those degrade a token a newer writer put in a database column, and this value is never read back from storage — it only ever travels outward. The corresponding defensiveness lives on the TypeScript side, which narrows the wire value and renders anything it does not recognise neutrally. ⚠️ Rendering an unrecognised state as a failure is a real bug this codebase has already shipped once (report runs, painted red by a switch with a default: arm).

string
Allowed values: queued running cancelling cancelled done
total
required
integer format: int32
updated_at
required
string
Example
[
{
"state": "queued"
}
]

No valid bearer token

Media typeapplication/json

The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies undescribed — a client that has to guess the failure shape ends up parsing the success shape and reading undefined.

object
error
required
object
code
required

Stable machine-readable code. Clients branch on this, never on the message.

string
message
required

Operator-facing sentence. Safe to display; never carries an internal error’s own text.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example"
}
}

Role lacks ManageConfig

Media typeapplication/json

The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies undescribed — a client that has to guess the failure shape ends up parsing the success shape and reading undefined.

object
error
required
object
code
required

Stable machine-readable code. Clients branch on this, never on the message.

string
message
required

Operator-facing sentence. Safe to display; never carries an internal error’s own text.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example"
}
}

Skeleton mode has no discovery runner

Media typeapplication/json

The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies undescribed — a client that has to guess the failure shape ends up parsing the success shape and reading undefined.

object
error
required
object
code
required

Stable machine-readable code. Clients branch on this, never on the message.

string
message
required

Operator-facing sentence. Safe to display; never carries an internal error’s own text.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example"
}
}