import_discovered
const url = 'https://example.com/api/v1/discovery/import';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"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"}]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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" } ] }'Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”Import body: the selected devices to create as nodes.
object
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.
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.
One discovered device the operator chose to add.
object
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.
Maker/model pre-filled from discovery’s sysDescr classification (editable before import).
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" } ]}Responses
Section titled “Responses”Nodes created, in one transaction
How many nodes an import created, and — when filing by IP range was asked for — how.
object
Present when the request set file_by_prefix, or — on a scan import — named a folder for
any row itself. Absent otherwise: an endpoint promotion without file_by_prefix (a
group_id alone does not bring it), and every scan import that decided nothing per row.
⚠️ skip_serializing_if rather than a zero-filled struct: an import that filed nothing by
range would read as one that filed zero rows, and with the field absent the wire shape is
what every existing client already parses. The endpoint import (ADR-179 Inc.8) fills it
only when it was asked to file by range, and never counts chosen. It counts the rows that were created — a skipped row was filed nowhere.
With file_by_prefix set, created == matched + ambiguous + unmatched + chosen; with it
off, only chosen is counted and the rest went to group_id.
object
Two or more folders claimed it at the same prefix length; filed into the fallback.
The operator named this row’s folder themselves, so no rule was applied to it
(ADR-131 decision 11). Counted apart from the three above because it is not an outcome of the
match — reporting it as matched would credit the rule with a choice a person made.
Filed into the one folder whose range contains the address.
No folder’s range contained it; filed into the fallback.
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.
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
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
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}No valid bearer token
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
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
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
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
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}Skeleton mode has no write side
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
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}