Icon Packs
Icon packs are collections of SVG device icons, typically imported from Visio stencil (.vssx) files. Listing and viewing icons is available to all authenticated users. Import, update, and delete require admin role.
List Icon Packs
GET /api/v1/icon-packs
List all icon packs with icon counts. Auth: any authenticated user.
Response: 200 OK
[
{
"id": "uuid",
"name": "Cisco Network",
"builtin": false,
"brand_color": "#049FD9",
"created_by": "user-uuid",
"created_at": "2026-03-03T12:00:00Z",
"icon_count": 42
}
]
List Icons in Pack
GET /api/v1/icon-packs/{packID}/icons
List all icons in a pack. Returns metadata only (no svg_data) for performance. Auth: any authenticated user.
Response: 200 OK
[
{
"id": "uuid",
"pack_id": "pack-uuid",
"name": "Router",
"device_type": "router",
"width": 200,
"height": 200
}
]
Get Icon SVG
GET /api/v1/icon-packs/{packID}/icons/{iconID}/svg
Returns the raw SVG markup for a single icon. Auth: any authenticated user.
Response: 200 OK — Content-Type: image/svg+xml, Cache-Control: public, max-age=86400, immutable
The response body is raw SVG XML, not JSON. Suitable for direct use in <img src> or fetch().
Get Icon PNG
GET /api/v1/icon-packs/{packID}/icons/{iconID}/png
Returns the icon rendered to a PNG (server-side via rsvg-convert), used as the canvas background-image for imported packs — cytoscape clips SVG background-images at non-1 zoom (#2750). Rendered once and cached in-memory; auth: any authenticated user.
Response: 200 OK — Content-Type: image/png, Cache-Control: public, max-age=86400, immutable
Error responses:
404 Not Found -- Icon not found
Get Builtin Icon PNG
GET /api/v1/icon-packs/builtin/{pack}/icons/{deviceType}/png[?color=%23RRGGBB]
Builtin packs (default, industrial, osprey3d) live in the binary, not the database, so they have no icon id — this is their equivalent of the endpoint above. The path parameter is pack, not packID: params ending in ID are validated as UUIDs, and a builtin pack id is a name. Used as the canvas background-image for packs whose artwork is too complex to hand the browser as a vector: cytoscape re-rasterizes an SVG background-image at every zoom level, which on the Osprey 3D pucks (130-170 paths per icon) drops the canvas to single-digit fps. A 256px PNG is decoded once and reused at every zoom.
color applies the pack's area recoloring server-side (brand-color substitution or face tint, depending on the pack), so the canvas and the Visio export share one implementation. A device type the pack does not ship falls back to its router icon. Rendered once and cached in-memory (bounded); auth: any authenticated user.
Response: 200 OK — Content-Type: image/png, Cache-Control: public, max-age=86400, immutable
Error responses:
400 Bad Request -- color is not #RRGGBB (a free-form value would widen the render cache key space)
404 Not Found -- Not a builtin pack id
Import Icon Pack (Admin)
POST /api/v1/icon-packs/import
Import a Visio stencil (.vssx) as an icon pack. Admin-only.
Body size limit: 20 MB
Request body:
{
"name": "Cisco Network",
"data": "<base64-encoded .vssx file>"
}
| Field |
Type |
Required |
Description |
name |
string |
Yes |
Display name for the icon pack (max 255 chars) |
data |
string |
Yes |
Base64-encoded .vssx stencil file |
Response: 201 Created
{
"id": "uuid",
"name": "Cisco Network",
"builtin": false,
"brand_color": "#049FD9",
"created_by": "user-uuid",
"created_at": "2026-03-03T12:00:00Z",
"icon_count": 42,
"icons": [
{
"id": "uuid",
"pack_id": "pack-uuid",
"name": "Router",
"device_type": "router",
"width": 200,
"height": 200
}
]
}
Notes:
- The file must be a valid Visio stencil (.vssx) with master shapes. Regular .vsdx files are rejected.
- Each master shape is converted to SVG at 200x200px using
vsdx.MasterToSVG().
- Brand color is auto-detected from the first successful SVG conversion.
- Device types are auto-mapped by name heuristic (contains "router" →
router, "switch" → switch, etc.).
- EMF-based stencils (Cisco-style) require
emf2svg-conv on the server. If missing, those masters are skipped with a warning.
- Masters that fail SVG conversion are skipped (logged as warnings).
- Created via transaction: pack + all icons are inserted atomically.
- Audit log entry created with name and icon count.
Error responses:
400 Bad Request -- Invalid JSON, missing name/data, invalid base64, not a stencil, no icons converted
401 Unauthorized -- Missing or invalid access token
403 Forbidden -- Non-admin user
409 Conflict -- Duplicate pack name
413 Payload Too Large -- Request body exceeds 20 MB limit
Update Icon (Admin)
PUT /api/v1/icon-packs/{packID}/icons/{iconID}
Update an icon's device_type mapping. Admin-only.
Request body:
{
"device_type": "switch"
}
Response: 200 OK — Updated icon (without svg_data)
Error responses:
400 Bad Request -- Invalid JSON
404 Not Found -- Icon not found
Duplicate Icon (Admin)
POST /api/v1/icon-packs/{packID}/icons/{iconID}/duplicate
Copy an icon (same SVG and dimensions) into a new, unassigned icon in the same pack, so one artwork can be assigned to multiple roles (e.g. ABR and ASBR). The copy is named "<name> (copy)" (or "<name> (copy 2)", … if that name is taken) and starts with no device_type. Admin-only. No request body.
Response: 201 Created — New icon (without svg_data)
Error responses:
404 Not Found -- Source icon not found
Delete Icon Pack (Admin)
DELETE /api/v1/icon-packs/{packID}
Delete an icon pack and all its icons. Built-in packs cannot be deleted. Admin-only.
Response: 204 No Content
Error responses:
403 Forbidden -- Cannot delete builtin icon pack
404 Not Found -- Pack not found