コンテンツにスキップ

import_discovered

POST
/api/v1/discovery/import
curl --request POST \
--url https://example.com/api/v1/discovery/import \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "file_by_prefix": true, "group_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "nodes": [ { "address": "example", "credential_id": "example", "group_id": "example", "model": "example", "name": "example", "profile_id": "example", "vendor": "example" } ] }'
Media typeapplication/json

Import body: the selected devices to create as nodes.

object
file_by_prefix

File each device into the folder whose IP range contains its address, falling back to group_id for one no range covers — or that two folders claim equally well (ADR-131).

⚠️ This does not reverse ADR-100 decision 10. That decision refuses a per-row folder field, because fifty rows could then disagree and the screen would have to explain it. This is a rule for the whole request: no row carries a choice, every destination is derived by one rule from data the operator did not type, and the request still names exactly one operator-chosen folder. One request, one intent, one thing to explain.

#[serde(default)] so an N-1 client’s body means exactly what it meant before.

boolean
group_id

Inventory folder to file every imported node under (ADR-100 decision 10), or absent for the tree root — which is what every import did before this existed.

⚠️ One folder for the whole request, not one per node. A sweep is aimed at a site, so the folder is a property of the sweep; per-row would invite a UI that lets fifty rows disagree and then have to explain itself.

When file_by_prefix is set this is the fallback rather than the destination — still one folder, still a property of the request.

string | null format: uuid
nodes
required
Array<object>

One discovered device the operator chose to add.

object
address
required
string
credential_id
string | null
group_id

The folder this one device goes into, overriding both the IP-range rule and the request’s group_id (ADR-131 decision 11).

🚨 Three states, not two, and Option<Uuid> cannot carry them. Absent means “follow the rule”; an id means that folder; null means the operator chose the tree root, which is a destination like any other. With a plain Option<Uuid> serde maps absent and null to the same None, so a device deliberately sent to the root would silently be filed by range instead — a control that lies about what it does. deserialize_some keeps them apart.

⚠️ This is the per-row field ADR-100 decision 10 refused, and it is admitted under a condition. That decision’s objection was a UI in which fifty rows each carry an independent choice and the screen has to explain the result. Here a row’s destination still comes from one rule by default, and this is an override of it — so the screen explains itself by saying which rows the operator changed, and a row nobody touched is still the rule’s answer. Remove the default and the original objection applies again in full.

string | null
model
string | null
name
required
string
profile_id
string | null
vendor

Maker/model pre-filled from discovery’s sysDescr classification (editable before import).

string | null
Examplegenerated
{
"file_by_prefix": true,
"group_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"nodes": [
{
"address": "example",
"credential_id": "example",
"group_id": "example",
"model": "example",
"name": "example",
"profile_id": "example",
"vendor": "example"
}
]
}

Nodes created, in one transaction

Media typeapplication/json

How many nodes an import created, and — when filing by IP range was asked for — how.

object
created
required
integer format: int32
filed
One of:
null
skipped_existing
required

Rows not created because a device node already stands at that address — or because an earlier row of the same request has just put one there. created + skipped_existing is the number of rows the request carried. 0 when nothing was skipped.

A URL or DNS monitor at the same address does not count: those store a resolved address, and the device itself is still importable.

integer format: int32
Examplegenerated
{
"created": 1,
"filed": {
"ambiguous": 1,
"chosen": 1,
"matched": 1,
"unmatched": 1
},
"skipped_existing": 1
}

More nodes than one sweep can find, an unparseable address, an empty name, a binding id that is not a UUID, or a group_id no folder has

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"
}
}

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, or (out_of_scope) a folder-scoped caller left a row bound for the tree root, which it cannot see — name a folder; nothing is written

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 write side

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"
}
}