Skip to content

set_node_bindings

PUT
/api/v1/nodes/{node_id}/bindings
curl --request PUT \
--url https://example.com/api/v1/nodes/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/bindings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "credential_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "model": "example", "name": "example", "notes": "example", "pool": "example", "profile_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "profile_locked": true, "tags": [ "example" ], "tags_excluded": [ "example" ], "vendor": "example" }'
node_id
required
string format: uuid

Node id

Media typeapplication/json

One “Edit node” save: the node’s own name and note, its profile + bound credential and descriptive maker/model, and optionally a move to a different poll-pool.

🚨 Two different readings of “absent” live in this one body, and the split is deliberate. profile_id/credential_id/vendor/model are replaced: the node-edit UI loads the current values and resends them, so omitting one CLEARS it. pool/name/notes are three-state: omitting one LEAVES IT ALONE.

The asymmetry is not history, it is what the columns can survive. A blanked vendor/model is refilled from the next poll’s sysDescr (fill_node_identity_batch). Nothing refills a name or a note — so an older client that has never heard of those fields must not be able to destroy them by saving a form (ADR-135 decision 4; the trap 026e1ef8 paid for).

object
credential_id
string | null format: uuid
model
string | null
name

The node’s display name. Absent = leave it unchanged; otherwise rename the node. "" (or whitespace) is 400, not a clear — nodes.name is NOT NULL.

Renaming is safe for everything downstream: Node::id is the identity every store is keyed by, so metric series and alert history follow the node across a rename. Nothing else in the product writes this column — no poll, no sweep, no classifier — so a hand-edited name stays.

string | null
notes

The operator’s free-text note about this node. Absent = leave it unchanged; "" (or whitespace) = clear it; otherwise set it. At most 2,000 characters.

Spelled notes rather than description on purpose: in this product “Description” already means what a device reports about one of its ports (interfaces.if_alias).

string | null
pool

Poll-pool assignment (ADR-009). Absent = leave the pool unchanged; "" (or whitespace) = clear it to the default pool; otherwise move the node to that pool (validated as a NATS-subject-safe token). See [validate_pool_update].

string | null
profile_id
string | null format: uuid
profile_locked

Whether a person fixed this node’s profile, so Nodes ▸ Reclassify never offers to change it (ADR-140). Absent = leave it unchanged. The edit dialog sets it when the operator picks a different profile, and an older client must not unlock a node by saving a form.

boolean | null
tags

The node’s own labels. Absent = leave them unchanged; otherwise the whole list is replaced by what is sent — the edit dialog shows every label and resends every label, so a replacement is what the operator sees. [] clears them all.

Free-form strings of at most 64 characters, at most 32 of them; trimmed, de-duplicated and sorted by the server. A label is what a ScopeLevel::Group threshold and a WindowScope::Group maintenance window match on.

⚠️ This sets only what the node itself carries. Labels it inherits from its folder are changed on the folder (PUT /api/v1/node-groups/{id}/tags) or refused here with tags_excluded.

⚠️ To add one label to many nodes without knowing what else they carry, use POST /api/v1/nodes/tags, which merges. Replacing from a bulk caller would silently wipe labels it never saw.

Array<string> | null
tags_excluded

Labels this node refuses to inherit from its folder chain. Same three-state and whole-value contract as tags; [] clears the refusals.

⚠️ Not length- or character-checked, unlike tags. A label already in the database may predate those rules, and refusing to let one be excluded because it is too long would make exactly the wrong labels unrefusable. Only trimmed and de-duplicated.

An entry naming a label no folder currently supplies is kept, not dropped: if an ancestor re-adds it later, the refusal still holds.

Array<string> | null
vendor
string | null
Examplegenerated
{
"credential_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"model": "example",
"name": "example",
"notes": "example",
"pool": "example",
"profile_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"profile_locked": true,
"tags": [
"example"
],
"tags_excluded": [
"example"
],
"vendor": "example"
}

Bindings updated

Illegal pool name, an empty name, or a note over 2,000 characters

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

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 such node, or the node is outside the caller’s scope

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

This deployment has no write side (skeleton mode)

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