set_node_bindings
const url = 'https://example.com/api/v1/nodes/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/bindings';const options = { method: 'PUT', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"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"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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" }'Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Node id
Request Bodyrequired
Section titled “Request Bodyrequired”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
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.
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).
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].
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.
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.
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.
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"}Responses
Section titled “Responses”Bindings updated
Illegal pool name, an empty name, or a note over 2,000 characters
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
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 such node, or the node is outside the caller’s scope
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" }}This deployment has no write side (skeleton mode)
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" }}