NetStacksNetStacks

Devices API

Full CRUD operations for managed network devices including filtering, bulk import, config backups, and credential testing.

Overview

The Devices API provides full CRUD operations for managing network devices in NetStacks. Use it to list, create, update, and delete devices, import devices in bulk via CSV or JSON, sync from NetBox, manage config backups, and test device credentials.

All device endpoints are nested under /api/devices and require authentication with the ManageDevices permission. See API Authentication to obtain a token.

Device Fields

Each device has: id (UUID), name, host (IP or hostname), port, device_type, manufacturer, model, platform, site, source, default_credential_id, created_at, and updated_at.

How It Works

The Devices API follows REST conventions. All endpoints return JSON and accept JSON request bodies. Device IDs are UUIDs assigned by the server on creation.

Endpoints

MethodPathDescription
GET/api/devicesList devices with filtering and pagination
POST/api/devicesCreate a new device
GET/api/devices/:idGet a single device by ID
PUT/api/devices/:idUpdate a device
DELETE/api/devices/:idDelete a device
POST/api/devices/import/csvBulk import from CSV
POST/api/devices/import/jsonBulk import from JSON
POST/api/devices/bulk/backupTrigger bulk config backup
POST/api/devices/bulk/test-credentialsTest credentials for multiple devices

Query Parameters for Listing

  • limit (int) — Items per page, default 50, max 100
  • offset (int) — Pagination offset, default 0
  • device_type (string) — Filter by device type (e.g., cisco_ios)
  • site (string) — Filter by site name
  • source (string) — Filter by source (manual, netbox, csv)

Step-by-Step Guide

1. Authenticate

Obtain a JWT token from the Authentication API. Include it in all device requests as Authorization: Bearer <token>.

2. List Devices with Filtering

Call GET /api/devices with optional query parameters to filter by device type, site, or source. Results are paginated.

3. Get a Single Device

Call GET /api/devices/:id with the device UUID to retrieve full device details including backup status.

4. Create a Device

Send a POST to /api/devices with the device name, host, port, device type, and optional credential ID.

5. Update a Device

Send a PUT to /api/devices/:id with the fields you want to change.

6. Delete a Device

Send a DELETE to /api/devices/:id. Returns 204 No Content on success.

7. Bulk Import

Use POST /api/devices/import/json to import multiple devices at once, or /import/csv for CSV files.

Code Examples

Create a Device

create-device.shbash
curl -X POST https://netstacks.example.net/api/devices \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "core-rtr-01.dc1.example.net",
    "host": "10.0.1.1",
    "port": 22,
    "device_type": "cisco_ios",
    "manufacturer": "Cisco",
    "model": "ISR 4451-X",
    "platform": "ios-xe",
    "site": "dc1-east",
    "default_credential_id": "c8a7b6d5-e4f3-2a1b-9c8d-7e6f5a4b3c2d"
  }'

# Response (201 Created):
# {
#   "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
#   "org_id": "a1b2c3d4-0000-0000-0000-000000000000",
#   "name": "core-rtr-01.dc1.example.net",
#   "host": "10.0.1.1",
#   "port": 22,
#   "device_type": "cisco_ios",
#   "manufacturer": "Cisco",
#   "model": "ISR 4451-X",
#   "platform": "ios-xe",
#   "site": "dc1-east",
#   "source": "manual",
#   "default_credential_id": "c8a7b6d5-e4f3-2a1b-9c8d-7e6f5a4b3c2d",
#   "created_at": "2026-03-10T14:30:00Z",
#   "updated_at": "2026-03-10T14:30:00Z"
# }
create-device.pypython
import requests

base_url = "https://netstacks.example.net/api"
headers = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs..."}

# Create a device
device = requests.post(f"{base_url}/devices", headers=headers, json={
    "name": "core-rtr-01.dc1.example.net",
    "host": "10.0.1.1",
    "port": 22,
    "device_type": "cisco_ios",
    "manufacturer": "Cisco",
    "model": "ISR 4451-X",
    "platform": "ios-xe",
    "site": "dc1-east",
    "default_credential_id": "c8a7b6d5-e4f3-2a1b-9c8d-7e6f5a4b3c2d"
})
device.raise_for_status()
print(f"Created device: {device.json()['id']}")
create-device.tstypescript
const BASE_URL = "https://netstacks.example.net/api";
const headers = {
  Authorization: "Bearer eyJhbGciOiJIUzI1NiIs...",
  "Content-Type": "application/json",
};

// Create a device
const resp = await fetch(`${BASE_URL}/devices`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    name: "core-rtr-01.dc1.example.net",
    host: "10.0.1.1",
    port: 22,
    device_type: "cisco_ios",
    manufacturer: "Cisco",
    model: "ISR 4451-X",
    platform: "ios-xe",
    site: "dc1-east",
    default_credential_id: "c8a7b6d5-e4f3-2a1b-9c8d-7e6f5a4b3c2d",
  }),
});
const device = await resp.json();
console.log(`Created device: ${device.id}`);

List Devices with Filters

