Operational knowledge from automating NetBox device management with h-cli. Covers device creation workflows, cable management gotchas, and critical verification patterns.
HTTP 200 OK does not mean the operation succeeded.
NetBox can return 200 and silently fail. Always check actual state after every write operation.
# Wrong — trusting the status code
curl -X POST ... "$NETBOX_URL/api/dcim/cables/"
echo "Done" # Maybe. Maybe not.
# Right — verify actual state
curl -X POST ... "$NETBOX_URL/api/dcim/cables/"
CABLE_COUNT=$(curl -s -H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/cables/" | python3 -c "import sys,json; print(json.load(sys.stdin)['count'])")
echo "Cables: $CABLE_COUNT" # Now you know.This applies to devices, interfaces, IPs, and especially cables.
| Object | Endpoint | Example |
|---|---|---|
| Manufacturer | /api/dcim/manufacturers/ |
Juniper (slug: juniper) |
| Device Type | /api/dcim/device-types/ |
vMX (slug: vmx) |
| Device Role | /api/dcim/device-roles/ |
Router (slug: router) |
| Site | /api/dcim/sites/ |
LAB (slug: lab) |
{
"name": "R1",
"device_type": {"slug": "vmx"},
"role": {"slug": "router"},
"site": {"slug": "lab"},
"status": "active",
"serial": "VM6975C09948"
}| Interface | Type | Purpose |
|---|---|---|
| fxp0 | 1000base-t | Management |
| ge-0/0/0 through ge-0/0/9 | 1000base-t | Data plane |
| lo0 | virtual | Loopback |
12 interfaces total. If a device-type template exists in NetBox, interfaces are auto-created. Otherwise, create them manually via POST /api/dcim/interfaces/.
# Create device
curl -s -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"R1","device_type":{"slug":"vmx"},"role":{"slug":"router"},"site":{"slug":"lab"},"status":"active"}' \
"$NETBOX_URL/api/dcim/devices/"# Create IP address on an interface
curl -s -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"address":"10.0.0.1/30","assigned_object_type":"dcim.interface","assigned_object_id":INTERFACE_ID}' \
"$NETBOX_URL/api/ipam/ip-addresses/"
# Set as primary IP on device
curl -s -X PATCH \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"primary_ip4":IP_ADDRESS_ID}' \
"$NETBOX_URL/api/dcim/devices/DEVICE_ID/"curl -s -X DELETE \
-H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/devices/DEVICE_ID/"Deletion cascades to interfaces and IP assignments, but NOT cables (see below).
curl -s -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"a_terminations": [{"object_type": "dcim.interface", "object_id": INTERFACE_A_ID}],
"b_terminations": [{"object_type": "dcim.interface", "object_id": INTERFACE_B_ID}],
"status": "connected"
}' \
"$NETBOX_URL/api/dcim/cables/"When you delete a device, NetBox clears the cable terminations but does not delete the cable object itself. This leaves orphaned cables in the database.
Symptoms: Cable count doesn't match expectations. Recreating devices and cables fails because "orphaned" cables occupy slots.
Fix: Always clean up cables explicitly after deleting devices.
# List all cables
curl -s -H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/cables/" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for c in data['results']:
print(f'Cable {c[\"id\"]}: {c[\"a_terminations\"]} <-> {c[\"b_terminations\"]}')"
# Delete orphaned cable
curl -s -X DELETE \
-H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/cables/CABLE_ID/"- Delete devices (cascades interfaces and IPs)
- List remaining cables
- Delete orphaned cables manually
- Verify: device count = 0, cable count = 0
"Add routers from EVE-NG to NetBox":
- Discover running routers in EVE-NG (console port discovery)
- For each router, connect via telnet and collect:
show chassis hardware→ serial numbershow version→ model, versionshow interfaces terse→ interface list and status
- Ensure prerequisites exist in NetBox (manufacturer, device type, role, site)
- Create devices with discovered serial numbers
- Create interfaces (or let device-type template handle it)
- If topology is known: create cables between interfaces
- Assign management IPs if applicable
- Verify everything — check device count, interface count, cable count
# Find device by name
curl -s -H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/devices/?name=R1"
# Find interface by device and name
curl -s -H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/interfaces/?device=R1&name=ge-0/0/0"NetBox supports bulk create/update/delete on most endpoints:
# Create multiple interfaces at once
curl -s -X POST \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '[
{"device":DEVICE_ID,"name":"ge-0/0/0","type":"1000base-t"},
{"device":DEVICE_ID,"name":"ge-0/0/1","type":"1000base-t"}
]' \
"$NETBOX_URL/api/dcim/interfaces/"NetBox paginates results (default 50 per page). For complete lists:
# Get total count
curl -s -H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/devices/" | python3 -c "import sys,json; print(json.load(sys.stdin)['count'])"
# Get all results (set high limit)
curl -s -H "Authorization: Token $TOKEN" \
"$NETBOX_URL/api/dcim/devices/?limit=1000"