Stacks API
Combine config templates into deployable stacks, create instances with per-device overrides, deploy to devices, and roll back via the REST API.
Overview
Config stacks, instances, and deployments are part of the NetStacks Controller. All endpoints below are served by the Controller API under the /api/config prefix and require an authenticated operator token.
The Stacks API lets you combine multiple configuration templates into a single deployable unit called a stack. A stack lists one or more services — each service binds a config template to a render order and a set of variables. You then create a deployment (directly, or from a saved instance) to render every service per device and push the result to your network.
Use stacks to deploy consistent configuration across your network: a “DC1 Edge Router” stack might combine VLAN, BGP, and ACL templates and deploy them to every edge router with device-specific values for router IDs, neighbor IPs, and ASNs.
All examples below assume a base URL of https://netstacks.example.net and a bearer token from the Authentication API.
Resource Model
There are three related resources:
Stack—/api/config/stacks- A reusable blueprint. It has a
name,description, anatomicflag, and aservicesarray. Each service references one config template bytemplate_idwith a renderorderand per-servicevariables. Instance—/api/config/instances- A saved, not-yet-deployed binding of a stack to target devices and variable values. Instances are a top-level resource (not nested under a stack); the stack is referenced by
stack_idin the body. Deploying an instance creates a deployment. Deployment—/api/config/deployments- An execution record. It tracks overall
status, per-device results, logs, and config backups, and is the resource you poll for progress and call to roll back.
The services array
A stack’s services entries each have these fields:
| Field | Type | Description |
|---|---|---|
template_id | string (UUID) | The config template this service renders. |
order | integer | Render/apply order within the stack (ascending). |
variables | object | Default variable values for this service’s template. |
api_variables | object | Optional. Variables resolved from an API resource at deploy time (resource_id, method, path, json_path). |
The atomic flag
atomic defaults to true. When set, the deployment treats the stack as a unit during execution; see your deployment results and logs for per-device outcomes.
Endpoint Reference
All paths are relative to the Controller’s API root.
Stacks
GET /api/config/stacks # list stacks
POST /api/config/stacks # create a stack
GET /api/config/stacks/:id # get a stack
PUT /api/config/stacks/:id # update a stack
DELETE /api/config/stacks/:id # delete a stack
POST /api/config/stacks/:id/render # dry-run render (no deploy)
POST /api/config/stacks/:id/resolve-variables# resolve API variablesInstances
GET /api/config/instances # list instances
POST /api/config/instances # create an instance
GET /api/config/instances/:id # get an instance
PUT /api/config/instances/:id # update an instance
DELETE /api/config/instances/:id # delete an instance
POST /api/config/instances/:id/deploy # deploy -> creates a deployment
POST /api/config/instances/:id/deploy-with-mop # deploy via a MOP planDeployments
POST /api/config/deployments # create & execute a deployment
GET /api/config/deployments # list (filter ?stack_id=)
GET /api/config/deployments/:id # detail incl. per-device results
GET /api/config/deployments/:id/logs # deployment logs (?device_id=)
POST /api/config/deployments/:id/rollback # roll back a deploymentWorking with Stacks
Create a stack
A create request needs name and a services array. description is optional and atomic defaults to true.
curl -X POST https://netstacks.example.net/api/config/stacks \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "DC1 Edge Router Config",
"description": "Standard edge router config: VLANs + BGP + ACLs",
"atomic": true,
"services": [
{
"template_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"order": 0,
"variables": { "site": "dc1" }
},
{
"template_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"order": 1,
"variables": { "local_asn": "65001" }
},
{
"template_id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"order": 2,
"variables": { "acl_name": "EDGE-INBOUND" }
}
]
}'
# Response (201 Created): the full ConfigStack, e.g.
# {
# "id": "d4e5f6a7-b8c9-0123-defa-456789012345",
# "name": "DC1 Edge Router Config",
# "atomic": true,
# "services": [ ... ],
# "shared": false,
# "created_at": "2026-03-10T16:00:00Z",
# "updated_at": "2026-03-10T16:00:00Z"
# }import requests
BASE = "https://netstacks.example.net/api/config"
headers = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."}
stack = requests.post(f"{BASE}/stacks", headers=headers, json={
"name": "DC1 Edge Router Config",
"description": "Standard edge router config: VLANs + BGP + ACLs",
"atomic": True,
"services": [
{"template_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "order": 0,
"variables": {"site": "dc1"}},
{"template_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012", "order": 1,
"variables": {"local_asn": "65001"}},
{"template_id": "c3d4e5f6-a7b8-9012-cdef-345678901234", "order": 2,
"variables": {"acl_name": "EDGE-INBOUND"}},
],
}).json()
print("Created stack:", stack["id"])Preview rendering (dry run)
POST /api/config/stacks/:id/render resolves the stack against a per-service config and returns rendered config jobs without touching any device. The variable_values body uses a per-service shape keyed by service index, where each entry carries devices, shared_vars, and device_vars.
curl -X POST https://netstacks.example.net/api/config/stacks/d4e5f6a7-.../render \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"variable_values": {
"0": {
"devices": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"],
"shared_vars": { "site": "dc1" },
"device_vars": {
"f47ac10b-58cc-4372-a567-0e02b2c3d479": { "vlan_id": "100" }
}
}
}
}'
# Response: { "jobs": [
# { "device_id": "...", "device_name": "core-rtr-01.dc1",
# "template_id": "...", "template_name": "vlan-base",
# "service_order": 0, "rendered_config": "vlan 100\n name ...",
# "target_path": "...", "operation": "merge", "config_format": "..." }
# ] }If a stack uses API-backed variables, call POST /api/config/stacks/:id/resolve-variables with a target_devices array (objects with device_id). The response returns resolved shared values and per_device values you can feed into a render or deployment.
Stack Instances
An instance saves a stack binding so you can review and deploy it later. Instances are created at the top-level /api/config/instances endpoint with stack_id in the body. The fields are stack_id, name, target_devices, variable_values, and device_overrides (the last three are JSON; they default to empty).
curl -X POST https://netstacks.example.net/api/config/instances \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"stack_id": "d4e5f6a7-b8c9-0123-defa-456789012345",
"name": "dc1-edge-deploy-2026-03",
"target_devices": [
"f47ac10b-58cc-4372-a567-0e02b2c3d479",
"e36bd20a-47bb-3261-9456-1d13a1b2c368"
],
"variable_values": { "site": "dc1", "local_asn": "65001" },
"device_overrides": {
"f47ac10b-58cc-4372-a567-0e02b2c3d479": { "router_id": "10.0.1.1", "vlan_id": "100" },
"e36bd20a-47bb-3261-9456-1d13a1b2c368": { "router_id": "10.0.2.1", "vlan_id": "200" }
}
}'
# Response (201 Created): the ConfigStackInstance with "id" and "state".Deploy an instance
POST /api/config/instances/:id/deploy reads the saved instance and creates a deployment from it. The response is a ConfigDeployment (status 201 Created); execution then runs in the background.
curl -X POST \
https://netstacks.example.net/api/config/instances/INSTANCE_ID/deploy \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Response (201 Created):
# {
# "id": "9f0e1d2c-...", # deployment id
# "stack_id": "d4e5f6a7-...",
# "name": "dc1-edge-deploy-2026-03",
# "status": "pending",
# "total_devices": 2,
# "succeeded_count": 0,
# "failed_count": 0,
# "created_at": "2026-03-10T16:05:00Z"
# }POST /api/config/instances/:id/deploy-with-mop turns the stack’s deployment procedure into a Method of Procedure plan. The optional body accepts control_mode and name. Use this when you want gated, step-by-step execution instead of a direct push.
Deployments & Rollback
Create a deployment directly
You can skip instances and create a deployment in one call. The body is a CreateDeployment: stack_id, name, target_devices (objects with device_id and optional credential_id), plus optional variable_values and device_overrides.
curl -X POST https://netstacks.example.net/api/config/deployments \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"stack_id": "d4e5f6a7-b8c9-0123-defa-456789012345",
"name": "dc1-edge-deploy-2026-03",
"target_devices": [
{ "device_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479" },
{ "device_id": "e36bd20a-47bb-3261-9456-1d13a1b2c368", "credential_id": "cred-uuid" }
],
"variable_values": {
"0": {
"devices": ["f47ac10b-58cc-4372-a567-0e02b2c3d479"],
"shared_vars": { "site": "dc1" },
"device_vars": {
"f47ac10b-58cc-4372-a567-0e02b2c3d479": { "router_id": "10.0.1.1" }
}
}
}
}'
# Response (201 Created): a ConfigDeployment with status "pending".Track progress
There is no separate status endpoint — poll the deployment detail at GET /api/config/deployments/:id. The response includes overall status, the counters total_devices / succeeded_count / failed_count, and a devices array with per-device results.
import requests, time
BASE = "https://netstacks.example.net/api/config"
headers = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."}
deployment_id = "9f0e1d2c-..." # from the create/deploy response
terminal = {"completed", "failed", "rolled_back"}
while True:
d = requests.get(f"{BASE}/deployments/{deployment_id}", headers=headers).json()
print(f"{d['status']}: {d['succeeded_count']}/{d['total_devices']} ok, "
f"{d['failed_count']} failed")
if d["status"] in terminal:
break
time.sleep(5)Deployment status moves through pending → in_progress → completed or failed (and rolled_back after a rollback). Each entry in the devices array has its own status (for example completed, failed, or rolled_back) and may carry an error_message and a backup_config_id.
Read logs
# All logs for a deployment
curl "https://netstacks.example.net/api/config/deployments/DEPLOY_ID/logs" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Filter to one device
curl "https://netstacks.example.net/api/config/deployments/DEPLOY_ID/logs?device_id=DEVICE_ID&limit=200" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Each log entry: { "level": "info|error", "message": "...",
# "device_id": "...", "created_at": "..." }Roll back
Rollback is first-class: POST /api/config/deployments/:id/rollback restores devices from the config backups captured before the deployment. The deployment must be in completed or failed status; otherwise the call returns 400 Bad Request.
curl -X POST \
https://netstacks.example.net/api/config/deployments/DEPLOY_ID/rollback \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Response: the updated ConfigDeployment.
# On success its status becomes "rolled_back".List deployment history
# All deployments
curl "https://netstacks.example.net/api/config/deployments" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Filter to one stack's deployments
curl "https://netstacks.example.net/api/config/deployments?stack_id=STACK_ID&limit=50" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Questions & Answers
- What is the correct base path for stacks?
- Stacks live under the config router:
/api/config/stacks. Instances and deployments are separate top-level resources at/api/config/instancesand/api/config/deployments. There is no/api/stackspath. - How do I define which templates a stack includes?
- Use the
servicesarray. Each service has atemplate_id, anorder, and avariablesobject. There is notemplate_idsfield. - How do I deploy a stack to multiple devices?
- Either create a deployment directly at
POST /api/config/deploymentswith atarget_devicesarray, or save an instance and callPOST /api/config/instances/:id/deploy. Both run the deployment in the background. - How do I set per-device variable overrides?
- Provide a
device_overridesobject keyed by device UUID, or use the per-servicedevice_varsmap insidevariable_values. Device-specific values take priority over shared stack values. - How do I check deployment progress?
- Poll
GET /api/config/deployments/:idfor overallstatus, the counters (total_devices,succeeded_count,failed_count), and per-device results. For detail, readGET /api/config/deployments/:id/logs. - What are the deployment statuses?
pending→in_progress→completedorfailed, plusrolled_backafter a successful rollback.- Can I roll back a failed deployment?
- Yes. Call
POST /api/config/deployments/:id/rollback. NetStacks backs up each device before applying changes and restores from those backups. Onlycompletedorfaileddeployments can be rolled back. - Can I preview the rendered config before deploying?
- Yes.
POST /api/config/stacks/:id/renderreturns rendered config jobs per device without pushing anything.
Troubleshooting
404 on /api/stacks
The resource is namespaced under config. Use /api/config/stacks, /api/config/instances, and /api/config/deployments.
Deployment stays in “pending” or fails fast
Execution runs as a background task after the 201 Created response. If it does not progress, read GET /api/config/deployments/:id/logs. Common causes are a template render error (a required variable missing from variables, variable_values, or device_overrides) or no resolvable target devices.
Device unreachable during deploy
The Controller cannot reach the device. Verify the device address, port, and credentials. Supply a credential_id on the target device if the stack should use a specific credential. See the Devices API.
Variable override not applied
Device-specific values (device_overrides / device_vars) take priority over shared values, which take priority over a service’s template defaults. Ensure the variable name matches the template variable exactly (case-sensitive).
Rollback rejected with 400
Rollback only applies to deployments in completed or failed status. A deployment still in_progress cannot be rolled back — wait for it to reach a terminal status first.
Related Features
- Templates API — Create the config templates that stack services reference.
- Devices API — Manage the devices that deployments target.
- API Authentication — Obtain operator tokens for API access.
- Error Codes — Reference for deployment and validation errors.
- Creating Stacks — UI guide for building stacks.
- Stack Instances — Saving and managing instances.
- Variable Overrides — How shared and per-device values resolve.
- Deployment History — Reviewing past deployments and rollbacks.