list-devices.shbash
# List Cisco IOS devices in dc1-east, first 25 results
curl "https://netstacks.example.net/api/devices?device_type=cisco_ios&site=dc1-east&limit=25" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# Response:
# {
#   "devices": [
#     {
#       "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
#       "name": "core-rtr-01.dc1.example.net",
#       "host": "10.0.1.1",
#       "port": 22,
#       "device_type": "cisco_ios",
#       "site": "dc1-east",
#       "source": "manual",
#       "last_backup_status": "success"
#     }
#   ],
#   "total": 42,
#   "limit": 25,
#   "offset": 0
# }
list-devices.pypython
# List devices filtered by type and site
devices = requests.get(
    f"{base_url}/devices",
    headers=headers,
    params={"device_type": "cisco_ios", "site": "dc1-east", "limit": 25}
)
data = devices.json()
print(f"Found {data['total']} Cisco IOS devices in dc1-east")
for d in data["devices"]:
    print(f"  {d['name']} ({d['host']})")
list-devices.tstypescript
// List devices filtered by type and site
const params = new URLSearchParams({
  device_type: "cisco_ios",
  site: "dc1-east",
  limit: "25",
});
const listResp = await fetch(`${BASE_URL}/devices?${params}`, {
  headers: { Authorization: "Bearer eyJhbGciOiJIUzI1NiIs..." },
});
const list = await listResp.json();
console.log(`Found ${list.total} Cisco IOS devices in dc1-east`);
list.devices.forEach((d: any) => console.log(`  ${d.name} (${d.host})`));

Update a Device

update-device.shbash
curl -X PUT https://netstacks.example.net/api/devices/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"site": "dc2-west", "port": 2222}'
update-device.pypython
# Update device site and port
resp = requests.put(
    f"{base_url}/devices/f47ac10b-58cc-4372-a567-0e02b2c3d479",
    headers=headers,
    json={"site": "dc2-west", "port": 2222}
)
resp.raise_for_status()
update-device.tstypescript
await fetch(`${BASE_URL}/devices/f47ac10b-58cc-4372-a567-0e02b2c3d479`, {
  method: "PUT",
  headers,
  body: JSON.stringify({ site: "dc2-west", port: 2222 }),
});

Delete a Device

delete-device.shbash
curl -X DELETE https://netstacks.example.net/api/devices/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Response: 204 No Content

Bulk Import (JSON)

bulk-import.shbash
curl -X POST https://netstacks.example.net/api/devices/import/json \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '[
    {"name": "dist-sw-01.dc1", "host": "10.0.10.1", "port": 22, "device_type": "cisco_ios", "site": "dc1-east"},
    {"name": "dist-sw-02.dc1", "host": "10.0.10.2", "port": 22, "device_type": "cisco_ios", "site": "dc1-east"},
    {"name": "fw-01.dc1", "host": "10.0.20.1", "port": 22, "device_type": "paloalto_panos", "site": "dc1-east"}
  ]'

# Response:
# {
#   "imported": 3,
#   "failed": 0,
#   "errors": []
# }
bulk-import.pypython
# Bulk import devices from a list
devices_to_import = [
    {"name": "dist-sw-01.dc1", "host": "10.0.10.1", "port": 22, "device_type": "cisco_ios", "site": "dc1-east"},
    {"name": "dist-sw-02.dc1", "host": "10.0.10.2", "port": 22, "device_type": "cisco_ios", "site": "dc1-east"},
    {"name": "fw-01.dc1", "host": "10.0.20.1", "port": 22, "device_type": "paloalto_panos", "site": "dc1-east"},
]
resp = requests.post(f"{base_url}/devices/import/json", headers=headers, json=devices_to_import)
result = resp.json()
print(f"Imported {result['imported']} devices, {result['failed']} failed")

Questions & Answers

How do I list all devices?
Send a GET request to /api/devices. Results are paginated with limit and offset query parameters. The response includes a total count for pagination.
How do I filter devices by type or site?
Add query parameters: /api/devices?device_type=cisco_ios&site=dc1-east. Supported filters are device_type, site, and source.
How do I add a device via the API?
Send a POST request to /api/devices with a JSON body containing at minimum name, host, port, and device_type. The response returns the created device with its assigned UUID.
How do I bulk import devices?
Use POST /api/devices/import/json with an array of device objects, or POST /api/devices/import/csv with a CSV file upload. Both endpoints return a summary of imported and failed devices.
What device types are supported?
NetStacks supports many device types including cisco_ios, cisco_nxos, cisco_asa, juniper_junos, arista_eos, paloalto_panos, fortinet_fortios, linux, and more. The device type determines how NetStacks connects and interacts with the device.
How do I test device credentials?
Use POST /api/devices/bulk/test-credentials with an optional list of device_ids. If no IDs are provided, all devices are tested. The response includes per-device results with connection timing.

Troubleshooting

404 Device Not Found

The device UUID does not exist or belongs to a different organization. Double-check the ID and ensure you are authenticated with the correct account.

409 Conflict (Duplicate Name)

A device with the same name already exists in your organization. Device names must be unique. Choose a different name or update the existing device.

422 Validation Error

The request body is missing required fields or contains invalid values. Check that name, host, port, and device_type are all provided and valid. See Error Codes for field-level error details.

Credential Test Failing After Creation

If the device was created successfully but credential tests fail, verify the IP address and port are correct, the device is reachable from the Controller, and the assigned credential has the correct username/password or SSH key.

Bulk Import Partial Failures

The import response includes an errors array with details about which devices failed and why. Common causes include duplicate names and invalid device types.