{
  "openapi": "3.0.3",
  "info": {
    "title": "Network Map API",
    "version": "1.0.0",
    "description": "The Network Map \u2014 topology sites and locations."
  },
  "servers": [
    {
      "url": "https://versus-demo.com"
    }
  ],
  "paths": {
    "/api/locations/": {
      "get": {
        "operationId": "locations_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "locations"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedLocationList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List locations"
      },
      "post": {
        "operationId": "locations_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "locations"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocationRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/LocationRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/LocationRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create location"
      }
    },
    "/api/locations/{id}/": {
      "get": {
        "operationId": "locations_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this location.",
            "required": true
          }
        ],
        "tags": [
          "locations"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve location"
      },
      "put": {
        "operationId": "locations_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this location.",
            "required": true
          }
        ],
        "tags": [
          "locations"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LocationRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/LocationRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/LocationRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update location"
      },
      "patch": {
        "operationId": "locations_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this location.",
            "required": true
          }
        ],
        "tags": [
          "locations"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedLocationRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedLocationRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedLocationRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update location"
      },
      "delete": {
        "operationId": "locations_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this location.",
            "required": true
          }
        ],
        "tags": [
          "locations"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete location"
      }
    },
    "/api/network-map/device/{id}/": {
      "get": {
        "operationId": "network_map_device_retrieve",
        "description": "GET /api/network-map/device/<pk>/\nReturns full routing table, interfaces (with VLAN), and dynamic routing\nsummary for one device \u00e2\u20ac\u201d used by the node detail panel.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "required": true
          }
        ],
        "tags": [
          "network-map"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Retrieve device"
      }
    },
    "/api/network-map/path-diagram/": {
      "post": {
        "operationId": "network_map_path_diagram_create",
        "description": "POST /api/network-map/path-diagram/\nBody: { \"source_ip\": \"10.20.10.50\", \"dest_ip\": \"10.80.30.20\" }\n\nRenders the discovered L3 path as a Visio-importable SVG vector diagram with\nper-device detail (interfaces/IPs, forwarding decision, routes, ARP/neighbor\ntables). Returns { svg, filename, hop_count }.",
        "tags": [
          "network-map"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create path diagram"
      }
    },
    "/api/network-map/positions/": {
      "post": {
        "operationId": "network_map_positions_create",
        "description": "POST /api/network-map/positions/\nBody: {\"positions\": {\"<device_id>\": {\"x\": <num>, \"y\": <num>}, ...}}\nPersists user-arranged Network Map node positions onto each device's\nextra_config['map_position'] so the hand-tuned layout survives reloads.",
        "tags": [
          "network-map"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create position"
      }
    },
    "/api/network-map/topology/": {
      "get": {
        "operationId": "network_map_topology_retrieve",
        "description": "GET /api/network-map/topology/\nReturns nodes (devices) and edges (CDP neighbors + shared-subnet links)\nsuitable for rendering in a graph UI.",
        "tags": [
          "network-map"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "List topologys"
      }
    },
    "/api/network-map/trace/": {
      "post": {
        "operationId": "network_map_trace_create",
        "description": "POST /api/network-map/trace/\nBody: { \"source_ip\": \"10.0.0.5\", \"dest_ip\": \"10.2.3.4\" }\n\nFinds which device owns the source IP (or is the closest gateway),\nthen simulates longest-prefix-match routing hop-by-hop until the\ndestination is reached or TTL expires. Returns ordered hop list.",
        "tags": [
          "network-map"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create trace"
      }
    },
    "/api/sites/": {
      "get": {
        "operationId": "sites_list",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "name": "ordering",
            "required": false,
            "in": "query",
            "description": "Which field to use when ordering the results.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "A page number within the paginated result set.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page_size",
            "required": false,
            "in": "query",
            "description": "Number of results to return per page.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "sites"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSiteList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List sites"
      },
      "post": {
        "operationId": "sites_create",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "tags": [
          "sites"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SiteRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SiteRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SiteRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create site"
      }
    },
    "/api/sites/{id}/": {
      "get": {
        "operationId": "sites_retrieve",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this site.",
            "required": true
          }
        ],
        "tags": [
          "sites"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve site"
      },
      "put": {
        "operationId": "sites_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this site.",
            "required": true
          }
        ],
        "tags": [
          "sites"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SiteRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SiteRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SiteRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update site"
      },
      "patch": {
        "operationId": "sites_partial_update",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this site.",
            "required": true
          }
        ],
        "tags": [
          "sites"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedSiteRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedSiteRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedSiteRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Site"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update site"
      },
      "delete": {
        "operationId": "sites_destroy",
        "description": "Deploy-safe base for making an existing DRF ModelViewSet tenant-isolated\n(VS-SPEC-TENANT-IDENTITY-001 \u00a714). Use as the FIRST base:\n\n    class DeviceViewSet(TenantScopedModelViewSet, viewsets.ModelViewSet):\n        queryset = Device.objects.select_related(...)\n        ...\n\nUnlike the strict TenantScopedViewSet pilot, this **degrades gracefully**:\nit is INERT unless the tenant_scoped alias is configured\n(TENANT_SCOPED_DATABASE_URL) AND the request carries a tenant_id claim.\nWhen inert it behaves exactly like a plain ModelViewSet \u2014 so the rollout\nlands additively and isolation switches on only once the non-superuser\nPostgres role exists (mirrors tenant_subdomain.py's \"inert unless\nconfigured\" contract). When active: reads route through the RLS-enforcing\nconnection and tenant_id is stamped server-side (never client input).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this site.",
            "required": true
          }
        ],
        "tags": [
          "sites"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete site"
      }
    }
  },
  "components": {
    "schemas": {
      "PaginatedSiteList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Site"
            }
          }
        }
      },
      "CollectorUpdatePolicyEnum": {
        "enum": [
          "manual",
          "auto",
          "pinned"
        ],
        "type": "string",
        "description": "* `manual` - Manual\n* `auto` - Automatic\n* `pinned` - Pinned"
      },
      "SiteKindEnum": {
        "enum": [
          "datacenter",
          "cloud_vpc",
          "branch",
          "lab"
        ],
        "type": "string",
        "description": "* `datacenter` - Datacenter\n* `cloud_vpc` - Cloud VPC\n* `branch` - Branch\n* `lab` - Lab"
      },
      "LocationRequest": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ]
      },
      "PatchedLocationRequest": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string"
          }
        }
      },
      "PaginatedLocationList": {
        "type": "object",
        "required": [
          "count",
          "results"
        ],
        "properties": {
          "count": {
            "type": "integer",
            "example": 123
          },
          "next": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=4"
          },
          "previous": {
            "type": "string",
            "nullable": true,
            "format": "uri",
            "example": "http://api.example.org/accounts/?page=2"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Location"
            }
          }
        }
      },
      "PatchedSiteRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "kind": {
            "$ref": "#/components/schemas/SiteKindEnum"
          },
          "collector_update_policy": {
            "$ref": "#/components/schemas/CollectorUpdatePolicyEnum"
          },
          "collector_maintenance_window": {
            "type": "string",
            "maxLength": 40
          },
          "collector_pinned_version": {
            "type": "string",
            "maxLength": 50
          }
        }
      },
      "Location": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "device_count": {
            "type": "string",
            "readOnly": true
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "description": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "device_count",
          "id",
          "name"
        ]
      },
      "SiteRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "kind": {
            "$ref": "#/components/schemas/SiteKindEnum"
          },
          "collector_update_policy": {
            "$ref": "#/components/schemas/CollectorUpdatePolicyEnum"
          },
          "collector_maintenance_window": {
            "type": "string",
            "maxLength": 40
          },
          "collector_pinned_version": {
            "type": "string",
            "maxLength": 50
          }
        },
        "required": [
          "name"
        ]
      },
      "Site": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "kind": {
            "$ref": "#/components/schemas/SiteKindEnum"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "collector_update_policy": {
            "$ref": "#/components/schemas/CollectorUpdatePolicyEnum"
          },
          "collector_maintenance_window": {
            "type": "string",
            "maxLength": 40
          },
          "collector_pinned_version": {
            "type": "string",
            "maxLength": 50
          }
        },
        "required": [
          "created_at",
          "id",
          "name"
        ]
      }
    }
  }
}