{
  "openapi": "3.0.3",
  "info": {
    "title": "CI/CD & Automation API",
    "version": "1.0.0",
    "description": "CI/CD and infrastructure automation \u2014 pipelines, Git repositories, environments, Ansible, and naming conventions."
  },
  "servers": [
    {
      "url": "https://versus-demo.com"
    }
  ],
  "paths": {
    "/api/ansible-files/": {
      "get": {
        "operationId": "ansible_files_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": [
          {
            "in": "query",
            "name": "device_group",
            "schema": {
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "git_repo",
            "schema": {
              "type": "integer"
            }
          },
          {
            "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"
            }
          },
          {
            "in": "query",
            "name": "virtual_server",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedAnsibleFileList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List ansible files"
      },
      "post": {
        "operationId": "ansible_files_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": [
          "ansible-files"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create ansible file"
      }
    },
    "/api/ansible-files/{id}/": {
      "get": {
        "operationId": "ansible_files_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 ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve ansible file"
      },
      "put": {
        "operationId": "ansible_files_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 ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update ansible file"
      },
      "patch": {
        "operationId": "ansible_files_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 ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedAnsibleFileRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedAnsibleFileRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedAnsibleFileRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update ansible file"
      },
      "delete": {
        "operationId": "ansible_files_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 ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete ansible file"
      }
    },
    "/api/ansible-files/{id}/content/": {
      "get": {
        "operationId": "ansible_files_content_retrieve",
        "description": "Return raw YML content of the file. Site-bound repos read it off the\ncollector (the checkout lives on-prem); cloud repos read local disk.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Content: ansible file"
      }
    },
    "/api/ansible-files/{id}/push/": {
      "post": {
        "operationId": "ansible_files_push_create",
        "description": "Git push this file.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Push: ansible file"
      }
    },
    "/api/ansible-files/{id}/run_workflow/": {
      "post": {
        "operationId": "ansible_files_run_workflow_create",
        "description": "Run a workflow against THIS matched file, taking it through the collector\ngit flow (pull \u2192 run/deploy \u2192 optional push). Gated on the file having a\nsource repo (matched-git). workflow_key auto-selected by the calling tab:\nf5.sitebuild | f5.run_ansible_playbook | f5.as3_deploy.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Run workflow: ansible file"
      }
    },
    "/api/ansible-files/{id}/save_content/": {
      "post": {
        "operationId": "ansible_files_save_content_create",
        "description": "Write content (does not push). Site-bound repos write on the collector.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this ansible file.",
            "required": true
          }
        ],
        "tags": [
          "ansible-files"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Save content: ansible file"
      }
    },
    "/api/ansible-files/save_for_vs/": {
      "post": {
        "operationId": "ansible_files_save_for_vs_create",
        "description": "Create or overwrite a file for a VS in a git repo.\n\nfile_format: 'ansible' (default, .yml) | 'as3_json' (.as3.json) | 'as3_yaml' (.as3.yaml)\n\nIf git_repo is omitted the backend auto-detects which repo already\ncontains a file at {group}/{vs_name}{ext}. If none does and only one\nready repo exists it is chosen automatically. If multiple ready repos\nexist and no file is found, returns needs_selection=True with the repo\nlist so the frontend can prompt the user.",
        "tags": [
          "ansible-files"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AnsibleFileRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnsibleFile"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create save for v"
      }
    },
    "/api/cicd/apply-jobs/": {
      "get": {
        "operationId": "cicd_apply_jobs_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedApplyJobList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List apply jobs"
      }
    },
    "/api/cicd/apply-jobs/{id}/": {
      "get": {
        "operationId": "cicd_apply_jobs_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 apply job.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplyJob"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve apply job"
      }
    },
    "/api/cicd/apply-jobs/{id}/rollback/": {
      "post": {
        "operationId": "cicd_apply_jobs_rollback_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this apply job.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApplyJobRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/ApplyJobRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ApplyJobRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplyJob"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Rollback: apply job"
      }
    },
    "/api/cicd/changesets/": {
      "get": {
        "operationId": "cicd_changesets_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedChangeSetList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List changesets"
      }
    },
    "/api/cicd/changesets/{id}/": {
      "get": {
        "operationId": "cicd_changesets_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 change set.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChangeSet"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve changeset"
      }
    },
    "/api/cicd/connections/": {
      "get": {
        "operationId": "cicd_connections_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedCICDConnectionList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List connections"
      },
      "post": {
        "operationId": "cicd_connections_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": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDConnection"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create connection"
      }
    },
    "/api/cicd/connections/{id}/": {
      "get": {
        "operationId": "cicd_connections_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 cicd connection.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDConnection"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve connection"
      },
      "put": {
        "operationId": "cicd_connections_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 cicd connection.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDConnection"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update connection"
      },
      "patch": {
        "operationId": "cicd_connections_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 cicd connection.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedCICDConnectionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedCICDConnectionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedCICDConnectionRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDConnection"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update connection"
      },
      "delete": {
        "operationId": "cicd_connections_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 cicd connection.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete connection"
      }
    },
    "/api/cicd/connections/{id}/test/": {
      "post": {
        "operationId": "cicd_connections_test_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this cicd connection.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CICDConnectionRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDConnection"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Test: connection"
      }
    },
    "/api/cicd/gitops-bindings/": {
      "get": {
        "operationId": "cicd_gitops_bindings_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedGitOpsBindingList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List gitops bindings"
      },
      "post": {
        "operationId": "cicd_gitops_bindings_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": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitOpsBinding"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create gitops binding"
      }
    },
    "/api/cicd/gitops-bindings/{id}/": {
      "get": {
        "operationId": "cicd_gitops_bindings_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 git ops binding.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitOpsBinding"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve gitops binding"
      },
      "put": {
        "operationId": "cicd_gitops_bindings_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 git ops binding.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitOpsBinding"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update gitops binding"
      },
      "patch": {
        "operationId": "cicd_gitops_bindings_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 git ops binding.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedGitOpsBindingRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedGitOpsBindingRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedGitOpsBindingRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitOpsBinding"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update gitops binding"
      },
      "delete": {
        "operationId": "cicd_gitops_bindings_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 git ops binding.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete gitops binding"
      }
    },
    "/api/cicd/gitops-bindings/{id}/drift_check/": {
      "post": {
        "operationId": "cicd_gitops_bindings_drift_check_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this git ops binding.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/GitOpsBindingRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitOpsBinding"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Drift check: gitops binding"
      }
    },
    "/api/cicd/op/{verb}/": {
      "post": {
        "operationId": "cicd_op_create",
        "description": "POST /api/cicd/op/<verb>/ \u2014 validate / plan / apply / backup / verify",
        "parameters": [
          {
            "in": "path",
            "name": "verb",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "cookieAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create op"
      }
    },
    "/api/cicd/pipelines/": {
      "get": {
        "operationId": "cicd_pipelines_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedCICDPipelineList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List pipelines"
      },
      "post": {
        "operationId": "cicd_pipelines_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": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDPipeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create pipeline"
      }
    },
    "/api/cicd/pipelines/{id}/": {
      "get": {
        "operationId": "cicd_pipelines_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 cicd pipeline.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDPipeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve pipeline"
      },
      "put": {
        "operationId": "cicd_pipelines_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 cicd pipeline.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDPipeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update pipeline"
      },
      "patch": {
        "operationId": "cicd_pipelines_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 cicd pipeline.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedCICDPipelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedCICDPipelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedCICDPipelineRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDPipeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update pipeline"
      },
      "delete": {
        "operationId": "cicd_pipelines_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 cicd pipeline.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete pipeline"
      }
    },
    "/api/cicd/pipelines/{id}/trigger/": {
      "post": {
        "operationId": "cicd_pipelines_trigger_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this cicd pipeline.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CICDPipelineRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDPipeline"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Trigger: pipeline"
      }
    },
    "/api/cicd/promotion-gates/": {
      "get": {
        "operationId": "cicd_promotion_gates_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedPromotionGateList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List promotion gates"
      },
      "post": {
        "operationId": "cicd_promotion_gates_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": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionGate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create promotion gate"
      }
    },
    "/api/cicd/promotion-gates/{id}/": {
      "get": {
        "operationId": "cicd_promotion_gates_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 promotion gate.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionGate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve promotion gate"
      },
      "put": {
        "operationId": "cicd_promotion_gates_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 promotion gate.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionGate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update promotion gate"
      },
      "patch": {
        "operationId": "cicd_promotion_gates_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 promotion gate.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedPromotionGateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedPromotionGateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedPromotionGateRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionGate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update promotion gate"
      },
      "delete": {
        "operationId": "cicd_promotion_gates_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 promotion gate.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete promotion gate"
      }
    },
    "/api/cicd/promotion-gates/{id}/approve/": {
      "post": {
        "operationId": "cicd_promotion_gates_approve_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this promotion gate.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionGate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Approve: promotion gate"
      }
    },
    "/api/cicd/promotion-gates/{id}/waive/": {
      "post": {
        "operationId": "cicd_promotion_gates_waive_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this promotion gate.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PromotionGateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionGate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Waive: promotion gate"
      }
    },
    "/api/cicd/runs/": {
      "get": {
        "operationId": "cicd_runs_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedCICDRunList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List runs"
      }
    },
    "/api/cicd/runs/{id}/": {
      "get": {
        "operationId": "cicd_runs_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 cicd run.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDRun"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve run"
      }
    },
    "/api/cicd/runs/{id}/cancel/": {
      "post": {
        "operationId": "cicd_runs_cancel_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this cicd run.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CICDRunRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/CICDRunRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CICDRunRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDRun"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Cancel: run"
      }
    },
    "/api/cicd/runs/{id}/logs/": {
      "get": {
        "operationId": "cicd_runs_logs_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 cicd run.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CICDRun"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Logs: run"
      }
    },
    "/api/cicd/tokens/": {
      "get": {
        "operationId": "cicd_tokens_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedIntegrationTokenList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List tokens"
      },
      "post": {
        "operationId": "cicd_tokens_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": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationToken"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create token"
      }
    },
    "/api/cicd/tokens/{id}/": {
      "get": {
        "operationId": "cicd_tokens_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 integration token.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationToken"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve token"
      },
      "put": {
        "operationId": "cicd_tokens_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 integration token.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationToken"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update token"
      },
      "patch": {
        "operationId": "cicd_tokens_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 integration token.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedIntegrationTokenRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedIntegrationTokenRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedIntegrationTokenRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationToken"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update token"
      },
      "delete": {
        "operationId": "cicd_tokens_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 integration token.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete token"
      }
    },
    "/api/cicd/tokens/{id}/revoke/": {
      "post": {
        "operationId": "cicd_tokens_revoke_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).",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this integration token.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationTokenRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationToken"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Revoke: token"
      }
    },
    "/api/cicd/webhook-events/": {
      "get": {
        "operationId": "cicd_webhook_events_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": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedWebhookEventList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List webhook events"
      }
    },
    "/api/cicd/webhook-events/{id}/": {
      "get": {
        "operationId": "cicd_webhook_events_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 webhook event.",
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEvent"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve webhook event"
      }
    },
    "/api/cicd/webhook/{connection_id}/": {
      "post": {
        "operationId": "cicd_webhook_create",
        "description": "Unsigned inbound endpoint: /api/cicd/webhook/<connection_id>/",
        "parameters": [
          {
            "in": "path",
            "name": "connection_id",
            "schema": {
              "type": "integer"
            },
            "required": true
          }
        ],
        "tags": [
          "cicd"
        ],
        "security": [
          {
            "jwtAuth": []
          },
          {}
        ],
        "responses": {
          "200": {
            "description": "No response body"
          }
        },
        "summary": "Create webhook"
      }
    },
    "/api/environments/": {
      "get": {
        "operationId": "environments_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": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "environments"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Environment"
                  }
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List environments"
      },
      "post": {
        "operationId": "environments_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": [
          "environments"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnvironmentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/EnvironmentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/EnvironmentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Environment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create environment"
      }
    },
    "/api/environments/{id}/": {
      "get": {
        "operationId": "environments_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 environment.",
            "required": true
          }
        ],
        "tags": [
          "environments"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Environment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve environment"
      },
      "put": {
        "operationId": "environments_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 environment.",
            "required": true
          }
        ],
        "tags": [
          "environments"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnvironmentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/EnvironmentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/EnvironmentRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Environment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update environment"
      },
      "patch": {
        "operationId": "environments_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 environment.",
            "required": true
          }
        ],
        "tags": [
          "environments"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedEnvironmentRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedEnvironmentRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedEnvironmentRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Environment"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update environment"
      },
      "delete": {
        "operationId": "environments_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 environment.",
            "required": true
          }
        ],
        "tags": [
          "environments"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete environment"
      }
    },
    "/api/git-repos/": {
      "get": {
        "operationId": "git_repos_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": [
          "git-repos"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedGitRepoList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List git repos"
      },
      "post": {
        "operationId": "git_repos_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": [
          "git-repos"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRepo"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create git repo"
      }
    },
    "/api/git-repos/{id}/": {
      "get": {
        "operationId": "git_repos_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 git repo.",
            "required": true
          }
        ],
        "tags": [
          "git-repos"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRepo"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve git repo"
      },
      "put": {
        "operationId": "git_repos_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 git repo.",
            "required": true
          }
        ],
        "tags": [
          "git-repos"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRepo"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update git repo"
      },
      "patch": {
        "operationId": "git_repos_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 git repo.",
            "required": true
          }
        ],
        "tags": [
          "git-repos"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedGitRepoRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedGitRepoRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedGitRepoRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRepo"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update git repo"
      },
      "delete": {
        "operationId": "git_repos_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 git repo.",
            "required": true
          }
        ],
        "tags": [
          "git-repos"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete git repo"
      }
    },
    "/api/git-repos/{id}/pull/": {
      "post": {
        "operationId": "git_repos_pull_create",
        "description": "Clone or pull the repo, then scan for VS files.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this git repo.",
            "required": true
          }
        ],
        "tags": [
          "git-repos"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRepo"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Pull: git repo"
      }
    },
    "/api/git-repos/{id}/rematch/": {
      "post": {
        "operationId": "git_repos_rematch_create",
        "description": "Re-match playbooks to VIPs by the playbook's virtualserver\ndestination:port \u2014 the datacenter-unique identity (the same app name\nexists across sites with different addresses, so name alone cross-matches\nthe wrong datacenter). Reads every playbook in bulk via the collector, so\nit runs in the background \u2014 refresh in a few seconds for updated matches\nand drift badges.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this git repo.",
            "required": true
          }
        ],
        "tags": [
          "git-repos"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/GitRepoRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRepo"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Rematch: git repo"
      }
    },
    "/api/git-repos/ansible_status/": {
      "get": {
        "operationId": "git_repos_ansible_status_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).",
        "tags": [
          "git-repos"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GitRepo"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List ansible status"
      }
    },
    "/api/naming-conventions/": {
      "get": {
        "operationId": "naming_conventions_list",
        "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": [
          "naming-conventions"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedNamingConventionList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List naming conventions"
      },
      "post": {
        "operationId": "naming_conventions_create",
        "tags": [
          "naming-conventions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingConvention"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create naming convention"
      }
    },
    "/api/naming-conventions/{id}/": {
      "get": {
        "operationId": "naming_conventions_retrieve",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming convention.",
            "required": true
          }
        ],
        "tags": [
          "naming-conventions"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingConvention"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve naming convention"
      },
      "put": {
        "operationId": "naming_conventions_update",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming convention.",
            "required": true
          }
        ],
        "tags": [
          "naming-conventions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingConvention"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update naming convention"
      },
      "patch": {
        "operationId": "naming_conventions_partial_update",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming convention.",
            "required": true
          }
        ],
        "tags": [
          "naming-conventions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedNamingConventionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedNamingConventionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedNamingConventionRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingConvention"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update naming convention"
      },
      "delete": {
        "operationId": "naming_conventions_destroy",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming convention.",
            "required": true
          }
        ],
        "tags": [
          "naming-conventions"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete naming convention"
      }
    },
    "/api/naming-conventions/{id}/preview/": {
      "post": {
        "operationId": "naming_conventions_preview_create",
        "description": "Preview the generated name for a given source_name, environment, port.",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming convention.",
            "required": true
          }
        ],
        "tags": [
          "naming-conventions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/NamingConventionRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingConvention"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Preview: naming convention"
      }
    },
    "/api/naming-templates/": {
      "get": {
        "operationId": "naming_templates_list",
        "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"
            }
          },
          {
            "in": "query",
            "name": "resource_type",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "A search term.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "tags": [
          "naming-templates"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedNamingTemplateList"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "List naming templates"
      },
      "post": {
        "operationId": "naming_templates_create",
        "tags": [
          "naming-templates"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NamingTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/NamingTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/NamingTemplateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Create naming template"
      }
    },
    "/api/naming-templates/{id}/": {
      "get": {
        "operationId": "naming_templates_retrieve",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming template.",
            "required": true
          }
        ],
        "tags": [
          "naming-templates"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Retrieve naming template"
      },
      "put": {
        "operationId": "naming_templates_update",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming template.",
            "required": true
          }
        ],
        "tags": [
          "naming-templates"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NamingTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/NamingTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/NamingTemplateRequest"
              }
            }
          },
          "required": true
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update naming template"
      },
      "patch": {
        "operationId": "naming_templates_partial_update",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming template.",
            "required": true
          }
        ],
        "tags": [
          "naming-templates"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatchedNamingTemplateRequest"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/PatchedNamingTemplateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PatchedNamingTemplateRequest"
              }
            }
          }
        },
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamingTemplate"
                }
              }
            },
            "description": ""
          }
        },
        "summary": "Update naming template"
      },
      "delete": {
        "operationId": "naming_templates_destroy",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "integer"
            },
            "description": "A unique integer value identifying this naming template.",
            "required": true
          }
        ],
        "tags": [
          "naming-templates"
        ],
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "responses": {
          "204": {
            "description": "No response body"
          }
        },
        "summary": "Delete naming template"
      }
    }
  },
  "components": {
    "schemas": {
      "CICDConnection": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "provider_type": {
            "$ref": "#/components/schemas/CICDConnectionProviderTypeEnum"
          },
          "base_url": {
            "type": "string",
            "maxLength": 1000
          },
          "default_scope": {},
          "capabilities": {},
          "verify_tls": {
            "type": "boolean"
          },
          "health_status": {
            "$ref": "#/components/schemas/HealthStatusEnum"
          },
          "health_message": {
            "type": "string"
          },
          "last_health_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "base_url",
          "created_at",
          "id",
          "name",
          "provider_type",
          "updated_at"
        ]
      },
      "PaginatedNamingTemplateList": {
        "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/NamingTemplate"
            }
          }
        }
      },
      "ChangeSet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "environment": {
            "type": "string",
            "maxLength": 200
          },
          "device_scope": {},
          "intent_source": {
            "description": "{repo, ref, path} or {inline}"
          },
          "state_hash": {
            "type": "string",
            "description": "SHA-256 of live state at plan time",
            "maxLength": 64
          },
          "diff": {},
          "risk_class": {
            "$ref": "#/components/schemas/RiskClassEnum"
          },
          "summary": {},
          "created_by_token": {
            "type": "integer",
            "nullable": true
          },
          "token_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "run_context": {},
          "status": {
            "$ref": "#/components/schemas/ChangeSetStatusEnum"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "id",
          "token_name"
        ]
      },
      "GitOpsBindingRequest": {
        "type": "object",
        "properties": {
          "connection": {
            "type": "integer",
            "nullable": true
          },
          "repo": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "environment": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "device_scope": {},
          "apply_policy": {
            "$ref": "#/components/schemas/ApplyPolicyEnum"
          },
          "auto_rollback": {
            "type": "boolean"
          },
          "enforce_mode": {
            "type": "boolean",
            "description": "Revert out-of-band changes (observe-only if false)"
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "environment",
          "repo"
        ]
      },
      "PatchedAnsibleFileRequest": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "file_path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "vs_name_hint": {
            "type": "string",
            "maxLength": 300
          },
          "pb_destination": {
            "type": "string",
            "maxLength": 200
          },
          "pb_port": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648,
            "nullable": true
          },
          "content_hash": {
            "type": "string",
            "maxLength": 64
          },
          "last_synced": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "drift_count": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "drift_checked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "git_repo": {
            "type": "integer"
          },
          "virtual_server": {
            "type": "integer",
            "nullable": true
          },
          "device_group": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "PatchedCICDPipelineRequest": {
        "type": "object",
        "properties": {
          "connection": {
            "type": "integer"
          },
          "external_ref": {
            "description": "Provider-specific: {job_path} Jenkins, {project_id, ref} GitLab, {owner, repo, workflow} GitHub..."
          },
          "display_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "direction": {
            "$ref": "#/components/schemas/DirectionEnum"
          },
          "default_params": {},
          "description": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "Environment": {
        "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": 50
          },
          "description": {
            "type": "string"
          },
          "sort_order": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "device_count",
          "id",
          "name"
        ]
      },
      "PatchedNamingTemplateRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "description": {
            "type": "string"
          },
          "example": {
            "type": "string",
            "maxLength": 200
          },
          "resource_type": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        }
      },
      "PaginatedCICDPipelineList": {
        "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/CICDPipeline"
            }
          }
        }
      },
      "PatchedGitRepoRequest": {
        "type": "object",
        "properties": {
          "git_token": {
            "type": "string",
            "writeOnly": true
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "branch": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string"
          },
          "git_username": {
            "type": "string",
            "maxLength": 200
          },
          "site": {
            "type": "integer",
            "nullable": true,
            "description": "Run git + ansible for this repo ON the site collector (for on-prem repos & devices)."
          }
        }
      },
      "NamingTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "pattern": {
            "type": "string",
            "maxLength": 500
          },
          "description": {
            "type": "string"
          },
          "example": {
            "type": "string",
            "maxLength": 200
          },
          "resource_type": {
            "type": "string",
            "maxLength": 100
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "id",
          "name",
          "pattern"
        ]
      },
      "PatchedEnvironmentRequest": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "description": {
            "type": "string"
          },
          "sort_order": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          }
        }
      },
      "PaginatedCICDRunList": {
        "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/CICDRun"
            }
          }
        }
      },
      "ApplyJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "change_set": {
            "type": "integer"
          },
          "environment": {
            "type": "string",
            "readOnly": true
          },
          "risk_class": {
            "type": "string",
            "readOnly": true
          },
          "idempotency_key": {
            "type": "string",
            "maxLength": 300
          },
          "rollback_backup_ref": {
            "type": "string",
            "maxLength": 500
          },
          "partial_failure_policy": {
            "$ref": "#/components/schemas/PartialFailurePolicyEnum"
          },
          "per_device_status": {},
          "state": {
            "$ref": "#/components/schemas/ApplyJobStateEnum"
          },
          "gate_results": {},
          "error_message": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "change_set",
          "created_at",
          "environment",
          "id",
          "risk_class",
          "updated_at"
        ]
      },
      "PaginatedChangeSetList": {
        "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/ChangeSet"
            }
          }
        }
      },
      "PatchedGitOpsBindingRequest": {
        "type": "object",
        "properties": {
          "connection": {
            "type": "integer",
            "nullable": true
          },
          "repo": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "environment": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "device_scope": {},
          "apply_policy": {
            "$ref": "#/components/schemas/ApplyPolicyEnum"
          },
          "auto_rollback": {
            "type": "boolean"
          },
          "enforce_mode": {
            "type": "boolean",
            "description": "Revert out-of-band changes (observe-only if false)"
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "CICDRunRequest": {
        "type": "object",
        "properties": {
          "pipeline": {
            "type": "integer"
          },
          "external_run_ref": {
            "type": "string",
            "maxLength": 500
          },
          "normalized_state": {
            "$ref": "#/components/schemas/NormalizedStateEnum"
          },
          "trigger_source": {
            "$ref": "#/components/schemas/TriggerSourceEnum"
          },
          "run_context": {},
          "idempotency_key": {
            "type": "string",
            "maxLength": 300
          },
          "params": {},
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "result_detail": {}
        },
        "required": [
          "pipeline"
        ]
      },
      "ApplyJobRequest": {
        "type": "object",
        "properties": {
          "change_set": {
            "type": "integer"
          },
          "idempotency_key": {
            "type": "string",
            "maxLength": 300
          },
          "rollback_backup_ref": {
            "type": "string",
            "maxLength": 500
          },
          "partial_failure_policy": {
            "$ref": "#/components/schemas/PartialFailurePolicyEnum"
          },
          "per_device_status": {},
          "state": {
            "$ref": "#/components/schemas/ApplyJobStateEnum"
          },
          "gate_results": {},
          "error_message": {
            "type": "string"
          }
        },
        "required": [
          "change_set"
        ]
      },
      "PaginatedAnsibleFileList": {
        "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/AnsibleFile"
            }
          }
        }
      },
      "HealthStatusEnum": {
        "enum": [
          "unchecked",
          "ok",
          "error"
        ],
        "type": "string",
        "description": "* `unchecked` - Not checked\n* `ok` - Connected\n* `error` - Error"
      },
      "PromotionGateStatusEnum": {
        "enum": [
          "pending",
          "passed",
          "failed",
          "waived"
        ],
        "type": "string",
        "description": "* `pending` - Pending\n* `passed` - Passed\n* `failed` - Failed\n* `waived` - Waived"
      },
      "NamingConventionRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "delimiter": {
            "$ref": "#/components/schemas/DelimiterEnum"
          },
          "case_style": {
            "$ref": "#/components/schemas/CaseStyleEnum"
          },
          "components": {},
          "environments": {},
          "vs_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "pool_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "hm_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "rule_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "client_ssl_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "server_ssl_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "vip_address_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "description": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ]
      },
      "ChangeSetStatusEnum": {
        "enum": [
          "planned",
          "consumed",
          "expired",
          "abandoned"
        ],
        "type": "string",
        "description": "* `planned` - Planned\n* `consumed` - Consumed\n* `expired` - Expired\n* `abandoned` - Abandoned"
      },
      "CICDConnectionProviderTypeEnum": {
        "enum": [
          "jenkins",
          "gitlab",
          "github_actions",
          "azure_devops"
        ],
        "type": "string",
        "description": "* `jenkins` - Jenkins\n* `gitlab` - GitLab CI\n* `github_actions` - GitHub Actions\n* `azure_devops` - Azure DevOps"
      },
      "PaginatedPromotionGateList": {
        "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/PromotionGate"
            }
          }
        }
      },
      "PatchedPromotionGateRequest": {
        "type": "object",
        "properties": {
          "subject_type": {
            "$ref": "#/components/schemas/SubjectTypeEnum"
          },
          "subject_id": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "gate_type": {
            "$ref": "#/components/schemas/GateTypeEnum"
          },
          "status": {
            "$ref": "#/components/schemas/PromotionGateStatusEnum"
          },
          "config": {},
          "evaluated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "evidence_ref": {
            "type": "string",
            "maxLength": 500
          },
          "actor": {
            "type": "integer",
            "nullable": true
          },
          "waiver_reason": {
            "type": "string"
          }
        }
      },
      "NamingConvention": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "delimiter": {
            "$ref": "#/components/schemas/DelimiterEnum"
          },
          "case_style": {
            "$ref": "#/components/schemas/CaseStyleEnum"
          },
          "components": {},
          "environments": {},
          "vs_suffix": {
            "type": "string",
            "maxLength": 50
          },
          "pool_suffix": {
            "type": "string",
            "maxLength": 50
          },
          "hm_suffix": {
            "type": "string",
            "maxLength": 50
          },
          "rule_suffix": {
            "type": "string",
            "maxLength": 50
          },
          "client_ssl_suffix": {
            "type": "string",
            "maxLength": 50
          },
          "server_ssl_suffix": {
            "type": "string",
            "maxLength": 50
          },
          "vip_address_suffix": {
            "type": "string",
            "maxLength": 50
          },
          "description": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "id",
          "name",
          "updated_at"
        ]
      },
      "GitRepo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "file_count": {
            "type": "string",
            "readOnly": true
          },
          "has_git_token": {
            "type": "string",
            "readOnly": true
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "url": {
            "type": "string",
            "maxLength": 500
          },
          "branch": {
            "type": "string",
            "maxLength": 100
          },
          "description": {
            "type": "string"
          },
          "collector_files": {
            "readOnly": true
          },
          "last_commit": {
            "type": "string",
            "readOnly": true
          },
          "git_username": {
            "type": "string",
            "maxLength": 200
          },
          "local_path": {
            "type": "string",
            "readOnly": true
          },
          "last_pulled": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "nullable": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GitRepoStatusEnum"
              }
            ],
            "readOnly": true
          },
          "error": {
            "type": "string",
            "readOnly": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "site": {
            "type": "integer",
            "nullable": true,
            "description": "Run git + ansible for this repo ON the site collector (for on-prem repos & devices)."
          }
        },
        "required": [
          "collector_files",
          "created_at",
          "error",
          "file_count",
          "has_git_token",
          "id",
          "last_commit",
          "last_pulled",
          "local_path",
          "name",
          "status",
          "updated_at",
          "url"
        ]
      },
      "SubjectTypeEnum": {
        "enum": [
          "apply",
          "promotion"
        ],
        "type": "string",
        "description": "* `apply` - ApplyJob\n* `promotion` - Environment Promotion"
      },
      "CICDConnectionRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "provider_type": {
            "$ref": "#/components/schemas/CICDConnectionProviderTypeEnum"
          },
          "base_url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "auth_credential": {
            "type": "string",
            "writeOnly": true,
            "description": "JSON: {username, token} for Jenkins; {token} for GitLab/GH/Azure; optionally {webhook_secret}",
            "maxLength": 2000
          },
          "default_scope": {},
          "capabilities": {},
          "verify_tls": {
            "type": "boolean"
          },
          "health_status": {
            "$ref": "#/components/schemas/HealthStatusEnum"
          },
          "health_message": {
            "type": "string"
          },
          "last_health_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "base_url",
          "name",
          "provider_type"
        ]
      },
      "GitRepoStatusEnum": {
        "enum": [
          "pending",
          "cloning",
          "ready",
          "error"
        ],
        "type": "string",
        "description": "* `pending` - Pending\n* `cloning` - Cloning\n* `ready` - Ready\n* `error` - Error"
      },
      "PromotionGateRequest": {
        "type": "object",
        "properties": {
          "subject_type": {
            "$ref": "#/components/schemas/SubjectTypeEnum"
          },
          "subject_id": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "gate_type": {
            "$ref": "#/components/schemas/GateTypeEnum"
          },
          "status": {
            "$ref": "#/components/schemas/PromotionGateStatusEnum"
          },
          "config": {},
          "evaluated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "evidence_ref": {
            "type": "string",
            "maxLength": 500
          },
          "actor": {
            "type": "integer",
            "nullable": true
          },
          "waiver_reason": {
            "type": "string"
          }
        },
        "required": [
          "gate_type",
          "subject_id",
          "subject_type"
        ]
      },
      "DelimiterEnum": {
        "enum": [
          "-",
          "_"
        ],
        "type": "string",
        "description": "* `-` - Dash (-)\n* `_` - Underscore (_)"
      },
      "NormalizedStateEnum": {
        "enum": [
          "queued",
          "running",
          "success",
          "failed",
          "canceled",
          "unstable",
          "timeout"
        ],
        "type": "string",
        "description": "* `queued` - Queued\n* `running` - Running\n* `success` - Success\n* `failed` - Failed\n* `canceled` - Canceled\n* `unstable` - Unstable\n* `timeout` - Timed Out"
      },
      "PaginatedWebhookEventList": {
        "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/WebhookEvent"
            }
          }
        }
      },
      "ApplyJobStateEnum": {
        "enum": [
          "queued",
          "running",
          "succeeded",
          "failed",
          "rolled_back"
        ],
        "type": "string",
        "description": "* `queued` - Queued\n* `running` - Running\n* `succeeded` - Succeeded\n* `failed` - Failed\n* `rolled_back` - Rolled Back"
      },
      "IntegrationToken": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "principal": {
            "type": "integer",
            "nullable": true,
            "description": "Human owner / creator of this token"
          },
          "principal_username": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "prefix": {
            "type": "string",
            "readOnly": true
          },
          "allowed_verbs": {
            "description": "List of verbs; empty = all allowed"
          },
          "scope_constraint": {
            "description": "{environment: str, device_ids: [...]}"
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/IntegrationTokenStatusEnum"
              }
            ],
            "readOnly": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true,
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "created_at",
          "id",
          "last_used_at",
          "name",
          "prefix",
          "principal_username",
          "status"
        ]
      },
      "GateTypeEnum": {
        "enum": [
          "approval",
          "change_window",
          "throughline_verify",
          "risk_threshold"
        ],
        "type": "string",
        "description": "* `approval` - Manual Approval\n* `change_window` - Change Window\n* `throughline_verify` - Throughline Verification\n* `risk_threshold` - Risk Threshold"
      },
      "GitRepoRequest": {
        "type": "object",
        "properties": {
          "git_token": {
            "type": "string",
            "writeOnly": true
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "branch": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string"
          },
          "git_username": {
            "type": "string",
            "maxLength": 200
          },
          "site": {
            "type": "integer",
            "nullable": true,
            "description": "Run git + ansible for this repo ON the site collector (for on-prem repos & devices)."
          }
        },
        "required": [
          "name",
          "url"
        ]
      },
      "NamingTemplateRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "description": {
            "type": "string"
          },
          "example": {
            "type": "string",
            "maxLength": 200
          },
          "resource_type": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        },
        "required": [
          "name",
          "pattern"
        ]
      },
      "DirectionEnum": {
        "enum": [
          "outbound_trigger",
          "inbound_callback",
          "both"
        ],
        "type": "string",
        "description": "* `outbound_trigger` - Outbound Trigger\n* `inbound_callback` - Inbound Callback\n* `both` - Both"
      },
      "PatchedNamingConventionRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "delimiter": {
            "$ref": "#/components/schemas/DelimiterEnum"
          },
          "case_style": {
            "$ref": "#/components/schemas/CaseStyleEnum"
          },
          "components": {},
          "environments": {},
          "vs_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "pool_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "hm_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "rule_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "client_ssl_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "server_ssl_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "vip_address_suffix": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "description": {
            "type": "string"
          }
        }
      },
      "CICDPipeline": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "connection": {
            "type": "integer"
          },
          "connection_name": {
            "type": "string",
            "readOnly": true
          },
          "provider_type": {
            "type": "string",
            "readOnly": true
          },
          "external_ref": {
            "description": "Provider-specific: {job_path} Jenkins, {project_id, ref} GitLab, {owner, repo, workflow} GitHub..."
          },
          "display_name": {
            "type": "string",
            "maxLength": 300
          },
          "direction": {
            "$ref": "#/components/schemas/DirectionEnum"
          },
          "default_params": {},
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "connection",
          "connection_name",
          "created_at",
          "display_name",
          "external_ref",
          "id",
          "provider_type"
        ]
      },
      "PaginatedApplyJobList": {
        "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/ApplyJob"
            }
          }
        }
      },
      "TriggerSourceEnum": {
        "enum": [
          "versus_workflow",
          "gitops",
          "manual",
          "external"
        ],
        "type": "string",
        "description": "* `versus_workflow` - Versus Workflow\n* `gitops` - GitOps\n* `manual` - Manual\n* `external` - External"
      },
      "PaginatedIntegrationTokenList": {
        "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/IntegrationToken"
            }
          }
        }
      },
      "PromotionGate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "subject_type": {
            "$ref": "#/components/schemas/SubjectTypeEnum"
          },
          "subject_id": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "gate_type": {
            "$ref": "#/components/schemas/GateTypeEnum"
          },
          "status": {
            "$ref": "#/components/schemas/PromotionGateStatusEnum"
          },
          "config": {},
          "evaluated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "evidence_ref": {
            "type": "string",
            "maxLength": 500
          },
          "actor": {
            "type": "integer",
            "nullable": true
          },
          "actor_username": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "waiver_reason": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "actor_username",
          "created_at",
          "gate_type",
          "id",
          "subject_id",
          "subject_type"
        ]
      },
      "IntegrationTokenRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "principal": {
            "type": "integer",
            "nullable": true,
            "description": "Human owner / creator of this token"
          },
          "allowed_verbs": {
            "description": "List of verbs; empty = all allowed"
          },
          "scope_constraint": {
            "description": "{environment: str, device_ids: [...]}"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "name"
        ]
      },
      "PaginatedCICDConnectionList": {
        "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/CICDConnection"
            }
          }
        }
      },
      "ApplyPolicyEnum": {
        "enum": [
          "manual_apply",
          "auto_apply",
          "plan_only"
        ],
        "type": "string",
        "description": "* `manual_apply` - Manual Apply (plan on PR, apply on approval)\n* `auto_apply` - Auto Apply on Merge\n* `plan_only` - Plan Only (advisory diffs)"
      },
      "GitOpsBinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "connection": {
            "type": "integer",
            "nullable": true
          },
          "connection_name": {
            "type": "string",
            "readOnly": true,
            "default": ""
          },
          "repo": {
            "type": "string",
            "maxLength": 500
          },
          "ref": {
            "type": "string",
            "maxLength": 200
          },
          "path": {
            "type": "string",
            "maxLength": 500
          },
          "environment": {
            "type": "string",
            "maxLength": 200
          },
          "device_scope": {},
          "apply_policy": {
            "$ref": "#/components/schemas/ApplyPolicyEnum"
          },
          "auto_rollback": {
            "type": "boolean"
          },
          "enforce_mode": {
            "type": "boolean",
            "description": "Revert out-of-band changes (observe-only if false)"
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "connection_name",
          "created_at",
          "environment",
          "id",
          "repo",
          "updated_at"
        ]
      },
      "PartialFailurePolicyEnum": {
        "enum": [
          "halt",
          "continue"
        ],
        "type": "string",
        "description": "* `halt` - Halt on first error\n* `continue` - Continue and report"
      },
      "PatchedIntegrationTokenRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "principal": {
            "type": "integer",
            "nullable": true,
            "description": "Human owner / creator of this token"
          },
          "allowed_verbs": {
            "description": "List of verbs; empty = all allowed"
          },
          "scope_constraint": {
            "description": "{environment: str, device_ids: [...]}"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "PaginatedGitRepoList": {
        "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/GitRepo"
            }
          }
        }
      },
      "AnsibleFile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "vs_name": {
            "type": "string",
            "readOnly": true,
            "nullable": true
          },
          "device_group_name": {
            "type": "string",
            "readOnly": true,
            "nullable": true
          },
          "git_repo_name": {
            "type": "string",
            "readOnly": true
          },
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "file_path": {
            "type": "string",
            "maxLength": 500
          },
          "vs_name_hint": {
            "type": "string",
            "maxLength": 300
          },
          "pb_destination": {
            "type": "string",
            "maxLength": 200
          },
          "pb_port": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648,
            "nullable": true
          },
          "content_hash": {
            "type": "string",
            "maxLength": 64
          },
          "last_synced": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "drift_count": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "drift_checked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "git_repo": {
            "type": "integer"
          },
          "virtual_server": {
            "type": "integer",
            "nullable": true
          },
          "device_group": {
            "type": "integer",
            "nullable": true
          }
        },
        "required": [
          "created_at",
          "device_group_name",
          "file_path",
          "git_repo",
          "git_repo_name",
          "id",
          "vs_name"
        ]
      },
      "CICDPipelineRequest": {
        "type": "object",
        "properties": {
          "connection": {
            "type": "integer"
          },
          "external_ref": {
            "description": "Provider-specific: {job_path} Jenkins, {project_id, ref} GitLab, {owner, repo, workflow} GitHub..."
          },
          "display_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "direction": {
            "$ref": "#/components/schemas/DirectionEnum"
          },
          "default_params": {},
          "description": {
            "type": "string",
            "maxLength": 500
          }
        },
        "required": [
          "connection",
          "display_name",
          "external_ref"
        ]
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "connection": {
            "type": "integer"
          },
          "connection_name": {
            "type": "string",
            "readOnly": true
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          },
          "signature_valid": {
            "type": "boolean"
          },
          "normalized_event": {},
          "processed": {
            "type": "boolean"
          },
          "processing_error": {
            "type": "string"
          }
        },
        "required": [
          "connection",
          "connection_name",
          "id",
          "received_at"
        ]
      },
      "CICDRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "readOnly": true
          },
          "pipeline": {
            "type": "integer"
          },
          "pipeline_name": {
            "type": "string",
            "readOnly": true
          },
          "connection_name": {
            "type": "string",
            "readOnly": true
          },
          "external_run_ref": {
            "type": "string",
            "maxLength": 500
          },
          "normalized_state": {
            "$ref": "#/components/schemas/NormalizedStateEnum"
          },
          "trigger_source": {
            "$ref": "#/components/schemas/TriggerSourceEnum"
          },
          "run_context": {},
          "idempotency_key": {
            "type": "string",
            "maxLength": 300
          },
          "params": {},
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "result_detail": {},
          "created_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        },
        "required": [
          "connection_name",
          "created_at",
          "id",
          "pipeline",
          "pipeline_name"
        ]
      },
      "CaseStyleEnum": {
        "enum": [
          "as_is",
          "lower",
          "upper",
          "title"
        ],
        "type": "string",
        "description": "* `as_is` - As-is\n* `lower` - lowercase\n* `upper` - UPPERCASE\n* `title` - Title Case"
      },
      "RiskClassEnum": {
        "enum": [
          "low",
          "medium",
          "high",
          "critical"
        ],
        "type": "string",
        "description": "* `low` - Low\n* `medium` - Medium\n* `high` - High\n* `critical` - Critical"
      },
      "PaginatedNamingConventionList": {
        "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/NamingConvention"
            }
          }
        }
      },
      "PatchedCICDConnectionRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "provider_type": {
            "$ref": "#/components/schemas/CICDConnectionProviderTypeEnum"
          },
          "base_url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
          },
          "auth_credential": {
            "type": "string",
            "writeOnly": true,
            "description": "JSON: {username, token} for Jenkins; {token} for GitLab/GH/Azure; optionally {webhook_secret}",
            "maxLength": 2000
          },
          "default_scope": {},
          "capabilities": {},
          "verify_tls": {
            "type": "boolean"
          },
          "health_status": {
            "$ref": "#/components/schemas/HealthStatusEnum"
          },
          "health_message": {
            "type": "string"
          },
          "last_health_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "IntegrationTokenStatusEnum": {
        "enum": [
          "active",
          "revoked"
        ],
        "type": "string",
        "description": "* `active` - Active\n* `revoked` - Revoked"
      },
      "AnsibleFileRequest": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "file_path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "vs_name_hint": {
            "type": "string",
            "maxLength": 300
          },
          "pb_destination": {
            "type": "string",
            "maxLength": 200
          },
          "pb_port": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648,
            "nullable": true
          },
          "content_hash": {
            "type": "string",
            "maxLength": 64
          },
          "last_synced": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "drift_count": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          },
          "drift_checked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "git_repo": {
            "type": "integer"
          },
          "virtual_server": {
            "type": "integer",
            "nullable": true
          },
          "device_group": {
            "type": "integer",
            "nullable": true
          }
        },
        "required": [
          "file_path",
          "git_repo"
        ]
      },
      "PaginatedGitOpsBindingList": {
        "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/GitOpsBinding"
            }
          }
        }
      },
      "EnvironmentRequest": {
        "type": "object",
        "properties": {
          "tenant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50
          },
          "description": {
            "type": "string"
          },
          "sort_order": {
            "type": "integer",
            "maximum": 2147483647,
            "minimum": -2147483648
          }
        },
        "required": [
          "name"
        ]
      }
    }
  }
}